bmad-module-skill-forge 2.2.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 +49 -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 +12 -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 +99 -11
  75. package/src/shared/scripts/skf-detect-docs.py +694 -2
  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 +209 -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 +15 -5
  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 +28 -19
  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 -59
  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 -90
  348. package/src/skf-setup/references/detect-and-tier.md +58 -67
  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 -679
  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,320 @@
1
+ /**
2
+ * SKF Tool Check
3
+ * Probes the tools SKF runs (the list in src/shared/tool-requirements.yaml)
4
+ * in parallel when a command starts, and prints one line per tool once the
5
+ * command's own work is done: the version found, its status against the
6
+ * tool's minimum and, when it is not ok, how to upgrade or install it.
7
+ *
8
+ * Each probe runs a binary resolved on PATH outside the project folder, with
9
+ * no shell and fixed arguments, and never npx, which can download. A .cmd or
10
+ * .bat shim on Windows runs through cmd.exe. A missing, hanging or failing
11
+ * probe reads as unknown: nothing here throws or changes an exit code.
12
+ */
13
+
14
+ const { spawn } = require('node:child_process');
15
+ const fs = require('node:fs');
16
+ const path = require('node:path');
17
+ const chalk = require('chalk');
18
+ const yaml = require('js-yaml');
19
+ const { TIMEOUT_MS, compareVersions, findVersion } = require('./version-check');
20
+
21
+ const PACKAGE_REQUIREMENTS = path.join(__dirname, '..', '..', '..', 'src', 'shared', 'tool-requirements.yaml');
22
+ // The copy the installer writes: the minimums of the SKF version the workflows run.
23
+ const PROJECT_REQUIREMENTS = path.join('_bmad', 'skf', 'shared', 'tool-requirements.yaml');
24
+ const MINIMUM = /^\d+(?:\.\d+)*$/;
25
+ // SKF runs these through npx when they are not installed, so a probe reads a
26
+ // binary on PATH only, and a missing one is no gap.
27
+ const NPX_TOOLS = new Set(['tessl', 'skill_check']);
28
+ // ccc has no --version: `uv tool list` names each package uv installed, with its version.
29
+ const CCC_PACKAGE = /^cocoindex-code v(\S+)/m;
30
+ const MAX_OUTPUT = 64 * 1024;
31
+ const STYLES = { ok: chalk.green, upgrade: chalk.yellow, unknown: chalk.yellow, missing: chalk.red, optional: chalk.dim };
32
+
33
+ /** The tools of a tool-requirements.yaml by key (mappings only), or null when it cannot be read. */
34
+ function readTools(file) {
35
+ try {
36
+ const tools = yaml.load(fs.readFileSync(file, 'utf8'))?.tools;
37
+ if (!tools || typeof tools !== 'object' || Array.isArray(tools)) return null;
38
+ return Object.fromEntries(Object.entries(tools).filter(([, tool]) => tool && typeof tool === 'object' && !Array.isArray(tool)));
39
+ } catch {
40
+ return null;
41
+ }
42
+ }
43
+
44
+ /** An environment variable, its name matched case-insensitively on Windows. */
45
+ function envValue(env, name) {
46
+ if (process.platform !== 'win32') return env[name];
47
+ const key = Object.keys(env).find((candidate) => candidate.toUpperCase() === name.toUpperCase());
48
+ return key === undefined ? undefined : env[key];
49
+ }
50
+
51
+ function realpath(file) {
52
+ try {
53
+ return fs.realpathSync.native(file);
54
+ } catch {
55
+ return file;
56
+ }
57
+ }
58
+
59
+ /** True when `target` is `folder` or lies inside it. */
60
+ function isInside(target, folder) {
61
+ const relative = path.relative(folder, target);
62
+ return !path.isAbsolute(relative) && relative.split(path.sep)[0] !== '..';
63
+ }
64
+
65
+ function isRunnable(file) {
66
+ try {
67
+ if (!fs.statSync(file).isFile()) return false;
68
+ if (process.platform !== 'win32') fs.accessSync(file, fs.constants.X_OK);
69
+ return true;
70
+ } catch {
71
+ return false;
72
+ }
73
+ }
74
+
75
+ /**
76
+ * The absolute path of `name` on PATH, or null. A PATH folder in or under the
77
+ * project, a relative PATH entry (a shell reads it from the project folder)
78
+ * and a binary whose real path lies in the project are all skipped, so a
79
+ * binary planted in the repository never runs. On Windows each PATHEXT
80
+ * extension is tried, never the bare name.
81
+ */
82
+ function resolveOutsideProject(name, { env, projectDir }) {
83
+ const project = [path.resolve(projectDir), realpath(path.resolve(projectDir))];
84
+ const inProject = (file) => project.some((folder) => isInside(file, folder));
85
+ const extensions = process.platform === 'win32' ? (envValue(env, 'PATHEXT') || '.COM;.EXE;.BAT;.CMD').split(';').filter(Boolean) : [''];
86
+ for (const entry of String(envValue(env, 'PATH') || '').split(path.delimiter)) {
87
+ const dir = entry.replaceAll('"', '');
88
+ if (!path.isAbsolute(dir) || inProject(dir) || inProject(realpath(dir))) continue;
89
+ for (const extension of extensions) {
90
+ const file = path.join(dir, name + extension);
91
+ if (isRunnable(file) && !inProject(realpath(file))) return file;
92
+ }
93
+ }
94
+ return null;
95
+ }
96
+
97
+ /**
98
+ * How to run `file` with `args`: the file itself, or, for a .cmd or .bat shim
99
+ * on Windows (Node refuses to spawn one without a shell), cmd.exe with the
100
+ * command line fixed here. null when that line would not be safe: a path
101
+ * cmd.exe would expand (`%`, `!`) or an argument that is not a plain word.
102
+ */
103
+ function commandFor(file, args, platform = process.platform, env = process.env) {
104
+ if (platform !== 'win32' || !/\.(?:cmd|bat)$/i.test(file)) return { file, args, options: {} };
105
+ if (/[%!"]/.test(file) || !args.every((arg) => /^[\w.-]+$/.test(arg))) return null;
106
+ const shell = envValue(env, 'ComSpec') || path.win32.join(envValue(env, 'SystemRoot') || String.raw`C:\Windows`, 'System32', 'cmd.exe');
107
+ return { file: shell, args: ['/d', '/s', '/c', `""${file}" ${args.join(' ')}"`], options: { windowsVerbatimArguments: true } };
108
+ }
109
+
110
+ /** Stop a probe that is still running, so it neither holds the command open nor outlives it. */
111
+ function stop(child) {
112
+ try {
113
+ child.kill('SIGKILL');
114
+ } catch {
115
+ // Already gone
116
+ }
117
+ child.stdout?.destroy();
118
+ child.unref();
119
+ }
120
+
121
+ /**
122
+ * Run one probe. Resolves { ok, stdout } and never rejects: a probe that
123
+ * cannot start, exits non-zero or outlives the timeout (it is then killed)
124
+ * is not ok.
125
+ */
126
+ function run(command, context) {
127
+ return new Promise((resolve) => {
128
+ let stdout = '';
129
+ let timer;
130
+ let child;
131
+ const finish = (ok) => {
132
+ clearTimeout(timer);
133
+ if (child) context.running.delete(child);
134
+ resolve({ ok, stdout });
135
+ };
136
+ // The report is already taken: a probe chained after it starts nothing.
137
+ if (context.stopped) {
138
+ finish(false);
139
+ return;
140
+ }
141
+ try {
142
+ child = spawn(command.file, command.args, {
143
+ cwd: context.cwd,
144
+ env: context.env,
145
+ stdio: ['ignore', 'pipe', 'ignore'],
146
+ windowsHide: true,
147
+ ...command.options,
148
+ });
149
+ } catch {
150
+ finish(false);
151
+ return;
152
+ }
153
+ context.running.add(child);
154
+ timer = setTimeout(() => {
155
+ stop(child);
156
+ finish(false);
157
+ }, context.timeoutMs);
158
+ child.stdout.setEncoding('utf8');
159
+ child.stdout.on('data', (chunk) => {
160
+ if (stdout.length < MAX_OUTPUT) stdout += chunk;
161
+ });
162
+ child.on('error', () => finish(false));
163
+ child.on('close', (code) => finish(code === 0));
164
+ });
165
+ }
166
+
167
+ /** The first x.y or x.y.z that `file` prints when run with `args`, or null. */
168
+ async function versionOf(file, args, context) {
169
+ const command = commandFor(file, args, process.platform, context.env);
170
+ if (!command) return null;
171
+ const { ok, stdout } = await run(command, context);
172
+ return ok ? findVersion(stdout) : null;
173
+ }
174
+
175
+ /** One tool: { found, version }, the version null when it cannot be read. */
176
+ async function probe(key, tool, context) {
177
+ if (key === 'node') return { found: true, version: process.versions.node };
178
+ if (key === 'python') {
179
+ // The interpreter uv runs SKF's helpers with. --system leaves out a
180
+ // virtual environment, whose interpreter would be the project's to run.
181
+ const uv = resolveOutsideProject('uv', context);
182
+ const version = uv ? await versionOf(uv, ['python', 'find', '--system', '--show-version'], context) : null;
183
+ if (version) return { found: true, version };
184
+ }
185
+ if (key === 'ccc') {
186
+ if (!resolveOutsideProject('ccc', context)) return { found: false };
187
+ const uv = resolveOutsideProject('uv', context);
188
+ const command = uv && commandFor(uv, ['tool', 'list'], process.platform, context.env);
189
+ const { ok, stdout } = command ? await run(command, context) : { ok: false, stdout: '' };
190
+ const match = ok ? CCC_PACKAGE.exec(stdout) : null;
191
+ return { found: true, version: match ? findVersion(match[1]) : null };
192
+ }
193
+ const [name, ...args] = Array.isArray(tool.version_command) ? tool.version_command.map(String) : [];
194
+ const file = name ? resolveOutsideProject(name, context) : null;
195
+ if (!file) return { found: false };
196
+ return { found: true, version: args.length > 0 ? await versionOf(file, args, context) : null };
197
+ }
198
+
199
+ /**
200
+ * The minimum of each tool, from the project's copy of the list when there is
201
+ * one (the SKF version its workflows run), else from the package's own.
202
+ */
203
+ function minimums(projectDir, tools) {
204
+ const source = readTools(path.join(projectDir, PROJECT_REQUIREMENTS)) || tools;
205
+ return Object.fromEntries(
206
+ Object.keys(tools).map((key) => {
207
+ const value = source[key]?.minimum;
208
+ return [key, typeof value === 'string' && MINIMUM.test(value) ? value : null];
209
+ }),
210
+ );
211
+ }
212
+
213
+ /** One report row per tool, in the list's order. A tool that has not answered yet was found and reads as unknown. */
214
+ function rowsFor(tools, answers, floors) {
215
+ return Object.entries(tools).map(([key, tool]) => {
216
+ const row = { key, name: String(tool.name || key), version: null, minimum: floors[key] };
217
+ const answer = answers.get(key) || { found: true, version: null };
218
+ if (!answer.found) {
219
+ const tiers = Array.isArray(tool.tiers) ? tool.tiers : [];
220
+ if (NPX_TOOLS.has(key)) return { ...row, status: 'optional', label: 'optional, runs through npx' };
221
+ if (tool.kind === 'tier' && tiers.length > 0)
222
+ return { ...row, status: 'optional', label: `optional, ${tiers[0]} tier`, hint: tool.install_url };
223
+ if (tool.kind === 'tier' || tool.kind === 'optional')
224
+ return { ...row, status: 'optional', label: 'optional', hint: tool.install_url };
225
+ return { ...row, status: 'missing', label: 'missing', hint: tool.install_url };
226
+ }
227
+ if (!answer.version) {
228
+ return { ...row, status: 'unknown', label: 'installed, version unknown', hint: row.minimum ? tool.upgrade : undefined };
229
+ }
230
+ // A minimum may name a major alone (Node.js 22); compareVersions reads x.y.
231
+ const floor = row.minimum && !row.minimum.includes('.') ? `${row.minimum}.0` : row.minimum;
232
+ if (floor && compareVersions(answer.version, floor)) {
233
+ return { ...row, version: answer.version, status: 'upgrade', label: `upgrade to >= ${row.minimum}`, hint: tool.upgrade };
234
+ }
235
+ return { ...row, version: answer.version, status: 'ok', label: 'ok' };
236
+ });
237
+ }
238
+
239
+ /** The report: a heading, then one aligned line per tool. */
240
+ function formatReport(rows) {
241
+ const width = (pick) => Math.max(0, ...rows.map((row) => pick(row).length));
242
+ const nameWidth = width((row) => row.name);
243
+ const versionWidth = width((row) => row.version || '-');
244
+ const labelWidth = width((row) => (row.hint ? row.label : ''));
245
+ const lines = [chalk.white.bold(' Tools')];
246
+ for (const row of rows) {
247
+ const style = STYLES[row.status];
248
+ const status = row.hint ? `${style(row.label.padEnd(labelWidth))} ${chalk.dim(row.hint)}` : style(row.label);
249
+ lines.push(` ${row.name.padEnd(nameWidth)} ${(row.version || '-').padEnd(versionWidth)} ${status}`);
250
+ }
251
+ return lines;
252
+ }
253
+
254
+ /**
255
+ * Start every probe now. The returned rows() waits for them at most
256
+ * `timeoutMs` more, stops any still running and resolves the report rows,
257
+ * or null when the package's tool list cannot be read. rows.stop() stops
258
+ * the probes still running and starts no more, without a report.
259
+ */
260
+ function beginCheck({ projectDir = process.cwd(), env = process.env, timeoutMs = TIMEOUT_MS, requirements = PACKAGE_REQUIREMENTS } = {}) {
261
+ const tools = readTools(requirements);
262
+ // Probes run from the filesystem root, so no tool reads the project's own
263
+ // configuration or runs its virtual environment.
264
+ const context = { env, projectDir, timeoutMs, cwd: path.parse(path.resolve(projectDir)).root, running: new Set(), stopped: false };
265
+ const answers = new Map();
266
+ const probes = Object.entries(tools || {}).map(([key, tool]) =>
267
+ probe(key, tool, context).then(
268
+ (answer) => answers.set(key, answer),
269
+ () => answers.set(key, { found: true, version: null }),
270
+ ),
271
+ );
272
+ const stopAll = () => {
273
+ context.stopped = true;
274
+ for (const child of context.running) stop(child);
275
+ };
276
+ async function rows() {
277
+ if (!tools) return null;
278
+ let timer;
279
+ const waited = new Promise((resolve) => {
280
+ timer = setTimeout(resolve, timeoutMs);
281
+ });
282
+ await Promise.race([Promise.all(probes), waited]);
283
+ clearTimeout(timer);
284
+ stopAll();
285
+ return rowsFor(tools, answers, minimums(projectDir, tools));
286
+ }
287
+ return Object.assign(rows, { stop: stopAll });
288
+ }
289
+
290
+ /** Probe every tool and resolve the report rows (see beginCheck). */
291
+ function checkTools(options) {
292
+ return beginCheck(options)();
293
+ }
294
+
295
+ /**
296
+ * Start an async tool check, with the shape of startVersionCheck: call the
297
+ * returned function after the command's own work to print the report. It
298
+ * waits at most about 3 s for the probes and never throws. A command that
299
+ * ends without the report calls its stop() instead, so no probe outlives
300
+ * the CLI; after the report, stop() does nothing.
301
+ */
302
+ function startToolCheck(options) {
303
+ let rows;
304
+ try {
305
+ rows = beginCheck(options);
306
+ } catch {
307
+ rows = Object.assign(async () => null, { stop() {} });
308
+ }
309
+ async function printIfReady() {
310
+ try {
311
+ const found = await rows();
312
+ console.log(found ? `${formatReport(found).join('\n')}\n` : chalk.dim(' Tools: not checked, the tool list could not be read\n'));
313
+ } catch {
314
+ // Never block or fail the CLI for a tool check
315
+ }
316
+ }
317
+ return Object.assign(printIfReady, { stop: rows.stop });
318
+ }
319
+
320
+ module.exports = { startToolCheck, checkTools, commandFor, formatReport, resolveOutsideProject };
@@ -10,6 +10,8 @@ const chalk = require('chalk');
10
10
  const PACKAGE_NAME = 'bmad-module-skill-forge';
11
11
  const REGISTRY_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/latest`;
12
12
  const TIMEOUT_MS = 3000;
13
+ // The first x.y or x.y.z in a string: `0.45`, `v3.0.0-rc.1`, `ast-grep 0.42.2`.
14
+ const VERSION_IN_TEXT = /(\d+)\.(\d+)(?:\.(\d+))?/;
13
15
 
14
16
  function fetchLatestVersion() {
15
17
  return new Promise((resolve) => {
@@ -42,15 +44,26 @@ function fetchLatestVersion() {
42
44
  });
43
45
  }
44
46
 
45
- function compareVersions(current, latest) {
46
- const parse = (v) => v.replace(/^v/, '').split('.').map(Number);
47
- const [cMajor, cMinor, cPatch] = parse(current);
48
- const [lMajor, lMinor, lPatch] = parse(latest);
47
+ /** The first x.y or x.y.z in `text`, as written there, or null. */
48
+ function findVersion(text) {
49
+ const match = VERSION_IN_TEXT.exec(String(text ?? ''));
50
+ return match ? match[0] : null;
51
+ }
49
52
 
50
- if (lMajor > cMajor) return true;
51
- if (lMajor === cMajor && lMinor > cMinor) return true;
52
- if (lMajor === cMajor && lMinor === cMinor && lPatch > cPatch) return true;
53
- return false;
53
+ /**
54
+ * True when `latest` is newer than `current`. Each is read as the first x.y
55
+ * or x.y.z it holds, a missing part as 0, so `0.45` and `ast-grep 0.42.2`
56
+ * both compare with `0.45.3`; a string with neither is never newer.
57
+ */
58
+ function compareVersions(current, latest) {
59
+ const parse = (text) => {
60
+ const match = VERSION_IN_TEXT.exec(String(text ?? ''));
61
+ return match ? match.slice(1, 4).map((part) => Number(part ?? 0)) : null;
62
+ };
63
+ const [from, to] = [parse(current), parse(latest)];
64
+ if (!from || !to) return false;
65
+ const index = from.findIndex((part, position) => part !== to[position]);
66
+ return index !== -1 && to[index] > from[index];
54
67
  }
55
68
 
56
69
  /**
@@ -83,4 +96,4 @@ function startVersionCheck(currentVersion) {
83
96
  };
84
97
  }
85
98
 
86
- module.exports = { startVersionCheck, compareVersions };
99
+ module.exports = { startVersionCheck, compareVersions, findVersion, TIMEOUT_MS };
@@ -30,11 +30,14 @@ for (const command of [installCommand, updateCommand, statusCommand, uninstallCo
30
30
  for (const option of command.options || []) {
31
31
  cmd.option(...option);
32
32
  }
33
- // Wrap action to print update notice after command completes
33
+ // Wrap action to print update notice after command completes. The actions
34
+ // return (setting process.exitCode on a failure) rather than exit, so the
35
+ // notice follows them; the exit ends the stdin that the fix above resumed.
34
36
  const originalAction = command.action;
35
37
  cmd.action(async (...args) => {
36
38
  await originalAction(...args);
37
39
  await printUpdateNotice();
40
+ process.exit(process.exitCode ?? 0);
38
41
  });
39
42
  }
40
43
 
package/.gitattributes DELETED
@@ -1,16 +0,0 @@
1
- # Normalize all text files to LF in the repo and on checkout, regardless of
2
- # the contributor's platform or core.autocrlf setting. SKF runs on Linux,
3
- # macOS, and Windows (Git Bash / WSL2); LF-everywhere keeps prettier
4
- # `endOfLine: 'lf'` clean and avoids spurious diffs from line-ending churn.
5
- * text=auto eol=lf
6
-
7
- # Binary blobs — never touch.
8
- *.png binary
9
- *.jpg binary
10
- *.jpeg binary
11
- *.gif binary
12
- *.ico binary
13
- *.pdf binary
14
- *.zip binary
15
- *.gz binary
16
- *.tgz binary
package/.nvmrc DELETED
@@ -1 +0,0 @@
1
- 24