bmad-module-skill-forge 2.0.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.nvmrc +1 -1
- package/docs/_data/pinned.yaml +1 -1
- package/package.json +2 -2
- package/src/knowledge/skill-lifecycle.md +1 -1
- package/src/module-help.csv +2 -2
- package/src/shared/scripts/skf-comention-pairs.py +350 -0
- package/src/shared/scripts/skf-count-tokens.py +222 -0
- package/src/shared/scripts/skf-detect-docs.py +232 -1
- package/src/shared/scripts/skf-detect-language.py +77 -0
- package/src/shared/scripts/skf-detect-tools.py +105 -2
- package/src/shared/scripts/skf-enumerate-stack-skills.py +111 -2
- package/src/shared/scripts/skf-find-cycles.py +246 -0
- package/src/shared/scripts/skf-hash-content.py +487 -2
- package/src/shared/scripts/skf-manifest-ops.py +67 -1
- package/src/shared/scripts/skf-rebuild-managed-sections.py +180 -0
- package/src/shared/scripts/skf-recommend-scope-type.py +1 -2
- package/src/shared/scripts/skf-render-metadata-stats.py +499 -0
- package/src/shared/scripts/skf-rewrite-skill-name.py +308 -0
- package/src/shared/scripts/skf-shard-body.py +461 -0
- package/src/shared/scripts/skf-skill-inventory.py +139 -3
- package/src/shared/scripts/skf-structural-diff.py +307 -67
- package/src/shared/scripts/skf-validate-brief-schema.py +39 -1
- package/src/shared/scripts/skf-validate-feasibility-report.py +234 -0
- package/src/shared/scripts/skf-validate-frontmatter.py +17 -12
- package/src/shared/scripts/skf-validate-output.py +443 -10
- package/src/shared/scripts/skf-validate-pins.py +1 -1
- package/src/shared/scripts/skf-verify-no-trace.py +214 -0
- package/src/shared/scripts/skf-verify-provenance-completeness.py +439 -0
- package/src/skf-analyze-source/SKILL.md +17 -32
- package/src/skf-analyze-source/customize.toml +2 -0
- package/src/skf-analyze-source/references/auto-docs-only.md +116 -0
- package/src/skf-analyze-source/references/continue.md +8 -13
- package/src/skf-analyze-source/references/generate-briefs.md +28 -12
- package/src/skf-analyze-source/references/headless-contract.md +33 -0
- package/src/skf-analyze-source/references/health-check.md +1 -2
- package/src/skf-analyze-source/references/identify-units.md +25 -36
- package/src/skf-analyze-source/references/init.md +12 -25
- package/src/skf-analyze-source/references/map-and-detect.md +8 -20
- package/src/skf-analyze-source/references/recommend.md +4 -14
- package/src/skf-analyze-source/references/scan-project.md +3 -11
- package/src/skf-analyze-source/references/step-auto-scope.md +42 -180
- package/src/skf-analyze-source/references/unit-detection-heuristics.md +2 -2
- package/src/skf-audit-skill/SKILL.md +9 -6
- package/src/skf-audit-skill/customize.toml +2 -0
- package/src/skf-audit-skill/references/init.md +58 -32
- package/src/skf-audit-skill/references/re-index.md +15 -34
- package/src/skf-audit-skill/references/report.md +11 -58
- package/src/skf-audit-skill/references/semantic-diff.md +4 -22
- package/src/skf-audit-skill/references/severity-classify.md +53 -87
- package/src/skf-audit-skill/references/step-doc-drift.md +49 -19
- package/src/skf-audit-skill/references/structural-diff.md +43 -68
- package/src/skf-brief-skill/SKILL.md +7 -36
- package/src/skf-brief-skill/assets/scope-templates.md +1 -1
- package/src/skf-brief-skill/customize.toml +14 -0
- package/src/skf-brief-skill/references/analyze-target.md +6 -14
- package/src/skf-brief-skill/references/confirm-brief.md +2 -14
- package/src/skf-brief-skill/references/draft-checkpoint.md +8 -8
- package/src/skf-brief-skill/references/gather-intent.md +19 -22
- package/src/skf-brief-skill/references/headless-args.md +1 -1
- package/src/skf-brief-skill/references/health-check.md +1 -22
- package/src/skf-brief-skill/references/invocation-contract.md +37 -0
- package/src/skf-brief-skill/references/scope-definition.md +4 -14
- package/src/skf-brief-skill/references/step-auto-brief.md +1 -5
- package/src/skf-brief-skill/references/step-auto-validate.md +1 -5
- package/src/skf-brief-skill/references/version-resolution.md +1 -1
- package/src/skf-brief-skill/references/write-brief.md +17 -10
- package/src/skf-campaign/SKILL.md +15 -84
- package/src/skf-campaign/customize.toml +2 -0
- package/src/skf-campaign/manifest.yaml +4 -0
- package/src/skf-campaign/references/campaign-contracts.md +67 -0
- package/src/skf-campaign/references/campaign-directive-spec.md +14 -103
- package/src/skf-campaign/references/step-01-setup.md +4 -5
- package/src/skf-campaign/references/step-02-strategy.md +8 -3
- package/src/skf-campaign/references/step-03-pins.md +1 -1
- package/src/skf-campaign/references/step-04-provenance.md +1 -1
- package/src/skf-campaign/references/step-05-skill-loop.md +1 -1
- package/src/skf-campaign/references/step-06-batch.md +7 -6
- package/src/skf-campaign/references/step-11-maintenance.md +2 -2
- package/src/skf-campaign/references/step-resume.md +9 -8
- package/src/skf-campaign/scripts/campaign-deps.py +10 -1
- package/src/skf-campaign/scripts/campaign-render-batch.py +199 -0
- package/src/skf-campaign/scripts/campaign-status.py +162 -0
- package/src/skf-campaign/templates/campaign-brief-template.yaml +1 -1
- package/src/skf-create-skill/SKILL.md +14 -3
- package/src/skf-create-skill/assets/compile-assembly-rules.md +17 -38
- package/src/skf-create-skill/assets/skill-sections.md +9 -100
- package/src/skf-create-skill/assets/tessl-dismissal-rules.md +4 -13
- package/src/skf-create-skill/customize.toml +13 -0
- package/src/skf-create-skill/references/authoritative-files-protocol.md +7 -7
- package/src/skf-create-skill/references/compile.md +34 -47
- package/src/skf-create-skill/references/component-extraction.md +8 -35
- package/src/skf-create-skill/references/ecosystem-check.md +9 -25
- package/src/skf-create-skill/references/enrich.md +4 -17
- package/src/skf-create-skill/references/extract.md +14 -29
- package/src/skf-create-skill/references/extraction-patterns-tracing.md +1 -1
- package/src/skf-create-skill/references/extraction-patterns.md +7 -7
- package/src/skf-create-skill/references/generate-artifacts.md +9 -23
- package/src/skf-create-skill/references/health-check.md +1 -1
- package/src/skf-create-skill/references/load-brief.md +16 -18
- package/src/skf-create-skill/references/report.md +12 -15
- package/src/skf-create-skill/references/source-resolution-protocols.md +3 -3
- package/src/skf-create-skill/references/step-auto-shard.md +52 -25
- package/src/skf-create-skill/references/step-doc-rot.md +30 -12
- package/src/skf-create-skill/references/step-doc-sources.md +2 -2
- package/src/skf-create-skill/references/sub/ccc-discover.md +2 -13
- package/src/skf-create-skill/references/sub/fetch-docs.md +8 -50
- package/src/skf-create-skill/references/sub/fetch-temporal.md +5 -20
- package/src/skf-create-skill/references/tier-degradation-rules.md +6 -8
- package/src/skf-create-skill/references/validate.md +59 -28
- package/src/skf-create-skill/scripts/scan-doc-rot.py +245 -0
- package/src/skf-create-stack-skill/SKILL.md +15 -9
- package/src/skf-create-stack-skill/assets/provenance-map-schema.md +1 -1
- package/src/skf-create-stack-skill/customize.toml +10 -0
- package/src/skf-create-stack-skill/references/compile-stack.md +7 -8
- package/src/skf-create-stack-skill/references/compose-mode-rules.md +2 -13
- package/src/skf-create-stack-skill/references/detect-integrations.md +49 -54
- package/src/skf-create-stack-skill/references/detect-manifests.md +37 -30
- package/src/skf-create-stack-skill/references/generate-output.md +24 -75
- package/src/skf-create-stack-skill/references/init.md +9 -81
- package/src/skf-create-stack-skill/references/manifest-patterns.md +5 -35
- package/src/skf-create-stack-skill/references/parallel-extract.md +12 -27
- package/src/skf-create-stack-skill/references/rank-and-confirm.md +6 -6
- package/src/skf-create-stack-skill/references/report.md +27 -62
- package/src/skf-create-stack-skill/references/validate.md +35 -31
- package/src/skf-drop-skill/SKILL.md +20 -23
- package/src/skf-drop-skill/customize.toml +10 -0
- package/src/skf-drop-skill/references/execute.md +51 -31
- package/src/skf-drop-skill/references/headless-contract.md +18 -0
- package/src/skf-drop-skill/references/health-check.md +1 -13
- package/src/skf-drop-skill/references/report.md +42 -10
- package/src/skf-drop-skill/references/select.md +88 -48
- package/src/skf-drop-skill/scripts/dir-sizes.py +116 -0
- package/src/skf-export-skill/SKILL.md +17 -14
- package/src/skf-export-skill/assets/managed-section-format.md +2 -5
- package/src/skf-export-skill/customize.toml +10 -6
- package/src/skf-export-skill/references/generate-snippet.md +21 -35
- package/src/skf-export-skill/references/load-skill.md +44 -42
- package/src/skf-export-skill/references/multi-skill-mode.md +1 -1
- package/src/skf-export-skill/references/orphan-context-detection.md +11 -3
- package/src/skf-export-skill/references/package.md +27 -33
- package/src/skf-export-skill/references/preflight-snippet-root-probe.md +24 -5
- package/src/skf-export-skill/references/result-envelope.md +28 -0
- package/src/skf-export-skill/references/summary.md +11 -5
- package/src/skf-export-skill/references/token-report.md +42 -24
- package/src/skf-export-skill/references/update-context.md +32 -33
- package/src/skf-forger/SKILL.md +27 -39
- package/src/skf-forger/references/pipeline-mode.md +34 -0
- package/src/skf-forger/scripts/parse-pipeline.py +270 -0
- package/src/skf-quick-skill/SKILL.md +10 -60
- package/src/skf-quick-skill/customize.toml +9 -0
- package/src/skf-quick-skill/references/batch-mode.md +6 -4
- package/src/skf-quick-skill/references/compile.md +15 -52
- package/src/skf-quick-skill/references/ecosystem-check.md +5 -20
- package/src/skf-quick-skill/references/finalize.md +12 -6
- package/src/skf-quick-skill/references/halt-contract.md +54 -0
- package/src/skf-quick-skill/references/health-check.md +3 -4
- package/src/skf-quick-skill/references/quick-extract.md +7 -18
- package/src/skf-quick-skill/references/registry-resolution.md +1 -23
- package/src/skf-quick-skill/references/resolve-target.md +27 -34
- package/src/skf-quick-skill/references/write-and-validate.md +10 -25
- package/src/skf-refine-architecture/SKILL.md +14 -7
- package/src/skf-refine-architecture/customize.toml +9 -0
- package/src/skf-refine-architecture/references/compile.md +9 -7
- package/src/skf-refine-architecture/references/gap-analysis.md +21 -50
- package/src/skf-refine-architecture/references/health-check.md +1 -1
- package/src/skf-refine-architecture/references/improvements.md +9 -25
- package/src/skf-refine-architecture/references/init.md +9 -16
- package/src/skf-refine-architecture/references/issue-detection.md +10 -25
- package/src/skf-refine-architecture/references/refinement-rules.md +4 -16
- package/src/skf-refine-architecture/references/report.md +13 -10
- package/src/skf-rename-skill/SKILL.md +11 -21
- package/src/skf-rename-skill/customize.toml +10 -0
- package/src/skf-rename-skill/references/execute.md +107 -78
- package/src/skf-rename-skill/references/exit-codes.md +12 -0
- package/src/skf-rename-skill/references/report.md +16 -8
- package/src/skf-rename-skill/references/select.md +69 -42
- package/src/skf-rename-skill/scripts/skf-validate-rename-name.py +161 -0
- package/src/skf-setup/SKILL.md +20 -12
- package/src/skf-setup/customize.toml +10 -0
- package/src/skf-setup/references/auto-index.md +9 -10
- package/src/skf-setup/references/ccc-index.md +14 -15
- package/src/skf-setup/references/detect-and-tier.md +6 -8
- package/src/skf-setup/references/report.md +22 -31
- package/src/skf-setup/references/tier-rules.md +2 -32
- package/src/skf-setup/references/write-config.md +9 -10
- package/src/skf-test-skill/SKILL.md +10 -8
- package/src/skf-test-skill/customize.toml +2 -0
- package/src/skf-test-skill/references/coherence-check.md +19 -31
- package/src/skf-test-skill/references/coverage-check.md +109 -75
- package/src/skf-test-skill/references/detect-mode.md +10 -41
- package/src/skf-test-skill/references/external-validators.md +11 -19
- package/src/skf-test-skill/references/init.md +38 -20
- package/src/skf-test-skill/references/migration-section-rules.md +4 -4
- package/src/skf-test-skill/references/report.md +60 -40
- package/src/skf-test-skill/references/score.md +32 -64
- package/src/skf-test-skill/references/scoring-rules.md +6 -62
- package/src/skf-test-skill/references/source-access-protocol.md +6 -6
- package/src/skf-test-skill/references/step-hard-gate.md +1 -3
- package/src/skf-test-skill/scripts/aggregate-coherence.py +276 -0
- package/src/skf-test-skill/scripts/check-metadata-coherence.py +359 -0
- package/src/skf-test-skill/scripts/combine-external-scores.py +190 -0
- package/src/skf-test-skill/scripts/compute-score.py +131 -1
- package/src/skf-test-skill/scripts/reconcile-coverage.py +327 -0
- package/src/skf-test-skill/scripts/validate-inventory.py +251 -0
- package/src/skf-test-skill/scripts/verify-declared-numerator.py +192 -0
- package/src/skf-update-skill/SKILL.md +17 -5
- package/src/skf-update-skill/customize.toml +14 -0
- package/src/skf-update-skill/references/detect-changes.md +19 -37
- package/src/skf-update-skill/references/health-check.md +3 -9
- package/src/skf-update-skill/references/init.md +39 -32
- package/src/skf-update-skill/references/manual-section-rules.md +1 -2
- package/src/skf-update-skill/references/merge-conflict-rules.md +1 -52
- package/src/skf-update-skill/references/merge.md +13 -57
- package/src/skf-update-skill/references/re-extract.md +12 -24
- package/src/skf-update-skill/references/report.md +14 -7
- package/src/skf-update-skill/references/validate.md +32 -30
- package/src/skf-update-skill/references/write.md +86 -51
- package/src/skf-update-skill/scripts/skf-new-file-diff.py +167 -0
- package/src/skf-verify-stack/SKILL.md +11 -21
- package/src/skf-verify-stack/customize.toml +12 -3
- package/src/skf-verify-stack/references/coverage-patterns.md +1 -29
- package/src/skf-verify-stack/references/coverage.md +21 -10
- package/src/skf-verify-stack/references/exit-codes.md +14 -0
- package/src/skf-verify-stack/references/health-check.md +1 -1
- package/src/skf-verify-stack/references/init.md +20 -25
- package/src/skf-verify-stack/references/integration-verification-rules.md +10 -19
- package/src/skf-verify-stack/references/integrations.md +44 -49
- package/src/skf-verify-stack/references/report.md +28 -34
- package/src/skf-verify-stack/references/requirements.md +3 -3
- package/src/skf-verify-stack/references/synthesize.md +36 -39
- package/src/skf-verify-stack/scripts/skf-coverage-tally.py +196 -0
- package/src/skf-verify-stack/scripts/skf-report-delta.py +256 -0
- package/src/skf-verify-stack/scripts/skf-verdict-rollup.py +258 -0
- package/src/skf-rename-skill/references/rebuild-context.md +0 -110
|
@@ -24,11 +24,28 @@ updateActiveSymlinkProbeOrder:
|
|
|
24
24
|
manifestOpsProbeOrder:
|
|
25
25
|
- '{project-root}/_bmad/skf/shared/scripts/skf-manifest-ops.py'
|
|
26
26
|
- '{project-root}/src/shared/scripts/skf-manifest-ops.py'
|
|
27
|
-
# Resolve `{rebuildManagedSectionsHelper}` similarly. §7
|
|
27
|
+
# Resolve `{rebuildManagedSectionsHelper}` similarly. §7 uses the
|
|
28
28
|
# `replace` action for the surgical between-marker rewrite.
|
|
29
29
|
rebuildManagedSectionsProbeOrder:
|
|
30
30
|
- '{project-root}/_bmad/skf/shared/scripts/skf-rebuild-managed-sections.py'
|
|
31
31
|
- '{project-root}/src/shared/scripts/skf-rebuild-managed-sections.py'
|
|
32
|
+
# Resolve `{rewriteSkillNameHelper}` similarly. §3 uses it for the four
|
|
33
|
+
# field-scoped in-file rename transforms (SKILL.md frontmatter `name`,
|
|
34
|
+
# metadata.json `name`, provenance-map.json `skill_name`, context-snippet
|
|
35
|
+
# header + `root:` paths) — each a JSON round-trip or anchored-region edit
|
|
36
|
+
# plus the atomic write in one call, so the LLM never hand-edits JSON (which
|
|
37
|
+
# risks key reorder/drop) or eyeballs "only within the frontmatter" (which
|
|
38
|
+
# mis-fires when {old_name} is a substring, e.g. rename -> renamer).
|
|
39
|
+
rewriteSkillNameProbeOrder:
|
|
40
|
+
- '{project-root}/_bmad/skf/shared/scripts/skf-rewrite-skill-name.py'
|
|
41
|
+
- '{project-root}/src/shared/scripts/skf-rewrite-skill-name.py'
|
|
42
|
+
# Resolve `{verifyNoTraceHelper}` similarly. §5 uses it for the commit-point
|
|
43
|
+
# no-trace gate: a fixed-file-set, name-token scan over affected_versions with
|
|
44
|
+
# the SKILL.md frontmatter/body region split (frontmatter = hard failure, body
|
|
45
|
+
# = advisory warning) and the directory-listing check, returned as JSON.
|
|
46
|
+
verifyNoTraceProbeOrder:
|
|
47
|
+
- '{project-root}/_bmad/skf/shared/scripts/skf-verify-no-trace.py'
|
|
48
|
+
- '{project-root}/src/shared/scripts/skf-verify-no-trace.py'
|
|
32
49
|
---
|
|
33
50
|
|
|
34
51
|
<!-- Config: communicate in {communication_language}. -->
|
|
@@ -47,9 +64,17 @@ Execute the rename decisions recorded in step 1 as a transaction. Copy the old `
|
|
|
47
64
|
- Do not proceed past a verification failure in section 5
|
|
48
65
|
- Report each section's outcome as it completes
|
|
49
66
|
|
|
67
|
+
**Headless error envelope (self-contained).** Every HALT below that says *"emit the error envelope"* means: write this single line to **stderr**, mirroring SKILL.md's Result Contract (Headless) so this stage stays parseable even if SKILL.md is out of context on a `nextStepFile` chain —
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
SKF_RENAME_SKILL_RESULT_JSON: {"status":"error","old_name":"{old_name}","new_name":"{new_name}","versions_renamed":[],"manifest_rekeyed":false,"context_files_updated":[],"exit_code":<code>,"halt_reason":"<reason>","headless_decisions":{headless_decisions}}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Set `exit_code` and `halt_reason` to the values named at that HALT site (see `references/exit-codes.md`); `old_name`/`new_name` are both resolved by step 1 before this stage runs. `{headless_decisions}` is the audit trail carried from step 1 (the §6 source-authority override if it fired; `[]` otherwise) — emit it verbatim so a halt in this stage still preserves the decision trail.
|
|
74
|
+
|
|
50
75
|
## MANDATORY SEQUENCE
|
|
51
76
|
|
|
52
|
-
**
|
|
77
|
+
**Transactional boundary.** After section 1 (copy), the old skill is untouched; a failure in any of sections 2-7 deletes the two new directories (`{new_skill_group}`, `{new_forge_group}`), reports, and halts with the old skill intact. Section 8 (delete old) is the only irreversible point.
|
|
53
78
|
|
|
54
79
|
### 0. Re-read Version-Paths Knowledge + Resolve Helpers
|
|
55
80
|
|
|
@@ -57,12 +82,14 @@ Read `{versionPathsKnowledge}` again and confirm the templates (`{skill_package}
|
|
|
57
82
|
|
|
58
83
|
**Resolve helpers** in parallel — these are independent file-existence checks that batch into one tool-call message:
|
|
59
84
|
|
|
60
|
-
- `{atomicWriteHelper}` ← first existing path in `{atomicWriteProbeOrder}` (used in §
|
|
85
|
+
- `{atomicWriteHelper}` ← first existing path in `{atomicWriteProbeOrder}` (used in §6 for crash-safe manifest restore)
|
|
86
|
+
- `{rewriteSkillNameHelper}` ← first existing path in `{rewriteSkillNameProbeOrder}` (used in §3 for the field-scoped in-file rename transforms + atomic write)
|
|
61
87
|
- `{updateActiveSymlinkHelper}` ← first existing path in `{updateActiveSymlinkProbeOrder}` (used in §4 for atomic symlink repair)
|
|
88
|
+
- `{verifyNoTraceHelper}` ← first existing path in `{verifyNoTraceProbeOrder}` (used in §5 for the deterministic no-trace commit gate)
|
|
62
89
|
- `{manifestOpsHelper}` ← first existing path in `{manifestOpsProbeOrder}` (used in §6 for the manifest re-key)
|
|
63
|
-
- `{rebuildManagedSectionsHelper}` ← first existing path in `{rebuildManagedSectionsProbeOrder}` (used in §7
|
|
90
|
+
- `{rebuildManagedSectionsHelper}` ← first existing path in `{rebuildManagedSectionsProbeOrder}` (used in §7 for between-marker swap)
|
|
64
91
|
|
|
65
|
-
If any helper has no existing candidate, release the lock and HALT (exit code 4, `halt_reason: "write-failed"`) — the rename's safety guarantees depend on these helpers and a fall-through to LLM-driven writes would silently regress atomicity.
|
|
92
|
+
If any helper has no existing candidate, release the lock and HALT (exit code 4, `halt_reason: "write-failed"`) — the rename's safety guarantees depend on these helpers, and a fall-through to LLM-driven writes/scans would silently regress atomicity (the write helpers) or the deterministic transform and commit-gate checks (`{rewriteSkillNameHelper}`, `{verifyNoTraceHelper}`).
|
|
66
93
|
|
|
67
94
|
**Lock release contract:** every halt path in this step ends with `rm -f "{forge_data_folder}/{old_name}/.skf-rename.lock"` before exiting. The terminal health-check (step 4) is the success-path release.
|
|
68
95
|
|
|
@@ -103,44 +130,25 @@ Report: "**Renamed {count} inner directories** to `{new_name}/`."
|
|
|
103
130
|
|
|
104
131
|
### 3. Update File Contents Inside the New Location
|
|
105
132
|
|
|
106
|
-
For each version `v` in `affected_versions`, operate on the files inside `{new_skill_group}/{v}/{new_name}/` (the freshly renamed inner directory) and `{new_forge_group}/{v}
|
|
133
|
+
For each version `v` in `affected_versions`, operate on the files inside `{new_skill_group}/{v}/{new_name}/` (the freshly renamed inner directory) and `{new_forge_group}/{v}/`.
|
|
107
134
|
|
|
108
|
-
**
|
|
135
|
+
**Transform semantics (apply to 3a / 3b / 3c / 3d):** `{rewriteSkillNameHelper}` performs each field-scoped substitution AND the crash-safe write (stage to `<target>.skf-tmp`, fsync, atomic rename) in one call — do NOT compute file content in the prompt. Each `--kind` edits exactly one field/region and leaves everything else byte-for-byte intact, so there is no key reorder/drop from hand-editing JSON and no wrong-region substitution when `{old_name}` is a substring (e.g. `rename` → `renamer`). Invoke it once per file:
|
|
109
136
|
|
|
110
137
|
```bash
|
|
111
|
-
|
|
138
|
+
python3 {rewriteSkillNameHelper} "{target_path}" \
|
|
139
|
+
--kind {skill-frontmatter|metadata-json|context-snippet|provenance-json} \
|
|
140
|
+
--old-name {old_name} --new-name {new_name}
|
|
112
141
|
```
|
|
113
142
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
- Path: `{new_skill_group}/{v}/{new_name}/SKILL.md`
|
|
117
|
-
- In the YAML frontmatter (between the leading `---` markers), replace the `name:` field value from `{old_name}` to `{new_name}`
|
|
118
|
-
- Only replace within the frontmatter block — do not substitute matches inside the body text
|
|
119
|
-
- Pipe via `{atomicWriteHelper} write` per the write semantics above
|
|
120
|
-
- If the file is missing, record it in `section3_warnings` and continue
|
|
121
|
-
|
|
122
|
-
**3b. metadata.json:**
|
|
143
|
+
Read the JSON result (`changed`, `wrote`, and per-kind fields). Exit 0 = processed (written only if the content changed). A non-zero exit — a missing structural region (no frontmatter delimiters), invalid JSON, or a write failure — is a **file update failure**: trigger the rollback below. Check file existence first: if the target file does not exist, skip the invocation and record it in `section3_warnings` per the per-item notes (a missing file is not a failure).
|
|
123
144
|
|
|
124
|
-
-
|
|
125
|
-
- Parse the JSON, set `name` = `{new_name}`, write back preserving formatting via `{atomicWriteHelper} write`
|
|
126
|
-
- If the file is missing, record it in `section3_warnings` and continue
|
|
145
|
+
**3a. SKILL.md frontmatter** — `--kind skill-frontmatter` on `{new_skill_group}/{v}/{new_name}/SKILL.md`. Replaces the top-level `name:` value inside the frontmatter block only (anchored on `^name:`, so a nested `name:` or a longer key like `renamed:` is untouched); body text is preserved verbatim, so a legitimate mention of `{old_name}` below the closing `---` survives. If the file is missing, record it in `section3_warnings` and continue.
|
|
127
146
|
|
|
128
|
-
**
|
|
147
|
+
**3b. metadata.json** — `--kind metadata-json` on `{new_skill_group}/{v}/{new_name}/metadata.json`. Sets `name` = `{new_name}` via a JSON round-trip (key order preserved). If the file is missing, record it in `section3_warnings` and continue.
|
|
129
148
|
|
|
130
|
-
-
|
|
131
|
-
- Replace the display name header `[{old_name} v...]` → `[{new_name} v...]` (preserving the version suffix)
|
|
132
|
-
- Rewrite every `root:` path that references the old name to use the new name. Parse the `root:` line as `root: {prefix}{old_name}/`, preserve the prefix as-is, and replace `{old_name}` with `{new_name}`. This generically handles any IDE's skill root path (e.g., `.claude/skills/`, `.windsurf/skills/`, `.github/skills/`) as well as the draft `skills/` prefix and legacy forms — no enumeration of known prefixes needed.
|
|
133
|
-
- Example: `root: .windsurf/skills/{old_name}/` → `root: .windsurf/skills/{new_name}/`
|
|
134
|
-
- Example: `root: skills/{old_name}/` → `root: skills/{new_name}/`
|
|
135
|
-
- Legacy pre-fix form `root: skills/{old_name}/active/{old_name}/` → `root: skills/{new_name}/` (normalize to flat form during rename)
|
|
136
|
-
- Pipe via `{atomicWriteHelper} write` per the write semantics above
|
|
137
|
-
- If the file is missing, record it in `section3_warnings` and continue
|
|
149
|
+
**3c. context-snippet.md** — `--kind context-snippet` on `{new_skill_group}/{v}/{new_name}/context-snippet.md`. Rewrites the display header `[{old_name} v...]` → `[{new_name} v...]` (version suffix preserved) and every `root:` path: it parses `root: {prefix}{old_name}/`, keeps the prefix verbatim, and swaps the trailing `{old_name}/` segment for `{new_name}/` — handling any IDE prefix (`.claude/skills/`, `.windsurf/skills/`, `.github/skills/`, the draft `skills/` prefix) generically, and flattening the legacy `root: skills/{old_name}/active/{old_name}/` form to `root: skills/{new_name}/`. If the file is missing, record it in `section3_warnings` and continue.
|
|
138
150
|
|
|
139
|
-
**3d. provenance-map.json
|
|
140
|
-
|
|
141
|
-
- Path: `{new_forge_group}/{v}/provenance-map.json`
|
|
142
|
-
- Parse JSON, set `skill_name` = `{new_name}`, write back via `{atomicWriteHelper} write`
|
|
143
|
-
- If the file is missing (some versions may not have a provenance map), record it in `section3_warnings` and continue
|
|
151
|
+
**3d. provenance-map.json** — `--kind provenance-json` on `{new_forge_group}/{v}/provenance-map.json`. Sets `skill_name` = `{new_name}` via a JSON round-trip. If the file is missing (some versions may not have a provenance map), record it in `section3_warnings` and continue.
|
|
144
152
|
|
|
145
153
|
**Rollback on any update failure (not just a missing file):**
|
|
146
154
|
|
|
@@ -173,26 +181,24 @@ Recreate or repair the `active` symlink in `{new_skill_group}` via `{updateActiv
|
|
|
173
181
|
|
|
174
182
|
### 5. Verify — No Trace of `{old_name}` Inside the New Location
|
|
175
183
|
|
|
176
|
-
This is the commit-point check. If any
|
|
177
|
-
|
|
178
|
-
For every version `v` in `affected_versions`, grep (literal substring, case-sensitive) for `{old_name}` in:
|
|
179
|
-
|
|
180
|
-
- `{new_skill_group}/{v}/{new_name}/SKILL.md`
|
|
181
|
-
- `{new_skill_group}/{v}/{new_name}/metadata.json`
|
|
182
|
-
- `{new_skill_group}/{v}/{new_name}/context-snippet.md`
|
|
183
|
-
- `{new_forge_group}/{v}/provenance-map.json`
|
|
184
|
+
This is the commit-point check. If any structural reference to `{old_name}` remains, the rename is not safe to commit. `{verifyNoTraceHelper}` performs the whole scan deterministically — a fixed-file-set, name-token scan across every `affected_versions` entry, the SKILL.md frontmatter/body region split, and the directory-listing check — and returns the verdict as JSON. Invoke it once:
|
|
184
185
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
-
|
|
186
|
+
```bash
|
|
187
|
+
python3 {verifyNoTraceHelper} "{new_skill_group}" \
|
|
188
|
+
--forge-group "{new_forge_group}" \
|
|
189
|
+
--old-name {old_name} --new-name {new_name} \
|
|
190
|
+
--versions {comma-separated affected_versions}
|
|
191
|
+
```
|
|
188
192
|
|
|
189
|
-
|
|
193
|
+
Per version `v`, the helper scans `SKILL.md` (frontmatter matches → `hard_matches`; matches in the body below the closing `---` → `body_warnings`), `metadata.json`, `context-snippet.md`, and `provenance-map.json` (any match → `hard_matches`), plus the `{new_skill_group}/{v}/` listing (an `{old_name}/` directory present, or the `{new_name}/` directory missing → `dir_violations`). It matches `{old_name}` only as a complete skill-name token — bounded by non-name characters — so a correctly-renamed `{new_name}` that contains `{old_name}` as a substring (e.g. `rename` → `rename-skill`) is never a false leftover, and the frontmatter=hard / body=warning split is applied by region with no meaning-interpretation. A missing file is recorded under `skipped`, not treated as a match. Exit 0 = clean; exit 1 = at least one hard match or dir violation.
|
|
190
194
|
|
|
191
|
-
|
|
195
|
+
Read the JSON and decide:
|
|
192
196
|
|
|
193
|
-
- `
|
|
194
|
-
-
|
|
195
|
-
-
|
|
197
|
+
- **If `clean` is `true` (empty `hard_matches` AND empty `dir_violations`):** the rename is safe to commit. Set `verification_warnings` = the returned `body_warnings` (informational SKILL.md body mentions of `{old_name}` that are retained). Proceed.
|
|
198
|
+
- **If `clean` is `false`:** this is a hard failure —
|
|
199
|
+
- `rm -rf {new_skill_group}` and `rm -rf {new_forge_group}`
|
|
200
|
+
- Release the lock: `rm -f "{forge_data_folder}/{old_name}/.skf-rename.lock"`
|
|
201
|
+
- Halt with: "**Verification failed.** `{old_name}` still appears in: {the files from `hard_matches` plus any `dir_violations`}. Rolled back both new directories. Old skill is intact." HALT (exit code 5, `halt_reason: "verify-failed"`). In headless, emit the error envelope.
|
|
196
202
|
|
|
197
203
|
Report: "**Verified** — no structural references to `{old_name}` remain inside the new location across {affected_versions_count} version(s). {if verification_warnings is non-empty: 'Informational body-text mentions retained in SKILL.md: {list}.'}"
|
|
198
204
|
|
|
@@ -228,26 +234,65 @@ Report: "**Manifest updated** — re-keyed `exports.{old_name}` → `exports.{ne
|
|
|
228
234
|
|
|
229
235
|
### 7. Rebuild Context Files
|
|
230
236
|
|
|
231
|
-
|
|
237
|
+
After §6 re-keys the manifest from `{old_name}` to `{new_name}`, every IDE's context file (`CLAUDE.md`, `.cursorrules`, `AGENTS.md`, etc.) still carries the old name in its managed-section snippet rows. This section rewrites each one in-place via the surgical between-marker swap so the on-disk managed sections reflect the new name. It runs only on the success path — the §4–§6 rollback jumps never reach here.
|
|
238
|
+
|
|
239
|
+
**7a. Resolve `target_context_files`.** Load the `ides` list from `config.yaml`. The installer writes IDE identifiers — map each to a context file and skill root via the "IDE → Context File Mapping" table in `{managedSectionLogic}`:
|
|
240
|
+
|
|
241
|
+
1. For each entry in `config.yaml.ides`, look up its `context_file` and `skill_root` from the mapping table.
|
|
242
|
+
2. For any entry not in the table, default to `{unknownIdeDefaultContextFile}` / `{unknownIdeDefaultSkillRoot}` and emit a warning: `Unknown IDE '{value}' in config.yaml — defaulting to {unknownIdeDefaultContextFile}`.
|
|
243
|
+
3. Deduplicate by `context_file` — when multiple IDEs map to the same context file, use the first configured IDE's `skill_root`.
|
|
244
|
+
4. If `config.yaml.ides` is absent or the mapping yields an empty list, fall back to `[{context_file: "{unknownIdeDefaultContextFile}", skill_root: "{unknownIdeDefaultSkillRoot}"}]` and emit a note: `No IDEs configured in config.yaml — defaulting to {unknownIdeDefaultContextFile}`.
|
|
245
|
+
|
|
246
|
+
**7b. Per-file loop.** For each entry in `target_context_files`:
|
|
247
|
+
|
|
248
|
+
1. **Resolve the target file** at `{context_file}` (absolute path).
|
|
249
|
+
2. **Read the current file:**
|
|
250
|
+
- If it does not exist, skip (nothing to rebuild — export-skill re-creates it on its next run).
|
|
251
|
+
- If it exists but has no `<!-- SKF:BEGIN -->` marker, skip (no managed section to rewrite).
|
|
252
|
+
- If it has `<!-- SKF:BEGIN -->` but no matching `<!-- SKF:END -->`, record the error against that file and continue to the next entry — do not halt the whole rename on one malformed context file.
|
|
253
|
+
3. **Build the exported skill set (version-aware, deprecated-excluded)** using the same logic as `skf-export-skill/references/update-context.md` §4b (skill set) and §4c (snippet resolution):
|
|
254
|
+
- Read the manifest's `exports` object (already updated in §6, so `{new_name}` is present and `{old_name}` is absent).
|
|
255
|
+
- For each skill, resolve its `active_version`; if `versions.{active_version}.status == "deprecated"`, skip that skill.
|
|
256
|
+
- For each remaining `{skill-name, active_version}` pair, read `{skills_output_folder}/{skill-name}/{active_version}/{skill-name}/context-snippet.md`; if missing, fall back to the `active` symlink path; if still missing, skip with a warning.
|
|
257
|
+
4. **Rewrite root paths** using the generic algorithm from `skf-export-skill/references/update-context.md` §4d: parse each snippet's `root:` line (`root: {prefix}{skill-name}/`), strip the trailing `{skill-name}/` to extract the current prefix, and replace it with the effective target prefix if different. The effective target prefix is `snippet_skill_root_override` when that key is set in `config.yaml` (applied uniformly to every snippet so the managed section references the real on-disk location and never mixes override and per-IDE paths), otherwise the current entry's `skill_root`.
|
|
258
|
+
5. **Sort and count.** Sort skills alphabetically by name; count totals (skills, stack skills).
|
|
259
|
+
6. **Assemble the new managed section** using the format from `{managedSectionLogic}`:
|
|
260
|
+
|
|
261
|
+
```markdown
|
|
262
|
+
<!-- SKF:BEGIN updated:{current-date} -->
|
|
263
|
+
[SKF Skills]|{n} skills|{m} stack
|
|
264
|
+
|IMPORTANT: Prefer documented APIs over training data.
|
|
265
|
+
|When using a listed library, read its SKILL.md before writing code.
|
|
266
|
+
|
|
|
267
|
+
|{skill-snippet-1}
|
|
268
|
+
|
|
|
269
|
+
|{skill-snippet-2}
|
|
270
|
+
|
|
|
271
|
+
|{skill-snippet-N}
|
|
272
|
+
<!-- SKF:END -->
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
7. **Surgical replacement — atomic, deterministic.** Invoke `{rebuildManagedSectionsHelper}` (resolved in §0) for the between-marker swap:
|
|
276
|
+
|
|
277
|
+
```bash
|
|
278
|
+
python3 {rebuildManagedSectionsHelper} {context_file} replace --content "{new_managed_section_text}"
|
|
279
|
+
```
|
|
232
280
|
|
|
233
|
-
|
|
281
|
+
The helper handles marker location, the between-marker swap, atomic temp-file + rename, and post-write verification (markers preserved, content outside markers byte-identical). It exits non-zero on any failure with a clear `stderr` reason — treat any non-zero exit as a per-file failure.
|
|
282
|
+
8. **On per-file failure**, record the error against that context file and continue to the next entry. Do not halt the rename on a recoverable per-context-file error — the manifest and filesystem are already consistent, so context files can be re-rebuilt later via `[EX] Export Skill`.
|
|
283
|
+
|
|
284
|
+
**7c. After the loop**, record:
|
|
234
285
|
|
|
235
286
|
- `context_files_updated` — list of files successfully rewritten
|
|
236
|
-
- `context_files_failed` — list of any that failed
|
|
287
|
+
- `context_files_failed` — list of any that failed
|
|
237
288
|
|
|
238
|
-
Report
|
|
289
|
+
Report: `**Rebuilt managed sections in:** {list of updated files}. {if any failed: 'Failed: {list} — re-run [EX] Export Skill to retry.'}` Then proceed to §8.
|
|
239
290
|
|
|
240
291
|
**Note:** §7 failures do not trigger a rollback. Platform context files are derived artifacts; the manifest and on-disk skill directories are the canonical state.
|
|
241
292
|
|
|
242
293
|
### 8. Delete Old Directories (Point of No Return)
|
|
243
294
|
|
|
244
|
-
This is the only section after which rollback is impossible.
|
|
245
|
-
|
|
246
|
-
- `{new_skill_group}` is fully materialized with all inner directories renamed and files updated
|
|
247
|
-
- `{new_forge_group}` is fully materialized with `skill_name` updated in every provenance map
|
|
248
|
-
- No structural references to `{old_name}` remain inside either new directory (verified in section 5)
|
|
249
|
-
- The manifest has been re-keyed (section 6)
|
|
250
|
-
- Platform context files reference `{new_name}` (section 7, best-effort)
|
|
295
|
+
This is the only section after which rollback is impossible. Precondition: §1–7 have fully materialized and verified the new name (both new directories renamed, no `{old_name}` references remaining, manifest re-keyed, context files rebuilt best-effort) — only now is deleting the old name safe.
|
|
251
296
|
|
|
252
297
|
Execute the deletes:
|
|
253
298
|
|
|
@@ -282,25 +327,9 @@ Store the following for step 3:
|
|
|
282
327
|
- `section3_warnings` — list of missing file warnings (empty if none)
|
|
283
328
|
- `verification_warnings` — list of informational SKILL.md body mentions of `{old_name}` retained (empty if none)
|
|
284
329
|
- `deletion_errors` — list of post-commit deletion errors (empty if none)
|
|
330
|
+
- `headless_decisions` — the audit trail of confirmation gates auto-resolved under `{headless_mode}`, carried forward from step 1 unchanged (empty in interactive runs). Step 3 surfaces it in the result envelope and the per-run result JSON.
|
|
285
331
|
|
|
286
332
|
### 10. Load Next Step
|
|
287
333
|
|
|
288
334
|
Load, read the full file, and then execute `{nextStepFile}`.
|
|
289
335
|
|
|
290
|
-
## Error Handling Summary
|
|
291
|
-
|
|
292
|
-
| Section | Failure Mode | Reversible? | Recovery Action |
|
|
293
|
-
|---------|--------------|-------------|-----------------|
|
|
294
|
-
| 1 | Copy failure | Yes | Delete whichever new directory exists; old skill intact |
|
|
295
|
-
| 2 | Inner rename failure | Yes | `rm -rf` both new directories; old skill intact |
|
|
296
|
-
| 3 | File update failure | Yes | `rm -rf` both new directories; old skill intact |
|
|
297
|
-
| 4 | `active` symlink repair failure | Yes | `rm -rf` both new directories; old skill intact |
|
|
298
|
-
| 5 | Verification failure | Yes | `rm -rf` both new directories; old skill intact |
|
|
299
|
-
| 6 | Manifest write failure | Yes | Restore manifest from backup; `rm -rf` both new directories; old skill intact |
|
|
300
|
-
| 7 | Platform context rebuild failure | Per-file | Record errors, continue other platforms; do NOT rollback — manifest and disk are canonical |
|
|
301
|
-
| 8 | Delete failure | **No** | Record deletion errors; new name is already committed; user removes remnants manually |
|
|
302
|
-
|
|
303
|
-
## CRITICAL STEP COMPLETION NOTE
|
|
304
|
-
|
|
305
|
-
ONLY WHEN all execution sections have been attempted (copy, inner rename, file updates, symlink fix, verification, manifest re-key, context rebuild, old delete) and results have been stored in context, will you then load and read fully `{nextStepFile}` to generate the final report.
|
|
306
|
-
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Exit Codes — skf-rename-skill
|
|
2
|
+
|
|
3
|
+
Every HARD HALT in the rename workflow exits with a stable code so headless automators can branch on the failure class without grepping message text:
|
|
4
|
+
|
|
5
|
+
| Code | Meaning | Raised by |
|
|
6
|
+
| ---- | -------------------- | -------------------------------------------------------------------------------------------- |
|
|
7
|
+
| 0 | success | step 4 (terminal) |
|
|
8
|
+
| 2 | input-missing / input-invalid | step 1 §4/§5 (headless missing `old_name`/`new_name` arg) → `input-missing`; new name fails kebab-case / length / same-as-old → `input-invalid` |
|
|
9
|
+
| 3 | resolution-failure | step 1 §2 (manifest is malformed JSON); step 1 §3 (no skills found anywhere) |
|
|
10
|
+
| 4 | write-failure | On-Activation §4 pre-flight write probe (skills_output_folder / forge_data_folder unwritable); step 2 §1 (copy → `copy-failed`); step 2 §2 (inner-dir rename) / §3 (file content update) / §4 (symlink repair) → `write-failed`; step 2 §6 (manifest write → `manifest-write-failed`). step 2 §7 (context-file rewrite) is **best-effort and never halts** — per-file failures are recorded in `context_files_failed` and the rename still succeeds (manifest + disk are canonical; re-run `[EX] Export Skill` to retry). |
|
|
11
|
+
| 5 | state-conflict | step 1 §5 (name-collision check fails: target name already in use); step 1 §6 (source-authority="official" headless without `force_source_authority_in_headless`); concurrency lock collision (`halted-for-concurrent-run`); step 2 §5 (no-trace verification failed — `{old_name}` still present inside the new location → `verify-failed`; note this rollback is code 5, unlike the copy/write rollbacks at code 4) |
|
|
12
|
+
| 6 | user-cancelled | step 1 §6 (source-authority warning [N]); step 1 §8 confirmation gate `[N]`; any prompt that accepted `cancel`/`exit`/`:q` |
|
|
@@ -15,7 +15,7 @@ Present a clear, final summary of what the rename workflow changed — old and n
|
|
|
15
15
|
- Focus only on reporting results stored in context by step 2 — do not re-execute any part of the rename
|
|
16
16
|
- Do not hide verification warnings, context file rebuild failures, or deletion errors
|
|
17
17
|
- Present next-steps guidance so the user knows which downstream workflows to run
|
|
18
|
-
- Chains to the local health-check step via `{nextStepFile}` after completion
|
|
18
|
+
- Chains to the local health-check step via `{nextStepFile}` after completion (see section 2)
|
|
19
19
|
|
|
20
20
|
## MANDATORY SEQUENCE
|
|
21
21
|
|
|
@@ -61,6 +61,10 @@ Informational: the old name still appears in SKILL.md body text (prose only, non
|
|
|
61
61
|
{list each error}
|
|
62
62
|
→ The new name is fully committed. Remove the remnants manually with `rm -rf {path}`.
|
|
63
63
|
|
|
64
|
+
{if headless_decisions is non-empty:}
|
|
65
|
+
Headless auto-decisions:
|
|
66
|
+
{for each entry: "{gate}: took {taken_action} (default {default_action}) — {reason}"}
|
|
67
|
+
|
|
64
68
|
---
|
|
65
69
|
|
|
66
70
|
**Next steps:**
|
|
@@ -71,21 +75,25 @@ Informational: the old name still appears in SKILL.md body text (prose only, non
|
|
|
71
75
|
|
|
72
76
|
### Result Contract
|
|
73
77
|
|
|
74
|
-
Write the result contract per `shared/references/output-contract-schema.md`: the per-run record at `{skills_output_folder}/{new_name}/rename-skill-result-{timestamp}.json` (reuse the activation-stored `{timestamp}`, resolution to seconds) and a copy at `{skills_output_folder}/{new_name}/rename-skill-result-latest.json` (stable path for pipeline consumers — copy, not symlink). Include all updated file paths (SKILL.md, metadata.json, context-snippet.md, provenance-map.json) in `outputs`; include `old_name`, `new_name`, and `
|
|
78
|
+
Write the result contract per `shared/references/output-contract-schema.md`: the per-run record at `{skills_output_folder}/{new_name}/rename-skill-result-{timestamp}.json` (reuse the activation-stored `{timestamp}`, resolution to seconds) and a copy at `{skills_output_folder}/{new_name}/rename-skill-result-latest.json` (stable path for pipeline consumers — copy, not symlink). Include all updated file paths (SKILL.md, metadata.json, context-snippet.md, provenance-map.json) in `outputs`; include `old_name`, `new_name`, `versions_renamed`, and `headless_decisions` (the auto-resolved gate audit trail carried from step 1 — `[]` in interactive runs) in `summary`.
|
|
75
79
|
|
|
76
80
|
When `{headless_mode}` is true, also emit the single-line envelope on **stdout** before chaining to step 4 (matches the SKILL.md "Result Contract (Headless)" shape):
|
|
77
81
|
|
|
78
82
|
```
|
|
79
|
-
SKF_RENAME_SKILL_RESULT_JSON: {"status":"success","old_name":"{old_name}","new_name":"{new_name}","versions_renamed":{affected_versions},"manifest_rekeyed":{manifest_rekeyed},"context_files_updated":{context_files_updated},"exit_code":0,"halt_reason":null}
|
|
83
|
+
SKF_RENAME_SKILL_RESULT_JSON: {"status":"success","old_name":"{old_name}","new_name":"{new_name}","versions_renamed":{affected_versions},"manifest_rekeyed":{manifest_rekeyed},"context_files_updated":{context_files_updated},"exit_code":0,"halt_reason":null,"headless_decisions":{headless_decisions}}
|
|
80
84
|
```
|
|
81
85
|
|
|
82
|
-
Substitute `{affected_versions}` and `{
|
|
86
|
+
Substitute `{affected_versions}`, `{context_files_updated}`, and `{headless_decisions}` as JSON arrays; `manifest_rekeyed` is the boolean from step 2's context.
|
|
83
87
|
|
|
84
|
-
|
|
88
|
+
**Post-completion hook (optional).** If `{onCompleteCommand}` is non-empty (resolved at SKILL.md On Activation §3 from `workflow.on_complete`), invoke it after the result contract is finalized, passing the per-run result path:
|
|
85
89
|
|
|
86
|
-
|
|
90
|
+
```bash
|
|
91
|
+
{onCompleteCommand} --result-path={skills_output_folder}/{new_name}/rename-skill-result-{timestamp}.json
|
|
92
|
+
```
|
|
87
93
|
|
|
88
|
-
|
|
94
|
+
Log success/failure but never fail the workflow on a hook error — the rename is already committed. The hook runs last so an audit-log emit, registry re-index, or notifier sees the completed rename. When `{onCompleteCommand}` is empty (bundled default), skip the invocation entirely.
|
|
89
95
|
|
|
90
|
-
|
|
96
|
+
### 2. Chain to Health Check
|
|
97
|
+
|
|
98
|
+
ONLY WHEN the rename report has been rendered and the result contract saved will you then load, read the full file, and execute `{nextStepFile}`. The health-check step is the true terminal step — do not stop here even though the report reads as final.
|
|
91
99
|
|
|
@@ -1,6 +1,31 @@
|
|
|
1
1
|
---
|
|
2
2
|
nextStepFile: 'execute.md'
|
|
3
3
|
versionPathsKnowledge: 'knowledge/version-paths.md'
|
|
4
|
+
# Resolve `{manifestOpsHelper}` by probing `{manifestOpsProbeOrder}` in order
|
|
5
|
+
# (installed SKF module path first, src/ dev-checkout fallback); first existing
|
|
6
|
+
# path wins. §7 uses its `affected-versions` action to enumerate the versions a
|
|
7
|
+
# rename must touch — the union of manifest version keys and on-disk version
|
|
8
|
+
# dirs, deduped and semver-sorted (numeric, so 0.10.0 precedes 0.9.0). Unlike
|
|
9
|
+
# execute.md's write helpers this one is not atomicity-critical: if neither path
|
|
10
|
+
# resolves, §7 falls back to computing the union in the prompt.
|
|
11
|
+
manifestOpsProbeOrder:
|
|
12
|
+
- '{project-root}/_bmad/skf/shared/scripts/skf-manifest-ops.py'
|
|
13
|
+
- '{project-root}/src/shared/scripts/skf-manifest-ops.py'
|
|
14
|
+
# Resolve `{skillInventoryHelper}` by probing `{skillInventoryProbeOrder}`
|
|
15
|
+
# (installed SKF module path first, src/ dev-checkout fallback). §3 uses it to
|
|
16
|
+
# enumerate the rename candidates: the union of manifest `exports` and on-disk
|
|
17
|
+
# skill directories, each with its version list and active version — computing
|
|
18
|
+
# that union/count in the prompt has one correct answer per input. §3 falls
|
|
19
|
+
# back to an in-prompt scan if neither path resolves.
|
|
20
|
+
skillInventoryProbeOrder:
|
|
21
|
+
- '{project-root}/_bmad/skf/shared/scripts/skf-skill-inventory.py'
|
|
22
|
+
- '{project-root}/src/shared/scripts/skf-skill-inventory.py'
|
|
23
|
+
# `{renameNameValidator}` is this skill's own deterministic new-name gate. §5
|
|
24
|
+
# uses it for the format / length / identity / collision checks (one correct
|
|
25
|
+
# answer per (name, filesystem, manifest) state) and the interrupted-rename
|
|
26
|
+
# recovery fingerprint; §5 falls back to the same checks in-prompt if Python is
|
|
27
|
+
# unavailable.
|
|
28
|
+
renameNameValidator: 'scripts/skf-validate-rename-name.py'
|
|
4
29
|
---
|
|
5
30
|
|
|
6
31
|
<!-- Config: communicate in {communication_language}. -->
|
|
@@ -18,6 +43,14 @@ Identify the skill the user wants to rename, validate the new name against the a
|
|
|
18
43
|
- Do not accept a new name that fails validation (kebab-case, length, uniqueness)
|
|
19
44
|
- Present the list of affected versions clearly so the user understands the scope
|
|
20
45
|
|
|
46
|
+
**Headless error envelope (self-contained).** Where a halt below says *"emit the error envelope per SKILL.md 'Result Contract (Headless)'"*, write this single line to **stderr** (restated here so selection halts stay parseable even if SKILL.md is out of context) —
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
SKF_RENAME_SKILL_RESULT_JSON: {"status":"error","old_name":"…|null","new_name":"…|null","versions_renamed":[],"manifest_rekeyed":false,"context_files_updated":[],"exit_code":<code>,"halt_reason":"<reason>","headless_decisions":[]}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Use the `old_name`/`new_name`, `exit_code`, and `halt_reason` named at each halt site (see `references/exit-codes.md`) — `old_name`/`new_name` are `null` until resolved in §4/§5. `headless_decisions` is `[]` at every selection-stage halt: the only decision this step records (the §6 source-authority override) means *proceed*, so no recorded decision is ever paired with a halt here.
|
|
53
|
+
|
|
21
54
|
## MANDATORY SEQUENCE
|
|
22
55
|
|
|
23
56
|
### 1. Load Knowledge
|
|
@@ -44,19 +77,19 @@ Load `{skills_output_folder}/.export-manifest.json` if it exists.
|
|
|
44
77
|
|
|
45
78
|
### 3. List Available Skills
|
|
46
79
|
|
|
47
|
-
|
|
80
|
+
Enumerate every skill available for rename deterministically. Resolve `{skillInventoryHelper}` ← first existing path in `{skillInventoryProbeOrder}` and run:
|
|
48
81
|
|
|
49
|
-
|
|
82
|
+
```bash
|
|
83
|
+
python3 {skillInventoryHelper} {skills_output_folder}
|
|
84
|
+
```
|
|
50
85
|
|
|
51
|
-
|
|
52
|
-
2. Count the number of versions in the skill's `versions` map
|
|
53
|
-
3. Display `{skill-name} ({n} versions, active: {active_version})`
|
|
86
|
+
Read the JSON. Each entry in `skills[]` carries `name`, its `versions` array, and `active_version` (the helper unions the manifest `exports` with the on-disk directories and dedupes, so manifest-tracked and orphaned skills both appear). A skill whose `name` is absent from `manifest.exports` is a draft/orphan the rename workflow can still handle — annotate it "(not in manifest)".
|
|
54
87
|
|
|
55
|
-
|
|
88
|
+
**If `{skillInventoryHelper}` has no existing candidate** (neither probe path resolves — e.g. Python/`uv` unavailable): build the list in the prompt instead — read `exports` from the manifest (if `manifest_exists`), scan `{skills_output_folder}/` for top-level directories, union the two, and read each one's `active_version` and version count. Directories absent from `exports` are the "(not in manifest)" entries.
|
|
56
89
|
|
|
57
|
-
**If the
|
|
90
|
+
**If the list is empty** (no manifest entries AND no on-disk skill directories): halt with "**Rename Skill — nothing to rename.** No skills found in `{skills_output_folder}/`. Run `[CS] Create Skill` first." HALT (exit code 3, `halt_reason: "nothing-to-rename"`). In headless mode, emit the error envelope with `old_name: null`, `new_name: null`.
|
|
58
91
|
|
|
59
|
-
Display the
|
|
92
|
+
Display the list, one line per skill as `{name} ({n} versions, active: {active_version})` (append "(not in manifest)" for orphans):
|
|
60
93
|
|
|
61
94
|
```
|
|
62
95
|
**Rename Skill — select target**
|
|
@@ -108,42 +141,38 @@ printf '%s\n%s\n' "$$" "$(date -u +%Y-%m-%dT%H:%M:%SZ)" > "$LOCK"
|
|
|
108
141
|
|
|
109
142
|
**Release contract:**
|
|
110
143
|
|
|
111
|
-
-
|
|
112
|
-
-
|
|
144
|
+
- This run owns the lock only after the `printf … > "$LOCK"` line above runs — i.e. once §4b has cleared the live-PID check and written this run's PID. The live-PID collision halt directly above is the **sole exception**: it exits *before* that line, so the lock belongs to the other live run and this run must **never** delete it (clearing a live lock it just honored would let a third invocation run concurrently).
|
|
145
|
+
- The terminal health-check step (step 4) deletes the lock as its final action on the success path.
|
|
146
|
+
- **Every halt from §5 onward — after this run has acquired the lock — must delete it before exiting** (`rm -f "$LOCK"` per halt site): the §5 input/format/collision halts, the §6 source-authority and cancel halts, the §8 cancel and dry-run exits, and execute.md's copy/verify/manifest/write rollbacks. Otherwise the next attempt would see a stale lock from this run.
|
|
113
147
|
|
|
114
148
|
### 5. Ask for New Name
|
|
115
149
|
|
|
116
150
|
"**What is the new name for this skill?**
|
|
117
|
-
The new name must be kebab-case: lowercase alphanumeric with hyphens, 1-64 characters, matching the regex `^[a-
|
|
151
|
+
The new name must be kebab-case: lowercase alphanumeric with hyphens, 1-64 characters, matching the regex `^[a-z0-9]([a-z0-9-]*[a-z0-9])?$` (starts and ends with a lowercase letter or digit; a single letter or digit is valid). Or type `cancel` / `exit` / `:q` to abort."
|
|
118
152
|
|
|
119
153
|
Wait for user input. Trim whitespace. **GATE [default: use args]** — If `{headless_mode}` and new_name was provided as argument: use it and auto-proceed through validation. If not provided, release the lock and HALT (exit code 2, `halt_reason: "input-missing"`): "headless mode requires new_name argument." In headless, emit the error envelope.
|
|
120
154
|
|
|
121
155
|
- If the user enters `cancel`, `exit`, `[X]`, `q`, or `:q`: release the lock (`rm -f {forge_data_folder}/{old_name}/.skf-rename.lock`), display "Cancelled — no changes were made.", and HALT (exit code 6, `halt_reason: "user-cancelled"`).
|
|
122
156
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
1. **Kebab-case format:** Must match `^[a-z][a-z0-9-]*[a-z0-9]$` (or `^[a-z0-9]$` for the single-character case). If it fails:
|
|
126
|
-
"**Invalid name format.** The new name must be lowercase alphanumeric with hyphens, starting with a letter and ending with a letter or digit. Try again."
|
|
127
|
-
Re-ask. In headless mode, instead of re-asking, release the lock (`rm -f {forge_data_folder}/{old_name}/.skf-rename.lock`) and HALT (exit code 2, `halt_reason: "input-invalid"`) and emit the error envelope.
|
|
157
|
+
**Validate the candidate deterministically.** Run `{renameNameValidator}` (a per-skill helper, always shipped with the skill) — it applies format, length, identity, and collision in that order and returns the verdict as JSON:
|
|
128
158
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
159
|
+
```bash
|
|
160
|
+
python3 {renameNameValidator} \
|
|
161
|
+
--old-name {old_name} --new-name {candidate} \
|
|
162
|
+
--skills-output-folder {skills_output_folder} \
|
|
163
|
+
--forge-data-folder {forge_data_folder}
|
|
164
|
+
```
|
|
132
165
|
|
|
133
|
-
|
|
134
|
-
"**The new name is identical to the current name.** Nothing to rename. Try again or abort the workflow."
|
|
135
|
-
Re-ask. In headless mode, instead of re-asking, release the lock (`rm -f {forge_data_folder}/{old_name}/.skf-rename.lock`) and HALT (exit code 2, `halt_reason: "input-invalid"`) and emit the error envelope.
|
|
166
|
+
The checks: **format** = the kebab regex `^[a-z0-9]([a-z0-9-]*[a-z0-9])?$` (the module's canonical rule, same as `skf-validate-output.py` / `skf-validate-brief-inputs.py`, so a digit-leading name like `3d-tools` renames cleanly); **length** = 1-64 characters (agentskills.io spec); **identity** = differs from `{old_name}`; **collision** = the name is not a manifest `exports` key, a `{skills_output_folder}` directory, or a `{forge_data_folder}` directory.
|
|
136
167
|
|
|
137
|
-
|
|
138
|
-
- It must not appear as a key in `exports` in the manifest
|
|
139
|
-
- It must not exist as a top-level directory in `{skills_output_folder}/`
|
|
140
|
-
- It must not exist as a top-level directory in `{forge_data_folder}/`
|
|
168
|
+
Read `valid`, `first_failure`, `checks`, and `interrupted_rename`. If `valid` is true, store the input as `new_name` and proceed to §6. Otherwise branch on `first_failure` — interactive: display the message and re-ask; headless: release the lock (`rm -f {forge_data_folder}/{old_name}/.skf-rename.lock`), HALT with the mapped code, and emit the error envelope:
|
|
141
169
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
170
|
+
- **`format`** → "**Invalid name format.** The new name must be lowercase alphanumeric with hyphens, starting and ending with a lowercase letter or digit. Try again." (headless HALT: exit code 2, `halt_reason: "input-invalid"`)
|
|
171
|
+
- **`length`** → "**Invalid name length.** The new name must be 1-64 characters. Try again." (headless HALT: exit code 2, `halt_reason: "input-invalid"`)
|
|
172
|
+
- **`identity`** → "**The new name is identical to the current name.** Nothing to rename. Try again or abort the workflow." (headless HALT: exit code 2, `halt_reason: "input-invalid"`)
|
|
173
|
+
- **`collision`** → "**Name collision.** `{new-name}` already exists at: {the `path` of each entry in `checks.collision.locations`}. Pick a different name." When `interrupted_rename` is true, append: "This may be a stranded partial rename from an earlier interrupted run — `{new_name}` was staged but `{old_name}` was never removed. Confirm the `{new_name}` directories are not a skill you want to keep, then clean them up (`rm -rf {skills_output_folder}/{new_name} {forge_data_folder}/{new_name}`) and re-run this rename." — this gives headless pipelines a named recovery path instead of a dead-end collision halt. (headless HALT: exit code 5, `halt_reason: "name-collision"`)
|
|
145
174
|
|
|
146
|
-
|
|
175
|
+
**If `{renameNameValidator}` cannot run** (Python/`uv` unavailable): apply the same four checks in the same order in the prompt — the kebab regex, the 1-64 length bound, inequality with `{old_name}`, and the three-source collision lookup (plus the interrupted-rename fingerprint above) — using the identical messages and halt mapping.
|
|
147
176
|
|
|
148
177
|
### 6. Source Authority Check
|
|
149
178
|
|
|
@@ -176,14 +205,15 @@ Wait for response.
|
|
|
176
205
|
|
|
177
206
|
### 7. Enumerate Affected Versions
|
|
178
207
|
|
|
179
|
-
|
|
208
|
+
Resolve `{manifestOpsHelper}` ← first existing path in `{manifestOpsProbeOrder}` (installed SKF module path first, `src/` dev-checkout fallback). Enumerate every version the rename must touch deterministically via its `affected-versions` action:
|
|
180
209
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
4. Sort descending where possible (newest first)
|
|
210
|
+
```bash
|
|
211
|
+
python3 {manifestOpsHelper} {skills_output_folder} affected-versions {old_name}
|
|
212
|
+
```
|
|
185
213
|
|
|
186
|
-
|
|
214
|
+
Read the JSON result. Store `affected_versions` = `result.affected_versions` and `affected_versions_count` = `result.count`. The helper unions the manifest's `exports.{old_name}.versions` keys with the on-disk version directories under `{skills_output_folder}/{old_name}/` (every entry that is not the `active` symlink), so it handles both manifest-tracked and orphaned on-disk versions, deduplicates, and applies a **numeric** semver-descending sort (so `0.10.0` correctly precedes `0.9.0`, which a lexical sort gets wrong). An incomplete union would risk leaving a version internally un-renamed in the new copy — a leftover that step 2 §5 only catches for the versions it was told about.
|
|
215
|
+
|
|
216
|
+
**If `{manifestOpsHelper}` has no existing candidate** (neither probe path resolves — e.g. Python/`uv` unavailable): compute `affected_versions` in the prompt instead — read every key under `exports.{old_name}.versions` in the manifest, list every directory under `{skills_output_folder}/{old_name}/` that is not `active`, union the two sets, and sort descending (newest first, comparing version components numerically). Store the list as `affected_versions` and its length as `affected_versions_count`.
|
|
187
217
|
|
|
188
218
|
Also resolve the four outer paths using the templates from `{versionPathsKnowledge}`:
|
|
189
219
|
|
|
@@ -225,11 +255,11 @@ directories are removed and the old skill remains intact.
|
|
|
225
255
|
Proceed? [Y/N]
|
|
226
256
|
```
|
|
227
257
|
|
|
228
|
-
**GATE [default: Y]** — If `{headless_mode}`: auto-proceed with [Y], log: "headless: auto-confirmed rename {old_name} → {new_name}"
|
|
258
|
+
**GATE [default: Y]** — If `{headless_mode}`: auto-proceed with [Y] and append `{gate: "confirm-rename", default_action: "proceed", taken_action: "proceed", reason: "headless auto-confirm"}` to `headless_decisions[]` (log line: "headless: auto-confirmed rename {old_name} → {new_name}"). Skip this append when `--dry-run` is set — a dry run does not execute the rename, so there is no confirmation to record.
|
|
229
259
|
|
|
230
260
|
Wait for explicit user response.
|
|
231
261
|
|
|
232
|
-
**If `--dry-run` was passed**: skip the Y/N prompt entirely. Release the lock (`rm -f {forge_data_folder}/{old_name}/.skf-rename.lock`), display "**[DRY RUN] No changes were made — preview above shows what would be renamed.**", and emit the success envelope per SKILL.md "Result Contract (Headless)" with `status: "dry-run"`, the resolved `old_name`, `new_name`,
|
|
262
|
+
**If `--dry-run` was passed**: skip the Y/N prompt entirely. Release the lock (`rm -f {forge_data_folder}/{old_name}/.skf-rename.lock`), display "**[DRY RUN] No changes were made — preview above shows what would be renamed.**", and emit the success envelope per SKILL.md "Result Contract (Headless)" with `status: "dry-run"`, the resolved `old_name`, `new_name`, `versions_renamed: {affected_versions}`, and `headless_decisions: {headless_decisions}` (carries the §6 source-authority override entry if it fired, else `[]`), then HALT (exit code 0). No copy, no manifest re-key, no delete.
|
|
233
263
|
|
|
234
264
|
- **If `Y`** → proceed to section 9
|
|
235
265
|
- **If `N`** (or `cancel` / `exit` / `[X]` / `:q`) → release the lock (`rm -f {forge_data_folder}/{old_name}/.skf-rename.lock`), display "**Cancelled.** No changes were made.", HALT (exit code 6, `halt_reason: "user-cancelled"`). In headless mode, emit the error envelope per SKILL.md "Result Contract (Headless)" with the resolved `old_name` and `new_name`.
|
|
@@ -248,12 +278,9 @@ Store the following decisions in workflow context for step 2:
|
|
|
248
278
|
- `old_forge_group` — absolute path `{forge_data_folder}/{old_name}/`
|
|
249
279
|
- `new_forge_group` — absolute path `{forge_data_folder}/{new_name}/`
|
|
250
280
|
- `source_authority_override` — boolean (true if the user acknowledged the `"official"` warning, false/absent otherwise)
|
|
281
|
+
- `headless_decisions` — the audit trail of confirmation gates auto-resolved under `{headless_mode}` (the §6 source-authority override and the §8 auto-confirm entries appended above). Initialize to `[]`; in interactive runs it stays `[]`. Step 2 carries it forward and step 3 surfaces it in the result envelope.
|
|
251
282
|
|
|
252
283
|
### 10. Load Next Step
|
|
253
284
|
|
|
254
285
|
Load, read the full file, and then execute `{nextStepFile}`.
|
|
255
286
|
|
|
256
|
-
## CRITICAL STEP COMPLETION NOTE
|
|
257
|
-
|
|
258
|
-
ONLY WHEN the user has confirmed with `Y` at the confirmation gate AND all selection decisions have been stored in context, will you then load and read fully `{nextStepFile}` to execute the rename.
|
|
259
|
-
|