bmad-module-skill-forge 2.1.0 → 3.0.0

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.
Files changed (435) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/README.md +51 -49
  3. package/docs/404.md +10 -8
  4. package/docs/_data/pinned.yaml +22 -13
  5. package/docs/_internal/STABILITY.md +58 -35
  6. package/docs/agents.md +39 -53
  7. package/docs/architecture.md +65 -51
  8. package/docs/bmad-synergy.md +53 -51
  9. package/docs/campaign.md +127 -75
  10. package/docs/concepts.md +34 -28
  11. package/docs/examples.md +70 -68
  12. package/docs/forge-auto.md +34 -31
  13. package/docs/getting-started.md +118 -57
  14. package/docs/how-it-works.md +25 -21
  15. package/docs/index.md +15 -15
  16. package/docs/skill-model.md +70 -59
  17. package/docs/troubleshooting.md +135 -18
  18. package/docs/verifying-a-skill.md +83 -57
  19. package/docs/why-skf.md +24 -22
  20. package/docs/workflows.md +274 -110
  21. package/package.json +18 -10
  22. package/src/forger/preferences.yaml +8 -1
  23. package/src/knowledge/agentskills-spec.md +2 -0
  24. package/src/knowledge/ccc-bridge.md +40 -24
  25. package/src/knowledge/confidence-tiers.md +9 -5
  26. package/src/knowledge/overview.md +3 -3
  27. package/src/knowledge/progressive-capability.md +8 -8
  28. package/src/knowledge/provenance-tracking.md +9 -5
  29. package/src/knowledge/qmd-registry.md +2 -2
  30. package/src/knowledge/skf-knowledge-index.csv +2 -2
  31. package/src/knowledge/skill-lifecycle.md +18 -11
  32. package/src/knowledge/split-body-strategy.md +0 -3
  33. package/src/knowledge/tool-resolution.md +4 -2
  34. package/src/knowledge/version-paths.md +101 -15
  35. package/src/module-help.csv +3 -3
  36. package/src/module.yaml +10 -0
  37. package/src/shared/data/ast-grep-recipes.yaml +608 -0
  38. package/src/shared/data/ide-context-files.json +30 -0
  39. package/src/shared/health-check.md +32 -12
  40. package/src/shared/references/description-guard-protocol.md +14 -9
  41. package/src/shared/references/feasibility-report-schema.md +19 -13
  42. package/src/shared/references/headless-gate-convention.md +55 -3
  43. package/src/shared/references/output-contract-schema.md +19 -8
  44. package/src/shared/references/pipeline-contracts.md +94 -29
  45. package/src/shared/references/tessl-review.md +77 -0
  46. package/src/shared/scripts/schemas/skf-analyze-result-envelope.v1.json +194 -0
  47. package/src/shared/scripts/schemas/skf-audit-result-envelope.v1.json +192 -0
  48. package/src/shared/scripts/schemas/skf-brief-result-envelope.v1.json +32 -4
  49. package/src/shared/scripts/schemas/skf-campaign-result-envelope.v1.json +174 -0
  50. package/src/shared/scripts/schemas/skf-create-skill-result-envelope.v1.json +284 -0
  51. package/src/shared/scripts/schemas/skf-drop-skill-result-envelope.v1.json +164 -0
  52. package/src/shared/scripts/schemas/skf-export-result-envelope.v1.json +160 -0
  53. package/src/shared/scripts/schemas/skf-quick-skill-result-envelope.v1.json +261 -0
  54. package/src/shared/scripts/schemas/skf-refine-architecture-result-envelope.v1.json +192 -0
  55. package/src/shared/scripts/schemas/skf-rename-skill-result-envelope.v1.json +179 -0
  56. package/src/shared/scripts/schemas/skf-setup-result-envelope.v1.json +27 -11
  57. package/src/shared/scripts/schemas/skf-stack-result-envelope.v1.json +176 -0
  58. package/src/shared/scripts/schemas/skf-test-result-envelope.v1.json +173 -0
  59. package/src/shared/scripts/schemas/skf-update-result-envelope.v1.json +19 -5
  60. package/src/shared/scripts/schemas/skf-verify-stack-result-envelope.v1.json +180 -0
  61. package/src/shared/scripts/schemas/skill-brief.v1.json +34 -0
  62. package/src/shared/scripts/schemas/workspace-detection.v1.json +1 -1
  63. package/src/shared/scripts/skf-atomic-write.py +30 -14
  64. package/src/shared/scripts/skf-build-change-manifest.py +2203 -24
  65. package/src/shared/scripts/skf-ccc-git-hygiene.py +532 -0
  66. package/src/shared/scripts/skf-check-unit-records.py +184 -0
  67. package/src/shared/scripts/skf-check-workspace-drift.py +538 -9
  68. package/src/shared/scripts/skf-classify-changed-files.py +694 -0
  69. package/src/shared/scripts/skf-comention-pairs.py +1333 -123
  70. package/src/shared/scripts/skf-compare-file-hashes.py +1 -1
  71. package/src/shared/scripts/skf-count-imports.py +1867 -0
  72. package/src/shared/scripts/skf-count-tokens.py +1 -1
  73. package/src/shared/scripts/skf-derive-assembly-shape.py +3 -2
  74. package/src/shared/scripts/skf-description-guard.py +117 -9
  75. package/src/shared/scripts/skf-detect-docs.py +892 -4
  76. package/src/shared/scripts/skf-detect-language.py +486 -107
  77. package/src/shared/scripts/skf-detect-registry.py +887 -0
  78. package/src/shared/scripts/skf-detect-scripts-assets.py +1 -1
  79. package/src/shared/scripts/skf-detect-tools.py +215 -27
  80. package/src/shared/scripts/skf-detect-workspaces.py +506 -30
  81. package/src/shared/scripts/skf-disqualify-candidates.py +821 -182
  82. package/src/shared/scripts/skf-emit-brief-result-envelope.py +69 -191
  83. package/src/shared/scripts/skf-emit-result-envelope.py +1604 -106
  84. package/src/shared/scripts/skf-enumerate-stack-skills.py +986 -117
  85. package/src/shared/scripts/skf-extract-public-api.py +4084 -85
  86. package/src/shared/scripts/skf-extraction-inventory.py +746 -0
  87. package/src/shared/scripts/skf-extraction-snapshot.py +539 -0
  88. package/src/shared/scripts/skf-fetch-temporal.py +674 -0
  89. package/src/shared/scripts/skf-find-cycles.py +97 -29
  90. package/src/shared/scripts/skf-find-test-report.py +476 -0
  91. package/src/shared/scripts/skf-forge-tier-rw.py +444 -203
  92. package/src/shared/scripts/skf-github-fetch.py +365 -0
  93. package/src/shared/scripts/skf-github-probe.py +661 -0
  94. package/src/shared/scripts/skf-hash-content.py +166 -12
  95. package/src/shared/scripts/skf-language-corpora.py +1 -1
  96. package/src/shared/scripts/skf-load-provenance.py +159 -27
  97. package/src/shared/scripts/skf-manifest-ops.py +1 -1
  98. package/src/shared/scripts/skf-merge-ccc-exclusions.py +2307 -223
  99. package/src/shared/scripts/skf-merge-doc-urls.py +1 -1
  100. package/src/shared/scripts/skf-names-present.py +221 -0
  101. package/src/shared/scripts/skf-pair-intersect.py +119 -28
  102. package/src/shared/scripts/skf-parse-gaps.py +1384 -0
  103. package/src/shared/scripts/skf-preapply.py +1 -1
  104. package/src/shared/scripts/skf-preflight.py +161 -27
  105. package/src/shared/scripts/skf-promote-staged.py +430 -0
  106. package/src/shared/scripts/skf-provenance-gap-dispatch.py +39 -7
  107. package/src/shared/scripts/skf-qmd-classify-collections.py +229 -45
  108. package/src/shared/scripts/skf-quick-batch.py +774 -0
  109. package/src/shared/scripts/skf-rebuild-managed-sections.py +972 -88
  110. package/src/shared/scripts/skf-recommend-scope-type.py +323 -156
  111. package/src/shared/scripts/skf-render-metadata-stats.py +156 -12
  112. package/src/shared/scripts/skf-render-quick-metadata.py +132 -28
  113. package/src/shared/scripts/skf-render-stack-metadata.py +976 -0
  114. package/src/shared/scripts/skf-resolve-authoritative-files.py +128 -13
  115. package/src/shared/scripts/skf-resolve-package.py +523 -106
  116. package/src/shared/scripts/skf-rewrite-skill-name.py +433 -47
  117. package/src/shared/scripts/skf-run-lock.py +577 -0
  118. package/src/shared/scripts/skf-scan-manifests.py +998 -57
  119. package/src/shared/scripts/skf-scan-skill-md-structure.py +1341 -37
  120. package/src/shared/scripts/skf-severity-classify.py +712 -108
  121. package/src/shared/scripts/skf-shape-detect.py +298 -204
  122. package/src/shared/scripts/skf-shard-body.py +9 -8
  123. package/src/shared/scripts/skf-skill-inventory.py +2412 -89
  124. package/src/shared/scripts/skf-skills-module.py +472 -0
  125. package/src/shared/scripts/skf-source-tree.py +1796 -0
  126. package/src/shared/scripts/skf-structural-diff.py +670 -119
  127. package/src/shared/scripts/skf-tessl-review.py +706 -0
  128. package/src/shared/scripts/skf-update-active-symlink.py +5 -5
  129. package/src/shared/scripts/skf-update-run-state.py +624 -0
  130. package/src/shared/scripts/skf-validate-brief-inputs.py +92 -29
  131. package/src/shared/scripts/skf-validate-brief-schema.py +4 -1
  132. package/src/shared/scripts/skf-validate-feasibility-report.py +577 -66
  133. package/src/shared/scripts/skf-validate-frontmatter.py +103 -26
  134. package/src/shared/scripts/skf-validate-output.py +712 -64
  135. package/src/shared/scripts/skf-validate-pins.py +1 -1
  136. package/src/shared/scripts/skf-verify-no-trace.py +116 -10
  137. package/src/shared/scripts/skf-verify-provenance-completeness.py +2919 -54
  138. package/src/shared/scripts/skf-write-skill-brief.py +367 -19
  139. package/src/shared/tool-requirements.yaml +201 -0
  140. package/src/skf-analyze-source/SKILL.md +41 -36
  141. package/src/skf-analyze-source/assets/skill-brief-schema.md +7 -8
  142. package/src/skf-analyze-source/customize.toml +36 -24
  143. package/src/skf-analyze-source/references/auto-docs-only.md +58 -20
  144. package/src/skf-analyze-source/references/continue.md +2 -13
  145. package/src/skf-analyze-source/references/discover-additional-source.md +147 -0
  146. package/src/skf-analyze-source/references/generate-briefs.md +121 -55
  147. package/src/skf-analyze-source/references/headless-contract.md +42 -15
  148. package/src/skf-analyze-source/references/identify-units.md +96 -110
  149. package/src/skf-analyze-source/references/init.md +73 -71
  150. package/src/skf-analyze-source/references/map-and-detect.md +123 -105
  151. package/src/skf-analyze-source/references/map-unit-exports.md +68 -0
  152. package/src/skf-analyze-source/references/recommend.md +51 -70
  153. package/src/skf-analyze-source/references/scan-project.md +60 -84
  154. package/src/skf-analyze-source/references/scan-root.md +39 -0
  155. package/src/skf-analyze-source/references/step-auto-scope-coexistence.md +47 -0
  156. package/src/skf-analyze-source/references/step-auto-scope-corpora.md +53 -0
  157. package/src/skf-analyze-source/references/step-auto-scope-split.md +91 -0
  158. package/src/skf-analyze-source/references/step-auto-scope.md +208 -293
  159. package/src/skf-analyze-source/references/step-shape-detect.md +14 -12
  160. package/src/skf-analyze-source/references/unit-detection-heuristics.md +37 -42
  161. package/src/skf-audit-skill/SKILL.md +43 -51
  162. package/src/skf-audit-skill/assets/drift-report-template.md +27 -3
  163. package/src/skf-audit-skill/customize.toml +41 -20
  164. package/src/skf-audit-skill/references/constituent-freshness.md +91 -0
  165. package/src/skf-audit-skill/references/doc-drift.md +141 -0
  166. package/src/skf-audit-skill/references/headless-contract.md +29 -0
  167. package/src/skf-audit-skill/references/init.md +148 -210
  168. package/src/skf-audit-skill/references/re-index.md +96 -108
  169. package/src/skf-audit-skill/references/report.md +96 -33
  170. package/src/skf-audit-skill/references/semantic-diff.md +27 -18
  171. package/src/skf-audit-skill/references/severity-classify.md +79 -65
  172. package/src/skf-audit-skill/references/severity-rules.md +15 -6
  173. package/src/skf-audit-skill/references/structural-diff.md +75 -90
  174. package/src/skf-audit-skill/references/upstream-checkout.md +77 -0
  175. package/src/skf-audit-skill/scripts/render-drift-tables.py +685 -0
  176. package/src/skf-brief-skill/SKILL.md +36 -25
  177. package/src/skf-brief-skill/assets/description-voice-examples.md +1 -1
  178. package/src/skf-brief-skill/assets/scope-templates.md +17 -24
  179. package/src/skf-brief-skill/assets/skill-brief-schema.md +29 -15
  180. package/src/skf-brief-skill/customize.toml +48 -20
  181. package/src/skf-brief-skill/references/analyze-target.md +142 -125
  182. package/src/skf-brief-skill/references/confirm-brief.md +10 -8
  183. package/src/skf-brief-skill/references/draft-checkpoint.md +36 -17
  184. package/src/skf-brief-skill/references/gather-intent-ratify.md +68 -0
  185. package/src/skf-brief-skill/references/gather-intent.md +87 -182
  186. package/src/skf-brief-skill/references/headless-args.md +84 -16
  187. package/src/skf-brief-skill/references/health-check.md +1 -1
  188. package/src/skf-brief-skill/references/invocation-contract.md +18 -13
  189. package/src/skf-brief-skill/references/portfolio-similarity-check.md +1 -1
  190. package/src/skf-brief-skill/references/qmd-collection-registration.md +7 -10
  191. package/src/skf-brief-skill/references/scope-definition.md +123 -47
  192. package/src/skf-brief-skill/references/step-auto-brief.md +24 -80
  193. package/src/skf-brief-skill/references/step-auto-validate.md +20 -121
  194. package/src/skf-brief-skill/references/version-resolution.md +3 -2
  195. package/src/skf-brief-skill/references/write-brief.md +66 -57
  196. package/src/skf-campaign/SKILL.md +31 -34
  197. package/src/skf-campaign/assets/campaign-state-schema.json +25 -4
  198. package/src/skf-campaign/customize.toml +47 -16
  199. package/src/skf-campaign/references/campaign-contracts.md +39 -29
  200. package/src/skf-campaign/references/campaign-directive-spec.md +4 -2
  201. package/src/skf-campaign/references/health-check.md +7 -16
  202. package/src/skf-campaign/references/step-01-setup.md +46 -84
  203. package/src/skf-campaign/references/step-02-strategy.md +31 -30
  204. package/src/skf-campaign/references/step-03-pins.md +26 -18
  205. package/src/skf-campaign/references/step-04-provenance.md +24 -23
  206. package/src/skf-campaign/references/step-05-skill-loop.md +67 -48
  207. package/src/skf-campaign/references/step-06-batch.md +41 -34
  208. package/src/skf-campaign/references/step-07-capstone.md +40 -25
  209. package/src/skf-campaign/references/step-08-verify.md +28 -29
  210. package/src/skf-campaign/references/step-09-refine.md +29 -30
  211. package/src/skf-campaign/references/step-10-export.md +48 -36
  212. package/src/skf-campaign/references/step-11-maintenance.md +31 -34
  213. package/src/skf-campaign/references/step-resume.md +37 -68
  214. package/src/skf-campaign/scripts/campaign-deps.py +57 -18
  215. package/src/skf-campaign/scripts/campaign-parse-manifest.py +361 -54
  216. package/src/skf-campaign/scripts/campaign-provenance.py +96 -53
  217. package/src/skf-campaign/scripts/campaign-quality-gate.py +592 -0
  218. package/src/skf-campaign/scripts/campaign-render-batch.py +257 -49
  219. package/src/skf-campaign/scripts/campaign-render-kickoff.py +343 -50
  220. package/src/skf-campaign/scripts/campaign-report.py +204 -37
  221. package/src/skf-campaign/scripts/campaign-state.py +1128 -0
  222. package/src/skf-campaign/scripts/campaign-status.py +10 -74
  223. package/src/skf-campaign/scripts/campaign-validate-pins.py +30 -3
  224. package/src/skf-campaign/scripts/campaign-validate-state.py +34 -4
  225. package/src/skf-campaign/templates/campaign-brief-template.yaml +4 -5
  226. package/src/skf-campaign/templates/campaign-report-template.md +5 -1
  227. package/src/skf-campaign/templates/kickoff-template.md +0 -14
  228. package/src/skf-create-skill/SKILL.md +27 -20
  229. package/src/skf-create-skill/assets/compile-assembly-rules.md +6 -5
  230. package/src/skf-create-skill/assets/skill-sections.md +17 -6
  231. package/src/skf-create-skill/customize.toml +20 -8
  232. package/src/skf-create-skill/references/authoritative-files-protocol.md +41 -42
  233. package/src/skf-create-skill/references/batch-mode.md +81 -0
  234. package/src/skf-create-skill/references/ccc-index-check.md +27 -0
  235. package/src/skf-create-skill/references/compile.md +107 -29
  236. package/src/skf-create-skill/references/component-extraction.md +113 -91
  237. package/src/skf-create-skill/references/enrich.md +41 -23
  238. package/src/skf-create-skill/references/entry-points-by-hand.md +20 -0
  239. package/src/skf-create-skill/references/extract.md +200 -161
  240. package/src/skf-create-skill/references/extraction-patterns-by-hand.md +141 -0
  241. package/src/skf-create-skill/references/extraction-patterns-tracing.md +2 -2
  242. package/src/skf-create-skill/references/extraction-patterns.md +584 -196
  243. package/src/skf-create-skill/references/generate-artifacts.md +145 -93
  244. package/src/skf-create-skill/references/health-check.md +1 -1
  245. package/src/skf-create-skill/references/load-brief.md +53 -36
  246. package/src/skf-create-skill/references/relabel-rule.md +12 -0
  247. package/src/skf-create-skill/references/report.md +28 -59
  248. package/src/skf-create-skill/references/source-resolution-protocols.md +49 -155
  249. package/src/skf-create-skill/references/step-auto-shard.md +20 -93
  250. package/src/skf-create-skill/references/step-doc-rot.md +44 -60
  251. package/src/skf-create-skill/references/step-doc-sources.md +55 -18
  252. package/src/skf-create-skill/references/sub/ccc-discover.md +18 -7
  253. package/src/skf-create-skill/references/sub/fetch-docs.md +106 -57
  254. package/src/skf-create-skill/references/sub/fetch-temporal.md +53 -166
  255. package/src/skf-create-skill/references/tier-degradation-rules.md +18 -9
  256. package/src/skf-create-skill/references/validate.md +173 -99
  257. package/src/skf-create-skill/scripts/scan-doc-rot.py +232 -99
  258. package/src/skf-create-stack-skill/SKILL.md +29 -56
  259. package/src/skf-create-stack-skill/assets/metadata-contract.md +51 -0
  260. package/src/skf-create-stack-skill/assets/provenance-map-schema.md +15 -15
  261. package/src/skf-create-stack-skill/assets/stack-skill-template.md +23 -57
  262. package/src/skf-create-stack-skill/customize.toml +23 -12
  263. package/src/skf-create-stack-skill/references/compile-stack.md +57 -66
  264. package/src/skf-create-stack-skill/references/compose-mode-rules.md +24 -44
  265. package/src/skf-create-stack-skill/references/detect-integrations.md +92 -95
  266. package/src/skf-create-stack-skill/references/detect-manifests.md +46 -71
  267. package/src/skf-create-stack-skill/references/generate-output.md +138 -128
  268. package/src/skf-create-stack-skill/references/init.md +90 -32
  269. package/src/skf-create-stack-skill/references/integration-patterns.md +1 -17
  270. package/src/skf-create-stack-skill/references/invocation-contract.md +34 -0
  271. package/src/skf-create-stack-skill/references/manifest-patterns.md +32 -20
  272. package/src/skf-create-stack-skill/references/parallel-extract.md +112 -38
  273. package/src/skf-create-stack-skill/references/rank-and-confirm.md +57 -75
  274. package/src/skf-create-stack-skill/references/report.md +39 -33
  275. package/src/skf-create-stack-skill/references/validate.md +20 -39
  276. package/src/skf-drop-skill/SKILL.md +28 -51
  277. package/src/skf-drop-skill/customize.toml +44 -30
  278. package/src/skf-drop-skill/references/execute.md +79 -146
  279. package/src/skf-drop-skill/references/invocation-contract.md +64 -0
  280. package/src/skf-drop-skill/references/report.md +46 -30
  281. package/src/skf-drop-skill/references/select.md +118 -124
  282. package/src/skf-drop-skill/scripts/dir-sizes.py +54 -26
  283. package/src/skf-drop-skill/scripts/drop-roster.py +322 -0
  284. package/src/skf-export-skill/SKILL.md +40 -62
  285. package/src/skf-export-skill/assets/managed-section-format.md +14 -50
  286. package/src/skf-export-skill/assets/snippet-format.md +3 -1
  287. package/src/skf-export-skill/customize.toml +42 -13
  288. package/src/skf-export-skill/references/generate-snippet.md +54 -46
  289. package/src/skf-export-skill/references/health-check.md +2 -0
  290. package/src/skf-export-skill/references/invocation-contract.md +15 -0
  291. package/src/skf-export-skill/references/load-skill.md +88 -62
  292. package/src/skf-export-skill/references/manifest-rebuild.md +9 -10
  293. package/src/skf-export-skill/references/multi-skill-mode.md +4 -4
  294. package/src/skf-export-skill/references/orphan-context-detection.md +13 -5
  295. package/src/skf-export-skill/references/orphan-row-detection.md +31 -22
  296. package/src/skf-export-skill/references/package.md +15 -73
  297. package/src/skf-export-skill/references/preflight-snippet-root-probe.md +51 -70
  298. package/src/skf-export-skill/references/result-envelope.md +31 -16
  299. package/src/skf-export-skill/references/summary.md +41 -17
  300. package/src/skf-export-skill/references/token-report.md +4 -12
  301. package/src/skf-export-skill/references/update-context.md +118 -237
  302. package/src/skf-forger/SKILL.md +61 -37
  303. package/src/skf-forger/bmad-skill-manifest.yaml +4 -1
  304. package/src/skf-forger/customize.toml +43 -0
  305. package/src/skf-forger/references/pipeline-mode.md +73 -18
  306. package/src/skf-forger/scripts/forge-status.py +299 -0
  307. package/src/skf-forger/scripts/parse-pipeline.py +322 -54
  308. package/src/skf-forger/scripts/pipeline-gate.py +428 -0
  309. package/src/skf-forger/scripts/pipeline-journal.py +1052 -0
  310. package/src/skf-quick-skill/SKILL.md +54 -53
  311. package/src/skf-quick-skill/assets/skill-template.md +4 -2
  312. package/src/skf-quick-skill/customize.toml +51 -20
  313. package/src/skf-quick-skill/references/batch-mode.md +74 -21
  314. package/src/skf-quick-skill/references/compile.md +30 -31
  315. package/src/skf-quick-skill/references/ecosystem-check.md +7 -57
  316. package/src/skf-quick-skill/references/finalize.md +69 -18
  317. package/src/skf-quick-skill/references/halt-contract.md +41 -29
  318. package/src/skf-quick-skill/references/health-check.md +5 -3
  319. package/src/skf-quick-skill/references/quick-extract.md +114 -45
  320. package/src/skf-quick-skill/references/registry-resolution.md +9 -64
  321. package/src/skf-quick-skill/references/resolve-target.md +116 -66
  322. package/src/skf-quick-skill/references/write-and-validate.md +79 -40
  323. package/src/skf-refine-architecture/SKILL.md +43 -53
  324. package/src/skf-refine-architecture/customize.toml +49 -20
  325. package/src/skf-refine-architecture/references/compile.md +97 -54
  326. package/src/skf-refine-architecture/references/exit-codes.md +30 -0
  327. package/src/skf-refine-architecture/references/finding-storage.md +21 -0
  328. package/src/skf-refine-architecture/references/gap-analysis.md +101 -38
  329. package/src/skf-refine-architecture/references/improvements.md +14 -20
  330. package/src/skf-refine-architecture/references/init.md +98 -57
  331. package/src/skf-refine-architecture/references/issue-detection.md +32 -39
  332. package/src/skf-refine-architecture/references/refinement-rules.md +37 -48
  333. package/src/skf-refine-architecture/references/report.md +65 -54
  334. package/src/skf-refine-architecture/scripts/skf-check-preservation.py +1619 -0
  335. package/src/skf-rename-skill/SKILL.md +19 -54
  336. package/src/skf-rename-skill/customize.toml +32 -26
  337. package/src/skf-rename-skill/references/execute.md +230 -184
  338. package/src/skf-rename-skill/references/exit-codes.md +7 -7
  339. package/src/skf-rename-skill/references/health-check.md +1 -7
  340. package/src/skf-rename-skill/references/invocation-contract.md +25 -0
  341. package/src/skf-rename-skill/references/report.md +63 -52
  342. package/src/skf-rename-skill/references/select.md +197 -115
  343. package/src/skf-rename-skill/scripts/skf-validate-rename-name.py +161 -21
  344. package/src/skf-setup/SKILL.md +26 -26
  345. package/src/skf-setup/customize.toml +14 -10
  346. package/src/skf-setup/references/auto-index.md +50 -63
  347. package/src/skf-setup/references/ccc-index.md +29 -89
  348. package/src/skf-setup/references/detect-and-tier.md +58 -66
  349. package/src/skf-setup/references/health-check.md +2 -0
  350. package/src/skf-setup/references/invocation-contract.md +14 -0
  351. package/src/skf-setup/references/report.md +43 -166
  352. package/src/skf-setup/references/tier-rules.md +5 -5
  353. package/src/skf-setup/references/write-config.md +34 -57
  354. package/src/skf-test-skill/SKILL.md +17 -57
  355. package/src/skf-test-skill/assets/output-section-formats.md +33 -34
  356. package/src/skf-test-skill/customize.toml +27 -13
  357. package/src/skf-test-skill/references/coherence-check.md +134 -62
  358. package/src/skf-test-skill/references/coverage-check-tiers.md +35 -0
  359. package/src/skf-test-skill/references/coverage-check.md +217 -254
  360. package/src/skf-test-skill/references/detect-mode.md +5 -5
  361. package/src/skf-test-skill/references/external-validators.md +94 -82
  362. package/src/skf-test-skill/references/health-check.md +6 -7
  363. package/src/skf-test-skill/references/init.md +163 -88
  364. package/src/skf-test-skill/references/invocation-contract.md +66 -0
  365. package/src/skf-test-skill/references/migration-section-rules.md +5 -2
  366. package/src/skf-test-skill/references/report.md +162 -142
  367. package/src/skf-test-skill/references/score.md +80 -107
  368. package/src/skf-test-skill/references/scoring-rules.md +41 -155
  369. package/src/skf-test-skill/references/source-access-protocol.md +19 -92
  370. package/src/skf-test-skill/references/step-hard-gate.md +51 -33
  371. package/src/skf-test-skill/scripts/aggregate-coherence.py +218 -30
  372. package/src/skf-test-skill/scripts/build-result-context.py +287 -0
  373. package/src/skf-test-skill/scripts/check-metadata-coherence.py +37 -4
  374. package/src/skf-test-skill/scripts/combine-external-scores.py +23 -6
  375. package/src/skf-test-skill/scripts/compute-score.py +223 -45
  376. package/src/skf-test-skill/scripts/gap-ledger.py +1381 -0
  377. package/src/skf-test-skill/scripts/hard-gate.py +203 -0
  378. package/src/skf-test-skill/scripts/load-coverage-inputs.py +760 -0
  379. package/src/skf-test-skill/scripts/locate-export-segments.py +835 -0
  380. package/src/skf-test-skill/scripts/reconcile-coverage.py +273 -79
  381. package/src/skf-test-skill/scripts/score-signatures.py +459 -0
  382. package/src/skf-test-skill/scripts/stage-helper-payload.py +148 -0
  383. package/src/skf-test-skill/scripts/validate-inventory.py +311 -16
  384. package/src/skf-test-skill/scripts/verify-declared-numerator.py +127 -52
  385. package/src/skf-test-skill/templates/test-report-template.md +21 -15
  386. package/src/skf-update-skill/SKILL.md +38 -25
  387. package/src/skf-update-skill/customize.toml +27 -12
  388. package/src/skf-update-skill/references/detect-changes.md +233 -222
  389. package/src/skf-update-skill/references/gap-driven.md +336 -0
  390. package/src/skf-update-skill/references/health-check.md +31 -5
  391. package/src/skf-update-skill/references/init.md +250 -84
  392. package/src/skf-update-skill/references/invocation-contract.md +12 -0
  393. package/src/skf-update-skill/references/manual-section-rules.md +6 -23
  394. package/src/skf-update-skill/references/merge.md +180 -47
  395. package/src/skf-update-skill/references/re-extract.md +53 -262
  396. package/src/skf-update-skill/references/report.md +100 -35
  397. package/src/skf-update-skill/references/write.md +331 -149
  398. package/src/skf-update-skill/scripts/skf-new-file-diff.py +29 -7
  399. package/src/skf-verify-stack/SKILL.md +23 -42
  400. package/src/skf-verify-stack/assets/feasibility-report-template.md +11 -5
  401. package/src/skf-verify-stack/customize.toml +55 -22
  402. package/src/skf-verify-stack/references/coverage-patterns.md +17 -8
  403. package/src/skf-verify-stack/references/coverage.md +67 -64
  404. package/src/skf-verify-stack/references/exit-codes.md +21 -8
  405. package/src/skf-verify-stack/references/init.md +107 -62
  406. package/src/skf-verify-stack/references/integration-verification-rules.md +12 -13
  407. package/src/skf-verify-stack/references/integrations.md +106 -65
  408. package/src/skf-verify-stack/references/report.md +59 -42
  409. package/src/skf-verify-stack/references/requirements.md +32 -9
  410. package/src/skf-verify-stack/references/synthesize.md +53 -39
  411. package/src/skf-verify-stack/scripts/skf-coverage-tally.py +398 -52
  412. package/src/skf-verify-stack/scripts/skf-previous-report.py +330 -0
  413. package/src/skf-verify-stack/scripts/skf-report-delta.py +453 -49
  414. package/src/skf-verify-stack/scripts/skf-verdict-rollup.py +240 -27
  415. package/tools/cli/commands/install.js +11 -4
  416. package/tools/cli/commands/status.js +15 -3
  417. package/tools/cli/commands/update.js +11 -4
  418. package/tools/cli/lib/tool-check.js +320 -0
  419. package/tools/cli/lib/version-check.js +22 -9
  420. package/tools/cli/skf-cli.js +4 -1
  421. package/.gitattributes +0 -16
  422. package/.nvmrc +0 -1
  423. package/docs/_internal/RELEASING.md +0 -645
  424. package/release-audits/v1.0.0-launch-audit.md +0 -728
  425. package/src/skf-audit-skill/references/step-doc-drift.md +0 -177
  426. package/src/skf-brief-skill/references/headless-source-authority-detection.md +0 -26
  427. package/src/skf-campaign/manifest.yaml +0 -15
  428. package/src/skf-create-skill/assets/tessl-dismissal-rules.md +0 -106
  429. package/src/skf-create-skill/references/ecosystem-check.md +0 -74
  430. package/src/skf-drop-skill/references/headless-contract.md +0 -18
  431. package/src/skf-update-skill/references/merge-conflict-rules.md +0 -15
  432. package/src/skf-update-skill/references/remote-source-resolution.md +0 -98
  433. package/src/skf-update-skill/references/validate.md +0 -124
  434. package/tools/validate-docs-drift.js +0 -233
  435. package/tools/validate-docs-links.js +0 -665
