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
@@ -12,7 +12,7 @@ Source reading via gh_bridge — infer exports from file structure and content.
12
12
  5. Infer types from JSDoc, docstrings, type annotations in source
13
13
 
14
14
  ### Confidence
15
- - All results: T1-low (source reading without structural verification)
15
+ - Every export is read by eye, so every entry is T1-low (source reading without structural verification): a `[SRC:...]` citation, `extraction_method: source-read` and `ast_node_type: null`
16
16
  - No co-import detection available
17
17
  - No AST-backed line numbers
18
18
 
@@ -28,65 +28,56 @@ Source reading via gh_bridge — infer exports from file structure and content.
28
28
 
29
29
  Structural extraction via ast-grep — verified exports with line-level citations.
30
30
 
31
- > **Note:** `ast_bridge.*`, `qmd_bridge.*`, and `ccc_bridge.*` references below are **conceptual interfaces**, not callable functions. Resolve them as follows:
32
- > - `ast_bridge.*` → ast-grep MCP tools (`mcp__ast-grep__find_code`, `mcp__ast-grep__find_code_by_rule`) or `ast-grep` CLI
33
- > - `qmd_bridge.*` → QMD MCP `query` tool (`mcp__plugin_qmd-plugin_qmd__query`) taking `searches=[{type:'lex'|'vec'|'hyde', query, intent}]`, or `qmd` CLI (`qmd search` / `qmd vector-search`). The legacy `vector_search` MCP tool has been removed; if a client surfaces a tool-not-found error, degrade gracefully per the QMD step 4 tool-probe note — do not retry the stale name.
34
- > - `ccc_bridge.*` → `/ccc` skill (Claude Code), ccc MCP server (Cursor), or `ccc` CLI
35
- > - `gh_bridge.*` → `gh api` commands or direct file I/O for local sources
36
- >
37
- > See `knowledge/tool-resolution.md` for the complete resolution table. Also see the AST Extraction Protocol section below and the TOOL/SUBPROCESS FALLBACK rule for dispatch details.
31
+ > **Note:** `ast_bridge.*`, `qmd_bridge.*`, `ccc_bridge.*` and `gh_bridge.*` below are **conceptual interfaces**, not callable functions: `ast_bridge.*` is the ast-grep MCP tools (`find_code`, `find_code_by_rule`) or the `ast-grep` CLI; `qmd_bridge.*` the QMD MCP `query` tool or the `qmd` CLI (`qmd search` / `qmd vsearch`), whose tool-not-found error is non-fatal, as step 4's tool probe says; `ccc_bridge.*` the `/ccc` skill, the ccc MCP server or the `ccc` CLI; `gh_bridge.*` `gh api` or direct file reads of a local source. `knowledge/tool-resolution.md` has the full table.
38
32
 
39
33
  ### Strategy
40
34
 
41
- 1. Detect language from brief or file extensions
42
- 2. Use ast-grep to extract all exports from `path` for the given `language` (scan definitions)
43
- 3. For each export: function name, full signature, parameter types, return type, line number
44
- 4. Use ast-grep to detect co-imported symbols in `path` for the given `libraries[]`
45
- 5. Build extraction rules YAML for reproducibility
35
+ 1. Run the AST Extraction Protocol below: the recipe runner finds every export in the files in scope, each with its file, `$NAME`'s line, recipe and node kind, and a function's signature, parameters and return type
36
+ 2. For each export: take its `signature`, `params` and `return_type` from the runner's record; read them from the source at its line only for a function whose `params` is null
37
+ 3. Use ast-grep to detect co-imported symbols in `path` for the given `libraries[]`
38
+ 4. Record the recipe set, the recipes and the ast-grep version the runner reports, for reproducibility
46
39
 
47
40
  ### Confidence
48
- - Exported functions with full signatures: T1 (AST-verified)
49
- - Type definitions and interfaces: T1
50
- - Co-import patterns: T1
41
+ - Each entry is labeled by the tool that produced it, not by the tier:
42
+ - An export an ast-grep rule matched (a function with its full signature, a type definition, an interface): T1 (AST-verified), an `[AST:...]` citation, `extraction_method: ast-grep` and `ast_node_type` set to the `kind` the matching recipe declares (see the recipes below)
43
+ - An export read by eye (ast-grep could not parse its file, the rules missed it, or the file was read instead of matched): T1-low, a `[SRC:...]` citation, `extraction_method: source-read` and `ast_node_type: null`
44
+ - Co-import patterns ast-grep detected: T1
51
45
  - Internal/private functions: excluded (not part of public API)
52
46
 
53
47
  ### ast-grep Patterns
54
- - JS/TS: `export function $NAME($$$PARAMS): $RET` / `export const $NAME = ($$$PARAMS) => $BODY` / `export const $NAME` / `export class $NAME`
55
- - Rust: `pub fn $NAME($$$PARAMS) -> $RET`
56
- - Python: function definitions within `__all__` list
57
- - Go: capitalized function definitions
48
+
49
+ One recipe matches each export shape: the recipes are under **YAML Rule Recipes by Language** below, and the table that gives each shape its recipe and kind, a `find_code` pattern's included, is in `extraction-patterns-by-hand.md`.
58
50
 
59
51
  ---
60
52
 
61
53
  ## Forge+ Tier (AST + CCC)
62
54
 
63
- Identical extraction to Forge tier. CCC adds an upstream semantic discovery step that pre-ranks the file extraction queue.
55
+ Identical extraction to Forge tier. CCC adds an upstream semantic discovery step that ranks the files extraction reads one at a time.
64
56
 
65
57
  ### When CCC Pre-Discovery Applies
66
58
 
67
59
  CCC pre-discovery runs in ccc-discover (before this extraction step) when ALL of the following are true:
68
60
  - Tier is Forge+ or Deep
69
61
  - `tools.ccc: true` in forge-tier.yaml
70
- - `ccc_index.status` is `"fresh"`, `"stale"`, `"created"`, or `"none"`/`"failed"` (step 2b attempts lazy indexing for the latter two)
62
+ - The source index is available: step 2b searches with `--refresh` when `ccc_index.status` is `"fresh"`, `"created"`, `"skipped"` or a status it does not know (such as a `"stale"` an older SKF recorded), and first attempts lazy indexing when it is `"none"` or `"failed"`
71
63
 
72
- The discovery step stores `{ccc_discovery: [{file, score, snippet}]}` in context. This extraction step consumes those results to pre-rank the file list.
64
+ The discovery step stores `{ccc_discovery: [{file, score, snippet}]}` in context. This extraction step ranks by those results the files it reads one at a time.
73
65
 
74
66
  ### CCC Pre-Ranking Strategy
75
67
 
76
- When `{ccc_discovery}` is present and non-empty:
68
+ When `{ccc_discovery}` is present and non-empty, rank the files extraction reads one at a time: those the recipe runner leaves to source reading (its `file_issues`, its extraction gaps and the files of a language no recipe reads), or the filtered file list when extraction runs without the runner.
77
69
 
78
- 1. Files appearing in `{ccc_discovery}` results move to the front of the extraction queue, sorted by relevance score descending
79
- 2. Files not in CCC results remain in the queue — they are not excluded, only deprioritized
70
+ 1. Files appearing in `{ccc_discovery}` results come first, sorted by relevance score descending
71
+ 2. Files not in CCC results follow them: they are not excluded, only deprioritized
80
72
  3. If the CCC intersection with scoped files produces <10 files: include all scoped files (CCC results too narrow)
81
- 4. Proceed with the AST Extraction Protocol on the pre-ranked list
82
73
 
