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,10 @@
|
|
|
1
1
|
---
|
|
2
2
|
nextStepFile: 'health-check.md'
|
|
3
3
|
outputFile: '{forge_data_folder}/analyze-source-report-{project_name}.md'
|
|
4
|
+
heuristicsFile: 'references/unit-detection-heuristics.md'
|
|
5
|
+
coexistenceFile: 'references/step-auto-scope-coexistence.md'
|
|
6
|
+
splitFile: 'references/step-auto-scope-split.md'
|
|
7
|
+
corporaFile: 'references/step-auto-scope-corpora.md'
|
|
4
8
|
shapeDetectProbeOrder:
|
|
5
9
|
- '{project-root}/_bmad/skf/shared/scripts/skf-shape-detect.py'
|
|
6
10
|
- '{project-root}/src/shared/scripts/skf-shape-detect.py'
|
|
@@ -16,9 +20,15 @@ scanManifestsProbeOrder:
|
|
|
16
20
|
detectLanguageProbeOrder:
|
|
17
21
|
- '{project-root}/_bmad/skf/shared/scripts/skf-detect-language.py'
|
|
18
22
|
- '{project-root}/src/shared/scripts/skf-detect-language.py'
|
|
19
|
-
|
|
20
|
-
- '{project-root}/_bmad/skf/shared/scripts/skf-
|
|
21
|
-
- '{project-root}/src/shared/scripts/skf-
|
|
23
|
+
detectWorkspacesProbeOrder:
|
|
24
|
+
- '{project-root}/_bmad/skf/shared/scripts/skf-detect-workspaces.py'
|
|
25
|
+
- '{project-root}/src/shared/scripts/skf-detect-workspaces.py'
|
|
26
|
+
writeSkillBriefProbeOrder:
|
|
27
|
+
- '{project-root}/_bmad/skf/shared/scripts/skf-write-skill-brief.py'
|
|
28
|
+
- '{project-root}/src/shared/scripts/skf-write-skill-brief.py'
|
|
29
|
+
validateBriefSchemaProbeOrder:
|
|
30
|
+
- '{project-root}/_bmad/skf/shared/scripts/skf-validate-brief-schema.py'
|
|
31
|
+
- '{project-root}/src/shared/scripts/skf-validate-brief-schema.py'
|
|
22
32
|
---
|
|
23
33
|
|
|
24
34
|
<!-- Config: communicate in {communication_language}. -->
|
|
@@ -36,217 +46,165 @@ To automatically scope a repo using shape detection and export surface analysis,
|
|
|
36
46
|
- Must produce the same output artifacts as the interactive chain: analysis report + skill-brief.yaml
|
|
37
47
|
- On unknown shape (exit code 1), fall back to `scan-project.md` (the normal interactive entry point)
|
|
38
48
|
- On error (exit code 2), HARD HALT with exit code 3 (`resolution-failure`)
|
|
49
|
+
- Every brief goes through the brief writer and the schema gate (§8): a brief either one rejects halts the run before any brief is written
|
|
39
50
|
|
|
40
51
|
## MANDATORY SEQUENCE
|
|
41
52
|
|
|
53
|
+
Every HARD HALT in this step names its exit code, `halt_reason` and phase. When `{headless_mode}` is true it first prints its envelope on stderr through the shared emitter (`{emitEnvelopeHelper}` and `{run_dir}` come from SKILL.md On Activation): stage `{run_dir}/halt.json` as `{"phase": "<phase>", "reason": "<the halt message>", "halt_reason": "<halt_reason>", "mode": "auto"}`, plus `"path"` when the halt names one, then run
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
uv run {emitEnvelopeHelper} emit-halt --workflow skf-analyze-source --run-dir "{run_dir}" --target stderr < "{run_dir}/halt.json"
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
and display the line it prints verbatim. Write the payload as valid JSON: in the halt message and `path`, replace each backslash with / and each double quote with a backtick. If the emitter exits non-zero or prints no line, display the halt message alone. An interactive HALT displays its message and emits nothing.
|
|
60
|
+
|
|
42
61
|
### 0. URL Type Detection
|
|
43
62
|
|
|
44
|
-
|
|
63
|
+
**Resolve `{skillInventoryHelper}`** from `{skillInventoryProbeOrder}`; first existing path wins. If neither resolves, HARD HALT (exit code 3, `halt_reason: "resolution-failure"`, phase `step-auto-scope:0`): "`skf-skill-inventory.py` is missing. Re-install SKF." The helper classifies `{project_path}` (the first of `project_paths[]`):
|
|
45
64
|
|
|
46
|
-
|
|
65
|
+
```bash
|
|
66
|
+
uv run {skillInventoryHelper} derive-name --target "{project_path}" --probe-git
|
|
67
|
+
```
|
|
47
68
|
|
|
48
|
-
|
|
|
49
|
-
|
|
50
|
-
| `
|
|
51
|
-
| `
|
|
52
|
-
|
|
|
53
|
-
| Any other `https://` or `http://` URL | Documentation URL | `references/auto-docs-only.md` (docs-only, via §0c) |
|
|
54
|
-
| Anything else (SSH URLs, `git://`, bare hostnames, etc.) | Unclassified | §1 (standard auto-scope) |
|
|
69
|
+
| `kind` | Classification | Route |
|
|
70
|
+
|---|---|---|
|
|
71
|
+
| `docs` | Documentation URL | HARD HALT (exit code 3, `halt_reason: "resolution-failure"`, phase `step-auto-scope:0`, path `{project_path}`) with its `halt_message` when it gives one, else `references/auto-docs-only.md`, via §0c |
|
|
72
|
+
| `local` | Local filesystem path | §1 |
|
|
73
|
+
| `remote` | Repo URL, cloned from `clone_url` | §1 |
|
|
55
74
|
|
|
56
|
-
Store
|
|
75
|
+
Store `{kind}` and `{clone_url}`; every kind continues to §0b.
|
|
57
76
|
|
|
58
77
|
### 0b. Pin Resolution
|
|
59
78
|
|
|
60
|
-
This section validates and resolves version pins.
|
|
79
|
+
This section validates and resolves version pins. Initialize `{pinned_ref}`, `{pinned_ref_type}`, and `{pinned_version}` as null.
|
|
61
80
|
|
|
62
|
-
**For documentation URLs
|
|
81
|
+
**For documentation URLs** (no git repo to pin against): skip to §0c.
|
|
63
82
|
|
|
64
|
-
**For local
|
|
83
|
+
**For a local path (`{kind}` is `local`) when `--pin` is provided:** Emit a warning: "**Local source may not match pinned version {pin_value}.** Ensure you've checked out the correct version locally, or use a remote GitHub URL so SKF can clone from the git tag automatically." Store `{pinned_ref}` = `{pin_value}`, `{pinned_ref_type}` = `"local"`, `{pinned_version}` = `{pin_value}`. Continue to §0c without running `skf-validate-pins.py`.
|
|
65
84
|
|
|
66
|
-
**For repo
|
|
85
|
+
**For a repo URL (`{kind}` is `remote`) when `--pin` is provided:**
|
|
67
86
|
|
|
68
|
-
**Resolve `{validatePinsHelper}`** from `{validatePinsProbeOrder}`; first existing path wins
|
|
87
|
+
**Resolve `{validatePinsHelper}`** from `{validatePinsProbeOrder}`; first existing path wins. If neither resolves, HARD HALT (exit code 3, `halt_reason: "resolution-failure"`, phase `step-auto-scope:0b`): "`skf-validate-pins.py` is missing. Re-install SKF."
|
|
69
88
|
|
|
70
89
|
```bash
|
|
71
|
-
uv run {validatePinsHelper} --repo-url {
|
|
90
|
+
uv run {validatePinsHelper} --repo-url "{clone_url}" --pin {pin_value}
|
|
72
91
|
```
|
|
73
92
|
|
|
74
93
|
Handle exit codes:
|
|
75
94
|
|
|
76
95
|
- **Exit 0** (`status: "valid"`): Store `{pinned_ref}` = `resolved_ref`, `{pinned_ref_type}` = `ref_type`, `{pinned_version}` = `version`. Continue to §0c.
|
|
77
|
-
- **Exit 1** (`status: "invalid"`): HARD HALT
|
|
78
|
-
- **Exit 2** (error): HARD HALT
|
|
96
|
+
- **Exit 1** (`status: "invalid"`): HARD HALT (exit code 3, `halt_reason: "pin-invalid"`, phase `step-auto-scope:0b`): "Version pin '{pin_value}' not found in {project_path}. Available matches: {suggestions}. Use a valid tag, branch, or omit --pin for latest."
|
|
97
|
+
- **Exit 2** (error): HARD HALT (exit code 3, `halt_reason: "resolution-failure"`, phase `step-auto-scope:0b`): "The version pin could not be checked: {the helper's error}."
|
|
79
98
|
|
|
80
99
|
**For repo URLs when `--pin` is not provided (default):**
|
|
81
100
|
|
|
82
101
|
Using the same `{validatePinsHelper}` resolved above:
|
|
83
102
|
|
|
84
103
|
```bash
|
|
85
|
-
uv run {validatePinsHelper} --repo-url {
|
|
104
|
+
uv run {validatePinsHelper} --repo-url "{clone_url}"
|
|
86
105
|
```
|
|
87
106
|
|
|
88
107
|
Handle exit codes:
|
|
89
108
|
|
|
90
109
|
- **Exit 0** (`status: "resolved"`): Store `{pinned_ref}` = `resolved_ref`, `{pinned_ref_type}` = `ref_type`, `{pinned_version}` = `version`. Log: "Default pin resolved: {resolved_ref}". Continue to §0c.
|
|
91
110
|
- **Exit 1** (no releases found): Set `{pinned_ref}` = null, `{pinned_ref_type}` = null, `{pinned_version}` = null. Log: "No release tags found — using HEAD." Continue to §0c without pinning.
|
|
92
|
-
- **Exit 2**:
|
|
111
|
+
- **Exit 2**: record the warning with `uv run {emitEnvelopeHelper} record --run-dir "{run_dir}" --warning 'pin_unresolved: <the helper message, a single quote written as a backtick>'` (if it fails, go on), then continue to §0c without pinning, as on exit 1.
|
|
93
112
|
|
|
94
113
|
### 0c. Coexistence Detection
|
|
95
114
|
|
|
96
|
-
This section checks for existing skills matching the target
|
|
115
|
+
This section checks for existing skills matching the target, whatever its kind, then goes on to its **route**: load, read fully, then execute `references/auto-docs-only.md` for a documentation URL, §1 for every other kind. Initialize `{coexistence_suffix}` as empty.
|
|
97
116
|
|
|
98
117
|
**1. Load skill inventory:**
|
|
99
118
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
Pass the target (`{project_path}`) so the helper computes the coexistence match set for you — do not re-match by hand:
|
|
119
|
+
Pass the target (`{project_path}`) to `{skillInventoryHelper}`, resolved in §0, so the helper computes the coexistence match set for you; do not re-match by hand:
|
|
103
120
|
|
|
104
121
|
```bash
|
|
105
|
-
uv run {skillInventoryHelper} {skills_output_folder} --match-target {project_path}
|
|
122
|
+
uv run {skillInventoryHelper} "{skills_output_folder}" --match-target "{project_path}"
|
|
106
123
|
```
|
|
107
124
|
|
|
108
|
-
Parse the JSON output. If the exit code is non-zero or the `skills` array is empty, skip coexistence detection silently (no existing skills to conflict with) and
|
|
125
|
+
Parse the JSON output. If the exit code is non-zero or the `skills` array is empty, skip coexistence detection silently (no existing skills to conflict with) and go on to the route.
|
|
109
126
|
|
|
110
127
|
**2. Read the match set:**
|
|
111
128
|
|
|
112
|
-
|
|
129
|
+
Read the top-level **`matches[]`** array; the helper has already matched, so do not re-match. Each entry is:
|
|
113
130
|
|
|
114
131
|
```json
|
|
115
|
-
{ "name": "...", "active_version": "...", "source_repo": "...", "active_path": "...", "match_reason": "url" | "name" | "both" }
|
|
132
|
+
{ "name": "...", "active_version": "...", "source_repo": "...", "active_path": "...", "match_reason": "url" | "name" | "both", "skf_skill": true | false }
|
|
116
133
|
```
|
|
117
134
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
Complete silently. Continue: execute `references/auto-docs-only.md` for documentation URLs; §1 for all other input types. No user output.
|
|
121
|
-
|
|
122
|
-
**4. If `matches[]` has one or more entries — coexistence gate:**
|
|
123
|
-
|
|
124
|
-
Present the user with the coexistence decision, one bullet per `matches[]` entry (`{skill_name}` = `matches[].name`, `{version}` = `matches[].active_version`, `{source_repo}` = `matches[].source_repo`):
|
|
135
|
+
`skf_skill` is true when SKF generated the matched skill (its `metadata.json` carries an SKF marker). Only those are offered for a merge: a merge hands the skill to update-skill, which rewrites its files, and SKF changes only the skills it generated.
|
|
125
136
|
|
|
126
|
-
|
|
127
|
-
⚠️ Existing skill(s) found for {target_name}:
|
|
128
|
-
|
|
129
|
-
• {skill_name} (v{version}) — source: {source_repo}
|
|
130
|
-
[repeat for each entry in matches[]]
|
|
131
|
-
|
|
132
|
-
Actions:
|
|
133
|
-
[A]longside — Create a new wiki skill with "-wiki" suffix (existing skill untouched)
|
|
134
|
-
[M]erge — Update the existing skill via US workflow (wiki data enriches it)
|
|
135
|
-
[S]kip — Do not create or modify any skill for this library
|
|
136
|
-
|
|
137
|
-
Choose [A/M/S]:
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
In headless mode (`{headless_mode}` is true): auto-select `[A]longside` and log: "Headless: coexistence detected for {target_name}, auto-selecting [A]longside"
|
|
137
|
+
**3. If `matches[]` is empty:**
|
|
141
138
|
|
|
142
|
-
|
|
139
|
+
Complete silently and go on to the route. No user output.
|
|
143
140
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
- **[M]erge:** If `matches[]` has more than one entry, prompt the user to select which one to merge into before proceeding. Read `{matched_skill_name}` = the selected entry's `matches[].name` and `{matched_active_path}` = its `matches[].active_path`. Emit a redirect envelope signaling the forger to route to the US workflow for the selected skill:
|
|
147
|
-
```
|
|
148
|
-
SKF_ANALYZE_RESULT_JSON: {"status":"redirect","redirect_to":"US","skill_name":"{matched_skill_name}","skill_path":"{matched_active_path}","exit_code":0,"halt_reason":null,"mode":"auto","coexistence":"merge"}
|
|
149
|
-
```
|
|
150
|
-
Write the result contract per `shared/references/output-contract-schema.md` with `status: "redirect"`.
|
|
151
|
-
Chain to {nextStepFile} (health-check.md). **STOP HERE — do not proceed to the docs-only sub-flow or §1.**
|
|
152
|
-
|
|
153
|
-
- **[S]kip:** Emit a skip envelope:
|
|
154
|
-
```
|
|
155
|
-
SKF_ANALYZE_RESULT_JSON: {"status":"skipped","report_path":null,"brief_paths":[],"unit_counts":{"confirmed":0,"skipped":1,"maybe":0},"exit_code":0,"halt_reason":null,"mode":"auto","coexistence":"skip","skipped_reason":"Existing skill for {matched_skill_name}"}
|
|
156
|
-
```
|
|
157
|
-
Write the result contract with `status: "skipped"`.
|
|
158
|
-
Chain to {nextStepFile} (health-check.md). **STOP HERE — do not proceed to the docs-only sub-flow or §1.**
|
|
141
|
+
**4. If `matches[]` has one or more entries:** load, read fully, then execute {coexistenceFile}, the coexistence gate. Its [A]longside comes back here with `{coexistence_suffix}` set to `-wiki` and goes on to the route; its [M]erge and [S]kip end the run through §9.
|
|
159
142
|
|
|
160
143
|
### 1. Load Context
|
|
161
144
|
|
|
162
145
|
Read {outputFile} frontmatter to obtain:
|
|
163
|
-
- `project_paths[]
|
|
164
|
-
- `forge_tier
|
|
146
|
+
- `project_paths[]`: the root(s) to analyze
|
|
147
|
+
- `forge_tier`: for brief generation
|
|
165
148
|
- `project_name`, `user_name`, `date`
|
|
149
|
+
- `scope_hint` (the folders or packages to focus on or skip) and `intent_hint` (the goal of the analysis), each possibly empty: §3b, §5 and §6 apply them
|
|
166
150
|
|
|
167
151
|
Load `references/step-shape-detect.md` as reference for shape detection invocation contract and shape→scope mapping.
|
|
168
152
|
|
|
169
153
|
### 2. Manifest Scan
|
|
170
154
|
|
|
171
|
-
Enumerate package manifests **deterministically** via `{scanManifestsHelper}` (the same helper the interactive `scan-project.md` uses)
|
|
155
|
+
Enumerate package manifests **deterministically** via `{scanManifestsHelper}` (the same helper the interactive `scan-project.md` uses): do not hand-scan. Resolve `{scanManifestsHelper}` as the first path in `{scanManifestsProbeOrder}` that exists; if none does, HARD HALT (exit code 3, `halt_reason: "resolution-failure"`, phase `step-auto-scope:2`): "`skf-scan-manifests.py` is missing. Re-install SKF." The scanner reads a **local directory**, so how you point it at the target depends on its `kind`: §0 gave `{kind}` and `{clone_url}` for its target, and §0's `derive-name --target` call gives them for any other path, and its `halt_message` halts the run as in §0, with phase `step-auto-scope:2` and that path as `{project_path}`.
|
|
172
156
|
|
|
173
157
|
**For each path in `project_paths[]`:**
|
|
174
158
|
|
|
175
|
-
- **
|
|
159
|
+
- **`kind` is `local`:** scan it directly. Its **scan root** is the path itself, with a leading `~` written as `$HOME`: the commands below quote every path, and a `~` inside double quotes does not expand.
|
|
176
160
|
|
|
177
161
|
```bash
|
|
178
|
-
uv run {scanManifestsHelper} scan {
|
|
162
|
+
uv run {scanManifestsHelper} scan "<the path's scan root>" > "{run_dir}/manifests-{i}.json"
|
|
179
163
|
```
|
|
180
164
|
|
|
181
|
-
-
|
|
165
|
+
- **`kind` is `remote`:** auto-scope has no working tree yet and the scanner cannot read a URL. Fetch **just the manifests** into the run folder (blobless + sparse + depth-1: no source blobs, typically KB to MB even for large monorepos); `{run_dir}/clone-{i}`, for the `{i}`-th entry of `project_paths[]` (`clone-1` for §0's target), is the path's **scan root**:
|
|
182
166
|
|
|
183
167
|
```bash
|
|
184
|
-
|
|
185
|
-
git clone
|
|
186
|
-
git -C "
|
|
187
|
-
|
|
188
|
-
uv run {scanManifestsHelper} scan "$tmp"
|
|
168
|
+
git clone --filter=blob:none --no-checkout --depth 1 {pinned_branch_flag} "{clone_url}" "{run_dir}/clone-{i}"
|
|
169
|
+
git -C "{run_dir}/clone-{i}" sparse-checkout set --no-cone '**/package.json' '**/Cargo.toml' '**/pyproject.toml' '**/go.mod' '**/pom.xml' '**/build.gradle' '**/build.gradle.kts' '**/Package.swift' 'pnpm-workspace.yaml' '**/pnpm-workspace.yaml' 'lerna.json' 'rush.json' 'nx.json'
|
|
170
|
+
git -C "{run_dir}/clone-{i}" checkout
|
|
171
|
+
uv run {scanManifestsHelper} scan "{run_dir}/clone-{i}" > "{run_dir}/manifests-{i}.json"
|
|
189
172
|
```
|
|
190
173
|
|
|
191
|
-
where `{pinned_branch_flag}` is `--branch {pinned_ref}` when a pin was resolved in §0b (so manifests match the target version), otherwise omitted.
|
|
192
|
-
|
|
193
|
-
Parse the JSON envelope: `{manifests: [{path, ecosystem, ...}], total_unique, monorepo, warnings?}`. The scanner discovers the project root plus monorepo workspace members (npm/pnpm/yarn `workspaces`, Cargo `[workspace]`, and other ecosystems) and sets the `monorepo` flag — so members are found without hand-listing each workspace convention, for both local trees and remote fetches.
|
|
174
|
+
where `{pinned_branch_flag}` is `--branch {pinned_ref}` when a pin was resolved in §0b (so manifests match the target version), otherwise omitted. If a `git` command fails, HARD HALT (exit code 3, `halt_reason: "resolution-failure"`, phase `step-auto-scope:2`, path `{path}`): "{path} could not be fetched: {the first stderr line}." The clone stays in the run folder until the run ends, so §3 and §5 read it in later commands.
|
|
194
175
|
|
|
195
|
-
From the envelope, record:
|
|
176
|
+
Each `{run_dir}/manifests-{i}.json` holds the scanner's JSON envelope: `{manifests: [{path, ecosystem, ...}], total_unique, monorepo, warnings?}`, each `path` relative to the scan root. The scanner discovers the project root plus monorepo workspace members (npm/pnpm/yarn `workspaces`, Cargo `[workspace]`, and other ecosystems) and sets the `monorepo` flag, so members are found without hand-listing each workspace convention, for both local trees and remote fetches. Shape detection (§3) reads `{run_dir}/manifests-1.json` itself, so no manifest list passes through you. From the envelope, record:
|
|
196
177
|
|
|
197
|
-
1. **
|
|
198
|
-
2.
|
|
178
|
+
1. **The `monorepo` flag**, a signal for the decomposition decision in §3a.
|
|
179
|
+
2. **The names and flags §3b, §6 and the split branch read:** the scan-root manifest's `name` and `private` (the `manifests[]` entry whose `path` has no folder), each member manifest's `name`, `private` and `internal_deps`, and `umbrella_candidates[]`. Read them from the envelope; never open a member manifest for them.
|
|
199
180
|
|
|
200
|
-
**
|
|
181
|
+
**List the target's files once.** Shape detection (§3) and language detection (§5 and the split branch) read the whole file list of the target (`{project_path}`, §0's target) from one run file, `{run_dir}/tree.txt`, so the listing never passes through you or a shell variable. Write it with the first command that fits the target, where `{scan_root}` is the target's scan root:
|
|
201
182
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
grammar_matches="$(printf '%s\n' "$files" \
|
|
208
|
-
| grep -Ei '\.(g4|pest|lalrpop|y|gram|lark|ebnf|peg|ungram)$|/grammar\.(js|json)$' \
|
|
209
|
-
| awk -F/ 'NF<=4' | head -n 50 | paste -sd, -)"
|
|
210
|
-
# Directory signals (trailing /) + depth-capped file basenames, narrowed to
|
|
211
|
-
# compiler-relevant paths so the argument stays bounded on huge repos. The
|
|
212
|
-
# filter is a loose superset of shape detection's gates — the script does the
|
|
213
|
-
# precise matching; this only keeps the list small.
|
|
214
|
-
tree_paths="$({ printf '%s\n' "$dirs" | sed 's#$#/#'; \
|
|
215
|
-
printf '%s\n' "$files" | awk -F/ 'NF<=5'; } \
|
|
216
|
-
| grep -Ei '(^|/)(compiler|compile|syntax|scanner|lexer|tokeniz|parser|parse|ast|binder|checker|codegen|ssagen|interpreter|vm|eval|rustc_[a-z]+)' \
|
|
217
|
-
| head -n 400 | paste -sd, -)"
|
|
218
|
-
```
|
|
219
|
-
- **Local path that is not a git repo** (`{path}`): list the tree with `find` instead, then derive `grammar_matches` / `tree_paths` the same way:
|
|
220
|
-
```bash
|
|
221
|
-
files="$(cd {path} && find . -type f -not -path '*/.git/*' | sed 's#^\./##')"
|
|
222
|
-
dirs="$( cd {path} && find . -type d -not -path '*/.git/*' | sed 's#^\./##')"
|
|
223
|
-
```
|
|
224
|
-
|
|
225
|
-
Record `<grammar_matches>` and `<tree_paths>` (each a comma-joined list, possibly empty) for §3.
|
|
183
|
+
```bash
|
|
184
|
+
git -C "{scan_root}" -c core.quotePath=false ls-tree -r --name-only HEAD > "{run_dir}/tree.txt"
|
|
185
|
+
git -C "{scan_root}" -c core.quotePath=false ls-files > "{run_dir}/tree.txt"
|
|
186
|
+
(cd "{scan_root}" && find . -type f -not -path '*/.git/*' | sed 's#^\./##') > "{run_dir}/tree.txt"
|
|
187
|
+
```
|
|
226
188
|
|
|
227
|
-
|
|
228
|
-
- **AND** `<grammar_matches>` is empty **AND** `<tree_paths>` shows no compiler directory (none of `compiler/`, `src/compiler/`, `cmd/compile/`, `internal/syntax/`, or a `Parser/`): the repo carries no language signal — emit fallback message: "**Auto-scope could not find any supported package manifests — switching to interactive mode.**" Load, read fully, then execute `references/scan-project.md`. **STOP HERE.**
|
|
229
|
-
- **Otherwise** (a grammar file or a compiler directory is present) the repo is a manifest-less language toolchain (CPython, Ruby): proceed to §3 with an **empty** `--manifests` and the harvested `--grammar-files` / `--tree-paths`.
|
|
189
|
+
The first lists a remote fetch (tree objects only: the blobless clone downloads no file for it), the second a local path inside a git work tree (`git -C "{scan_root}" rev-parse --is-inside-work-tree` prints `true`), the third any other local folder. If the command fails or writes an empty file, HARD HALT (exit code 3, `halt_reason: "resolution-failure"`, phase `step-auto-scope:2`, path `{project_path}`): "The files of {project_path} could not be listed: {the first stderr line}."
|
|
230
190
|
|
|
231
191
|
### 3. Invoke Shape Detection
|
|
232
192
|
|
|
233
|
-
**Resolve `{shapeDetectHelper}`** from `{shapeDetectProbeOrder}`; first existing path wins
|
|
193
|
+
**Resolve `{shapeDetectHelper}`** from `{shapeDetectProbeOrder}`; first existing path wins. If neither resolves, HARD HALT (exit code 3, `halt_reason: "resolution-failure"`, phase `step-auto-scope:3`): "`skf-shape-detect.py` is missing. Re-install SKF."
|
|
234
194
|
|
|
235
|
-
Invoke the shape detection script
|
|
195
|
+
Invoke the shape detection script on the §2 manifest scan and file list. It keeps the manifest types it classifies, resolves each against the scan root and finds the tree-level signals (grammar files, a compiler folder and its lexer, parser and ast) itself, so a manifest-less language toolchain (CPython, Ruby) is recognized from its files:
|
|
236
196
|
|
|
237
|
-
```
|
|
238
|
-
uv run {shapeDetectHelper} --repo-url
|
|
239
|
-
--manifests <comma_separated_manifest_paths> \
|
|
240
|
-
--grammar-files <grammar_matches> --tree-paths <tree_paths>
|
|
197
|
+
```bash
|
|
198
|
+
uv run {shapeDetectHelper} --repo-url "{project_path}" --manifests-file "{run_dir}/manifests-1.json" --manifest-dir "{scan_root}" --tree-file "{run_dir}/tree.txt"
|
|
241
199
|
```
|
|
242
200
|
|
|
243
|
-
|
|
201
|
+
Parse the JSON output: `{shape, signals, confidence, export_count, package_count}`
|
|
244
202
|
|
|
245
203
|
**Handle exit codes:**
|
|
246
204
|
|
|
247
205
|
- **Exit 0 (shape classified):** Continue to §3a.
|
|
248
|
-
- **Exit 1 (unknown shape):**
|
|
249
|
-
- **Exit 2 (error):** HARD HALT
|
|
206
|
+
- **Exit 1 (unknown shape):** the run goes on as an interactive analysis. Set `mode: 'interactive'` in {outputFile}'s frontmatter (so a later session resumes it: init archives an unfinished `'auto'` report) and `{auto_mode}` to false. When `{pinned_ref_type}` is `"tag"` or `"branch"`, also set `target_ref: '{pinned_ref}'` and `refs: {each project path: '{pinned_ref}'}` there, so the chain scans every path and writes every brief at the §0b pin (a `"local"` pin, or none, sets neither, as the §8 pin table gives no `target_ref` for them). Then emit the fallback message: "**Auto-scope could not classify this repo: switching to interactive mode.**", then load, read fully, and execute `references/scan-project.md`. **STOP HERE.**
|
|
207
|
+
- **Exit 2 (error):** HARD HALT (exit code 3, `halt_reason: "resolution-failure"`, phase `step-auto-scope:3`): "Shape detection failed: {the `error` its stderr JSON names}."
|
|
250
208
|
|
|
251
209
|
### 3a. Check Decomposition Thresholds
|
|
252
210
|
|
|
@@ -256,131 +214,94 @@ Apply the **Decomposition Thresholds** ladder from `step-shape-detect.md` (loade
|
|
|
256
214
|
|
|
257
215
|
**Decision:**
|
|
258
216
|
|
|
259
|
-
- **Threshold not met** (`package_count ≤ 3`) → Continue to §4 (single-scope flow
|
|
217
|
+
- **Threshold not met** (`package_count ≤ 3`) → Continue to §4 (single-scope flow).
|
|
260
218
|
- **Threshold met** (`package_count > 3`) → this repo is a **decomposition candidate**. A threshold firing means the repo *could* decompose, not that it *should* — continue to §3b to decide merge-vs-split. Log: "Auto-decomposition candidate: package_threshold ({value} packages exceeds 3)".
|
|
261
219
|
|
|
262
220
|
### 3b. Cohesion Check — Merge to One Skill vs Split into N
|
|
263
221
|
|
|
264
|
-
Reached only when §3a flagged a decomposition candidate. Most published monorepos are **cohesive** and produce a better single skill than a pile of fragments
|
|
222
|
+
Reached only when §3a flagged a decomposition candidate. Most published monorepos are **cohesive** and produce a better single skill than a pile of fragments. Load {heuristicsFile} and apply its **Cohesion Triggers**, the one statement of when members belong in one skill, reading their evidence from the §2 envelope (`umbrella_candidates[]`, and each member's `name`, `private` and `internal_deps`) and never from the member manifests themselves. Decide deliberately:
|
|
265
223
|
|
|
266
|
-
**Merge into ONE cohesive skill** (override the threshold → continue to §4 single-scope) when **any**
|
|
224
|
+
- **Merge into ONE cohesive skill** (override the threshold → continue to §4 single-scope) when **any** cohesion trigger holds.
|
|
225
|
+
- **Split into N skills** when the members meet its split condition instead: load, read fully, then execute {splitFile}, which replaces §4 to §6 and returns to §7.
|
|
267
226
|
|
|
268
|
-
|
|
269
|
-
- **Shared runtime contract** — the members are consumed together through one entry point, and teaching the shared invariant covers them (e.g. tRPC's adapters around `@trpc/server`; aws-sdk's `new XClient(...) → client.send(new YCommand(...))` shared by every `@aws-sdk/client-*`).
|
|
270
|
-
- **Internal building blocks** — the members are private/internal pieces of one product, not independently meaningful to a consumer.
|
|
227
|
+
If genuinely unsure, **prefer merge** — a too-broad single skill is recoverable with `US`; N fragmented skills are not.
|
|
271
228
|
|
|
272
|
-
**
|
|
229
|
+
**Record the decision** the moment you make it, so the run's result and a later halt both carry it: write `{run_dir}/decision.json` as `{"gate": "auto-scope.cohesion", "default_action": "merge", "taken_action": "<merge or split>", "reason": "<the cohesion trigger that held, or why the members split>", "evidence": {"package_count": <package_count>, "members": [<the §2 member names>]}}` and run
|
|
273
230
|
|
|
274
|
-
|
|
231
|
+
```bash
|
|
232
|
+
uv run {emitEnvelopeHelper} record --workflow skf-analyze-source --run-dir "{run_dir}" --decision < "{run_dir}/decision.json"
|
|
233
|
+
```
|
|
275
234
|
|
|
276
|
-
If
|
|
235
|
+
If the command fails, go on: only that entry is lost.
|
|
277
236
|
|
|
278
237
|
**Facet-coverage guard (merged facet-diverse repos only).** When you merge a repo whose members have genuinely distinct surfaces and you scope to only some of them, record the decision explicitly — never drop a facet silently:
|
|
279
238
|
|
|
280
|
-
-
|
|
239
|
+
- When `intent_hint` is not empty, the facets it names are the ones in scope.
|
|
240
|
+
- In `scope.notes`, name the in-scope facets **and** the excluded major facets, e.g. _"Scoped to react + react-dom core; excludes react-server-dom-\* (RSC), the specialized renderers (react-art/native/test), and the compiler: forge a separate skill for those."_
|
|
281
241
|
- Surface the excluded facets in the analysis report (§7) so the operator can re-scope or forge a companion skill.
|
|
282
242
|
|
|
283
243
|
### 4. Map Shape to Scope
|
|
284
244
|
|
|
285
|
-
Apply the canonical **Shape → Scope Type Mapping** table from `step-shape-detect.md` (loaded at §1)
|
|
245
|
+
Apply the canonical **Shape → Scope Type Mapping** table from `step-shape-detect.md` (loaded at §1).
|
|
286
246
|
|
|
287
|
-
|
|
247
|
+
**An app or a library on a framework.** When §3's `signals` hold `app_or_library:framework_dep`, take that table's row for it and judge which of its two cases the repository is from the scan-root manifest's `description` (its `manifests[]` entry in `{run_dir}/manifests-1.json`), or, when that does not settle it, the README's opening paragraph (a remote fetch checked out only the manifests: `git -C "{scan_root}" sparse-checkout add '/README*'` brings the README in). From here on `{shape}` is the shape you chose. Record the choice: write `{run_dir}/decision.json` as `{"gate": "auto-scope.shape", "default_action": "reference-app", "taken_action": "<reference-app or library-API>", "reason": "<the sentence of the description or README that decided>", "evidence": {"signals": [<the §3 signals>]}}` and run `uv run {emitEnvelopeHelper} record --workflow skf-analyze-source --run-dir "{run_dir}" --decision < "{run_dir}/decision.json"` (if it fails, go on).
|
|
288
248
|
|
|
289
|
-
|
|
249
|
+
**A language on a parser.** When §3's `signals` hold `language_or_user:parser_dep`, judge which case of that signal's row the repository is, from the same description or README: from here on `{shape}` is the shape that case gives. Record it the same way (after the choice above when both hold), with `default_action` the shape you judged from and `taken_action` the new `{shape}`.
|
|
290
250
|
|
|
291
|
-
|
|
251
|
+
### 5. Generate Include/Exclude Patterns
|
|
252
|
+
|
|
253
|
+
**Detect the primary language once**, from the §2 file list, with the shared helper. **Resolve `{detectWorkspacesHelper}`** from `{detectWorkspacesProbeOrder}` and **`{detectLanguageHelper}`** from `{detectLanguageProbeOrder}`; first existing path wins for each. If one has no candidate, HARD HALT (exit code 3, `halt_reason: "resolution-failure"`, phase `step-auto-scope:5`): "`{the missing script}` is missing. Re-install SKF." First find the workspace layout, which reads the root manifests from the scan root (§2):
|
|
292
254
|
|
|
293
255
|
```bash
|
|
294
|
-
|
|
256
|
+
uv run {detectWorkspacesHelper} --tree-file "{run_dir}/tree.txt" --manifest-dir "{scan_root}"
|
|
295
257
|
```
|
|
296
258
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
**Default patterns (adjust based on actual project structure):**
|
|
300
|
-
|
|
301
|
-
| Language | Default include | Default exclude |
|
|
302
|
-
|----------|-----------------|-----------------|
|
|
303
|
-
| TypeScript/JavaScript | `['src/**/*.ts', 'src/**/*.tsx']` | `['**/*.test.ts', '**/*.spec.ts', '**/node_modules/**']` |
|
|
304
|
-
| Python | `['src/**/*.py']` or `['{package_name}/**/*.py']` | `['**/*_test.py', '**/test_*.py', '**/tests/**']` |
|
|
305
|
-
| Rust | `['src/**/*.rs']` | `['**/tests/**', '**/benches/**']` |
|
|
306
|
-
| Go | `['**/*.go']` | `['**/*_test.go', '**/vendor/**']` |
|
|
307
|
-
| Java | `['src/main/java/**/*.java']` | `['**/src/test/**']` |
|
|
308
|
-
| Kotlin | `['src/main/kotlin/**/*.kt']` | `['**/src/test/**']` |
|
|
309
|
-
| Swift | `['Sources/**/*.swift']` | `['**/Tests/**']` |
|
|
310
|
-
|
|
311
|
-
**Adjust for actual layout:** If the project uses a non-standard layout (e.g., `lib/` instead of `src/`, or a named package directory for Python), detect and use the actual paths. Check for the existence of common source directories (`src/`, `lib/`, `pkg/`, the package name directory) and prefer the one that exists.
|
|
259
|
+
`{workspace_kind}` ← its `manifest_kind` (null when it exits non-zero). Then detect the language, passing `--workspace-signal {workspace_kind}` only when `{workspace_kind}` is not null, so a Cargo or Python workspace root wins over a nested package.json:
|
|
312
260
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
Build the scope object:
|
|
316
|
-
```yaml
|
|
317
|
-
scope:
|
|
318
|
-
type: '{mapped_scope_type}'
|
|
319
|
-
include: ['{generated_include_patterns}']
|
|
320
|
-
exclude: ['{generated_exclude_patterns}']
|
|
321
|
-
notes: 'Auto-scoped from shape detection (shape: {shape}, confidence: {confidence}).{corpus_caveat}'
|
|
261
|
+
```bash
|
|
262
|
+
uv run {detectLanguageHelper} --tree-file "{run_dir}/tree.txt" [--workspace-signal {workspace_kind}]
|
|
322
263
|
```
|
|
323
264
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
For the brief's `language` field, **reuse `{detected_language}` from §5** — do not re-detect (the §5 helper already resolved js-vs-ts from `tsconfig.json` and Java-vs-Kotlin from the tree).
|
|
327
|
-
|
|
328
|
-
### 6b. Seed Companion Corpora (whole-language references only)
|
|
329
|
-
|
|
330
|
-
Runs only when §3 classified the repo as `language-reference` **via a whole-language signal** — the `signals` array contains a `grammar_file:` or `tree_triad:` entry (a compiler / interpreter / grammar repo such as rust-lang/rust, TypeScript, CPython). **Skip** when `language-reference` fired only from `parser_producer:` / `parser_dep:` signals (a parser *library* such as pest or lalrpop): there the code **is** the product, so no companion prose is needed and the §6/§7 caveat below does not apply.
|
|
331
|
-
|
|
332
|
-
A whole-language skill's value is in the language's **prose** — the guide/Book, the standard/library API docs, idioms — not the compiler internals. Seed those canonical corpora so the forged skill teaches the language rather than its implementation.
|
|
333
|
-
|
|
334
|
-
**Resolve `{detectLanguageHelper}`** from `{detectLanguageProbeOrder}` and **`{languageCorporaHelper}`** from `{languageCorporaProbeOrder}` (first existing path wins).
|
|
335
|
-
|
|
336
|
-
1. **Derive the corpus language key `{corpus_language}`.** Prefer `{detected_language}` (from §5) when non-empty. Otherwise — a manifest-less toolchain such as CPython or Ruby — resolve it from the file paths harvested in §2:
|
|
337
|
-
```bash
|
|
338
|
-
echo '{"tree": [<harvested §2 file paths>]}' | uv run {detectLanguageHelper}
|
|
339
|
-
```
|
|
340
|
-
Use its `language` field. (`.c`/`.h`/`.y` are not in the detector's extension map, so a C-hosted language resolves by its real sources — Ruby via `.rb`.)
|
|
341
|
-
2. **Look up canonical corpora:**
|
|
342
|
-
```bash
|
|
343
|
-
uv run {languageCorporaHelper} --language {corpus_language}
|
|
344
|
-
```
|
|
345
|
-
- exit 0 → parse the `[{url, label, source}]` array (each seed carries `source: language-registry`) → these are `{corpus_seeds}`.
|
|
346
|
-
- exit 1 → no registry entry (long-tail language) → `{corpus_seeds}` is empty (README detection in brief-skill remains the only source).
|
|
347
|
-
- exit 2 → log a warning and treat as empty (best-effort; never halt).
|
|
348
|
-
3. Record `{N}` = number of seeds and `{corpus_labels}` = comma-joined labels, carried into the brief `doc_urls` (§8) and the honest caveat (§6/§7).
|
|
349
|
-
4. Build `{corpus_caveat}` (appended to `scope.notes` in §6/§8 and surfaced in §7) so the operator knows a code-only whole-language skill is low-value:
|
|
350
|
-
- `{N}` ≥ 1: `" LANGUAGE-REFERENCE CAVEAT: this skill's value is the {corpus_language} prose (guide/Book + std/library docs), not compiler internals. Seeded {N} corpus URL(s): {corpus_labels}. create-skill foregrounds this registry prose as the skill's Language Guide and demotes compiler-internal signatures to a reference-only section — review the forged skill if compiler internals still dominate."`
|
|
351
|
-
- `{N}` == 0: `" LANGUAGE-REFERENCE CAVEAT: no canonical corpora were found for {corpus_language} (README detection and the registry both came up empty). This skill is LOW-VALUE as code-only — attach the {corpus_language} guide + std/library docs manually (re-run with a doc URL, or enrich via US) before forging."`
|
|
352
|
-
|
|
353
|
-
For a parser-library `language-reference` (skipped above) and every other shape, `{corpus_caveat}` is empty.
|
|
265
|
+
The helper owns the `tsconfig.json` JS-vs-TS and `build.gradle` Java-vs-Kotlin disambiguation, ranks the manifests nearest the root first, and never lets a manifest in a docs, example, test or tool folder decide while another one exists. Read `{detected_language}`:
|
|
354
266
|
|
|
355
|
-
|
|
267
|
+
- **`detected_languages` has one entry or none, or `.confidence` is `high` and the §2 scan-root manifests (the `manifests[]` entries whose `path` has no folder) are all of one ecosystem:** `{detected_language}` ← `.language`. A manifest at the root decided, so the choice below would give the same answer.
|
|
268
|
+
- **Otherwise** (several languages, and no single-ecosystem root manifest decided): `.language` is the helper's first guess, not the answer. Choose the language the skill documents: among `detected_languages`, the language of the scan-root manifest (the §2 `manifests[]` entry whose `path` has no folder; with several, the one whose `name` is the package this repository publishes), or, when there is none, `.source_language`, the language most of the files are written in (CPython's listing holds only manifests below its root, so `.confidence` is `medium` and its sources say `python`). Record the choice: write `{run_dir}/decision.json` as `{"gate": "auto-scope.language", "default_action": "<detected_languages[0]>", "taken_action": "<the language you chose>", "reason": "<the manifest or the source files that decided>", "evidence": {"detected_languages": [<detected_languages>], "source_language": "<source_language>"}}` and run `uv run {emitEnvelopeHelper} record --workflow skf-analyze-source --run-dir "{run_dir}" --decision < "{run_dir}/decision.json"` (if it fails, go on).
|
|
356
269
|
|
|
357
|
-
|
|
270
|
+
**Take the patterns from the §2 listing**, never from the folders on disk (a remote scan root holds only the manifests): `scope.include` and `scope.exclude` are what this prints:
|
|
358
271
|
|
|
359
|
-
|
|
272
|
+
```bash
|
|
273
|
+
uv run {detectLanguageHelper} scope-patterns --tree-file "{run_dir}/tree.txt" --language {detected_language}
|
|
274
|
+
```
|
|
360
275
|
|
|
361
|
-
**
|
|
276
|
+
**Apply `scope_hint`** when it is not empty: root the include patterns at the folders or packages it focuses on (`packages/auth/**/*.ts`), and add `<folder>/**` to the exclude patterns for each folder it skips. Name the hint in `scope.notes`.
|
|
362
277
|
|
|
363
|
-
|
|
278
|
+
### 6. Build Scope and Determine Skill Name
|
|
364
279
|
|
|
365
|
-
|
|
280
|
+
The single-scope brief's scope (§8 hands these values to the brief writer):
|
|
366
281
|
|
|
367
|
-
|
|
282
|
+
- `scope.type`: the §4 mapped scope type
|
|
283
|
+
- `scope.include` and `scope.exclude`: the §5 patterns
|
|
284
|
+
- `scope.notes`: `Auto-scoped from shape detection (shape: {shape}, confidence: {confidence}).{corpus_caveat}`, plus the `scope_hint` note of §5 when there is one
|
|
368
285
|
|
|
369
|
-
|
|
286
|
+
**Whole-language reference:** when §3 classified the repo as `language-reference` and its `signals` hold a `grammar_file:` or `tree_triad:` entry, load, read fully, then execute {corporaFile} now: it sets `{corpus_seeds}`, `{corpus_caveat}`, `{corpus_doc_urls}` and the report's Companion Corpora subsection, then returns here. Otherwise (every other shape, a parser library included) `{corpus_caveat}` is empty and `{corpus_doc_urls}` null.
|
|
370
287
|
|
|
371
|
-
|
|
288
|
+
Name the skill with the helper that names every brief, by {heuristicsFile}'s Unit Names, so the name the brief gets is the name the coexistence check looks up:
|
|
372
289
|
|
|
373
|
-
|
|
290
|
+
```bash
|
|
291
|
+
uv run {skillInventoryHelper} derive-name --from - --skills-folder "{skills_output_folder}" <<'SKF_SKILL_NAME'
|
|
292
|
+
[{"target": "{project_path}", "manifest_name": <name>, "private": <private>, "members": [<member names>]}]
|
|
293
|
+
SKF_SKILL_NAME
|
|
294
|
+
```
|
|
374
295
|
|
|
375
|
-
|
|
296
|
+
`manifest_name` is the umbrella candidate's `name` when §3b merged on the umbrella facade trigger, else the §2 scan-root manifest's `name` (null without one), and `private` that manifest's `private`. `members` lists the `name` of every §2 member manifest whose `private` is not `true` (`[]` for a single package): a merged monorepo whose root names no published package takes the name they share (`aws-sdk` for the `@aws-sdk/*` packages), else the repository's. `{skill_name}` ← `names[0].name`; when the entry is in `unnamed` (a target with no folder name), name the skill after the analyzed folder yourself. A split names its boundaries in one call the same way, and names the entries `unnamed` and `duplicates` list from their folders.
|
|
376
297
|
|
|
377
|
-
|
|
298
|
+
When `names[0].existing` is not null and names a skill §0c did not present (a skill from another source that already has the name this brief gets), load, read fully, then execute {coexistenceFile} with it as the one `matches[]` entry before §7 writes anything: [M]erge and [S]kip end the run there; [A]longside, the headless choice, sets `{coexistence_suffix}` to `-wiki` and continues here. Then, if `{coexistence_suffix}` is non-empty, append it to the skill name.
|
|
378
299
|
|
|
379
|
-
|
|
300
|
+
The brief's `description` (one to three sentences, from the shape, the language and the manifest name) is worded toward `intent_hint` when it is not empty.
|
|
380
301
|
|
|
381
302
|
### 7. Write Analysis Report
|
|
382
303
|
|
|
383
|
-
Update {outputFile} with auto-scope results. If the write fails, HARD HALT
|
|
304
|
+
Update {outputFile} with auto-scope results. If the write fails, HARD HALT (exit code 4, `halt_reason: "write-failed"`, phase `step-auto-scope:7`, path `{outputFile}`): "The analysis report could not be written: {the error}."
|
|
384
305
|
|
|
385
306
|
**Update frontmatter:**
|
|
386
307
|
```yaml
|
|
@@ -408,11 +329,11 @@ decomposition:
|
|
|
408
329
|
|
|
409
330
|
Each `confirmed_units` entry includes `boundary_path` — the relative path to the boundary's root (e.g., `packages/core`). Omit the `decomposition` key entirely when single-scope (N = 1).
|
|
410
331
|
|
|
411
|
-
**When single-scope (N = 1):** No `decomposition` key. `confirmed_units` contains a single entry
|
|
332
|
+
**When single-scope (N = 1):** No `decomposition` key. `confirmed_units` contains a single entry.
|
|
412
333
|
|
|
413
334
|
**Append body section:**
|
|
414
335
|
|
|
415
|
-
For single-scope
|
|
336
|
+
For single-scope:
|
|
416
337
|
```markdown
|
|
417
338
|
## Auto-Scope Analysis
|
|
418
339
|
|
|
@@ -426,114 +347,108 @@ For single-scope (unchanged):
|
|
|
426
347
|
**Exclude Patterns:** {exclude patterns}
|
|
427
348
|
```
|
|
428
349
|
|
|
429
|
-
**When
|
|
350
|
+
**When {corporaFile} ran** (a whole-language `language-reference`), append the Companion Corpora subsection it gives.
|
|
430
351
|
|
|
431
|
-
|
|
432
|
-
## Companion Corpora (language-reference)
|
|
352
|
+
**For a split (N > 1):** append the decomposition body {splitFile} gives instead of the single-scope section.
|
|
433
353
|
|
|
434
|
-
|
|
435
|
-
**Corpora in brief doc_urls:** {final_doc_urls_count}
|
|
436
|
-
- {label}: {url} # one line per doc_urls entry
|
|
437
|
-
**Status:** {ATTACHED — canonical corpora present | DEGRADED — code-only, no canonical corpora; attach the {corpus_language} guide + std/library docs before forging}
|
|
438
|
-
```
|
|
439
|
-
|
|
440
|
-
For multi-scope (N > 1):
|
|
441
|
-
```markdown
|
|
442
|
-
## Auto-Scope Analysis — Decomposition ({N} skills)
|
|
354
|
+
### 8. Write Skill Briefs
|
|
443
355
|
|
|
444
|
-
**
|
|
445
|
-
**Decomposition:** {reason} ({N} boundaries)
|
|
446
|
-
**Parent Shape:** {shape} (confidence: {confidence})
|
|
447
|
-
**Export Count:** {export_count}
|
|
448
|
-
**Package Count:** {package_count}
|
|
356
|
+
The brief writer renders, checks and writes every brief, and applies the version precedence (`target_version`, then `detected_version`, then `1.0.0`) and the rule that `target_version` equals `version`. This section supplies values and never writes YAML. **Resolve `{writeSkillBriefHelper}`** from `{writeSkillBriefProbeOrder}` and **`{validateBriefSchemaHelper}`** from `{validateBriefSchemaProbeOrder}`; first existing path wins for each. If one has no candidate, HARD HALT (exit code 3, `halt_reason: "resolution-failure"`, phase `step-auto-scope:8`): "`{the missing script}` is missing. Re-install SKF."
|
|
449
357
|
|
|
450
|
-
|
|
451
|
-
**Scope Type:** {scope_type}
|
|
452
|
-
**Boundary Path:** {boundary_path}
|
|
453
|
-
**Include Patterns:** {include patterns}
|
|
454
|
-
**Exclude Patterns:** {exclude patterns}
|
|
455
|
-
**Rationale:** {boundary_rationale}
|
|
358
|
+
**For each confirmed unit** (1 for single-scope, N for decomposition), write its context as `{run_dir}/brief-{skill_name}.json`, a file and never an echo'd string (the language caveat holds an apostrophe):
|
|
456
359
|
|
|
457
|
-
|
|
458
|
-
|
|
360
|
+
```json
|
|
361
|
+
{
|
|
362
|
+
"name": "{skill_name}",
|
|
363
|
+
"target_version": "{target_version from the pin table, or null}",
|
|
364
|
+
"target_ref": "{target_ref from the pin table, or null}",
|
|
365
|
+
"detected_version": "{detected_version, or null}",
|
|
366
|
+
"source_type": "source",
|
|
367
|
+
"source_repo": "{project_path}",
|
|
368
|
+
"language": "{detected_language}",
|
|
369
|
+
"description": "{1-3 sentence description based on shape, language, and manifest name}",
|
|
370
|
+
"forge_tier": "{forge_tier}",
|
|
371
|
+
"created": "{current_date}",
|
|
372
|
+
"created_by": "{user_name}",
|
|
373
|
+
"scope_type": "{scope_type}",
|
|
374
|
+
"scope_include": ["{include_patterns}"],
|
|
375
|
+
"scope_exclude": ["{exclude_patterns}"],
|
|
376
|
+
"scope_notes": "{scope.notes}",
|
|
377
|
+
"scope_rationale": null,
|
|
378
|
+
"scope_tier_a_include": null,
|
|
379
|
+
"scope_amendments": null,
|
|
380
|
+
"scope_registry_path": null,
|
|
381
|
+
"scope_ui_variants": null,
|
|
382
|
+
"scope_demo_patterns": null,
|
|
383
|
+
"doc_urls": null,
|
|
384
|
+
"scripts_intent": null,
|
|
385
|
+
"assets_intent": null,
|
|
386
|
+
"source_authority": null,
|
|
387
|
+
"source_ref": null
|
|
388
|
+
}
|
|
459
389
|
```
|
|
460
390
|
|
|
461
|
-
|
|
391
|
+
Only `description` and `scope_notes` are in your words. Every other value comes from an earlier section:
|
|
462
392
|
|
|
463
|
-
|
|
393
|
+
- `name`, `scope_type`, `scope_include`, `scope_exclude` and `scope_notes`: §6 for a single scope; the split branch's §6a for each boundary of a decomposition.
|
|
394
|
+
- `language`: §5's `{detected_language}` for a single scope (detect once); the split branch's §6a for each boundary.
|
|
395
|
+
- `detected_version`: by `assets/skill-brief-schema.md`'s **Version Detection**, for the §2 scan-root manifest (its `manifests[]` entry in `{run_dir}/manifests-1.json`).
|
|
396
|
+
- `target_version` and `target_ref`, from the §0b pin, the same for every brief:
|
|
464
397
|
|
|
465
|
-
|
|
398
|
+
| `{pinned_ref_type}` | `target_version` | `target_ref` |
|
|
399
|
+
|---|---|---|
|
|
400
|
+
| `"tag"` | `{pinned_version}` | `{pinned_ref}` |
|
|
401
|
+
| `"branch"` | null | `{pinned_ref}` |
|
|
402
|
+
| `"local"` | `{pinned_version}` when it is a version (`X.Y.Z`, with an optional leading `v`), else null | null |
|
|
403
|
+
| null (no pin, no release) | null | null |
|
|
466
404
|
|
|
467
|
-
|
|
405
|
+
- `doc_urls`: `{corpus_doc_urls}` (null unless {corporaFile} seeded corpora).
|
|
468
406
|
|
|
469
|
-
|
|
470
|
-
name: '{skill_name}'
|
|
471
|
-
version: '{detected_version or 1.0.0}'
|
|
472
|
-
source_repo: '{project_path}'
|
|
473
|
-
language: '{detected_language}'
|
|
474
|
-
scope:
|
|
475
|
-
type: '{scope_type}'
|
|
476
|
-
include:
|
|
477
|
-
- '{include_patterns}'
|
|
478
|
-
exclude:
|
|
479
|
-
- '{exclude_patterns}'
|
|
480
|
-
notes: 'Auto-scoped from shape detection (shape: {shape}, confidence: {confidence}).{corpus_caveat}'
|
|
481
|
-
description: '{1-3 sentence description based on shape, language, and manifest name}'
|
|
482
|
-
forge_tier: '{forge_tier}'
|
|
483
|
-
created: '{current_date}'
|
|
484
|
-
created_by: '{user_name}'
|
|
485
|
-
```
|
|
407
|
+
**Gate every brief before any is written.** For each one, render it into the run folder and run on it the schema check skf-brief-skill runs when it reads a brief:
|
|
486
408
|
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
doc_urls:
|
|
491
|
-
- { url: '{seed.url}', label: '{seed.label}', source: '{seed.source}' } # one entry per §6b seed; source is 'language-registry'
|
|
409
|
+
```bash
|
|
410
|
+
uv run {writeSkillBriefHelper} write --target "{run_dir}/briefs/{skill_name}/skill-brief.yaml" --from-flat < "{run_dir}/brief-{skill_name}.json"
|
|
411
|
+
uv run {validateBriefSchemaHelper} "{run_dir}/briefs/{skill_name}/skill-brief.yaml"
|
|
492
412
|
```
|
|
493
413
|
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
**When decomposition is active (N > 1 units):**
|
|
497
|
-
|
|
498
|
-
Loop over all N boundaries. For each boundary:
|
|
499
|
-
- `name` is the boundary-derived skill name (e.g., `my-monorepo-core`)
|
|
500
|
-
- `include`/`exclude` patterns are boundary-scoped (from §5a)
|
|
501
|
-
- `scope.notes` includes decomposition context: "Decomposed from {project_name} ({N} skills) — boundary {i}/{N}: {boundary_description}"
|
|
502
|
-
- `description` references the parent project and boundary role (e.g., "Core library package of the my-monorepo project, providing...")
|
|
503
|
-
- All N briefs share the same `version`, `source_repo`, `language`, `forge_tier`, `created`, `created_by` values as the parent project
|
|
414
|
+
When the writer exits non-zero or the validator answers `valid: false`, the brief is rejected: HARD HALT (exit code 4, `halt_reason: "write-failed"`, phase `step-auto-scope:8`, path `{forge_data_folder}/{skill_name}/skill-brief.yaml`): "The brief for {skill_name} was rejected: {the writer's `message` and `field`, or each `errors[].message` of the validator}." No brief reaches `{forge_data_folder}` then, so the run never reports a brief that does not validate.
|
|
504
415
|
|
|
505
|
-
**
|
|
416
|
+
**Then write each brief** to its place, from the same context (the writer renders the same bytes, creates the folder and writes atomically):
|
|
506
417
|
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
- If `{pinned_ref_type}` is `"branch"`: set `target_ref` = `{pinned_ref}`, leave `target_version` = null, `version` = auto-detected or `1.0.0`.
|
|
511
|
-
- If `{pinned_ref_type}` is `"local"`: set `target_version` = `{pinned_version}`, `target_ref` = null, `version` = `{pinned_version}`.
|
|
512
|
-
|
|
513
|
-
When `{pinned_ref}` is null (no pin, no releases): leave `target_version` = null, `target_ref` = null — existing version detection applies unchanged.
|
|
418
|
+
```bash
|
|
419
|
+
uv run {writeSkillBriefHelper} write --target "{forge_data_folder}/{skill_name}/skill-brief.yaml" --from-flat < "{run_dir}/brief-{skill_name}.json"
|
|
420
|
+
```
|
|
514
421
|
|
|
515
|
-
|
|
422
|
+
Keep each `brief_path` it prints for §9. If it exits non-zero, HARD HALT (exit code 4, `halt_reason: "write-failed"`, phase `step-auto-scope:8`, path `{forge_data_folder}/{skill_name}/skill-brief.yaml`, with `unit_counts` and the `brief_paths` written so far): "The brief for {skill_name} could not be written: {its `message`}."
|
|
516
423
|
|
|
517
|
-
### 9.
|
|
424
|
+
### 9. End the Run
|
|
518
425
|
|
|
519
|
-
|
|
426
|
+
The shared emitter writes the result files and prints the envelope; this section stages their content, in every mode. After §8, write `{run_dir}/result-context.json` (a redirect or a skip from §0c has already written its own and comes here directly):
|
|
520
427
|
|
|
521
|
-
```
|
|
522
|
-
|
|
428
|
+
```json
|
|
429
|
+
{
|
|
430
|
+
"status": "success",
|
|
431
|
+
"report_path": "{outputFile as an absolute path}",
|
|
432
|
+
"brief_paths": ["{the brief_path of each brief §8 wrote}"],
|
|
433
|
+
"unit_counts": {"confirmed": <N>, "skipped": 0, "maybe": 0},
|
|
434
|
+
"mode": "auto",
|
|
435
|
+
"result_contract": {
|
|
436
|
+
"skill": "skf-analyze-source",
|
|
437
|
+
"status": "success",
|
|
438
|
+
"outputs": [{"type": "report", "path": "{outputFile as an absolute path}"}, {"type": "brief", "path": "{each brief_path}"}],
|
|
439
|
+
"summary": {"mode": "auto", "shape": "{shape}", "brief_count": <N>, "units": ["{each skill_name}"]}
|
|
440
|
+
}
|
|
441
|
+
}
|
|
523
442
|
```
|
|
524
443
|
|
|
525
|
-
`
|
|
444
|
+
Add `"coexistence": "alongside"` when `{coexistence_suffix}` is non-empty ([A]longside was selected in §0c), `"pinned_ref": "{pinned_ref}"` when `{pinned_ref}` is non-null, and `"pinned_version": "{pinned_version}"` when `{pinned_version}` is non-null (a branch pin, or a latest release whose tag is not a version, has none): these flow downstream to BS/CS for provenance recording. Put the same pin fields in `summary`. Then run:
|
|
526
445
|
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
### 10. Write Result Contract
|
|
532
|
-
|
|
533
|
-
Write the result contract per `shared/references/output-contract-schema.md`: the per-run record at `{forge_data_folder}/analyze-source-result-{YYYYMMDD-HHmmss}.json` (UTC timestamp, resolution to seconds) and a copy at `{forge_data_folder}/analyze-source-result-latest.json`. `outputs` lists all N brief paths and `summary` includes the brief count N. If the per-run record cannot be written, HARD HALT with exit code 4 (`write-failed`) per `references/headless-contract.md`.
|
|
446
|
+
```bash
|
|
447
|
+
uv run {emitEnvelopeHelper} emit --workflow skf-analyze-source --run-dir "{run_dir}" --result-dir "{forge_data_folder}" < "{run_dir}/result-context.json"
|
|
448
|
+
```
|
|
534
449
|
|
|
535
|
-
|
|
450
|
+
The emitter stamps the UTC time, the run id and the decisions and warnings the run recorded into the record, writes `{forge_data_folder}/analyze-source-result-{YYYYMMDD-HHmmss}.json` and its `analyze-source-result-latest.json` copy, and prints the `SKF_ANALYZE_RESULT_JSON:` line on stdout (field rules in `references/headless-contract.md`). When `{headless_mode}` is true, display the line verbatim. A result file that could not be written leaves the line's `result_path` null and a `result_file_write_failed` warning in it, and the run still finishes. If the emitter exits non-zero, correct `result-context.json` from the message on its stderr and run it once more; if it fails again, HARD HALT (exit code 4, `halt_reason: "write-failed"`, phase `step-auto-scope:9`, path `{forge_data_folder}`): "The result contract could not be written: {its message}."
|
|
536
451
|
|
|
537
|
-
|
|
452
|
+
If `{onCompleteCommand}` is non-empty and the line's `result_path` is not null, invoke it now: `{onCompleteCommand} --result-path={forge_data_folder}/analyze-source-result-latest.json`. Display a hook failure; it never fails the run.
|
|
538
453
|
|
|
539
|
-
Load, read fully, then execute {nextStepFile} to run the shared workflow health check.
|
|
454
|
+
Then delete the run folder, whose payloads the emitter has read: `rm -rf "{run_dir}"`. Load, read fully, then execute {nextStepFile} to run the shared workflow health check.
|