@@ -0,0 +1,322 @@
1
+ #!/usr/bin/env python3
2
+ # /// script
3
+ # requires-python = ">=3.11"
4
+ # dependencies = []
5
+ # ///
6
+ """Drop Skill roster: the two lists a drop reads, each from one call.
7
+
8
+ skills <skills-folder> --forge-data-folder <path> [--skill <name>]
9
+ select.md section 2: every skill the drop can offer (section 3 lists
10
+ it). It joins the export manifest, read through skf-manifest-ops.py
11
+ (which migrates a v1 manifest), with the on-disk scan of
12
+ skf-skill-inventory.py: each manifest skill, and each skill SKF
13
+ generated that the manifest does not list (a draft, which only a purge
14
+ can drop). A folder holding a skill SKF did not generate is never
15
+ offered: it is listed in `not_offered`, with the scan's errors for it.
16
+ Each skill carries its versions newest first, in the order
17
+ skf-skill-inventory.py resolve gives them (numeric, so 0.10.0 comes
18
+ before 0.9.0), each with its manifest status, last export date and
19
+ IDEs, and the counts the active-version guard and the blast-radius
20
+ line read. With --skill the roster holds that skill only, so a run
21
+ given its target reads one skill rather than every one; `empty` and
22
+ `not_offered` still cover the whole folder.
23
+
24
+ Without skf-skill-inventory.py (an older or broken install), or when
25
+ its scan or resolve fails, `inventory` is false and the roster holds
26
+ the manifest skills alone: SKF cannot check that it generated a folder
27
+ the manifest does not list, so none is offered.
28
+
29
+ rows --skill <name> [--version <v>]... <context-file>...
30
+ execute.md section 5: the managed-section rows the context files still
31
+ hold for a skill (for those versions only, with --version), as
32
+ {file, version}, read with the parser skf-rebuild-managed-sections.py
33
+ writes the section with. A row's snippet text never comes back. A file
34
+ whose markers are malformed, or that cannot be read, is listed in
35
+ `unchecked` with the reason: its rows are unknown. A missing file holds
36
+ no row.
37
+
38
+ Both shared scripts are read from the shared scripts folder beside this
39
+ skill's folder (shared/scripts/), where the installer puts them in an
40
+ installed project as in this checkout.
41
+
42
+ Output (stdout, one JSON object):
43
+
44
+ skills: {"status": "ok", "inventory": bool, "inventory_error": str|null,
45
+ "skills": [{"name", "in_manifest", "purge_only", "active_version",
46
+ "layout": "versioned"|"flat"|"none"|null,
47
+ "versions": [{"version", "status", "active",
48
+ "in_manifest", "on_disk",
49
+ "last_exported", "ides"}],
50
+ "counts": {"non_deprecated", "on_disk"}}],
51
+ "not_offered": [{"name", "errors"}], "empty": bool}
52
+ rows: {"status": "ok", "skill", "versions": [...]|null,
53
+ "rows": [{"file", "version"}], "unchecked": [{"file", "error"}]}
54
+
55
+ `status` of a version is its manifest status, null for a folder the manifest
56
+ does not list; `active` marks the manifest's active_version. `counts` gives
57
+ `non_deprecated` (the manifest versions whose status is not "deprecated")
58
+ and `on_disk` (the version folders; null without the inventory helper).
59
+
60
+ Exit codes:
61
+ 0 status "ok"
62
+ 1 status "error", with a `code`: manifest-corrupt (the export manifest
63
+ exists but does not parse, is not UTF-8 or is not an object of entry
64
+ objects; `path` names it) or helper-missing (a shared script this one
65
+ reads cannot be loaded)
66
+ 2 usage error (JSON on stderr)
67
+ """
68
+ from __future__ import annotations
69
+
70
+ import argparse
71
+ import importlib.util
72
+ import json
73
+ import sys
74
+ from pathlib import Path
75
+
76
+ # Installed (under _bmad/skf/ or an IDE's skills folder) and in a dev
77
+ # checkout (src/), shared/ sits beside this skill's folder.
78
+ SHARED_SCRIPTS = Path(__file__).resolve().parent.parent.parent / "shared" / "scripts"
79
+ MANIFEST_OPS = "skf-manifest-ops.py"
80
+ INVENTORY = "skf-skill-inventory.py"
81
+ REBUILD = "skf-rebuild-managed-sections.py"
82
+ MANIFEST_FILE = ".export-manifest.json"
83
+ # What a shared helper raises on a file it cannot read as it expects (a
84
+ # manifest of the wrong shape or encoding, a folder it cannot list).
85
+ HELPER_ERRORS = (AttributeError, TypeError, ValueError, OSError)
86
+
87
+
88
+ def _load(filename: str):
89
+ """(module, None) for a shared script, or (None, the reason it cannot load)."""
90
+ path = SHARED_SCRIPTS / filename
91
+ try:
92
+ spec = importlib.util.spec_from_file_location(filename[:-3].replace("-", "_"), path)
93
+ module = importlib.util.module_from_spec(spec)
94
+ spec.loader.exec_module(module)
95
+ except (ImportError, OSError, AttributeError, SyntaxError) as e:
96
+ return None, f"cannot load {filename} from {SHARED_SCRIPTS} ({e}): re-install SKF"
97
+ return module, None
98
+
99
+
100
+ def _plain_name(value) -> bool:
101
+ """True for a name that is one folder name and nothing else."""
102
+ return isinstance(value, str) and value not in ("", ".", "..") and not any(c in value for c in "/\\:\0")
103
+
104
+
105
+ def _manifest_rows(entry) -> tuple[str | None, dict]:
106
+ """(active_version, {version: {status, last_exported, ides}}) of a migrated manifest entry."""
107
+ if not isinstance(entry, dict):
108
+ return None, {}
109
+ active = entry.get("active_version")
110
+ active = active if isinstance(active, str) and active else None
111
+ versions = entry.get("versions")
112
+ records = {}
113
+ if isinstance(versions, dict):
114
+ for version, record in versions.items():
115
+ record = record if isinstance(record, dict) else {}
116
+ status = record.get("status")
117
+ last = record.get("last_exported")
118
+ ides = record.get("ides")
119
+ records[version] = {
120
+ "status": status if isinstance(status, str) else None,
121
+ "last_exported": last if isinstance(last, str) and last else None,
122
+ "ides": [i for i in ides if isinstance(i, str)] if isinstance(ides, list) else [],
123
+ }
124
+ return active, records
125
+
126
+
127
+ def _version_row(version: str, active: str | None, records: dict, on_disk: bool) -> dict:
128
+ record = records.get(version, {})
129
+ return {
130
+ "version": version,
131
+ "status": record.get("status"),
132
+ "active": version == active,
133
+ "in_manifest": version in records,
134
+ "on_disk": on_disk,
135
+ "last_exported": record.get("last_exported"),
136
+ "ides": record.get("ides", []),
137
+ }
138
+
139
+
140
+ def _skill(name, entry, ops, manifest_path, inventory, skills_folder, forge_folder) -> dict:
141
+ """One roster entry: the skill's versions newest first, with their manifest records and counts."""
142
+ active, records = _manifest_rows(entry)
143
+ in_manifest = entry is not None
144
+ resolved = None
145
+ if inventory is not None and _plain_name(name):
146
+ resolved = inventory.resolve_skill(skills_folder, name, forge_folder)
147
+ if resolved is not None:
148
+ rows = [_version_row(r["version"], active, records, r["on_disk"]) for r in resolved["versions"]]
149
+ layout = resolved["layout"]
150
+ else:
151
+ # No on-disk scan: the manifest's versions, in the manifest helper's order.
152
+ ordered = ops.cmd_affected_versions(manifest_path, skills_folder, name).get("sources", {}).get("manifest", [])
153
+ rows = [_version_row(v, active, records, False) for v in ordered if v in records]
154
+ layout = None
155
+ return {
156
+ "name": name,
157
+ "in_manifest": in_manifest,
158
+ "purge_only": not in_manifest,
159
+ "active_version": active,
160
+ "layout": layout,
161
+ "versions": rows,
162
+ "counts": {
163
+ "non_deprecated": sum(1 for r in rows if r["in_manifest"] and r["status"] != "deprecated"),
164
+ "on_disk": sum(1 for r in rows if r["on_disk"]) if resolved is not None else None,
165
+ },
166
+ }
167
+
168
+
169
+ def _read_manifest(ops, manifest_path: Path) -> tuple[dict | None, str | None]:
170
+ """(data, None), or (None, why the manifest cannot be read as one).
171
+
172
+ The manifest helper reports a file that is not JSON; a JSON value of the
173
+ wrong shape (a list, or an entry that is not an object) or a file that is
174
+ not UTF-8 makes it raise instead, which is the same corrupt manifest.
175
+ """
176
+ try:
177
+ data, err = ops.read_manifest(manifest_path)
178
+ except HELPER_ERRORS as e:
179
+ return None, f"Manifest cannot be read: {type(e).__name__}: {e}"
180
+ if err:
181
+ return None, err
182
+ if not isinstance(data, dict) or not isinstance(data.get("exports", {}), dict):
183
+ return None, "Manifest is not an object with an `exports` object"
184
+ return data, None
185
+
186
+
187
+ def roster(skills_folder: str, forge_folder: str, only: str | None = None) -> tuple[dict, int]:
188
+ """The `skills` result and its exit code."""
189
+ ops, err = _load(MANIFEST_OPS)
190
+ if ops is None:
191
+ return {"status": "error", "code": "helper-missing", "error": err}, 1
192
+ manifest_path = Path(skills_folder) / MANIFEST_FILE
193
+ data, err = _read_manifest(ops, manifest_path)
194
+ if err:
195
+ return {"status": "error", "code": "manifest-corrupt", "error": err, "path": str(manifest_path)}, 1
196
+ exports = data.get("exports", {})
197
+
198
+ inventory, inventory_error = _load(INVENTORY)
199
+ drafts, not_offered = [], []
200
+ if inventory is not None:
201
+ try:
202
+ scan = inventory.scan_inventory(skills_folder, forge_data_folder=forge_folder)
203
+ except HELPER_ERRORS as e:
204
+ scan = {"status": "error", "error": f"the inventory scan failed: {type(e).__name__}: {e}"}
205
+ if scan.get("status") == "ok":
206
+ for found in scan["skills"]:
207
+ if found.get("skf_skill") and found["name"] not in exports:
208
+ drafts.append(found["name"])
209
+ errors = {found["name"]: found.get("errors", []) for found in scan["skills"]}
210
+ not_offered = [{"name": name, "errors": errors.get(name, [])}
211
+ for name in scan.get("not_skf_output", []) if name not in exports]
212
+ elif scan.get("code") != "DIR_NOT_FOUND": # a missing folder holds no skill
213
+ inventory, inventory_error = None, scan.get("error", "the inventory scan failed")
214
+
215
+ names = sorted(exports) + sorted(drafts)
216
+ listed = [n for n in names if n == only] if only is not None else names
217
+ try:
218
+ skills = [_skill(name, exports.get(name), ops, manifest_path, inventory, skills_folder, forge_folder)
219
+ for name in listed]
220
+ except HELPER_ERRORS as e:
221
+ if inventory is None:
222
+ raise
223
+ # A resolve that fails is a scan that failed: the manifest skills alone, as without the helper.
224
+ inventory, inventory_error = None, f"the inventory resolve failed: {type(e).__name__}: {e}"
225
+ drafts, not_offered = [], []
226
+ names = sorted(exports)
227
+ listed = [n for n in names if n == only] if only is not None else names
228
+ skills = [_skill(name, exports.get(name), ops, manifest_path, None, skills_folder, forge_folder)
229
+ for name in listed]
230
+ return {
231
+ "status": "ok",
232
+ "inventory": inventory is not None,
233
+ "inventory_error": None if inventory is not None else inventory_error,
234
+ "skills": skills,
235
+ "not_offered": not_offered,
236
+ "empty": not names,
237
+ }, 0
238
+
239
+
240
+ def rows(skill: str, versions: list[str], context_files: list[str]) -> tuple[dict, int]:
241
+ """The `rows` result and its exit code."""
242
+ rebuild, err = _load(REBUILD)
243
+ if rebuild is None:
244
+ return {"status": "error", "code": "helper-missing", "error": err}, 1
245
+ found, unchecked = [], []
246
+ for path in context_files:
247
+ content, err = rebuild.read_context_file(path)
248
+ if err:
249
+ if not rebuild.is_missing(err):
250
+ unchecked.append({"file": path, "error": err})
251
+ continue
252
+ problem = rebuild.find_unclosed_begin(content)
253
+ if problem:
254
+ unchecked.append({"file": path, "error": f"malformed markers: {problem[1]}"})
255
+ continue
256
+ match = rebuild.find_managed_section(content)
257
+ if not match:
258
+ continue
259
+ for row in rebuild.parse_managed_rows(match.group(2)):
260
+ if row["skill_name"] == skill and (not versions or row["version"] in versions):
261
+ found.append({"file": path, "version": row["version"]})
262
+ return {"status": "ok", "skill": skill, "versions": versions or None, "rows": found,
263
+ "unchecked": unchecked}, 0
264
+
265
+
266
+ class _JsonErrorParser(argparse.ArgumentParser):
267
+ """Report a usage error as JSON on stderr with exit 2, the subcommands included."""
268
+
269
+ def error(self, message: str) -> None:
270
+ print(json.dumps({"status": "error", "error": f"usage error: {self.prog}: {message}"}), file=sys.stderr)
271
+ sys.exit(2)
272
+
273
+
274
+ def _build_parser() -> argparse.ArgumentParser:
275
+ parser = _JsonErrorParser(
276
+ prog="drop-roster.py",
277
+ description=__doc__,
278
+ formatter_class=argparse.RawDescriptionHelpFormatter,
279
+ )
280
+ sub = parser.add_subparsers(dest="op", required=True, metavar="{skills,rows}")
281
+
282
+ p_skills = sub.add_parser("skills", help="the skills a drop can offer, with their versions")
283
+ p_skills.add_argument("skills_folder", metavar="skills-folder", help="{skills_output_folder}")
284
+ p_skills.add_argument("--forge-data-folder", required=True, help="{forge_data_folder}")
285
+ p_skills.add_argument("--skill", help="list this skill only (the name the run was given)")
286
+
287
+ p_rows = sub.add_parser("rows", help="the managed-section rows the context files hold for a skill")
288
+ p_rows.add_argument("--skill", required=True, help="the skill whose rows to list")
289
+ p_rows.add_argument("--version", action="append", default=[], dest="versions",
290
+ help="list the rows of this version only (repeatable)")
291
+ p_rows.add_argument("context_files", nargs="+", metavar="context-file", help="a context file to read")
292
+ return parser
293
+
294
+
295
+ def main(argv: list[str] | None = None) -> int:
296
+ args = _build_parser().parse_args(argv)
297
+ if args.op == "skills":
298
+ result, code = roster(args.skills_folder, args.forge_data_folder, args.skill)
299
+ else:
300
+ result, code = rows(args.skill, args.versions, args.context_files)
301
+ print(json.dumps(result, indent=2))
302
+ return code
303
+
304
+
305
+ def _force_utf8(*streams) -> None:
306
+ """Reconfigure stdout and stderr to UTF-8, keeping each stream's error handler.
307
+
308
+ A Windows console pipes them as cp1252, which cannot print every character
309
+ a path may hold.
310
+ """
311
+ for stream in streams:
312
+ if hasattr(stream, "reconfigure"):
313
+ errors = getattr(stream, "errors", None)
314
+ if errors is None:
315
+ stream.reconfigure(encoding="utf-8")
316
+ else:
317
+ stream.reconfigure(encoding="utf-8", errors=errors)
318
+
319
+
320
+ if __name__ == "__main__":
321
+ _force_utf8(sys.stdout, sys.stderr)
322
+ sys.exit(main())
@@ -1,36 +1,37 @@
1
1
  ---