83
74
  ### ast-grep Patterns
84
75
 
85
- Same patterns as Forge tier — see Forge tier section above. CCC pre-ranking does not change which AST patterns are used, only which files are processed first.
76
+ Same recipes as Forge tier. CCC pre-ranking changes no recipe.
86
77
 
87
78
  ### Confidence
88
79
 
89
- All results: T1 (AST-verified) — identical to Forge tier. CCC is upstream discovery only and is invisible in the output artifact.
80
+ Labeled by tool, exactly as at Forge tier: an export an ast-grep rule matched is T1 (AST-verified), an export read by eye is T1-low. CCC is upstream discovery only and is invisible in the output artifact.
90
81
 
91
82
  ### Important
92
83
 
@@ -104,90 +95,118 @@ Same extraction as Forge tier. Deep tier adds enrichment in step 4, not extracti
104
95
  - Extraction results carry forward unchanged
105
96
 
106
97
  ### Confidence
107
- - Extraction: same as Forge (T1)
98
+ - Extraction: labeled by tool, same as Forge (T1 for an ast-grep match, T1-low for an export read by eye)
108
99
  - Enrichment annotations added in step 4: T2
109
100
 
110
101
  ---
111
102
 
112
103
  ## AST Extraction Protocol
113
104
 
114
- When AST tools are available (Forge/Deep tier), follow this deterministic protocol to prevent output overflow on large codebases.
105
+ When AST tools are available (Forge, Forge+ and Deep tiers) and the step that runs this protocol gives the recipe runner's command, the runner extracts: `skf-extract-public-api.py --mode full` runs the recipes below over the files in scope in one call and writes every export, with its exact file, line, name and node kind, as JSON. The command names `--source-root` and either the brief (`--brief`, for its scope globs, scope type and language) or the files to read (`--files-from`, one path per line relative to the source root), and `-o` for the JSON, a file the step removes before the call, so that a JSON there after the call is this call's. While the runner can run, never run the recipes one at a time, batch them, or merge and dedupe their matches by hand.
115
106
 
116
- **"Files in scope"** = files remaining after applying `include_patterns` and `exclude_patterns` from the brief, filtered by the target language extension. This is not the total repository file count from step 1's tree listing. Use the filtered count from step 3 section 2 as the decision tree input.
107
+ ### Recipe Runner
117
108
 
118
- ### Decision Tree
109
+ - **Files in scope:** a file is in scope when an include glob matches it (every file, when there is none) and no exclude glob does. `**` spans any number of path segments, none included, and `*` and `?` stay inside one, so `src/**/*.ts` matches `src/index.ts` and `**/test_*` matches a top-level `test_x.py`. The runner reads those a recipe reads (`.vue` files included) and counts them in `files_in_scope`, the filtered file count; `files_without_recipes` counts by extension those it leaves, in a language no recipe reads. `skf-extract-public-api.py --help` gives the rest of its rules.
110
+ - **Recipes:** the recipes below, which SKF's shared recipe file, `ast-grep-recipes.yaml`, holds too: the `standard` set, or the `component-library` set for that scope type.
111
+ - **Head cap:** each recipe keeps at most 200 matches, or at Forge+ and Deep 500 for `scope.type: "full-library"` and 300 for `scope.type: "component-library"`, in file and line order. `truncated` is true, and so is that recipe's `truncated` in `recipes[]`, when a recipe matched more: the matches past the cap are missing, so warn about them.
112
+ - **Each export** is one name in one file, and gives `export_name`, `source_file`, `source_line` (`$NAME`'s line), `signature_line` (the source line holding it), `citation` (`[AST:{file}:L{line}]`), `ast_recipe`, `ast_node_type` (the `kind` its recipe declares), `export_type`, `from` (a re-export's module), `signature` (the declaration on one line), `params`, `return_type`, `confidence: T1` and `extraction_method: ast-grep`. Record them as they are.
113
+ - **Signatures:** a function's `params` lists each parameter (its name, type, default and whether it is optional), and is null for any other export and for a declaration the runner does not parse; `return_type` is null when the declaration states none.
114
+ - **Entry points:** `entry_point_diff` diffs the names the package's entry points export with the recipe matches (`public`, `internal`, `extraction_gaps`, `outside_scope`), `counts` gives `exports_public_api`, `exports_internal` and `effective_denominator`, and `arms` the monorepo, specific-modules and multi-subpath `exports` flags.
115
+ - **Read by eye (T1-low)** only what it leaves: each file in `file_issues` (a syntax error where a recipe can miss an export, or a file that is not UTF-8, unreadable or missing), the files `files_without_recipes` counts that hold public API (by the Quick tier Strategy), the forms Known Limitation #11 below lists, and the `extraction_gaps` names.
119
116
 
120
- Apply the first matching condition:
117
+ Act on its `status`, not its exit code: `ok`, every file in scope was read; `incomplete`, an ast-grep run failed or timed out: keep its exports and warn that the files of each `errors[]` item (how many, and the first) may be unread; `no-ast-grep`, no ast-grep it can run, so no recipe ran.
121
118
 
122
- ```
123
- Files in scope ≤ 100
124
- → Use ast-grep MCP tool: find_code(pattern, max_results=100, output_format="text")
125
- → Parse compact text output directly into extraction inventory
119
+ ### When the Runner Cannot Run
126
120
 
127
- Files in scope 101–500
128
- → Use ast-grep MCP tool: find_code_by_rule(yaml, max_results=150, output_format="text")
129
- → Use scoped YAML rules (see recipes below) to filter at the AST level
130
- → Parse compact text output into extraction inventory
121
+ There is no runner result when the step gives no runner command, no runner path resolves, or no JSON is at the `-o` path after the call (`uv` missing or failing, an input error, which prints one line on stderr, a crash, or a shell timeout that stopped it). Then, and when its `status` is `no-ast-grep`, run the recipes as **Running the Recipes Without the Runner** below says, on the filtered file list: read this file on from that heading (a run the runner serves stops there), and first load `extraction-patterns-by-hand.md`, beside this file, completely: it holds each recipe's notes and the limitations of patterns run by hand. When no ast-grep can run them either (no `ast-grep` CLI and no ast-grep MCP tool), extraction degrades to source reading: see `tier-degradation-rules.md` "AST Tool Unavailable". When the runner's `scope.languages` is empty, whatever its `status`, no recipe reads the brief's language (the runner warns `no recipe reads {language} files` and reads no file): extract by the Quick tier Strategy (source reading, T1-low).
131
122
 
132
- Files in scope > 500
133
- → CLI streaming fallback: ast-grep run --json=stream + line-by-line Python processing
134
- → Process in directory batches, cap per-batch output
135
- → Merge batch results into extraction inventory
136
- ```
123
+ **"Files in scope"** are the files the brief's include and exclude globs keep, filtered by the language's extensions, never the repository's whole file count: use the filtered count from step 3 section 2 as the decision tree input.
124
+
125
+ ### Known ast-grep Limitations
137
126
 
138
- ### Safety Valve
127
+ Limitation #11 applies to every run: the runner reads with the same recipes. #4 is the fallback of a recipe run by hand, which `find_code` serves; #1 to #3 and #5 to #10 concern patterns run by hand too, and are in `extraction-patterns-by-hand.md`.
139
128
 
