♻️ refactor(skills): standardize first-party skill contracts
upgrade commit-message, style-cleanup, and bulk-refactor-workflow to use trigger-focused frontmatter and a shared contract structure. add a first-party skill quality test gate and register it in tests documentation.
This commit is contained in:
parent
2c5050de09
commit
e0b1c3ab1b
|
|
@ -1,65 +1,109 @@
|
||||||
---
|
---
|
||||||
name: bulk-refactor-workflow
|
name: bulk-refactor-workflow
|
||||||
description:
|
description: Use when renaming symbols, migrating APIs, or applying safe mechanical changes across many files in the repository.
|
||||||
"Safe bulk refactors and mass edits across a repo (rename APIs, global
|
|
||||||
replacements, mechanical changes). Triggers: bulk refactor, mass edit, rename
|
|
||||||
symbol, global replace, 批量重构, 全局替换, 统一改名, 大范围修改."
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# Bulk Refactor Workflow(批量重构 / 大范围修改)
|
# Bulk Refactor Workflow(批量重构 / 大范围修改)
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Guide repository-wide mechanical edits without losing scope control or damaging
|
||||||
|
unrelated work. Core principle: enumerate first, transform mechanically, verify
|
||||||
|
in loops, and keep every batch auditable.
|
||||||
|
|
||||||
## When to Use
|
## When to Use
|
||||||
|
|
||||||
- Rename API / symbol / file convention across many files
|
- Rename APIs, symbols, file conventions, or repeated string patterns
|
||||||
- Mechanical refactors (imports, formatting, lint fixes, signature migrations)
|
- Apply scripted or structural edits across many files
|
||||||
- Cross-cutting changes touching 10+ files
|
- Migrate callers to a new interface where the change pattern is repeatable
|
||||||
|
|
||||||
## Inputs(required)
|
## When Not to Use
|
||||||
|
|
||||||
- Scope:目录/文件类型/排除项(include/exclude)
|
- One-off logic changes where no repeatable transformation exists
|
||||||
- Transformation:要做的规则(rename A→B、替换模式、接口迁移策略)
|
- Exploratory redesign or feature work that needs new architecture decisions
|
||||||
- Constraints:是否允许行为变化?是否需要兼容期?是否允许自动格式化?
|
- Cleanup that is only formatting/lint alignment; use `style-cleanup` directly
|
||||||
- Verification:必须通过哪些命令/检查(最少一个)
|
|
||||||
|
|
||||||
## Procedure(default)
|
## Inputs
|
||||||
|
|
||||||
1. **Baseline**
|
- Scope: directories, file types, include/exclude rules
|
||||||
|
- Transformation: rename, replace, codemod, scripted edit, migration strategy
|
||||||
|
- Constraints: behavior-preserving vs. compatibility window vs. staged rollout
|
||||||
|
- Verification commands: smallest useful loop plus final gate
|
||||||
|
|
||||||
- 确保工作区干净:`git status --porcelain`
|
## Procedure
|
||||||
- 跑一个基线验证(至少 build 或核心测试子集),避免“本来就坏”
|
|
||||||
|
|
||||||
2. **Enumerate**
|
1. **Baseline and bound the scope**
|
||||||
|
|
||||||
- 先搜索再改:用 `rg`/`git grep` 列出全部命中
|
- Record `git status --short`.
|
||||||
- 分类命中:真实调用 vs 注释/文档/样例;避免误改
|
- Dirty worktrees are allowed.
|
||||||
|
- Do not revert unrelated changes.
|
||||||
|
- Mark the exact target file set before editing.
|
||||||
|
- Run a baseline verification command so existing failures are not misattributed
|
||||||
|
to the refactor.
|
||||||
|
|
||||||
3. **Apply Mechanical Change**
|
2. **Enumerate every intended hit**
|
||||||
|
|
||||||
- 优先使用确定性的机械变换(脚本/结构化编辑)而非手工逐个改
|
- Search before editing with `rg`, `git grep`, or a structured query.
|
||||||
- 每轮改动后立即做小验证(编译/单测子集)
|
- Separate real code hits from comments, docs, generated files, and samples.
|
||||||
- 复杂迁移优先“两阶段”:先兼容旧接口(deprecated),再清理旧接口
|
- Write down exclusions before running the transformation.
|
||||||
|
|
||||||
4. **Format & Lint(按项目约定)**
|
3. **Choose the safest mechanical transform**
|
||||||
|
|
||||||
- 仅在确认“会破坏 diff 可读性”前提下分批格式化(避免把重构和格式揉在一起)
|
- Prefer deterministic transforms: codemods, scripts, structured edits, or
|
||||||
|
narrow search/replace with explicit scope.
|
||||||
|
- If compatibility matters, use a staged migration: adapt providers first,
|
||||||
|
migrate callers second, remove compatibility layer last.
|
||||||
|
|
||||||
5. **Verify & Report**
|
4. **Apply in bounded batches**
|
||||||
- 跑约定的验证命令并记录输出
|
|
||||||
- 汇总影响范围:改动文件数、主要改动点、潜在风险
|
|
||||||
|
|
||||||
## Execution Hint(optional)
|
- First, apply the transformation in bounded batches.
|
||||||
|
- Verify each batch before widening scope.
|
||||||
|
- Stop immediately if output differs from the expected hit class.
|
||||||
|
|
||||||
如果环境支持“执行型批量处理”(例如脚本执行),优先用脚本完成批量修改,然后只把**最小 diff + 摘要**交付,避免上下文膨胀与漏改。
|
5. **Verify in loops**
|
||||||
|
|
||||||
## Output Contract(stable)
|
- Run the smallest relevant verification after each batch.
|
||||||
|
- Re-check the hit list after edits to confirm the intended pattern is gone and
|
||||||
|
unintended patterns were not introduced.
|
||||||
|
|
||||||
- Scope:改动覆盖范围(文件/目录/语言)
|
6. **Finish with targeted cleanup**
|
||||||
- Transformation:执行的规则(可复用)
|
|
||||||
- Changes:关键改动摘要(按类别)
|
- If the mechanical change leaves formatting or lint fallout, Use `style-cleanup` for the final formatting/lint pass.
|
||||||
- Verification:命令 + 证据(输出/退出码/产物)
|
- Keep that cleanup scoped to the refactor set unless the user approves a wider
|
||||||
- Risks:高风险点与回滚建议
|
pass.
|
||||||
|
|
||||||
|
7. **Report**
|
||||||
|
|
||||||
|
- Summarize files touched, transform rules used, verification evidence, and any
|
||||||
|
residual risk.
|
||||||
|
|
||||||
|
## Output Contract
|
||||||
|
|
||||||
|
- `Scope:` files, directories, and exclusions actually used
|
||||||
|
- `Transformation:` exact rule or script applied
|
||||||
|
- `Batches:` how the work was partitioned
|
||||||
|
- `Verification:` commands, exit status, and what each loop proved
|
||||||
|
- `Risks:` remaining ambiguous areas, compatibility debt, or deferred cleanup
|
||||||
|
|
||||||
|
## Success Criteria
|
||||||
|
|
||||||
|
- Every changed file belongs to the declared scope
|
||||||
|
- The transformation is reproducible and explainable
|
||||||
|
- Verification passes at both batch level and final gate
|
||||||
|
- Unrelated work in the repository is preserved
|
||||||
|
|
||||||
|
## Failure Handling
|
||||||
|
|
||||||
|
- If the hit list is ambiguous, stop and refine scope before editing
|
||||||
|
- If the transform cannot be expressed mechanically, downgrade to a normal
|
||||||
|
refactor plan instead of pretending it is safe bulk work
|
||||||
|
- If verification fails mid-batch, stop, inspect the smallest failing delta, and
|
||||||
|
correct the transform before continuing
|
||||||
|
- If compatibility risk is higher than expected, split the migration into staged
|
||||||
|
commits or phases
|
||||||
|
|
||||||
## Guardrails
|
## Guardrails
|
||||||
|
|
||||||
- 任何“全局替换”都必须先给出命中清单与排除策略
|
- Never run repository-wide replacement without an explicit hit review
|
||||||
- 避免把行为重构与格式化/无关清理混在同一轮
|
- Never mix unrelated cleanup into the same batch
|
||||||
|
- Never hide a large-scope behavior change behind the label "mechanical refactor"
|
||||||
|
|
|
||||||
|
|
@ -1,61 +1,98 @@
|
||||||
---
|
---
|
||||||
name: commit-message
|
name: commit-message
|
||||||
description:
|
description: Use when the user asks for help writing a commit message, wants emoji/type(scope): subject formatting, or asks to review staged changes before committing.
|
||||||
"基于 staged diff 生成符合 commit_message.md 的提交信息建议(:emoji:
|
|
||||||
type(scope): subject)。Triggers: commit message, 提交信息, 写提交说明,
|
|
||||||
生成提交信息, emoji commit, git commit."
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# Commit Message(提交信息建议器)
|
# Commit Message(提交信息建议器)
|
||||||
|
|
||||||
目标:基于 `git diff --cached`(staged
|
## Overview
|
||||||
diff)生成 1–3 条提交信息建议:`:emoji: type(scope): subject`(可选 body/footer)。
|
|
||||||
|
|
||||||
权威规范(单一真源):
|
Turn a staged diff into a commit recommendation that matches repository policy.
|
||||||
|
Core principle: understand staged intent and commit boundaries before drafting the
|
||||||
|
message.
|
||||||
|
|
||||||
|
Authority source:
|
||||||
|
|
||||||
- `docs/common/commit_message.md`
|
- `docs/common/commit_message.md`
|
||||||
|
- `docs/standards/playbook/docs/common/commit_message.md` (vendored playbook)
|
||||||
|
|
||||||
## When to use
|
## When to Use
|
||||||
|
|
||||||
- 用户要“写提交信息 / 生成 commit message / 按规范写提交说明 / emoji commit”
|
- The user asks to write or improve a commit message
|
||||||
- 已经 `git add` 了一批改动,准备 `git commit`
|
- The user wants emoji/type(scope): subject formatting
|
||||||
|
- The user asks whether current staged changes should be split before commit
|
||||||
|
- The user asks to review staged changes before running `git commit`
|
||||||
|
|
||||||
## Procedure(default)
|
## When Not to Use
|
||||||
|
|
||||||
1. **收集 staged 概览(尽量小上下文)**
|
- No staged changes exist and the user expects a final commit message based on the
|
||||||
|
actual staged diff
|
||||||
|
- The task is writing a PR title, release note, or changelog instead of a git
|
||||||
|
commit message
|
||||||
|
- The user already provided an exact commit message and only wants it executed
|
||||||
|
|
||||||
- `git diff --cached --name-status`
|
## Inputs
|
||||||
- `git diff --cached --stat`
|
|
||||||
- 必要时只看关键文件:`git diff --cached -- <path>`
|
|
||||||
|
|
||||||
2. **读取并遵循权威规范**
|
- Current staging state (`git status --short`, `git diff --cached`)
|
||||||
|
- Nearest applicable `commit_message.md`
|
||||||
|
- Whether the user wants suggestions only or a command-ready final message
|
||||||
|
|
||||||
- 优先读取就近的
|
## Procedure
|
||||||
`commit_message.md`(见上方路径),以其中的 type/emoji/格式为准。
|
|
||||||
|
|
||||||
3. **生成 1 条主建议 + 2 条备选**
|
1. **Baseline the repo state**
|
||||||
|
|
||||||
- 格式固定:`:emoji: type(scope): subject`(scope 可省略)。
|
- Inspect staged and unstaged state separately.
|
||||||
- subject 用一句话描述“做了什么”,避免含糊词;尽量 ≤ 72 字符,不加句号。
|
- If nothing is staged, stop. Explain that a staged-diff-based recommendation
|
||||||
|
is unavailable and ask whether files should be staged first.
|
||||||
|
- If only unstaged changes exist, stop before proposing a final message. Offer
|
||||||
|
a draft only if the user explicitly wants one.
|
||||||
|
|
||||||
4. **判断是否建议拆分提交**
|
2. **Load the authority spec**
|
||||||
|
|
||||||
- 当 staged 同时包含多个不相关模块/目的时:建议拆分,并给出拆分方式(按目录/功能点/风险)。
|
- Prefer the nearest `commit_message.md`.
|
||||||
|
- Reuse its type, emoji, scope, and body/footer conventions exactly.
|
||||||
|
|
||||||
5. **可选:补充 body/footer(如需要)**
|
3. **Summarize staged intent**
|
||||||
|
|
||||||
- body:说明 why/impact/verify(按规范建议换行)。
|
- Read the staged diff at the smallest useful granularity.
|
||||||
- footer:任务号或 `BREAKING CHANGE:`(若有)。
|
- Identify the main logical change, affected areas, and whether the staged set
|
||||||
|
mixes unrelated work.
|
||||||
|
- If staged changes combine unrelated goals, strongly recommend splitting the commit
|
||||||
|
before drafting a single message. In other words, strongly recommend splitting
|
||||||
|
the commit instead of hiding multiple intents behind one subject.
|
||||||
|
|
||||||
6. **只给建议,不默认执行 `git commit`**
|
4. **Draft message options**
|
||||||
- 仅当用户明确要求时,才根据选定方案生成最终提交信息。
|
|
||||||
|
|
||||||
## Output contract(固定输出)
|
- Produce one recommended option and up to two alternatives.
|
||||||
|
- Keep the subject concrete, specific, and under the repo limit.
|
||||||
|
- Use body/footer only when they add real context such as motivation, impact,
|
||||||
|
verification, issue links, or breaking-change notes.
|
||||||
|
|
||||||
- Detected: staged files summary(文件数 + 关键路径 + 是否可能需要拆分)
|
5. **Finalize safely**
|
||||||
- Proposed:
|
|
||||||
- Option A(recommended):`:emoji: type(scope): subject`
|
- Make it explicit whether the output is a suggestion or a final chosen message.
|
||||||
- Option B:...
|
- Do not run `git commit` unless the user explicitly asks for execution after
|
||||||
- Option C:...
|
reviewing the recommendation.
|
||||||
- Optional body/footer(如适用)
|
|
||||||
- Notes:规范路径命中情况(哪个 `commit_message.md` 被使用)
|
## Output Contract
|
||||||
|
|
||||||
|
- `Detected:` staged files summary, dominant intent, and split/no-split judgment
|
||||||
|
- `Spec:` which `commit_message.md` was used
|
||||||
|
- `Proposed:` Option A (recommended), Option B, Option C
|
||||||
|
- `Optional body/footer:` only when justified
|
||||||
|
- `Notes:` risks, ambiguity, or split advice
|
||||||
|
|
||||||
|
## Success Criteria
|
||||||
|
|
||||||
|
- The recommendation matches the staged diff rather than guessed intent
|
||||||
|
- The selected format complies with the nearest commit policy
|
||||||
|
- Split advice appears when staged changes are logically mixed
|
||||||
|
- No commit command is executed unless the user asked for it
|
||||||
|
|
||||||
|
## Failure Handling
|
||||||
|
|
||||||
|
- If no staged diff exists, stop and explain the limitation instead of inventing a
|
||||||
|
final message
|
||||||
|
- If the staged set is too broad or ambiguous, recommend splitting and explain why
|
||||||
|
- If no local policy file is found, state the fallback convention before drafting
|
||||||
|
- If repo state changes during review, rerun the staged summary before finalizing
|
||||||
|
|
|
||||||
|
|
@ -1,94 +1,107 @@
|
||||||
---
|
---
|
||||||
name: style-cleanup
|
name: style-cleanup
|
||||||
description:
|
description: Use when the user asks to format code, fix lint issues, or align style with the repository's existing toolchain without changing behavior.
|
||||||
"Clean up formatting and code style with the repo’s existing toolchain
|
|
||||||
(clang-format/black/isort/flake8/pre-commit/etc). Triggers: 整理代码风格,
|
|
||||||
格式化, format, fmt, lint fix, clang-format, black, isort."
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# Style Cleanup Workflow(整理代码风格 / 格式化)
|
# Style Cleanup Workflow(整理代码风格 / 格式化)
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Apply the repository's existing formatter and lint toolchain without changing
|
||||||
|
behavior. Core principle: trust repo truth, keep scope tight, and verify with a
|
||||||
|
rerun.
|
||||||
|
|
||||||
## When to Use
|
## When to Use
|
||||||
|
|
||||||
- “整理代码风格 / 格式化 / format / fmt / lint fix”
|
- The user asks for formatting, `fmt`, `format`, or lint cleanup
|
||||||
- 合并前做一次风格对齐(不做语义级重构)
|
- A code change is done and needs a final style pass before review or commit
|
||||||
- 批量改动后,希望把格式化与机械性风格问题收敛到可控 diff
|
- A mechanical change produced noisy diff that should be normalized by the repo's
|
||||||
|
existing toolchain
|
||||||
|
|
||||||
## Inputs(required)
|
## When Not to Use
|
||||||
|
|
||||||
- Scope:仅本次改动文件(默认)|全仓库|指定目录/文件类型
|
- This is not for semantic refactors or behavior changes
|
||||||
- Languages:自动识别;如为多语言仓库请确认优先级
|
- This is not for introducing a new formatter or lint configuration
|
||||||
- Verification:至少一个可执行的验证命令(如未知,先问/再推断)
|
- This is not for full-repo reformatting unless the user explicitly requests that
|
||||||
|
scope
|
||||||
|
|
||||||
## Procedure(default)
|
## Inputs
|
||||||
|
|
||||||
1. **Baseline**
|
- Target scope: staged files, unstaged files, explicit file list, or explicit
|
||||||
|
directory
|
||||||
|
- Relevant languages in scope
|
||||||
|
- Verification commands available in the repository
|
||||||
|
|
||||||
- 记录当前状态:`git status --porcelain`
|
## Procedure
|
||||||
- 明确范围(默认只处理变更文件):
|
|
||||||
- staged:`git diff --name-only --cached`
|
|
||||||
- unstaged:`git diff --name-only`
|
|
||||||
- untracked:`git ls-files -o --exclude-standard`
|
|
||||||
|
|
||||||
2. **Detect Toolchain(prefer repo truth)**
|
1. **Baseline the scope**
|
||||||
|
|
||||||
- 优先用仓库既有入口脚本 / 配置:
|
- Record `git status --short`.
|
||||||
- JS/TS:`package.json`
|
- Default to changed files only unless the user requested broader scope.
|
||||||
scripts(`format`/`lint`/`lint:fix`)、prettier/biome/eslint 配置
|
- Resolve the target file set before running any formatter.
|
||||||
- Python:`pyproject.toml` / `.flake8` / `.pylintrc` /
|
|
||||||
`.pre-commit-config.yaml`
|
|
||||||
- C/C++:`.clang-format`(唯一真相),可选 `.clang-tidy`
|
|
||||||
- Shell:`shfmt`/`shellcheck`(若仓库已使用)
|
|
||||||
- Markdown:prettier/markdownlint(仅在仓库已固定时使用)
|
|
||||||
- 禁止默认“引入新 formatter/linter 配置”;缺配置时只做最小手工调整,并先确认是否允许落地配置文件。
|
|
||||||
|
|
||||||
3. **Apply(format first, then lint)**
|
2. **Detect the repo toolchain**
|
||||||
|
|
||||||
- 先 formatter(会改文件),再 lint(检查),再 lint
|
- Prefer repository scripts and checked-in config over ad hoc commands.
|
||||||
--fix(如有),最后再跑一次 check 确认干净。
|
- Use the repo's existing formatter/linter stack:
|
||||||
- 默认只处理目标文件集合;避免全仓库 reformat(除非用户明确要求)。
|
- JS/TS: `package.json`, prettier, biome, eslint
|
||||||
- 典型命令(按仓库实际替换):
|
- Python: `pyproject.toml`, `isort`, `black`, `ruff`, `flake8`,
|
||||||
- C++:`clang-format -i <files...>`;CI 校验:`clang-format --dry-run --Werror <files...>`
|
`pre-commit`
|
||||||
- Python:`black <files...>` + `isort <files...>`;或
|
- C/C++: `.clang-format`, optional `.clang-tidy`
|
||||||
`pre-commit run --files <files...>`
|
- Shell: `shfmt`, `shellcheck` if already present
|
||||||
- JS/TS:`npm run format -- <files...>` / `pnpm ...` /
|
- Markdown: prettier/markdownlint only if the repo already uses them
|
||||||
`npx prettier -w <files...>`(以项目脚本为准)
|
|
||||||
|
|
||||||
4. **Guardrails**
|
3. **Run cleanup in a fixed loop**
|
||||||
|
|
||||||
- 只做风格与格式:不改变行为、不改 public API、不做重构。
|
- Use `formatter -> lint/check -> lint --fix -> final check`.
|
||||||
- 如格式化导致 diff 暴涨(文件数/行数过大):先停下,给出原因与两种方案让用户选:
|
- Prefer repository entrypoints over raw tool invocations.
|
||||||
1. 仅格式化本次改动文件(推荐默认)
|
- Keep execution scoped to the chosen files whenever the toolchain supports it.
|
||||||
2. 全仓库统一格式(通常需要单独 PR/提交)
|
|
||||||
|
4. **Control blast radius**
|
||||||
|
|
||||||
|
- If the formatter expands the diff far beyond the requested scope, stop and
|
||||||
|
explain the tradeoff.
|
||||||
|
- Offer a scoped pass vs. a full-repo normalization pass instead of silently
|
||||||
|
widening scope.
|
||||||
|
|
||||||
5. **Verify**
|
5. **Verify**
|
||||||
- 跑最小验证命令(仓库已有命令优先)。
|
|
||||||
- 若无法运行(缺环境/缺权限/缺依赖):说明原因,并给出替代验证(例如 formatter 二次运行无 diff、lint 输出为 0)。
|
|
||||||
|
|
||||||
## Playbook as Authority(如果项目 vendoring 了本 Playbook)
|
- Re-run the lightweight checks after fixes.
|
||||||
|
- Confirm that the second formatter run produces no additional diff.
|
||||||
|
- If the repo has a canonical lint or test command for style verification, run
|
||||||
|
that command and report the result.
|
||||||
|
|
||||||
当目标仓库包含 `docs/standards/playbook/docs/`(或直接包含
|
## Playbook as Authority
|
||||||
`docs/tsl|cpp|python/...`),风格决策参考:
|
|
||||||
|
|
||||||
- TSL:`docs/tsl/code_style.md`、`docs/tsl/naming.md`、`docs/tsl/toolchain.md`
|
When the target repo vendors this playbook, prefer these references for style
|
||||||
- C++:`docs/cpp/code_style.md`、`docs/cpp/naming.md`、`docs/cpp/toolchain.md`
|
judgment:
|
||||||
- Python:`docs/python/style_guide.md`、`docs/python/tooling.md`、`docs/python/configuration.md`
|
|
||||||
|
|
||||||
## Output Contract(stable)
|
- TSL: `docs/tsl/code_style.md`, `docs/tsl/naming.md`, `docs/tsl/toolchain.md`
|
||||||
|
- C++: `docs/cpp/code_style.md`, `docs/cpp/naming.md`, `docs/cpp/toolchain.md`
|
||||||
|
- Python: `docs/python/style_guide.md`, `docs/python/tooling.md`,
|
||||||
|
`docs/python/configuration.md`
|
||||||
|
|
||||||
- Scope:实际处理范围(文件/目录/语言)
|
## Output Contract
|
||||||
- Toolchain:使用了哪些工具与配置依据
|
|
||||||
- Commands:实际执行命令(按顺序)
|
- `Scope:` actual files/directories/languages processed
|
||||||
- Changes:修改文件列表 + 改动规模概览
|
- `Toolchain:` repo configs and commands used
|
||||||
- Remaining:仍未修复的问题(分类:formatter / lint / style)+ 下一步建议
|
- `Commands:` execution order
|
||||||
|
- `Changes:` modified files plus diff-size summary
|
||||||
|
- `Remaining:` unresolved lint/style issues plus why they remain
|
||||||
|
|
||||||
## Success Criteria
|
## Success Criteria
|
||||||
|
|
||||||
- formatter 二次运行无新增 diff
|
- Style cleanup stays within the requested or agreed scope
|
||||||
- lint/检查命令通过(或仅剩已确认的例外)
|
- The second formatter run produces no additional diff
|
||||||
- 未引入语义变更(仅格式/风格)
|
- Verification commands pass, or any remaining exception is explicitly explained
|
||||||
|
- No semantic behavior change is introduced
|
||||||
|
|
||||||
## Failure Handling
|
## Failure Handling
|
||||||
|
|
||||||
- 工具缺失:优先提示安装方式或替代命令;无法解决则退回“最小手工风格修复 + 明确未覆盖项”
|
- If the repo lacks a formatter/linter, say so and fall back to minimal manual
|
||||||
- 规则冲突(如 black vs flake8):以仓库配置为准;必要时调整例外配置但需先确认
|
cleanup only with clear limits
|
||||||
|
- If tools are missing locally, report the missing dependency and stop before
|
||||||
|
inventing a new toolchain
|
||||||
|
- If two configured tools conflict, follow checked-in repo config and surface the
|
||||||
|
conflict instead of papering over it
|
||||||
|
- If cleanup would require wider scope than requested, stop and ask for approval
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ tests/
|
||||||
├── cli/ # Python CLI 测试(unittest)
|
├── cli/ # Python CLI 测试(unittest)
|
||||||
│ └── test_playbook_cli.py # playbook.py 基础功能测试
|
│ └── test_playbook_cli.py # playbook.py 基础功能测试
|
||||||
├── test_format_md_action.py # format_md 动作测试
|
├── test_format_md_action.py # format_md 动作测试
|
||||||
|
├── test_firstparty_skills_quality.py # first-party skills 元数据与结构质量测试
|
||||||
├── test_gitattributes_modes.py # gitattr_mode 行为测试
|
├── test_gitattributes_modes.py # gitattr_mode 行为测试
|
||||||
├── test_no_backup_flags.py # no_backup 行为测试
|
├── test_no_backup_flags.py # no_backup 行为测试
|
||||||
├── test_sync_directory_actions.py # sync_memory_bank/sync_prompts 行为测试
|
├── test_sync_directory_actions.py # sync_memory_bank/sync_prompts 行为测试
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,104 @@
|
||||||
|
import re
|
||||||
|
import unittest
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
|
SKILLS_ROOT = ROOT / "skills"
|
||||||
|
FIRST_PARTY_SKILLS = {
|
||||||
|
"commit-message": SKILLS_ROOT / "commit-message" / "SKILL.md",
|
||||||
|
"style-cleanup": SKILLS_ROOT / "style-cleanup" / "SKILL.md",
|
||||||
|
"bulk-refactor-workflow": SKILLS_ROOT / "bulk-refactor-workflow" / "SKILL.md",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def read_text(path: Path) -> str:
|
||||||
|
return path.read_text(encoding="utf-8")
|
||||||
|
|
||||||
|
|
||||||
|
def normalize_space(text: str) -> str:
|
||||||
|
return " ".join(text.split())
|
||||||
|
|
||||||
|
|
||||||
|
def parse_frontmatter(text: str) -> dict[str, str]:
|
||||||
|
match = re.match(r"^---\n(.*?)\n---\n", text, re.DOTALL)
|
||||||
|
if match is None:
|
||||||
|
raise AssertionError("missing YAML frontmatter")
|
||||||
|
block = match.group(1)
|
||||||
|
data: dict[str, str] = {}
|
||||||
|
current_key: str | None = None
|
||||||
|
current_value: list[str] = []
|
||||||
|
for raw_line in block.splitlines():
|
||||||
|
if raw_line.startswith(" ") and current_key is not None:
|
||||||
|
current_value.append(raw_line.strip())
|
||||||
|
continue
|
||||||
|
if current_key is not None:
|
||||||
|
data[current_key] = " ".join(current_value).strip().strip('"')
|
||||||
|
current_key = None
|
||||||
|
current_value = []
|
||||||
|
key, value = raw_line.split(":", 1)
|
||||||
|
current_key = key.strip()
|
||||||
|
current_value = [value.strip()]
|
||||||
|
if current_key is not None:
|
||||||
|
data[current_key] = " ".join(current_value).strip().strip('"')
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
class FirstPartySkillsQualityTests(unittest.TestCase):
|
||||||
|
def test_first_party_skill_frontmatter_is_minimal_and_named_consistently(self):
|
||||||
|
for name, path in FIRST_PARTY_SKILLS.items():
|
||||||
|
with self.subTest(skill=name):
|
||||||
|
frontmatter = parse_frontmatter(read_text(path))
|
||||||
|
self.assertEqual(set(frontmatter), {"name", "description"})
|
||||||
|
self.assertEqual(frontmatter["name"], name)
|
||||||
|
self.assertRegex(frontmatter["name"], r"^[a-z0-9-]+$")
|
||||||
|
|
||||||
|
def test_first_party_skill_descriptions_are_trigger_focused(self):
|
||||||
|
for name, path in FIRST_PARTY_SKILLS.items():
|
||||||
|
with self.subTest(skill=name):
|
||||||
|
description = parse_frontmatter(read_text(path))["description"]
|
||||||
|
self.assertTrue(description.startswith("Use when"))
|
||||||
|
self.assertLessEqual(len(description), 500)
|
||||||
|
self.assertNotIn("Triggers:", description)
|
||||||
|
|
||||||
|
def test_first_party_skills_have_required_sections(self):
|
||||||
|
required_sections = (
|
||||||
|
"## Overview",
|
||||||
|
"## When to Use",
|
||||||
|
"## When Not to Use",
|
||||||
|
"## Inputs",
|
||||||
|
"## Procedure",
|
||||||
|
"## Output Contract",
|
||||||
|
"## Success Criteria",
|
||||||
|
"## Failure Handling",
|
||||||
|
)
|
||||||
|
for name, path in FIRST_PARTY_SKILLS.items():
|
||||||
|
text = read_text(path)
|
||||||
|
with self.subTest(skill=name):
|
||||||
|
for section in required_sections:
|
||||||
|
self.assertIn(section, text)
|
||||||
|
|
||||||
|
def test_commit_message_skill_handles_missing_or_mixed_staging_states(self):
|
||||||
|
text = normalize_space(read_text(FIRST_PARTY_SKILLS["commit-message"]))
|
||||||
|
self.assertIn("If nothing is staged", text)
|
||||||
|
self.assertIn("If only unstaged changes exist", text)
|
||||||
|
self.assertIn("strongly recommend splitting the commit", text)
|
||||||
|
self.assertIn("Do not run `git commit`", text)
|
||||||
|
|
||||||
|
def test_style_cleanup_skill_has_clear_non_goals_and_verification_loop(self):
|
||||||
|
text = normalize_space(read_text(FIRST_PARTY_SKILLS["style-cleanup"]))
|
||||||
|
self.assertIn("not for semantic refactors", text)
|
||||||
|
self.assertIn("not for introducing a new formatter or lint configuration", text)
|
||||||
|
self.assertIn("formatter -> lint/check -> lint --fix -> final check", text)
|
||||||
|
self.assertIn("second formatter run produces no additional diff", text)
|
||||||
|
|
||||||
|
def test_bulk_refactor_skill_is_dirty_aware_and_delegates_final_cleanup(self):
|
||||||
|
text = normalize_space(read_text(FIRST_PARTY_SKILLS["bulk-refactor-workflow"]))
|
||||||
|
self.assertIn("Dirty worktrees are allowed", text)
|
||||||
|
self.assertIn("Do not revert unrelated changes", text)
|
||||||
|
self.assertIn("Use `style-cleanup` for the final formatting/lint pass", text)
|
||||||
|
self.assertIn("apply the transformation in bounded batches", text)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
Loading…
Reference in New Issue