bmad-module-skill-forge 2.1.0 → 3.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 +51 -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 +135 -18
- package/docs/verifying-a-skill.md +83 -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 +2 -2
- 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 +32 -12
- package/src/shared/references/description-guard-protocol.md +14 -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 +30 -14
- package/src/shared/scripts/skf-build-change-manifest.py +2203 -24
- 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 +694 -0
- package/src/shared/scripts/skf-comention-pairs.py +1333 -123
- package/src/shared/scripts/skf-compare-file-hashes.py +1 -1
- 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 +3 -2
- package/src/shared/scripts/skf-description-guard.py +117 -9
- package/src/shared/scripts/skf-detect-docs.py +892 -4
- 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 +1 -1
- package/src/shared/scripts/skf-detect-tools.py +215 -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 +4084 -85
- package/src/shared/scripts/skf-extraction-inventory.py +746 -0
- package/src/shared/scripts/skf-extraction-snapshot.py +539 -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 +444 -203
- package/src/shared/scripts/skf-github-fetch.py +365 -0
- package/src/shared/scripts/skf-github-probe.py +661 -0
- package/src/shared/scripts/skf-hash-content.py +166 -12
- package/src/shared/scripts/skf-language-corpora.py +1 -1
- package/src/shared/scripts/skf-load-provenance.py +159 -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 +1 -1
- 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 +161 -27
- package/src/shared/scripts/skf-promote-staged.py +430 -0
- package/src/shared/scripts/skf-provenance-gap-dispatch.py +39 -7
- 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 +128 -13
- 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 +670 -119
- package/src/shared/scripts/skf-tessl-review.py +706 -0
- package/src/shared/scripts/skf-update-active-symlink.py +5 -5
- 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 +4 -1
- 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 +2919 -54
- package/src/shared/scripts/skf-write-skill-brief.py +367 -19
- 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 +27 -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/init.md +148 -210
- package/src/skf-audit-skill/references/re-index.md +96 -108
- package/src/skf-audit-skill/references/report.md +96 -33
- package/src/skf-audit-skill/references/semantic-diff.md +27 -18
- 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 +75 -90
- package/src/skf-audit-skill/references/upstream-checkout.md +77 -0
- package/src/skf-audit-skill/scripts/render-drift-tables.py +685 -0
- package/src/skf-brief-skill/SKILL.md +36 -25
- package/src/skf-brief-skill/assets/description-voice-examples.md +1 -1
- package/src/skf-brief-skill/assets/scope-templates.md +17 -24
- package/src/skf-brief-skill/assets/skill-brief-schema.md +29 -15
- package/src/skf-brief-skill/customize.toml +48 -20
- package/src/skf-brief-skill/references/analyze-target.md +142 -125
- package/src/skf-brief-skill/references/confirm-brief.md +10 -8
- 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 +24 -80
- package/src/skf-brief-skill/references/step-auto-validate.md +20 -121
- package/src/skf-brief-skill/references/version-resolution.md +3 -2
- 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 +6 -5
- package/src/skf-create-skill/assets/skill-sections.md +17 -6
- package/src/skf-create-skill/customize.toml +20 -8
- package/src/skf-create-skill/references/authoritative-files-protocol.md +41 -42
- 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 +107 -29
- 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 +20 -0
- package/src/skf-create-skill/references/extract.md +200 -161
- package/src/skf-create-skill/references/extraction-patterns-by-hand.md +141 -0
- package/src/skf-create-skill/references/extraction-patterns-tracing.md +2 -2
- package/src/skf-create-skill/references/extraction-patterns.md +584 -196
- 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 +28 -59
- package/src/skf-create-skill/references/source-resolution-protocols.md +49 -155
- 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 +55 -18
- package/src/skf-create-skill/references/sub/ccc-discover.md +18 -7
- package/src/skf-create-skill/references/sub/fetch-docs.md +106 -57
- 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 +79 -146
- 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 +118 -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 -89
- package/src/skf-setup/references/detect-and-tier.md +58 -66
- 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 +217 -254
- 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 +163 -88
- 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 +162 -142
- package/src/skf-test-skill/references/score.md +80 -107
- package/src/skf-test-skill/references/scoring-rules.md +41 -155
- package/src/skf-test-skill/references/source-access-protocol.md +19 -92
- 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 +287 -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 +1381 -0
- package/src/skf-test-skill/scripts/hard-gate.py +203 -0
- package/src/skf-test-skill/scripts/load-coverage-inputs.py +760 -0
- package/src/skf-test-skill/scripts/locate-export-segments.py +835 -0
- package/src/skf-test-skill/scripts/reconcile-coverage.py +273 -79
- package/src/skf-test-skill/scripts/score-signatures.py +459 -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 +127 -52
- 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 +233 -222
- 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 +250 -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 +180 -47
- package/src/skf-update-skill/references/re-extract.md +53 -262
- package/src/skf-update-skill/references/report.md +100 -35
- package/src/skf-update-skill/references/write.md +331 -149
- package/src/skf-update-skill/scripts/skf-new-file-diff.py +29 -7
- 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 -645
- 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
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# /// script
|
|
3
|
+
# requires-python = ">=3.11"
|
|
4
|
+
# dependencies = []
|
|
5
|
+
# ///
|
|
6
|
+
"""Pick the previous feasibility report for skf-verify-stack (init.md §1).
|
|
7
|
+
|
|
8
|
+
The delta in synthesize.md §3 compares this run with an earlier report.
|
|
9
|
+
Which file that is has one correct answer, so it is decided here rather than in
|
|
10
|
+
prose:
|
|
11
|
+
|
|
12
|
+
* A given path (--provided) is used when it is a readable file, unless it is
|
|
13
|
+
one of the two files this run writes: this run's timestamped report
|
|
14
|
+
(outputFile) or the -latest copy (outputFileLatest). init.md §4 writes the
|
|
15
|
+
first before synthesize reads the previous report, so a run compared with
|
|
16
|
+
it would be compared with itself and report every finding unchanged.
|
|
17
|
+
report.md §1 copies the finished report over the second, so a report
|
|
18
|
+
compared with it would, from then on, name its own copy as its previous
|
|
19
|
+
report. Files are compared as files (os.path.samefile), not as path
|
|
20
|
+
strings, so a symlink or another spelling of the same file matches.
|
|
21
|
+
* With no given path, the newest finished report of this project in the
|
|
22
|
+
folder is picked: feasibility-report-<slug>-<YYYYMMDD-HHmmss>.md, newest by
|
|
23
|
+
that timestamp, whose frontmatter stepsCompleted lists `synthesize` and
|
|
24
|
+
which passes the feasibility-report check report.md §1 runs before it
|
|
25
|
+
publishes a report. A run that halted before synthesize left a partial
|
|
26
|
+
report, and one that halted at that check left a report no consumer reads:
|
|
27
|
+
neither is the baseline. This run's own file, the -latest copy and any file
|
|
28
|
+
that is one of the two are never picked, and neither is the report of
|
|
29
|
+
another project whose slug starts with this one (the timestamp must follow
|
|
30
|
+
the slug directly) or a report this process cannot read.
|
|
31
|
+
|
|
32
|
+
The slug is the producer's `projectSlug`, as `skf-validate-feasibility-report.py
|
|
33
|
+
--locate` returns it: this script applies no slug rule of its own. It reads
|
|
34
|
+
stepsCompleted with the frontmatter reader of that shared script, and checks a
|
|
35
|
+
report with its read_report(), the check report.md §1 runs. The script sits in
|
|
36
|
+
the shared scripts folder beside this skill's folder, so the rules that read the
|
|
37
|
+
list and check the report are the ones the report's consumers use.
|
|
38
|
+
|
|
39
|
+
CLI usage:
|
|
40
|
+
uv run skf-previous-report.py --folder <dir> --slug <slug> --timestamp <YYYYMMDD-HHmmss>
|
|
41
|
+
uv run skf-previous-report.py --folder <dir> --slug <slug> --timestamp <ts> --provided <path>
|
|
42
|
+
|
|
43
|
+
Output (stdout, one object):
|
|
44
|
+
{
|
|
45
|
+
"status": "provided" | "discovered" | "none" | "not-found" | "collision"
|
|
46
|
+
| "reader-missing",
|
|
47
|
+
"previousReport": "<path>" | null, # the report to compare against
|
|
48
|
+
"previousTimestamp": "<YYYYMMDD-HHmmss>" | null,
|
|
49
|
+
"provided": "<path as given>" | null,
|
|
50
|
+
"collidesWith": "<outputFile or outputFileLatest>" | null,
|
|
51
|
+
"outputFile": "<folder>/feasibility-report-<slug>-<timestamp>.md",
|
|
52
|
+
"outputFileLatest": "<folder>/feasibility-report-<slug>-latest.md"
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
provided the given path is a readable file and neither file this run
|
|
56
|
+
writes (it is used as given, finished or not: the delta names
|
|
57
|
+
a report it cannot compare)
|
|
58
|
+
discovered no path was given; previousReport is the newest finished
|
|
59
|
+
earlier report (it lists synthesize and passes the check)
|
|
60
|
+
none no path was given and the folder holds no finished earlier
|
|
61
|
+
report
|
|
62
|
+
not-found the given path is not a readable file (no previous report)
|
|
63
|
+
collision the given path is outputFile or outputFileLatest
|
|
64
|
+
reader-missing no path was given and the shared skf-validate-feasibility-report.py
|
|
65
|
+
is not in the shared scripts folder beside this skill's folder,
|
|
66
|
+
so no report can be read or checked; the output also carries
|
|
67
|
+
"error", naming the missing file
|
|
68
|
+
|
|
69
|
+
previousTimestamp is the timestamp in the previous report's file name, null
|
|
70
|
+
when there is no previous report or its name carries none.
|
|
71
|
+
|
|
72
|
+
Exit codes:
|
|
73
|
+
0 status provided, discovered, none or not-found
|
|
74
|
+
1 status collision: the caller halts (previous-report-collision)
|
|
75
|
+
2 usage error: a missing flag, a --timestamp not shaped YYYYMMDD-HHmmss, or
|
|
76
|
+
a --slug that is not a project slug (argparse prints the reason; no JSON)
|
|
77
|
+
3 status reader-missing: the caller halts (resolution-failure)
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
from __future__ import annotations
|
|
81
|
+
|
|
82
|
+
import argparse
|
|
83
|
+
import importlib.util
|
|
84
|
+
import json
|
|
85
|
+
import os
|
|
86
|
+
import re
|
|
87
|
+
import sys
|
|
88
|
+
from pathlib import Path
|
|
89
|
+
|
|
90
|
+
REPORT_NAME = "feasibility-report-{slug}-{suffix}.md"
|
|
91
|
+
LATEST_SUFFIX = "latest"
|
|
92
|
+
|
|
93
|
+
# A report is finished once synthesize.md §5 appended its step to this list.
|
|
94
|
+
STEPS_KEY = "stepsCompleted"
|
|
95
|
+
FINISHED_STEP = "synthesize"
|
|
96
|
+
|
|
97
|
+
# The shared feasibility-report reader. Installed (under _bmad/skf/ or an
|
|
98
|
+
# IDE's skills folder) and in a dev checkout (src/), shared/ sits beside
|
|
99
|
+
# this skill's folder.
|
|
100
|
+
SHARED_READER = (
|
|
101
|
+
Path(__file__).resolve().parent.parent.parent
|
|
102
|
+
/ "shared"
|
|
103
|
+
/ "scripts"
|
|
104
|
+
/ "skf-validate-feasibility-report.py"
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
# A run timestamp: UTC, to the second (SKILL.md On Activation §2).
|
|
108
|
+
_TIMESTAMP_RE = re.compile(r"^\d{8}-\d{6}$")
|
|
109
|
+
|
|
110
|
+
# What skf-validate-feasibility-report.py's slugify() can return: lowercase
|
|
111
|
+
# ASCII letters and digits in runs joined by single hyphens.
|
|
112
|
+
_SLUG_RE = re.compile(r"^[a-z0-9]+(?:-[a-z0-9]+)*$")
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _same_file(a: Path, b: Path) -> bool:
|
|
116
|
+
"""True when both paths exist and are one file."""
|
|
117
|
+
try:
|
|
118
|
+
return os.path.samefile(a, b)
|
|
119
|
+
except OSError:
|
|
120
|
+
return False # either path is missing or cannot be read
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def _readable_file(path: Path) -> bool:
|
|
124
|
+
"""True when path is a file this process can read.
|
|
125
|
+
|
|
126
|
+
Before Python 3.14, Path.is_file() raises for most OSErrors, such as a
|
|
127
|
+
PermissionError under a folder the user cannot search or an unreachable
|
|
128
|
+
network path; from 3.14 on it returns False. Either way the path is no
|
|
129
|
+
readable file.
|
|
130
|
+
"""
|
|
131
|
+
try:
|
|
132
|
+
return path.is_file() and os.access(path, os.R_OK)
|
|
133
|
+
except OSError:
|
|
134
|
+
return False
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def _timestamp_of(path: Path, slug: str) -> str | None:
|
|
138
|
+
"""The run timestamp in a report file name of this project, or None."""
|
|
139
|
+
m = re.fullmatch(rf"feasibility-report-{re.escape(slug)}-(\d{{8}}-\d{{6}})\.md", path.name)
|
|
140
|
+
return m.group(1) if m else None
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def load_reader():
|
|
144
|
+
"""Import the shared feasibility-report reader, or None when it is missing."""
|
|
145
|
+
try:
|
|
146
|
+
if not SHARED_READER.is_file():
|
|
147
|
+
return None
|
|
148
|
+
except OSError:
|
|
149
|
+
return None # a folder on the way that cannot be searched
|
|
150
|
+
spec = importlib.util.spec_from_file_location("skf_validate_feasibility_report", SHARED_READER)
|
|
151
|
+
if spec is None or spec.loader is None:
|
|
152
|
+
return None
|
|
153
|
+
module = importlib.util.module_from_spec(spec)
|
|
154
|
+
spec.loader.exec_module(module)
|
|
155
|
+
return module
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def _finished(path: Path, reader) -> bool:
|
|
159
|
+
"""True when the report's frontmatter stepsCompleted lists synthesize and
|
|
160
|
+
the report passes the shared reader's check (read_report), as report.md §1
|
|
161
|
+
requires before it publishes a report.
|
|
162
|
+
|
|
163
|
+
A report this process cannot read, whose frontmatter holds no such list,
|
|
164
|
+
or that fails the check is not finished.
|
|
165
|
+
"""
|
|
166
|
+
try:
|
|
167
|
+
# utf-8-sig drops a byte order mark, as the shared reader does.
|
|
168
|
+
content = path.read_text(encoding="utf-8-sig")
|
|
169
|
+
except (OSError, UnicodeDecodeError):
|
|
170
|
+
return False
|
|
171
|
+
steps = reader.frontmatter_list(content, STEPS_KEY)
|
|
172
|
+
if steps is None or FINISHED_STEP not in steps:
|
|
173
|
+
return False
|
|
174
|
+
_fields, ok = reader.read_report(content)
|
|
175
|
+
return ok
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def _earlier_reports(folder: Path, slug: str, timestamp: str, reader) -> list[tuple[str, Path]]:
|
|
179
|
+
"""(timestamp, path) of this project's finished timestamped reports, this run's excluded.
|
|
180
|
+
|
|
181
|
+
A report counts only when it is finished (_finished): a run that halted
|
|
182
|
+
earlier left a partial or unchecked report behind.
|
|
183
|
+
"""
|
|
184
|
+
try:
|
|
185
|
+
entries = list(folder.iterdir())
|
|
186
|
+
except OSError:
|
|
187
|
+
return [] # a missing or unreadable folder holds no earlier report
|
|
188
|
+
found = []
|
|
189
|
+
for entry in entries:
|
|
190
|
+
stamp = _timestamp_of(entry, slug)
|
|
191
|
+
if stamp is not None and stamp != timestamp and _readable_file(entry) and _finished(entry, reader):
|
|
192
|
+
found.append((stamp, entry))
|
|
193
|
+
return found
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def resolve(folder: str, slug: str, timestamp: str, provided: str | None = None) -> tuple[dict, int]:
|
|
197
|
+
"""Pick the previous report. Returns (result_dict, exit_code)."""
|
|
198
|
+
base = Path(folder)
|
|
199
|
+
output_file = base / REPORT_NAME.format(slug=slug, suffix=timestamp)
|
|
200
|
+
output_latest = base / REPORT_NAME.format(slug=slug, suffix=LATEST_SUFFIX)
|
|
201
|
+
written = (output_latest, output_file)
|
|
202
|
+
result = {
|
|
203
|
+
"status": "none",
|
|
204
|
+
"previousReport": None,
|
|
205
|
+
"previousTimestamp": None,
|
|
206
|
+
"provided": provided,
|
|
207
|
+
"collidesWith": None,
|
|
208
|
+
"outputFile": str(output_file),
|
|
209
|
+
"outputFileLatest": str(output_latest),
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if provided is not None:
|
|
213
|
+
path = Path(provided)
|
|
214
|
+
if not _readable_file(path):
|
|
215
|
+
result["status"] = "not-found"
|
|
216
|
+
return result, 0
|
|
217
|
+
for target in written:
|
|
218
|
+
if _same_file(path, target):
|
|
219
|
+
result.update(status="collision", collidesWith=str(target))
|
|
220
|
+
return result, 1
|
|
221
|
+
result.update(
|
|
222
|
+
status="provided",
|
|
223
|
+
previousReport=provided,
|
|
224
|
+
previousTimestamp=_timestamp_of(path, slug),
|
|
225
|
+
)
|
|
226
|
+
return result, 0
|
|
227
|
+
|
|
228
|
+
reader = load_reader()
|
|
229
|
+
if reader is None:
|
|
230
|
+
result.update(status="reader-missing", error=f"the shared report reader is missing: {SHARED_READER}")
|
|
231
|
+
return result, 3
|
|
232
|
+
# A report name that is a link to a file this run writes is no earlier run.
|
|
233
|
+
candidates = [
|
|
234
|
+
(stamp, path)
|
|
235
|
+
for stamp, path in _earlier_reports(base, slug, timestamp, reader)
|
|
236
|
+
if not any(_same_file(path, target) for target in written)
|
|
237
|
+
]
|
|
238
|
+
if candidates:
|
|
239
|
+
stamp, path = max(candidates)
|
|
240
|
+
result.update(status="discovered", previousReport=str(path), previousTimestamp=stamp)
|
|
241
|
+
return result, 0
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def _timestamp(value: str) -> str:
|
|
245
|
+
if not _TIMESTAMP_RE.match(value):
|
|
246
|
+
raise argparse.ArgumentTypeError(f"{value!r} is not a run timestamp (YYYYMMDD-HHmmss)")
|
|
247
|
+
return value
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def _slug(value: str) -> str:
|
|
251
|
+
if not _SLUG_RE.match(value):
|
|
252
|
+
raise argparse.ArgumentTypeError(
|
|
253
|
+
f"{value!r} is not a project slug (lowercase letters and digits joined by "
|
|
254
|
+
"single hyphens): pass the projectSlug skf-validate-feasibility-report.py "
|
|
255
|
+
"--locate returns"
|
|
256
|
+
)
|
|
257
|
+
return value
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def _build_parser():
|
|
261
|
+
parser = argparse.ArgumentParser(
|
|
262
|
+
prog="skf-previous-report",
|
|
263
|
+
description=(
|
|
264
|
+
"Pick the feasibility report this verify-stack run compares against "
|
|
265
|
+
"(init.md §1). With --provided: use that file unless it is one of "
|
|
266
|
+
"the two files this run writes (the timestamped report or the -latest "
|
|
267
|
+
"copy), which is a collision (exit 1). Without it: pick the newest "
|
|
268
|
+
"finished timestamped report of this project in --folder (its "
|
|
269
|
+
"stepsCompleted lists synthesize and it passes the feasibility-report "
|
|
270
|
+
"check); exit 3 when the shared report reader that reads and checks "
|
|
271
|
+
"it is missing. Prints JSON; exit 0 otherwise, 2 on a usage error."
|
|
272
|
+
),
|
|
273
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
274
|
+
epilog=(
|
|
275
|
+
"Example:\n"
|
|
276
|
+
" uv run skf-previous-report.py --folder forge-data --slug my-app "
|
|
277
|
+
"--timestamp 20260930-120000 --provided forge-data/feasibility-report-my-app-latest.md"
|
|
278
|
+
),
|
|
279
|
+
)
|
|
280
|
+
parser.add_argument(
|
|
281
|
+
"--folder",
|
|
282
|
+
required=True,
|
|
283
|
+
help="the folder the reports are written to ({outputFolderPath})",
|
|
284
|
+
)
|
|
285
|
+
parser.add_argument(
|
|
286
|
+
"--slug",
|
|
287
|
+
required=True,
|
|
288
|
+
type=_slug,
|
|
289
|
+
help="the run's project slug ({project_slug})",
|
|
290
|
+
)
|
|
291
|
+
parser.add_argument(
|
|
292
|
+
"--timestamp",
|
|
293
|
+
required=True,
|
|
294
|
+
type=_timestamp,
|
|
295
|
+
help="the run's timestamp, YYYYMMDD-HHmmss ({timestamp})",
|
|
296
|
+
)
|
|
297
|
+
parser.add_argument(
|
|
298
|
+
"--provided",
|
|
299
|
+
metavar="PATH",
|
|
300
|
+
help="the previous report the user gave; omit to pick the newest earlier one",
|
|
301
|
+
)
|
|
302
|
+
return parser
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
def main(argv=None):
|
|
306
|
+
args = _build_parser().parse_args(argv)
|
|
307
|
+
provided = args.provided if args.provided and args.provided.strip() else None
|
|
308
|
+
result, code = resolve(args.folder, args.slug, args.timestamp, provided)
|
|
309
|
+
print(json.dumps(result, indent=2))
|
|
310
|
+
return code
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
def _force_utf8(*streams) -> None:
|
|
314
|
+
"""Reconfigure stdout and stderr to UTF-8, keeping each stream's error handler.
|
|
315
|
+
|
|
316
|
+
A Windows console pipes them as cp1252, which cannot print every character
|
|
317
|
+
a --help text or a path may hold.
|
|
318
|
+
"""
|
|
319
|
+
for stream in streams:
|
|
320
|
+
if hasattr(stream, "reconfigure"):
|
|
321
|
+
errors = getattr(stream, "errors", None)
|
|
322
|
+
if errors is None:
|
|
323
|
+
stream.reconfigure(encoding="utf-8")
|
|
324
|
+
else:
|
|
325
|
+
stream.reconfigure(encoding="utf-8", errors=errors)
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
if __name__ == "__main__":
|
|
329
|
+
_force_utf8(sys.stdout, sys.stderr)
|
|
330
|
+
sys.exit(main())
|