140
- If any ast-grep operation (MCP or CLI) visibly causes a timeout, returns an error related to output size, or produces unexpectedly large output: immediately switch to the CLI streaming fallback with `--json=stream`. Do not retry the same approach. When falling back to the CLI streaming template, inject the brief's `scope.exclude` patterns into the `EXCLUDES` list (use `[]` if absent) — this applies regardless of which path triggered the fallback. Note: `max_results` in the MCP tool and `| head -N` in the CLI path provide hard caps, but this safety valve covers cases where the upstream tool itself fails before returning results (e.g., OOM during JSON serialization).
129
+ 4. **Fallback protocol:** If an ast-grep pattern returns errors or zero results when results are expected:
130
+ - First: retry with `find_code()` using a simpler pattern (drop type annotations, use broader match)
131
+ - Second: if `find_code()` also fails, fall back to source reading for that pattern category (T1-low confidence)
132
+ - Never silently accept zero results for a pattern category that the source language commonly uses
141
133
 
142
- ### MCP Tool Usage (Preferred)
134
+ Read the forms #11 lists by eye whether or not the runner ran:
143
135
 
144
- **Simple pattern search:**
136
+ 11. **Forms the recipes deliberately do not cover (ast-grep 0.45.3):** read these by eye at T1-low when the source uses them, or follow them with the Re-Export Tracing protocol in `extraction-patterns-tracing.md` where noted.
137
+ - **Every JS/TS `export_statement` recipe:** `export` followed by a line break before the declaration (tree-sitter reads the bare `export` as a statement of its own, so there is no `export_statement` to match; formatters never write it); an export nested in a `namespace`, `declare module` or `declare global` block, a `.d.ts` file's `declare module "pkg" { ... }` included, since it is a member, not a module export.
138
+ - **`js-exported-functions`, `react-component-functions`:** an anonymous `export default function () {}` (#3); a function exported through a list (`export { f }` and `export { f as g }`, which `js-local-exports` finds at the list's line), `export default f;` or `export = f`; a function bound to a `const` (the constants and arrow-function recipes find it); a wrapped default such as `export default memo(function Name() {})` or `forwardRef(...)`, whose value is a call, not a declaration; class, object-literal and interface methods; the implementation of an overloaded function (its signatures match instead). The React recipes also skip generator functions (an overload signature of one still matches, since a signature does not show it) and class components and keep a PascalCase function that is not a component (such as a Next.js `GET` handler).
139
+ - **`js-exported-constants`, `js-exported-arrow-functions`, `react-component-arrow-functions`, `react-wrapped-components`:** every declarator after the first one a recipe accepts in `export const a = 1, b = () => 2` (one match per statement); destructuring (`export const { x } = obj`, which has no identifier name); `let`, `var` and `export declare const`. The arrow-function recipes also skip an arrow wrapped in parentheses, `as`, `satisfies` or a call, and a `function` expression; the constants recipe reports them, and `react-wrapped-components` reports a PascalCase `memo`, `forwardRef` or `lazy` call. `react-wrapped-components` skips any other wrapper (`observer(...)`, `styled.div`, `connect(...)(Component)`), a wrapped call cast with `as` or `satisfies`, and `export default memo(...)`, whose exposed name is `default`.
140
+ - **`js-exported-classes`, `react-props-interfaces`:** `export default class` and `export default interface` (their exposed name is `default`); `export declare class`, and `export declare interface` (`ts-exported-types` finds it); a class expression bound to a `const` (the constants recipe reports it); a props type written as a type alias (`export type XProps = {...}`, which `ts-exported-types` finds); a type exported through a list (`export type { T }`, which `js-local-exports` or `js-reexports` finds).
141
+ - **`ts-exported-types`:** `export default interface` (its exposed name is `default`); an interface, type alias or enum inside a `namespace`, `declare module` or `declare global` block, a member rather than a module export; a type exported through a list (`export type { T }`, `export { type T }`), which `js-local-exports` finds, or `js-reexports` when the list has a `from`.
142
+ - **`js-reexports`, `js-namespace-reexports`, `js-local-exports`:** a bare `export * from '...'`, which exposes no name of its own; an `export { a } from '...' with { type: 'json' }` statement, or the legacy `export { a } from '...' assert { type: 'json' }`, which parse as ERROR nodes; `export default x;` and `export = x`; a list inside a `namespace`, `declare module` or `declare global` block, a member rather than a module export; in a `.js` file, `default` as a list item's name or alias (`export { default } from '...'`, `export { x as default }`), a keyword token there that no `$NAME` captures (TypeScript parses it as a name). Follow these with the Re-Export Tracing protocol.
143
+ - **`rust-public-functions`:** a `pub fn` in an `impl` block (#8); trait methods, which carry no `pub`; `pub fn` imports in an `extern "C" { }` block; functions a macro generates (`macro_rules!` and `cfg_if!` bodies are token trees, which ast-grep does not parse). One file cannot show reachability across files: a `pub fn` at the top of `foo.rs` matches whether or not `mod foo;` is `pub`, so check `pub use` re-exports with the Re-Export Tracing protocol. `#[cfg]`-gated and `#[doc(hidden)]` functions still match, and a raw identifier keeps its prefix (`r#match`).
144
+ - **`go-exported-functions`:** methods; an exported function-typed variable (`var F = func() {}`). `Test*`, `Benchmark*` and `Example*` functions in `_test.go` files, and the exported functions of `package main` or an `internal/` package, match but are not importable API: exclude them through `scope.exclude`.
145
+ - **`python-public-functions`, `python-public-classes`:** a definition under `elif __name__ == "__main__":` or a compound condition (`__name__ == "__main__" or DEBUG`), which still matches; a function made by `lambda`, `partial(...)` or `exec`, or declared `global` inside another function; a class made by `type(...)`, `NamedTuple(...)`, `TypedDict(...)` or `Enum(...)`, and an alias (`X = Other`); names created at runtime (`globals()`, `setattr`); names imported into the module (`from .x import name`: Re-Export Tracing). The recipes do not read `__all__`: a public-looking name it leaves out still matches, and a name it lists but imports from elsewhere is found by Re-Export Tracing.
146
+ - **`vue-define-props`, `vue-define-props-tsx`:** a call with two type arguments (`defineProps<A, B>()`); runtime `defineProps({ ... })` and `defineProps([...])`, which carry no type; the Options API `props:` option; `defineProps?.<X>()` and `(defineProps)<X>()`; `defineProps` inside a function, an arrow function, a class, a block, or an `if`, `for`, `while` or `switch` statement; a `<script setup>` with no `lang` (plain JavaScript, no type argument). `find_code_by_rule` never reaches a `.vue` file, and the CLI does only with a scratch `sgconfig.yml` (see the Vue note in `extraction-patterns-by-hand.md`).
145
147
 
146
- ```
147
- find_code(
148
- project_folder="{source_path}",
149
- pattern="async def $NAME($$$PARAMS)",
150
- language="python",
151
- max_results=100,
152
- output_format="text"
153
- )
154
- ```
148
+ ### Re-Export Tracing and Script/Asset Extraction
155
149
 
156
- **Scoped YAML rule search (for larger repos):**
150
+ See `extraction-patterns-tracing.md` for:
151
+ - **Re-export tracing protocol:** resolving module imports through `__init__.py`, barrel files, `pub use`
152
+ - **Script/asset extraction patterns:** detection heuristics, inclusion rules, provenance, inventory structure
153
+
154
+ ---
155
+
156
+ ## Relabel Rule
157
+
158
+ The rule that sets a provenance entry's labels from its `extraction_method` is in `relabel-rule.md`, beside this file, which compile.md §4 and validate.md §7 and §7a load themselves: load it when another reader, such as update-skill's write.md, meets a `provenance.entries[<i>].*` violation.
159
+
160
+ ---
161
+
162
+ ## Running the Recipes Without the Runner
163
+
164
+ Read this part only when **When the Runner Cannot Run** sends you here: a run the recipe runner serves stops reading this file at this heading. It holds how to run the recipes one at a time, the CLI streaming template, and the recipes themselves, which the runner's recipe file, `ast-grep-recipes.yaml`, mirrors and `kind-at` reads here.
165
+
166
+ ### Decision Tree
167
+
168
+ Apply the first matching condition:
157
169
 
158
170
  ```
159
- find_code_by_rule(
160
- project_folder="{source_path}",
161
- yaml="id: public-api\nlanguage: python\nrule:\n pattern: 'def $NAME($$$PARAMS)'\n inside:\n kind: module\n stopBy: end\nconstraints:\n NAME:\n regex: '^[^_]'",
162
- max_results=150,
163
- output_format="text"
164
- )
171
+ .vue files (the Vue recipes), at any scope size
172
+ → CLI streaming with -c {scratch}/sgconfig.yml (find_code_by_rule cannot read them; see the Vue note in extraction-patterns-by-hand.md)
173
+
174
+ Files in scope ≤ 500
175
+ → Use ast-grep MCP tool: find_code_by_rule(yaml=<recipe>, max_results=150, output_format="json")
176
+ → Run every recipe for the language (see recipes below), one call per recipe
177
+ → Read $NAME (and $SOURCE) from each match's metaVariables, and its line from $NAME
178
+ → Merge the matches into extraction inventory
179
+
180
+ Files in scope > 500
181
+ → CLI streaming: ast-grep scan -r {recipe_file} --json=stream + line-by-line Python processing
182
+ → Process in directory batches, cap per-batch output
183
+ → Merge batch results into extraction inventory
165
184
  ```
166
185
 
167
- ### CLI Streaming Fallback
186
+ `extraction-patterns-by-hand.md`'s MCP Tool Usage shows the call with a recipe as its `yaml`.
168
187
 
169
- When MCP tools are unavailable or the repo exceeds 500 files in scope, use `--json=stream` (not `--json` or `--json=pretty`, which load the whole result set into memory) with line-by-line Python processing:
188
+ ### CLI Streaming Fallback
170
189
 
171
- **Head cap selection:** The `| head -N` cap at the end of the pipeline controls how many exports are captured. Select `N` based on scope and tier:
172
- - **Default (Quick/Forge, any scope):** `N = 200`
173
- - **Forge+/Deep with `scope.type: "full-library"`:** `N = 500`
174
- - **Forge+/Deep with `scope.type: "component-library"`:** `N = 300` (components have fewer but richer exports; props interfaces are the primary API surface)
190
+ When MCP tools are unavailable or more than 500 files are in scope, stream the matches (`--json=stream`, never `--json`, which loads them all into memory) through line-by-line Python:
175
191
 
176
- For full-library skills at higher tiers, the larger cap prevents silently dropping internal module exports that maintainers need. The cap is applied AFTER exclude-pattern filtering, so useful results are not wasted on excluded files.
192
+ The `| head -N` at the end of the pipeline caps the exports kept, after the exclude filter, at the Recipe Runner's head cap for the tier and scope type (`{HEAD_CAP}` below).
177
193
 
178
194
  ```bash
179
- # Note: use $$$ for variadic params in ast-grep patterns (e.g., 'def $NAME($$$PARAMS)')
180
- # {exclude_patterns} = Python list from brief's scope.exclude, e.g. ['tests/**', '**/test_*']
181
- # If scope.exclude is absent or empty in the brief, inject [] as the default.
182
- # Patterns are matched against the full file path as emitted by ast-grep.
183
- # Ensure paths are relative to the same root as the patterns (strip ./ prefix if needed).
184
- # {HEAD_CAP} = 200 (default) or 500 (Forge+/Deep full-library) — see head cap selection above.
185
- # IMPORTANT: The explicit 'run' subcommand is required for --json=stream to work.
186
- ast-grep run -p '{pattern}' -l {language} --json=stream {path} | python3 -c "
195
+ # {recipe_file}: one recipe's YAML in a scratch file (never inline: its regexes are quoted).
196
+ # {recipe_id}, {node_kind}: its id and declared kind, each export's ast_recipe and ast_node_type.
197
+ # .vue files: add -c {scratch}/sgconfig.yml (the by-hand file's Vue note). {HEAD_CAP}: the Recipe Runner's head cap.
198
+ # {exclude_patterns}: the brief's scope.exclude as a Python list, [] when it has none.
199
+ # fnmatch is not the Files in scope rule above: its `*` also crosses `/`, and `**/x`
200
+ # needs a folder before x, so add x too for a top-level file ('test_*' beside '**/test_*').
201
+ # The patterns match ast-grep's file paths, relative to the root they name (no ./).
202
+ ast-grep scan -r {recipe_file} --json=stream {path} | python3 -c "
187
203
  import sys, json, fnmatch, signal
188
- signal.signal(signal.SIGPIPE, signal.SIG_DFL)
204
+ if hasattr(signal, 'SIGPIPE'): # POSIX only; Windows has no SIGPIPE
205
+ signal.signal(signal.SIGPIPE, signal.SIG_DFL)
189
206
 
190
207
  EXCLUDES = {exclude_patterns}
208
+ RECIPE = '{recipe_id}'
209
+ KIND = '{node_kind}'
191
210
 
192
211
  for line in sys.stdin:
193
212
  try:
@@ -195,35 +214,50 @@ for line in sys.stdin:
195
214
  f = m.get('file','')
196
215
  if EXCLUDES and any(fnmatch.fnmatch(f, pat) for pat in EXCLUDES):
197
216
  continue
198
- v = m.get('metaVariables',{})
199
- name = v.get('single',{}).get('NAME',{}).get('text','')
217
+ v = m.get('metaVariables',{}).get('single',{})
218
+ name = v.get('NAME',{}).get('text','')
200
219
  if name and not name.startswith('_'):
201
- ln = m.get('range',{}).get('start',{}).get('line',0)+1
202
- sig = m.get('text','').split(chr(10))[0].strip()
203
- print(f'[AST:{f}:L{ln}] {sig}')
220
+ ln = v['NAME'].get('range',{}).get('start',{}).get('line',0)+1
221
+ first = m.get('range',{}).get('start',{}).get('line',0)+1
222
+ text = m.get('text','').split(chr(10))
223
+ sig = text[min(ln-first, len(text)-1)].strip()
224
+ src = v.get('SOURCE',{}).get('text','')
225
+ print(f'[AST:{f}:L{ln}] {RECIPE} kind={KIND} name={name}' + (f' from={src}' if src else '') + f' {sig}')
204
226
  except: pass
205
227
  " | head -{HEAD_CAP}
206
228
  ```
207
229
 
208
- **Streaming constraints (these prevent OOM on large result sets):**
230
+ Past 500 files, run the template per top-level source folder, 20 to 50 files a batch, with the same head cap, then merge the results by export name and file, keeping the first line (overloads and a definition in both branches of an `if` repeat a name in one file).
209
231
 
210
- - Use `--json=stream`, not `--json` — the latter loads the entire array into memory
211
- - Process line-by-line (`for line in sys.stdin`), not `json.load(sys.stdin)`
212
- - Cap output with `| head -N` as a safety valve
213
- - For repos > 500 files, process in directory batches of 20-50 files each: split by top-level source directory, run the CLI streaming template per batch with the same head cap, then merge results and deduplicate by export name (keep the first occurrence if duplicates exist across batches)
232
+ **Line numbers are 0-based in ast-grep JSON** (the CLI's and the MCP tools'), and the line to cite is `$NAME`'s: every `[AST:{file}:L{line}]` citation and provenance `source_line` is `metaVariables.single.NAME.range.start.line + 1`, never the match's own first line, which for a decorated class is the decorator's.
214
233
 
215
234
  ### YAML Rule Recipes by Language
216
235
 
236
+ Each recipe declares the `kind` of the node it matches (an export it matched records it as its `ast_node_type`) and captures the export's name as `$NAME`; each was verified on ast-grep 0.45.3. The runner's recipe file, `ast-grep-recipes.yaml`, holds the same rules, and `kind-at` reads them here. To run one without the runner, `extraction-patterns-by-hand.md` gives each recipe's notes, and Known Limitation #11 above lists the forms each recipe deliberately does not cover.
237
+
217
238
  **Python — public functions:**
218
239
 
219
240
  ```yaml
220
241
  id: python-public-functions
221
242
  language: python
222
243
  rule:
223
- pattern: 'def $NAME($$$PARAMS)'
244
+ pattern: 'def $NAME'
245
+ kind: function_definition
224
246
  inside:
225
247
  kind: module
226
- stopBy: end
248
+ stopBy:
249
+ any:
250
+ - kind: function_definition
251
+ - kind: class_definition
252
+ not:
253
+ inside:
254
+ kind: block
255
+ stopBy: end
256
+ inside:
257
+ kind: if_statement
258
+ has:
259
+ field: condition
260
+ regex: '^\(?\s*(__name__\s*==\s*[''"]__main__[''"]|[''"]__main__[''"]\s*==\s*__name__)\s*\)?$'
227
261
  constraints:
228
262
  NAME:
229
263
  regex: '^[^_]'
@@ -239,23 +273,84 @@ rule:
239
273
  kind: class_definition
240
274
  inside:
241
275
  kind: module
242
- stopBy: end
276
+ stopBy:
277
+ any:
278
+ - kind: function_definition
279
+ - kind: class_definition
280
+ not:
281
+ inside:
282
+ kind: block
283
+ stopBy: end
284
+ inside:
285
+ kind: if_statement
286
+ has:
287
+ field: condition
288
+ regex: '^\(?\s*(__name__\s*==\s*[''"]__main__[''"]|[''"]__main__[''"]\s*==\s*__name__)\s*\)?$'
243
289
  constraints:
244
290
  NAME:
245
291
  regex: '^[^_]'
246
292
  ```
247
293
 
248
- > **Pattern note:** The minimal `class $NAME` pattern (with `kind: class_definition` to disambiguate the AST node) works on ast-grep 0.42.x via both MCP `find_code_by_rule` and CLI `--json=stream`. The previously documented `class $NAME($$$BASES)` and `class $NAME($$$BASES):` variants are known-broken on 0.42.x — see Known Limitations #7 below. For simple CLI extraction without a YAML rule, use `ast-grep run -p 'class $NAME' -l python --json=stream {path}` and post-filter names via the `^[^_]` regex in the Python processing step of the CLI streaming template.
249
-
250
294
  **JavaScript/TypeScript — exported functions:**
251
295
 
252
- > **Language selection:** Use `language: typescript` for `.ts` files and `language: tsx` for `.tsx` files. Patterns that work with `typescript` may return zero results with `tsx` and vice versa — they use different tree-sitter parsers. For mixed codebases, run each pattern twice (once per language) and merge results. Note: the `export function $NAME($$$PARAMS)` pattern returns **zero** with `tsx` on ast-grep 0.41.x (see Known Limitation #5) **and** with plain `typescript` on 0.42.x (see Known Limitation #9) — use source reading as the fallback for `export function` on both.
296
+ ```yaml
297
+ id: js-exported-functions
298
+ language: typescript # Use 'tsx' for .tsx files; the javascript form below for .js
299
+ rule:
300
+ kind: export_statement
301
+ inside:
302
+ kind: program
303
+ any:
304
+ - has:
305
+ field: declaration
306
+ any:
307
+ - kind: function_declaration
308
+ - kind: generator_function_declaration
309
+ - kind: function_signature
310
+ has:
311
+ field: name
312
+ pattern: $NAME
313
+ - has:
314
+ field: declaration
315
+ kind: ambient_declaration
316
+ has:
317
+ kind: function_signature
318
+ has:
319
+ field: name
320
+ pattern: $NAME
321
+ not:
322
+ has:
323
+ field: declaration
324
+ any:
325
+ - kind: function_declaration
326
+ - kind: generator_function_declaration
327
+ follows:
328
+ stopBy:
329
+ not:
330
+ kind: comment
331
+ kind: export_statement
332
+ has:
333
+ field: declaration
334
+ kind: function_signature
335
+ ```
336
+
337
+ **JavaScript: exported functions (`.js`, `.jsx`, `.mjs`, `.cjs`):**
253
338
 
254
339
  ```yaml
255
340
  id: js-exported-functions
256
- language: typescript # Use 'tsx' for .tsx files — see language selection note above
341
+ language: javascript
257
342
  rule:
258
- pattern: 'export function $NAME($$$PARAMS)'
343
+ kind: export_statement
344
+ inside:
345
+ kind: program
346
+ has:
347
+ field: declaration
348
+ any:
349
+ - kind: function_declaration
350
+ - kind: generator_function_declaration
351
+ has:
352
+ field: name
353
+ pattern: $NAME
259
354
  ```
260
355
 
261
356
  **JavaScript/TypeScript — exported constants:**
@@ -264,7 +359,26 @@ rule:
264
359
  id: js-exported-constants
265
360
  language: typescript
266
361
  rule:
267
- pattern: 'export const $NAME = $VALUE'
362
+ kind: export_statement
363
+ inside:
364
+ kind: program
365
+ has:
366
+ field: declaration
367
+ kind: lexical_declaration
368
+ all:
369
+ - has:
370
+ field: kind
371
+ regex: '^const$'
372
+ - has:
373
+ kind: variable_declarator
374
+ all:
375
+ - has:
376
+ field: name
377
+ kind: identifier
378
+ pattern: $NAME
379
+ - has:
380
+ field: value
381
+ pattern: $VALUE
268
382
  ```
269
383
 
270
384
  **JavaScript/TypeScript — exported arrow functions:**
@@ -273,25 +387,193 @@ rule:
273
387
  id: js-exported-arrow-functions
274
388
  language: typescript
275
389
  rule:
276
- pattern: 'export const $NAME = ($$$PARAMS) => $BODY'
390
+ kind: export_statement
391
+ inside:
392
+ kind: program
393
+ has:
394
+ field: declaration
395
+ kind: lexical_declaration
396
+ all:
397
+ - has:
398
+ field: kind
399
+ regex: '^const$'
400
+ - has:
401
+ kind: variable_declarator
402
+ all:
403
+ - has:
404
+ field: name
405
+ kind: identifier
406
+ pattern: $NAME
407
+ - has:
408
+ field: value
409
+ kind: arrow_function
410
+ pattern: $VALUE
277
411
  ```
278
412
 
279
- > **JS/TS Pattern Merging:** Modern TypeScript codebases often use `export const` exclusively for all exports (arrow functions, objects, constants). Run ALL four JS/TS patterns (functions, arrow functions, constants, classes) and merge results by `$NAME`. Priority when deduplicating: arrow function match > function declaration match > constant match. Arrow function matches capture parameters directly; constant matches require inspecting `$VALUE` to extract signatures.
280
-
281
413
  **JavaScript/TypeScript — exported classes:**
282
414
 
283
415
  ```yaml
284
416
  id: js-exported-classes
285
417
  language: typescript
286
418
  rule:
287
- pattern: 'export class $NAME { $$$ }'
419
+ kind: export_statement
420
+ inside:
421
+ kind: program
422
+ not:
423
+ has:
424
+ regex: '^default$'
425
+ has:
426
+ field: declaration
427
+ all:
428
+ - has:
429
+ field: name
430
+ pattern: $NAME
431
+ - has:
432
+ field: body
433
+ kind: class_body
288
434
  ```
289
435
 
290
- > **Important:** The body (`{ $$$ }`) is required on ast-grep 0.42.x. The bare `export class $NAME` pattern returns zero matches — and emits a `Pattern contains an ERROR node` warning — through **both** `find_code()` and the CLI, because an incomplete class declaration does not parse as a complete statement (see Known Limitation #9). With the body present, the simple `find_code()` pattern detects class exports reliably — it matches non-generic classes only; generic (`export class $NAME<T>`) and generic-extends (`export class $NAME<T> extends $BASE`) forms are skipped, so source-read them via `^export (abstract )?class` and merge by name+file (see Known Limitation #10). `find_code_by_rule` would additionally require an explicit AST `kind` rule.
436
+ **TypeScript: exported interfaces, type aliases and enums:**
291
437
 
292
- **JavaScript/TypeScript — re-export detection (use `find_code`):**
438
+ ```yaml
439
+ id: ts-exported-types
440
+ language: typescript # Use 'tsx' for .tsx files
441
+ rule:
442
+ kind: export_statement
443
+ inside:
444
+ kind: program
445
+ not:
446
+ has:
447
+ regex: '^default$'
448
+ has:
449
+ field: declaration
450
+ any:
451
+ - any:
452
+ - kind: interface_declaration
453
+ - kind: type_alias_declaration
454
+ - kind: enum_declaration
455
+ has:
456
+ field: name
457
+ pattern: $NAME
458
+ - kind: ambient_declaration
459
+ has:
460
+ any:
461
+ - kind: interface_declaration
462
+ - kind: type_alias_declaration
463
+ - kind: enum_declaration
464
+ has:
465
+ field: name
466
+ pattern: $NAME
467
+ ```
293
468
 
294
- Use `find_code()` with pattern `export { $$$NAMES } from $SOURCE` for re-export detection. Note: this pattern may produce multiple AST node matches. Post-process results to split comma-separated names from `$$$NAMES`. For complex re-export chains (aliased exports, default re-exports, namespace re-exports), fall back to the Re-Export Tracing protocol in `extraction-patterns-tracing.md`.
469
+ **JavaScript/TypeScript: re-exported names:**
470
+
471
+ ```yaml
472
+ id: js-reexports
473
+ language: typescript
474
+ rule:
475
+ kind: export_specifier
476
+ inside:
477
+ kind: export_clause
478
+ inside:
479
+ kind: export_statement
480
+ inside:
481
+ kind: program
482
+ has:
483
+ field: source
484
+ has:
485
+ kind: string_fragment
486
+ pattern: $SOURCE
487
+ any:
488
+ - has:
489
+ field: alias
490
+ any:
491
+ - has:
492
+ kind: string_fragment
493
+ pattern: $NAME
494
+ - not:
495
+ kind: string
496
+ pattern: $NAME
497
+ - all:
498
+ - not:
499
+ has:
500
+ field: alias
501
+ regex: '.'
502
+ - has:
503
+ field: name
504
+ any:
505
+ - has:
506
+ kind: string_fragment
507
+ pattern: $NAME
508
+ - not:
509
+ kind: string
510
+ pattern: $NAME
511
+ ```
512
+
513
+ **JavaScript/TypeScript: namespace re-exports:**
514
+
515
+ ```yaml
516
+ id: js-namespace-reexports
517
+ language: typescript
518
+ rule:
519
+ kind: export_statement
520
+ inside:
521
+ kind: program
522
+ all:
523
+ - has:
524
+ field: source
525
+ has:
526
+ kind: string_fragment
527
+ pattern: $SOURCE
528
+ - has:
529
+ kind: namespace_export
530
+ any:
531
+ - has:
532
+ kind: identifier
533
+ pattern: $NAME
534
+ - has:
535
+ kind: string
536
+ has:
537
+ kind: string_fragment
538
+ pattern: $NAME
539
+ ```
540
+
541
+ **JavaScript/TypeScript: local export lists:**
542
+
543
+ ```yaml
544
+ id: js-local-exports
545
+ language: typescript # Use 'tsx' for .tsx files, 'javascript' for .js files
546
+ rule:
547
+ kind: export_specifier
548
+ inside:
549
+ kind: export_clause
550
+ inside:
551
+ kind: export_statement
552
+ inside:
553
+ kind: program
554
+ not:
555
+ has:
556
+ field: source
557
+ kind: string
558
+ any:
559
+ - has:
560
+ field: alias
561
+ any:
562
+ - has:
563
+ kind: string_fragment
564
+ pattern: $NAME
565
+ - not:
566
+ kind: string
567
+ pattern: $NAME
568
+ - all:
569
+ - not:
570
+ has:
571
+ field: alias
572
+ regex: '.'
573
+ - has:
574
+ field: name
575
+ pattern: $NAME
576
+ ```
295
577
 
296
578
  **Rust — public functions:**
297
579
 
@@ -299,9 +581,32 @@ Use `find_code()` with pattern `export { $$$NAMES } from $SOURCE` for re-export
299
581
  id: rust-public-functions
300
582
  language: rust
301
583
  rule:
302
- any:
303
- - pattern: 'pub fn $NAME($$$PARAMS) -> $RET'
304
- - pattern: 'pub fn $NAME($$$PARAMS)'
584
+ kind: function_item
585
+ has:
586
+ field: name
587
+ pattern: $NAME
588
+ all:
589
+ - has:
590
+ kind: visibility_modifier
591
+ regex: '^pub$'
592
+ - inside:
593
+ any:
594
+ - kind: source_file
595
+ - kind: declaration_list
596
+ inside:
597
+ kind: mod_item
598
+ - not:
599
+ inside:
600
+ kind: mod_item
601
+ stopBy: end
602
+ not:
603
+ has:
604
+ kind: visibility_modifier
605
+ regex: '^pub$'
606
+ - not:
607
+ inside:
608
+ kind: block
609
+ stopBy: end
305
610
  ```
306
611
 
307
612
  **Go — exported functions (capitalized):**
@@ -310,12 +615,17 @@ rule:
310
615
  id: go-exported-functions
311
616
  language: go
312
617
  rule:
313
- any:
314
- - pattern: 'func $NAME($$$PARAMS) $RET'
315
- - pattern: 'func $NAME($$$PARAMS)'
618
+ kind: function_declaration
619
+ has:
620
+ field: name
621
+ pattern: $NAME
622
+ not:
623
+ has:
624
+ field: receiver
625
+ kind: parameter_list
316
626
  constraints:
317
627
  NAME:
318
- regex: '^[A-Z]'
628
+ regex: '^\p{Lu}'
319
629
  ```
320
630
 
321
631
  ### Component Library YAML Rule Recipes
@@ -328,7 +638,18 @@ These patterns are used by `component-extraction.md` when `scope.type: "componen
328
638
  id: react-props-interfaces
329
639
  language: typescript # Use 'tsx' for .tsx files
330
640
  rule:
331
- pattern: 'export interface $NAME { $$$ }'
641
+ kind: export_statement
642
+ inside:
643
+ kind: program
644
+ not:
645
+ has:
646
+ regex: '^default$'
647
+ has:
648
+ field: declaration
649
+ kind: interface_declaration
650
+ has:
651
+ field: name
652
+ pattern: $NAME
332
653
  constraints:
333
654
  NAME:
334
655
  regex: '.*Props$'
@@ -336,28 +657,133 @@ constraints:
336
657
 
337
658
  **React/TypeScript — Component function exports (PascalCase):**
338
659
 
339
- > **Language note:** Use `language: tsx` for `.tsx` files. The `export function` pattern may fail with tsx on ast-grep 0.41.x (see Known Limitations #5). Use `export const` patterns as primary and fall back to source reading for `export function` in tsx files.
340
-
341
660
  ```yaml
342
661
  id: react-component-functions
343
662
  language: tsx
344
663
  rule:
345
- pattern: 'export function $NAME($$$PARAMS)'
664
+ kind: export_statement
665
+ inside:
666
+ kind: program
667
+ any:
668
+ - has:
669
+ field: declaration
670
+ any:
671
+ - kind: function_declaration
672
+ - kind: function_signature
673
+ has:
674
+ field: name
675
+ pattern: $NAME
676
+ - has:
677
+ field: declaration
678
+ kind: ambient_declaration
679
+ has:
680
+ kind: function_signature
681
+ has:
682
+ field: name
683
+ pattern: $NAME
684
+ not:
685
+ has:
686
+ field: declaration
687
+ kind: function_declaration
688
+ follows:
689
+ stopBy:
690
+ not:
691
+ kind: comment
692
+ kind: export_statement
693
+ has:
694
+ field: declaration
695
+ kind: function_signature
346
696
  constraints:
347
697
  NAME:
348
- regex: '^[A-Z]'
698
+ regex: '^\p{Lu}'
699
+ ```
700
+
701
+ **React/JavaScript: component function exports (`.js`, `.jsx`, `.mjs`, `.cjs`):**
702
+
703
+ ```yaml
704
+ id: react-component-functions
705
+ language: javascript
706
+ rule:
707
+ kind: export_statement
708
+ inside:
709
+ kind: program
710
+ has:
711
+ field: declaration
712
+ kind: function_declaration
713
+ has:
714
+ field: name
715
+ pattern: $NAME
716
+ constraints:
717
+ NAME:
718
+ regex: '^\p{Lu}'
349
719
  ```
350
720
 
351
721
  **React/TypeScript — Component arrow function exports:**
352
722
 
353
723
  ```yaml
354
724
  id: react-component-arrow-functions
355
- language: typescript
725
+ language: typescript # Use 'tsx' for .tsx files
356
726
  rule:
357
- pattern: 'export const $NAME = ($$$PARAMS) => $BODY'
727
+ kind: export_statement
728
+ inside:
729
+ kind: program
730
+ has:
731
+ field: declaration
732
+ kind: lexical_declaration
733
+ all:
734
+ - has:
735
+ field: kind
736
+ regex: '^const$'
737
+ - has:
738
+ kind: variable_declarator
739
+ all:
740
+ - has:
741
+ field: name
742
+ kind: identifier
743
+ regex: '^\p{Lu}'
744
+ pattern: $NAME
745
+ - has:
746
+ field: value
747
+ kind: arrow_function
748
+ pattern: $VALUE
358
749
  constraints:
359
750
  NAME:
360
- regex: '^[A-Z]'
751
+ regex: '^\p{Lu}'
752
+ ```
753
+
754
+ **React: wrapped component exports (`memo`, `forwardRef`, `lazy`):**
755
+
756
+ ```yaml
757
+ id: react-wrapped-components
758
+ language: tsx
759
+ rule:
760
+ kind: export_statement
761
+ inside:
762
+ kind: program
763
+ has:
764
+ field: declaration
765
+ kind: lexical_declaration
766
+ all:
767
+ - has:
768
+ field: kind
769
+ regex: '^const$'
770
+ - has:
771
+ kind: variable_declarator
772
+ all:
773
+ - has:
774
+ field: name
775
+ kind: identifier
776
+ regex: '^\p{Lu}'
777
+ pattern: $NAME
778
+ - has:
779
+ field: value
780
+ kind: call_expression
781
+ has:
782
+ field: function
783
+ regex: '^(React\.)?(memo|forwardRef|lazy)$'
784
+ constraints:
785
+ NAME:
786
+ regex: '^\p{Lu}'
361
787
  ```
362
788
 
363
789
  **Vue — defineProps extraction:**
@@ -366,78 +792,40 @@ constraints:
366
792
  id: vue-define-props
367
793
  language: typescript
368
794
  rule:
369
- pattern: 'defineProps<$TYPE>()'
795
+ pattern: 'defineProps<$NAME>()'
796
+ kind: call_expression
797
+ not:
798
+ inside:
799
+ stopBy: end
800
+ any:
801
+ - kind: statement_block
802
+ - kind: arrow_function
803
+ - kind: class_body
804
+ - kind: if_statement
805
+ - kind: for_statement
806
+ - kind: for_in_statement
807
+ - kind: while_statement
808
+ - kind: switch_case
370
809
  ```
371
810
 
372
- **Props-to-Component linking strategy:**
373
-
374
- After extracting Props interfaces and component exports, link them using this 3-level fallback chain:
375
-
376
- 1. **Naming convention (primary):** Strip `Props` suffix from interface name → match to component export (e.g., `NativeLiquidButtonProps` → `NativeLiquidButton`)
377
- 2. **File co-location (fallback):** If naming doesn't match, check if a Props interface and a PascalCase export function are defined in the same file — link them
378
- 3. **Generic parameter (deep fallback):** Search for `ComponentProps<typeof $NAME>` or `React.ComponentProps<typeof $NAME>` patterns that reference the component by name
379
-
380
- Unlinked Props interfaces are included as standalone type exports. Unlinked component exports are included with a note that no Props interface was found (signature-only, T1-low confidence for API contract).
381
-
382
- ### Known ast-grep Limitations
383
-
384
- When using ast-grep for extraction, be aware of these documented limitations:
385
-
386
- 1. **`export class $NAME` needs a body on 0.42.x; `find_code_by_rule` needs explicit `kind`:** The bare `export class $NAME` pattern returns zero through **both** `find_code()` and the CLI on ast-grep 0.42.x — add the body, `export class $NAME { $$$ }` (see #9). With `find_code_by_rule`, a class export additionally needs a `kind` rule for the tree-sitter node type; the simpler `find_code()` with the body-form pattern is the lighter path.
387
-
388
- 2. **Re-export patterns produce multiple AST nodes:** `export { A, B, C } from './module'` decomposes into multiple metavariable bindings for `$$$NAMES`. Results require post-processing to split comma-separated names.
389
-
390
- 3. **Default anonymous exports capture no name:** `export default function $NAME` works, but `export default $EXPR` (anonymous default export) captures no name in `$NAME`. Fall back to source reading (T1-low) for anonymous defaults.
391
-
392
- 4. **Fallback protocol:** If an ast-grep pattern returns errors or zero results when results are expected:
393
- - First: retry with `find_code()` using a simpler pattern (drop type annotations, use broader match)
394
- - Second: if `find_code()` also fails, fall back to source reading for that pattern category (T1-low confidence)
395
- - Never silently accept zero results for a pattern category that the source language commonly uses
396
-
397
- 5. **TSX `export function` pattern failure:** The `export function $NAME($$$PARAMS)` pattern may return zero results in TSX files with ast-grep 0.41.x. This affects both MCP tools and CLI. `export const` and `export type` patterns are unaffected. **Workaround:** For TSX files, use `export const` patterns first (which work), then fall back to source reading (grep/file read) for `export function` declarations. When a TSX codebase shows zero `export function` matches but source files clearly contain them, this is a known ast-grep tree-sitter tsx parser limitation — not an extraction error. Log it in the evidence report and proceed with T1-low confidence for those exports.
398
-
399
- 6. **CLI `--json=stream` may produce no output:** On ast-grep 0.41.x, `--json=stream` may produce empty output for certain patterns. The `--json=stream` flag requires the explicit `run` subcommand: use `ast-grep run -p '{pattern}' --json=stream` (not `ast-grep -p '{pattern}' --json=stream`). If streaming still produces no output, fall back to the MCP tool or source reading.
400
-
401
- 7. **Python class patterns with bases/colon return zero (ast-grep 0.42.x):** The patterns `class $NAME($$$BASES)` and `class $NAME($$$BASES):` return zero matches on real Python sources with ast-grep 0.42.0, even on files containing dozens of subclassed public classes. `find_code_by_rule` also rejects the bare inline rule without `kind` as `Rule must specify a set of AST kinds to match. Try adding \`kind\` rule.` **Workaround:** Use the minimal `class $NAME` pattern with `kind: class_definition` (YAML) or `ast-grep run -p 'class $NAME' -l python --json=stream` (CLI), then post-filter names via the `^[^_]` regex. The `^[^_]` constraint enforces the "public" filter since ast-grep's base-match rule is what's broken, not the name-match rule. See the Python — public classes recipe above.
402
-
403
- 8. **Rust `pub fn` any-pattern returns zero; bare `pub fn $NAME` over-captures (ast-grep 0.42.x):** The `rust-public-functions` recipe's `any:` of `pub fn $NAME($$$PARAMS) -> $RET` / `pub fn $NAME($$$PARAMS)` returns "No matches found" on real Rust sources with ast-grep 0.42.2, even on crates containing 200+ public functions. Dropping to the bare `pub fn $NAME` pattern matches, but over-captures restricted-visibility functions such as `pub(crate) fn` / `pub(super) fn`, which are **not** public API. **Workaround:** Prefer a visibility-constrained source grep — `rg '^\s*pub fn ' <src>` filtered to exclude lines beginning `pub(` — cross-checked against the AN-verified public surface, at T1-low confidence. Never silently accept zero results for Rust public functions, and never treat a bare `pub fn $NAME` match set as the public API without stripping `pub(...)`-restricted items. See the Rust — public functions recipe above.
404
-
405
- 9. **Plain `language: typescript` declaration patterns without a body return zero (ast-grep 0.42.x):** For `language: typescript` on ast-grep 0.42.2, the incomplete-statement patterns `export class $NAME`, `export function $NAME($$$PARAMS)`, `export type $NAME`, and `export enum $NAME` all return **zero** matches against real `.ts` sources — `export class` / `export type` / `export enum` additionally print `Pattern contains an ERROR node`. This affects the CLI (`ast-grep run -p ... -l typescript`) and the MCP `find_code()` API **identically** — `find_code()` is not a workaround. Only `export const $NAME = $VALUE` matches as documented. The cause is that a declaration pattern missing its body/initializer does not parse as a complete statement. **Workarounds (verified on 0.42.2 via both CLI and `find_code`):**
406
- - **class:** add the body — `export class $NAME { $$$ }` matches.
407
- - **enum:** add the body — `export enum $NAME { $$$ }` matches.
408
- - **type alias:** drop `export` and include the initializer — `type $NAME = $T` matches (and captures both `export type` and bare `type` declarations).
409
- - **interface:** `export interface $NAME { $$$ }` already carries a body and **works** for plain interfaces — but it misses generic (`interface $NAME<T>`) and `extends` forms; source-read those at T1-low.
410
- - **function:** the body form `export function $NAME($$$PARAMS) { $$$ }` matches only functions with no return-type annotation and no `async` modifier, so it is unreliable. Prefer a source-read fallback (or a barrel cross-check) at T1-low for `export function`, mirroring the tsx guidance in #5.
411
-
412
- Never silently accept zero results for a declaration form the source language commonly uses.
413
-
414
- 10. **Generic class declarations do not match non-generic class patterns (ast-grep 0.42.x):** The non-generic patterns `export class $NAME { $$$ }` and `export class $NAME extends $BASE { $$$ }` silently skip every generic form on ast-grep 0.42.2 — verified via both the CLI (`ast-grep run -p ... -l typescript`) and `find_code()`, the first matched only a plain `Plain` and the second only a plain `PlainExtends` on a fixture of `Plain` / `Generic<T>` / `GenericExtends<T> extends Base<T>` / `abstract AbstractGeneric<T>` / `PlainExtends`. The per-shape generic patterns `export class $NAME<$$$P> { $$$ }`, `export class $NAME<$$$P> extends $BASE { $$$ }`, and `export abstract class $NAME<$$$P> { $$$ }` each match exactly **one** shape — no single pattern covers all of them. **Workaround:** Always run a source-read fallback `^export (abstract )?class` over the in-scope `.ts` sources (T1-low) and merge by name+file with the AST results, mirroring the `export function` guidance in #9. The bare `class $NAME` pattern catches every form but over-captures non-exported classes, so it still needs the source-read pass to re-impose the exported-only filter — this is the opposite trade from the Python #7 `^[^_]` re-filter, since TS has no name-prefix convention for exports. Never accept a class inventory that omits generic classes when the source uses them.
415
-
416
- ### Component Library Demo/Example Auto-Exclusion
417
-
418
- When `scope.type: "component-library"`, auto-detect and propose demo/example exclusions before extraction begins. **User confirmation is required before applying** — some `examples/` directories contain API-level code.
419
-
420
- **Auto-detect directory patterns:**
421
- - `**/demo/**`, `**/demos/**`
422
- - `**/stories/**`, `**/__stories__/**`, `**/storybook/**`
423
- - `**/examples/**`, `**/example/**`
424
-
425
- **Auto-detect file patterns:**
426
- - `**/*.stories.*`, `**/*.story.*`
427
- - `**/*.example.*`, `**/*.demo.*`
428
-
429
- If `demo_patterns` is specified in the brief, use those instead of auto-detection.
430
-
431
- **Procedure:**
432
- 1. Scan the scoped file tree for matching directories and files
433
- 2. Count matches per pattern category
434
- 3. Present to user: "**Auto-detected {N} demo/example files** in {M} directories matching these patterns: {list}. Confirm exclusion? [Y/n] Or adjust patterns:"
435
- 4. Apply confirmed patterns to the exclude list before AST extraction
436
- 5. Record in extraction inventory: `demo_files_excluded: {count}`
437
-
438
- ### Re-Export Tracing and Script/Asset Extraction
439
-
440
- See `extraction-patterns-tracing.md` for:
441
- - **Re-export tracing protocol** — resolving module imports through `__init__.py`, barrel files, `pub use`
442
- - **Script/asset extraction patterns** — detection heuristics, inclusion rules, provenance, inventory structure
811
+ **Vue: defineProps extraction for `<script setup lang="tsx">`:**
443
812
 
813
+ ```yaml
814
+ id: vue-define-props-tsx
815
+ language: tsx
816
+ rule:
817
+ pattern: 'defineProps<$NAME>()'
818
+ kind: call_expression
819
+ not:
820
+ inside:
821
+ stopBy: end
822
+ any:
823
+ - kind: statement_block
824
+ - kind: arrow_function
825
+ - kind: class_body
826
+ - kind: if_statement
827
+ - kind: for_statement
828
+ - kind: for_in_statement
829
+ - kind: while_statement
830
+ - kind: switch_case
831
+ ```