bmad-module-skill-forge 2.2.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/README.md +49 -49
- package/docs/404.md +10 -8
- package/docs/_data/pinned.yaml +22 -13
- package/docs/_internal/STABILITY.md +58 -35
- package/docs/agents.md +39 -53
- package/docs/architecture.md +65 -51
- package/docs/bmad-synergy.md +53 -51
- package/docs/campaign.md +127 -75
- package/docs/concepts.md +34 -28
- package/docs/examples.md +70 -68
- package/docs/forge-auto.md +34 -31
- package/docs/getting-started.md +118 -57
- package/docs/how-it-works.md +25 -21
- package/docs/index.md +15 -15
- package/docs/skill-model.md +70 -59
- package/docs/troubleshooting.md +143 -18
- package/docs/verifying-a-skill.md +87 -57
- package/docs/why-skf.md +24 -22
- package/docs/workflows.md +274 -110
- package/package.json +18 -10
- package/src/forger/preferences.yaml +8 -1
- package/src/knowledge/agentskills-spec.md +2 -0
- package/src/knowledge/ccc-bridge.md +40 -24
- package/src/knowledge/confidence-tiers.md +9 -5
- package/src/knowledge/overview.md +3 -3
- package/src/knowledge/progressive-capability.md +8 -8
- package/src/knowledge/provenance-tracking.md +9 -5
- package/src/knowledge/qmd-registry.md +3 -3
- package/src/knowledge/skf-knowledge-index.csv +2 -2
- package/src/knowledge/skill-lifecycle.md +18 -11
- package/src/knowledge/split-body-strategy.md +0 -3
- package/src/knowledge/tool-resolution.md +4 -2
- package/src/knowledge/version-paths.md +101 -15
- package/src/module-help.csv +3 -3
- package/src/module.yaml +10 -0
- package/src/shared/data/ast-grep-recipes.yaml +608 -0
- package/src/shared/data/ide-context-files.json +30 -0
- package/src/shared/health-check.md +31 -59
- package/src/shared/references/description-guard-protocol.md +12 -9
- package/src/shared/references/feasibility-report-schema.md +19 -13
- package/src/shared/references/headless-gate-convention.md +55 -3
- package/src/shared/references/output-contract-schema.md +19 -8
- package/src/shared/references/pipeline-contracts.md +94 -29
- package/src/shared/references/tessl-review.md +77 -0
- package/src/shared/scripts/schemas/skf-analyze-result-envelope.v1.json +194 -0
- package/src/shared/scripts/schemas/skf-audit-result-envelope.v1.json +192 -0
- package/src/shared/scripts/schemas/skf-brief-result-envelope.v1.json +32 -4
- package/src/shared/scripts/schemas/skf-campaign-result-envelope.v1.json +174 -0
- package/src/shared/scripts/schemas/skf-create-skill-result-envelope.v1.json +284 -0
- package/src/shared/scripts/schemas/skf-drop-skill-result-envelope.v1.json +164 -0
- package/src/shared/scripts/schemas/skf-export-result-envelope.v1.json +160 -0
- package/src/shared/scripts/schemas/skf-quick-skill-result-envelope.v1.json +261 -0
- package/src/shared/scripts/schemas/skf-refine-architecture-result-envelope.v1.json +192 -0
- package/src/shared/scripts/schemas/skf-rename-skill-result-envelope.v1.json +179 -0
- package/src/shared/scripts/schemas/skf-setup-result-envelope.v1.json +27 -11
- package/src/shared/scripts/schemas/skf-stack-result-envelope.v1.json +176 -0
- package/src/shared/scripts/schemas/skf-test-result-envelope.v1.json +173 -0
- package/src/shared/scripts/schemas/skf-update-result-envelope.v1.json +19 -5
- package/src/shared/scripts/schemas/skf-verify-stack-result-envelope.v1.json +180 -0
- package/src/shared/scripts/schemas/skill-brief.v1.json +34 -0
- package/src/shared/scripts/schemas/workspace-detection.v1.json +1 -1
- package/src/shared/scripts/skf-atomic-write.py +233 -64
- package/src/shared/scripts/skf-build-change-manifest.py +2753 -28
- package/src/shared/scripts/skf-ccc-git-hygiene.py +532 -0
- package/src/shared/scripts/skf-check-unit-records.py +184 -0
- package/src/shared/scripts/skf-check-workspace-drift.py +538 -9
- package/src/shared/scripts/skf-classify-changed-files.py +772 -0
- package/src/shared/scripts/skf-comention-pairs.py +1333 -123
- package/src/shared/scripts/skf-compare-file-hashes.py +267 -105
- package/src/shared/scripts/skf-count-imports.py +1867 -0
- package/src/shared/scripts/skf-count-tokens.py +1 -1
- package/src/shared/scripts/skf-derive-assembly-shape.py +21 -3
- package/src/shared/scripts/skf-description-guard.py +99 -11
- package/src/shared/scripts/skf-detect-docs.py +694 -2
- package/src/shared/scripts/skf-detect-language.py +486 -107
- package/src/shared/scripts/skf-detect-registry.py +887 -0
- package/src/shared/scripts/skf-detect-scripts-assets.py +197 -55
- package/src/shared/scripts/skf-detect-tools.py +209 -27
- package/src/shared/scripts/skf-detect-workspaces.py +506 -30
- package/src/shared/scripts/skf-disqualify-candidates.py +821 -182
- package/src/shared/scripts/skf-emit-brief-result-envelope.py +69 -191
- package/src/shared/scripts/skf-emit-result-envelope.py +1604 -106
- package/src/shared/scripts/skf-enumerate-stack-skills.py +986 -117
- package/src/shared/scripts/skf-extract-public-api.py +5247 -96
- package/src/shared/scripts/skf-extraction-inventory.py +1280 -0
- package/src/shared/scripts/skf-extraction-snapshot.py +664 -0
- package/src/shared/scripts/skf-fetch-temporal.py +674 -0
- package/src/shared/scripts/skf-find-cycles.py +97 -29
- package/src/shared/scripts/skf-find-test-report.py +476 -0
- package/src/shared/scripts/skf-forge-tier-rw.py +469 -205
- package/src/shared/scripts/skf-github-fetch.py +365 -0
- package/src/shared/scripts/skf-github-probe.py +700 -0
- package/src/shared/scripts/skf-hash-content.py +180 -20
- package/src/shared/scripts/skf-language-corpora.py +1 -1
- package/src/shared/scripts/skf-load-provenance.py +161 -27
- package/src/shared/scripts/skf-manifest-ops.py +1 -1
- package/src/shared/scripts/skf-merge-ccc-exclusions.py +2307 -223
- package/src/shared/scripts/skf-merge-doc-urls.py +143 -20
- package/src/shared/scripts/skf-names-present.py +221 -0
- package/src/shared/scripts/skf-pair-intersect.py +119 -28
- package/src/shared/scripts/skf-parse-gaps.py +1384 -0
- package/src/shared/scripts/skf-preapply.py +1 -1
- package/src/shared/scripts/skf-preflight.py +176 -28
- package/src/shared/scripts/skf-promote-staged.py +430 -0
- package/src/shared/scripts/skf-provenance-gap-dispatch.py +64 -8
- package/src/shared/scripts/skf-qmd-classify-collections.py +229 -45
- package/src/shared/scripts/skf-quick-batch.py +774 -0
- package/src/shared/scripts/skf-rebuild-managed-sections.py +972 -88
- package/src/shared/scripts/skf-recommend-scope-type.py +323 -156
- package/src/shared/scripts/skf-render-metadata-stats.py +156 -12
- package/src/shared/scripts/skf-render-quick-metadata.py +132 -28
- package/src/shared/scripts/skf-render-stack-metadata.py +976 -0
- package/src/shared/scripts/skf-resolve-authoritative-files.py +155 -14
- package/src/shared/scripts/skf-resolve-package.py +523 -106
- package/src/shared/scripts/skf-rewrite-skill-name.py +433 -47
- package/src/shared/scripts/skf-run-lock.py +577 -0
- package/src/shared/scripts/skf-scan-manifests.py +998 -57
- package/src/shared/scripts/skf-scan-skill-md-structure.py +1341 -37
- package/src/shared/scripts/skf-severity-classify.py +712 -108
- package/src/shared/scripts/skf-shape-detect.py +298 -204
- package/src/shared/scripts/skf-shard-body.py +9 -8
- package/src/shared/scripts/skf-skill-inventory.py +2412 -89
- package/src/shared/scripts/skf-skills-module.py +472 -0
- package/src/shared/scripts/skf-source-tree.py +1796 -0
- package/src/shared/scripts/skf-structural-diff.py +787 -121
- package/src/shared/scripts/skf-tessl-review.py +706 -0
- package/src/shared/scripts/skf-update-active-symlink.py +120 -67
- package/src/shared/scripts/skf-update-run-state.py +624 -0
- package/src/shared/scripts/skf-validate-brief-inputs.py +92 -29
- package/src/shared/scripts/skf-validate-brief-schema.py +23 -2
- package/src/shared/scripts/skf-validate-feasibility-report.py +577 -66
- package/src/shared/scripts/skf-validate-frontmatter.py +103 -26
- package/src/shared/scripts/skf-validate-output.py +712 -64
- package/src/shared/scripts/skf-validate-pins.py +1 -1
- package/src/shared/scripts/skf-verify-no-trace.py +116 -10
- package/src/shared/scripts/skf-verify-provenance-completeness.py +3945 -152
- package/src/shared/scripts/skf-write-skill-brief.py +406 -25
- package/src/shared/tool-requirements.yaml +201 -0
- package/src/skf-analyze-source/SKILL.md +41 -36
- package/src/skf-analyze-source/assets/skill-brief-schema.md +7 -8
- package/src/skf-analyze-source/customize.toml +36 -24
- package/src/skf-analyze-source/references/auto-docs-only.md +58 -20
- package/src/skf-analyze-source/references/continue.md +2 -13
- package/src/skf-analyze-source/references/discover-additional-source.md +147 -0
- package/src/skf-analyze-source/references/generate-briefs.md +121 -55
- package/src/skf-analyze-source/references/headless-contract.md +42 -15
- package/src/skf-analyze-source/references/identify-units.md +96 -110
- package/src/skf-analyze-source/references/init.md +73 -71
- package/src/skf-analyze-source/references/map-and-detect.md +123 -105
- package/src/skf-analyze-source/references/map-unit-exports.md +68 -0
- package/src/skf-analyze-source/references/recommend.md +51 -70
- package/src/skf-analyze-source/references/scan-project.md +60 -84
- package/src/skf-analyze-source/references/scan-root.md +39 -0
- package/src/skf-analyze-source/references/step-auto-scope-coexistence.md +47 -0
- package/src/skf-analyze-source/references/step-auto-scope-corpora.md +53 -0
- package/src/skf-analyze-source/references/step-auto-scope-split.md +91 -0
- package/src/skf-analyze-source/references/step-auto-scope.md +208 -293
- package/src/skf-analyze-source/references/step-shape-detect.md +14 -12
- package/src/skf-analyze-source/references/unit-detection-heuristics.md +37 -42
- package/src/skf-audit-skill/SKILL.md +43 -51
- package/src/skf-audit-skill/assets/drift-report-template.md +28 -3
- package/src/skf-audit-skill/customize.toml +41 -20
- package/src/skf-audit-skill/references/constituent-freshness.md +91 -0
- package/src/skf-audit-skill/references/doc-drift.md +141 -0
- package/src/skf-audit-skill/references/headless-contract.md +29 -0
- package/src/skf-audit-skill/references/health-check.md +2 -0
- package/src/skf-audit-skill/references/init.md +149 -210
- package/src/skf-audit-skill/references/re-index.md +103 -110
- package/src/skf-audit-skill/references/report.md +96 -33
- package/src/skf-audit-skill/references/semantic-diff.md +34 -25
- package/src/skf-audit-skill/references/severity-classify.md +79 -65
- package/src/skf-audit-skill/references/severity-rules.md +15 -6
- package/src/skf-audit-skill/references/structural-diff.md +80 -90
- package/src/skf-audit-skill/references/upstream-checkout.md +77 -0
- package/src/skf-audit-skill/scripts/render-drift-tables.py +712 -0
- package/src/skf-brief-skill/SKILL.md +37 -26
- package/src/skf-brief-skill/assets/description-voice-examples.md +1 -1
- package/src/skf-brief-skill/assets/scope-templates.md +18 -25
- package/src/skf-brief-skill/assets/skill-brief-schema.md +30 -23
- package/src/skf-brief-skill/customize.toml +48 -20
- package/src/skf-brief-skill/references/analyze-target.md +144 -127
- package/src/skf-brief-skill/references/confirm-brief.md +11 -9
- package/src/skf-brief-skill/references/draft-checkpoint.md +36 -17
- package/src/skf-brief-skill/references/gather-intent-ratify.md +68 -0
- package/src/skf-brief-skill/references/gather-intent.md +87 -182
- package/src/skf-brief-skill/references/headless-args.md +84 -16
- package/src/skf-brief-skill/references/health-check.md +1 -1
- package/src/skf-brief-skill/references/invocation-contract.md +18 -13
- package/src/skf-brief-skill/references/portfolio-similarity-check.md +1 -1
- package/src/skf-brief-skill/references/qmd-collection-registration.md +7 -10
- package/src/skf-brief-skill/references/scope-definition.md +123 -47
- package/src/skf-brief-skill/references/step-auto-brief.md +26 -100
- package/src/skf-brief-skill/references/step-auto-validate.md +20 -121
- package/src/skf-brief-skill/references/version-resolution.md +7 -10
- package/src/skf-brief-skill/references/write-brief.md +66 -57
- package/src/skf-campaign/SKILL.md +31 -34
- package/src/skf-campaign/assets/campaign-state-schema.json +25 -4
- package/src/skf-campaign/customize.toml +47 -16
- package/src/skf-campaign/references/campaign-contracts.md +39 -29
- package/src/skf-campaign/references/campaign-directive-spec.md +4 -2
- package/src/skf-campaign/references/health-check.md +7 -16
- package/src/skf-campaign/references/step-01-setup.md +46 -84
- package/src/skf-campaign/references/step-02-strategy.md +31 -30
- package/src/skf-campaign/references/step-03-pins.md +26 -18
- package/src/skf-campaign/references/step-04-provenance.md +24 -23
- package/src/skf-campaign/references/step-05-skill-loop.md +67 -48
- package/src/skf-campaign/references/step-06-batch.md +41 -34
- package/src/skf-campaign/references/step-07-capstone.md +40 -25
- package/src/skf-campaign/references/step-08-verify.md +28 -29
- package/src/skf-campaign/references/step-09-refine.md +29 -30
- package/src/skf-campaign/references/step-10-export.md +48 -36
- package/src/skf-campaign/references/step-11-maintenance.md +31 -34
- package/src/skf-campaign/references/step-resume.md +37 -68
- package/src/skf-campaign/scripts/campaign-deps.py +57 -18
- package/src/skf-campaign/scripts/campaign-parse-manifest.py +361 -54
- package/src/skf-campaign/scripts/campaign-provenance.py +96 -53
- package/src/skf-campaign/scripts/campaign-quality-gate.py +592 -0
- package/src/skf-campaign/scripts/campaign-render-batch.py +257 -49
- package/src/skf-campaign/scripts/campaign-render-kickoff.py +343 -50
- package/src/skf-campaign/scripts/campaign-report.py +204 -37
- package/src/skf-campaign/scripts/campaign-state.py +1128 -0
- package/src/skf-campaign/scripts/campaign-status.py +10 -74
- package/src/skf-campaign/scripts/campaign-validate-pins.py +30 -3
- package/src/skf-campaign/scripts/campaign-validate-state.py +34 -4
- package/src/skf-campaign/templates/campaign-brief-template.yaml +4 -5
- package/src/skf-campaign/templates/campaign-report-template.md +5 -1
- package/src/skf-campaign/templates/kickoff-template.md +0 -14
- package/src/skf-create-skill/SKILL.md +27 -20
- package/src/skf-create-skill/assets/compile-assembly-rules.md +11 -8
- package/src/skf-create-skill/assets/skill-sections.md +21 -11
- package/src/skf-create-skill/customize.toml +20 -8
- package/src/skf-create-skill/references/authoritative-files-protocol.md +36 -58
- package/src/skf-create-skill/references/batch-mode.md +81 -0
- package/src/skf-create-skill/references/ccc-index-check.md +27 -0
- package/src/skf-create-skill/references/compile.md +119 -70
- package/src/skf-create-skill/references/component-extraction.md +113 -91
- package/src/skf-create-skill/references/enrich.md +41 -23
- package/src/skf-create-skill/references/entry-points-by-hand.md +37 -0
- package/src/skf-create-skill/references/extract.md +204 -162
- package/src/skf-create-skill/references/extraction-patterns-by-hand.md +141 -0
- package/src/skf-create-skill/references/extraction-patterns-tracing.md +11 -11
- package/src/skf-create-skill/references/extraction-patterns.md +588 -201
- package/src/skf-create-skill/references/generate-artifacts.md +145 -93
- package/src/skf-create-skill/references/health-check.md +1 -1
- package/src/skf-create-skill/references/load-brief.md +53 -36
- package/src/skf-create-skill/references/relabel-rule.md +12 -0
- package/src/skf-create-skill/references/report.md +37 -57
- package/src/skf-create-skill/references/source-resolution-protocols.md +58 -169
- package/src/skf-create-skill/references/step-auto-shard.md +20 -93
- package/src/skf-create-skill/references/step-doc-rot.md +44 -60
- package/src/skf-create-skill/references/step-doc-sources.md +28 -19
- package/src/skf-create-skill/references/sub/ccc-discover.md +18 -7
- package/src/skf-create-skill/references/sub/fetch-docs.md +106 -59
- package/src/skf-create-skill/references/sub/fetch-temporal.md +53 -166
- package/src/skf-create-skill/references/tier-degradation-rules.md +18 -9
- package/src/skf-create-skill/references/validate.md +173 -99
- package/src/skf-create-skill/scripts/scan-doc-rot.py +232 -99
- package/src/skf-create-stack-skill/SKILL.md +29 -56
- package/src/skf-create-stack-skill/assets/metadata-contract.md +51 -0
- package/src/skf-create-stack-skill/assets/provenance-map-schema.md +15 -15
- package/src/skf-create-stack-skill/assets/stack-skill-template.md +23 -57
- package/src/skf-create-stack-skill/customize.toml +23 -12
- package/src/skf-create-stack-skill/references/compile-stack.md +57 -66
- package/src/skf-create-stack-skill/references/compose-mode-rules.md +24 -44
- package/src/skf-create-stack-skill/references/detect-integrations.md +92 -95
- package/src/skf-create-stack-skill/references/detect-manifests.md +46 -71
- package/src/skf-create-stack-skill/references/generate-output.md +138 -128
- package/src/skf-create-stack-skill/references/init.md +90 -32
- package/src/skf-create-stack-skill/references/integration-patterns.md +1 -17
- package/src/skf-create-stack-skill/references/invocation-contract.md +34 -0
- package/src/skf-create-stack-skill/references/manifest-patterns.md +32 -20
- package/src/skf-create-stack-skill/references/parallel-extract.md +112 -38
- package/src/skf-create-stack-skill/references/rank-and-confirm.md +57 -75
- package/src/skf-create-stack-skill/references/report.md +39 -33
- package/src/skf-create-stack-skill/references/validate.md +20 -39
- package/src/skf-drop-skill/SKILL.md +28 -51
- package/src/skf-drop-skill/customize.toml +44 -30
- package/src/skf-drop-skill/references/execute.md +83 -146
- package/src/skf-drop-skill/references/health-check.md +2 -0
- package/src/skf-drop-skill/references/invocation-contract.md +64 -0
- package/src/skf-drop-skill/references/report.md +46 -30
- package/src/skf-drop-skill/references/select.md +122 -124
- package/src/skf-drop-skill/scripts/dir-sizes.py +54 -26
- package/src/skf-drop-skill/scripts/drop-roster.py +322 -0
- package/src/skf-export-skill/SKILL.md +40 -62
- package/src/skf-export-skill/assets/managed-section-format.md +14 -50
- package/src/skf-export-skill/assets/snippet-format.md +3 -1
- package/src/skf-export-skill/customize.toml +42 -13
- package/src/skf-export-skill/references/generate-snippet.md +54 -46
- package/src/skf-export-skill/references/health-check.md +2 -0
- package/src/skf-export-skill/references/invocation-contract.md +15 -0
- package/src/skf-export-skill/references/load-skill.md +88 -62
- package/src/skf-export-skill/references/manifest-rebuild.md +9 -10
- package/src/skf-export-skill/references/multi-skill-mode.md +4 -4
- package/src/skf-export-skill/references/orphan-context-detection.md +13 -5
- package/src/skf-export-skill/references/orphan-row-detection.md +31 -22
- package/src/skf-export-skill/references/package.md +15 -73
- package/src/skf-export-skill/references/preflight-snippet-root-probe.md +51 -70
- package/src/skf-export-skill/references/result-envelope.md +31 -16
- package/src/skf-export-skill/references/summary.md +41 -17
- package/src/skf-export-skill/references/token-report.md +4 -12
- package/src/skf-export-skill/references/update-context.md +118 -237
- package/src/skf-forger/SKILL.md +61 -37
- package/src/skf-forger/bmad-skill-manifest.yaml +4 -1
- package/src/skf-forger/customize.toml +43 -0
- package/src/skf-forger/references/pipeline-mode.md +73 -18
- package/src/skf-forger/scripts/forge-status.py +299 -0
- package/src/skf-forger/scripts/parse-pipeline.py +322 -54
- package/src/skf-forger/scripts/pipeline-gate.py +428 -0
- package/src/skf-forger/scripts/pipeline-journal.py +1052 -0
- package/src/skf-quick-skill/SKILL.md +54 -53
- package/src/skf-quick-skill/assets/skill-template.md +4 -2
- package/src/skf-quick-skill/customize.toml +51 -20
- package/src/skf-quick-skill/references/batch-mode.md +74 -21
- package/src/skf-quick-skill/references/compile.md +30 -31
- package/src/skf-quick-skill/references/ecosystem-check.md +7 -57
- package/src/skf-quick-skill/references/finalize.md +69 -18
- package/src/skf-quick-skill/references/halt-contract.md +41 -29
- package/src/skf-quick-skill/references/health-check.md +5 -3
- package/src/skf-quick-skill/references/quick-extract.md +114 -45
- package/src/skf-quick-skill/references/registry-resolution.md +9 -64
- package/src/skf-quick-skill/references/resolve-target.md +116 -66
- package/src/skf-quick-skill/references/write-and-validate.md +79 -40
- package/src/skf-refine-architecture/SKILL.md +43 -53
- package/src/skf-refine-architecture/customize.toml +49 -20
- package/src/skf-refine-architecture/references/compile.md +97 -54
- package/src/skf-refine-architecture/references/exit-codes.md +30 -0
- package/src/skf-refine-architecture/references/finding-storage.md +21 -0
- package/src/skf-refine-architecture/references/gap-analysis.md +101 -38
- package/src/skf-refine-architecture/references/improvements.md +14 -20
- package/src/skf-refine-architecture/references/init.md +98 -57
- package/src/skf-refine-architecture/references/issue-detection.md +32 -39
- package/src/skf-refine-architecture/references/refinement-rules.md +37 -48
- package/src/skf-refine-architecture/references/report.md +65 -54
- package/src/skf-refine-architecture/scripts/skf-check-preservation.py +1619 -0
- package/src/skf-rename-skill/SKILL.md +19 -54
- package/src/skf-rename-skill/customize.toml +32 -26
- package/src/skf-rename-skill/references/execute.md +230 -184
- package/src/skf-rename-skill/references/exit-codes.md +7 -7
- package/src/skf-rename-skill/references/health-check.md +1 -7
- package/src/skf-rename-skill/references/invocation-contract.md +25 -0
- package/src/skf-rename-skill/references/report.md +63 -52
- package/src/skf-rename-skill/references/select.md +197 -115
- package/src/skf-rename-skill/scripts/skf-validate-rename-name.py +161 -21
- package/src/skf-setup/SKILL.md +26 -26
- package/src/skf-setup/customize.toml +14 -10
- package/src/skf-setup/references/auto-index.md +50 -63
- package/src/skf-setup/references/ccc-index.md +29 -90
- package/src/skf-setup/references/detect-and-tier.md +58 -67
- package/src/skf-setup/references/health-check.md +2 -0
- package/src/skf-setup/references/invocation-contract.md +14 -0
- package/src/skf-setup/references/report.md +43 -166
- package/src/skf-setup/references/tier-rules.md +5 -5
- package/src/skf-setup/references/write-config.md +34 -57
- package/src/skf-test-skill/SKILL.md +17 -57
- package/src/skf-test-skill/assets/output-section-formats.md +33 -34
- package/src/skf-test-skill/customize.toml +27 -13
- package/src/skf-test-skill/references/coherence-check.md +134 -62
- package/src/skf-test-skill/references/coverage-check-tiers.md +35 -0
- package/src/skf-test-skill/references/coverage-check.md +209 -264
- package/src/skf-test-skill/references/detect-mode.md +5 -5
- package/src/skf-test-skill/references/external-validators.md +94 -82
- package/src/skf-test-skill/references/health-check.md +6 -7
- package/src/skf-test-skill/references/init.md +164 -91
- package/src/skf-test-skill/references/invocation-contract.md +66 -0
- package/src/skf-test-skill/references/migration-section-rules.md +5 -2
- package/src/skf-test-skill/references/report.md +164 -142
- package/src/skf-test-skill/references/score.md +80 -107
- package/src/skf-test-skill/references/scoring-rules.md +42 -155
- package/src/skf-test-skill/references/source-access-protocol.md +18 -94
- package/src/skf-test-skill/references/step-hard-gate.md +51 -33
- package/src/skf-test-skill/scripts/aggregate-coherence.py +218 -30
- package/src/skf-test-skill/scripts/build-result-context.py +380 -0
- package/src/skf-test-skill/scripts/check-metadata-coherence.py +37 -4
- package/src/skf-test-skill/scripts/combine-external-scores.py +23 -6
- package/src/skf-test-skill/scripts/compute-score.py +223 -45
- package/src/skf-test-skill/scripts/gap-ledger.py +1666 -0
- package/src/skf-test-skill/scripts/hard-gate.py +203 -0
- package/src/skf-test-skill/scripts/load-coverage-inputs.py +1198 -0
- package/src/skf-test-skill/scripts/locate-export-segments.py +835 -0
- package/src/skf-test-skill/scripts/reconcile-coverage.py +547 -79
- package/src/skf-test-skill/scripts/score-signatures.py +479 -0
- package/src/skf-test-skill/scripts/stage-helper-payload.py +148 -0
- package/src/skf-test-skill/scripts/validate-inventory.py +311 -16
- package/src/skf-test-skill/scripts/verify-declared-numerator.py +133 -56
- package/src/skf-test-skill/templates/test-report-template.md +21 -15
- package/src/skf-update-skill/SKILL.md +38 -25
- package/src/skf-update-skill/customize.toml +27 -12
- package/src/skf-update-skill/references/detect-changes.md +253 -223
- package/src/skf-update-skill/references/gap-driven.md +336 -0
- package/src/skf-update-skill/references/health-check.md +31 -5
- package/src/skf-update-skill/references/init.md +254 -84
- package/src/skf-update-skill/references/invocation-contract.md +12 -0
- package/src/skf-update-skill/references/manual-section-rules.md +6 -23
- package/src/skf-update-skill/references/merge.md +182 -49
- package/src/skf-update-skill/references/re-extract.md +53 -262
- package/src/skf-update-skill/references/report.md +102 -35
- package/src/skf-update-skill/references/write.md +357 -155
- package/src/skf-update-skill/scripts/skf-new-file-diff.py +183 -46
- package/src/skf-verify-stack/SKILL.md +23 -42
- package/src/skf-verify-stack/assets/feasibility-report-template.md +11 -5
- package/src/skf-verify-stack/customize.toml +55 -22
- package/src/skf-verify-stack/references/coverage-patterns.md +17 -8
- package/src/skf-verify-stack/references/coverage.md +67 -64
- package/src/skf-verify-stack/references/exit-codes.md +21 -8
- package/src/skf-verify-stack/references/init.md +107 -62
- package/src/skf-verify-stack/references/integration-verification-rules.md +12 -13
- package/src/skf-verify-stack/references/integrations.md +106 -65
- package/src/skf-verify-stack/references/report.md +59 -42
- package/src/skf-verify-stack/references/requirements.md +32 -9
- package/src/skf-verify-stack/references/synthesize.md +53 -39
- package/src/skf-verify-stack/scripts/skf-coverage-tally.py +398 -52
- package/src/skf-verify-stack/scripts/skf-previous-report.py +330 -0
- package/src/skf-verify-stack/scripts/skf-report-delta.py +453 -49
- package/src/skf-verify-stack/scripts/skf-verdict-rollup.py +240 -27
- package/tools/cli/commands/install.js +11 -4
- package/tools/cli/commands/status.js +15 -3
- package/tools/cli/commands/update.js +11 -4
- package/tools/cli/lib/tool-check.js +320 -0
- package/tools/cli/lib/version-check.js +22 -9
- package/tools/cli/skf-cli.js +4 -1
- package/.gitattributes +0 -16
- package/.nvmrc +0 -1
- package/docs/_internal/RELEASING.md +0 -679
- package/release-audits/v1.0.0-launch-audit.md +0 -728
- package/src/skf-audit-skill/references/step-doc-drift.md +0 -177
- package/src/skf-brief-skill/references/headless-source-authority-detection.md +0 -26
- package/src/skf-campaign/manifest.yaml +0 -15
- package/src/skf-create-skill/assets/tessl-dismissal-rules.md +0 -106
- package/src/skf-create-skill/references/ecosystem-check.md +0 -74
- package/src/skf-drop-skill/references/headless-contract.md +0 -18
- package/src/skf-update-skill/references/merge-conflict-rules.md +0 -15
- package/src/skf-update-skill/references/remote-source-resolution.md +0 -98
- package/src/skf-update-skill/references/validate.md +0 -124
- package/tools/validate-docs-drift.js +0 -233
- package/tools/validate-docs-links.js +0 -665
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
---
|
|
2
2
|
nextStepFile: 'analyze-target.md'
|
|
3
|
-
|
|
4
|
-
forgeTierFile: '{sidecar_path}/forge-tier.yaml'
|
|
3
|
+
autoBriefFile: 'references/step-auto-brief.md'
|
|
5
4
|
headlessArgsFile: 'references/headless-args.md'
|
|
6
|
-
|
|
5
|
+
ratifyFile: 'references/gather-intent-ratify.md'
|
|
6
|
+
forgeTierFile: '{sidecar_path}/forge-tier.yaml'
|
|
7
7
|
portfolioSimilarityCheckFile: 'references/portfolio-similarity-check.md'
|
|
8
8
|
draftCheckpointFile: 'references/draft-checkpoint.md'
|
|
9
|
+
githubProbeProbeOrder:
|
|
10
|
+
- '{project-root}/_bmad/skf/shared/scripts/skf-github-probe.py'
|
|
11
|
+
- '{project-root}/src/shared/scripts/skf-github-probe.py'
|
|
12
|
+
resolvePackageProbeOrder:
|
|
13
|
+
- '{project-root}/_bmad/skf/shared/scripts/skf-resolve-package.py'
|
|
14
|
+
- '{project-root}/src/shared/scripts/skf-resolve-package.py'
|
|
9
15
|
validateBriefInputsProbeOrder:
|
|
10
16
|
- '{project-root}/_bmad/skf/shared/scripts/skf-validate-brief-inputs.py'
|
|
11
17
|
- '{project-root}/src/shared/scripts/skf-validate-brief-inputs.py'
|
|
12
|
-
validateBriefSchemaProbeOrder:
|
|
13
|
-
- '{project-root}/_bmad/skf/shared/scripts/skf-validate-brief-schema.py'
|
|
14
|
-
- '{project-root}/src/shared/scripts/skf-validate-brief-schema.py'
|
|
15
|
-
emitBriefEnvelopeProbeOrder:
|
|
16
|
-
- '{project-root}/_bmad/skf/shared/scripts/skf-emit-brief-result-envelope.py'
|
|
17
|
-
- '{project-root}/src/shared/scripts/skf-emit-brief-result-envelope.py'
|
|
18
18
|
---
|
|
19
19
|
|
|
20
20
|
<!-- Config: communicate in {communication_language}. -->
|
|
@@ -32,15 +32,19 @@ emitBriefEnvelopeProbeOrder:
|
|
|
32
32
|
|
|
33
33
|
### 1. Discover Forge Tier
|
|
34
34
|
|
|
35
|
-
**Pre-flight write probe.** Before any conversational state accumulates, verify `{forge_data_folder}` is writable. A read-only mount, full disk, or permissions-denied path otherwise only surfaces at step 5's atomic write
|
|
35
|
+
**Pre-flight write probe and run folder.** Before any conversational state accumulates, verify `{forge_data_folder}` is writable, and create the run folder, where later steps stage what they hand to a helper (the repository's file list, fetched files, JSON payloads) instead of typing it into a command. A read-only mount, full disk, or permissions-denied path otherwise only surfaces at step 5's atomic write, after the user has invested 5–15 minutes. Run a single-byte write-and-remove probe, then create the folder:
|
|
36
36
|
|
|
37
37
|
```bash
|
|
38
38
|
mkdir -p "{forge_data_folder}" && \
|
|
39
39
|
printf 'probe' > "{forge_data_folder}/.skf-write-probe" && \
|
|
40
|
-
rm "{forge_data_folder}/.skf-write-probe"
|
|
40
|
+
rm "{forge_data_folder}/.skf-write-probe" && \
|
|
41
|
+
mkdir -p "{project-root}/_bmad-output/.skf-run" && \
|
|
42
|
+
mktemp -d "{project-root}/_bmad-output/.skf-run/skf-brief-skill-XXXXXXXX"
|
|
41
43
|
```
|
|
42
44
|
|
|
43
|
-
|
|
45
|
+
Bind `{run_dir}` ← the path the last command prints. Step 5, or step-auto-validate.md at the end of an `[auto]` run, removes the folder once the brief is written, and an `[X]` cancel removes it before it stops; any other halt leaves it in place with what the run staged. A step stages a payload there through a heredoc whose delimiter is quoted (`'SKF_JSON'`), so a quote or an apostrophe in it needs no escaping.
|
|
46
|
+
|
|
47
|
+
`mkdir -p` succeeds on a pre-existing read-only mount, but the `printf > file` redirect actually attempts a write, which catches read-only, disk-full, and permissions-denied uniformly. **On any non-zero exit:** emit the halt envelope first, `uv run {emitBriefEnvelopeHelper} emit --target stderr` with `halt_reason: "write-failed"` (SKILL.md Halt Contract), then HALT (exit code 4): `"**Error:** {forge_data_folder} or the run folder under {project-root}/_bmad-output/.skf-run/ is not writable: {captured stderr}. Verify the path exists, the mount is writable, and there is free disk space, then re-run."` On success, continue silently to the forge-tier load below.
|
|
44
48
|
|
|
45
49
|
Attempt to load `{forgeTierFile}`:
|
|
46
50
|
|
|
@@ -54,24 +58,16 @@ Attempt to load `{forgeTierFile}`:
|
|
|
54
58
|
- Display: "**Cannot read forge-tier.yaml** at `{forgeTierFile}` — the file exists but failed to parse: `{parser error message}`. The setup workflow can rewrite it cleanly. Until then, the brief workflow falls back to **Quick** tier (no extra tools assumed)."
|
|
55
59
|
- Continue with `tier = "Quick"` and `tools = {}` — do not HALT. Record `tier_source: "fallback-corrupted-config"` for later diagnostics.
|
|
56
60
|
|
|
57
|
-
**If not found:**
|
|
58
|
-
- "**Cannot proceed.** forge-tier.yaml not found at `{forgeTierFile}`. Run the **setup** workflow first to configure your forge tier (Quick/Forge/Forge+/Deep)."
|
|
59
|
-
- In headless mode, emit the error envelope per **step 5 §4b** with `halt_reason: "forge-tier-missing"` (`skill_name` is not yet resolved here — use the `"unknown"` placeholder convention documented in §4b).
|
|
60
|
-
- HALT (exit code 3, `halt_reason: "forge-tier-missing"`) — do not proceed.
|
|
61
|
-
|
|
62
|
-
### 1b. Auto Mode Check
|
|
61
|
+
**If not found:** emit the halt envelope first, `uv run {emitBriefEnvelopeHelper} emit --target stderr` with `halt_reason: "forge-tier-missing"` (SKILL.md Halt Contract), then HALT (exit code 3): "**Cannot proceed.** forge-tier.yaml not found at `{forgeTierFile}`. Run the **setup** workflow first to configure your forge tier (Quick/Forge/Forge+/Deep)."
|
|
63
62
|
|
|
64
|
-
|
|
63
|
+
### 1b. Route by Mode
|
|
65
64
|
|
|
66
|
-
|
|
65
|
+
The first route that applies runs, and it leaves this file:
|
|
67
66
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
**IF `{auto_mode}` is NOT true:**
|
|
74
|
-
Continue to §2 as normal — the entire interactive flow below is unchanged.
|
|
67
|
+
- **`{auto_mode}` is true** (a pipeline's `BS[auto]`): read `brief_path` from the pipeline data context (the forger passes it from AN's `SKF_ANALYZE_RESULT_JSON` `brief_paths[]`). If `brief_path` is not available, emit the halt envelope first, `uv run {emitBriefEnvelopeHelper} emit --target stderr` with `halt_reason: "input-missing"` and `mode` `"auto"` (SKILL.md Halt Contract), then HARD HALT (exit code 2): "**Auto mode requires `brief_path` in pipeline context: AN must run before BS[auto].**"
|
|
68
|
+
Read `source_repo` from the pipeline data context too (the target the forger forwards), else from the upstream brief. Display "**Auto mode activated: bypassing the interactive brief workflow.**", then load, read fully, and execute `{autoBriefFile}`.
|
|
69
|
+
- **`{headless_mode}` is true:** load, read fully, and execute `{headlessArgsFile}`. Its input gate validates the arguments before any later section uses them, then continues at step 2, or ratifies the brief a `from_brief` argument names.
|
|
70
|
+
- **Otherwise** the run is interactive: continue at §2.
|
|
75
71
|
|
|
76
72
|
### 2. Welcome and Explain
|
|
77
73
|
|
|
@@ -92,11 +88,11 @@ Let's get started."
|
|
|
92
88
|
|
|
93
89
|
### 3. Gather Target Repository
|
|
94
90
|
|
|
95
|
-
This section has
|
|
91
|
+
This section has five sub-flows. Execute exactly one branch, 3.1a *or* 3.2 *or* 3.3, based on the user's response in 3.1 (3.1b first resolves a package name to the repository 3.3 takes), then end with the shared confirmation (3.1a leaves this file for the ratify flow). Do not mix branches.
|
|
96
92
|
|
|
97
93
|
#### 3.1 Collect target
|
|
98
94
|
|
|
99
|
-
**Open-floor opening.** Lead with an open invitation so an expert can state everything in one
|
|
95
|
+
**Open-floor opening.** Lead with an open invitation, so an expert can state everything in one answer; a first-timer who pastes only a bare URL still gets the guided sequence below.
|
|
100
96
|
|
|
101
97
|
"**What repository or documentation do you want to create a skill for?**
|
|
102
98
|
|
|
@@ -104,6 +100,7 @@ Tell me everything you have — the repo or docs, what you want to skill and why
|
|
|
104
100
|
|
|
105
101
|
Provide one of:
|
|
106
102
|
- A **GitHub URL** (e.g., `https://github.com/org/repo`)
|
|
103
|
+
- A **package name or registry page** (e.g., `zod`, `requests==2.31.0`, `https://www.npmjs.com/package/zod`), which I resolve to its GitHub repository
|
|
107
104
|
- A **local path** (e.g., `/path/to/project`)
|
|
108
105
|
- **Documentation URLs** for a docs-only skill (e.g., `https://docs.stripe.com/api`) — use this when no source code is available (SaaS, closed-source)
|
|
109
106
|
- A **path to an existing `skill-brief.yaml`** (file path or a directory containing one) — use this to ratify a brief produced by another workflow (e.g. `skf-analyze-source`) without re-deriving fields
|
|
@@ -114,65 +111,42 @@ Or type `cancel` / `exit` / `[X]` to leave without writing anything.
|
|
|
114
111
|
|
|
115
112
|
Wait for user response. **Parse the response for any of the fields the later sections collect** — `target_version` (§3b), intent (§4), scope hints (§5), source authority (§3.3), a proposed name (§6) — and pre-fill every field the user covered, holding them in workflow context. Sections §3b/§4/§5/§6/§7b then **acknowledge a pre-filled field instead of re-asking** ("I noted you're targeting v4.0.0"), and prompt only for the gaps. An expert who stated it all collapses to the §3.1 target branch plus the §7b description confirmation; a bare URL falls through to the full sequence. Then branch on the response for the target itself:
|
|
116
113
|
|
|
117
|
-
- Empty input, `cancel`, `exit`, `[X]`, `q`, or `:q` →
|
|
118
|
-
- Path that resolves to an existing `skill-brief.yaml` (file path ending in `skill-brief.yaml` that exists, OR a directory containing a `skill-brief.yaml`) → §3.1a
|
|
119
|
-
- Documentation URLs only (no source location) → §3.2
|
|
120
|
-
- GitHub URL or local filesystem path → §3.3
|
|
114
|
+
- Empty input, `cancel`, `exit`, `[X]`, `q`, or `:q` → Remove the run folder (`case "{run_dir}" in "{project-root}/_bmad-output/.skf-run/skf-brief-skill-"*) rm -rf "{run_dir}" ;; esac`), display `"Cancelled: no brief was written."` and HALT (exit code 6, `halt_reason: "user-cancelled"`).
|
|
121
115
|
- Any other free-form question (e.g. "what is this?", "show me an example", "how does SKF work?") → answer briefly, re-display the prompt
|
|
122
|
-
|
|
123
|
-
#### 3.1a Branch — Ratify existing brief
|
|
124
|
-
|
|
125
|
-
This branch handles the AN→BS handoff: another workflow (typically `skf-analyze-source`) has already produced a `skill-brief.yaml`, and the user wants to review and confirm it without re-running gather-intent / analyze-target / scope-definition. **This §3.1a path is reached only interactively** — it is entered by typing a brief path at the §3.1 prompt, which headless mode never does. The headless equivalent is the §8 GATE `from_brief` route: it consumes a `from_brief` argument, runs the same schema validation, sets the same `ratify_mode`, hydrates from the same parsed payload, and jumps to step 4 exactly as `[R]` does below — see §8. Keep the two paths' hydration in sync.
|
|
126
|
-
|
|
127
|
-
Resolve the path:
|
|
128
|
-
|
|
129
|
-
- If the user's input ends in `skill-brief.yaml` and points at an existing file → that is the brief path.
|
|
130
|
-
- Otherwise (input was a directory) → the brief path is `<input>/skill-brief.yaml`.
|
|
131
|
-
|
|
132
|
-
**Validate the brief against the schema** before presenting it. Resolve `{validateBriefSchemaHelper}` from `{validateBriefSchemaProbeOrder}` (first existing path wins; HALT if no candidate exists), then:
|
|
116
|
+
- Otherwise read the target the response names (the URL, path or package name, without the sentence around it) with `{resolvePackageHelper}`, resolved from `{resolvePackageProbeOrder}` (first existing path wins). Write the target exactly as given on the line between the markers, quotes, backticks and `$` included. `--local-first` reads a target that names an existing file or folder (relative to the working folder, `~` expanded) as a `local-path`, whatever its shape: `skill-brief.yaml`, `briefs/skill-brief.yaml`, `libs/mylib` or a path with a space.
|
|
133
117
|
|
|
134
118
|
```bash
|
|
135
|
-
uv run {
|
|
119
|
+
uv run {resolvePackageHelper} parse-target --local-first <<'SKF_TARGET'
|
|
120
|
+
{target}
|
|
121
|
+
SKF_TARGET
|
|
136
122
|
```
|
|
137
123
|
|
|
138
|
-
|
|
124
|
+
Route on the `kind` it returns:
|
|
125
|
+
- `local-path`: a `skill-brief.yaml` file, or a folder that holds one → §3.1a; any other path → §3.3.
|
|
126
|
+
- `github` → §3.3, with its `url` as the target. A pinned version (`target_version` not null, as in `owner/repo@1.2.0`) pre-fills `target_version`, which §3b checks before it keeps it: a full X.Y.Z is acknowledged, and a partial pin (`owner/repo@1.2`) is only a hint that §3b names when it asks again; a `/tree/<ref>/<folder>` URL pre-fills `target_ref` ← `ref` and adds `subdir` to the scope hints.
|
|
127
|
+
- `package` or `registry-page` (`lodash`, `@scope/name@2.1.0`, `requests==2.31.0`, an npmjs.com, pypi.org or crates.io page) → §3.1b, which finds its repository; its `target_version`, when not null, pre-fills `target_version` the same way (`react@18` is a partial pin).
|
|
128
|
+
- `other-host`: documentation pages (a docs site, an API reference) → §3.2; a repository on another git host (GitLab, Bitbucket, Codeberg, a self-hosted forge) → display "**Brief Skill reads GitHub repositories and local folders.** `{url}` is on `{host}`: clone it and give me the local path, or paste its GitHub URL if it has one." and re-display the prompt.
|
|
129
|
+
- `unparsed`: several documentation URLs → §3.2; anything else → answer briefly and re-display the prompt.
|
|
139
130
|
|
|
140
|
-
|
|
141
|
-
- **`valid: true`** — proceed with the parsed `brief` payload.
|
|
131
|
+
When `{resolvePackageHelper}` has no path, or the call prints no JSON, route the target by reading it yourself, by the same kinds (a path that exists is a `local-path`).
|
|
142
132
|
|
|
143
|
-
|
|
133
|
+
#### 3.1a Branch: Ratify an Existing Brief
|
|
144
134
|
|
|
145
|
-
|
|
146
|
-
**Existing brief detected at `{path}`.**
|
|
147
|
-
|
|
148
|
-
- **Name:** {brief.name}
|
|
149
|
-
- **Target:** {brief.source_repo}
|
|
150
|
-
- **Description:** "{brief.description}"
|
|
151
|
-
- **Created:** {brief.created} by {brief.created_by}
|
|
152
|
-
- **Scope:** {brief.scope.type}
|
|
153
|
-
|
|
154
|
-
Pick one:
|
|
155
|
-
[R] Ratify — review in step 4 and write (overwriting this file once approved)
|
|
156
|
-
[F] Start fresh — discard this brief and re-prompt for a target
|
|
157
|
-
[X] Cancel and exit
|
|
158
|
-
```
|
|
135
|
+
The target is a `skill-brief.yaml`, or a folder holding one, that another workflow (typically `skf-analyze-source`) produced, to review and confirm instead of re-deriving. Load, read entirely, and execute `{ratifyFile}` with that path: it validates the brief, offers `[R]` Ratify, `[F]` Start fresh and `[X]` Cancel, and on `[R]` hands the run to step 4, past every later section of this file and steps 2 and 3.
|
|
159
136
|
|
|
160
|
-
|
|
137
|
+
#### 3.1b Branch: Resolve a Package Name
|
|
161
138
|
|
|
162
|
-
|
|
163
|
-
- `name` ← `brief.name`; `version` ← `brief.version`; `target_version` ← `brief.target_version`
|
|
164
|
-
- `target_ref` ← `brief.target_ref`; `source_ref` ← `brief.source_ref` (optional git refs; preserve when present)
|
|
165
|
-
- `source_repo` ← `brief.source_repo`; `source_type` ← `brief.source_type`; `source_authority` ← `brief.source_authority`; `doc_urls` ← `brief.doc_urls`
|
|
166
|
-
- `language` ← `brief.language`; `description` ← `brief.description`; `forge_tier` ← `brief.forge_tier`
|
|
167
|
-
- `created` ← `brief.created`; `created_by` ← `brief.created_by`
|
|
168
|
-
- `scope.type` / `scope.include` / `scope.exclude` / `scope.tier_a_include` / `scope.notes` / `scope.rationale` / `scope.amendments` ← `brief.scope.*` (preserve `tier_a_include` and the `amendments` log verbatim — do not re-derive or drop them)
|
|
169
|
-
- `scripts_intent` ← `brief.scripts_intent`; `assets_intent` ← `brief.assets_intent`
|
|
139
|
+
The target names a package (or its registry page), not a repository. Ask the registries for the repository it is published from, with the same helper:
|
|
170
140
|
|
|
171
|
-
|
|
141
|
+
```bash
|
|
142
|
+
uv run {resolvePackageHelper} resolve "{package_name}" --timeout 10 [--registry {registry}]
|
|
143
|
+
```
|
|
172
144
|
|
|
173
|
-
-
|
|
174
|
-
|
|
175
|
-
-
|
|
145
|
+
Pass `--registry` when parse-target set `registry` (a registry page's, PyPI for a `==` pin, npm for a scoped name). The JSON's `status` is `ok`, `ambiguous` or `fallthrough` (exit 0, 3 or 1):
|
|
146
|
+
|
|
147
|
+
- **`ok`**: show "`{package_name}` is published from {resolved_url} ({registry_used})." and continue at §3.3 with `resolved_url` as the target. When `source_subdir` is set (the package's folder in a monorepo), add it to the scope hints.
|
|
148
|
+
- **`ambiguous`**: the name may belong to more than one project. Offer every candidate, numbered: `resolved_url` from `registry_used` first, then each `also_found_in` entry that has a `resolved_url`. Let the user pick one by number or paste the GitHub URL they mean, and continue at §3.3 with the pick; its `source_subdir`, when set, joins the scope hints.
|
|
149
|
+
- **`fallthrough`** (no registry gave a GitHub repository), or no JSON: display "**`{package_name}` did not resolve to a GitHub repository** on npm, PyPI or crates.io. Paste its GitHub URL or a local path." and re-display the §3.1 prompt.
|
|
176
150
|
|
|
177
151
|
#### 3.2 Branch — Documentation URLs (docs-only)
|
|
178
152
|
|
|
@@ -180,7 +154,7 @@ Wait for user response. Branch:
|
|
|
180
154
|
- Collect one or more doc URLs with optional labels
|
|
181
155
|
- HEAD-check the collected URLs in parallel — do not loop sequentially. Issue all N `curl -sI {url}` (or equivalent) calls in a **single message with N parallel Bash calls**, then process the responses together. Each call must use a 5-second timeout (`curl -sI --max-time 5 {url}`) to bound worst-case wall-time on hung hosts. Per response:
|
|
182
156
|
- On 2xx/3xx: silently accept.
|
|
183
|
-
- On 4xx/5xx, DNS failure, or timeout: warn `"Could not reach {url}
|
|
157
|
+
- On 4xx/5xx, DNS failure, or timeout: warn `"Could not reach {url} ({status or error}). Confirm the URL is correct, or proceed anyway."` and re-prompt for a corrected URL or `[K] Keep anyway`.
|
|
184
158
|
- Set `source_authority: "community"` (forced for docs-only — T3 external documentation; the §3.3 source-authority prompt is skipped)
|
|
185
159
|
- Note: `source_repo` becomes optional (can be set to the main doc site URL for reference)
|
|
186
160
|
|
|
@@ -189,17 +163,16 @@ Skip §3.3 and continue at "Confirm the target" below.
|
|
|
189
163
|
#### 3.3 Branch — Source (GitHub URL or local path)
|
|
190
164
|
|
|
191
165
|
- Set `source_type: "source"` (default)
|
|
192
|
-
- **Pre-validate the target before continuing.**
|
|
193
|
-
- **GitHub URL:** `
|
|
194
|
-
|
|
195
|
-
|
|
166
|
+
- **Pre-validate the target before continuing.** An access problem caught at URL entry spares the user 5+ minutes of intent investment. Never HALT here: the canonical HALT stays in step 2 §1.
|
|
167
|
+
- **GitHub URL:** resolve `{githubProbeHelper}` from `{githubProbeProbeOrder}` (first existing path wins) and run `uv run {githubProbeHelper} repo --repo "{url}" --timeout 20`. It prints one JSON line, and tells a missing or logged-out `gh`, a repository that does not exist and one the account cannot read apart:
|
|
168
|
+
- `status: "ok"`: accept silently, a private repository that `gh` can read and a public one read without `gh` included.
|
|
169
|
+
- `status: "unavailable"`: warn with its `message`, which names the cause (`cause`) and the fix, and offer `[K] Keep anyway` or a corrected URL, which is probed the same way. On `[K]`, keep the URL: step 2 §1 reports the cause again if it still holds.
|
|
170
|
+
- No JSON (the helper is missing, or exits 1 or 2): continue silently; step 2 §1 checks the repository.
|
|
196
171
|
- **Local path:** verify the directory exists (`test -d {path}`). If not, warn `"Local path {path} does not exist."` and re-prompt.
|
|
197
172
|
- Optionally ask: "Are there any documentation URLs you'd like to include for supplemental context? (These will be fetched as T3 external references.)"
|
|
198
173
|
- If yes: collect doc URLs into `doc_urls`
|
|
199
174
|
|
|
200
|
-
**Source authority (this branch only
|
|
201
|
-
|
|
202
|
-
**Interactive only** — skip this prompt entirely when `{headless_mode}` is true; the GATE in §8 resolves source_authority headlessly via the detection branch documented there.
|
|
175
|
+
**Source authority (this branch only: docs-only forces `community` in §3.2):**
|
|
203
176
|
|
|
204
177
|
"**Are you the maintainer of this library, or creating a community skill?**"
|
|
205
178
|
- If maintainer: set `source_authority: "official"`
|
|
@@ -212,7 +185,7 @@ Default to `"community"` if user does not specify or skips.
|
|
|
212
185
|
|
|
213
186
|
Confirm the target.
|
|
214
187
|
|
|
215
|
-
**Draft-resume check
|
|
188
|
+
**Draft-resume check.** Now that the target is confirmed, and *before* the version prompt (§3b), intent (§4), or scope (§5) spend the user's time, offer to resume an in-progress draft that already covers this exact target, so a returning user re-types nothing. Keying on the target (not the not-yet-derived skill name) is what lets the offer fire this early:
|
|
216
189
|
|
|
217
190
|
1. **Cheap pre-filter** — list any draft file that mentions the confirmed target at all:
|
|
218
191
|
|
|
@@ -224,24 +197,33 @@ Confirm the target.
|
|
|
224
197
|
|
|
225
198
|
2. **Confirm each candidate on the target *field*, not free text.** Read the candidate draft's JSON and keep it only if its `target_repo` equals the confirmed target (source targets) or the target appears in its `doc_urls` (docs-only) — this rejects a draft that merely mentions the URL in its `intent` or `description`. Drop any candidate that has a finished `skill-brief.yaml` in the same directory (that brief is done; step 5's overwrite gate owns it).
|
|
226
199
|
|
|
227
|
-
3. If one or more live drafts survive, load `{draftCheckpointFile}` and follow Half 1 (Resume Check) against the most-recently-modified survivor; its directory basename is the candidate skill name. On `[Y]` resume, Half 1 restores every
|
|
200
|
+
3. If one or more live drafts survive, load `{draftCheckpointFile}` and follow Half 1 (Resume Check) against the most-recently-modified survivor; its directory basename is the candidate skill name. On `[Y]` resume, Half 1 restores every answer the draft holds and jumps straight to §8, or to step 4 when the draft holds the scope too: **§3b, §4, §5, §6, §7, and §7b are all skipped**. Otherwise (no surviving draft, or every match already has a finished brief beside it) skip the load and continue to §3b.
|
|
228
201
|
|
|
229
202
|
### 3b. Gather Target Version
|
|
230
203
|
|
|
231
|
-
This step only collects `target_version` and
|
|
204
|
+
This step only collects `target_version` and checks its shape: auto-detection runs in step 2 and precedence/invariant resolution lands in step 5's writer script. The canonical precedence rules live in `references/version-resolution.md`; load it from step 2 / step 5 only when the relevant section needs it.
|
|
232
205
|
|
|
233
|
-
|
|
206
|
+
When §3.1 pre-filled no version, ask:
|
|
234
207
|
|
|
235
208
|
"**Are you targeting a specific version of this library?**
|
|
236
209
|
(Leave blank to auto-detect from source)"
|
|
237
210
|
|
|
238
211
|
{If source_type is "docs-only":}
|
|
239
|
-
"Since this is a docs-only skill with no source code, specifying the version is recommended
|
|
212
|
+
"Since this is a docs-only skill with no source code, specifying the version is recommended: otherwise it defaults to 1.0.0."
|
|
240
213
|
|
|
241
|
-
Wait for user response.
|
|
214
|
+
Wait for user response. **If blank:** proceed without `target_version`: version will be auto-detected in step 02.
|
|
242
215
|
|
|
243
|
-
**
|
|
244
|
-
|
|
216
|
+
**Check the version before you keep it.** For a version typed here, or one §3.1 pre-filled (from parse-target or from the free-form answer), resolve `{validateBriefInputsHelper}` from `{validateBriefInputsProbeOrder}` (first existing path wins; HALT if no candidate exists) and run its field-only check, with the version written as a JSON string:
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
uv run {validateBriefInputsHelper} --only target_version <<'SKF_JSON'
|
|
220
|
+
{"target_version": "<the version>"}
|
|
221
|
+
SKF_JSON
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
- **Exit 0** (`valid: true`): store it as `target_version` and set `version` to it. Acknowledge a pre-filled one ("I noted you're targeting v4.0.0") instead of asking.
|
|
225
|
+
- **Exit 1** (`valid: false`): keep nothing. Ask again, naming the value with its first error's `message`: `"{message}, or leave blank to auto-detect."` (for `react@18`, `'18' is a major version only`). Check the answer the same way; blank proceeds without `target_version`.
|
|
226
|
+
- **Exit 2** (the payload is not valid JSON, such as a quote left unescaped): write it again and re-run the check.
|
|
245
227
|
|
|
246
228
|
{If target_version was set AND doc_urls are being collected (either docs-only primary or supplemental):}
|
|
247
229
|
|
|
@@ -252,7 +234,7 @@ Wait for user response.
|
|
|
252
234
|
|
|
253
235
|
### 4. Gather User Intent
|
|
254
236
|
|
|
255
|
-
**First-timer rail
|
|
237
|
+
**First-timer rail.** Before the intent prompt, check whether `{forge_data_folder}/` contains any prior briefs:
|
|
256
238
|
|
|
257
239
|
```bash
|
|
258
240
|
find "{forge_data_folder}" -maxdepth 2 -name "skill-brief.yaml" -print -quit
|
|
@@ -298,36 +280,14 @@ This will be used for the output directory and file naming. Want to use this nam
|
|
|
298
280
|
|
|
299
281
|
Wait for confirmation or alternative.
|
|
300
282
|
|
|
301
|
-
**Collision check
|
|
302
|
-
|
|
303
|
-
- Interactive: generate 1–3 non-colliding candidate alternates by scanning sibling directories under `{forge_data_folder}/`. Apply each rule that fires; skip rules whose precondition isn't met:
|
|
304
|
-
1. `{name}-v{N}` where `N` is the smallest positive integer that doesn't collide (e.g. `{name}-v2`, `{name}-v3`) — always applies
|
|
305
|
-
2. `{name}-{target_version}` if `target_version` is set and the suffix wouldn't collide (e.g. `marked-1.2.3`)
|
|
306
|
-
3. `{name}-{source_authority}` if `source_authority` is not `community` (e.g. `marked-internal` for an internal fork)
|
|
307
|
-
|
|
308
|
-
Number the surviving alternates `[1] [2] [3]…` in the order produced (1 alternate for a community-authority brief with no `target_version`; 2–3 otherwise). Then present:
|
|
309
|
-
|
|
310
|
-
```
|
|
311
|
-
**Heads up — a brief for `{name}` already exists at `{path}`.**
|
|
312
|
-
|
|
313
|
-
Suggested alternates (none collide):
|
|
314
|
-
[1] {alternate-1}
|
|
315
|
-
{if a second alternate was produced:} [2] {alternate-2}
|
|
316
|
-
{if a third alternate was produced:} [3] {alternate-3}
|
|
317
|
-
|
|
318
|
-
Pick a number to use that name, type a different name, or press Enter to keep `{name}` and let step 5 §2b handle the overwrite prompt.
|
|
319
|
-
```
|
|
320
|
-
|
|
321
|
-
On a numbered choice, replace `{name}` with the chosen alternate. On Enter, fall through to step 5's overwrite gate. On any other input, treat as a new candidate name and re-run the collision check against it.
|
|
322
|
-
|
|
323
|
-
- Headless: log `"warn: skill name '{name}' collides with existing brief at {path}"` and proceed; the existing-brief overwrite policy in step 5 §2b is the canonical gate (HALT with `overwrite-cancelled` unless `force` was supplied).
|
|
283
|
+
**Collision check.** If `{forge_data_folder}/{name}/skill-brief.yaml` already exists, say so and offer up to three alternates that do not collide, numbered: `{name}-v{N}` with the smallest free `N`, `{name}-{target_version}` when `target_version` is set, and `{name}-{source_authority}` when the authority is not `community`. A number takes that name; a typed name is checked the same way; Enter keeps `{name}` and leaves the overwrite question to step 5 §2b.
|
|
324
284
|
|
|
325
|
-
**Portfolio-similarity check.** When
|
|
326
|
-
|
|
327
|
-
(The resume-a-draft offer for a returning user fires earlier, right after the target is confirmed in §3 — see the §3 "Draft-resume check" — so the intent, scope, and description a draft holds are spared *before* they get re-gathered here.)
|
|
285
|
+
**Portfolio-similarity check.** When forge tier is `Deep` AND `tools.qmd` is true in `forge-tier.yaml`, load `{portfolioSimilarityCheckFile}` and follow the procedure there to catch semantic near-duplicates that exact-name collision misses. Otherwise skip the load: the check does not run.
|
|
328
286
|
|
|
329
287
|
### 7. Summarize Gathered Intent
|
|
330
288
|
|
|
289
|
+
Compose the §7b description first, then show this summary, the description and the §8 menu in one message, and wait once:
|
|
290
|
+
|
|
331
291
|
"**Here's what I've captured:**
|
|
332
292
|
|
|
333
293
|
- **Target:** {repo URL or path}
|
|
@@ -340,84 +300,29 @@ Wait for confirmation or alternative.
|
|
|
340
300
|
- **Target version:** {target_version} (user-specified)
|
|
341
301
|
{If doc_urls collected:}
|
|
342
302
|
- **Doc URLs:** {count} supplemental documentation URLs
|
|
343
|
-
- **Forge tier:** {tier}
|
|
344
|
-
|
|
345
|
-
Ready to analyze the target repository?"
|
|
346
|
-
|
|
347
|
-
**Draft checkpoint.** When the flow is interactive, load `{draftCheckpointFile}` (or reuse it if already loaded for the §3 resume check) and follow Half 2 (Checkpoint Write) to persist the captured state atomically. Headless mode skips this — the run completes in a single invocation, no resume is meaningful.
|
|
303
|
+
- **Forge tier:** {tier}"
|
|
348
304
|
|
|
349
305
|
### 7b. Synthesize Skill Description
|
|
350
306
|
|
|
351
|
-
The schema's `description` field is 1-3 sentences and surfaces in skill registries
|
|
352
|
-
|
|
353
|
-
Compose a candidate 1-3 sentence description from the gathered material. **Write like a human library maintainer would** — what does an agent get from this skill, and when should it route here? Two facts must come through (what the skill is, when to use it); everything else is voice. Resist filling in the same skeleton every time.
|
|
354
|
-
|
|
355
|
-
Load `{descriptionVoiceExamplesPath}` for the five voice examples (range of acceptable leads and structures) and the "do not template-stamp" guidance, then compose in that spirit. The asset documents what "in that spirit" means; the gathered material to draw on is the target repo, the user's intent, the version if set, and any scope hints.
|
|
307
|
+
The schema's `description` field is 1-3 sentences and surfaces in skill registries, so compose it here, while the intent is fresh. Load `{descriptionVoiceExamplesPath}` and compose a candidate in that spirit from the target repo, the user's intent, the version if set and any scope hints, in `{document_output_language}`, the language of the text the brief persists.
|
|
356
308
|
|
|
357
309
|
**Whatever lead you choose, the description must contain a literal `Use when` clause somewhere** — validators test for that exact phrase, so alternatives like "Triggers on…" or "Reach for this when…" do not satisfy it on their own. The clause need not lead: a descriptive opener followed by `Use when …` is both good voice and validator-clean.
|
|
358
310
|
|
|
359
|
-
Present:
|
|
311
|
+
Present it under the §7 summary:
|
|
360
312
|
|
|
361
313
|
"**Proposed skill description:**
|
|
362
314
|
|
|
363
315
|
> {synthesized description}
|
|
364
316
|
|
|
365
|
-
This is the text agents read when deciding whether to route to your skill
|
|
366
|
-
|
|
367
|
-
Wait for user confirmation or alternative.
|
|
368
|
-
|
|
369
|
-
**Soft sentence-count check (interactive only).** Before storing the accepted text, count terminal sentence punctuation (`.`, `!`, `?` followed by whitespace or end-of-string) — abbreviations like `e.g.` will inflate the count slightly but the check is a soft nudge, not a HALT. If the count exceeds 3, present:
|
|
370
|
-
|
|
371
|
-
"**Heads up — that description reads as ~{N} sentences.** The conventional norm is 1-3 (it surfaces in registry rows alongside other skills, where length crowds out the trigger phrase). Tighten now, or accept as-is?"
|
|
372
|
-
|
|
373
|
-
On `tighten` or a fresh edit: re-prompt for the description. On `accept` or any non-edit response: store the accepted text and proceed. Counts of 1-3 store silently.
|
|
374
|
-
|
|
375
|
-
Store the accepted text as the brief's `description` field. The same field is re-presented in step 4 §3 for a final review pass — refinements there flow back to this value.
|
|
376
|
-
|
|
377
|
-
**Headless:** if the `intent` argument was supplied, load `{descriptionVoiceExamplesPath}` and run the same synthesis against it (in `{document_output_language}`), then store the result. If `intent` was not supplied, fall back in priority order:
|
|
378
|
-
|
|
379
|
-
1. **GitHub repo description** — when `target_repo` is a GitHub URL, fetch `gh api repos/{owner}/{repo} --jq .description` (5-second timeout). If a non-empty description comes back, load `{descriptionVoiceExamplesPath}` and synthesize using the GitHub description as the seed in place of `intent`. Write the synthesized description in `{document_output_language}` regardless of the seed's language (the seed may be in any language; the output's language is dictated by the workflow's document-output configuration). Log `"info: description seeded from GitHub repo description"`. (The full `gh api repos` response is fetched again in step 2 §1; this lightweight `--jq .description` call only retrieves the one field.)
|
|
380
|
-
2. **Generic stub** — when no GitHub description is available (local-path target, GitHub repo with empty description, or `gh api` fails): derive from `target_repo` + `skill_name` (`"Use the {skill_name} skill to work with code or content from {target_repo}."`) — the generic fallback does not need the asset — and log `"warn: description synthesized without intent or repo description — narrow registry text."`
|
|
317
|
+
This is the text agents read when deciding whether to route to your skill, in a registry row beside dozens of others: a specific 'Use when' trigger helps agents match real requests. Edit it, replace it, or accept it with [C]."
|
|
381
318
|
|
|
382
319
|
### 8. Present MENU OPTIONS
|
|
383
320
|
|
|
384
|
-
|
|
321
|
+
End the §7 message with: "**Select:** edit the description or any field above · [C] Continue to Target Analysis · [X] Cancel and exit"
|
|
385
322
|
|
|
386
323
|
#### Menu Handling Logic:
|
|
387
324
|
|
|
388
|
-
- IF C: Load, read entire file, then execute {nextStepFile}
|
|
389
|
-
- IF X: Treat as user-cancellation.
|
|
325
|
+
- IF C: when the description runs past three sentences, nudge once to tighten it (a long one crowds the trigger out of its registry row) and keep what the user settles on. Store it as the brief's `description`: step 4 §3 shows it again for a final pass. Load `{draftCheckpointFile}` (or reuse it from the §3 resume check) and follow Half 2 (Checkpoint Write), so the draft holds the gathered intent and the accepted description. Then load, read entire file, then execute {nextStepFile}
|
|
326
|
+
- IF X: Treat as user-cancellation. Remove the run folder (`case "{run_dir}" in "{project-root}/_bmad-output/.skf-run/skf-brief-skill-"*) rm -rf "{run_dir}" ;; esac`), display `"Cancelled: no brief was written."` and HALT (exit code 6, `halt_reason: "user-cancelled"`). No brief was written; a draft saved earlier stays for a later resume.
|
|
327
|
+
- IF an edit to the description or another field: apply it (a new name runs the §6 collision check again), show the summary and the description again, then [Redisplay Menu Options](#8-present-menu-options)
|
|
390
328
|
- IF Any other: Help user, then [Redisplay Menu Options](#8-present-menu-options)
|
|
391
|
-
|
|
392
|
-
#### Execution rules:
|
|
393
|
-
|
|
394
|
-
- **Resolve `{validateBriefInputsHelper}`** from `{validateBriefInputsProbeOrder}`; first existing path wins. HALT if no candidate exists.
|
|
395
|
-
|
|
396
|
-
- **GATE [default: use args]** — If `{headless_mode}`, consume pre-supplied arguments and auto-proceed. The full argument set (required/optional, defaults, halt codes, enum values) is documented in `{headlessArgsFile}` — load it now if you need to look up a specific argument. Validation is delegated to `{validateBriefInputsHelper}`; the table is the canonical operator-facing documentation, the script enforces it.
|
|
397
|
-
|
|
398
|
-
**Preset merge (before validation).** Skip this merge entirely when a `from_brief` argument is present — presets seed a *derived* brief and have no meaning on the ratify route below. Otherwise: if the headless args include a `preset` field, load `{sidecar_path}/brief-presets/{preset}.yaml` and merge its contents as defaults — explicit args override preset values, key by key. The preset file is YAML; if it does not exist, log `"warn: preset '{name}' not found at {path} — proceeding without preset"` and continue (do not HALT). If it parses but contains unknown fields, log per-field warnings and pass through unchanged (the validator's KNOWN_FIELDS check will catch any that survive). Drop the `preset` key itself from the merged dict before passing to the validator (it is consumed at this level and is not a brief field).
|
|
399
|
-
|
|
400
|
-
**Delegate validation to `{validateBriefInputsHelper}`** instead of reasoning through the table rules in prose:
|
|
401
|
-
|
|
402
|
-
```bash
|
|
403
|
-
echo '<headless-args-as-json>' | uv run {validateBriefInputsHelper}
|
|
404
|
-
```
|
|
405
|
-
|
|
406
|
-
The script returns a JSON envelope: `{valid, errors[], warnings[], normalized, halt_reason}`. Apply the result deterministically:
|
|
407
|
-
|
|
408
|
-
- **`valid: false`** — emit the error envelope per **step 5 §4b** with the script's `halt_reason` (`"input-missing"` for absent required args / docs-only without doc_urls; `"input-invalid"` for enum violations, malformed semver, malformed kebab-case skill_name). Surface `errors[]` to the operator log so the failure is debuggable. HALT.
|
|
409
|
-
- **`valid: true`** — consume the `normalized` object as the source of truth (it has defaults applied per the table). Surface `warnings[]` to the operator log but do not HALT. Auto-proceed.
|
|
410
|
-
|
|
411
|
-
The script's `KNOWN_FIELDS` set must stay in sync with the table in `{headlessArgsFile}`.
|
|
412
|
-
|
|
413
|
-
**Ratify route — `from_brief` present.** After a `valid: true` result, branch on `normalized.from_brief`. When it is set, this run ratifies a pre-authored brief instead of deriving one — the headless mirror of the interactive §3.1a `[R]` branch. Take this route *before* the source-authority detection and analyze-target routing below (both belong to the derive path and do not apply here):
|
|
414
|
-
|
|
415
|
-
1. **Resolve the brief path.** If `normalized.from_brief` ends in `skill-brief.yaml`, that is the path; otherwise treat it as a directory and use `<from_brief>/skill-brief.yaml`.
|
|
416
|
-
2. **Schema-validate.** Resolve `{validateBriefSchemaHelper}` from `{validateBriefSchemaProbeOrder}` (first existing path wins; HALT if no candidate exists), then run `uv run {validateBriefSchemaHelper} <resolved-brief-path>`. The script returns `{valid, errors[], warnings[], halt_reason, brief}`. Apply it — and note that, unlike the interactive §3.1a branch (which re-prompts because the operator might have a corrected path to offer), headless has no second chance, so an unusable brief is terminal:
|
|
417
|
-
- **`valid: false`** with `halt_reason: "brief-missing"` (path absent / unreadable) — emit the error envelope per **step 5 §4b** with `halt_reason: "input-missing"`, surface `errors[]` to the operator log, HALT (exit 2).
|
|
418
|
-
- **`valid: false`** with any other `halt_reason` (`brief-malformed` / `brief-invalid`) — emit the error envelope with `halt_reason: "input-invalid"`, surface `errors[]`, HALT (exit 2).
|
|
419
|
-
- **`valid: true`** — surface any non-empty `warnings[]` to the operator log and proceed with the parsed `brief` payload.
|
|
420
|
-
3. **Hydrate and route.** Store `ratify_mode: true` and `ratify_source_path: <resolved-brief-path>` in workflow context, then hydrate the brief context variables from the parsed `brief` payload exactly as the §3.1a `[R]` branch does (the identical field-mapping list: `name`/`version`/`target_version`, `target_ref`/`source_ref`, `source_repo`/`source_type`/`source_authority`/`doc_urls`, `language`/`description`/`forge_tier`, `created`/`created_by`, `scope.type`/`scope.include`/`scope.exclude`/`scope.tier_a_include`/`scope.notes`/`scope.rationale`/`scope.amendments`, `scripts_intent`/`assets_intent` — preserving `target_ref`/`source_ref`/`tier_a_include`/`amendments` verbatim). Load, read entirely, and execute `{ratifyTargetFile}` — bypassing step 2 (analyze-target) and step 3 (scope-definition), both of which would re-derive fields already on disk. The forward chain resumes at step 4 (confirm-brief), which auto-confirms `[C]` under headless and proceeds to step 5's write (the step 5 §2b ratify branch auto-overwrites in place). Do **not** run the source-authority detection or the `[C] → {nextStepFile}` routing below — they belong to the derive path.
|
|
421
|
-
|
|
422
|
-
**Headless source-authority detection (derive route only — no `from_brief`).** After consuming `normalized`, if `source_authority` is absent AND `source_type=source` AND `target_repo` is a GitHub URL, load `{headlessSourceAuthorityDetectionFile}` and follow the procedure there. Otherwise (precondition unmet, value already supplied, docs-only, or local-path) skip the load — `community` is the implicit default for the unmet branches.
|
|
423
|
-
|
|
@@ -1,23 +1,91 @@
|
|
|
1
|
-
|
|
1
|
+
---
|
|
2
|
+
nextStepFile: 'analyze-target.md'
|
|
3
|
+
ratifyFile: 'references/gather-intent-ratify.md'
|
|
4
|
+
validateBriefInputsProbeOrder:
|
|
5
|
+
- '{project-root}/_bmad/skf/shared/scripts/skf-validate-brief-inputs.py'
|
|
6
|
+
- '{project-root}/src/shared/scripts/skf-validate-brief-inputs.py'
|
|
7
|
+
---
|
|
2
8
|
|
|
3
|
-
|
|
9
|
+
<!-- Config: communicate in {communication_language}. -->
|
|
10
|
+
|
|
11
|
+
# Step 1 (Headless): Input Gate
|
|
12
|
+
|
|
13
|
+
Step 1 §1b loads this file when `{headless_mode}` is true outside `[auto]` mode, in place of step 1's interactive §2 to §8. The arguments are merged and validated here, before any section uses them, and every later value the run takes from an argument comes from the validated `normalized` object.
|
|
14
|
+
|
|
15
|
+
## Arguments
|
|
16
|
+
|
|
17
|
+
`{validateBriefInputsHelper}` enforces this table.
|
|
4
18
|
|
|
5
19
|
| Argument | Required | Default | Notes |
|
|
6
20
|
|----------|----------|---------|-------|
|
|
7
|
-
| `target_repo` | yes¹ |
|
|
8
|
-
| `skill_name` | yes¹ |
|
|
9
|
-
| `from_brief` | no |
|
|
21
|
+
| `target_repo` | yes¹ | none | HALT (exit 2, `halt_reason: "input-missing"`) if absent. ¹Not required when `from_brief` is supplied: the ratify route takes the target from the brief and ignores `target_repo` (with a warning) if also passed |
|
|
22
|
+
| `skill_name` | yes¹ | none | HALT (exit 2, `halt_reason: "input-missing"`) if absent; HALT (exit 2, `halt_reason: "input-invalid"`) if not kebab-case. ¹Not required when `from_brief` is supplied: the ratify route takes the name from the brief and ignores `skill_name` (with a warning) if also passed |
|
|
23
|
+
| `from_brief` | no | none | Path to a pre-authored `skill-brief.yaml` (a file, or a folder holding one) to **ratify** instead of deriving a brief (§3): schema-validate it, skip steps 2 and 3, write through the canonical writer to `{forge_data_folder}/{name}/skill-brief.yaml` (no `force` needed when the brief already lives at that path; otherwise `force` applies as on the derive route). `target_repo` and `skill_name` become optional and are ignored if also passed. HALT (exit 2, `halt_reason: "input-missing"`) if the value is empty or the path does not exist; HALT (exit 2, `halt_reason: "input-invalid"`) if the brief fails schema validation |
|
|
10
24
|
| `source_type` | no | `source` | If `docs-only`, `doc_urls` becomes required |
|
|
11
|
-
| `doc_urls` | conditional |
|
|
12
|
-
| `source_authority` | no | detected | `official` / `community` / `internal`. When absent and `target_repo` is a GitHub URL,
|
|
13
|
-
| `target_version` | no |
|
|
14
|
-
| `scope_hint` | no |
|
|
15
|
-
| `language_hint` | no |
|
|
16
|
-
| `scope_type` | no | heuristic | `full-library` / `specific-modules` / `public-api` / `component-library` / `reference-app` / `docs-only`. When absent and `source_type=source`, step 3 §2c
|
|
17
|
-
| `include` | no |
|
|
18
|
-
| `exclude` | no |
|
|
25
|
+
| `doc_urls` | conditional | none | Required when `source_type=docs-only` (HALT exit 2, `halt_reason: "input-missing"` if empty). List of `url` or `url,label` |
|
|
26
|
+
| `source_authority` | no | detected | `official` / `community` / `internal`. When absent and `target_repo` is a GitHub URL, §4 compares the `gh api user` login with the URL owner: a match is `official`, anything else `community`. A local path, or a failed `gh api user`, is `community`. Forced to `community` when `source_type=docs-only` |
|
|
27
|
+
| `target_version` | no | none | Auto-detected in step 2 if absent. Full X.Y.Z semver required (HALT exit 2, `halt_reason: "input-invalid"` on partial forms like `1`, `1.2`, `v2`) |
|
|
28
|
+
| `scope_hint` | no | none | Free-text scope steering, classified with `intent` into the scope-type signals at step 3 §2c |
|
|
29
|
+
| `language_hint` | no | none | Overrides language detection, consumed at step 2 §3: the detector still runs for the informational Detected-language line, but the hint becomes the confirmed language and the step 3 §4 low-confidence override does not fire |
|
|
30
|
+
| `scope_type` | no | heuristic | `full-library` / `specific-modules` / `public-api` / `component-library` / `reference-app` / `docs-only`. When absent and `source_type=source`, step 3 §2c recommends one from the `intent` and `scope_hint` arguments and step 2's analysis; `source_type=docs-only` always short-circuits to `docs-only` |
|
|
31
|
+
| `include` | no | the scope type's default | Comma-separated globs, used by step 3 §3 as given. When absent, step 3 §3 takes the scope type's headless default and names it in the envelope's `warnings`; a `scope_type=reference-app` argument halts here instead (§2), and a `reference-app` type the recommender picked, or a `specific-modules` `scope_type` argument whose `intent` and `scope_hint` name no module, halts at step 3 §3, each with `input-missing` (exit 2) |
|
|
32
|
+
| `exclude` | no | see Notes | Comma-separated globs. With an `include`, step 3 §3 uses them as given; without one, it adds them to the exclusions of the scope type's headless default and names them in its `warn:` line. When absent: none beside a given `include`, else the default's exclusions |
|
|
19
33
|
| `scripts_intent` | no | `detect` | `detect` / `none` / free-text |
|
|
20
34
|
| `assets_intent` | no | `detect` | `detect` / `none` / free-text |
|
|
21
|
-
| `intent` | no |
|
|
22
|
-
| `force` | no |
|
|
23
|
-
| `preset` | no |
|
|
35
|
+
| `intent` | no | none | Free text the description is composed from (§5) |
|
|
36
|
+
| `force` | no | none | Overwrite an existing brief without prompting (consumed in step 5 §2b) |
|
|
37
|
+
| `preset` | no | none | Name of a preset YAML file at `{sidecar_path}/brief-presets/{preset}.yaml`, merged as defaults by §1: explicit arguments override preset values. Useful for repeated patterns (e.g. briefing 5 SaaS API SDKs with the same `source_authority`/`scope_type`/`scripts_intent`). The preset file is YAML holding any subset of the arguments above; unknown fields are ignored with a warning |
|
|
38
|
+
|
|
39
|
+
## Sequence
|
|
40
|
+
|
|
41
|
+
**GATE [default: use args]**: consume the arguments in the order below and auto-proceed; nothing here prompts.
|
|
42
|
+
|
|
43
|
+
### 1. Preset Merge
|
|
44
|
+
|
|
45
|
+
Skip this merge when a `from_brief` argument is present: a preset seeds a derived brief and means nothing on the ratify route. Otherwise, when the arguments hold a `preset`, load `{sidecar_path}/brief-presets/{preset}.yaml` and merge its keys as defaults, an explicit argument winning key by key. When the file does not exist, log `"warn: preset '{name}' not found at {path}; proceeding without preset"`, add it to `workflow_warnings[]` and continue. An unknown field the preset holds passes through: the validator warns about it in §2. Drop the `preset` key itself before validation: it is not a brief field.
|
|
46
|
+
|
|
47
|
+
### 2. Validate the Arguments
|
|
48
|
+
|
|
49
|
+
Resolve `{validateBriefInputsHelper}` from `{validateBriefInputsProbeOrder}` (first existing path wins; HALT if no candidate exists). Stage the merged arguments as one JSON object in the run folder, then run the helper on the file:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
cat > "{run_dir}/headless-args.json" <<'SKF_JSON'
|
|
53
|
+
<the merged headless arguments, as one JSON object>
|
|
54
|
+
SKF_JSON
|
|
55
|
+
uv run {validateBriefInputsHelper} < "{run_dir}/headless-args.json"
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
It returns `{valid, errors[], warnings[], normalized, halt_reason}`:
|
|
59
|
+
|
|
60
|
+
- **`valid: false`**: emit the halt envelope first, `uv run {emitBriefEnvelopeHelper} emit --target stderr` with the script's `halt_reason` (`input-missing` for an absent required argument, docs-only without `doc_urls`, or a `reference-app` `scope_type` without `include`; `input-invalid` for an enum violation, malformed semver or a `skill_name` that is not kebab-case) (SKILL.md Halt Contract), surface `errors[]` to the operator log, then HALT (exit code 2).
|
|
61
|
+
- **`valid: true`**: `normalized`, the arguments with the table's defaults applied, is the source of truth for the rest of the run. Log each `warnings[]` entry and add it to `workflow_warnings[]` as `<field>: <message>`.
|
|
62
|
+
|
|
63
|
+
### 3. Ratify Route (`from_brief`)
|
|
64
|
+
|
|
65
|
+
When `normalized.from_brief` is set, this run ratifies a pre-authored brief instead of deriving one: load, read entirely, and execute `{ratifyFile}` with that path. It validates the brief and hands the run to step 4, so nothing below runs.
|
|
66
|
+
|
|
67
|
+
### 4. Source Authority
|
|
68
|
+
|
|
69
|
+
When `normalized.source_authority` is absent, `source_type` is `source` and `target_repo` is a GitHub URL (`https://github.com/<owner>/<repo>`), probe the operator's login:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
gh api user --jq .login
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Compare it with `<owner>`, both lower-cased (GitHub owners match case-insensitively, but the API keeps the case): a match sets `source_authority: "official"`, another login `community`. When the call fails (`gh` missing or not logged in, no network), set `community`, log `"warn: source-authority detection skipped: gh api user failed"` and add it to `workflow_warnings[]`. In every other case `source_authority` is the value given, else `community`.
|
|
76
|
+
|
|
77
|
+
### 5. Derive the Brief Fields
|
|
78
|
+
|
|
79
|
+
From `normalized`, before step 2 runs:
|
|
80
|
+
|
|
81
|
+
- **Version:** a `target_version` sets `version` too; without one, step 2 detects the version.
|
|
82
|
+
- **Doc URLs:** HEAD-check every `doc_urls` entry in parallel, all calls in one message, each `curl -sI --max-time 5 {url}`. Keep a URL that fails, log `"warn: could not reach {url} ({status or error})"` and add it to `workflow_warnings[]`: the failure shows now instead of in skf-create-skill.
|
|
83
|
+
- **Name collision:** when `{forge_data_folder}/{skill_name}/skill-brief.yaml` exists, log `"warn: skill name '{skill_name}' collides with existing brief at {path}"`, add it to `workflow_warnings[]` and proceed: step 5 §2b halts with `overwrite-cancelled` unless `force` was supplied.
|
|
84
|
+
- **Description:** compose 1-3 sentences in `{document_output_language}` that say what the skill is and contain a literal `Use when` clause, in the spirit of `{descriptionVoiceExamplesPath}` (load it), from the first seed available:
|
|
85
|
+
1. `intent`.
|
|
86
|
+
2. The repository's own description, when `target_repo` is a GitHub URL: `gh api repos/{owner}/{repo} --jq .description` (5-second timeout). When it is not empty, log `"info: description seeded from GitHub repo description"`.
|
|
87
|
+
3. None (a local path, an empty description, a failed call): write `"Use the {skill_name} skill to work with code or content from {target_repo}."`, log `"warn: description synthesized without intent or repo description: narrow registry text."` and add it to `workflow_warnings[]`.
|
|
88
|
+
|
|
89
|
+
### 6. Continue
|
|
90
|
+
|
|
91
|
+
Load, read entire file, then execute `{nextStepFile}` (step 2).
|
|
@@ -9,4 +9,4 @@ nextStepFile: 'shared/health-check.md'
|
|
|
9
9
|
|
|
10
10
|
# Step 6: Workflow Health Check
|
|
11
11
|
|
|
12
|
-
This is the terminal step of brief-skill. Load `{nextStepFile}`, read it fully, then execute it
|
|
12
|
+
This is the terminal step of brief-skill. Load `{nextStepFile}`, read it fully, then execute it: do nothing else here (no user-facing reports, file writes, or result contracts; those were step 5). In a headless or `[auto]` run, the shared health check displays the `{result_envelope_line}` that write-brief.md §4b or step-auto-validate.md §3 bound as the run's last line. After `{nextStepFile}` returns control, the brief-skill workflow is fully complete: do not re-enter step 5 or step 6, load any further step file, or loop back into the workflow.
|