2
2
  name: skf-export-skill
3
- description: Package for distribution and inject context into CLAUDE.md/AGENTS.md/.cursorrules. Use when the user requests to "export" or "package a skill."
3
+ description: Exports an SKF skill, validating its package, writing its context snippet and updating the managed section in CLAUDE.md, AGENTS.md or .cursorrules. Use when the user requests to "export a skill" or "package a skill."
4
4
  ---
5
5
 
6
6
  # Export Skill
7
7
 
8
8
  ## Overview
9
9
 
10
- Packages a completed skill as an agentskills.io-compliant package, generates context snippets, and updates the managed section in CLAUDE.md/.cursorrules/AGENTS.md for platform-aware context injection. It is the sole publishing gate — create-skill/update-skill produce drafts; only export writes platform context files and distribution packages.
10
+ Checks a completed skill against the agentskills.io export gate, writes its context snippet and updates the managed section in CLAUDE.md, AGENTS.md or .cursorrules. It is the sole publishing gate: create-skill and update-skill produce drafts, and only export writes the context files and records the skill in `.export-manifest.json`.
11
11
 
12
12
  ## Conventions
13
13
 
14
14
  - Bare paths (e.g. `references/<name>.md`) resolve from the skill root.
15
- - **Module-level path exception:** bare `knowledge/` and `shared/` paths resolve from the SKF module root (`{project-root}/_bmad/skf/` installed, `src/` in dev), not the skill root (e.g. `knowledge/version-paths.md`, `shared/health-check.md`).
16
- - `references/` holds prompt content carved out of SKILL.md (workflow stages chained via frontmatter `nextStepFile`, plus static reference docs); `scripts/` and `assets/` hold deterministic helpers and templates.
15
+ - **Module-level path exception:** bare paths beginning with `knowledge/` or `shared/` resolve from the SKF module root (`{project-root}/_bmad/skf/` installed, `src/` in dev), not the skill root; for example `knowledge/version-paths.md` and `shared/health-check.md`.
16
+ - **Sibling skills:** a path that names another SKF skill's folder (`skf-<name>/...`) resolves from the SKF module root, and that skill must be installed with this one.
17
+ - `references/` holds the workflow stages, chained by frontmatter `nextStepFile`; the files a stage loads when it needs them (`load-skill.md` loads `multi-skill-mode.md` and `preflight-snippet-root-probe.md`, `update-context.md` loads `orphan-context-detection.md`, `orphan-row-detection.md` and `manifest-rebuild.md`); and the headless contract (`invocation-contract.md`, `result-envelope.md`). `assets/` holds the snippet and managed-section templates.
17
18
  - `{skill-root}` resolves to this skill's installed directory (where `customize.toml` lives, if present).
