From 83d03a3dab37833aa87df6f442319d6fb2c3a5a1 Mon Sep 17 00:00:00 2001 From: Jz0ojiang Date: Mon, 31 Aug 2026 15:42:46 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96=20why-here=20skill?= =?UTF-8?q?=20=E4=BB=93=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitea/workflows/release.yml | 88 +++++++++++++++++++ .gitignore | 11 +++ README.md | 111 ++++++++++++++++++++++++ skill/SKILL.md | 159 +++++++++++++++++++++++++++++++++++ 4 files changed, 369 insertions(+) create mode 100644 .gitea/workflows/release.yml create mode 100644 .gitignore create mode 100644 README.md create mode 100644 skill/SKILL.md diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml new file mode 100644 index 0000000..4cd5e35 --- /dev/null +++ b/.gitea/workflows/release.yml @@ -0,0 +1,88 @@ +name: 发布 skill + +# 推送与 v* 匹配的 annotated tag 时,将 skill/ 目录打包为 ZIP 文件,并发布到当前仓库的 Release。 +# ZIP 根目录直接包含 SKILL.md;Release 正文使用 annotated tag message。 + +on: + push: + tags: + - "v*" + +jobs: + release: + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: 检出源代码 + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: 读取版本说明并打包 skill + id: pack + run: | + set -eu + + tag_ref="refs/tags/$GITHUB_REF_NAME" + # actions/checkout may replace an annotated tag ref with its peeled commit + # when a branch has the same name. Fetch the full tag ref again before + # reading its object type and message. + git fetch --force origin "$tag_ref:$tag_ref" + object_type=$(git cat-file -t "$tag_ref") + test "$object_type" = tag || { + echo "发布 tag 必须是 annotated tag,当前对象类型:$object_type" + exit 1 + } + + git for-each-ref "$tag_ref" --format='%(contents)' > release-message.md + test -s release-message.md || { + echo "annotated tag message 为空" + exit 1 + } + + name=$(sed -n 's/^name:[[:space:]]*//p' skill/SKILL.md | head -1) + test -n "$name" || { + echo "无法从 skill/SKILL.md 读取 name 字段" + exit 1 + } + + (cd skill && zip -qr "../$name.zip" .) + echo "name=$name" >> "$GITHUB_OUTPUT" + echo "已生成 ZIP 文件:$name.zip" + echo "ZIP 文件清单:" + unzip -l "$name.zip" + + - name: 创建 Release 并上传附件 + env: + TOKEN: ${{ secrets.GITHUB_TOKEN }} + API: ${{ github.server_url }}/api/v1/repos/${{ github.repository }} + TAG: ${{ github.ref_name }} + ZIP: ${{ steps.pack.outputs.name }}.zip + run: | + set -eu + + payload=$(python3 - <<'PY' + import json + import os + from pathlib import Path + + print(json.dumps({ + "tag_name": os.environ["TAG"], + "name": os.environ["TAG"], + "body": Path("release-message.md").read_text(encoding="utf-8").rstrip(), + })) + PY + ) + + response=$(curl -fsS -X POST "$API/releases" \ + -H "Authorization: token $TOKEN" \ + -H "Content-Type: application/json" \ + -d "$payload") + id=$(printf '%s' "$response" | python3 -c 'import json, sys; print(json.load(sys.stdin)["id"])') + + curl -fsS -X POST "$API/releases/$id/assets?name=$ZIP" \ + -H "Authorization: token $TOKEN" \ + -F "attachment=@$ZIP" + echo "已发布 Release:$TAG" + echo "已上传附件:$ZIP" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6d25db6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +# System and editor files +.DS_Store +Thumbs.db +Desktop.ini +*.swp +*.swo +*~ + +# Local tool state +.claude/ +.pi/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..3790adb --- /dev/null +++ b/README.md @@ -0,0 +1,111 @@ +# why-here + +`why-here` 是一个在交付前使用的审查 skill(供 agent 按需加载的一组工作规则)。它对改动里每个可疑的部分问同一个问题:**它现在为什么还在这里?** + +完成后的结果仍然需要它,就留下。它只是因为做的过程走过某条路才存在,就删掉。 + +## 它解决什么问题 + +一次改动做完时,常会留下只对制作过程有意义的内容:回应早先误解的说明、放弃的方案留下的分支、迁移完成后仍在的临时字段、被替换掉的旧实现。这些内容一旦进入代码、接口、配置、注释或文档,下一个读到它们的人会把它们当成当前设计的一部分继续理解和维护。 + +普通的“简化”定位不了这个问题。一段说明可能很短,却仍然只是在记录来路;一个结构可能看起来不整洁,却仍被调用方、公开契约、已有数据或外部系统要求,删掉就出事。判断标准不是长短,也不是整洁,而是完成后的结果现在还需不需要它。 + +## 它如何判断 + +审查分五步: + +1. 先确定完成后的结果是什么。从用户的请求、验收标准、相关文档、当前契约和真实调用方推导需要的行为、结构和文档。依据缺失或互相矛盾时,标记为证据不足,不从当前实现反推。 +2. 找出可能已经过期的部分:回应旧误解的说明、复述尝试和反复的叙述、迁移留下的临时字段和分支、保护已被删除行为的测试、新旧两套表示同时保留的情况。 +3. 逐项找现在仍然成立的理由。文字看删除后是否会改变读者的理解、判断或操作;代码、接口、配置和数据结构要检查当前调用方、契约、已有数据、外部约束。历史能解释一个东西为什么出现,但只有当前证据能决定它是否留下。 +4. 只在证明过的范围内清理。默认只审查和报告,用户明确要求修改时才动文件。公开接口、持久化数据、传输格式、迁移历史和仍在使用的兼容路径,先给出证据和拟议结果,取得授权再改。 +5. 跑针对性验证,检查有没有产生新的孤立代码、测试、配置或文档。 + +“如果从完成后的设计开始,还会加入它吗”这个反问只用来找候选项。答案是“不会”只说明值得调查,不等于可以删。 + +最终回复说明完成后的状态、仍然成立的约束、实际跑过的检查,以及因为证据或授权不足而保持原样的部分。它不会把刚清掉的过程改写成一份删除清单再交回来。 + +## 适用场景 + +`why-here` 可以由 agent 在交付前主动选择,也可以由你显式调用。它适合: + +- 一次改动中途换过方案、纠正过误解,或者完成了一次内部迁移; +- 你想知道某个字段、状态、分支、注释或文档段落是否还属于完成后的结果; +- 你要清理旧实现留下的内容,同时保留真实的兼容要求。 + +它不适合只想减少行数的重构和代码美化,不适合以找出全仓库死代码和重复抽象为目标的简化审计,也不适合只要求把文字写得自包含或核实事实。调查和方案设计还在进行时不要用它:那时的过程记录仍在起作用。 + +## 安装 + +所有 skill 统一从分发仓 `agent-skills/skills` 安装: + +```bash +npx skills add https://git.im0o.top/agent-skills/skills.git +``` + +安装程序会列出全部 skill,勾选 `why-here` 即可。获取仓库前,可以先检查当前机器是否有访问权限: + +```bash +git ls-remote https://git.im0o.top/agent-skills/skills.git HEAD +``` + +命令返回 commit hash 就表示可以继续安装。 + +本仓库负责开发和发布,不作为常规安装来源。需要单独取一份时,可以从本仓 Release 下载 `why-here.zip`,解压后把目录交给安装工具: + +```bash +npx skills add ./why-here +``` + +安装工具不接受 ZIP 文件路径,只接受解压后的目录。 + +## 使用 + +交付前直接说明要审查的范围: + +```text +这次改动中途换过方案。提交前用 why-here 看看还有没有只属于旧方案的内容。 +``` + +```text +迁移已经做完了。用 why-here 检查这些临时字段和兼容分支现在还有没有人用,先只报告不要改。 +``` + +```text +这份文档里有几段是在回应我早先的误解。用 why-here 判断哪些对现在的读者还有用,可以直接改。 +``` + +在提供同名斜杠命令的客户端中,也可以运行: + +```text +/why-here +``` + +默认是只审查不修改。需要它直接动文件时,在请求里说清楚,例如“可以直接改”“清理掉”。 + +## 与其他 skill 的边界 + +`think-twice` 负责把一段文字重写成读者可以独立理解和执行的最终文本,范围只有文字。`why-here` 同时看文字和有运行意义的产物,而且对两者用不同的修改门槛:文字按读者是否需要判断,代码、接口和数据结构必须再检查真实调用方、契约、已有数据和外部约束。 + +以查找死代码、重复状态和无收益抽象为目标的通用简化审计是另一件事。`why-here` 的判据只有一条:这个部分现在是否只因为做的过程走过某条路才存在。 + +`why-here` 不依赖其他 skill,可以单独安装使用。 + +## 文件说明 + +```text +skill/ +└── SKILL.md # 审查范围、五步流程、分类判据和交付要求 +``` + +发布包只包含 `skill/` 目录中的内容,因此解压后的 `why-here.zip` 根目录直接包含 `SKILL.md`。 + +## 发布 + +推送与 `v*` 匹配的 annotated tag(带说明文字的 tag)后,Gitea Actions 会运行 `.gitea/workflows/release.yml`: + +1. 读取 annotated tag message,作为 Release 正文; +2. 读取 `skill/SKILL.md` 中的 `name` 字段; +3. 将 `skill/` 打包为 `why-here.zip`; +4. 创建同名 Release,并上传 ZIP 文件。 + +发布 tag 必须是 annotated tag,而且 message 不能为空。创建和推送 tag 时使用完整的 `refs/tags/` 路径,避免同名 branch 与 tag 混淆。 diff --git a/skill/SKILL.md b/skill/SKILL.md new file mode 100644 index 0000000..82bd6cc --- /dev/null +++ b/skill/SKILL.md @@ -0,0 +1,159 @@ +--- +name: why-here +description: > + Review a change before delivery after the work involved an abandoned attempt, + corrected misunderstanding, completed transition, or superseded implementation, + or when the user asks what no longer belongs in the finished result. Keep + current requirements and unproven cases unchanged. Do not use for ordinary + prose editing, general refactoring, or a repository-wide simplification review. +--- + +# Why Here + +Review what is about to be delivered by asking of each questionable part: +**Why is this here now?** + +Keep it when the finished result still needs it. Remove it when it exists only +because of an abandoned attempt, an earlier misunderstanding, a completed +transition, or an old implementation that no current requirement preserves. +This is not a general simplification pass and less is not automatically better. + +## Scope + +Start with the current diff, user-named files, and any text or artifact being +delivered with this change. Treat the task as review-only unless the user +explicitly asks to edit, clean up, remove, or otherwise change the artifact. +Loading or naming this skill alone does not grant permission to modify files. Do +not turn the review into repository-wide cleanup. Note an adjacent problem only +when it would materially affect this delivery. + +Preserve unrelated user work. Inspect first; do not assume that an unfamiliar +field, branch, comment, test, or document is unnecessary. + +## Process + +### 1. Establish the finished result + +Derive the required behavior, structure, contract, and documentation from the +user's request, acceptance criteria, owning documentation, current contracts, and +real consumers. Identify any public interface, stored data, external system, or +compatibility promise in scope. When the sources are missing or conflict, mark the +corresponding target as unproven instead of inferring it from the current +implementation. Candidates whose required result is unproven remain **Unproven**; +do not classify them as **Keep**, **Confine**, or **Remove**. + +**Complete when:** the required result and the boundaries that can constrain +cleanup are explicit. + +### 2. Find parts whose reason may have expired + +Inspect the scoped change for parts that appear to exist only because work took +a particular route: + +- explanations, denials, or comments that answer an earlier misunderstanding or + abandoned implementation rather than a likely question or current constraint + raised by the finished result; +- narration of attempts, reversals, or investigation instead of the conclusion + and its evidence; +- temporary fields, states, flags, branches, adapters, or documentation left by + a completed internal transition; +- tests for behavior that was intentionally removed or whose coverage has been + replaced; +- old and new representations both retained after the old one has lost its + purpose. + +Use the counterfactual only to find candidates: if the work had begun from the +finished design, would this part still have been introduced? A “no” is a reason +to investigate, not permission to delete. + +**Complete when:** every candidate has a location and a concrete reason it may +no longer belong. + +### 3. Prove what still earns a place + +For prose and comments, judge from the finished artifact. The reader is the +person who will receive, use, or maintain that artifact after this conversation, +without access to the conversation. Would removing the candidate create a +reasonable misunderstanding or change that reader's current understanding, +decision, or action? Check any source or requirement the statement claims to +preserve. + +For code, interfaces, configuration, tests, and data structures, check current +callers, clients, configuration, stored data, documented contracts, and external +constraints. History may explain why a candidate appeared, but current evidence +decides whether it stays. + +Classify the result: + +- **Keep:** current understanding or action, behavior, a real consumer, a + contract, stored data, or an external constraint still requires it. +- **Confine:** a temporary compatibility need remains, but keeping it at the + conversion, migration, or version boundary would prevent it from spreading + through the maintained model. This classification does not by itself authorize + the move. +- **Remove prose:** removing it changes no intended reader's understanding, + decision, or action, and no current requirement calls for it. +- **Remove structure:** no current behavior or obligation depends on it, and + available validation can show that removal is safe. +- **Unproven:** evidence is insufficient. Keep it unchanged and state what would + settle the question. + +Do not treat an awkward external requirement as residue merely because a clean +redesign would prefer not to have it. + +**Complete when:** no removal rests only on taste, age, or the counterfactual. + +### 4. Clean only within the proven boundary + +If the current request authorizes edits, remove prose and comments when their +absence changes no current understanding, decision, or action. Otherwise, +classify and report them without editing. Keep a reason, constraint, or +invalidation condition that a future maintainer needs and cannot recover from +the artifact itself. Treat safety, security, privacy, recovery, legal, generated, +and tool-read notices as behavior or constraints; remove them only after proving +they no longer apply and checking the system or tool that consumes them. + +If the current request authorizes edits, remove a test only when its protected +behavior was intentionally removed or its purpose is covered elsewhere. Otherwise, +classify and report it without editing. Read the assertions and confirm the +remaining coverage; absence of a runtime caller is not evidence that a test is +obsolete. + +If the current request authorizes edits, remove internal code or structure only +after consumer and contract checks support it and relevant validation is +available. A **Confine** change may be made under that authorization only when it +is internal, preserves supported behavior, and does not affect a public +interface, durable data, wire format, migration history, or a still-used +compatibility path. + +Before changing a public interface, durable data, wire format, migration history, +or still-used compatibility path, present the evidence and proposed finished +state, then obtain explicit authorization. + +**Complete when:** every authorized edit is supported by the classification +above, every review-only candidate remains unchanged, and no unrelated cleanup +entered the diff. + +### 5. Validate the result + +If edits were made, run the focused checks that can detect a changed behavior, +broken consumer, or invalid contract. Repeat relevant searches after removal and +inspect the final diff for newly orphaned code, tests, configuration, or +documentation. For a review-only run, report the evidence checks performed and +which validation would be required before an edit. + +**Complete when:** edited results are supported by concrete checks; review-only +findings name the evidence still required; and every unresolved boundary remains +unchanged and is reported. + +## Delivery + +Report the finished state, material constraints that remain, checks actually +run, and any candidate left unchanged for lack of evidence or authorization. +Do not recreate the discarded path as a removal diary unless the user explicitly +asks for an audit record. + +Finish by accounting for every reviewed candidate: a retained candidate has a +current reason to stay; an unproven candidate remains unchanged with the missing +evidence or authorization named; and a removed candidate has supporting evidence +and validation.