bmad-module-skill-forge 1.9.0 → 2.0.1
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/.nvmrc +1 -1
- package/README.md +10 -5
- package/docs/_data/pinned.yaml +2 -2
- package/docs/agents.md +11 -2
- package/docs/architecture.md +5 -4
- package/docs/bmad-synergy.md +30 -7
- package/docs/campaign.md +172 -0
- package/docs/examples.md +7 -3
- package/docs/forge-auto.md +90 -0
- package/docs/getting-started.md +9 -0
- package/docs/how-it-works.md +6 -4
- package/docs/index.md +4 -3
- package/docs/skill-model.md +1 -1
- package/docs/troubleshooting.md +17 -1
- package/docs/verifying-a-skill.md +2 -2
- package/docs/why-skf.md +1 -1
- package/docs/workflows.md +64 -24
- package/package.json +2 -2
- package/src/knowledge/skill-lifecycle.md +1 -1
- package/src/module-help.csv +4 -3
- package/src/shared/data/language-corpora.json +32 -0
- package/src/shared/references/pipeline-contracts.md +6 -3
- package/src/shared/scripts/schemas/skill-brief.v1.json +6 -1
- package/src/shared/scripts/skf-comention-pairs.py +350 -0
- package/src/shared/scripts/skf-count-tokens.py +222 -0
- package/src/shared/scripts/skf-derive-assembly-shape.py +107 -0
- package/src/shared/scripts/skf-detect-docs.py +240 -4
- package/src/shared/scripts/skf-detect-language.py +82 -3
- package/src/shared/scripts/skf-detect-tools.py +105 -2
- package/src/shared/scripts/skf-emit-result-envelope.py +17 -0
- package/src/shared/scripts/skf-enumerate-stack-skills.py +111 -2
- package/src/shared/scripts/skf-extract-public-api.py +53 -0
- package/src/shared/scripts/skf-find-cycles.py +246 -0
- package/src/shared/scripts/skf-hash-content.py +487 -2
- package/src/shared/scripts/skf-language-corpora.py +100 -0
- package/src/shared/scripts/skf-manifest-ops.py +67 -1
- package/src/shared/scripts/skf-merge-doc-urls.py +244 -0
- package/src/shared/scripts/skf-preapply.py +1 -1
- package/src/shared/scripts/skf-rebuild-managed-sections.py +180 -0
- package/src/shared/scripts/skf-recommend-scope-type.py +1 -2
- package/src/shared/scripts/skf-render-metadata-stats.py +499 -0
- package/src/shared/scripts/skf-rewrite-skill-name.py +308 -0
- package/src/shared/scripts/skf-shape-detect.py +576 -25
- package/src/shared/scripts/skf-shard-body.py +461 -0
- package/src/shared/scripts/skf-skill-inventory.py +139 -3
- package/src/shared/scripts/skf-structural-diff.py +307 -67
- package/src/shared/scripts/skf-validate-brief-schema.py +41 -6
- package/src/shared/scripts/skf-validate-feasibility-report.py +234 -0
- package/src/shared/scripts/skf-validate-frontmatter.py +17 -12
- package/src/shared/scripts/skf-validate-output.py +443 -10
- package/src/shared/scripts/skf-validate-pins.py +3 -3
- package/src/shared/scripts/skf-verify-no-trace.py +214 -0
- package/src/shared/scripts/skf-verify-provenance-completeness.py +439 -0
- package/src/shared/scripts/skf-write-skill-brief.py +21 -5
- package/src/skf-analyze-source/SKILL.md +17 -32
- package/src/skf-analyze-source/assets/skill-brief-schema.md +1 -1
- package/src/skf-analyze-source/customize.toml +2 -0
- package/src/skf-analyze-source/references/auto-docs-only.md +116 -0
- package/src/skf-analyze-source/references/continue.md +8 -13
- package/src/skf-analyze-source/references/generate-briefs.md +28 -12
- package/src/skf-analyze-source/references/headless-contract.md +33 -0
- package/src/skf-analyze-source/references/health-check.md +1 -2
- package/src/skf-analyze-source/references/identify-units.md +25 -36
- package/src/skf-analyze-source/references/init.md +12 -25
- package/src/skf-analyze-source/references/map-and-detect.md +8 -20
- package/src/skf-analyze-source/references/recommend.md +4 -14
- package/src/skf-analyze-source/references/scan-project.md +3 -11
- package/src/skf-analyze-source/references/step-auto-scope.md +211 -197
- package/src/skf-analyze-source/references/step-shape-detect.md +20 -13
- package/src/skf-analyze-source/references/unit-detection-heuristics.md +2 -2
- package/src/skf-audit-skill/SKILL.md +9 -6
- package/src/skf-audit-skill/customize.toml +2 -0
- package/src/skf-audit-skill/references/init.md +58 -32
- package/src/skf-audit-skill/references/re-index.md +15 -34
- package/src/skf-audit-skill/references/report.md +11 -58
- package/src/skf-audit-skill/references/semantic-diff.md +4 -22
- package/src/skf-audit-skill/references/severity-classify.md +53 -87
- package/src/skf-audit-skill/references/step-doc-drift.md +49 -19
- package/src/skf-audit-skill/references/structural-diff.md +43 -68
- package/src/skf-brief-skill/SKILL.md +7 -36
- package/src/skf-brief-skill/assets/scope-templates.md +1 -1
- package/src/skf-brief-skill/assets/skill-brief-schema.md +1 -1
- package/src/skf-brief-skill/customize.toml +14 -0
- package/src/skf-brief-skill/references/analyze-target.md +13 -18
- package/src/skf-brief-skill/references/confirm-brief.md +2 -15
- package/src/skf-brief-skill/references/draft-checkpoint.md +8 -8
- package/src/skf-brief-skill/references/gather-intent.md +27 -24
- package/src/skf-brief-skill/references/headless-args.md +1 -1
- package/src/skf-brief-skill/references/health-check.md +1 -22
- package/src/skf-brief-skill/references/invocation-contract.md +37 -0
- package/src/skf-brief-skill/references/scope-definition.md +6 -15
- package/src/skf-brief-skill/references/step-auto-brief.md +22 -9
- package/src/skf-brief-skill/references/step-auto-validate.md +3 -7
- package/src/skf-brief-skill/references/version-resolution.md +1 -1
- package/src/skf-brief-skill/references/write-brief.md +19 -13
- package/src/skf-campaign/SKILL.md +121 -0
- package/src/skf-campaign/assets/campaign-state-schema.json +191 -0
- package/src/skf-campaign/customize.toml +75 -0
- package/src/skf-campaign/manifest.yaml +15 -0
- package/src/skf-campaign/references/campaign-contracts.md +67 -0
- package/src/skf-campaign/references/campaign-directive-spec.md +32 -0
- package/src/skf-campaign/references/health-check.md +35 -0
- package/src/skf-campaign/references/step-01-setup.md +121 -0
- package/src/skf-campaign/references/step-02-strategy.md +102 -0
- package/src/skf-campaign/references/step-03-pins.md +56 -0
- package/src/skf-campaign/references/step-04-provenance.md +63 -0
- package/src/skf-campaign/references/step-05-skill-loop.md +103 -0
- package/src/skf-campaign/references/step-06-batch.md +88 -0
- package/src/skf-campaign/references/step-07-capstone.md +63 -0
- package/src/skf-campaign/references/step-08-verify.md +75 -0
- package/src/skf-campaign/references/step-09-refine.md +83 -0
- package/src/skf-campaign/references/step-10-export.md +106 -0
- package/src/skf-campaign/references/step-11-maintenance.md +84 -0
- package/src/skf-campaign/references/step-resume.md +115 -0
- package/src/skf-campaign/scripts/.gitkeep +0 -0
- package/src/skf-campaign/scripts/campaign-deps.py +244 -0
- package/src/skf-campaign/scripts/campaign-parse-manifest.py +119 -0
- package/src/skf-campaign/scripts/campaign-provenance.py +247 -0
- package/src/skf-campaign/scripts/campaign-render-batch.py +199 -0
- package/src/skf-campaign/scripts/campaign-render-kickoff.py +158 -0
- package/src/skf-campaign/scripts/campaign-report.py +249 -0
- package/src/skf-campaign/scripts/campaign-status.py +162 -0
- package/src/skf-campaign/scripts/campaign-validate-pins.py +174 -0
- package/src/skf-campaign/scripts/campaign-validate-state.py +207 -0
- package/src/skf-campaign/templates/campaign-brief-template.yaml +34 -0
- package/src/skf-campaign/templates/campaign-report-template.md +54 -0
- package/src/skf-campaign/templates/kickoff-template.md +48 -0
- package/src/skf-create-skill/SKILL.md +15 -4
- package/src/skf-create-skill/assets/compile-assembly-rules.md +35 -34
- package/src/skf-create-skill/assets/skill-sections.md +9 -100
- package/src/skf-create-skill/assets/tessl-dismissal-rules.md +4 -13
- package/src/skf-create-skill/customize.toml +13 -0
- package/src/skf-create-skill/references/authoritative-files-protocol.md +7 -7
- package/src/skf-create-skill/references/compile.md +37 -47
- package/src/skf-create-skill/references/component-extraction.md +8 -35
- package/src/skf-create-skill/references/ecosystem-check.md +9 -25
- package/src/skf-create-skill/references/enrich.md +4 -17
- package/src/skf-create-skill/references/extract.md +20 -27
- package/src/skf-create-skill/references/extraction-patterns-tracing.md +1 -1
- package/src/skf-create-skill/references/extraction-patterns.md +10 -8
- package/src/skf-create-skill/references/generate-artifacts.md +20 -25
- package/src/skf-create-skill/references/health-check.md +2 -2
- package/src/skf-create-skill/references/load-brief.md +16 -18
- package/src/skf-create-skill/references/report.md +27 -14
- package/src/skf-create-skill/references/source-resolution-protocols.md +4 -4
- package/src/skf-create-skill/references/step-auto-shard.md +52 -25
- package/src/skf-create-skill/references/step-doc-rot.md +33 -15
- package/src/skf-create-skill/references/step-doc-sources.md +13 -4
- package/src/skf-create-skill/references/sub/ccc-discover.md +2 -13
- package/src/skf-create-skill/references/sub/fetch-docs.md +33 -46
- package/src/skf-create-skill/references/sub/fetch-temporal.md +5 -20
- package/src/skf-create-skill/references/tier-degradation-rules.md +6 -8
- package/src/skf-create-skill/references/validate.md +69 -29
- package/src/skf-create-skill/scripts/scan-doc-rot.py +245 -0
- package/src/skf-create-stack-skill/SKILL.md +15 -9
- package/src/skf-create-stack-skill/assets/provenance-map-schema.md +1 -1
- package/src/skf-create-stack-skill/customize.toml +10 -0
- package/src/skf-create-stack-skill/references/compile-stack.md +7 -8
- package/src/skf-create-stack-skill/references/compose-mode-rules.md +2 -13
- package/src/skf-create-stack-skill/references/detect-integrations.md +49 -54
- package/src/skf-create-stack-skill/references/detect-manifests.md +37 -30
- package/src/skf-create-stack-skill/references/generate-output.md +24 -75
- package/src/skf-create-stack-skill/references/init.md +9 -81
- package/src/skf-create-stack-skill/references/manifest-patterns.md +5 -35
- package/src/skf-create-stack-skill/references/parallel-extract.md +12 -27
- package/src/skf-create-stack-skill/references/rank-and-confirm.md +6 -6
- package/src/skf-create-stack-skill/references/report.md +27 -62
- package/src/skf-create-stack-skill/references/validate.md +35 -31
- package/src/skf-drop-skill/SKILL.md +20 -23
- package/src/skf-drop-skill/customize.toml +10 -0
- package/src/skf-drop-skill/references/execute.md +67 -36
- package/src/skf-drop-skill/references/headless-contract.md +18 -0
- package/src/skf-drop-skill/references/health-check.md +1 -13
- package/src/skf-drop-skill/references/report.md +44 -10
- package/src/skf-drop-skill/references/select.md +93 -48
- package/src/skf-drop-skill/scripts/dir-sizes.py +116 -0
- package/src/skf-export-skill/SKILL.md +17 -14
- package/src/skf-export-skill/assets/managed-section-format.md +2 -5
- package/src/skf-export-skill/customize.toml +10 -6
- package/src/skf-export-skill/references/generate-snippet.md +21 -35
- package/src/skf-export-skill/references/load-skill.md +44 -42
- package/src/skf-export-skill/references/multi-skill-mode.md +1 -1
- package/src/skf-export-skill/references/orphan-context-detection.md +11 -3
- package/src/skf-export-skill/references/package.md +27 -33
- package/src/skf-export-skill/references/preflight-snippet-root-probe.md +24 -5
- package/src/skf-export-skill/references/result-envelope.md +28 -0
- package/src/skf-export-skill/references/summary.md +11 -5
- package/src/skf-export-skill/references/token-report.md +42 -24
- package/src/skf-export-skill/references/update-context.md +32 -33
- package/src/skf-forger/SKILL.md +32 -39
- package/src/skf-forger/references/pipeline-mode.md +34 -0
- package/src/skf-forger/scripts/parse-pipeline.py +270 -0
- package/src/skf-quick-skill/SKILL.md +10 -60
- package/src/skf-quick-skill/customize.toml +9 -0
- package/src/skf-quick-skill/references/batch-mode.md +6 -4
- package/src/skf-quick-skill/references/compile.md +15 -52
- package/src/skf-quick-skill/references/ecosystem-check.md +5 -20
- package/src/skf-quick-skill/references/finalize.md +12 -6
- package/src/skf-quick-skill/references/halt-contract.md +54 -0
- package/src/skf-quick-skill/references/health-check.md +3 -4
- package/src/skf-quick-skill/references/quick-extract.md +7 -18
- package/src/skf-quick-skill/references/registry-resolution.md +1 -23
- package/src/skf-quick-skill/references/resolve-target.md +27 -34
- package/src/skf-quick-skill/references/write-and-validate.md +10 -25
- package/src/skf-refine-architecture/SKILL.md +16 -8
- package/src/skf-refine-architecture/customize.toml +9 -0
- package/src/skf-refine-architecture/references/compile.md +10 -8
- package/src/skf-refine-architecture/references/gap-analysis.md +21 -50
- package/src/skf-refine-architecture/references/health-check.md +1 -1
- package/src/skf-refine-architecture/references/improvements.md +9 -25
- package/src/skf-refine-architecture/references/init.md +9 -16
- package/src/skf-refine-architecture/references/issue-detection.md +10 -25
- package/src/skf-refine-architecture/references/refinement-rules.md +4 -16
- package/src/skf-refine-architecture/references/report.md +14 -11
- package/src/skf-rename-skill/SKILL.md +11 -21
- package/src/skf-rename-skill/customize.toml +10 -0
- package/src/skf-rename-skill/references/execute.md +112 -82
- package/src/skf-rename-skill/references/exit-codes.md +12 -0
- package/src/skf-rename-skill/references/report.md +16 -8
- package/src/skf-rename-skill/references/select.md +69 -42
- package/src/skf-rename-skill/scripts/skf-validate-rename-name.py +161 -0
- package/src/skf-setup/SKILL.md +21 -13
- package/src/skf-setup/customize.toml +10 -0
- package/src/skf-setup/references/auto-index.md +11 -10
- package/src/skf-setup/references/ccc-index.md +14 -15
- package/src/skf-setup/references/detect-and-tier.md +10 -8
- package/src/skf-setup/references/report.md +26 -32
- package/src/skf-setup/references/tier-rules.md +2 -32
- package/src/skf-setup/references/write-config.md +9 -10
- package/src/skf-test-skill/SKILL.md +10 -8
- package/src/skf-test-skill/customize.toml +2 -0
- package/src/skf-test-skill/references/coherence-check.md +19 -31
- package/src/skf-test-skill/references/coverage-check.md +111 -67
- package/src/skf-test-skill/references/detect-mode.md +10 -41
- package/src/skf-test-skill/references/external-validators.md +11 -19
- package/src/skf-test-skill/references/init.md +39 -21
- package/src/skf-test-skill/references/migration-section-rules.md +4 -4
- package/src/skf-test-skill/references/report.md +60 -40
- package/src/skf-test-skill/references/score.md +32 -64
- package/src/skf-test-skill/references/scoring-rules.md +6 -62
- package/src/skf-test-skill/references/source-access-protocol.md +15 -5
- package/src/skf-test-skill/references/step-hard-gate.md +1 -3
- package/src/skf-test-skill/scripts/aggregate-coherence.py +276 -0
- package/src/skf-test-skill/scripts/check-metadata-coherence.py +359 -0
- package/src/skf-test-skill/scripts/combine-external-scores.py +190 -0
- package/src/skf-test-skill/scripts/compute-score.py +135 -4
- package/src/skf-test-skill/scripts/reconcile-coverage.py +327 -0
- package/src/skf-test-skill/scripts/validate-inventory.py +251 -0
- package/src/skf-test-skill/scripts/verify-declared-numerator.py +192 -0
- package/src/skf-update-skill/SKILL.md +17 -5
- package/src/skf-update-skill/customize.toml +11 -6
- package/src/skf-update-skill/references/detect-changes.md +48 -36
- package/src/skf-update-skill/references/health-check.md +5 -11
- package/src/skf-update-skill/references/init.md +40 -32
- package/src/skf-update-skill/references/manual-section-rules.md +1 -2
- package/src/skf-update-skill/references/merge-conflict-rules.md +1 -52
- package/src/skf-update-skill/references/merge.md +13 -57
- package/src/skf-update-skill/references/re-extract.md +27 -25
- package/src/skf-update-skill/references/report.md +14 -7
- package/src/skf-update-skill/references/validate.md +32 -30
- package/src/skf-update-skill/references/write.md +86 -51
- package/src/skf-update-skill/scripts/skf-new-file-diff.py +167 -0
- package/src/skf-verify-stack/SKILL.md +11 -21
- package/src/skf-verify-stack/customize.toml +12 -3
- package/src/skf-verify-stack/references/coverage-patterns.md +1 -29
- package/src/skf-verify-stack/references/coverage.md +21 -10
- package/src/skf-verify-stack/references/exit-codes.md +14 -0
- package/src/skf-verify-stack/references/health-check.md +1 -1
- package/src/skf-verify-stack/references/init.md +20 -25
- package/src/skf-verify-stack/references/integration-verification-rules.md +10 -19
- package/src/skf-verify-stack/references/integrations.md +44 -49
- package/src/skf-verify-stack/references/report.md +28 -34
- package/src/skf-verify-stack/references/requirements.md +3 -3
- package/src/skf-verify-stack/references/synthesize.md +36 -39
- package/src/skf-verify-stack/scripts/skf-coverage-tally.py +196 -0
- package/src/skf-verify-stack/scripts/skf-report-delta.py +256 -0
- package/src/skf-verify-stack/scripts/skf-verdict-rollup.py +258 -0
- package/tools/cli/lib/ui.js +3 -2
- package/docs/deepwiki.md +0 -89
- package/src/skf-rename-skill/references/rebuild-context.md +0 -110
|
@@ -1,7 +1,24 @@
|
|
|
1
1
|
---
|
|
2
2
|
nextStepFile: 'health-check.md'
|
|
3
3
|
outputFile: '{forge_data_folder}/analyze-source-report-{project_name}.md'
|
|
4
|
-
|
|
4
|
+
shapeDetectProbeOrder:
|
|
5
|
+
- '{project-root}/_bmad/skf/shared/scripts/skf-shape-detect.py'
|
|
6
|
+
- '{project-root}/src/shared/scripts/skf-shape-detect.py'
|
|
7
|
+
validatePinsProbeOrder:
|
|
8
|
+
- '{project-root}/_bmad/skf/shared/scripts/skf-validate-pins.py'
|
|
9
|
+
- '{project-root}/src/shared/scripts/skf-validate-pins.py'
|
|
10
|
+
skillInventoryProbeOrder:
|
|
11
|
+
- '{project-root}/_bmad/skf/shared/scripts/skf-skill-inventory.py'
|
|
12
|
+
- '{project-root}/src/shared/scripts/skf-skill-inventory.py'
|
|
13
|
+
scanManifestsProbeOrder:
|
|
14
|
+
- '{project-root}/_bmad/skf/shared/scripts/skf-scan-manifests.py'
|
|
15
|
+
- '{project-root}/src/shared/scripts/skf-scan-manifests.py'
|
|
16
|
+
detectLanguageProbeOrder:
|
|
17
|
+
- '{project-root}/_bmad/skf/shared/scripts/skf-detect-language.py'
|
|
18
|
+
- '{project-root}/src/shared/scripts/skf-detect-language.py'
|
|
19
|
+
languageCorporaProbeOrder:
|
|
20
|
+
- '{project-root}/_bmad/skf/shared/scripts/skf-language-corpora.py'
|
|
21
|
+
- '{project-root}/src/shared/scripts/skf-language-corpora.py'
|
|
5
22
|
---
|
|
6
23
|
|
|
7
24
|
<!-- Config: communicate in {communication_language}. -->
|
|
@@ -33,7 +50,7 @@ Apply the following heuristic to classify the input:
|
|
|
33
50
|
| `github.com/{owner}/{repo}` (with or without `.git` suffix, with or without scheme prefix) | GitHub repo | §1 (standard auto-scope) |
|
|
34
51
|
| `gitlab.com/...`, `bitbucket.org/...` | Git hosting | §1 (standard auto-scope) |
|
|
35
52
|
| Starts with `/`, `./`, `~/`, or `~` | Local filesystem path | §1 (standard auto-scope) |
|
|
36
|
-
| Any other `https://` or `http://` URL | Documentation URL |
|
|
53
|
+
| Any other `https://` or `http://` URL | Documentation URL | `references/auto-docs-only.md` (docs-only, via §0c) |
|
|
37
54
|
| Anything else (SSH URLs, `git://`, bare hostnames, etc.) | Unclassified | §1 (standard auto-scope) |
|
|
38
55
|
|
|
39
56
|
Store the classification result (documentation URL vs. repo/local/other). For all input types, continue to §0b (Pin Resolution).
|
|
@@ -48,23 +65,24 @@ This section validates and resolves version pins. It runs for repo URLs and loca
|
|
|
48
65
|
|
|
49
66
|
**For repo URLs when `--pin` is provided:**
|
|
50
67
|
|
|
68
|
+
**Resolve `{validatePinsHelper}`** from `{validatePinsProbeOrder}`; first existing path wins; HALT if neither resolves.
|
|
69
|
+
|
|
51
70
|
```bash
|
|
52
|
-
uv run
|
|
71
|
+
uv run {validatePinsHelper} --repo-url {project_path} --pin {pin_value}
|
|
53
72
|
```
|
|
54
73
|
|
|
55
74
|
Handle exit codes:
|
|
56
75
|
|
|
57
76
|
- **Exit 0** (`status: "valid"`): Store `{pinned_ref}` = `resolved_ref`, `{pinned_ref_type}` = `ref_type`, `{pinned_version}` = `version`. Continue to §0c.
|
|
58
|
-
- **Exit 1** (`status: "invalid"`): HARD HALT with exit code 3 (`resolution-failure`). Emit error: `"Version pin '{pin_value}' not found in {project_path}. Available matches: {suggestions}. Use a valid tag, branch, or omit --pin for latest."` Emit error envelope:
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
- **Exit 2** (error): HARD HALT with exit code 3 (`resolution-failure`). Emit error envelope with `halt_reason: "resolution-failure"`.
|
|
77
|
+
- **Exit 1** (`status: "invalid"`): HARD HALT with exit code 3 (`resolution-failure`). Emit error: `"Version pin '{pin_value}' not found in {project_path}. Available matches: {suggestions}. Use a valid tag, branch, or omit --pin for latest."` Emit the error envelope (shape in `references/headless-contract.md`) with `exit_code: 3`, `halt_reason: "pin-invalid"`, `mode: "auto"`.
|
|
78
|
+
- **Exit 2** (error): HARD HALT with exit code 3 (`resolution-failure`). Emit the error envelope with `halt_reason: "resolution-failure"`.
|
|
79
|
+
|
|
80
|
+
**For repo URLs when `--pin` is not provided (default):**
|
|
63
81
|
|
|
64
|
-
|
|
82
|
+
Using the same `{validatePinsHelper}` resolved above:
|
|
65
83
|
|
|
66
84
|
```bash
|
|
67
|
-
uv run
|
|
85
|
+
uv run {validatePinsHelper} --repo-url {project_path}
|
|
68
86
|
```
|
|
69
87
|
|
|
70
88
|
Handle exit codes:
|
|
@@ -79,32 +97,37 @@ This section checks for existing skills matching the target before proceeding. I
|
|
|
79
97
|
|
|
80
98
|
**1. Load skill inventory:**
|
|
81
99
|
|
|
100
|
+
**Resolve `{skillInventoryHelper}`** from `{skillInventoryProbeOrder}`; first existing path wins; HALT if neither resolves.
|
|
101
|
+
|
|
102
|
+
Pass the target (`{project_path}`) so the helper computes the coexistence match set for you — do not re-match by hand:
|
|
103
|
+
|
|
82
104
|
```bash
|
|
83
|
-
uv run
|
|
105
|
+
uv run {skillInventoryHelper} {skills_output_folder} --match-target {project_path}
|
|
84
106
|
```
|
|
85
107
|
|
|
86
|
-
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 continue
|
|
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 continue: load, read fully, then execute `references/auto-docs-only.md` for documentation URLs; §1 for all other input types.
|
|
87
109
|
|
|
88
|
-
**2.
|
|
110
|
+
**2. Read the match set:**
|
|
89
111
|
|
|
90
|
-
|
|
112
|
+
The helper already performed the match deterministically — scheme / trailing-`.git` / trailing-slash normalization, kebab expected-name derivation (§6 repo/package name, doc hostname per `references/auto-docs-only.md`), and case-insensitive comparison of both the normalized `source_repo` (URL match) and the derived name (name match). Read the top-level **`matches[]`** array from the JSON; do not normalize, derive, or compare anything in the prompt. Each entry is:
|
|
91
113
|
|
|
92
|
-
|
|
93
|
-
|
|
114
|
+
```json
|
|
115
|
+
{ "name": "...", "active_version": "...", "source_repo": "...", "active_path": "...", "match_reason": "url" | "name" | "both" }
|
|
116
|
+
```
|
|
94
117
|
|
|
95
|
-
**3. If
|
|
118
|
+
**3. If `matches[]` is empty:**
|
|
96
119
|
|
|
97
|
-
Complete silently. Continue
|
|
120
|
+
Complete silently. Continue: execute `references/auto-docs-only.md` for documentation URLs; §1 for all other input types. No user output.
|
|
98
121
|
|
|
99
|
-
**4. If one or more
|
|
122
|
+
**4. If `matches[]` has one or more entries — coexistence gate:**
|
|
100
123
|
|
|
101
|
-
Present the user with the coexistence decision:
|
|
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`):
|
|
102
125
|
|
|
103
126
|
```
|
|
104
127
|
⚠️ Existing skill(s) found for {target_name}:
|
|
105
128
|
|
|
106
129
|
• {skill_name} (v{version}) — source: {source_repo}
|
|
107
|
-
[repeat for each
|
|
130
|
+
[repeat for each entry in matches[]]
|
|
108
131
|
|
|
109
132
|
Actions:
|
|
110
133
|
[A]longside — Create a new wiki skill with "-wiki" suffix (existing skill untouched)
|
|
@@ -118,201 +141,160 @@ In headless mode (`{headless_mode}` is true): auto-select `[A]longside` and log:
|
|
|
118
141
|
|
|
119
142
|
**5. Handle user selection:**
|
|
120
143
|
|
|
121
|
-
- **[A]longside:** Set `{coexistence_suffix}` to `-wiki`. Continue
|
|
144
|
+
- **[A]longside:** Set `{coexistence_suffix}` to `-wiki`. Continue: execute `references/auto-docs-only.md` for documentation URLs; §1 for all other input types. The existing skill is untouched.
|
|
122
145
|
|
|
123
|
-
- **[M]erge:** If
|
|
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:
|
|
124
147
|
```
|
|
125
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"}
|
|
126
149
|
```
|
|
127
150
|
Write the result contract per `shared/references/output-contract-schema.md` with `status: "redirect"`.
|
|
128
|
-
Chain to {nextStepFile} (health-check.md). **STOP HERE — do not proceed to
|
|
151
|
+
Chain to {nextStepFile} (health-check.md). **STOP HERE — do not proceed to the docs-only sub-flow or §1.**
|
|
129
152
|
|
|
130
153
|
- **[S]kip:** Emit a skip envelope:
|
|
131
154
|
```
|
|
132
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}"}
|
|
133
156
|
```
|
|
134
157
|
Write the result contract with `status: "skipped"`.
|
|
135
|
-
Chain to {nextStepFile} (health-check.md). **STOP HERE — do not proceed to
|
|
136
|
-
|
|
137
|
-
### 0a. Docs-Only Short-Circuit
|
|
158
|
+
Chain to {nextStepFile} (health-check.md). **STOP HERE — do not proceed to the docs-only sub-flow or §1.**
|
|
138
159
|
|
|
139
|
-
|
|
160
|
+
### 1. Load Context
|
|
140
161
|
|
|
141
|
-
|
|
162
|
+
Read {outputFile} frontmatter to obtain:
|
|
163
|
+
- `project_paths[]` — the root(s) to analyze
|
|
164
|
+
- `forge_tier` — for brief generation
|
|
165
|
+
- `project_name`, `user_name`, `date`
|
|
142
166
|
|
|
143
|
-
|
|
144
|
-
curl -sI --max-time 5 {url}
|
|
145
|
-
```
|
|
167
|
+
Load `references/step-shape-detect.md` as reference for shape detection invocation contract and shape→scope mapping.
|
|
146
168
|
|
|
147
|
-
|
|
148
|
-
- On **4xx/5xx**, DNS failure, or timeout: HARD HALT with exit code 3 (`resolution-failure`). Emit error message: `"Documentation URL unreachable: {url} — {status or error}"`. Emit error envelope:
|
|
149
|
-
```
|
|
150
|
-
SKF_ANALYZE_RESULT_JSON: {"status":"error","report_path":null,"brief_paths":[],"unit_counts":{"confirmed":0,"skipped":0,"maybe":0},"exit_code":3,"halt_reason":"path-invalid","mode":"auto","source_type":"docs-only"}
|
|
151
|
-
```
|
|
169
|
+
### 2. Manifest Scan
|
|
152
170
|
|
|
153
|
-
**
|
|
171
|
+
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. The scanner reads a **local directory**, so how you point it at the target depends on the input form classified in §0:
|
|
154
172
|
|
|
155
|
-
|
|
173
|
+
**For each path in `project_paths[]`:**
|
|
156
174
|
|
|
157
|
-
**
|
|
175
|
+
- **Local filesystem path** (starts with `/`, `./`, `~/`, `~`, or is an existing directory) — scan it directly:
|
|
158
176
|
|
|
159
|
-
|
|
177
|
+
```bash
|
|
178
|
+
uv run {scanManifestsHelper} scan {path}
|
|
179
|
+
```
|
|
160
180
|
|
|
161
|
-
**
|
|
162
|
-
```yaml
|
|
163
|
-
stepsCompleted: ['init', 'auto-scope']
|
|
164
|
-
lastStep: 'auto-scope'
|
|
165
|
-
source_type: docs-only
|
|
166
|
-
confirmed_units:
|
|
167
|
-
- name: '{skill_name}'
|
|
168
|
-
shape: 'docs-only'
|
|
169
|
-
confidence: 1.0
|
|
170
|
-
export_count: 0
|
|
171
|
-
package_count: 0
|
|
172
|
-
```
|
|
181
|
+
- **Remote git URL** (e.g. `github.com/{owner}/{repo}`) — auto-scope has no working tree yet and the scanner cannot read a URL. Fetch **just the manifests** first (blobless + sparse + depth-1 — no source blobs, typically KB–MB even for large monorepos), then scan that tree:
|
|
173
182
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
183
|
+
```bash
|
|
184
|
+
tmp="$(mktemp -d)"
|
|
185
|
+
git clone --filter=blob:none --no-checkout --depth 1 {pinned_branch_flag} {path} "$tmp"
|
|
186
|
+
git -C "$tmp" 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'
|
|
187
|
+
git -C "$tmp" checkout
|
|
188
|
+
uv run {scanManifestsHelper} scan "$tmp"
|
|
189
|
+
```
|
|
177
190
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
191
|
+
where `{pinned_branch_flag}` is `--branch {pinned_ref}` when a pin was resolved in §0b (so manifests match the target version), otherwise omitted. **Retain `"$tmp"` through §3** — shape detection reads the discovered manifest files from it — then it may be discarded.
|
|
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.
|
|
194
|
+
|
|
195
|
+
From the envelope, record:
|
|
196
|
+
|
|
197
|
+
1. **Supported manifest paths** — filter `manifests[].path` to the types `skf-shape-detect.py` accepts (`package.json`, `pyproject.toml`, `Cargo.toml`, `go.mod`, `pom.xml`, `build.gradle`, `build.gradle.kts`, `Package.swift`). Each `manifests[].path` is **relative to the scan root**, so resolve them against that root (`{path}` for a local scan, `"$tmp"` for a remote fetch) before use. This filtered, comma-joined list of resolved paths is fed to shape detection in §3. For a monorepo, it includes each workspace member's manifest, so the package surface is classified accurately rather than from a bare (and often export-less) repo root. The scanner may discover ecosystems shape detection does not yet classify; those are excluded here, so a repo with no supported manifest falls back to interactive at the next check rather than auto-scoping.
|
|
198
|
+
2. **`monorepo` flag** and the count of discovered supported packages — carried forward as a signal for the decomposition decision in §3a.
|
|
199
|
+
|
|
200
|
+
**Harvest tree-level language signals.** A whole-language repo may declare no parser-generator dependency (a hand-written compiler such as rustc, TypeScript, or the Go toolchain) or carry no supported manifest at all (CPython, Ruby). From the **same** fetched tree — no second clone and no blobs, since tree objects are already present in the blobless clone — collect two signals for shape detection. These are pure path listings (`git ls-tree` reads tree objects; no checkout, no blob download):
|
|
201
|
+
|
|
202
|
+
- **Remote fetch** (`"$tmp"`), or a **local path** that is a git repo (`git -C {path}`):
|
|
203
|
+
```bash
|
|
204
|
+
files="$(git -C "$tmp" ls-tree -r --name-only HEAD)" # every file path
|
|
205
|
+
dirs="$( git -C "$tmp" ls-tree -r -d --name-only HEAD)" # every directory
|
|
206
|
+
# Grammar files (depth-capped to skip deep vendored fixtures, hard-capped):
|
|
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
|
+
```
|
|
183
224
|
|
|
184
|
-
|
|
225
|
+
Record `<grammar_matches>` and `<tree_paths>` (each a comma-joined list, possibly empty) for §3.
|
|
185
226
|
|
|
186
|
-
|
|
227
|
+
**IF no supported manifests are found** (the filtered list is empty):
|
|
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`.
|
|
187
230
|
|
|
188
|
-
|
|
231
|
+
### 3. Invoke Shape Detection
|
|
189
232
|
|
|
190
|
-
|
|
191
|
-
{
|
|
192
|
-
"name": "{skill_name}",
|
|
193
|
-
"target_version": null,
|
|
194
|
-
"detected_version": null,
|
|
195
|
-
"source_type": "docs-only",
|
|
196
|
-
"source_repo": "{url}",
|
|
197
|
-
"language": "",
|
|
198
|
-
"description": "Skill created from documentation at {url}",
|
|
199
|
-
"forge_tier": "{forge_tier}",
|
|
200
|
-
"created": "{current_date}",
|
|
201
|
-
"created_by": "{user_name}",
|
|
202
|
-
"scope_type": "docs-only",
|
|
203
|
-
"scope_include": [],
|
|
204
|
-
"scope_exclude": [],
|
|
205
|
-
"scope_notes": "Docs-only skill created from documentation URL",
|
|
206
|
-
"scope_rationale": null,
|
|
207
|
-
"scope_tier_a_include": null,
|
|
208
|
-
"scope_amendments": null,
|
|
209
|
-
"doc_urls": [{"url": "{url}", "label": "Primary Documentation"}],
|
|
210
|
-
"scripts_intent": null,
|
|
211
|
-
"assets_intent": null,
|
|
212
|
-
"source_authority": "community",
|
|
213
|
-
"target_ref": null,
|
|
214
|
-
"source_ref": null,
|
|
215
|
-
"version_resolved": "1.0.0"
|
|
216
|
-
}
|
|
217
|
-
```
|
|
233
|
+
**Resolve `{shapeDetectHelper}`** from `{shapeDetectProbeOrder}`; first existing path wins; HALT if neither resolves.
|
|
218
234
|
|
|
219
|
-
|
|
235
|
+
Invoke the shape detection script with the discovered manifests and the harvested tree-level signals:
|
|
220
236
|
|
|
221
237
|
```
|
|
222
|
-
|
|
238
|
+
uv run {shapeDetectHelper} --repo-url <project_path_or_url> \
|
|
239
|
+
--manifests <comma_separated_manifest_paths> \
|
|
240
|
+
--grammar-files <grammar_matches> --tree-paths <tree_paths>
|
|
223
241
|
```
|
|
224
242
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
The `source_type` field signals downstream consumers (BS) to skip repo-based enrichment.
|
|
228
|
-
|
|
229
|
-
**6. Write result contract** per `shared/references/output-contract-schema.md`: the per-run record and latest copy, same as §10.
|
|
230
|
-
|
|
231
|
-
If `{onCompleteCommand}` is non-empty, invoke it now with `--result-path={result_json_path}`.
|
|
232
|
-
|
|
233
|
-
**7. Chain to health check:**
|
|
234
|
-
|
|
235
|
-
Load, read fully, then execute {nextStepFile} to run the shared workflow health check. **Skip §1 through §11 entirely.**
|
|
236
|
-
|
|
237
|
-
### 1. Load Context
|
|
243
|
+
`<comma_separated_manifest_paths>` may be empty for a manifest-less language repo, provided `<grammar_matches>` or `<tree_paths>` carries the signal. Parse the JSON output: `{shape, signals, confidence, export_count, package_count}`
|
|
238
244
|
|
|
239
|
-
|
|
240
|
-
- `project_paths[]` — the root(s) to analyze
|
|
241
|
-
- `forge_tier` — for brief generation
|
|
242
|
-
- `project_name`, `user_name`, `date`
|
|
243
|
-
|
|
244
|
-
Load `references/step-shape-detect.md` as reference for shape detection invocation contract and shape→scope mapping.
|
|
245
|
-
|
|
246
|
-
### 2. Manifest Scan
|
|
247
|
-
|
|
248
|
-
Perform a lightweight manifest scan — find standard package manifests in the project root and workspace paths. Do NOT crawl the full directory tree.
|
|
245
|
+
**Handle exit codes:**
|
|
249
246
|
|
|
250
|
-
**
|
|
247
|
+
- **Exit 0 (shape classified):** Continue to §3a.
|
|
248
|
+
- **Exit 1 (unknown shape):** Emit fallback message: "**Auto-scope could not classify this repo — switching to interactive mode.**" Load, read fully, then execute `references/scan-project.md`. **STOP HERE.**
|
|
249
|
+
- **Exit 2 (error):** HARD HALT with exit code 3 (`resolution-failure`). Emit the error envelope (shape in `references/headless-contract.md`) with `exit_code: 3`, `halt_reason: "resolution-failure"`, `mode: "auto"`.
|
|
251
250
|
|
|
252
|
-
|
|
253
|
-
2. If a workspace configuration exists (e.g., `pnpm-workspace.yaml`, Cargo.toml `[workspace].members`), scan workspace member paths for additional manifests
|
|
254
|
-
3. Record each discovered manifest as `{path, type}` pairs
|
|
251
|
+
### 3a. Check Decomposition Thresholds
|
|
255
252
|
|
|
256
|
-
**
|
|
257
|
-
- Emit fallback message: "**Auto-scope could not find any package manifests — switching to interactive mode.**"
|
|
258
|
-
- Load, read fully, then execute `references/scan-project.md`. **STOP HERE.**
|
|
253
|
+
Evaluate the shape detection output to determine whether this **monorepo** should be decomposed into multiple skills.
|
|
259
254
|
|
|
260
|
-
|
|
255
|
+
Apply the **Decomposition Thresholds** ladder from `step-shape-detect.md` (loaded at §1). A *single* package with a large API surface is **not** a trigger — only a genuine multi-package monorepo is.
|
|
261
256
|
|
|
262
|
-
|
|
257
|
+
**Decision:**
|
|
263
258
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
```
|
|
259
|
+
- **Threshold not met** (`package_count ≤ 3`) → Continue to §4 (single-scope flow, entirely unchanged).
|
|
260
|
+
- **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)".
|
|
267
261
|
|
|
268
|
-
|
|
262
|
+
### 3b. Cohesion Check — Merge to One Skill vs Split into N
|
|
269
263
|
|
|
270
|
-
**
|
|
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 — empirically, 5/5 real monorepos (animato 15 crates, trpc, react 38 packages, aws-sdk-js-v3 442 packages, plus zod) were best served as one cohesive skill or a curated few, not one-skill-per-package. Decide deliberately:
|
|
271
265
|
|
|
272
|
-
|
|
273
|
-
- **Exit 1 (unknown shape):** Emit fallback message: "**Auto-scope could not classify this repo — switching to interactive mode.**" Load, read fully, then execute `references/scan-project.md`. **STOP HERE.**
|
|
274
|
-
- **Exit 2 (error):** HARD HALT with exit code 3 (`resolution-failure`). Emit the error envelope:
|
|
275
|
-
```
|
|
276
|
-
SKF_ANALYZE_RESULT_JSON: {"status":"error","report_path":null,"brief_paths":[],"unit_counts":{"confirmed":0,"skipped":0,"maybe":0},"exit_code":3,"halt_reason":"resolution-failure","mode":"auto"}
|
|
277
|
-
```
|
|
266
|
+
**Merge into ONE cohesive skill** (override the threshold → continue to §4 single-scope) when **any** of these hold:
|
|
278
267
|
|
|
279
|
-
|
|
268
|
+
- **Umbrella facade** — one package re-exports the members: a root or named package whose dependencies include the other workspace members, or which `pub use` / `export *`s them. The facade *is* the public surface (e.g. animato's `crates/animato` re-exporting its 15 sub-crates).
|
|
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.
|
|
280
271
|
|
|
281
|
-
|
|
272
|
+
**Split into N skills** (→ §4a) when:
|
|
282
273
|
|
|
283
|
-
**
|
|
274
|
+
- The members are **independently published with distinct public surfaces serving different concerns**, **and no umbrella re-exports them** — e.g. `react-dom` and `react-server-dom-*` are separate installs with separate jobs, or a federated SDK where a consumer only ever wants one service. Each genuinely-distinct facet earns its own skill.
|
|
284
275
|
|
|
285
|
-
|
|
286
|
-
|-----------|-----------|--------|
|
|
287
|
-
| Large export surface | `export_count > 500` `[PENDING VALIDATION]` | Single skill covering 500+ exports produces unwieldy output |
|
|
288
|
-
| Monorepo / multi-package | `package_count > 3` `[PENDING VALIDATION]` | 4+ packages are almost always multi-concern |
|
|
276
|
+
If genuinely unsure, **prefer merge** — a too-broad single skill is recoverable with `US`; N fragmented skills are not.
|
|
289
277
|
|
|
290
|
-
**
|
|
278
|
+
**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:
|
|
291
279
|
|
|
292
|
-
-
|
|
293
|
-
-
|
|
280
|
+
- 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
|
+
- Surface the excluded facets in the analysis report (§7) so the operator can re-scope or forge a companion skill.
|
|
294
282
|
|
|
295
283
|
### 4. Map Shape to Scope
|
|
296
284
|
|
|
297
|
-
Apply the shape→
|
|
298
|
-
|
|
299
|
-
| Shape (from skf-shape-detect.py) | scope.type | Condition |
|
|
300
|
-
|----------------------------------|------------|-----------|
|
|
301
|
-
| `library-API` | `full-library` | export_count ≤ 200 |
|
|
302
|
-
| `library-API` | `public-api` | export_count > 200 |
|
|
303
|
-
| `reference-app` | `reference-app` | — |
|
|
304
|
-
| `language-reference` | `full-library` | — |
|
|
305
|
-
| `stack-compose` | `full-library` | Multi-scope via §3a when `package_count > 3` `[PENDING VALIDATION]` |
|
|
285
|
+
Apply the canonical **Shape → Scope Type Mapping** table from `step-shape-detect.md` (loaded at §1) — the single source of truth for this ladder (the `export_count > 200 → public-api` split, the `language-reference` corpora caveat, and the `stack-compose` decomposition note).
|
|
306
286
|
|
|
307
287
|
### 5. Generate Include/Exclude Patterns
|
|
308
288
|
|
|
309
289
|
Generate `scope.include` and `scope.exclude` arrays from the detected language and project structure.
|
|
310
290
|
|
|
311
|
-
**Detect primary language** from manifest
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
291
|
+
**Detect the primary language once, deterministically**, via the shared helper — the single source of truth for the manifest→language rule table (§6b resolves the same helper; do not restate the table in prose, where it drifts from the script). **Resolve `{detectLanguageHelper}`** from `{detectLanguageProbeOrder}` (first existing path wins). Pipe the §2 supported-manifest paths (and, for a manifest-less toolchain, the harvested `<tree_paths>`) as the file tree:
|
|
292
|
+
|
|
293
|
+
```bash
|
|
294
|
+
echo '{"tree": [<§2 supported manifest paths + harvested tree paths>]}' | uv run {detectLanguageHelper}
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
Read `.language` as `{detected_language}` (and `.confidence`) — the helper owns the `tsconfig.json` JS-vs-TS and `build.gradle` Java-vs-Kotlin disambiguation. §6 reuses `{detected_language}` for the brief's `language` field: detect once.
|
|
316
298
|
|
|
317
299
|
**Default patterns (adjust based on actual project structure):**
|
|
318
300
|
|
|
@@ -321,6 +303,10 @@ Generate `scope.include` and `scope.exclude` arrays from the detected language a
|
|
|
321
303
|
| TypeScript/JavaScript | `['src/**/*.ts', 'src/**/*.tsx']` | `['**/*.test.ts', '**/*.spec.ts', '**/node_modules/**']` |
|
|
322
304
|
| Python | `['src/**/*.py']` or `['{package_name}/**/*.py']` | `['**/*_test.py', '**/test_*.py', '**/tests/**']` |
|
|
323
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/**']` |
|
|
324
310
|
|
|
325
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.
|
|
326
312
|
|
|
@@ -332,41 +318,53 @@ scope:
|
|
|
332
318
|
type: '{mapped_scope_type}'
|
|
333
319
|
include: ['{generated_include_patterns}']
|
|
334
320
|
exclude: ['{generated_exclude_patterns}']
|
|
335
|
-
notes: 'Auto-scoped from shape detection (shape: {shape}, confidence: {confidence})'
|
|
321
|
+
notes: 'Auto-scoped from shape detection (shape: {shape}, confidence: {confidence}).{corpus_caveat}'
|
|
336
322
|
```
|
|
337
323
|
|
|
338
324
|
Determine the skill name from the project name or package name (kebab-case, lowercase). Use the manifest `name` field if available, otherwise derive from the project directory name. If `{coexistence_suffix}` is non-empty, append it to the skill name.
|
|
339
325
|
|
|
340
|
-
|
|
341
|
-
- `npm` → `typescript` (or `javascript` if no `.ts` files in includes)
|
|
342
|
-
- `python` → `python`
|
|
343
|
-
- `rust` → `rust`
|
|
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).
|
|
344
327
|
|
|
345
|
-
###
|
|
328
|
+
### 6b. Seed Companion Corpora (whole-language references only)
|
|
346
329
|
|
|
347
|
-
|
|
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.
|
|
348
331
|
|
|
349
|
-
**
|
|
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.
|
|
350
333
|
|
|
351
|
-
|
|
352
|
-
- **Large-export path** (`export_count > 500`, single package): Group by top-level source directory modules (e.g., `src/auth/`, `src/core/`, `src/api/`). Each directory subtree with a meaningful export surface becomes a separate skill boundary. Candidate boundaries with fewer than ~50 exports `[PENDING VALIDATION]` should be merged into an "other" catch-all skill rather than becoming standalone skills. Name each skill as `{project_name}-{module_name}` (kebab-case); if `{coexistence_suffix}` is non-empty, append it. If no clear module structure exists (flat `src/` with all files at root level), **do not force decomposition** — fall back to single-scope flow at §4.
|
|
353
|
-
- **Combined path** (both thresholds met): Use the monorepo path. Package boundaries are explicit and take priority over export-count grouping (which is heuristic).
|
|
334
|
+
**Resolve `{detectLanguageHelper}`** from `{detectLanguageProbeOrder}` and **`{languageCorporaHelper}`** from `{languageCorporaProbeOrder}` (first existing path wins).
|
|
354
335
|
|
|
355
|
-
**
|
|
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.
|
|
356
354
|
|
|
357
|
-
|
|
355
|
+
### 4a. Multi-Scope Decomposition
|
|
358
356
|
|
|
359
|
-
|
|
360
|
-
|--------------------|---------------------------|
|
|
361
|
-
| Monorepo (`package_count > 3`) | Re-run the shape→scope heuristic ladder from `step-shape-detect.md` per package using each package's own manifest data. Packages may have different shapes (e.g., a `library-API` core + a `reference-app` CLI). |
|
|
362
|
-
| Large-export (`export_count > 500`) | All boundaries inherit the parent shape. scope.type varies by per-boundary export count (e.g., ≤200 → `full-library`, >200 → `public-api`). |
|
|
357
|
+
This section is reached only from §3b when the cohesion check decided to **split** a monorepo (members are independently published with distinct surfaces and no umbrella re-exports them). It replaces §4→§5→§6 for repos that will produce N > 1 skills.
|
|
363
358
|
|
|
364
|
-
|
|
359
|
+
**Decompose by workspace package:** Use workspace package discovery from §2 manifest scan results. Each workspace package with its own manifest becomes a separate skill boundary. Name each skill as `{project_name}-{package_name}` (kebab-case); if `{coexistence_suffix}` is non-empty, append it. Trivial workspace members (no source files, no exports) are excluded.
|
|
360
|
+
|
|
361
|
+
**Per-boundary shape→scope mapping:**
|
|
362
|
+
|
|
363
|
+
For each decomposed boundary, apply the shape→scope mapping from §4 independently — re-run the shape→scope heuristic ladder from `step-shape-detect.md` per package using each package's own manifest data. Packages may have different shapes (e.g., a `library-API` core + a `reference-app` CLI).
|
|
365
364
|
|
|
366
|
-
|
|
365
|
+
### 5a. Generate Multi-Scope Patterns
|
|
367
366
|
|
|
368
|
-
- Monorepo boundaries
|
|
369
|
-
- Large-export boundaries: patterns are rooted at the module directory (e.g., `src/auth/**/*.ts`)
|
|
367
|
+
For each decomposed boundary, generate include/exclude patterns using the same language-aware rules as §5, but scoped to the boundary's source paths. Monorepo boundaries are rooted at the package path (e.g., `packages/auth/src/**/*.ts` instead of `src/**/*.ts`).
|
|
370
368
|
|
|
371
369
|
### 6a. Build Multi-Scope
|
|
372
370
|
|
|
@@ -382,7 +380,7 @@ After building all N scopes, continue to §7 with the full set of boundaries.
|
|
|
382
380
|
|
|
383
381
|
### 7. Write Analysis Report
|
|
384
382
|
|
|
385
|
-
Update {outputFile} with auto-scope results.
|
|
383
|
+
Update {outputFile} with auto-scope results. If the write fails, HARD HALT with exit code 4 (`write-failed`) per `references/headless-contract.md`.
|
|
386
384
|
|
|
387
385
|
**Update frontmatter:**
|
|
388
386
|
```yaml
|
|
@@ -404,11 +402,11 @@ Add `decomposition` to frontmatter:
|
|
|
404
402
|
```yaml
|
|
405
403
|
decomposition:
|
|
406
404
|
triggered: true
|
|
407
|
-
reason: '
|
|
405
|
+
reason: 'package_threshold'
|
|
408
406
|
boundary_count: N
|
|
409
407
|
```
|
|
410
408
|
|
|
411
|
-
Each `confirmed_units` entry includes `boundary_path` — the relative path to the boundary's root (e.g., `packages/core`
|
|
409
|
+
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).
|
|
412
410
|
|
|
413
411
|
**When single-scope (N = 1):** No `decomposition` key. `confirmed_units` contains a single entry (existing behavior).
|
|
414
412
|
|
|
@@ -428,6 +426,17 @@ For single-scope (unchanged):
|
|
|
428
426
|
**Exclude Patterns:** {exclude patterns}
|
|
429
427
|
```
|
|
430
428
|
|
|
429
|
+
**When the shape is a whole-language `language-reference`** (§6b ran — a `grammar_file:`/`tree_triad:` signal), append a Companion Corpora subsection so the operator sees whether the skill has the prose that makes it useful. The status is computed from the **final** brief `doc_urls` (the entries that will actually be fetched), not the seed count alone:
|
|
430
|
+
|
|
431
|
+
```markdown
|
|
432
|
+
## Companion Corpora (language-reference)
|
|
433
|
+
|
|
434
|
+
**Why:** A whole-language skill's value is its prose (guide/Book, std/library docs, idioms), not compiler internals.
|
|
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
|
+
|
|
431
440
|
For multi-scope (N > 1):
|
|
432
441
|
```markdown
|
|
433
442
|
## Auto-Scope Analysis — Decomposition ({N} skills)
|
|
@@ -453,9 +462,9 @@ For multi-scope (N > 1):
|
|
|
453
462
|
|
|
454
463
|
**For each confirmed unit** (1 for single-scope, N for decomposition):
|
|
455
464
|
|
|
456
|
-
Create directory `{forge_data_folder}/{skill_name}/` if it does not exist.
|
|
465
|
+
Create directory `{forge_data_folder}/{skill_name}/` if it does not exist. If a brief write fails, HARD HALT with exit code 4 (`write-failed`) per `references/headless-contract.md`.
|
|
457
466
|
|
|
458
|
-
Write `{forge_data_folder}/{skill_name}/skill-brief.yaml` conforming to the skill-brief schema (`
|
|
467
|
+
Write `{forge_data_folder}/{skill_name}/skill-brief.yaml` conforming to the skill-brief schema (`{briefSchemaPath}`):
|
|
459
468
|
|
|
460
469
|
```yaml
|
|
461
470
|
name: '{skill_name}'
|
|
@@ -468,13 +477,22 @@ scope:
|
|
|
468
477
|
- '{include_patterns}'
|
|
469
478
|
exclude:
|
|
470
479
|
- '{exclude_patterns}'
|
|
471
|
-
notes: 'Auto-scoped from shape detection (shape: {shape}, confidence: {confidence})'
|
|
480
|
+
notes: 'Auto-scoped from shape detection (shape: {shape}, confidence: {confidence}).{corpus_caveat}'
|
|
472
481
|
description: '{1-3 sentence description based on shape, language, and manifest name}'
|
|
473
482
|
forge_tier: '{forge_tier}'
|
|
474
483
|
created: '{current_date}'
|
|
475
484
|
created_by: '{user_name}'
|
|
476
485
|
```
|
|
477
486
|
|
|
487
|
+
**Companion corpora (whole-language references).** When §6b produced `{corpus_seeds}` (`{N}` ≥ 1), add them as the brief's `doc_urls` so the language's prose is fetched and assembled alongside the code:
|
|
488
|
+
|
|
489
|
+
```yaml
|
|
490
|
+
doc_urls:
|
|
491
|
+
- { url: '{seed.url}', label: '{seed.label}', source: '{seed.source}' } # one entry per §6b seed; source is 'language-registry'
|
|
492
|
+
```
|
|
493
|
+
|
|
494
|
+
These are the brief's *existing* `doc_urls`; brief-skill's README detection then merges additional discovered docs on top (existing entries win). **When `{N}` is 0, omit the `doc_urls` key entirely** — the schema requires at least one entry when the key is present.
|
|
495
|
+
|
|
478
496
|
**When decomposition is active (N > 1 units):**
|
|
479
497
|
|
|
480
498
|
Loop over all N boundaries. For each boundary:
|
|
@@ -484,7 +502,7 @@ Loop over all N boundaries. For each boundary:
|
|
|
484
502
|
- `description` references the parent project and boundary role (e.g., "Core library package of the my-monorepo project, providing...")
|
|
485
503
|
- All N briefs share the same `version`, `source_repo`, `language`, `forge_tier`, `created`, `created_by` values as the parent project
|
|
486
504
|
|
|
487
|
-
**Version detection:** Attempt to auto-detect the source version per the version detection rules in `
|
|
505
|
+
**Version detection:** Attempt to auto-detect the source version per the version detection rules in `{briefSchemaPath}`. Fall back to `1.0.0` if detection fails.
|
|
488
506
|
|
|
489
507
|
**Pin data (from §0b):** When `{pinned_ref}` is non-null, enrich the brief with pin data:
|
|
490
508
|
|
|
@@ -494,7 +512,7 @@ Loop over all N boundaries. For each boundary:
|
|
|
494
512
|
|
|
495
513
|
When `{pinned_ref}` is null (no pin, no releases): leave `target_version` = null, `target_ref` = null — existing version detection applies unchanged.
|
|
496
514
|
|
|
497
|
-
In the docs-only path (
|
|
515
|
+
In the docs-only path (`references/auto-docs-only.md`), `--pin` is ignored (already skipped at §0b). No changes to that path.
|
|
498
516
|
|
|
499
517
|
### 9. Emit Result Envelope
|
|
500
518
|
|
|
@@ -504,22 +522,18 @@ Emit the `SKF_ANALYZE_RESULT_JSON` envelope on stdout:
|
|
|
504
522
|
SKF_ANALYZE_RESULT_JSON: {"status":"success","report_path":"{outputFile_path}","brief_paths":["{brief_path_1}","{brief_path_2}",...,"{brief_path_N}"],"unit_counts":{"confirmed":N,"skipped":0,"maybe":0},"exit_code":0,"halt_reason":null,"mode":"auto"}
|
|
505
523
|
```
|
|
506
524
|
|
|
507
|
-
`brief_paths` contains N paths (one per confirmed unit). `unit_counts.confirmed` is N.
|
|
525
|
+
`brief_paths` contains N paths (one per confirmed unit). `unit_counts.confirmed` is N.
|
|
508
526
|
|
|
509
527
|
If `{coexistence_suffix}` is non-empty (i.e., [A]longside was selected in §0c), include `"coexistence":"alongside"` in the envelope.
|
|
510
528
|
|
|
511
|
-
When `{pinned_ref}` is non-null, include `"pinned_ref":"{pinned_ref}"` and `"pinned_version":"{pinned_version}"` in the envelope. These flow downstream to BS/CS for provenance recording. When `{pinned_ref}` is null, omit these fields
|
|
529
|
+
When `{pinned_ref}` is non-null, include `"pinned_ref":"{pinned_ref}"` and `"pinned_version":"{pinned_version}"` in the envelope. These flow downstream to BS/CS for provenance recording. When `{pinned_ref}` is null, omit these fields.
|
|
512
530
|
|
|
513
531
|
### 10. Write Result Contract
|
|
514
532
|
|
|
515
|
-
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.
|
|
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`.
|
|
516
534
|
|
|
517
535
|
If `{onCompleteCommand}` is non-empty, invoke it now with `--result-path={result_json_path}`.
|
|
518
536
|
|
|
519
537
|
### 11. Chain to Health Check
|
|
520
538
|
|
|
521
539
|
Load, read fully, then execute {nextStepFile} to run the shared workflow health check.
|
|
522
|
-
|
|
523
|
-
## CRITICAL STEP COMPLETION NOTE
|
|
524
|
-
|
|
525
|
-
ONLY WHEN the analysis report has been updated, the skill-brief.yaml written and validated, the result envelope emitted, and the result contract saved will you load and read fully {nextStepFile} to begin the health check.
|