18
19
  - `{project-root}`-prefixed paths resolve from the project working directory.
19
20
  - `{skill-name}` resolves to the skill directory's basename.
20
- - **Cross-skill data coupling:** `assets/managed-section-format.md` (loaded by drop-skill and rename-skill's `execute.md`) and `references/update-context.md` §4a (v2 manifest schema enforced by `skf-manifest-ops.py`) are the source of truth for those contracts — schema-breaking changes here need coordinated updates across those skills.
21
+ - **Cross-skill data coupling:** export-skill, drop-skill and rename-skill write the managed section only through `shared/scripts/skf-rebuild-managed-sections.py`, in the format `assets/managed-section-format.md` documents, and change `.export-manifest.json` only through `shared/scripts/skf-manifest-ops.py` (v2 schema in `references/manifest-rebuild.md`). A change to either contract changes all three skills.
21
22
 
22
23
  ## Role
23
24
 
24
- You are a delivery and packaging specialist collaborating with a skill developer, pairing your skill-packaging, ecosystem-compliance, and context-injection expertise with their completed skill and distribution requirements.
25
+ You are Ferris in Delivery mode, a packaging specialist: you check a developer's completed skill against the ecosystem rules and inject it into the context files it targets.
25
26
 
26
27
  ## Workflow Rules
27
28
 
28
- These rules apply to every step in this workflow:
29
-
30
29
  - Only load one step file at a time — never preload future steps
31
30
  - Always communicate in `{communication_language}`
32
31
  - At any interactive prompt, the inputs `cancel`, `exit`, `[X]`, `q`, or `:q` exit cleanly with exit code 6 (`halt_reason: "user-cancelled"`)
33
- - If `{headless_mode}` is true, auto-proceed through confirmation gates with their default action and log each auto-decision
32
+ - If `{headless_mode}` is true, auto-proceed through confirmation gates with their default action and record each auto-decision in the run sink (`references/result-envelope.md`)
33
+ - Every HALT names its exit code, `halt_reason` and phase; in headless mode it first emits its envelope as `references/result-envelope.md` states
34
+ - **Snippet timing:** step 3 only stages each `context-snippet.md` outside the project; step 4 §9c copies it into the skill package after its [C] gate, as step 4's last write. A cancel, a dry run or a halt before §9c leaves every snippet, and a carried gotchas line's one `[CARRIED]` cycle, as it was.
34
35
 
35
36
  ## Stages
36
37
 
@@ -46,77 +47,52 @@ These rules apply to every step in this workflow:
46
47
 
47
48
  ## Invocation Contract
48
49
 
49
- | Aspect | Detail |
50
- |--------|--------|
51
- | **Inputs** | `skill_name` [one or more, required unless `--all`] |
52
- | **Flags** | `--headless` / `-H` (auto-resolve all gates); `--all` (export every non-deprecated skill in `.export-manifest.json`); `--dry-run` (stage everything but write nothing — context files, manifest, and snippet are previewed only; the run completes read-only through the terminal step; `status="dry-run"`) |
53
- | **Gates** | step 1: single Confirm Gate [C] for the whole batch | step 4: single Confirm Gate [C] for the whole batch |
54
- | **Outputs** | Updated `.export-manifest.json` (every skill in the batch), updated context files (CLAUDE.md/AGENTS.md/.cursorrules), per-skill `context-snippet.md`, per-run result contract `export-skill-result-{timestamp}.json` and `export-skill-result-latest.json` |
55
- | **Multi-skill mode** | Activated when more than one skill is selected (via `--all`, multi-selection, or multi-argument invocation). See `references/load-skill.md` §1c for the per-step iteration map. |
56
- | **Headless** | All gates auto-resolve with default action when `{headless_mode}` is true. Each auto-resolved gate appends a `{gate, default_action, taken_action, reason}` entry to `headless_decisions[]`, surfaced in step 6's `SKF_EXPORT_RESULT_JSON` envelope so non-interactive runs can be audited post-hoc. |
57
- | **Exit codes** | See "Exit Codes" below |
58
-
59
- ## Exit Codes
60
-
61
- Every HARD HALT in this workflow exits with a stable code so headless automators can branch on the failure class without grepping message text:
62
-
63
- | Code | Meaning | Raised by (halt site → `halt_reason`) |
64
- | ---- | -------------------- | -------------------------------------------------------------------------------------------- |
65
- | 0 | success | step 7 (terminal); also `status="dry-run"` when `--dry-run` is set |
66
- | 2 | input-missing | step 1 §1 — headless run with no `skill_name` and no `--all` (a non-interactive run cannot answer the skill-selection menu) → `input-missing` |
67
- | 3 | resolution-failure | step 1 §1 (discovery finds no skills on disk / in the manifest); step 1 §2 (a named skill's required artifacts are missing or its metadata is invalid — export-gate FAIL); multi-skill batch (any skill failing §2 validation halts the whole batch) → `resolution-failure` |
68
- | 4 | write-failure | On-Activation §5 pre-flight write probe → `write-failed`; step 4 §3b / §9 / §4c.1 (managed-section create/append/rewrite/clear verify fails, or a required helper is unresolvable) → `context-rebuild-failed`; step 4 §9b manifest write → `manifest-write-failed` |
69
- | 5 | state-conflict | step 4 §6 — malformed `<!-- SKF:BEGIN/END -->` markers in the target context file (`<!-- SKF:BEGIN` present, `<!-- SKF:END -->` missing) → `malformed-markers` |
70
- | 6 | user-cancelled | step 1 §6 gate `[X]`/cancel; step 1 §1b snippet-root probe (a)/(c); step 4 §8 gate `[X]`/cancel; step 4 §4c.1 orphan-row (c) Cancel; any prompt accepting `cancel`/`exit`/`:q` → `user-cancelled` |
71
-
72
- ## Result Contract (Headless)
73
-
74
- When `{headless_mode}` is true, step 6 emits a single-line JSON envelope on **stdout** before chaining to step 7, and every HARD HALT emits the same envelope shape on **stderr** with `status: "error"`:
75
-
76
- ```
77
- SKF_EXPORT_RESULT_JSON: {"status":"success|error|dry-run","skills":[],"context_files_updated":[],"manifest_path":"…|null","headless_decisions":[],"exit_code":0,"halt_reason":null}
78
- ```
79
-
80
- `references/result-envelope.md` is the single source for the full field semantics and the `halt_reason` enum — halting stages load it directly, so the enum lives in exactly one place. `exit_code` matches the Exit Codes table above.
50
+ Headless callers and pipelines: the inputs, flags, gates, outputs and exit codes are in `references/invocation-contract.md`, and the `SKF_EXPORT_RESULT_JSON` envelope in `references/result-envelope.md`. Interactive runs do not need them.
81
51
 
82
52
  ## On Activation
83
53
 
84
54
  1. Load config from `{project-root}/_bmad/skf/config.yaml` and resolve:
85
55
  - `project_name`, `output_folder`, `user_name`, `communication_language`, `document_output_language`
86
56
  - `skills_output_folder`, `forge_data_folder`, `sidecar_path`
87
- - `snippet_skill_root_override` (optional string) — when set, overrides the IDE-derived `skill_root` for snippet `root:` paths. Authoring repos that keep all skills under a single on-disk folder (e.g. `skills/`) set this once so exported snippets reference the real layout instead of a per-IDE directory that does not exist. Consuming projects omit it.
57
+ - `snippet_skill_root_override` (optional): the `root:` prefix of every snippet and managed-section row in place of the IDE skill folder, for an authoring repo that keeps its skills in one folder such as `skills/`; step 1's snippet-root option (d) sets it for one run without changing `config.yaml`.
88
58
 
89
- 2. **Compute run-scoped variables:**
90
- - `timestamp` ← UTC `YYYYMMDD-HHmmss` captured at activation time. Fixed for the entire workflow run; summary.md reuses this when writing the result contract.
59
+ 2. **Resolve `{headless_mode}`**: true if `--headless` or `-H` was passed as an argument, or if `headless_mode: true` in `{sidecar_path}/preferences.yaml`. Default: false.
91
60
 
92
- 3. **Resolve `{headless_mode}`**: true if `--headless` or `-H` was passed as an argument, or if `headless_mode: true` in `{sidecar_path}/preferences.yaml`. Default: false.
93
-
94
- 4. **Resolve workflow customization.** Run:
61
+ 3. **Resolve workflow customization.** Run:
95
62
 
96
63
  ```bash
97
- python3 {project-root}/_bmad/scripts/resolve_customization.py \
98
- --skill {skill-root} --key workflow
64
+ uv run {project-root}/_bmad/scripts/resolve_customization.py --skill {skill-root} --project-root {project-root} --key workflow
99
65
  ```
100
66
 
101
- The script merges the three customization layers per `bmad-customize`'s structural merge rules (scalars override, arrays append):
67
+ It merges the bundled `{skill-root}/customize.toml` with `{project-root}/_bmad/custom/skf-export-skill.toml` (team overrides, committed) and `.user.toml` (personal overrides, gitignored). When it exits non-zero, prints no JSON or is missing, print one line, `[activation/warn] customization_resolver_unavailable: <reason>` (`<reason>`: its first stderr line, `not found` when the script is missing, `no JSON` when it printed none). If the resolver cannot run, read `{skill-root}/customize.toml` alone and use its bundled defaults: the `{project-root}/_bmad/custom/` overrides do not apply to this run. Keep the reason as `{customization_resolver_unavailable}` (unset when the resolver ran): a headless run records it once step 4 creates its run folder, and an interactive run only prints it.
102
68
 
103
- - `{skill-root}/customize.toml` — bundled defaults
104
- - `_bmad/custom/<skill-name>.toml` under `{project-root}` — team overrides (committed)
105
- - `_bmad/custom/<skill-name>.user.toml` under `{project-root}` — personal overrides (gitignored)
69
+ Bind each scalar to its bundled default when the merged value is empty or absent:
106
70
 
107
- If the script fails or is missing, fall back to reading `{skill-root}/customize.toml` directly — the bundled defaults are an empty string for each path scalar.
71
+ - `{snippetFormatPath}` ← `workflow.snippet_format_path`, else `assets/snippet-format.md`
72
+ - `{onCompleteCommand}` ← `workflow.on_complete`, else empty (step 6 then runs no hook)
108
73
 
109
- Apply the fallback now so stage files don't have to repeat the conditional logic. For each scalar, if the merged value is empty or absent, use the bundled default:
74
+ Then apply the array surfaces: execute each entry in `workflow.activation_steps_prepend` in order now; treat every entry in `workflow.persistent_facts` as standing context for the whole run (`file:`-prefixed entries are paths or globs whose contents load as facts: the bundled default loads every `project-context.md` under `{project-root}`; an entry prefixed `!` drops each earlier entry it names and loads nothing itself, so an override's `"!file:{project-root}/**/project-context.md"` turns that default off); then, after activation completes and before the first stage runs, execute each entry in `workflow.activation_steps_append` in order.
110
75
 
111
- - `{managedSectionFormatPath}` ← `workflow.managed_section_format_path` if non-empty, else `assets/managed-section-format.md`
112
- - `{snippetFormatPath}` ← `workflow.snippet_format_path` if non-empty, else `assets/snippet-format.md`
113
- - `{onCompleteCommand}` ← `workflow.on_complete` if non-empty, else empty string (no-op — step 6 skips the hook invocation)
76
+ 4. **Resolve the helpers and create the run folder.** Resolve each helper to the first of its two paths that exists, the installed module first, all in parallel:
114
77
 
115
- Stash all three as workflow-context variables. Stage files reference them directly — no conditional at the usage site.
78
+ - `{emitEnvelopeHelper}` ← the first existing path of `{project-root}/_bmad/skf/shared/scripts/skf-emit-result-envelope.py` and `{project-root}/src/shared/scripts/skf-emit-result-envelope.py` (every envelope, result file and headless record).
79
+ - `{manifestOpsHelper}` ← `{project-root}/_bmad/skf/shared/scripts/skf-manifest-ops.py`, else `{project-root}/src/shared/scripts/skf-manifest-ops.py` (steps 1, 3 and 4).
80
+ - `{skillInventoryHelper}` ← `{project-root}/_bmad/skf/shared/scripts/skf-skill-inventory.py`, else `{project-root}/src/shared/scripts/skf-skill-inventory.py` (step 1).
81
+ - `{rebuildManagedSectionsHelper}` ← `{project-root}/_bmad/skf/shared/scripts/skf-rebuild-managed-sections.py`, else `{project-root}/src/shared/scripts/skf-rebuild-managed-sections.py` (steps 1 and 4).
82
+ - `{validateOutputHelper}` ← `{project-root}/_bmad/skf/shared/scripts/skf-validate-output.py`, else `{project-root}/src/shared/scripts/skf-validate-output.py` (steps 1 and 2).
83
+ - `{countTokensHelper}` ← `{project-root}/_bmad/skf/shared/scripts/skf-count-tokens.py`, else `{project-root}/src/shared/scripts/skf-count-tokens.py` (steps 3 and 5).
116
84
 
117
- **Apply the array surfaces so they are not silent no-ops:** execute each entry in `workflow.activation_steps_prepend` in order now (org-wide pre-flight such as auth, network, or compliance); treat every entry in `workflow.persistent_facts` as standing context for the whole run (`file:`-prefixed entries are paths or globs whose contents load as facts — the bundled default loads any `project-context.md`); then, after activation completes and before the first stage runs, execute each entry in `workflow.activation_steps_append` in order.
85
+ No step has a fallback for them, so check them all here, before any prompt or write. If one has no existing path, HALT (exit code 4, `halt_reason: "context-rebuild-failed"`) at phase `on-activation §4`: "`{the missing helper's file name}` is missing. Nothing was changed. Re-install SKF."
118
86
 
119
- 5. **Pre-flight write probe.** Verify `{skills_output_folder}` is writable. A read-only mount, full disk, or permissions-denied path otherwise only surfaces at step 4's managed-section rewrite — by then the user has already confirmed the batch:
87
+ When `{headless_mode}` is true, create the run folder, whose sink holds the headless auto-decisions:
88
+
89
+ ```bash
90
+ mkdir -p "{project-root}/_bmad-output/.skf-run" && mktemp -d "{project-root}/_bmad-output/.skf-run/skf-export-skill-XXXXXXXX"
91
+ ```
92
+
93
+ Bind `{run_dir}` ← the path it prints. Step 6 deletes it once the run's envelope is built; a HARD HALT leaves it in place. If it cannot be created, HALT (exit code 4, `halt_reason: "write-failed"`) at phase `on-activation §4`. When `{customization_resolver_unavailable}` is set, record it there: write `customization_resolver_unavailable: <reason>` to `{run_dir}/resolver-warning.txt` with a file write (a resolver error can hold quotes or `$( )`, so never echo it or type it into an argument), then run `uv run {emitEnvelopeHelper} record --run-dir "{run_dir}" --warning "$(cat "{run_dir}/resolver-warning.txt")"` from `{project-root}`.
94
+
95
+ 5. **Pre-flight write check.** Verify `{skills_output_folder}` is writable, so a read-only mount, a full disk or a denied path stops the run before the user confirms a batch. Without `--dry-run`, probe it with a write:
120
96
 
121
97
  ```bash
122
98
  mkdir -p "{skills_output_folder}" && \
@@ -124,6 +100,8 @@ SKF_EXPORT_RESULT_JSON: {"status":"success|error|dry-run","skills":[],"context_f
124
100
  rm "{skills_output_folder}/.skf-write-probe"
125
101
  ```
126
102
 
127
- On any non-zero exit: HALT (exit code 4, `halt_reason: "write-failed"`). In headless mode, emit the error envelope per `references/result-envelope.md` with `skills: []`, `context_files_updated: []`, `manifest_path: null`.
103
+ With `--dry-run`, check without writing: `test -w "{skills_output_folder}"` when the folder exists; when it does not, skip the check, since step 1 then finds no skill to export.
104
+
105
+ On any non-zero exit: HALT (exit code 4, `halt_reason: "write-failed"`) at phase `on-activation §5`.
128
106
 
129
107
  6. Load, read the full file, and then execute `references/load-skill.md` to begin the workflow.
@@ -14,9 +14,11 @@
14
14
  <!-- SKF:END -->
15
15
  ```
16
16
 
17
+ `skf-rebuild-managed-sections.py` writes this section for export-skill, drop-skill and rename-skill. Its `assemble` action builds the body between the markers, and its `insert` and `replace` actions write both markers and the `updated:` date around that body, so the three skills write the same bytes for the same manifest and snippets.
18
+
17
19
  ## IDE → Context File Mapping (config.yaml `ides` list)
18
20
 
19
- The installer writes IDE identifiers to `config.yaml` under the `ides` key. Any workflow that rebuilds context files from `config.yaml` MUST map each entry to its context file and skill root using this table — it is the **single source of truth**.
21
+ The installer writes IDE identifiers to `config.yaml` under the `ides` key. The helper's `resolve-targets` action maps each one to its context file and skill root from `shared/data/ide-context-files.json`, the one mapping export-skill, drop-skill and rename-skill read. The tables below show that file for readers, and a test keeps them equal to it.
20
22
 
21
23
  Each IDE has two independent properties:
22
24
 
@@ -62,74 +64,36 @@ All other IDEs use AGENTS.md as the context file, each with its own skill direct
62
64
 
63
65
  ### Resolution rules
64
66
 
65
- **Deduplication:** When multiple IDE entries map to the same context file, deduplicate so each context file is processed exactly once. Use the **first configured IDE's** skill root for that context file's snippet root paths. Report the deduplication to the user: "Multiple IDEs target AGENTS.md — using {first IDE}'s skill root (`{skill_root}`). Each IDE's skills are installed to its own directory."
66
-
67
- **Missing `ides` key:** If the `ides` key is absent from `config.yaml`, treat it as an empty list. If the resulting set is empty (no recognized entries), fall back to AGENTS.md with `.agents/skills/` as the skill root.
68
-
69
- **Unknown IDE values:** Warn: "Unknown IDE '{value}' in config.yaml — defaulting to AGENTS.md with `.agents/skills/`"
67
+ `resolve-targets` returns one target per context file, with the warnings and notes to show: IDEs that share a context file take the first configured IDE's skill root, and an absent, empty or unknown IDE resolves to AGENTS.md with `.agents/skills/`. To add an IDE, add it to the shared mapping; no workflow setting changes the mapping.
70
68
 
71
69
  **`snippet_skill_root_override` (optional):** Authoring repos where all skills live under one shared on-disk directory (e.g. `skills/`) that does not match any per-IDE skill root may set `snippet_skill_root_override: skills/` in `config.yaml`. When set:
72
70
 
73
71
  - `export-skill/step 3` §2.7 uses the override as `{skill_root}` for snippet generation instead of the IDE-mapped value
74
- - `export-skill/step 4` §4d (and its equivalents in `drop-skill/step 2` and `rename-skill/step 2`) treat the override as the effective target prefix for the managed-section rebuild: snippets whose prefix already matches the override pass through unchanged, and any other prefix — including per-IDE prefixes carried by sibling snippets that were exported before the override was adopted, and legacy `skills/` drafts — is rewritten to the override so the rebuilt managed section uniformly references the real on-disk location
72
+ - export-skill, drop-skill and rename-skill pass it to `assemble` as `--skill-root-override`, which makes it the `root:` prefix of every row it writes: a snippet whose prefix already matches passes through unchanged, and any other prefix (a per-IDE prefix on a snippet exported before the override was adopted, or a legacy `skills/` draft) is rewritten to it, so the section uniformly references the real on-disk location
75
73
 
76
74
  Consuming projects (the common case) omit the field and keep the default IDE-mapping behavior. The override is a narrow escape hatch for repos that author skills into `{skills_output_folder}` and never duplicate them into `.claude/skills/`-style directories.
77
75
 
78
76
  ### Consumers
79
77
 
80
- This mapping is the single source of truth. Workflows that need it: `export-skill/step 1` (resolves `target_context_files` from config.yaml IDE list), `export-skill/step 4` (applies four-case logic and rewrites root paths when writing managed sections), `drop-skill/step 2` and `rename-skill/step 2` (rebuild context files after a management operation).
78
+ Workflows that resolve context files through `resolve-targets`: `export-skill/step 1` (the target context files), `drop-skill/step 1` (the confirmation's context-file count) and step 2, and `rename-skill/step 2` (the rebuild after a management operation).
81
79
 
82
80
  ## Four-Case Logic
83
81
 
84
- ### Case 1: Create (No File Exists)
85
-
86
- Target file does not exist. Create new file with managed section only.
87
-
88
- ```markdown
89
- <!-- SKF:BEGIN updated:{date} -->
90
- {managed section content}
91
- <!-- SKF:END -->
92
- ```
93
-
94
- ### Case 2: Append (File Exists, No Section)
95
-
96
- Target file exists but contains no `<!-- SKF:BEGIN` marker. Append managed section at end of file.
97
-
98
- 1. Read existing file content
99
- 2. Append two blank lines
100
- 3. Append managed section with markers
101
- 4. Write file
102
-
103
- ### Case 3: Regenerate (Existing Section)
82
+ `skf-rebuild-managed-sections.py <context-file> check` reports the case of each context file as `case`, and the case picks the write. Both writes take only the body between the markers, as `assemble` stages it:
104
83
 
105
- Target file contains `<!-- SKF:BEGIN` and `<!-- SKF:END -->` markers. Replace content between markers.
84
+ - **Create** (no file): `insert`, which creates the file with the section.
85
+ - **Append** (a file with no `<!-- SKF:BEGIN` marker): `insert`, which adds the section at the end of the file.
86
+ - **Regenerate** (a file with a section): `replace`, which swaps the old section for the new one and writes both markers itself.
87
+ - **Malformed** (a `<!-- SKF:BEGIN` with no `<!-- SKF:END -->` closing it): HALT and leave the file untouched. `check` names the line; the user restores the end marker, or deletes the stray begin marker, and re-runs.
106
88
 
107
- 1. Read existing file content
108
- 2. Find `<!-- SKF:BEGIN` line (preserve everything before it)
109
- 3. Find `<!-- SKF:END -->` line (preserve everything after it)
110
- 4. Replace everything between markers (inclusive) with new managed section
111
- 5. Write file
112
-
113
- ### Case 4: Malformed Markers (Halt)
114
-
115
- Target file contains `<!-- SKF:BEGIN` but no matching `<!-- SKF:END -->` marker. This indicates a corrupted managed section.
116
-
117
- 1. Halt workflow with error: "Malformed managed section — `<!-- SKF:BEGIN` found but no `<!-- SKF:END -->`. Fix the markers manually before re-running export."
118
- 2. Do not modify the file
89
+ drop-skill and rename-skill rewrite only a section that exists: they leave a missing file, or one with no section, as it is (export-skill adds the section on its next run), and report a malformed file as failed while they rebuild the others.
119
90
 
120
91
  ## Regeneration: Full Index Rebuild
121
92
 
122
- When regenerating (Case 3) or creating/appending (Cases 1-2), rebuild the skill index from the **exported skill set** only:
123
-
124
- 1. Read `{skills_output_folder}/.export-manifest.json` (v2 schema — see `knowledge/version-paths.md`) to determine which skills have been explicitly exported and their `active_version` (if no manifest exists, only the current export target qualifies)
125
- 2. For each skill in the exported set, resolve the snippet at `{skills_output_folder}/{skill-name}/{active_version}/{skill-name}/context-snippet.md` (i.e., `{skill_package}/context-snippet.md`). Do NOT use glob patterns across version directories — always resolve via manifest `active_version` or `active` symlink, falling back to the flat `{skills_output_folder}/{skill-name}/context-snippet.md` path for a skill not yet migrated to the versioned layout
126
- 3. Count total skills and stack skills (from filtered set only)
127
- 4. Assemble filtered snippets into managed section
128
- 5. Sort alphabetically by skill name
129
- 6. Update the header line with correct counts
93
+ `skf-rebuild-managed-sections.py assemble` builds the body from the **exported skill set** only: the skills of `{skills_output_folder}/.export-manifest.json` whose active version is not deprecated, plus the skills an export names with `--include`. Its docstring lists the rules it applies (where it reads each snippet, the rows of skills the manifest does not know, the sort and the counts).
130
94
 
131
95
  **Rationale:** create-skill and update-skill also write `context-snippet.md` as a build artifact, but only export-skill is the publishing gate (ADR-K). The `.export-manifest.json` file tracks which skills have passed through export-skill, preventing draft skills from leaking into the agent's passive context.
132
96
 
133
97
  ## Safety Rules
134
98
 
135
- Only the bytes between `<!-- SKF:BEGIN -->` and `<!-- SKF:END -->` are yours to rewrite; everything above and below is the user's own file (their CLAUDE.md/AGENTS.md/.cursorrules) and must survive byte-for-byte — rewriting outside the markers destroys their content. On write failure, report and stop — never leave a partial write. Route writes through the atomic-write / rebuild-managed-sections helpers: they verify byte-identity of the content outside the markers after each write, so no separate hand-check is needed.
99
+ Only the bytes between the `<!-- SKF:BEGIN` and `<!-- SKF:END -->` markers are SKF's to rewrite. Everything above and below them is the user's own file (their CLAUDE.md, AGENTS.md or .cursorrules) and must survive byte-for-byte. Route every write through `skf-rebuild-managed-sections.py`: it writes atomically and reads the file back to check the markers and the bytes outside them, so no separate hand-check is needed. On a write failure, report and stop.
@@ -6,7 +6,9 @@ The snippet `root:` path must reflect the IDE's actual skill directory — its `
6
6
 
7
7
  The root path in context-snippet.md on disk uses the **`skill_root` of the first entry in `target_context_files`** (resolved in step 1 from config.yaml IDE mapping). When assembling managed sections for other context files, step 4 rewrites root paths to match the target context file's skill root.
8
8
 
9
- **Authoring-repo override:** If `snippet_skill_root_override` is set in `config.yaml`, it replaces the IDE-mapped value for snippet generation and freezes matching prefixes against step 4 rewrites. See the "Resolution rules" section in `managed-section-format.md` for the full semantics.
9
+ **Authoring-repo override:** If `snippet_skill_root_override` is set in `config.yaml`, or for one run by step 1's snippet-root option (d), it replaces the IDE-mapped value for snippet generation and becomes every row's root in step 4. See the "Resolution rules" section in `managed-section-format.md` for the full semantics.
10
+
11
+ **A house-style copy** of this file (`snippet_format_path` in `customize.toml`) keeps the lines scripts parse: line 1 with its `root:` field, line 2's `|IMPORTANT:` start and skill name, the `|gotchas:` label with its `[CARRIED]` marker, and no bare `|` line. That setting's comment says which script reads each one.
10
12
 
11
13
  ## Format Rules
12
14