bmad-module-skill-forge 1.4.0 → 1.5.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 +0 -8
- package/docs/_data/pinned.yaml +1 -1
- package/docs/_internal/STABILITY.md +1 -1
- package/docs/architecture.md +2 -2
- package/docs/examples.md +1 -1
- package/docs/workflows.md +5 -5
- package/package.json +2 -2
- package/src/README.md +1 -1
- package/src/knowledge/ccc-bridge.md +12 -12
- package/src/knowledge/qmd-registry.md +5 -5
- package/src/shared/health-check.md +5 -5
- package/src/shared/references/description-guard-protocol.md +100 -0
- package/src/shared/references/output-contract-schema.md +1 -1
- package/src/shared/scripts/schemas/skf-brief-result-envelope.v1.json +6 -6
- package/src/shared/scripts/schemas/skf-setup-result-envelope.v1.json +9 -3
- package/src/shared/scripts/schemas/skf-update-result-envelope.v1.json +149 -0
- package/src/shared/scripts/schemas/skill-brief.v1.json +22 -2
- package/src/shared/scripts/skf-build-change-manifest.py +420 -0
- package/src/shared/scripts/skf-check-workspace-drift.py +321 -0
- package/src/shared/scripts/skf-compare-file-hashes.py +357 -0
- package/src/shared/scripts/skf-description-guard.py +359 -0
- package/src/shared/scripts/skf-detect-language.py +2 -2
- package/src/shared/scripts/skf-detect-scripts-assets.py +613 -0
- package/src/shared/scripts/skf-detect-tools.py +97 -8
- package/src/shared/scripts/skf-detect-workspaces.py +1 -1
- package/src/shared/scripts/skf-disqualify-candidates.py +576 -0
- package/src/shared/scripts/skf-emit-brief-result-envelope.py +3 -3
- package/src/shared/scripts/skf-emit-result-envelope.py +93 -9
- package/src/shared/scripts/skf-enumerate-stack-skills.py +514 -0
- package/src/shared/scripts/skf-forge-tier-rw.py +11 -11
- package/src/shared/scripts/skf-hash-content.py +284 -0
- package/src/shared/scripts/skf-load-provenance.py +295 -0
- package/src/shared/scripts/skf-merge-ccc-exclusions.py +45 -1
- package/src/shared/scripts/skf-pair-intersect.py +250 -0
- package/src/shared/scripts/skf-provenance-gap-dispatch.py +433 -0
- package/src/shared/scripts/skf-qmd-classify-collections.py +42 -8
- package/src/shared/scripts/skf-recommend-scope-type.py +2 -2
- package/src/shared/scripts/skf-render-quick-metadata.py +2 -2
- package/src/shared/scripts/skf-resolve-authoritative-files.py +504 -0
- package/src/shared/scripts/skf-scan-manifests.py +738 -0
- package/src/shared/scripts/skf-scan-skill-md-structure.py +375 -0
- package/src/shared/scripts/skf-update-active-symlink.py +315 -0
- package/src/shared/scripts/skf-validate-brief-inputs.py +5 -5
- package/src/shared/scripts/skf-validate-brief-schema.py +348 -0
- package/src/shared/scripts/skf-write-skill-brief.py +61 -5
- package/src/skf-analyze-source/SKILL.md +69 -16
- package/src/skf-analyze-source/assets/skill-brief-schema.md +1 -1
- package/src/skf-analyze-source/customize.toml +55 -0
- package/src/skf-analyze-source/{steps-c/step-01b-continue.md → references/continue.md} +12 -12
- package/src/skf-analyze-source/{steps-c/step-06-generate-briefs.md → references/generate-briefs.md} +29 -10
- package/src/skf-analyze-source/{steps-c/step-07-health-check.md → references/health-check.md} +5 -3
- package/src/skf-analyze-source/{steps-c/step-03-identify-units.md → references/identify-units.md} +44 -14
- package/src/skf-analyze-source/{steps-c/step-01-init.md → references/init.md} +27 -6
- package/src/skf-analyze-source/{steps-c/step-04-map-and-detect.md → references/map-and-detect.md} +52 -25
- package/src/skf-analyze-source/{steps-c/step-05-recommend.md → references/recommend.md} +15 -8
- package/src/skf-analyze-source/{steps-c/step-02-scan-project.md → references/scan-project.md} +12 -10
- package/src/skf-analyze-source/references/unit-detection-heuristics.md +2 -0
- package/src/skf-analyze-source/templates/analysis-report-template.md +6 -6
- package/src/skf-audit-skill/SKILL.md +66 -15
- package/src/skf-audit-skill/assets/drift-report-template.md +6 -5
- package/src/skf-audit-skill/customize.toml +49 -0
- package/src/skf-audit-skill/{steps-c/step-07-health-check.md → references/health-check.md} +5 -3
- package/src/skf-audit-skill/references/init.md +305 -0
- package/src/skf-audit-skill/{steps-c/step-02-re-index.md → references/re-index.md} +11 -11
- package/src/skf-audit-skill/{steps-c/step-06-report.md → references/report.md} +25 -7
- package/src/skf-audit-skill/{steps-c/step-04-semantic-diff.md → references/semantic-diff.md} +6 -6
- package/src/skf-audit-skill/{steps-c/step-05-severity-classify.md → references/severity-classify.md} +5 -5
- package/src/skf-audit-skill/references/severity-rules.md +2 -0
- package/src/skf-audit-skill/{steps-c/step-03-structural-diff.md → references/structural-diff.md} +29 -16
- package/src/skf-brief-skill/SKILL.md +48 -18
- package/src/skf-brief-skill/assets/description-voice-examples.md +1 -1
- package/src/skf-brief-skill/assets/skill-brief-schema.md +32 -2
- package/src/skf-brief-skill/customize.toml +44 -0
- package/src/skf-brief-skill/{steps-c/step-02-analyze-target.md → references/analyze-target.md} +5 -5
- package/src/skf-brief-skill/{steps-c/step-04-confirm-brief.md → references/confirm-brief.md} +13 -10
- package/src/skf-brief-skill/references/draft-checkpoint.md +4 -4
- package/src/skf-brief-skill/{steps-c/step-01-gather-intent.md → references/gather-intent.md} +24 -24
- package/src/skf-brief-skill/references/headless-args.md +9 -9
- package/src/skf-brief-skill/references/headless-source-authority-detection.md +1 -1
- package/src/skf-brief-skill/{steps-c/step-06-health-check.md → references/health-check.md} +4 -2
- package/src/skf-brief-skill/references/portfolio-similarity-check.md +2 -2
- package/src/skf-brief-skill/references/qmd-collection-registration.md +1 -1
- package/src/skf-brief-skill/{steps-c/step-03-scope-definition.md → references/scope-definition.md} +19 -16
- package/src/skf-brief-skill/references/version-resolution.md +2 -2
- package/src/skf-brief-skill/{steps-c/step-05-write-brief.md → references/write-brief.md} +11 -10
- package/src/skf-create-skill/SKILL.md +25 -19
- package/src/skf-create-skill/assets/compile-assembly-rules.md +3 -3
- package/src/skf-create-skill/assets/skill-sections.md +5 -5
- package/src/skf-create-skill/assets/tessl-dismissal-rules.md +11 -11
- package/src/skf-create-skill/customize.toml +35 -0
- package/src/skf-create-skill/references/authoritative-files-protocol.md +142 -0
- package/src/skf-create-skill/{steps-c/step-05-compile.md → references/compile.md} +16 -16
- package/src/skf-create-skill/{steps-c/step-03d-component-extraction.md → references/component-extraction.md} +16 -16
- package/src/skf-create-skill/{steps-c/step-02-ecosystem-check.md → references/ecosystem-check.md} +4 -4
- package/src/skf-create-skill/{steps-c/step-04-enrich.md → references/enrich.md} +3 -3
- package/src/skf-create-skill/{steps-c/step-03-extract.md → references/extract.md} +52 -120
- package/src/skf-create-skill/references/extraction-patterns.md +8 -8
- package/src/skf-create-skill/{steps-c/step-07-generate-artifacts.md → references/generate-artifacts.md} +17 -9
- package/src/skf-create-skill/{steps-c/step-09-health-check.md → references/health-check.md} +4 -2
- package/src/skf-create-skill/{steps-c/step-01-load-brief.md → references/load-brief.md} +40 -32
- package/src/skf-create-skill/{steps-c/step-08-report.md → references/report.md} +8 -8
- package/src/skf-create-skill/references/source-resolution-protocols.md +6 -6
- package/src/skf-create-skill/{steps-c/sub/step-02b-ccc-discover.md → references/sub/ccc-discover.md} +5 -5
- package/src/skf-create-skill/{steps-c/sub/step-03c-fetch-docs.md → references/sub/fetch-docs.md} +28 -12
- package/src/skf-create-skill/{steps-c/sub/step-03b-fetch-temporal.md → references/sub/fetch-temporal.md} +81 -50
- package/src/skf-create-skill/{steps-c/step-06-validate.md → references/validate.md} +46 -26
- package/src/skf-create-stack-skill/SKILL.md +76 -17
- package/src/skf-create-stack-skill/assets/provenance-map-schema.md +102 -0
- package/src/skf-create-stack-skill/assets/stack-skill-template.md +1 -1
- package/src/skf-create-stack-skill/customize.toml +46 -0
- package/src/skf-create-stack-skill/{steps-c/step-06-compile-stack.md → references/compile-stack.md} +6 -5
- package/src/skf-create-stack-skill/references/compose-mode-rules.md +3 -1
- package/src/skf-create-stack-skill/{steps-c/step-05-detect-integrations.md → references/detect-integrations.md} +32 -11
- package/src/skf-create-stack-skill/{steps-c/step-02-detect-manifests.md → references/detect-manifests.md} +28 -28
- package/src/skf-create-stack-skill/{steps-c/step-07-generate-output.md → references/generate-output.md} +11 -85
- package/src/skf-create-stack-skill/{steps-c/step-10-health-check.md → references/health-check.md} +4 -2
- package/src/skf-create-stack-skill/{steps-c/step-01-init.md → references/init.md} +70 -4
- package/src/skf-create-stack-skill/references/integration-patterns.md +2 -0
- package/src/skf-create-stack-skill/references/manifest-patterns.md +2 -0
- package/src/skf-create-stack-skill/{steps-c/step-04-parallel-extract.md → references/parallel-extract.md} +40 -17
- package/src/skf-create-stack-skill/{steps-c/step-03-rank-and-confirm.md → references/rank-and-confirm.md} +5 -4
- package/src/skf-create-stack-skill/{steps-c/step-09-report.md → references/report.md} +3 -3
- package/src/skf-create-stack-skill/{steps-c/step-08-validate.md → references/validate.md} +6 -6
- package/src/skf-drop-skill/SKILL.md +84 -12
- package/src/skf-drop-skill/customize.toml +57 -0
- package/src/skf-drop-skill/{steps-c/step-02-execute.md → references/execute.md} +49 -34
- package/src/skf-drop-skill/{steps-c/step-04-health-check.md → references/health-check.md} +6 -4
- package/src/skf-drop-skill/{steps-c/step-03-report.md → references/report.md} +13 -5
- package/src/skf-drop-skill/{steps-c/step-01-select.md → references/select.md} +63 -15
- package/src/skf-export-skill/SKILL.md +85 -16
- package/src/skf-export-skill/assets/managed-section-format.md +3 -3
- package/src/skf-export-skill/assets/snippet-format.md +2 -2
- package/src/skf-export-skill/customize.toml +49 -0
- package/src/skf-export-skill/{steps-c/step-03-generate-snippet.md → references/generate-snippet.md} +18 -12
- package/src/skf-export-skill/{steps-c/step-07-health-check.md → references/health-check.md} +6 -4
- package/src/skf-export-skill/{steps-c/step-01-load-skill.md → references/load-skill.md} +21 -51
- package/src/skf-export-skill/references/manifest-rebuild.md +68 -0
- package/src/skf-export-skill/references/multi-skill-mode.md +38 -0
- package/src/skf-export-skill/references/orphan-context-detection.md +75 -0
- package/src/skf-export-skill/references/orphan-row-detection.md +102 -0
- package/src/skf-export-skill/{steps-c/step-02-package.md → references/package.md} +7 -7
- package/src/skf-export-skill/references/preflight-snippet-root-probe.md +74 -0
- package/src/skf-export-skill/{steps-c/step-06-summary.md → references/summary.md} +30 -6
- package/src/skf-export-skill/{steps-c/step-05-token-report.md → references/token-report.md} +5 -5
- package/src/skf-export-skill/{steps-c/step-04-update-context.md → references/update-context.md} +116 -91
- package/src/skf-quick-skill/SKILL.md +63 -123
- package/src/skf-quick-skill/customize.toml +44 -0
- package/src/skf-quick-skill/references/batch-mode.md +102 -0
- package/src/skf-quick-skill/{steps-c/step-04-compile.md → references/compile.md} +10 -10
- package/src/skf-quick-skill/{steps-c/step-02-ecosystem-check.md → references/ecosystem-check.md} +5 -5
- package/src/skf-quick-skill/{steps-c/step-06-finalize.md → references/finalize.md} +7 -7
- package/src/skf-quick-skill/{steps-c/step-07-health-check.md → references/health-check.md} +3 -3
- package/src/skf-quick-skill/{steps-c/step-03-quick-extract.md → references/quick-extract.md} +5 -5
- package/src/skf-quick-skill/references/registry-resolution.md +2 -0
- package/src/skf-quick-skill/{steps-c/step-01-resolve-target.md → references/resolve-target.md} +14 -10
- package/src/skf-quick-skill/{steps-c/step-05-write-and-validate.md → references/write-and-validate.md} +4 -4
- package/src/skf-refine-architecture/SKILL.md +86 -16
- package/src/skf-refine-architecture/customize.toml +49 -0
- package/src/skf-refine-architecture/{steps-c/step-05-compile.md → references/compile.md} +4 -4
- package/src/skf-refine-architecture/{steps-c/step-02-gap-analysis.md → references/gap-analysis.md} +5 -5
- package/src/skf-refine-architecture/{steps-c/step-07-health-check.md → references/health-check.md} +6 -4
- package/src/skf-refine-architecture/{steps-c/step-04-improvements.md → references/improvements.md} +5 -5
- package/src/skf-refine-architecture/references/init.md +144 -0
- package/src/skf-refine-architecture/{steps-c/step-03-issue-detection.md → references/issue-detection.md} +5 -5
- package/src/skf-refine-architecture/references/refinement-rules.md +2 -0
- package/src/skf-refine-architecture/{steps-c/step-06-report.md → references/report.md} +14 -5
- package/src/skf-rename-skill/SKILL.md +82 -12
- package/src/skf-rename-skill/customize.toml +52 -0
- package/src/skf-rename-skill/{steps-c/step-02-execute.md → references/execute.md} +94 -99
- package/src/skf-rename-skill/references/health-check.md +30 -0
- package/src/skf-rename-skill/references/rebuild-context.md +110 -0
- package/src/skf-rename-skill/{steps-c/step-03-report.md → references/report.md} +13 -5
- package/src/skf-rename-skill/{steps-c/step-01-select.md → references/select.md} +59 -20
- package/src/skf-setup/SKILL.md +35 -35
- package/src/skf-setup/customize.toml +33 -0
- package/src/skf-setup/{steps-c/step-03-auto-index.md → references/auto-index.md} +10 -18
- package/src/skf-setup/{steps-c/step-01b-ccc-index.md → references/ccc-index.md} +14 -22
- package/src/skf-setup/{steps-c/step-01-detect-and-tier.md → references/detect-and-tier.md} +23 -10
- package/src/skf-setup/{steps-c/step-05-health-check.md → references/health-check.md} +1 -1
- package/src/skf-setup/{steps-c/step-04-report.md → references/report.md} +21 -19
- package/src/skf-setup/references/tier-rules.md +1 -1
- package/src/skf-setup/{steps-c/step-02-write-config.md → references/write-config.md} +14 -13
- package/src/skf-test-skill/SKILL.md +77 -15
- package/src/skf-test-skill/customize.toml +54 -0
- package/src/skf-test-skill/{steps-c/step-04-coherence-check.md → references/coherence-check.md} +28 -71
- package/src/skf-test-skill/{steps-c/step-03-coverage-check.md → references/coverage-check.md} +16 -39
- package/src/skf-test-skill/{steps-c/step-02-detect-mode.md → references/detect-mode.md} +4 -26
- package/src/skf-test-skill/{steps-c/step-04b-external-validators.md → references/external-validators.md} +6 -28
- package/src/skf-test-skill/references/health-check.md +14 -0
- package/src/skf-test-skill/{steps-c/step-01-init.md → references/init.md} +21 -37
- package/src/skf-test-skill/references/migration-section-rules.md +4 -2
- package/src/skf-test-skill/{steps-c/step-06-report.md → references/report.md} +41 -51
- package/src/skf-test-skill/{steps-c/step-05-score.md → references/score.md} +26 -43
- package/src/skf-test-skill/references/scoring-rules.md +8 -6
- package/src/skf-test-skill/references/source-access-protocol.md +7 -5
- package/src/skf-test-skill/scripts/compute-score.py +88 -17
- package/src/skf-test-skill/templates/test-report-template.md +13 -13
- package/src/skf-update-skill/SKILL.md +23 -14
- package/src/skf-update-skill/customize.toml +44 -0
- package/src/skf-update-skill/{steps-c/step-02-detect-changes.md → references/detect-changes.md} +176 -68
- package/src/skf-update-skill/references/health-check.md +32 -0
- package/src/skf-update-skill/{steps-c/step-01-init.md → references/init.md} +56 -8
- package/src/skf-update-skill/references/manual-section-rules.md +4 -0
- package/src/skf-update-skill/references/merge-conflict-rules.md +4 -0
- package/src/skf-update-skill/{steps-c/step-04-merge.md → references/merge.md} +8 -8
- package/src/skf-update-skill/{steps-c/step-03-re-extract.md → references/re-extract.md} +53 -44
- package/src/skf-update-skill/references/remote-source-resolution.md +5 -1
- package/src/skf-update-skill/references/report.md +214 -0
- package/src/skf-update-skill/{steps-c/step-05-validate.md → references/validate.md} +5 -5
- package/src/skf-update-skill/{steps-c/step-06-write.md → references/write.md} +73 -51
- package/src/skf-verify-stack/SKILL.md +89 -17
- package/src/skf-verify-stack/assets/feasibility-report-template.md +4 -4
- package/src/skf-verify-stack/customize.toml +50 -0
- package/src/skf-verify-stack/references/coverage-patterns.md +2 -2
- package/src/skf-verify-stack/{steps-c/step-02-coverage.md → references/coverage.md} +7 -7
- package/src/skf-verify-stack/{steps-c/step-07-health-check.md → references/health-check.md} +7 -5
- package/src/skf-verify-stack/references/init.md +166 -0
- package/src/skf-verify-stack/references/integration-verification-rules.md +1 -1
- package/src/skf-verify-stack/{steps-c/step-03-integrations.md → references/integrations.md} +12 -12
- package/src/skf-verify-stack/{steps-c/step-06-report.md → references/report.md} +22 -41
- package/src/skf-verify-stack/{steps-c/step-04-requirements.md → references/requirements.md} +7 -7
- package/src/skf-verify-stack/{steps-c/step-05-synthesize.md → references/synthesize.md} +8 -8
- package/tools/validate-docs-drift.js +1 -1
- package/src/skf-audit-skill/steps-c/step-01-init.md +0 -221
- package/src/skf-refine-architecture/steps-c/step-01-init.md +0 -136
- package/src/skf-rename-skill/steps-c/step-04-health-check.md +0 -22
- package/src/skf-test-skill/steps-c/step-07-health-check.md +0 -25
- package/src/skf-update-skill/steps-c/step-07-report.md +0 -148
- package/src/skf-update-skill/steps-c/step-08-health-check.md +0 -22
- package/src/skf-verify-stack/steps-c/step-01-init.md +0 -178
|
@@ -43,9 +43,9 @@ When `brief.target_version` is absent but `brief.version` is present AND `source
|
|
|
43
43
|
3. **Resolution outcomes:**
|
|
44
44
|
- **Single match:** Store the matched tag as `source_ref`. Use it as `{branch}` in all subsequent clone/API commands. Do not warn — this is the expected path.
|
|
45
45
|
- **Multiple matches:** Present the matching tags to the user — "Multiple tags match `brief.version` ({brief.version}): {list}. Which one should I use, or fall back to HEAD?" Wait for selection.
|
|
46
|
-
- **Zero matches:** ⚠️ Warn: "No git tag found matching `brief.version` ({brief.version}). Falling back to default branch — **extracted code may not match the declared version.** If you intended to pin a specific version, set `target_version` explicitly in the brief." Set `source_ref` to `HEAD` and proceed with default branch. Append `tag_resolution: {status: "fallback-head", requested: "{brief.version}", reason: "no-matching-tag"}` to the in-context evidence-report payload so step
|
|
46
|
+
- **Zero matches:** ⚠️ Warn: "No git tag found matching `brief.version` ({brief.version}). Falling back to default branch — **extracted code may not match the declared version.** If you intended to pin a specific version, set `target_version` explicitly in the brief." Set `source_ref` to `HEAD` and proceed with default branch. Append `tag_resolution: {status: "fallback-head", requested: "{brief.version}", reason: "no-matching-tag"}` to the in-context evidence-report payload so step 5 §7 surfaces the fallback in the evidence report. This turns the warning into a persistent audit trail a reviewer can grep later, not just a one-shot stderr line.
|
|
47
47
|
|
|
48
|
-
4. **Do not halt on zero matches.** Unlike the explicit path, implicit resolution never blocks compilation — `brief.version` is an auto-populated hint, and some repositories simply do not tag releases. The warning is sufficient notice; the evidence report in step
|
|
48
|
+
4. **Do not halt on zero matches.** Unlike the explicit path, implicit resolution never blocks compilation — `brief.version` is an auto-populated hint, and some repositories simply do not tag releases. The warning is sufficient notice; the evidence report in step 8 will surface the HEAD fallback for reviewers.
|
|
49
49
|
|
|
50
50
|
5. **Store `source_ref`** in context exactly as in the explicit path. It flows through to metadata.json and provenance-map.json so downstream workflows (update-skill, audit-skill) can re-clone from the same ref.
|
|
51
51
|
|
|
@@ -65,7 +65,7 @@ Implicit resolution via `brief.version` is **not applied to local sources** —
|
|
|
65
65
|
|
|
66
66
|
## Remote Source Resolution
|
|
67
67
|
|
|
68
|
-
**Note:** Quick-tier remote sources do not use the workspace/clone protocol described below. Quick tier accesses remote files via the `gh_bridge.read_file` path described in step
|
|
68
|
+
**Note:** Quick-tier remote sources do not use the workspace/clone protocol described below. Quick tier accesses remote files via the `gh_bridge.read_file` path described in step 3 section 4.
|
|
69
69
|
|
|
70
70
|
If `source_repo` is a local path: proceed with the tier-appropriate strategy as normal.
|
|
71
71
|
|
|
@@ -84,7 +84,7 @@ If `source_repo` is a remote URL (GitHub URL or owner/repo format) AND tier is F
|
|
|
84
84
|
|
|
85
85
|
3. **Workspace check — resolve the source locally:**
|
|
86
86
|
|
|
87
|
-
**Concurrency guard:** all of the operations below (fetch, checkout, rev-parse, and the extraction read that follows in step
|
|
87
|
+
**Concurrency guard:** all of the operations below (fetch, checkout, rev-parse, and the extraction read that follows in step 3) must be wrapped in an exclusive `flock` on `{workspace_repo_path}/.skf-workspace.lock`. Acquire the lock before the workspace-hit check, hold it across fetch + checkout + rev-parse, AND keep holding it through the extraction-time read of the working tree. Two concurrent batch runs that target the same workspace clone but different `source_ref` values would otherwise race — one would `checkout` while the other was reading files mid-extraction, corrupting the inventory. The lock makes the per-workspace-repo unit of work serial. Use `flock -x {lockfile} -c "..."` or `fcntl.flock(LOCK_EX)`. If `flock` is unavailable, log a warning ("Concurrency guard unavailable — concurrent forges against the same workspace repo may produce inconsistent extraction inventories") and proceed.
|
|
88
88
|
|
|
89
89
|
**If `{workspace_repo_path}/.git/` exists (workspace hit):**
|
|
90
90
|
|
|
@@ -217,7 +217,7 @@ Also store `source_ref` in context (from tag resolution above, or `HEAD` if no t
|
|
|
217
217
|
|
|
218
218
|
**If `source_type: "docs-only"`:** skip this section — no source files exist to reconcile.
|
|
219
219
|
|
|
220
|
-
After the source path is accessible (local path from step
|
|
220
|
+
After the source path is accessible (local path from step 1, or workspace/ephemeral clone from above), check whether the source contains a version identifier and reconcile it with `brief.version`. Look for the first matching version file in the resolved source path:
|
|
221
221
|
|
|
222
222
|
- Python: `pyproject.toml` (`[project] version`), `setup.py` (`version=`), `__version__` in `__init__.py`
|
|
223
223
|
- JavaScript/TypeScript: `package.json` (`"version"`). **Monorepo resolution:** When multiple `package.json` files exist (workspace root + packages), resolve version using this priority:
|
|
@@ -232,7 +232,7 @@ After the source path is accessible (local path from step-01, or workspace/ephem
|
|
|
232
232
|
|
|
233
233
|
⚠️ Warn the user: "Brief version ({brief.version}) differs from source version ({source_version}). Using source version ({source_version})."
|
|
234
234
|
|
|
235
|
-
Update the working version in context to the source version. Record the mismatch in context for the evidence report (step
|
|
235
|
+
Update the working version in context to the source version. Record the mismatch in context for the evidence report (step 8).
|
|
236
236
|
|
|
237
237
|
**If no version file is found or version cannot be extracted:** keep `brief.version` as-is. No warning needed.
|
|
238
238
|
|
package/src/skf-create-skill/{steps-c/sub/step-02b-ccc-discover.md → references/sub/ccc-discover.md}
RENAMED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
---
|
|
2
|
-
nextStepFile: '../
|
|
2
|
+
nextStepFile: '../extract.md'
|
|
3
3
|
---
|
|
4
4
|
|
|
5
|
+
<!-- Config: communicate in {communication_language}. -->
|
|
6
|
+
|
|
5
7
|
# Step 2b: CCC Semantic Discovery
|
|
6
8
|
|
|
7
9
|
## STEP GOAL:
|
|
8
10
|
|
|
9
|
-
If tier is Forge+ or Deep AND ccc is available, perform a semantic discovery pass over the source code to identify the most relevant files for the skill being created. Store ranked discovery results in context to pre-rank the file extraction queue in step
|
|
11
|
+
If tier is Forge+ or Deep AND ccc is available, perform a semantic discovery pass over the source code to identify the most relevant files for the skill being created. Store ranked discovery results in context to pre-rank the file extraction queue in step 3.
|
|
10
12
|
|
|
11
13
|
For Quick and Forge tiers, or when ccc is unavailable, skip silently and proceed.
|
|
12
14
|
|
|
@@ -18,8 +20,6 @@ For Quick and Forge tiers, or when ccc is unavailable, skip silently and proceed
|
|
|
18
20
|
|
|
19
21
|
## MANDATORY SEQUENCE
|
|
20
22
|
|
|
21
|
-
**CRITICAL:** Follow this sequence exactly. Do not skip, reorder, or improvise.
|
|
22
|
-
|
|
23
23
|
### 1. Check Tier Eligibility
|
|
24
24
|
|
|
25
25
|
**If tier is Quick or Forge:**
|
|
@@ -32,7 +32,7 @@ Check `tools.ccc` from forge-tier.yaml. If `tools.ccc` is false, set `{ccc_disco
|
|
|
32
32
|
|
|
33
33
|
If `tools.ccc` is true, check the remote source guard **before** proceeding to section 2:
|
|
34
34
|
|
|
35
|
-
**Remote source guard:** If `source_root` is a remote URL (GitHub repository — workspace clone or ephemeral clone happens in step
|
|
35
|
+
**Remote source guard:** If `source_root` is a remote URL (GitHub repository — workspace clone or ephemeral clone happens in step 3), CCC cannot operate yet. Set `{ccc_discovery: []}` and display: "CCC discovery deferred — remote source will be indexed after clone in step 3." Auto-proceed to section 5 (step completion). Step-03 will detect the deferred scenario and run CCC discovery on the resolved clone (workspace or ephemeral) before AST extraction begins.
|
|
36
36
|
|
|
37
37
|
If `source_root` is a local path, continue to section 2.
|
|
38
38
|
|
package/src/skf-create-skill/{steps-c/sub/step-03c-fetch-docs.md → references/sub/fetch-docs.md}
RENAMED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
nextStepFile: '../
|
|
2
|
+
nextStepFile: '../enrich.md'
|
|
3
3
|
# Resolve `{atomicWriteHelper}` by probing `{atomicWriteProbeOrder}` in order
|
|
4
4
|
# (installed SKF module path first, src/ dev-checkout fallback); first existing
|
|
5
5
|
# path wins. HALT if neither resolves.
|
|
@@ -8,6 +8,8 @@ atomicWriteProbeOrder:
|
|
|
8
8
|
- '{project-root}/src/shared/scripts/skf-atomic-write.py'
|
|
9
9
|
---
|
|
10
10
|
|
|
11
|
+
<!-- Config: communicate in {communication_language}. -->
|
|
12
|
+
|
|
11
13
|
# Step 3c: Fetch Remote Documentation
|
|
12
14
|
|
|
13
15
|
## STEP GOAL:
|
|
@@ -23,13 +25,11 @@ Fetch remote documentation from brief-specified URLs using whatever web fetching
|
|
|
23
25
|
|
|
24
26
|
## MANDATORY SEQUENCE
|
|
25
27
|
|
|
26
|
-
**CRITICAL:** Follow this sequence exactly. Do not skip, reorder, or improvise.
|
|
27
|
-
|
|
28
28
|
### 1. Check Eligibility
|
|
29
29
|
|
|
30
30
|
Evaluate the following conditions. **If the condition fails, skip silently to section 7 (auto-proceed) with no output:**
|
|
31
31
|
|
|
32
|
-
1. **`doc_urls` is present in the brief data:** Check that `doc_urls` contains at least one URL entry from step
|
|
32
|
+
1. **`doc_urls` is present in the brief data:** Check that `doc_urls` contains at least one URL entry from step 1 context. If `doc_urls` is absent or empty, skip silently.
|
|
33
33
|
|
|
34
34
|
No tier gate — if `doc_urls` are present, this step runs at Quick, Forge, and Deep tiers alike.
|
|
35
35
|
|
|
@@ -84,12 +84,28 @@ If neither trigger fires, keep the page content as-is and do NOT trigger subpage
|
|
|
84
84
|
|
|
85
85
|
2. **Filter discovered URLs by relevance and origin:** Restrict candidates to the same **registrable domain** as the root URL — strip the URL down to its eTLD+1 (e.g., for root `https://docs.example.com/intro`, accept any subdomain of `example.com` such as `api.example.com` or `docs.example.com`, but reject `example.org` or `cdn.partner.io`). Cross-origin links must be discarded before any fetch. The same-registrable-domain rule prevents Mintlify/Docusaurus link clouds from pulling in tracking pixels, doc-site CDNs, or third-party embeds as if they were canonical docs. From the surviving same-domain candidates, select the most relevant pages by searching for API-related terms in the URL path or title (e.g., `api`, `reference`, `quickstart`, `setup`, `config`, `getting-started`, `guide`, `sdk`, `methods`, `functions`). Exclude pages that are clearly non-API content (e.g., `blog`, `changelog`, `pricing`, `about`, `careers`).
|
|
86
86
|
|
|
87
|
-
3. **Fetch top subpages:** Fetch up to **10** of the most relevant subpages.
|
|
87
|
+
3. **Fetch top subpages (in parallel):** Fetch up to **10** of the most relevant subpages **concurrently** — subpage fetches are independent and network-bound, so wall-clock benefits substantially from parallel execution. Bound concurrency to **4 in flight** at a time to stay polite to documentation hosts (Mintlify/Docusaurus typically allow more, but conservatism here protects against unrecognized rate limits).
|
|
88
|
+
|
|
89
|
+
The parallel pattern depends on the fetch tool:
|
|
90
|
+
|
|
91
|
+
- **LLM-driven tools** (Firecrawl `firecrawl_scrape`, `WebFetch`, MCP fetch, browser tools): issue up to 4 tool calls **in a single message**. The agent runtime executes parallel tool calls concurrently; collect results from the batch before issuing the next set of up to 4. Repeat until all up-to-10 subpages have been attempted or rate limiting halts the batch.
|
|
92
|
+
- **Bash-driven tools** (`curl`, `wget`): use `xargs -P 4 -n 1` to fan out from a newline-separated subpage list. Example:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
printf '%s\n' "${subpages[@]}" | xargs -P 4 -n 1 -I {} bash -c '
|
|
96
|
+
url="{}"
|
|
97
|
+
safe=$(echo -n "$url" | sha256sum | cut -c1-12)
|
|
98
|
+
curl -sSL --max-time 30 "$url" > "{staging}/subpage-$safe.md" \
|
|
99
|
+
|| echo "fetch failed: $url" > "{staging}/subpage-$safe.md"
|
|
100
|
+
'
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
For each subpage (regardless of tool):
|
|
88
104
|
- Use the same web fetching tool as the root URL
|
|
89
105
|
- Store with the subpage URL as provenance: `[EXT:{subpage-url}]`
|
|
90
|
-
- If a subpage fetch fails, skip it and continue
|
|
106
|
+
- If a subpage fetch fails, skip it and continue with the rest of the batch — do not halt the whole stage
|
|
91
107
|
|
|
92
|
-
4. **Rate limiting:** If rate limiting (HTTP 429) is encountered during subpage fetching, stop discovery for this root URL. Keep results collected so far. Log: "Subpage discovery stopped due to rate limiting."
|
|
108
|
+
4. **Rate limiting:** If rate limiting (HTTP 429) is encountered during subpage fetching, stop discovery for this root URL. Keep results collected so far. Log: "Subpage discovery stopped due to rate limiting." For the parallel-tool-call pattern, drop any not-yet-issued tool calls from subsequent batches; for the `xargs` pattern, interrupt the pipeline (set `--max-procs 0` is **not** a graceful stop — the simplest stop is to kill the xargs PID and let in-flight writers complete naturally).
|
|
93
109
|
|
|
94
110
|
**If ALL URLs fail (including any subpage fetches):** Log warning: "No documentation could be fetched. Proceeding without T3 content." Skip to section 7 (auto-proceed).
|
|
95
111
|
|
|
@@ -110,8 +126,8 @@ Parse the successfully fetched markdown for:
|
|
|
110
126
|
|
|
111
127
|
**Mode determines merge behavior:**
|
|
112
128
|
|
|
113
|
-
- **`source_type: "docs-only"`** — The doc-fetch inventory IS the extraction inventory. It replaces the empty inventory from step
|
|
114
|
-
- **`source_type: "source"` (supplemental mode)** — Merge T3 items into the existing extraction inventory from step
|
|
129
|
+
- **`source_type: "docs-only"`** — The doc-fetch inventory IS the extraction inventory. It replaces the empty inventory from step 3, since there was no source code to extract from.
|
|
130
|
+
- **`source_type: "source"` (supplemental mode)** — Merge T3 items into the existing extraction inventory from step 3.
|
|
115
131
|
|
|
116
132
|
**Conflict rule:** T3 items NEVER override existing T1, T1-low, or T2 items for the same export. When an export already has a higher-confidence entry, the T3 item is discarded. T3 has the lowest priority.
|
|
117
133
|
|
|
@@ -131,7 +147,7 @@ Parse the successfully fetched markdown for:
|
|
|
131
147
|
1. Write fetched markdown files to a staging directory: `_bmad-output/{skill-name}-docs/`
|
|
132
148
|
2. Index into QMD with atomic replace + rollback: if a `{skill-name}-docs` collection already exists, run `qmd collection remove {skill-name}-docs` first, then `qmd collection add {project-root}/_bmad-output/{skill-name}-docs/ --name {skill-name}-docs --mask "*.md"`. **If `qmd collection add` fails after a successful `remove`:** remove any matching `{skill-name}-docs` entry from `forge-tier.yaml` → `qmd_collections[]` to keep the registry consistent with QMD's actual state, warn in evidence-report, and skip the embed — docs enrichment degrades gracefully.
|
|
133
149
|
3. Generate embeddings scoped to this collection (only if step 2 `add` succeeded): `qmd embed --collection {skill-name}-docs` (required for semantic `type:'vec'` and HyDE `type:'hyde'` sub-queries within the QMD `query` tool). If the installed `qmd` CLI does not accept `--collection`, gate the embed behind a freshness check: skip re-embedding if the existing `{skill-name}-docs` registry entry is within 24 hours, and log the skip in the evidence report to prevent unbounded batch-mode re-embedding.
|
|
134
|
-
4. Register in forge-tier.yaml `qmd_collections` array — **acquire an exclusive `flock` on `{sidecar_path}/forge-tier.yaml.lock` for the read-modify-write** (see the locking pattern documented in step
|
|
150
|
+
4. Register in forge-tier.yaml `qmd_collections` array — **acquire an exclusive `flock` on `{sidecar_path}/forge-tier.yaml.lock` for the read-modify-write** (see the locking pattern documented in step 3b §4). Write via `python3 {atomicWriteHelper} write --target {sidecar_path}/forge-tier.yaml`. If `flock` is unavailable, fall back to read-CAS-by-mtime (capture `st_mtime` before, re-check after; refuse to clobber if a concurrent run wrote in between).
|
|
135
151
|
|
|
136
152
|
```yaml
|
|
137
153
|
- name: "{skill-name}-docs"
|
|
@@ -190,7 +206,7 @@ ONLY WHEN documentation is fetched and T3 items are merged into the extraction i
|
|
|
190
206
|
- Existing T1/T1-low/T2 items never overridden by T3 data
|
|
191
207
|
- Docs-only mode: doc-fetch inventory correctly replaces empty extraction inventory
|
|
192
208
|
- Supplemental mode: T3 items merged into existing inventory respecting conflict rule
|
|
193
|
-
- Auto-proceeded to step
|
|
209
|
+
- Auto-proceeded to step 4
|
|
194
210
|
|
|
195
211
|
### SYSTEM FAILURE:
|
|
196
212
|
|
|
@@ -199,6 +215,6 @@ ONLY WHEN documentation is fetched and T3 items are merged into the extraction i
|
|
|
199
215
|
- Overriding existing higher-confidence extractions (T1, T1-low, T2) with T3 data
|
|
200
216
|
- Hardcoding a specific fetching tool instead of being tool-agnostic
|
|
201
217
|
- Hallucinating API details not found in the fetched content
|
|
202
|
-
- Beginning compilation in this step (that is step
|
|
218
|
+
- Beginning compilation in this step (that is step 5)
|
|
203
219
|
|
|
204
220
|
**Master Rule:** Documentation fetching is best-effort T3 enrichment. Fetch what you can, cite everything as `[EXT:{url}]`, never override higher-confidence data, and move on. Failures degrade gracefully — they never block the skill compilation pipeline.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
nextStepFile: '
|
|
2
|
+
nextStepFile: 'fetch-docs.md'
|
|
3
3
|
# Resolve `{atomicWriteHelper}` by probing `{atomicWriteProbeOrder}` in order
|
|
4
4
|
# (installed SKF module path first, src/ dev-checkout fallback); first existing
|
|
5
5
|
# path wins. HALT if neither resolves.
|
|
@@ -8,23 +8,23 @@ atomicWriteProbeOrder:
|
|
|
8
8
|
- '{project-root}/src/shared/scripts/skf-atomic-write.py'
|
|
9
9
|
---
|
|
10
10
|
|
|
11
|
+
<!-- Config: communicate in {communication_language}. -->
|
|
12
|
+
|
|
11
13
|
# Step 3b: Fetch Temporal Context
|
|
12
14
|
|
|
13
15
|
## STEP GOAL:
|
|
14
16
|
|
|
15
|
-
To fetch temporal context (issues, PRs, changelogs, release notes) from the source repository and index it into a QMD collection for Deep tier enrichment. This ensures step
|
|
17
|
+
To fetch temporal context (issues, PRs, changelogs, release notes) from the source repository and index it into a QMD collection for Deep tier enrichment. This ensures step 4 has historical data to search when annotating extracted functions with T2 provenance.
|
|
16
18
|
|
|
17
19
|
## Rules
|
|
18
20
|
|
|
19
21
|
- Deep tier only — Quick, Forge, and Forge+ tiers skip this step entirely and silently
|
|
20
22
|
- GitHub repositories only — other source types degrade gracefully
|
|
21
23
|
- Do not halt the workflow if fetching or indexing fails
|
|
22
|
-
- Do not modify extraction data from step
|
|
24
|
+
- Do not modify extraction data from step 3 — this step only creates QMD collections
|
|
23
25
|
|
|
24
26
|
## MANDATORY SEQUENCE
|
|
25
27
|
|
|
26
|
-
**CRITICAL:** Follow this sequence exactly. Do not skip, reorder, or improvise.
|
|
27
|
-
|
|
28
28
|
### 1. Check Eligibility
|
|
29
29
|
|
|
30
30
|
Evaluate the following conditions sequentially. **If ANY condition fails, skip silently to section 5 (auto-proceed) with no output:**
|
|
@@ -69,57 +69,65 @@ Create a staging directory: `_bmad-output/{skill-name}-temporal/`
|
|
|
69
69
|
|
|
70
70
|
Resolve the `owner` and `repo` from `source_repo` (e.g., `acme/toolkit` from `https://github.com/acme/toolkit`).
|
|
71
71
|
|
|
72
|
-
Execute the
|
|
73
|
-
|
|
74
|
-
1. **Issues (last 100):** (rationale: 100 is `gh issue list`'s default max-per-page; a single paginated call captures recent activity without extra round trips or rate-limit pressure)
|
|
72
|
+
Execute the four fetches below **in parallel** — they are independent and the network round-trips dominate wall-clock. Background each, then `wait` for the batch. **If any individual fetch fails, log a warning and continue with the others.** The 4-concurrent fan-out is well under GitHub's authenticated REST rate limit (5000/hr); no bounded-concurrency guard is needed for this set.
|
|
75
73
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
74
|
+
```bash
|
|
75
|
+
mkdir -p {staging}
|
|
76
|
+
# 1. Issues (last 100)
|
|
77
|
+
( gh issue list -R {owner}/{repo} --state all --limit 100 \
|
|
78
|
+
--json number,title,state,labels,createdAt,closedAt,body \
|
|
79
|
+
| jq -r '...' > {staging}/issues.md ) &
|
|
80
|
+
# 2. Merged PRs (last 100)
|
|
81
|
+
( gh pr list -R {owner}/{repo} --state merged --limit 100 \
|
|
82
|
+
--json number,title,mergedAt,labels,body \
|
|
83
|
+
| jq -r '...' > {staging}/prs.md ) &
|
|
84
|
+
# 3. Release tags only (the per-tag fetch loop runs sequentially below to
|
|
85
|
+
# preserve append-immediately crash-resume semantics for releases.md)
|
|
86
|
+
( gh release list -R {owner}/{repo} --limit 10 \
|
|
87
|
+
--json tagName,name,publishedAt > {staging}/.release-tags.json ) &
|
|
88
|
+
# 4. Changelog (404 is silent skip — note `set +e` so the subshell doesn't
|
|
89
|
+
# propagate `gh api`'s non-zero exit on missing file)
|
|
90
|
+
( set +e
|
|
91
|
+
gh api repos/{owner}/{repo}/contents/CHANGELOG.md --jq '.content' \
|
|
92
|
+
| base64 -d > {staging}/changelog.md 2>/dev/null
|
|
93
|
+
[ -s {staging}/changelog.md ] || rm -f {staging}/changelog.md ) &
|
|
94
|
+
wait
|
|
95
|
+
```
|
|
79
96
|
|
|
80
|
-
|
|
97
|
+
Per-call rationale:
|
|
81
98
|
|
|
82
|
-
|
|
99
|
+
1. **Issues (last 100):** 100 is `gh issue list`'s default max-per-page; one paginated call captures recent activity without extra round trips or rate-limit pressure. Output → `{staging}/issues.md` formatted as a markdown document with one section per issue.
|
|
83
100
|
|
|
84
|
-
|
|
85
|
-
gh pr list -R {owner}/{repo} --state merged --limit 100 --json number,title,mergedAt,labels,body | ...
|
|
86
|
-
```
|
|
101
|
+
2. **Merged PRs (last 100):** Same 100-per-page convention as issues — captures the most recent merges in one API call. Output → `{staging}/prs.md`.
|
|
87
102
|
|
|
88
|
-
|
|
103
|
+
3. **Release tags + per-release fetches (last 10):** Release notes accumulate slowly relative to issues/PRs; the most recent 10 tags cover roughly the last 6-18 months of changelog-relevant history for typical OSS projects, which is enough context for T2-past annotations without fanning out to dozens of `gh release view` calls.
|
|
89
104
|
|
|
90
|
-
|
|
105
|
+
**Note:** `gh release list --json` does **not** support the `body` field. The parallel block above fetches tags only (Step 1). After `wait`, run **Step 2 sequentially** to preserve the append-immediately crash-resume contract:
|
|
91
106
|
|
|
92
|
-
|
|
107
|
+
If `{staging}/.release-tags.json` is empty (no releases), skip Step 2 and omit the releases section entirely. Otherwise:
|
|
93
108
|
|
|
94
109
|
```bash
|
|
95
|
-
#
|
|
96
|
-
|
|
110
|
+
# Sequential per-tag loop. Iterate the JSON tag array verbatim so a
|
|
111
|
+
# crash mid-loop leaves a partial-but-well-formed releases.md on disk.
|
|
112
|
+
echo "# Releases (partial if interrupted)" > {staging}/releases.md
|
|
113
|
+
ERR_FILE="{staging}/.gh-release-err" # per-run stderr capture inside staging
|
|
114
|
+
jq -r '.[].tagName' {staging}/.release-tags.json | while IFS= read -r tag; do
|
|
115
|
+
if gh release view "$tag" -R {owner}/{repo} \
|
|
116
|
+
--json tagName,name,publishedAt,body 2>"$ERR_FILE"; then
|
|
117
|
+
jq -r '...' >> {staging}/releases.md # one ## {tag} block per release
|
|
118
|
+
else
|
|
119
|
+
echo "## $tag — fetch failed: $(cat "$ERR_FILE")" \
|
|
120
|
+
>> {staging}/releases.md
|
|
121
|
+
fi
|
|
122
|
+
done
|
|
123
|
+
rm -f "$ERR_FILE"
|
|
97
124
|
```
|
|
98
125
|
|
|
99
|
-
|
|
126
|
+
Failed individual fetches get a one-line placeholder; the loop continues with remaining tags. If a rate limit (HTTP 429) is hit, stop the release loop, keep the partial `releases.md` file in place (do NOT delete it), and log: "Release fetch stopped at tag {N}/{total} due to rate limiting — partial releases.md retained."
|
|
100
127
|
|
|
101
|
-
|
|
102
|
-
# Step 2: For EACH tagName from Step 1, fetch the full release
|
|
103
|
-
gh release view {tagName} -R {owner}/{repo} --json tagName,name,publishedAt,body
|
|
104
|
-
```
|
|
128
|
+
**Why sequential here when the rest is parallel:** the append-per-release pattern guarantees that a mid-loop abort (rate limit, network drop, user interrupt) leaves a partial but well-formed `releases.md` with every release fetched so far. Parallel writers appending to the same file would need file locking and per-writer ordering — the simpler sequential loop is robust for free, and 10 release fetches contribute only ~5-10s of the total wall-clock.
|
|
105
129
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
Write an empty `{staging}/releases.md` with a header (`# Releases (partial if interrupted)`) before the loop, then append one `## {tagName} — {name} ({publishedAt})` section per successful fetch. Failed individual fetches get a one-line placeholder: `## {tagName} — fetch failed: {error}`.
|
|
109
|
-
|
|
110
|
-
If `gh release view` fails for a specific tag, log a warning and skip that release — continue with remaining tags. If a rate limit (HTTP 429) is hit, stop the release loop, keep the partial `releases.md` file in place (do NOT delete it), and log: "Release fetch stopped at tag {N}/{total} due to rate limiting — partial releases.md retained."
|
|
111
|
-
|
|
112
|
-
Format each section as a markdown block with tag, name, date, and body.
|
|
113
|
-
|
|
114
|
-
4. **Changelog (if exists):**
|
|
115
|
-
|
|
116
|
-
Check if `CHANGELOG.md` or `RELEASES.md` exists in the repository root:
|
|
117
|
-
|
|
118
|
-
```bash
|
|
119
|
-
gh api repos/{owner}/{repo}/contents/CHANGELOG.md --jq '.content' | base64 -d
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
If found, write to `{staging}/changelog.md`. If not found (404), skip silently.
|
|
130
|
+
4. **Changelog:** `CHANGELOG.md` or `RELEASES.md` at the repository root. The parallel block above writes to `{staging}/changelog.md` only when the file exists; non-existence (404 from `gh api`) leaves no file behind.
|
|
123
131
|
|
|
124
132
|
#### 3b. Targeted Function Searches (Uses Extraction Inventory)
|
|
125
133
|
|
|
@@ -127,19 +135,42 @@ After the generic fetches above, perform **targeted searches** using the top-lev
|
|
|
127
135
|
|
|
128
136
|
**Short-circuit on empty `top_exports`:** If `extraction_inventory.top_exports` is missing or `== []` (docs-only mode, or a source extraction that produced zero public exports), skip this sub-section entirely with a one-line log: "No exports in inventory — skipping targeted function searches." The generic fetches from §3 remain in place and continue to provide baseline temporal context.
|
|
129
137
|
|
|
130
|
-
**Limit:** Search the top **10 function names** maximum to control API call volume and avoid `gh` rate limiting. (rationale: 10 targeted searches + generic fetches from §3 stays well under GitHub's unauthenticated search rate limit of 10 requests/minute and authenticated 30/minute; matches the `top_exports[]` size emitted by step
|
|
138
|
+
**Limit:** Search the top **10 function names** maximum to control API call volume and avoid `gh` rate limiting. (rationale: 10 targeted searches + generic fetches from §3 stays well under GitHub's unauthenticated search rate limit of 10 requests/minute and authenticated 30/minute; matches the `top_exports[]` size emitted by step 3 §5 so every tracked export gets one search.)
|
|
131
139
|
|
|
132
140
|
For each function name in `top_exports[]` (up to 10), **sanitize first**: strip every character that is not in `[A-Za-z0-9_]` from `function_name` to produce `safe_name`. This prevents shell injection and `gh` query parser errors when an export name contains punctuation (e.g., `<T>`, `.method`, `::namespace`, quotes). If `safe_name` is empty after sanitization (the original was entirely punctuation — rare but possible for symbol exports), fall back to piping the original name through stdin via `--query-from-file -`-style indirection if your `gh` version supports it; otherwise skip that one entry with a log line — never substitute the unsanitized name back into the shell command.
|
|
133
141
|
|
|
142
|
+
**Parallel fan-out (bounded concurrency = 5):** sanitized searches are independent and benefit from parallelism. Use `xargs -P 5` so at most 5 `gh search` calls are in flight at once — this stays well under GitHub's authenticated search rate limit (30/min) while saving ~5-8s of wall-clock on a top-10 export list compared to fully sequential.
|
|
143
|
+
|
|
134
144
|
```bash
|
|
135
|
-
#
|
|
136
|
-
#
|
|
137
|
-
#
|
|
138
|
-
|
|
139
|
-
|
|
145
|
+
# 1. Sanitize and write one safe_name per line; skip empties.
|
|
146
|
+
# Python is the canonical sanitizer because awk/sed regex semantics
|
|
147
|
+
# vary across platforms.
|
|
148
|
+
jq -r '.top_exports[]' {extraction_inventory.json} \
|
|
149
|
+
| python3 -c 'import sys,re
|
|
150
|
+
for line in sys.stdin:
|
|
151
|
+
s = re.sub(r"[^A-Za-z0-9_]", "", line.strip())
|
|
152
|
+
if s: print(s)' > {staging}/.safe-names.txt
|
|
153
|
+
|
|
154
|
+
# 2. Fan out to gh search, 5 in flight. Each writer emits a self-contained
|
|
155
|
+
# section to its own per-name file; we concatenate after the wait.
|
|
156
|
+
# --limit 5: top-5 issues per function keeps signal-to-noise high and caps
|
|
157
|
+
# total response size across 10 fan-outs at 50 issues.
|
|
158
|
+
mkdir -p {staging}/targeted
|
|
159
|
+
cat {staging}/.safe-names.txt | xargs -P 5 -I {} bash -c '
|
|
160
|
+
gh search issues --repo {owner}/{repo} "{}" --limit 5 \
|
|
161
|
+
--json number,title,state,body 2>/dev/null \
|
|
162
|
+
| jq -r ". | \"## {}\\n\" + (...)" > {staging}/targeted/{}.md \
|
|
163
|
+
|| echo "## {} — fetch failed" > {staging}/targeted/{}.md
|
|
164
|
+
'
|
|
165
|
+
|
|
166
|
+
# 3. Concatenate in stable order (alpha by safe_name) into the single
|
|
167
|
+
# aggregated file the rest of the workflow expects.
|
|
168
|
+
sort {staging}/.safe-names.txt | while IFS= read -r name; do
|
|
169
|
+
cat "{staging}/targeted/$name.md"
|
|
170
|
+
done > {staging}/targeted-issues.md
|
|
140
171
|
```
|
|
141
172
|
|
|
142
|
-
Aggregate all targeted search results into a single file: `{staging}/targeted-issues.md`.
|
|
173
|
+
Aggregate all targeted search results into a single file: `{staging}/targeted-issues.md`. The per-name temp directory `{staging}/targeted/` can be removed after concat — it exists only to give each parallel writer an isolated output stream.
|
|
143
174
|
|
|
144
175
|
**If `gh search` is unavailable** (older `gh` CLI versions): skip targeted searches silently. The generic fetches from section 3 still provide baseline temporal context.
|
|
145
176
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
nextStepFile: '
|
|
2
|
+
nextStepFile: 'generate-artifacts.md'
|
|
3
3
|
tesslDismissalData: 'assets/tessl-dismissal-rules.md'
|
|
4
|
+
descriptionGuardProtocol: '{project-root}/src/shared/references/description-guard-protocol.md'
|
|
4
5
|
# Resolve `{atomicWriteHelper}` by probing `{atomicWriteProbeOrder}` in order
|
|
5
6
|
# (installed SKF module path first, src/ dev-checkout fallback); first existing
|
|
6
7
|
# path wins. HALT if neither resolves — losing atomic-write guarantees is not
|
|
@@ -8,8 +9,18 @@ tesslDismissalData: 'assets/tessl-dismissal-rules.md'
|
|
|
8
9
|
atomicWriteProbeOrder:
|
|
9
10
|
- '{project-root}/_bmad/skf/shared/scripts/skf-atomic-write.py'
|
|
10
11
|
- '{project-root}/src/shared/scripts/skf-atomic-write.py'
|
|
12
|
+
# Resolve `{descriptionGuardHelper}` by probing `{descriptionGuardProbeOrder}`
|
|
13
|
+
# in order (installed SKF module path first, src/ dev-checkout fallback);
|
|
14
|
+
# first existing path wins. HALT if neither resolves — letting an external
|
|
15
|
+
# tool's rewrite of the description field stand would silently regress
|
|
16
|
+
# discovery quality.
|
|
17
|
+
descriptionGuardProbeOrder:
|
|
18
|
+
- '{project-root}/_bmad/skf/shared/scripts/skf-description-guard.py'
|
|
19
|
+
- '{project-root}/src/shared/scripts/skf-description-guard.py'
|
|
11
20
|
---
|
|
12
21
|
|
|
22
|
+
<!-- Config: communicate in {communication_language}. -->
|
|
23
|
+
|
|
13
24
|
# Step 6: Validate
|
|
14
25
|
|
|
15
26
|
## STEP GOAL:
|
|
@@ -20,38 +31,19 @@ To validate the compiled SKILL.md content against the agentskills.io specificati
|
|
|
20
31
|
|
|
21
32
|
- Focus only on validating compiled content against spec — only fix spec compliance issues
|
|
22
33
|
- Validation and auto-fix modify files in the staging directory
|
|
23
|
-
- `<staging-skill-dir>` resolves to `_bmad-output/{skill-name}/` as created by step
|
|
34
|
+
- `<staging-skill-dir>` resolves to `_bmad-output/{skill-name}/` as created by step 5. The directory name must match the skill's frontmatter `name` field exactly — `skill-check`'s `frontmatter.name_matches_directory` rule rejects any suffix.
|
|
24
35
|
- If skill-check unavailable: skip validation, add warning to evidence report
|
|
25
36
|
- Ignore non-zero exit codes from skill-check if JSON output shows 0 errors
|
|
26
37
|
|
|
27
38
|
## MANDATORY SEQUENCE
|
|
28
39
|
|
|
29
|
-
**CRITICAL:** Follow this sequence exactly. Do not skip, reorder, or improvise.
|
|
30
|
-
|
|
31
40
|
### 0. Description Guard Protocol
|
|
32
41
|
|
|
33
42
|
**Used by:** §2 (`skill-check check --fix`), §4 (`split-body`), and any future tool invocation that may modify SKILL.md.
|
|
34
43
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
To prevent this, any tool invocation that may touch SKILL.md must run inside the following four-phase guard:
|
|
38
|
-
|
|
39
|
-
1. **Capture.** Before invoking the tool, read the current SKILL.md frontmatter and snapshot the exact `description` value into a local variable (e.g., `guarded_description`). Capture the in-context copy as well.
|
|
40
|
-
2. **Execute.** Run the tool as specified in its section.
|
|
41
|
-
3. **Verify.** After the tool completes, re-read the on-disk SKILL.md and compare its frontmatter `description` against `guarded_description` as **token streams**: split each string on whitespace (`str.split()` — any run of spaces/tabs/newlines collapses) and compare the resulting lists element-by-element. This catches content divergence (missing words, replaced phrases, truncation, angle-bracket re-introduction) while ignoring cosmetic whitespace changes that a tool may apply (trailing newline, re-wrapped quoted strings). Do NOT use a normalized-string equality — a tool that rewrites `"foo bar"` to `"foo bar"` (collapsed inner run) would trip a naive normalization even though no semantic content changed, and a tool that swapped one word would slip past a looser fuzzy match. Token-stream comparison is the sweet spot.
|
|
42
|
-
4. **Restore on divergence.** If the post-tool description differs from `guarded_description` in any way other than whitespace normalization, write `guarded_description` back to the on-disk SKILL.md frontmatter and update the in-context copy to match. Record `description_guard_restored: true` with the tool name in context for the evidence report.
|
|
43
|
-
5. **Re-validate restored description.** After a restore, run `uv run {project-root}/src/shared/scripts/skf-validate-frontmatter.py <staging-skill-dir>/SKILL.md` against the on-disk file to confirm the restored description still satisfies the frontmatter contract (length limits, forbidden tokens, required fields). The script declares pyyaml in its PEP 723 inline metadata; `uv run` resolves it automatically (per `docs/getting-started.md`'s uv prereq), while bare `python3` would `ModuleNotFoundError` on a fresh interpreter. Capture `schema_revalidation_result` in context. If the validator exits non-zero OR reports failure for the `description` field: flip the Schema result back to `FAIL` in the evidence report (overriding any prior PASS/WARN from §2), record `description_guard_revalidation: FAIL` with the validator's diagnostic message, and continue — do not halt (step-09 health-check and result contract still need to run so the failure is surfaced through the normal artifact path).
|
|
44
|
-
|
|
45
|
-
**What counts as divergence:**
|
|
44
|
+
Load `{descriptionGuardProtocol}` for the full prose explanation of the four-phase guard (why it exists, what counts as divergence, why token-stream comparison is the right shape). The deterministic phases are executed via `{descriptionGuardHelper}` — the calling sections (§2 and §4) invoke the helper at the capture and verify-restore points.
|
|
46
45
|
|
|
47
|
-
-
|
|
48
|
-
- The description was truncated (suffix missing).
|
|
49
|
-
- Angle-bracket tokens were re-introduced (should never happen after step-05 §2a, but protect anyway).
|
|
50
|
-
- The field was deleted entirely (extreme tool behavior).
|
|
51
|
-
|
|
52
|
-
**What does NOT count as divergence:** whitespace-only differences (trailing newline, trimmed spaces) — treat as equivalent.
|
|
53
|
-
|
|
54
|
-
**Why this is centralized:** previously, §2 and §4 each contained their own capture/verify/restore prose. Duplicated defensive code drifts: a fix in one section doesn't propagate to the other, and adding a new tool invocation in the future requires remembering to copy the pattern. Centralizing the protocol gives step-06 one place to update when external validator behavior changes.
|
|
46
|
+
**This skill's post-restore re-validation hook:** after `{descriptionGuardHelper}` reports `restored: true`, run `uv run {project-root}/src/shared/scripts/skf-validate-frontmatter.py <staging-skill-dir>/SKILL.md` and capture `schema_revalidation_result` in context. If the validator exits non-zero OR reports failure for the `description` field, flip the Schema result back to `FAIL` in the evidence report (overriding any prior PASS/WARN from §2), record `description_guard_revalidation: FAIL` with the validator's diagnostic message, and continue — do not halt (step 9 health-check and result contract still need to run so the failure is surfaced through the normal artifact path).
|
|
55
47
|
|
|
56
48
|
### 1. Check Tool Availability
|
|
57
49
|
|
|
@@ -86,7 +78,21 @@ This performs frontmatter validation, description quality checks, body limit enf
|
|
|
86
78
|
|
|
87
79
|
**Parse the JSON output** for: `qualityScore` (0-100), `diagnostics[]` (remaining issues), `fixed[]` (auto-corrected issues).
|
|
88
80
|
|
|
89
|
-
**Description Guard Protocol:** This invocation may modify SKILL.md (especially when `fixed[]` is non-empty). Wrap the `skill-check check --fix` call in the four-phase
|
|
81
|
+
**Description Guard Protocol:** This invocation may modify SKILL.md (especially when `fixed[]` is non-empty). Wrap the `skill-check check --fix` call in the four-phase guard defined in §0 by invoking `{descriptionGuardHelper}` at the capture and verify-restore points:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
# Phase 1 — capture before the tool call
|
|
85
|
+
uv run {descriptionGuardHelper} capture <staging-skill-dir>/SKILL.md
|
|
86
|
+
# stash the returned `description` as `guarded_description` in workflow context
|
|
87
|
+
|
|
88
|
+
# Phase 2 — run skill-check (see command block above)
|
|
89
|
+
|
|
90
|
+
# Phases 3+4 — verify and restore after the tool call
|
|
91
|
+
uv run {descriptionGuardHelper} verify-restore <staging-skill-dir>/SKILL.md \
|
|
92
|
+
--captured-description "{guarded_description}"
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
If `restored: true` in the verify-restore output, apply §0's post-restore re-validation hook. If `fixed[]` was non-empty in the skill-check output, also re-read the modified SKILL.md to sync the in-context copy before proceeding — this prevents silent divergence between the in-context and on-disk versions that step 7 will use for artifact generation.
|
|
90
96
|
|
|
91
97
|
**Note:** `skill-check` may return non-zero exit code even when `errorCount` is 0. Always rely on parsed JSON, not the shell exit code.
|
|
92
98
|
|
|
@@ -113,7 +119,21 @@ If fails: auto-fix (deterministic), re-validate once, record result. If passes:
|
|
|
113
119
|
|
|
114
120
|
**If step 2 reported `body.max_lines` failure:**
|
|
115
121
|
|
|
116
|
-
**Description Guard Protocol:** Split operations may rewrite the frontmatter. Wrap the split invocation in the four-phase
|
|
122
|
+
**Description Guard Protocol:** Split operations may rewrite the frontmatter. Wrap the split invocation in the four-phase guard defined in §0:
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
# Phase 1 — capture before the split
|
|
126
|
+
uv run {descriptionGuardHelper} capture <staging-skill-dir>/SKILL.md
|
|
127
|
+
# stash returned `description` as `guarded_description`
|
|
128
|
+
|
|
129
|
+
# Phase 2 — run the split (selective extraction or, last-resort, split-body --write)
|
|
130
|
+
|
|
131
|
+
# Phases 3+4 — verify and restore after the split
|
|
132
|
+
uv run {descriptionGuardHelper} verify-restore <staging-skill-dir>/SKILL.md \
|
|
133
|
+
--captured-description "{guarded_description}"
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
If `restored: true` in the verify-restore output, apply §0's post-restore re-validation hook.
|
|
117
137
|
|
|
118
138
|
**Mandatory approach — selective split:** Identify Tier 2 sections by their `## Full` heading prefix (e.g., `## Full API Reference`, `## Full Type Definitions`, `## Full Integration Patterns`). Extract ONLY those sections to `references/`, starting with the largest. Keep ALL Tier 1 content and any smaller sections inline. Inline passive context achieves 100% task accuracy vs 79% for on-demand retrieval (per Vercel research).
|
|
119
139
|
|
|
@@ -162,7 +182,7 @@ Parse output for: `description_score`, `content_score`, `review_score`, `validat
|
|
|
162
182
|
**Apply dismissal rules** in this order:
|
|
163
183
|
|
|
164
184
|
1. **Check score thresholds** against the "Score Thresholds" table in `{tesslDismissalData}`. Most importantly:
|
|
165
|
-
- If `description_score < 100`: follow the **recover-then-halt** path defined by the `description-xml-tags-guarded-upstream` rule in `{tesslDismissalData}`. Re-apply step
|
|
185
|
+
- If `description_score < 100`: follow the **recover-then-halt** path defined by the `description-xml-tags-guarded-upstream` rule in `{tesslDismissalData}`. Re-apply step 5 §2a's `<`/`>` → `{`/`}` substitution in place on the staging SKILL.md frontmatter `description`, re-sync the in-context copy, and re-run `npx -y tessl skill review <staging-skill-dir>` once. **Re-run gate:** treat `description_score == 100` on the re-run as the only successful recovery outcome. ANY value strictly less than 100 on the re-run — including 99, intermediate-but-improved scores like 95 (formerly tolerated as "close enough"), or 0 — counts as recovery failure: halt with the original `description-xml-tags-guarded-upstream` failure message from `{tesslDismissalData}`, do NOT proceed to §6b, and do NOT downgrade the recovery to a warning. If the re-run produces `description_score == 100`, log `description-recovery: applied ({count} substitutions)` in the evidence report under "Dismissed tessl suggestions" and continue suggestion iteration against the rerun's `judge_suggestions[]`.
|
|
166
186
|
- If `review_score < 60` or `content_score < 60`: record warnings in the evidence report, continue.
|
|
167
187
|
2. **Iterate `judge_suggestions[]`.** For each suggestion:
|
|
168
188
|
- Cross-reference against the rules in `{tesslDismissalData}` in order.
|