bmad-module-skill-forge 2.2.0 → 4.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.
- package/.claude-plugin/marketplace.json +1 -1
- package/README.md +49 -49
- package/docs/404.md +10 -8
- package/docs/_data/pinned.yaml +22 -13
- package/docs/_internal/STABILITY.md +58 -35
- package/docs/agents.md +39 -53
- package/docs/architecture.md +65 -51
- package/docs/bmad-synergy.md +53 -51
- package/docs/campaign.md +127 -75
- package/docs/concepts.md +34 -28
- package/docs/examples.md +70 -68
- package/docs/forge-auto.md +34 -31
- package/docs/getting-started.md +118 -57
- package/docs/how-it-works.md +25 -21
- package/docs/index.md +15 -15
- package/docs/skill-model.md +70 -59
- package/docs/troubleshooting.md +143 -18
- package/docs/verifying-a-skill.md +87 -57
- package/docs/why-skf.md +24 -22
- package/docs/workflows.md +274 -110
- package/package.json +18 -10
- package/src/forger/preferences.yaml +8 -1
- package/src/knowledge/agentskills-spec.md +2 -0
- package/src/knowledge/ccc-bridge.md +40 -24
- package/src/knowledge/confidence-tiers.md +9 -5
- package/src/knowledge/overview.md +3 -3
- package/src/knowledge/progressive-capability.md +8 -8
- package/src/knowledge/provenance-tracking.md +9 -5
- package/src/knowledge/qmd-registry.md +3 -3
- package/src/knowledge/skf-knowledge-index.csv +2 -2
- package/src/knowledge/skill-lifecycle.md +18 -11
- package/src/knowledge/split-body-strategy.md +0 -3
- package/src/knowledge/tool-resolution.md +4 -2
- package/src/knowledge/version-paths.md +101 -15
- package/src/module-help.csv +3 -3
- package/src/module.yaml +10 -0
- package/src/shared/data/ast-grep-recipes.yaml +608 -0
- package/src/shared/data/ide-context-files.json +30 -0
- package/src/shared/health-check.md +31 -59
- package/src/shared/references/description-guard-protocol.md +12 -9
- package/src/shared/references/feasibility-report-schema.md +19 -13
- package/src/shared/references/headless-gate-convention.md +55 -3
- package/src/shared/references/output-contract-schema.md +19 -8
- package/src/shared/references/pipeline-contracts.md +94 -29
- package/src/shared/references/tessl-review.md +77 -0
- package/src/shared/scripts/schemas/skf-analyze-result-envelope.v1.json +194 -0
- package/src/shared/scripts/schemas/skf-audit-result-envelope.v1.json +192 -0
- package/src/shared/scripts/schemas/skf-brief-result-envelope.v1.json +32 -4
- package/src/shared/scripts/schemas/skf-campaign-result-envelope.v1.json +174 -0
- package/src/shared/scripts/schemas/skf-create-skill-result-envelope.v1.json +284 -0
- package/src/shared/scripts/schemas/skf-drop-skill-result-envelope.v1.json +164 -0
- package/src/shared/scripts/schemas/skf-export-result-envelope.v1.json +160 -0
- package/src/shared/scripts/schemas/skf-quick-skill-result-envelope.v1.json +261 -0
- package/src/shared/scripts/schemas/skf-refine-architecture-result-envelope.v1.json +192 -0
- package/src/shared/scripts/schemas/skf-rename-skill-result-envelope.v1.json +179 -0
- package/src/shared/scripts/schemas/skf-setup-result-envelope.v1.json +27 -11
- package/src/shared/scripts/schemas/skf-stack-result-envelope.v1.json +176 -0
- package/src/shared/scripts/schemas/skf-test-result-envelope.v1.json +173 -0
- package/src/shared/scripts/schemas/skf-update-result-envelope.v1.json +19 -5
- package/src/shared/scripts/schemas/skf-verify-stack-result-envelope.v1.json +180 -0
- package/src/shared/scripts/schemas/skill-brief.v1.json +34 -0
- package/src/shared/scripts/schemas/workspace-detection.v1.json +1 -1
- package/src/shared/scripts/skf-atomic-write.py +233 -64
- package/src/shared/scripts/skf-build-change-manifest.py +2753 -28
- package/src/shared/scripts/skf-ccc-git-hygiene.py +532 -0
- package/src/shared/scripts/skf-check-unit-records.py +184 -0
- package/src/shared/scripts/skf-check-workspace-drift.py +538 -9
- package/src/shared/scripts/skf-classify-changed-files.py +772 -0
- package/src/shared/scripts/skf-comention-pairs.py +1333 -123
- package/src/shared/scripts/skf-compare-file-hashes.py +267 -105
- package/src/shared/scripts/skf-count-imports.py +1867 -0
- package/src/shared/scripts/skf-count-tokens.py +1 -1
- package/src/shared/scripts/skf-derive-assembly-shape.py +21 -3
- package/src/shared/scripts/skf-description-guard.py +99 -11
- package/src/shared/scripts/skf-detect-docs.py +694 -2
- package/src/shared/scripts/skf-detect-language.py +486 -107
- package/src/shared/scripts/skf-detect-registry.py +887 -0
- package/src/shared/scripts/skf-detect-scripts-assets.py +197 -55
- package/src/shared/scripts/skf-detect-tools.py +209 -27
- package/src/shared/scripts/skf-detect-workspaces.py +506 -30
- package/src/shared/scripts/skf-disqualify-candidates.py +821 -182
- package/src/shared/scripts/skf-emit-brief-result-envelope.py +69 -191
- package/src/shared/scripts/skf-emit-result-envelope.py +1604 -106
- package/src/shared/scripts/skf-enumerate-stack-skills.py +986 -117
- package/src/shared/scripts/skf-extract-public-api.py +5247 -96
- package/src/shared/scripts/skf-extraction-inventory.py +1280 -0
- package/src/shared/scripts/skf-extraction-snapshot.py +664 -0
- package/src/shared/scripts/skf-fetch-temporal.py +674 -0
- package/src/shared/scripts/skf-find-cycles.py +97 -29
- package/src/shared/scripts/skf-find-test-report.py +476 -0
- package/src/shared/scripts/skf-forge-tier-rw.py +469 -205
- package/src/shared/scripts/skf-github-fetch.py +365 -0
- package/src/shared/scripts/skf-github-probe.py +700 -0
- package/src/shared/scripts/skf-hash-content.py +180 -20
- package/src/shared/scripts/skf-language-corpora.py +1 -1
- package/src/shared/scripts/skf-load-provenance.py +161 -27
- package/src/shared/scripts/skf-manifest-ops.py +1 -1
- package/src/shared/scripts/skf-merge-ccc-exclusions.py +2307 -223
- package/src/shared/scripts/skf-merge-doc-urls.py +143 -20
- package/src/shared/scripts/skf-names-present.py +221 -0
- package/src/shared/scripts/skf-pair-intersect.py +119 -28
- package/src/shared/scripts/skf-parse-gaps.py +1384 -0
- package/src/shared/scripts/skf-preapply.py +1 -1
- package/src/shared/scripts/skf-preflight.py +176 -28
- package/src/shared/scripts/skf-promote-staged.py +430 -0
- package/src/shared/scripts/skf-provenance-gap-dispatch.py +64 -8
- package/src/shared/scripts/skf-qmd-classify-collections.py +229 -45
- package/src/shared/scripts/skf-quick-batch.py +774 -0
- package/src/shared/scripts/skf-rebuild-managed-sections.py +972 -88
- package/src/shared/scripts/skf-recommend-scope-type.py +323 -156
- package/src/shared/scripts/skf-render-metadata-stats.py +156 -12
- package/src/shared/scripts/skf-render-quick-metadata.py +132 -28
- package/src/shared/scripts/skf-render-stack-metadata.py +976 -0
- package/src/shared/scripts/skf-resolve-authoritative-files.py +155 -14
- package/src/shared/scripts/skf-resolve-package.py +523 -106
- package/src/shared/scripts/skf-rewrite-skill-name.py +433 -47
- package/src/shared/scripts/skf-run-lock.py +577 -0
- package/src/shared/scripts/skf-scan-manifests.py +998 -57
- package/src/shared/scripts/skf-scan-skill-md-structure.py +1341 -37
- package/src/shared/scripts/skf-severity-classify.py +712 -108
- package/src/shared/scripts/skf-shape-detect.py +298 -204
- package/src/shared/scripts/skf-shard-body.py +9 -8
- package/src/shared/scripts/skf-skill-inventory.py +2412 -89
- package/src/shared/scripts/skf-skills-module.py +472 -0
- package/src/shared/scripts/skf-source-tree.py +1796 -0
- package/src/shared/scripts/skf-structural-diff.py +787 -121
- package/src/shared/scripts/skf-tessl-review.py +706 -0
- package/src/shared/scripts/skf-update-active-symlink.py +120 -67
- package/src/shared/scripts/skf-update-run-state.py +624 -0
- package/src/shared/scripts/skf-validate-brief-inputs.py +92 -29
- package/src/shared/scripts/skf-validate-brief-schema.py +23 -2
- package/src/shared/scripts/skf-validate-feasibility-report.py +577 -66
- package/src/shared/scripts/skf-validate-frontmatter.py +103 -26
- package/src/shared/scripts/skf-validate-output.py +712 -64
- package/src/shared/scripts/skf-validate-pins.py +1 -1
- package/src/shared/scripts/skf-verify-no-trace.py +116 -10
- package/src/shared/scripts/skf-verify-provenance-completeness.py +3945 -152
- package/src/shared/scripts/skf-write-skill-brief.py +406 -25
- package/src/shared/tool-requirements.yaml +201 -0
- package/src/skf-analyze-source/SKILL.md +41 -36
- package/src/skf-analyze-source/assets/skill-brief-schema.md +7 -8
- package/src/skf-analyze-source/customize.toml +36 -24
- package/src/skf-analyze-source/references/auto-docs-only.md +58 -20
- package/src/skf-analyze-source/references/continue.md +2 -13
- package/src/skf-analyze-source/references/discover-additional-source.md +147 -0
- package/src/skf-analyze-source/references/generate-briefs.md +121 -55
- package/src/skf-analyze-source/references/headless-contract.md +42 -15
- package/src/skf-analyze-source/references/identify-units.md +96 -110
- package/src/skf-analyze-source/references/init.md +73 -71
- package/src/skf-analyze-source/references/map-and-detect.md +123 -105
- package/src/skf-analyze-source/references/map-unit-exports.md +68 -0
- package/src/skf-analyze-source/references/recommend.md +51 -70
- package/src/skf-analyze-source/references/scan-project.md +60 -84
- package/src/skf-analyze-source/references/scan-root.md +39 -0
- package/src/skf-analyze-source/references/step-auto-scope-coexistence.md +47 -0
- package/src/skf-analyze-source/references/step-auto-scope-corpora.md +53 -0
- package/src/skf-analyze-source/references/step-auto-scope-split.md +91 -0
- package/src/skf-analyze-source/references/step-auto-scope.md +208 -293
- package/src/skf-analyze-source/references/step-shape-detect.md +14 -12
- package/src/skf-analyze-source/references/unit-detection-heuristics.md +37 -42
- package/src/skf-audit-skill/SKILL.md +43 -51
- package/src/skf-audit-skill/assets/drift-report-template.md +28 -3
- package/src/skf-audit-skill/customize.toml +41 -20
- package/src/skf-audit-skill/references/constituent-freshness.md +91 -0
- package/src/skf-audit-skill/references/doc-drift.md +141 -0
- package/src/skf-audit-skill/references/headless-contract.md +29 -0
- package/src/skf-audit-skill/references/health-check.md +2 -0
- package/src/skf-audit-skill/references/init.md +149 -210
- package/src/skf-audit-skill/references/re-index.md +103 -110
- package/src/skf-audit-skill/references/report.md +96 -33
- package/src/skf-audit-skill/references/semantic-diff.md +34 -25
- package/src/skf-audit-skill/references/severity-classify.md +79 -65
- package/src/skf-audit-skill/references/severity-rules.md +15 -6
- package/src/skf-audit-skill/references/structural-diff.md +80 -90
- package/src/skf-audit-skill/references/upstream-checkout.md +77 -0
- package/src/skf-audit-skill/scripts/render-drift-tables.py +712 -0
- package/src/skf-brief-skill/SKILL.md +37 -26
- package/src/skf-brief-skill/assets/description-voice-examples.md +1 -1
- package/src/skf-brief-skill/assets/scope-templates.md +18 -25
- package/src/skf-brief-skill/assets/skill-brief-schema.md +30 -23
- package/src/skf-brief-skill/customize.toml +48 -20
- package/src/skf-brief-skill/references/analyze-target.md +144 -127
- package/src/skf-brief-skill/references/confirm-brief.md +11 -9
- package/src/skf-brief-skill/references/draft-checkpoint.md +36 -17
- package/src/skf-brief-skill/references/gather-intent-ratify.md +68 -0
- package/src/skf-brief-skill/references/gather-intent.md +87 -182
- package/src/skf-brief-skill/references/headless-args.md +84 -16
- package/src/skf-brief-skill/references/health-check.md +1 -1
- package/src/skf-brief-skill/references/invocation-contract.md +18 -13
- package/src/skf-brief-skill/references/portfolio-similarity-check.md +1 -1
- package/src/skf-brief-skill/references/qmd-collection-registration.md +7 -10
- package/src/skf-brief-skill/references/scope-definition.md +123 -47
- package/src/skf-brief-skill/references/step-auto-brief.md +26 -100
- package/src/skf-brief-skill/references/step-auto-validate.md +20 -121
- package/src/skf-brief-skill/references/version-resolution.md +7 -10
- package/src/skf-brief-skill/references/write-brief.md +66 -57
- package/src/skf-campaign/SKILL.md +31 -34
- package/src/skf-campaign/assets/campaign-state-schema.json +25 -4
- package/src/skf-campaign/customize.toml +47 -16
- package/src/skf-campaign/references/campaign-contracts.md +39 -29
- package/src/skf-campaign/references/campaign-directive-spec.md +4 -2
- package/src/skf-campaign/references/health-check.md +7 -16
- package/src/skf-campaign/references/step-01-setup.md +46 -84
- package/src/skf-campaign/references/step-02-strategy.md +31 -30
- package/src/skf-campaign/references/step-03-pins.md +26 -18
- package/src/skf-campaign/references/step-04-provenance.md +24 -23
- package/src/skf-campaign/references/step-05-skill-loop.md +67 -48
- package/src/skf-campaign/references/step-06-batch.md +41 -34
- package/src/skf-campaign/references/step-07-capstone.md +40 -25
- package/src/skf-campaign/references/step-08-verify.md +28 -29
- package/src/skf-campaign/references/step-09-refine.md +29 -30
- package/src/skf-campaign/references/step-10-export.md +48 -36
- package/src/skf-campaign/references/step-11-maintenance.md +31 -34
- package/src/skf-campaign/references/step-resume.md +37 -68
- package/src/skf-campaign/scripts/campaign-deps.py +57 -18
- package/src/skf-campaign/scripts/campaign-parse-manifest.py +361 -54
- package/src/skf-campaign/scripts/campaign-provenance.py +96 -53
- package/src/skf-campaign/scripts/campaign-quality-gate.py +592 -0
- package/src/skf-campaign/scripts/campaign-render-batch.py +257 -49
- package/src/skf-campaign/scripts/campaign-render-kickoff.py +343 -50
- package/src/skf-campaign/scripts/campaign-report.py +204 -37
- package/src/skf-campaign/scripts/campaign-state.py +1128 -0
- package/src/skf-campaign/scripts/campaign-status.py +10 -74
- package/src/skf-campaign/scripts/campaign-validate-pins.py +30 -3
- package/src/skf-campaign/scripts/campaign-validate-state.py +34 -4
- package/src/skf-campaign/templates/campaign-brief-template.yaml +4 -5
- package/src/skf-campaign/templates/campaign-report-template.md +5 -1
- package/src/skf-campaign/templates/kickoff-template.md +0 -14
- package/src/skf-create-skill/SKILL.md +27 -20
- package/src/skf-create-skill/assets/compile-assembly-rules.md +11 -8
- package/src/skf-create-skill/assets/skill-sections.md +21 -11
- package/src/skf-create-skill/customize.toml +20 -8
- package/src/skf-create-skill/references/authoritative-files-protocol.md +36 -58
- package/src/skf-create-skill/references/batch-mode.md +81 -0
- package/src/skf-create-skill/references/ccc-index-check.md +27 -0
- package/src/skf-create-skill/references/compile.md +119 -70
- package/src/skf-create-skill/references/component-extraction.md +113 -91
- package/src/skf-create-skill/references/enrich.md +41 -23
- package/src/skf-create-skill/references/entry-points-by-hand.md +37 -0
- package/src/skf-create-skill/references/extract.md +204 -162
- package/src/skf-create-skill/references/extraction-patterns-by-hand.md +141 -0
- package/src/skf-create-skill/references/extraction-patterns-tracing.md +11 -11
- package/src/skf-create-skill/references/extraction-patterns.md +588 -201
- package/src/skf-create-skill/references/generate-artifacts.md +145 -93
- package/src/skf-create-skill/references/health-check.md +1 -1
- package/src/skf-create-skill/references/load-brief.md +53 -36
- package/src/skf-create-skill/references/relabel-rule.md +12 -0
- package/src/skf-create-skill/references/report.md +37 -57
- package/src/skf-create-skill/references/source-resolution-protocols.md +58 -169
- package/src/skf-create-skill/references/step-auto-shard.md +20 -93
- package/src/skf-create-skill/references/step-doc-rot.md +44 -60
- package/src/skf-create-skill/references/step-doc-sources.md +28 -19
- package/src/skf-create-skill/references/sub/ccc-discover.md +18 -7
- package/src/skf-create-skill/references/sub/fetch-docs.md +106 -59
- package/src/skf-create-skill/references/sub/fetch-temporal.md +53 -166
- package/src/skf-create-skill/references/tier-degradation-rules.md +18 -9
- package/src/skf-create-skill/references/validate.md +173 -99
- package/src/skf-create-skill/scripts/scan-doc-rot.py +232 -99
- package/src/skf-create-stack-skill/SKILL.md +29 -56
- package/src/skf-create-stack-skill/assets/metadata-contract.md +51 -0
- package/src/skf-create-stack-skill/assets/provenance-map-schema.md +15 -15
- package/src/skf-create-stack-skill/assets/stack-skill-template.md +23 -57
- package/src/skf-create-stack-skill/customize.toml +23 -12
- package/src/skf-create-stack-skill/references/compile-stack.md +57 -66
- package/src/skf-create-stack-skill/references/compose-mode-rules.md +24 -44
- package/src/skf-create-stack-skill/references/detect-integrations.md +92 -95
- package/src/skf-create-stack-skill/references/detect-manifests.md +46 -71
- package/src/skf-create-stack-skill/references/generate-output.md +138 -128
- package/src/skf-create-stack-skill/references/init.md +90 -32
- package/src/skf-create-stack-skill/references/integration-patterns.md +1 -17
- package/src/skf-create-stack-skill/references/invocation-contract.md +34 -0
- package/src/skf-create-stack-skill/references/manifest-patterns.md +32 -20
- package/src/skf-create-stack-skill/references/parallel-extract.md +112 -38
- package/src/skf-create-stack-skill/references/rank-and-confirm.md +57 -75
- package/src/skf-create-stack-skill/references/report.md +39 -33
- package/src/skf-create-stack-skill/references/validate.md +20 -39
- package/src/skf-drop-skill/SKILL.md +28 -51
- package/src/skf-drop-skill/customize.toml +44 -30
- package/src/skf-drop-skill/references/execute.md +83 -146
- package/src/skf-drop-skill/references/health-check.md +2 -0
- package/src/skf-drop-skill/references/invocation-contract.md +64 -0
- package/src/skf-drop-skill/references/report.md +46 -30
- package/src/skf-drop-skill/references/select.md +122 -124
- package/src/skf-drop-skill/scripts/dir-sizes.py +54 -26
- package/src/skf-drop-skill/scripts/drop-roster.py +322 -0
- package/src/skf-export-skill/SKILL.md +40 -62
- package/src/skf-export-skill/assets/managed-section-format.md +14 -50
- package/src/skf-export-skill/assets/snippet-format.md +3 -1
- package/src/skf-export-skill/customize.toml +42 -13
- package/src/skf-export-skill/references/generate-snippet.md +54 -46
- package/src/skf-export-skill/references/health-check.md +2 -0
- package/src/skf-export-skill/references/invocation-contract.md +15 -0
- package/src/skf-export-skill/references/load-skill.md +88 -62
- package/src/skf-export-skill/references/manifest-rebuild.md +9 -10
- package/src/skf-export-skill/references/multi-skill-mode.md +4 -4
- package/src/skf-export-skill/references/orphan-context-detection.md +13 -5
- package/src/skf-export-skill/references/orphan-row-detection.md +31 -22
- package/src/skf-export-skill/references/package.md +15 -73
- package/src/skf-export-skill/references/preflight-snippet-root-probe.md +51 -70
- package/src/skf-export-skill/references/result-envelope.md +31 -16
- package/src/skf-export-skill/references/summary.md +41 -17
- package/src/skf-export-skill/references/token-report.md +4 -12
- package/src/skf-export-skill/references/update-context.md +118 -237
- package/src/skf-forger/SKILL.md +61 -37
- package/src/skf-forger/bmad-skill-manifest.yaml +4 -1
- package/src/skf-forger/customize.toml +43 -0
- package/src/skf-forger/references/pipeline-mode.md +73 -18
- package/src/skf-forger/scripts/forge-status.py +299 -0
- package/src/skf-forger/scripts/parse-pipeline.py +322 -54
- package/src/skf-forger/scripts/pipeline-gate.py +428 -0
- package/src/skf-forger/scripts/pipeline-journal.py +1052 -0
- package/src/skf-quick-skill/SKILL.md +54 -53
- package/src/skf-quick-skill/assets/skill-template.md +4 -2
- package/src/skf-quick-skill/customize.toml +51 -20
- package/src/skf-quick-skill/references/batch-mode.md +74 -21
- package/src/skf-quick-skill/references/compile.md +30 -31
- package/src/skf-quick-skill/references/ecosystem-check.md +7 -57
- package/src/skf-quick-skill/references/finalize.md +69 -18
- package/src/skf-quick-skill/references/halt-contract.md +41 -29
- package/src/skf-quick-skill/references/health-check.md +5 -3
- package/src/skf-quick-skill/references/quick-extract.md +114 -45
- package/src/skf-quick-skill/references/registry-resolution.md +9 -64
- package/src/skf-quick-skill/references/resolve-target.md +116 -66
- package/src/skf-quick-skill/references/write-and-validate.md +79 -40
- package/src/skf-refine-architecture/SKILL.md +43 -53
- package/src/skf-refine-architecture/customize.toml +49 -20
- package/src/skf-refine-architecture/references/compile.md +97 -54
- package/src/skf-refine-architecture/references/exit-codes.md +30 -0
- package/src/skf-refine-architecture/references/finding-storage.md +21 -0
- package/src/skf-refine-architecture/references/gap-analysis.md +101 -38
- package/src/skf-refine-architecture/references/improvements.md +14 -20
- package/src/skf-refine-architecture/references/init.md +98 -57
- package/src/skf-refine-architecture/references/issue-detection.md +32 -39
- package/src/skf-refine-architecture/references/refinement-rules.md +37 -48
- package/src/skf-refine-architecture/references/report.md +65 -54
- package/src/skf-refine-architecture/scripts/skf-check-preservation.py +1619 -0
- package/src/skf-rename-skill/SKILL.md +19 -54
- package/src/skf-rename-skill/customize.toml +32 -26
- package/src/skf-rename-skill/references/execute.md +230 -184
- package/src/skf-rename-skill/references/exit-codes.md +7 -7
- package/src/skf-rename-skill/references/health-check.md +1 -7
- package/src/skf-rename-skill/references/invocation-contract.md +25 -0
- package/src/skf-rename-skill/references/report.md +63 -52
- package/src/skf-rename-skill/references/select.md +197 -115
- package/src/skf-rename-skill/scripts/skf-validate-rename-name.py +161 -21
- package/src/skf-setup/SKILL.md +26 -26
- package/src/skf-setup/customize.toml +14 -10
- package/src/skf-setup/references/auto-index.md +50 -63
- package/src/skf-setup/references/ccc-index.md +29 -90
- package/src/skf-setup/references/detect-and-tier.md +58 -67
- package/src/skf-setup/references/health-check.md +2 -0
- package/src/skf-setup/references/invocation-contract.md +14 -0
- package/src/skf-setup/references/report.md +43 -166
- package/src/skf-setup/references/tier-rules.md +5 -5
- package/src/skf-setup/references/write-config.md +34 -57
- package/src/skf-test-skill/SKILL.md +17 -57
- package/src/skf-test-skill/assets/output-section-formats.md +33 -34
- package/src/skf-test-skill/customize.toml +27 -13
- package/src/skf-test-skill/references/coherence-check.md +134 -62
- package/src/skf-test-skill/references/coverage-check-tiers.md +35 -0
- package/src/skf-test-skill/references/coverage-check.md +209 -264
- package/src/skf-test-skill/references/detect-mode.md +5 -5
- package/src/skf-test-skill/references/external-validators.md +94 -82
- package/src/skf-test-skill/references/health-check.md +6 -7
- package/src/skf-test-skill/references/init.md +164 -91
- package/src/skf-test-skill/references/invocation-contract.md +66 -0
- package/src/skf-test-skill/references/migration-section-rules.md +5 -2
- package/src/skf-test-skill/references/report.md +164 -142
- package/src/skf-test-skill/references/score.md +80 -107
- package/src/skf-test-skill/references/scoring-rules.md +42 -155
- package/src/skf-test-skill/references/source-access-protocol.md +18 -94
- package/src/skf-test-skill/references/step-hard-gate.md +51 -33
- package/src/skf-test-skill/scripts/aggregate-coherence.py +218 -30
- package/src/skf-test-skill/scripts/build-result-context.py +380 -0
- package/src/skf-test-skill/scripts/check-metadata-coherence.py +37 -4
- package/src/skf-test-skill/scripts/combine-external-scores.py +23 -6
- package/src/skf-test-skill/scripts/compute-score.py +223 -45
- package/src/skf-test-skill/scripts/gap-ledger.py +1666 -0
- package/src/skf-test-skill/scripts/hard-gate.py +203 -0
- package/src/skf-test-skill/scripts/load-coverage-inputs.py +1198 -0
- package/src/skf-test-skill/scripts/locate-export-segments.py +835 -0
- package/src/skf-test-skill/scripts/reconcile-coverage.py +547 -79
- package/src/skf-test-skill/scripts/score-signatures.py +479 -0
- package/src/skf-test-skill/scripts/stage-helper-payload.py +148 -0
- package/src/skf-test-skill/scripts/validate-inventory.py +311 -16
- package/src/skf-test-skill/scripts/verify-declared-numerator.py +133 -56
- package/src/skf-test-skill/templates/test-report-template.md +21 -15
- package/src/skf-update-skill/SKILL.md +38 -25
- package/src/skf-update-skill/customize.toml +27 -12
- package/src/skf-update-skill/references/detect-changes.md +253 -223
- package/src/skf-update-skill/references/gap-driven.md +336 -0
- package/src/skf-update-skill/references/health-check.md +31 -5
- package/src/skf-update-skill/references/init.md +254 -84
- package/src/skf-update-skill/references/invocation-contract.md +12 -0
- package/src/skf-update-skill/references/manual-section-rules.md +6 -23
- package/src/skf-update-skill/references/merge.md +182 -49
- package/src/skf-update-skill/references/re-extract.md +53 -262
- package/src/skf-update-skill/references/report.md +102 -35
- package/src/skf-update-skill/references/write.md +357 -155
- package/src/skf-update-skill/scripts/skf-new-file-diff.py +183 -46
- package/src/skf-verify-stack/SKILL.md +23 -42
- package/src/skf-verify-stack/assets/feasibility-report-template.md +11 -5
- package/src/skf-verify-stack/customize.toml +55 -22
- package/src/skf-verify-stack/references/coverage-patterns.md +17 -8
- package/src/skf-verify-stack/references/coverage.md +67 -64
- package/src/skf-verify-stack/references/exit-codes.md +21 -8
- package/src/skf-verify-stack/references/init.md +107 -62
- package/src/skf-verify-stack/references/integration-verification-rules.md +12 -13
- package/src/skf-verify-stack/references/integrations.md +106 -65
- package/src/skf-verify-stack/references/report.md +59 -42
- package/src/skf-verify-stack/references/requirements.md +32 -9
- package/src/skf-verify-stack/references/synthesize.md +53 -39
- package/src/skf-verify-stack/scripts/skf-coverage-tally.py +398 -52
- package/src/skf-verify-stack/scripts/skf-previous-report.py +330 -0
- package/src/skf-verify-stack/scripts/skf-report-delta.py +453 -49
- package/src/skf-verify-stack/scripts/skf-verdict-rollup.py +240 -27
- package/tools/cli/commands/install.js +11 -4
- package/tools/cli/commands/status.js +15 -3
- package/tools/cli/commands/update.js +11 -4
- package/tools/cli/lib/tool-check.js +320 -0
- package/tools/cli/lib/version-check.js +22 -9
- package/tools/cli/skf-cli.js +4 -1
- package/.gitattributes +0 -16
- package/.nvmrc +0 -1
- package/docs/_internal/RELEASING.md +0 -679
- package/release-audits/v1.0.0-launch-audit.md +0 -728
- package/src/skf-audit-skill/references/step-doc-drift.md +0 -177
- package/src/skf-brief-skill/references/headless-source-authority-detection.md +0 -26
- package/src/skf-campaign/manifest.yaml +0 -15
- package/src/skf-create-skill/assets/tessl-dismissal-rules.md +0 -106
- package/src/skf-create-skill/references/ecosystem-check.md +0 -74
- package/src/skf-drop-skill/references/headless-contract.md +0 -18
- package/src/skf-update-skill/references/merge-conflict-rules.md +0 -15
- package/src/skf-update-skill/references/remote-source-resolution.md +0 -98
- package/src/skf-update-skill/references/validate.md +0 -124
- package/tools/validate-docs-drift.js +0 -233
- package/tools/validate-docs-links.js +0 -665
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
---
|
|
2
2
|
returnToStep: 'extract.md'
|
|
3
3
|
extractionPatternsData: 'references/extraction-patterns.md'
|
|
4
|
+
# If neither path exists, Phase 1 goes on without demo exclusion or
|
|
5
|
+
# registry detection.
|
|
6
|
+
detectRegistryProbeOrder:
|
|
7
|
+
- '{project-root}/_bmad/skf/shared/scripts/skf-detect-registry.py'
|
|
8
|
+
- '{project-root}/src/shared/scripts/skf-detect-registry.py'
|
|
9
|
+
# If neither path exists, Phase 4 runs the AST Extraction Protocol instead.
|
|
10
|
+
extractPublicApiProbeOrder:
|
|
11
|
+
- '{project-root}/_bmad/skf/shared/scripts/skf-extract-public-api.py'
|
|
12
|
+
- '{project-root}/src/shared/scripts/skf-extract-public-api.py'
|
|
13
|
+
# If neither path exists, Phase 2b saves no answer and the next run asks
|
|
14
|
+
# again.
|
|
15
|
+
writeSkillBriefProbeOrder:
|
|
16
|
+
- '{project-root}/_bmad/skf/shared/scripts/skf-write-skill-brief.py'
|
|
17
|
+
- '{project-root}/src/shared/scripts/skf-write-skill-brief.py'
|
|
4
18
|
---
|
|
5
19
|
|
|
6
20
|
<!-- Config: communicate in {communication_language}. -->
|
|
@@ -14,105 +28,119 @@ When `scope.type: "component-library"`, perform specialized extraction that trea
|
|
|
14
28
|
## Rules
|
|
15
29
|
|
|
16
30
|
- Focus only on extracting component catalog, props interfaces, and shared types
|
|
17
|
-
- Do not compile SKILL.md content (Step 05)
|
|
31
|
+
- Do not compile SKILL.md content (Step 05). Write only the scratch files under `{component_scan}`, Phase 4's runner JSON at `{extraction_json}` and, in Phase 2b, the brief
|
|
18
32
|
- Every extracted item must have a provenance citation: `[AST:{file}:L{line}]` or `[SRC:{file}:L{line}]`
|
|
19
33
|
|
|
20
34
|
## MANDATORY SEQUENCE
|
|
21
35
|
|
|
22
|
-
**Prerequisite
|
|
36
|
+
**Prerequisite: §2a already ran.** Step-03 executes `§2a Discovered Authoritative Files Protocol` before delegating to this file. Any promoted authoritative files (`llms.txt`, `AGENTS.md`, etc.) are tracked in the `promoted_docs[]` context list and will be written to `file_entries[]` with `file_type: "doc"` by step 5 §6. Phase 1 lists them with the brief's other files (a promotion adds each to `scope.include`), and no Phase 4 recipe reads a doc, so this file needs no special handling for them. See step 3 §2a for the full flow.
|
|
23
37
|
|
|
24
38
|
### Phase 1: Demo/Example Exclusion
|
|
25
39
|
|
|
26
40
|
Before extraction, identify and exclude demo/example files to avoid inflating export counts.
|
|
27
41
|
|
|
28
|
-
|
|
42
|
+
Bind `{brief_file}` ← the brief file step 1 loaded: `{brief_path}`, `{forge_data_folder}/{skill-name}/skill-brief.yaml` for a skill name, or the current brief of a `--batch` run. Make a scratch folder for this step's files, from `{project-root}`, and bind `{component_scan}` ← the path it prints:
|
|
29
43
|
|
|
30
|
-
|
|
44
|
+
```bash
|
|
45
|
+
mkdir -p "{project-root}/_bmad-output/.skf-run" && mktemp -d "{project-root}/_bmad-output/.skf-run/skf-create-skill-3d-XXXXXXXX"
|
|
46
|
+
```
|
|
31
47
|
|
|
32
|
-
|
|
48
|
+
Resolve `{detectRegistryHelper}` from `{detectRegistryProbeOrder}`. It lists the files of the brief's scope under `{source_root}`, the tree extraction reads, and counts the demo files among them. From `{project-root}`, run it with one `--pattern` for each glob of the brief's `scope.demo_patterns` when it sets them:
|
|
33
49
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
50
|
+
```bash
|
|
51
|
+
uv run {detectRegistryHelper} demo --source-root "{source_root}" --brief "{brief_file}" [--pattern "{glob}"] --files-to "{component_scan}/files.txt" --kept-to "{component_scan}/kept.txt"
|
|
52
|
+
```
|
|
37
53
|
|
|
38
|
-
|
|
54
|
+
With no local tree (`{source_root}` is not a folder: a Quick-tier remote source, or one step 3 §2b could not read into a tree), pipe it the repository's file list, `gh api "repos/{owner}/{repo}/git/trees/{source_ref}?recursive=1" --jq '.tree[] | select(.type == "blob") | .path'`, with `--files-from -` in place of `--source-root`.
|
|
39
55
|
|
|
40
|
-
|
|
41
|
-
{list detected patterns with counts}
|
|
56
|
+
It writes the brief's files to `files.txt` and those no demo pattern matches to `kept.txt`, and prints `patterns[]` (each glob, its `files` count and a `sample`), `excluded`, `directories` and `also_matched[]`: `uv run {detectRegistryHelper} --help` describes them.
|
|
42
57
|
|
|
43
|
-
|
|
58
|
+
**If `scope.demo_patterns` is specified in the brief:** exclude what those patterns match, with no prompt. When `also_matched[]` is not empty, log: "Files the default demo patterns match are left in, since the brief's `scope.demo_patterns` names none of them: {each `also_matched[]` glob, its file count and its sample}. Add a glob to the brief's `scope.demo_patterns` to leave them out."
|
|
44
59
|
|
|
45
|
-
**
|
|
60
|
+
**Otherwise, when `excluded` is not 0, ask for confirmation**, since some `examples/` directories hold API-level code (`{N}` is `excluded`, `{M}` is `directories`):
|
|
46
61
|
|
|
47
|
-
|
|
62
|
+
"**Auto-detected {N} demo/example files** in {M} directories matching these patterns:
|
|
63
|
+
{for each patterns[] entry: its glob, its file count and its sample files}
|
|
48
64
|
|
|
49
|
-
|
|
65
|
+
Confirm exclusion? [Y/n] Or adjust patterns:"
|
|
50
66
|
|
|
51
|
-
|
|
67
|
+
**GATE [default: Y]**: if `{headless_mode}` is true, auto-confirm the auto-detected exclusion patterns, log "headless: auto-confirm demo/example exclusion ({N} files, {M} directories)", and record it per the Workflow Rules: stage `{"step": "component-extraction", "gate": "demo-exclusion", "decision": "Y", "value": "{N} files / {M} dirs", "rationale": "headless mode: auto-detected demo patterns accepted", "timestamp": "{ISO}"}` as `{run_dir}/decision.json` and run `uv run {emitEnvelopeHelper} record --workflow skf-create-skill --run-dir "{run_dir}" --decision < "{run_dir}/decision.json"` (step 5 §7 renders it in the evidence report's `## Auto-Decisions` table). Then proceed without waiting.
|
|
52
68
|
|
|
53
|
-
|
|
69
|
+
Wait for user response (interactive only). When the user adjusts the patterns, run the command again with one `--pattern` for each pattern given and use that result. On `n`, exclude nothing. Record `demo_files_excluded: {count}` in context (0 on `n`), and `demo_answer` ← the `patterns[]` globs the user confirmed or gave (none on `n`, in headless mode, or when the brief set them).
|
|
54
70
|
|
|
55
|
-
|
|
71
|
+
Bind `{scan_list}`, the filtered file list for the rest of this step: `{component_scan}/kept.txt` when files were excluded, else `{component_scan}/files.txt`.
|
|
56
72
|
|
|
57
|
-
|
|
73
|
+
**If the command exits non-zero, or no `{detectRegistryHelper}` candidate resolves:** warn "Demo exclusion and registry detection are skipped: {its stderr, or 'skf-detect-registry.py is missing, so re-install SKF'}.", exclude nothing and bind `{scan_list}` to null. Phase 2 then takes a brief's `scope.registry_path` as the registry unscored, or goes on with no registry.
|
|
58
74
|
|
|
59
|
-
|
|
75
|
+
### Phase 2: Registry Detection
|
|
76
|
+
|
|
77
|
+
The component registry is the primary API surface for component libraries. Score it with the helper, from `{project-root}`:
|
|
60
78
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
- Files named `components.ts`, `components.tsx` in `registry/`, `catalog/`, or `components/` directories
|
|
64
|
-
- Files named `index.ts`, `index.tsx` in `registry/` or `catalog/` directories
|
|
79
|
+
- **If the brief sets `scope.registry_path`:** `uv run {detectRegistryHelper} registry --source-root "{source_root}" --path "{scope.registry_path}"`. Its `selected` file is the registry whatever its score: continue to Phase 3 with no prompt. When `selected` is null (`candidates[0].error` says why), warn "The brief's scope.registry_path `{scope.registry_path}` could not be read ({error}): detecting the registry instead.", set `registry_path_unreadable` ← true, and run the next command.
|
|
80
|
+
- **Otherwise:** `uv run {detectRegistryHelper} registry --files-from "{scan_list}" --source-root "{source_root}"`.
|
|
65
81
|
|
|
66
|
-
|
|
67
|
-
- Arrays of objects with fields like `id`, `name`, `component`, `category`
|
|
68
|
-
- Type annotations containing `Component[]` or similar array types
|
|
69
|
-
- 10+ entries to qualify as a registry (reject small arrays)
|
|
82
|
+
With no local tree, first fetch the files the command reads into `{component_scan}/registry/`, each at its own relative path (`gh api "repos/{owner}/{repo}/contents/{path}?ref={source_ref}" -H "Accept: application/vnd.github.raw"`): the brief's `scope.registry_path` or a path the user gives below, else the paths `uv run {detectRegistryHelper} registry --files-from "{scan_list}" --candidates-only` prints, one per line. Then pass `--source-root "{component_scan}/registry"`.
|
|
70
83
|
|
|
71
|
-
|
|
72
|
-
- +3 points: Contains `id` field per entry
|
|
73
|
-
- +2 points: Contains `name` or `component` field per entry
|
|
74
|
-
- +2 points: Contains `category` or `tags` field per entry
|
|
75
|
-
- +1 point: Located in a `registry/` or `catalog/` directory
|
|
76
|
-
- +1 point: Has 20+ entries
|
|
77
|
-
- Minimum threshold: 5 points to qualify
|
|
84
|
+
It prints `selected` (the best qualifying candidate, or null), `headless_accept` and `candidates[]`, each with its `path`, `score` out of 9, `entry_count` and `sample` (its first entries): `uv run {detectRegistryHelper} --help` gives the candidate rules and the score rubric. When it exits non-zero, go on as when no registry was found.
|
|
78
85
|
|
|
79
|
-
|
|
86
|
+
**If `selected` is not null, ask for confirmation:**
|
|
80
87
|
|
|
81
88
|
"**Registry candidate detected** at `{path}` (confidence: {score}/9, {count} entries).
|
|
82
89
|
|
|
83
90
|
Sample entries:
|
|
84
|
-
{show
|
|
91
|
+
{show the selected candidate's sample entries with id, name, category}
|
|
92
|
+
{if another candidate qualifies: 'Other candidates: {path} ({score}/9, {count} entries)'}
|
|
85
93
|
|
|
86
94
|
Is this the component registry? [Y/n] Or provide the correct path:"
|
|
87
95
|
|
|
88
|
-
**GATE [default: Y]
|
|
96
|
+
**GATE [default: Y]**: if `{headless_mode}` is true and `headless_accept` is true: auto-confirm the `selected` candidate, log "headless: auto-confirm registry candidate `{path}` (score {score}/9)", and record it per the Workflow Rules: stage `{"step": "component-extraction", "gate": "registry-confirm", "decision": "Y", "value": "{path} score={score}/9", "rationale": "headless mode: high-confidence registry candidate auto-accepted", "timestamp": "{ISO}"}` as `{run_dir}/decision.json` and run `uv run {emitEnvelopeHelper} record --workflow skf-create-skill --run-dir "{run_dir}" --decision < "{run_dir}/decision.json"`. If `headless_accept` is false in headless mode, auto-reject the candidate (treat as "no registry found"), log "headless: auto-reject registry candidate `{path}` (score {score}/9), below the auto-accept threshold", record the decision the same way (`"decision": "reject-low-score"`), and fall through to the "no registry found" branch below.
|
|
89
97
|
|
|
90
|
-
Wait for user response (interactive only).
|
|
98
|
+
Wait for user response (interactive only). On `Y`, the registry is `selected`: set `registry_answer` ← its path, with `registry_evidence` ← "score {score}/9, {count} entries". When the user gives a path instead, run the `--path` command above with it: when its `selected` is null, show its `error` and ask again; otherwise use that file, with `registry_answer` ← the path and `registry_evidence` ← "given by the user, {count} entries". On `n` with no path, no registry was found.
|
|
91
99
|
|
|
92
|
-
**If no registry found and no `registry_path
|
|
100
|
+
**If no registry was found and the brief holds no usable `scope.registry_path`:**
|
|
93
101
|
|
|
94
102
|
"**No component registry detected.** Component-library extraction works best with a registry file. Options:
|
|
95
103
|
- **[P]** Provide the registry file path
|
|
96
104
|
- **[S]** Skip registry — proceed with standard props-first extraction only"
|
|
97
105
|
|
|
98
|
-
**GATE [default: S]
|
|
106
|
+
**GATE [default: S]**: if `{headless_mode}` is true: auto-select [S] Skip (props-first extraction without registry), log "headless: no registry detected, auto-skip to props-first extraction (no usable path in brief.scope.registry_path)", and record it per the Workflow Rules: stage `{"step": "component-extraction", "gate": "provide-or-skip-registry", "decision": "S", "rationale": "headless mode: no human to provide registry path", "timestamp": "{ISO}"}` as `{run_dir}/decision.json` and run `uv run {emitEnvelopeHelper} record --workflow skf-create-skill --run-dir "{run_dir}" --decision < "{run_dir}/decision.json"`. The default is `[S]` rather than `[P]` because providing a path requires user input that headless cannot supply; skipping degrades gracefully to a smaller but valid extraction.
|
|
107
|
+
|
|
108
|
+
Wait for user response (interactive only). On [P], run the `--path` command above with the path given: when its `selected` is null, show its `error` and ask again; otherwise use that file, with `registry_answer` ← the path and `registry_evidence` ← "given by the user, {count} entries".
|
|
109
|
+
|
|
110
|
+
### Phase 2b: Record the Answers in the Brief
|
|
111
|
+
|
|
112
|
+
Write back only what a user answered at a gate above, never a headless auto-decision, so that a second run of the same brief asks neither question: `demo_answer`, which the brief never set, and `registry_answer`, which fills an unset `scope.registry_path` or replaces one Phase 2 could not read (`registry_path_unreadable`). **Skip this phase when neither `demo_answer` nor `registry_answer` is set.**
|
|
113
|
+
|
|
114
|
+
Resolve `{writeSkillBriefHelper}` from `{writeSkillBriefProbeOrder}` and, from `{project-root}`, pipe it each answer that is set (leave out the key of one that is not), with its amendment entries: one per glob of `demo_answer` (`action: "demo-excluded"`, `evidence`: "{files} files" from the glob's `patterns[]` entry) and one for `registry_answer` (`action: "registry-confirmed"`, `evidence`: `registry_evidence`). Every entry takes `category: "demo-and-registry"`, today's ISO `date`, `workflow: "skf-create-skill"` and a `reason`: the user's own, else "user confirmed the demo exclusion at create-skill step 3d", "user adjusted the demo patterns at create-skill step 3d", "user confirmed the detected registry at create-skill step 3d", "user gave the registry path at create-skill step 3d" or, when `registry_path_unreadable` is true, "user replaced the unreadable registry path at create-skill step 3d":
|
|
99
115
|
|
|
100
|
-
|
|
116
|
+
```bash
|
|
117
|
+
uv run {writeSkillBriefHelper} amend --target "{brief_file}" <<'SKF_BRIEF_ANSWERS'
|
|
118
|
+
{"demo_patterns": ["{glob}", ...], "registry_path": "{registry_answer}", "amendments": [{"path": "{glob or registry file}", "action": "{action}", "category": "demo-and-registry", "reason": "{reason}", "evidence": "{evidence}", "date": "{today ISO}", "workflow": "skf-create-skill"}, ...]}
|
|
119
|
+
SKF_BRIEF_ANSWERS
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
It reads the brief again (step 3 §2a may have amended it since step 1 loaded it), sets the fields, appends the entries, keeps the previous brief as `{brief_file}.bak` and writes the new one atomically. On exit 0, display: "**Recorded in the brief:** {`scope.demo_patterns` and/or `scope.registry_path`}, with a `demo-and-registry` amendment for each." When it exits non-zero, or no candidate resolves, warn "The step 3d answers were not saved to the brief ({its stderr}): the next run asks again." and continue.
|
|
101
123
|
|
|
102
124
|
### Phase 3: Parse Registry
|
|
103
125
|
|
|
104
|
-
If a registry was confirmed,
|
|
126
|
+
If a registry was confirmed, bind `{registry_file}` ← its path (the brief's `scope.registry_path`, `selected` or the path the user gave) and list its entries, from `{project-root}`, with the `--source-root` Phase 2 used:
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
uv run {detectRegistryHelper} registry --source-root "{source_root}" --path "{registry_file}" --entries
|
|
130
|
+
```
|
|
105
131
|
|
|
106
|
-
|
|
132
|
+
Map each of its `entries[]` (one per object literal of the registry's array, each with its `line`, its `keys` and the `id`, `name`, `component`, `category`, `tags` and `description` it has) to one `component_catalog[]` entry:
|
|
107
133
|
|
|
108
|
-
- `id
|
|
109
|
-
- `name
|
|
110
|
-
- `description`
|
|
111
|
-
- `category`
|
|
112
|
-
- `available_in[]
|
|
113
|
-
- `code_paths[]
|
|
114
|
-
- `tags[]`
|
|
115
|
-
- Provenance citation: `[SRC:{registry_file}:L{line}]`
|
|
134
|
+
- `id`: the entry's `id` (used for the CLI `add` command)
|
|
135
|
+
- `name`: display name (PascalCase), from `name` or `component`
|
|
136
|
+
- `description`: `description`, or empty
|
|
137
|
+
- `category`: `category`, or from the directory structure
|
|
138
|
+
- `available_in[]`: which design system variants include this component
|
|
139
|
+
- `code_paths[]`: source file path for each variant (from the entry's other `keys`, read at its `line` in the registry file, or from the directory structure)
|
|
140
|
+
- `tags[]`: `tags`, or empty
|
|
141
|
+
- Provenance citation: `[SRC:{registry_file}:L{line}]`, with the entry's `line`
|
|
142
|
+
|
|
143
|
+
When the command exits non-zero, or no candidate resolves, read the entries from the registry file by eye instead.
|
|
116
144
|
|
|
117
145
|
Display: "**Parsed component catalog: {N} components across {M} categories.**"
|
|
118
146
|
|
|
@@ -122,47 +150,35 @@ Display: "**Parsed component catalog: {N} components across {M} categories.**"
|
|
|
122
150
|
|
|
123
151
|
Extract props interfaces as the primary API contracts, then link to components.
|
|
124
152
|
|
|
125
|
-
|
|
153
|
+
At Quick tier no recipe runs: read the shapes the steps below name from source, as T1-low. At Forge, Forge+ and Deep tiers, run the component-library recipes over `{scan_list}` in one call. Resolve `{extractPublicApiHelper}` from `{extractPublicApiProbeOrder}`; it writes `{extraction_json}`, which step 3 §2b bound beside the staging folder: step 3 §5's `init` seeds the inventory from it, so it sits outside the scratch folder Phase 6 removes. From `{project-root}`, create its folder (the runner creates none), remove the JSON an earlier run left there and run the runner with `{tier}` the forge tier (when `{scan_list}` is null, pass `--brief "{brief_file}"` in place of `--files-from "{scan_list}"`: the runner then lists the brief's files itself):
|
|
126
154
|
|
|
127
|
-
|
|
155
|
+
```bash
|
|
156
|
+
mkdir -p "{project-root}/_bmad-output/.skf-stage"
|
|
157
|
+
rm -f "{extraction_json}"
|
|
158
|
+
uv run {extractPublicApiHelper} --mode full --source-root "{source_root}" --files-from "{scan_list}" --recipe-set component-library --scope-type component-library --tier {tier} -o "{extraction_json}"
|
|
159
|
+
```
|
|
128
160
|
|
|
129
|
-
|
|
161
|
+
Act on the JSON's `status`, never on the exit code. **On `incomplete`**, remove the JSON and run the command once more, then act on the second run's JSON. **On `ok` or `incomplete`**, each of the `exports[]` in `{extraction_json}` is T1: it carries its `citation` (`[AST:{file}:L{line}]`), `ast_recipe`, `ast_node_type`, `export_type`, `confidence` and `extraction_method`, and the steps below sort the exports by `ast_recipe`, and an export read by eye, which has none, by its shape, as at Quick tier (a `*Props` interface read by eye is a Props contract for Steps 1 and 3). Read by eye, as T1-low, each file its `file_issues` lists as `syntax-errors` or `not-utf8`, where a recipe can miss an export, and, when the run is still incomplete, each file an `errors[]` item lists in `unread` (a failed call can keep some of a file's exports and lose the rest). A `no-recipes` issue is a file no recipe reads, such as a stylesheet: there is nothing to extract from it. A `missing` or `unreadable` issue is a listed file the runner could not read: record it for the evidence report as not extracted. Step 3 §5's `init` records the runner's warnings, and Gate 2 shows them: the head-cap warning when `truncated` is true (a recipe, which `recipes[]` marks, found more matches than the head cap keeps), and each `errors[]` item of an `incomplete` run (an ast-grep run failed or timed out, so its files were read by eye).
|
|
130
162
|
|
|
131
|
-
|
|
132
|
-
# React/TypeScript props interfaces
|
|
133
|
-
id: react-props-interfaces
|
|
134
|
-
language: typescript
|
|
135
|
-
rule:
|
|
136
|
-
pattern: 'export interface $NAME { $$$ }'
|
|
137
|
-
constraints:
|
|
138
|
-
NAME:
|
|
139
|
-
regex: '.*Props$'
|
|
140
|
-
```
|
|
163
|
+
**On `no-ast-grep`, or with no JSON at `{extraction_json}`** (an input error, or no candidate resolves): load `{extractionPatternsData}`, which holds every recipe the steps below name and the forms each does not cover (Known Limitation #11), and run each of them as its AST Extraction Protocol says: its **When the Runner Cannot Run** loads the recipes' notes, the languages each runs in among them. When that section warns that the runner failed (no JSON, and a stderr line that holds `error:`), send the warning with step 3 §5's `add --field warnings`, so Gate 2 and the evidence report show it.
|
|
141
164
|
|
|
142
|
-
|
|
143
|
-
|
|
165
|
+
**Step 1 (extract Props interfaces):**
|
|
166
|
+
|
|
167
|
+
The Props contracts are the exports `react-props-interfaces` finds (`typescript` for `.ts` files, `tsx` for `.tsx` files) and, in a Vue component library, those `vue-define-props` finds in the `.vue` files (`vue-define-props-tsx` for a `<script setup lang="tsx">` block): each such match's `$NAME` is the props type of the component its `.vue` file defines. Without the runner, the Vue recipes run through the CLI with the scratch `sgconfig.yml` the Vue note in `extraction-patterns-by-hand.md` describes.
|
|
168
|
+
|
|
169
|
+
For each Props contract found (a `*Props` interface, or a Vue props type):
|
|
170
|
+
- Extract all fields with types, optionality, and default values (for a Vue props type, from the interface or type literal it names)
|
|
144
171
|
- Extract JSDoc descriptions per field (if present)
|
|
145
172
|
- Record: interface name, fields[], source file, line number
|
|
146
173
|
- Provenance: `[AST:{file}:L{line}]` or `[SRC:{file}:L{line}]`
|
|
147
174
|
|
|
148
|
-
**Step 2
|
|
149
|
-
|
|
150
|
-
```yaml
|
|
151
|
-
# React component exports (PascalCase)
|
|
152
|
-
id: react-component-exports
|
|
153
|
-
language: tsx # Use 'tsx' for .tsx files, 'typescript' for .ts files
|
|
154
|
-
rule:
|
|
155
|
-
pattern: 'export function $NAME($$$PARAMS)'
|
|
156
|
-
constraints:
|
|
157
|
-
NAME:
|
|
158
|
-
regex: '^[A-Z]'
|
|
159
|
-
```
|
|
175
|
+
**Step 2 (extract component exports):**
|
|
160
176
|
|
|
161
|
-
|
|
177
|
+
The component exports are those `react-component-functions` (its `javascript` form for `.js` and `.jsx` files), `react-component-arrow-functions` and `react-wrapped-components` (for `memo`, `forwardRef` and `lazy` components) find, and the items of a local list such as the `export { Button, buttonVariants }` that ends a shadcn/ui component file, which `js-local-exports` finds. Sort a local list's items by what they name: the runner records an item's `export_type` as the kind of the declaration it names (`interface`, `type` or `enum` for a type). A type-only item (one in an `export type { ... }` list, or a `type X` specifier) is a type: one whose name ends in `Props` is a Props contract for Steps 1 and 3 (read its fields from the interface or type the list names), and any other is a shared type for Step 4. A PascalCase value item is a component, and any other value item (such as `buttonVariants`) is a shared export: record it with the shared types of Step 4.
|
|
162
178
|
|
|
163
179
|
For each component export: record name, source file, line number. Do not document the function signature in detail (it's always `(props: XProps) => JSX.Element`).
|
|
164
180
|
|
|
165
|
-
**Step 3
|
|
181
|
+
**Step 3 (link Props to components):**
|
|
166
182
|
|
|
167
183
|
Use a 3-level fallback chain:
|
|
168
184
|
|
|
@@ -170,14 +186,11 @@ Use a 3-level fallback chain:
|
|
|
170
186
|
2. **File co-location (fallback):** If naming doesn't match, check if a Props interface and a component are defined in the same file
|
|
171
187
|
3. **Generic parameter (deep fallback):** Look for `ComponentProps<typeof Foo>` or similar generic patterns that reference the component
|
|
172
188
|
|
|
173
|
-
For each linked pair, record the association. For unlinked Props interfaces, include them as standalone type exports.
|
|
189
|
+
For each linked pair, record the association. For unlinked Props interfaces, include them as standalone type exports. Include an unlinked component export with a note that no Props interface was found (signature-only, T1-low confidence for API contract).
|
|
174
190
|
|
|
175
|
-
**Step 4
|
|
191
|
+
**Step 4 (extract shared types):**
|
|
176
192
|
|
|
177
|
-
|
|
178
|
-
- `export type $NAME = $VALUE`
|
|
179
|
-
- `export enum $NAME { $$$ }`
|
|
180
|
-
- `export interface $NAME { $$$ }` (excluding `*Props` already captured)
|
|
193
|
+
The shared types are the exports `ts-exported-types` finds: the exported interfaces, type aliases and enums, generic, `extends` and `export declare` forms included, less the `*Props` interfaces Step 1 took. It returns top-level exports only: a member of an `export namespace` block is not a module export.
|
|
181
194
|
|
|
182
195
|
### Phase 5: Variant Consolidation
|
|
183
196
|
|
|
@@ -204,17 +217,20 @@ Display: "**Variant consolidation: {unique} unique components across {variant_co
|
|
|
204
217
|
|
|
205
218
|
### Phase 6: Build Component Extraction Results
|
|
206
219
|
|
|
207
|
-
|
|
220
|
+
Step 3 §5 takes the runner's exports from `{extraction_json}` as it wrote them. Prepare what it sends on top: an entry below for each export read by eye (Quick tier, a `file_issues` file, a file an `errors[]` item lists in `unread`, or the runner's fallback), each Props interface's fields as its `params`, and `component_catalog[]`; keep the aggregate counts below in context for step 5's `metadata.json` stats:
|
|
208
221
|
|
|
209
222
|
**Per-export entry (for Props interfaces — primary API):**
|
|
210
223
|
|
|
211
224
|
- Interface name (e.g., `NativeLiquidButtonProps`)
|
|
212
225
|
- Full interface with all fields and types
|
|
213
|
-
-
|
|
226
|
+
- `params`: each field as `{name, type, default, optional, description}`, sent with `patch`
|
|
214
227
|
- Linked component name (e.g., `NativeLiquidButton`)
|
|
215
228
|
- Source file and line number
|
|
216
229
|
- Provenance citation
|
|
217
|
-
- Confidence tier
|
|
230
|
+
- Confidence tier: T1 when an ast-grep rule matched the interface, T1-low when it was read by eye
|
|
231
|
+
- `extraction_method`: `ast-grep` or `source-read`, the tool that produced the entry
|
|
232
|
+
- `ast_node_type`: the `kind` the matching recipe declares (`export_statement` for `react-props-interfaces`: the recipe matches the whole export, not the `interface_declaration` inside it), or `null` when read by eye
|
|
233
|
+
- `ast_recipe`: the `id` of the recipe that matched it, or `null` when read by eye
|
|
218
234
|
|
|
219
235
|
**Per-export entry (for component functions):**
|
|
220
236
|
|
|
@@ -222,7 +238,7 @@ Compile all extracted data into the format expected by step 3 section 5:
|
|
|
222
238
|
- Linked Props interface (if found)
|
|
223
239
|
- Source file and line number
|
|
224
240
|
- Provenance citation
|
|
225
|
-
- Confidence tier
|
|
241
|
+
- Confidence tier, `extraction_method`, `ast_node_type` and `ast_recipe`, labeled by the tool that produced the entry as for Props interfaces above (a component `js-local-exports` found records `export_specifier`, the kind that recipe declares)
|
|
226
242
|
|
|
227
243
|
**Per-export entry (for shared types):**
|
|
228
244
|
|
|
@@ -239,6 +255,12 @@ Compile all extracted data into the format expected by step 3 section 5:
|
|
|
239
255
|
|
|
240
256
|
**Store `component_catalog[]` in context** — this is consumed by step 5 for the Component Catalog section.
|
|
241
257
|
|
|
258
|
+
Remove this step's scratch folder, from `{project-root}` (the `case` guard deletes nothing unless `{component_scan}` is the folder Phase 1 made):
|
|
259
|
+
|
|
260
|
+
```bash
|
|
261
|
+
case "{component_scan}" in "{project-root}/_bmad-output/.skf-run/skf-create-skill-3d-"*) rm -rf "{component_scan}" ;; esac
|
|
262
|
+
```
|
|
263
|
+
|
|
242
264
|
Display: "**Component extraction complete.** Returning to main extraction flow."
|
|
243
265
|
|
|
244
266
|
## RETURN PROTOCOL
|
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
---
|
|
2
2
|
nextStepFile: 'compile.md'
|
|
3
|
+
forgeTierConfig: '{sidecar_path}/forge-tier.yaml'
|
|
4
|
+
# If neither path exists, §2 finds no enrichment collection and the step is
|
|
5
|
+
# skipped.
|
|
6
|
+
forgeTierRwProbeOrder:
|
|
7
|
+
- '{project-root}/_bmad/skf/shared/scripts/skf-forge-tier-rw.py'
|
|
8
|
+
- '{project-root}/src/shared/scripts/skf-forge-tier-rw.py'
|
|
9
|
+
# §4 appends the T2 annotations to the extraction inventory through it.
|
|
10
|
+
extractionInventoryProbeOrder:
|
|
11
|
+
- '{project-root}/_bmad/skf/shared/scripts/skf-extraction-inventory.py'
|
|
12
|
+
- '{project-root}/src/shared/scripts/skf-extraction-inventory.py'
|
|
3
13
|
---
|
|
4
14
|
|
|
5
15
|
<!-- Config: communicate in {communication_language}. -->
|
|
@@ -31,44 +41,47 @@ Continue to step 2.
|
|
|
31
41
|
|
|
32
42
|
### 2. Collection Inventory Pre-check (Deep Tier Only)
|
|
33
43
|
|
|
34
|
-
Before searching,
|
|
44
|
+
Before searching, find this skill's enrichment collections. Resolve `{forgeTierRwHelper}` from `{forgeTierRwProbeOrder}` and read the registry as JSON, from `{project-root}`:
|
|
35
45
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
46
|
+
```bash
|
|
47
|
+
uv run {forgeTierRwHelper} read --target "{forgeTierConfig}"
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
1. Bind `{enrichment_collections}` ← the `name` of each entry of its `data.qmd_collections` whose `skill_name` is `{skill-name}` and whose `type` is `"temporal"` (issues, PRs and changelogs, from step 3b) or `"docs"` (fetched external documentation, from step 3c): at most `{skill-name}-temporal` and `{skill-name}-docs`. Another skill's collections never count, and neither do `"extraction"` or `"brief"` collections. The list is empty when `exists` is false, the command fails or no candidate resolves.
|
|
51
|
+
2. **If `{enrichment_collections}` is empty:** report this and auto-proceed. Display:
|
|
39
52
|
|
|
40
53
|
"**Enrichment: no enrichment collections available.**
|
|
41
|
-
|
|
54
|
+
No temporal context (issues, PRs, changelogs) or docs collection of this skill is indexed in QMD. {IF extraction_mode is 'docs-only' AND T3 items exist in extraction_inventory: 'T3 documentation content is available in the extraction inventory: enrichment through QMD is skipped, and the T3 content will be compiled.'} {ELSE: 'Enrichment skipped (expected for first-run skill creation). Enrichment becomes available when temporal or docs context is indexed into QMD collections.'}
|
|
42
55
|
|
|
43
56
|
Proceeding to compilation..."
|
|
44
57
|
|
|
45
58
|
Then immediately load, read entire file, then execute `{nextStepFile}`.
|
|
46
59
|
|
|
47
|
-
|
|
60
|
+
3. **Otherwise** (a temporal collection, a docs collection, or both): continue to step 3.
|
|
48
61
|
|
|
49
62
|
### 3. QMD Enrichment Searches (Deep Tier Only)
|
|
50
63
|
|
|
51
|
-
For each major exported function
|
|
64
|
+
For each major exported function (the **top-level public API surface**, typically the 10-20 functions that will appear in the context-snippet, not all extracted exports, and never one the inventory marks `public: false`, off a `public-api` skill's public surface), search `{enrichment_collections}` for context. Every query below passes them as its collections, so no other skill's issues or docs can annotate this one:
|
|
52
65
|
|
|
53
66
|
**Search query construction:**
|
|
54
67
|
|
|
55
|
-
For each function, derive the **module context** from
|
|
68
|
+
Read the functions and their source files from `{extraction_inventory}`, the extraction inventory step 3 §5 wrote, leaving out each export marked `public: false`. For each function, derive the **module context** from its source file path (e.g., `src/graph/neo4j/index.ts` → module context `graph neo4j`). This context improves search relevance by scoping results to the function's subsystem without adding extra queries.
|
|
56
69
|
|
|
57
70
|
**Primary searches (BM25 — always runs, no GPU/VRAM dependency):**
|
|
58
71
|
|
|
59
|
-
1. **Issues/PRs:** `qmd_bridge.query(searches=[{type:'lex', query:'{module_context} {function_name}', intent:'issues-prs'}])
|
|
60
|
-
2. **Changelog entries:** `qmd_bridge.query(searches=[{type:'lex', query:'{function_name} changelog', intent:'changelog'}])
|
|
61
|
-
3. **Migration notes:** `qmd_bridge.query(searches=[{type:'lex', query:'{function_name} migration deprecated breaking', intent:'migration'}])
|
|
72
|
+
1. **Issues/PRs:** `qmd_bridge.query(searches=[{type:'lex', query:'{module_context} {function_name}', intent:'issues-prs'}], collections={enrichment_collections})`: related discussions, scoped by module context
|
|
73
|
+
2. **Changelog entries:** `qmd_bridge.query(searches=[{type:'lex', query:'{function_name} changelog', intent:'changelog'}], collections={enrichment_collections})`: version history and breaking changes (kept generic, since changelogs rarely use module paths)
|
|
74
|
+
3. **Migration notes:** `qmd_bridge.query(searches=[{type:'lex', query:'{function_name} migration deprecated breaking', intent:'migration'}], collections={enrichment_collections})`: deprecation or migration context, by exact keyword matching
|
|
62
75
|
|
|
63
76
|
**Supplemental search (best-effort — requires GPU/VRAM, may fail):**
|
|
64
77
|
|
|
65
|
-
4. **Semantic migration context:** `qmd_bridge.query(searches=[{type:'vec', query:'{module_context} {function_name} migration deprecated', intent:'semantic-migration'}])
|
|
78
|
+
4. **Semantic migration context:** `qmd_bridge.query(searches=[{type:'vec', query:'{module_context} {function_name} migration deprecated', intent:'semantic-migration'}], collections={enrichment_collections})`: adds semantic matches (synonyms, paraphrases) that BM25 keyword search may miss. Merge results with search #3, deduplicating by document ID. If the `vec`-type search fails (VRAM, GPU driver, model loading), discard silently: the BM25 (`lex`) results from search #3 provide baseline coverage.
|
|
66
79
|
|
|
67
|
-
**Tool resolution for qmd_bridge:** QMD MCP exposes a single `query` tool that accepts a `searches[]` array. Each
|
|
80
|
+
**Tool resolution for qmd_bridge:** QMD MCP exposes a single `query` tool that accepts a `searches[]` array and a `collections` list. Each search has a `type` field (`'lex'` for BM25 keyword search, `'vec'` for semantic vector search, `'hyde'` for hypothetical-document retrieval) plus `query` and `intent` fields. Claude Code: `mcp__plugin_qmd-plugin_qmd__query`. Cursor: qmd MCP server. CLI: `qmd search "{query}"` (BM25) or `qmd vsearch "{query}"` (semantic), with one `-c {collection}` for each collection of `{enrichment_collections}`. See `knowledge/tool-resolution.md`.
|
|
68
81
|
|
|
69
82
|
**Tool probe (graceful degradation):** If any tool-not-found error surfaces while invoking `query` (e.g., legacy `vector_search` still expected by an older client, or a bridge returns "tool not registered"), treat it as a non-fatal failure for that function's enrichment and continue with the remaining functions. Do not retry against the stale `vector_search` tool name — that name was removed from the QMD MCP server. Record the degradation in context for the evidence report.
|
|
70
83
|
|
|
71
|
-
**For each QMD result
|
|
84
|
+
**For each QMD result from a collection of `{enrichment_collections}`** (drop a result from any other collection):
|
|
72
85
|
|
|
73
86
|
- Create a T2 annotation: `[QMD:{collection}:{doc}]`
|
|
74
87
|
- Classify temporal relevance:
|
|
@@ -83,19 +96,24 @@ For each function, derive the **module context** from the extraction inventory's
|
|
|
83
96
|
|
|
84
97
|
### 4. Annotate Extraction Inventory
|
|
85
98
|
|
|
86
|
-
Add enrichment annotations to
|
|
99
|
+
Add the enrichment annotations to `{extraction_inventory}` without modifying its extraction data, so compile reads them from disk. One annotation per finding, for each function that has one:
|
|
87
100
|
|
|
88
|
-
**Per-function enrichment (if found):**
|
|
89
101
|
- Related issues/PRs with summary
|
|
90
102
|
- Changelog history (version changes, breaking changes)
|
|
91
103
|
- Migration/deprecation context
|
|
92
104
|
- T2 provenance citation for each annotation
|
|
93
105
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
106
|
+
Resolve `{extractionInventoryHelper}` ← first existing path in `{extractionInventoryProbeOrder}` and, from `{project-root}`, append them all in one call:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
uv run {extractionInventoryHelper} add --inventory "{extraction_inventory}" --field t2_annotations <<'SKF_T2'
|
|
110
|
+
[{"export_name": "...", "kind": "issue|pr|changelog|migration", "temporal": "T2-past|T2-future", "summary": "...", "citation": "[QMD:{collection}:{doc}]"}]
|
|
111
|
+
SKF_T2
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
When it exits non-zero, fix the JSON and run it once more: an annotation the inventory already holds is not added twice. When it fails again, or no path resolves, warn "T2 annotations were not saved to the extraction inventory: {its message}" and continue without them: enrichment never halts the workflow.
|
|
115
|
+
|
|
116
|
+
**Enrichment summary counts** come from the call's output, never counted by hand: `counts.functions_enriched` (the functions with at least one annotation), `counts.t2_annotations`, `counts.t2_past` and `counts.t2_future` (the annotations by their `temporal`). When the call failed, each is 0.
|
|
99
117
|
|
|
100
118
|
### 5. Report Enrichment (Deep Tier Only)
|
|
101
119
|
|
|
@@ -103,8 +121,8 @@ Display brief enrichment summary:
|
|
|
103
121
|
|
|
104
122
|
"**Enrichment complete.**
|
|
105
123
|
|
|
106
|
-
**Functions enriched:** {
|
|
107
|
-
**T2 annotations:** {
|
|
124
|
+
**Functions enriched:** {counts.functions_enriched} of {the functions §3 searched}
|
|
125
|
+
**T2 annotations:** {counts.t2_annotations} ({counts.t2_past} historical, {counts.t2_future} forward-looking)
|
|
108
126
|
|
|
109
127
|
Proceeding to compilation..."
|
|
110
128
|
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<!-- Config: communicate in {communication_language}. -->
|
|
2
|
+
|
|
3
|
+
# Entry Points by Hand
|
|
4
|
+
|
|
5
|
+
Where the recipe runner gives no file list or did not diff the entry points, `{extractPublicApiHelper}`'s `--mode denominator` lists the files in scope, counts the public names you find, diffs them with the exports extraction found and decides whether to store `effective_denominator`, by the runner's own rules: no list, count or diff is made by hand. Create-skill's step 3 runs step 1 below at §2 and steps 2 to 4 at §4b; update-skill's write.md §2 runs them for the same cases. Run each call from `{project-root}`. `<the brief>` and `<the JSON>` are create-skill's `{brief_path}` and `{scope_json}` (`{project-root}/_bmad-output/.skf-stage/{skill-name}.scope.json`), and update-skill's `{forge_data_folder}/{skill_name}/skill-brief.yaml` (leave `--brief` out when that file does not exist) and `{run_dir}/denominator.json`.
|
|
6
|
+
|
|
7
|
+
1. **The files in scope and the entry points**, unless a runner JSON gives them (its `no-ast-grep` result too):
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
rm -f "<the JSON>"
|
|
11
|
+
echo '{}' | uv run {extractPublicApiHelper} --mode denominator --source-root "{source_root}" [--brief "<the brief>"] -o "<the JSON>"
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
For a remote source with no local tree, pipe it `gh api "repos/{owner}/{repo}/git/trees/{source_ref}?recursive=1" --jq '.tree[] | select(.type == "blob") | .path'` in place of the `echo`, with `--tree-file -` in place of `--source-root`. The filtered file list is its `files_in_scope_list` (the files a recipe reads) and `files_without_recipes_list` (those in a language no recipe reads): record "**Filtered file count: {`files_in_scope`} files in scope**". The entry points are its `entry_points.files`; a remote source has none: find them with the reads below.
|
|
15
|
+
2. **Their public names, each at the file that defines it:**
|
|
16
|
+
- **Python, JavaScript or TypeScript, from a local source:** run `uv run {extractPublicApiHelper} --mode quick --language {language} --source-root "{source_root}" --entry-file <entry-file>`, one `--entry-file` for each entry file of one package, and save its JSON beside `<the JSON>`: each export carries the file that defines it (`defined_in`). Read by eye only the statements its `warnings` name.
|
|
17
|
+
- **Otherwise,** read each entry point: **Python:** `__init__.py`, its imports. **TypeScript/JS:** `index.ts`/`index.js`. **Rust:** `lib.rs`, its `pub use` items. **Go:** the exported (capitalized) identifiers.
|
|
18
|
+
- **A submodule the entry point exports by name** (`from . import sub`, `export * as ns`, a Rust `pub mod`) stands for the names its module passes on: give it its `members`, each at its own file.
|
|
19
|
+
3. **The counts and the diff.** Send each name you read by eye in `public` and each export extraction read by eye in `exports`, as `{"name", "file", "language"}` with `file` relative to `{source_root}` and written with `/`:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
uv run {extractPublicApiHelper} --mode denominator --source-root "{source_root}" [--brief "<the brief>"] [--quick "<a quick JSON>"]... [--extraction "<the runner's JSON>"] -o "<the JSON>" <<'SKF_NAMES'
|
|
23
|
+
{"public": [...], "exports": [...]}
|
|
24
|
+
SKF_NAMES
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Pass each quick JSON of step 2, and `--extraction` when the runner wrote a JSON with status `ok` or `incomplete` (create-skill's `{extraction_json}`, update-skill's `{run_dir}/public-api.json`): its names count too, each once with yours. Leave out `--source-root` for a remote source with no local tree. Its `counts` and `arms` stand in for the runner's. Its `entry_point_diff` replaces a compare by eye: trace each `extraction_gaps` name (re-export protocol), and send each `internal` export with `"internal": true` (create-skill's §5 `add --field exports`), which keeps it out of `metadata.json`'s `exports[]`.
|
|
28
|
+
4. **When a call fails:** on exit 2, show the stderr line that holds `error:` and run the call once more, with the payload fixed when that line names it, or with the brief's scope as `--include`, `--exclude`, `--tier-a-include`, `--scope-type` and `--language` in place of `--brief` when it names the brief. When it fails again or writes no JSON, or no `{extractPublicApiHelper}` path resolves, no count is made by hand: the step that loaded this file halts.
|
|
29
|
+
|
|
30
|
+
**Multi-entry packages (`exports` map / declaration-file entry points).** A single per-language entry-point read misses public surface that a package ships through its `package.json` `exports` map, especially committed `.d.ts` / `.d.mts` declaration files that resolve **outside** the conventional source dir (e.g. a monorepo package whose `./macro` subpath maps to `macro/index.d.mts`, listed in `files[]` but not under `src/`). When the in-scope package declares an `exports` map:
|
|
31
|
+
|
|
32
|
+
- Resolve each `exports` subpath to its target file and treat that file, and any committed `.d.ts` / `.d.mts` declaration it resolves to, as an authoritative public entry point, reading it the same way as the primary barrel above even when it lives outside `src/`.
|
|
33
|
+
- If a resolved `exports` subpath target falls **outside** the brief's `scope.include` globs, surface a note: `"warn: public entry point {path} (exports subpath '{subpath}') resolves outside scope.include: widen scope.include before extraction, or this surface stays undocumented while exports_public_api still counts it, so public_api_coverage drops (only effective_denominator, for the curated-subset shapes, leaves it out)."` Widening `scope.include` here keeps the documented surface aligned with the `effective_denominator` the runner counts from those same globs, without mid-run scope surgery.
|
|
34
|
+
|
|
35
|
+
Use the entry point as the authoritative source for `metadata.json`'s `exports[]` array.
|
|
36
|
+
|
|
37
|
+
**If entry point is missing or unreadable:** Skip validation with a warning.
|