bmad-module-skill-forge 2.2.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/README.md +49 -49
- package/docs/404.md +10 -8
- package/docs/_data/pinned.yaml +22 -13
- package/docs/_internal/STABILITY.md +58 -35
- package/docs/agents.md +39 -53
- package/docs/architecture.md +65 -51
- package/docs/bmad-synergy.md +53 -51
- package/docs/campaign.md +127 -75
- package/docs/concepts.md +34 -28
- package/docs/examples.md +70 -68
- package/docs/forge-auto.md +34 -31
- package/docs/getting-started.md +118 -57
- package/docs/how-it-works.md +25 -21
- package/docs/index.md +15 -15
- package/docs/skill-model.md +70 -59
- package/docs/troubleshooting.md +143 -18
- package/docs/verifying-a-skill.md +87 -57
- package/docs/why-skf.md +24 -22
- package/docs/workflows.md +274 -110
- package/package.json +18 -10
- package/src/forger/preferences.yaml +8 -1
- package/src/knowledge/agentskills-spec.md +2 -0
- package/src/knowledge/ccc-bridge.md +40 -24
- package/src/knowledge/confidence-tiers.md +9 -5
- package/src/knowledge/overview.md +3 -3
- package/src/knowledge/progressive-capability.md +8 -8
- package/src/knowledge/provenance-tracking.md +9 -5
- package/src/knowledge/qmd-registry.md +3 -3
- package/src/knowledge/skf-knowledge-index.csv +2 -2
- package/src/knowledge/skill-lifecycle.md +18 -11
- package/src/knowledge/split-body-strategy.md +0 -3
- package/src/knowledge/tool-resolution.md +4 -2
- package/src/knowledge/version-paths.md +101 -15
- package/src/module-help.csv +3 -3
- package/src/module.yaml +10 -0
- package/src/shared/data/ast-grep-recipes.yaml +608 -0
- package/src/shared/data/ide-context-files.json +30 -0
- package/src/shared/health-check.md +31 -59
- package/src/shared/references/description-guard-protocol.md +12 -9
- package/src/shared/references/feasibility-report-schema.md +19 -13
- package/src/shared/references/headless-gate-convention.md +55 -3
- package/src/shared/references/output-contract-schema.md +19 -8
- package/src/shared/references/pipeline-contracts.md +94 -29
- package/src/shared/references/tessl-review.md +77 -0
- package/src/shared/scripts/schemas/skf-analyze-result-envelope.v1.json +194 -0
- package/src/shared/scripts/schemas/skf-audit-result-envelope.v1.json +192 -0
- package/src/shared/scripts/schemas/skf-brief-result-envelope.v1.json +32 -4
- package/src/shared/scripts/schemas/skf-campaign-result-envelope.v1.json +174 -0
- package/src/shared/scripts/schemas/skf-create-skill-result-envelope.v1.json +284 -0
- package/src/shared/scripts/schemas/skf-drop-skill-result-envelope.v1.json +164 -0
- package/src/shared/scripts/schemas/skf-export-result-envelope.v1.json +160 -0
- package/src/shared/scripts/schemas/skf-quick-skill-result-envelope.v1.json +261 -0
- package/src/shared/scripts/schemas/skf-refine-architecture-result-envelope.v1.json +192 -0
- package/src/shared/scripts/schemas/skf-rename-skill-result-envelope.v1.json +179 -0
- package/src/shared/scripts/schemas/skf-setup-result-envelope.v1.json +27 -11
- package/src/shared/scripts/schemas/skf-stack-result-envelope.v1.json +176 -0
- package/src/shared/scripts/schemas/skf-test-result-envelope.v1.json +173 -0
- package/src/shared/scripts/schemas/skf-update-result-envelope.v1.json +19 -5
- package/src/shared/scripts/schemas/skf-verify-stack-result-envelope.v1.json +180 -0
- package/src/shared/scripts/schemas/skill-brief.v1.json +34 -0
- package/src/shared/scripts/schemas/workspace-detection.v1.json +1 -1
- package/src/shared/scripts/skf-atomic-write.py +233 -64
- package/src/shared/scripts/skf-build-change-manifest.py +2753 -28
- package/src/shared/scripts/skf-ccc-git-hygiene.py +532 -0
- package/src/shared/scripts/skf-check-unit-records.py +184 -0
- package/src/shared/scripts/skf-check-workspace-drift.py +538 -9
- package/src/shared/scripts/skf-classify-changed-files.py +772 -0
- package/src/shared/scripts/skf-comention-pairs.py +1333 -123
- package/src/shared/scripts/skf-compare-file-hashes.py +267 -105
- package/src/shared/scripts/skf-count-imports.py +1867 -0
- package/src/shared/scripts/skf-count-tokens.py +1 -1
- package/src/shared/scripts/skf-derive-assembly-shape.py +21 -3
- package/src/shared/scripts/skf-description-guard.py +99 -11
- package/src/shared/scripts/skf-detect-docs.py +694 -2
- package/src/shared/scripts/skf-detect-language.py +486 -107
- package/src/shared/scripts/skf-detect-registry.py +887 -0
- package/src/shared/scripts/skf-detect-scripts-assets.py +197 -55
- package/src/shared/scripts/skf-detect-tools.py +209 -27
- package/src/shared/scripts/skf-detect-workspaces.py +506 -30
- package/src/shared/scripts/skf-disqualify-candidates.py +821 -182
- package/src/shared/scripts/skf-emit-brief-result-envelope.py +69 -191
- package/src/shared/scripts/skf-emit-result-envelope.py +1604 -106
- package/src/shared/scripts/skf-enumerate-stack-skills.py +986 -117
- package/src/shared/scripts/skf-extract-public-api.py +5247 -96
- package/src/shared/scripts/skf-extraction-inventory.py +1280 -0
- package/src/shared/scripts/skf-extraction-snapshot.py +664 -0
- package/src/shared/scripts/skf-fetch-temporal.py +674 -0
- package/src/shared/scripts/skf-find-cycles.py +97 -29
- package/src/shared/scripts/skf-find-test-report.py +476 -0
- package/src/shared/scripts/skf-forge-tier-rw.py +469 -205
- package/src/shared/scripts/skf-github-fetch.py +365 -0
- package/src/shared/scripts/skf-github-probe.py +700 -0
- package/src/shared/scripts/skf-hash-content.py +180 -20
- package/src/shared/scripts/skf-language-corpora.py +1 -1
- package/src/shared/scripts/skf-load-provenance.py +161 -27
- package/src/shared/scripts/skf-manifest-ops.py +1 -1
- package/src/shared/scripts/skf-merge-ccc-exclusions.py +2307 -223
- package/src/shared/scripts/skf-merge-doc-urls.py +143 -20
- package/src/shared/scripts/skf-names-present.py +221 -0
- package/src/shared/scripts/skf-pair-intersect.py +119 -28
- package/src/shared/scripts/skf-parse-gaps.py +1384 -0
- package/src/shared/scripts/skf-preapply.py +1 -1
- package/src/shared/scripts/skf-preflight.py +176 -28
- package/src/shared/scripts/skf-promote-staged.py +430 -0
- package/src/shared/scripts/skf-provenance-gap-dispatch.py +64 -8
- package/src/shared/scripts/skf-qmd-classify-collections.py +229 -45
- package/src/shared/scripts/skf-quick-batch.py +774 -0
- package/src/shared/scripts/skf-rebuild-managed-sections.py +972 -88
- package/src/shared/scripts/skf-recommend-scope-type.py +323 -156
- package/src/shared/scripts/skf-render-metadata-stats.py +156 -12
- package/src/shared/scripts/skf-render-quick-metadata.py +132 -28
- package/src/shared/scripts/skf-render-stack-metadata.py +976 -0
- package/src/shared/scripts/skf-resolve-authoritative-files.py +155 -14
- package/src/shared/scripts/skf-resolve-package.py +523 -106
- package/src/shared/scripts/skf-rewrite-skill-name.py +433 -47
- package/src/shared/scripts/skf-run-lock.py +577 -0
- package/src/shared/scripts/skf-scan-manifests.py +998 -57
- package/src/shared/scripts/skf-scan-skill-md-structure.py +1341 -37
- package/src/shared/scripts/skf-severity-classify.py +712 -108
- package/src/shared/scripts/skf-shape-detect.py +298 -204
- package/src/shared/scripts/skf-shard-body.py +9 -8
- package/src/shared/scripts/skf-skill-inventory.py +2412 -89
- package/src/shared/scripts/skf-skills-module.py +472 -0
- package/src/shared/scripts/skf-source-tree.py +1796 -0
- package/src/shared/scripts/skf-structural-diff.py +787 -121
- package/src/shared/scripts/skf-tessl-review.py +706 -0
- package/src/shared/scripts/skf-update-active-symlink.py +120 -67
- package/src/shared/scripts/skf-update-run-state.py +624 -0
- package/src/shared/scripts/skf-validate-brief-inputs.py +92 -29
- package/src/shared/scripts/skf-validate-brief-schema.py +23 -2
- package/src/shared/scripts/skf-validate-feasibility-report.py +577 -66
- package/src/shared/scripts/skf-validate-frontmatter.py +103 -26
- package/src/shared/scripts/skf-validate-output.py +712 -64
- package/src/shared/scripts/skf-validate-pins.py +1 -1
- package/src/shared/scripts/skf-verify-no-trace.py +116 -10
- package/src/shared/scripts/skf-verify-provenance-completeness.py +3945 -152
- package/src/shared/scripts/skf-write-skill-brief.py +406 -25
- package/src/shared/tool-requirements.yaml +201 -0
- package/src/skf-analyze-source/SKILL.md +41 -36
- package/src/skf-analyze-source/assets/skill-brief-schema.md +7 -8
- package/src/skf-analyze-source/customize.toml +36 -24
- package/src/skf-analyze-source/references/auto-docs-only.md +58 -20
- package/src/skf-analyze-source/references/continue.md +2 -13
- package/src/skf-analyze-source/references/discover-additional-source.md +147 -0
- package/src/skf-analyze-source/references/generate-briefs.md +121 -55
- package/src/skf-analyze-source/references/headless-contract.md +42 -15
- package/src/skf-analyze-source/references/identify-units.md +96 -110
- package/src/skf-analyze-source/references/init.md +73 -71
- package/src/skf-analyze-source/references/map-and-detect.md +123 -105
- package/src/skf-analyze-source/references/map-unit-exports.md +68 -0
- package/src/skf-analyze-source/references/recommend.md +51 -70
- package/src/skf-analyze-source/references/scan-project.md +60 -84
- package/src/skf-analyze-source/references/scan-root.md +39 -0
- package/src/skf-analyze-source/references/step-auto-scope-coexistence.md +47 -0
- package/src/skf-analyze-source/references/step-auto-scope-corpora.md +53 -0
- package/src/skf-analyze-source/references/step-auto-scope-split.md +91 -0
- package/src/skf-analyze-source/references/step-auto-scope.md +208 -293
- package/src/skf-analyze-source/references/step-shape-detect.md +14 -12
- package/src/skf-analyze-source/references/unit-detection-heuristics.md +37 -42
- package/src/skf-audit-skill/SKILL.md +43 -51
- package/src/skf-audit-skill/assets/drift-report-template.md +28 -3
- package/src/skf-audit-skill/customize.toml +41 -20
- package/src/skf-audit-skill/references/constituent-freshness.md +91 -0
- package/src/skf-audit-skill/references/doc-drift.md +141 -0
- package/src/skf-audit-skill/references/headless-contract.md +29 -0
- package/src/skf-audit-skill/references/health-check.md +2 -0
- package/src/skf-audit-skill/references/init.md +149 -210
- package/src/skf-audit-skill/references/re-index.md +103 -110
- package/src/skf-audit-skill/references/report.md +96 -33
- package/src/skf-audit-skill/references/semantic-diff.md +34 -25
- package/src/skf-audit-skill/references/severity-classify.md +79 -65
- package/src/skf-audit-skill/references/severity-rules.md +15 -6
- package/src/skf-audit-skill/references/structural-diff.md +80 -90
- package/src/skf-audit-skill/references/upstream-checkout.md +77 -0
- package/src/skf-audit-skill/scripts/render-drift-tables.py +712 -0
- package/src/skf-brief-skill/SKILL.md +37 -26
- package/src/skf-brief-skill/assets/description-voice-examples.md +1 -1
- package/src/skf-brief-skill/assets/scope-templates.md +18 -25
- package/src/skf-brief-skill/assets/skill-brief-schema.md +30 -23
- package/src/skf-brief-skill/customize.toml +48 -20
- package/src/skf-brief-skill/references/analyze-target.md +144 -127
- package/src/skf-brief-skill/references/confirm-brief.md +11 -9
- package/src/skf-brief-skill/references/draft-checkpoint.md +36 -17
- package/src/skf-brief-skill/references/gather-intent-ratify.md +68 -0
- package/src/skf-brief-skill/references/gather-intent.md +87 -182
- package/src/skf-brief-skill/references/headless-args.md +84 -16
- package/src/skf-brief-skill/references/health-check.md +1 -1
- package/src/skf-brief-skill/references/invocation-contract.md +18 -13
- package/src/skf-brief-skill/references/portfolio-similarity-check.md +1 -1
- package/src/skf-brief-skill/references/qmd-collection-registration.md +7 -10
- package/src/skf-brief-skill/references/scope-definition.md +123 -47
- package/src/skf-brief-skill/references/step-auto-brief.md +26 -100
- package/src/skf-brief-skill/references/step-auto-validate.md +20 -121
- package/src/skf-brief-skill/references/version-resolution.md +7 -10
- package/src/skf-brief-skill/references/write-brief.md +66 -57
- package/src/skf-campaign/SKILL.md +31 -34
- package/src/skf-campaign/assets/campaign-state-schema.json +25 -4
- package/src/skf-campaign/customize.toml +47 -16
- package/src/skf-campaign/references/campaign-contracts.md +39 -29
- package/src/skf-campaign/references/campaign-directive-spec.md +4 -2
- package/src/skf-campaign/references/health-check.md +7 -16
- package/src/skf-campaign/references/step-01-setup.md +46 -84
- package/src/skf-campaign/references/step-02-strategy.md +31 -30
- package/src/skf-campaign/references/step-03-pins.md +26 -18
- package/src/skf-campaign/references/step-04-provenance.md +24 -23
- package/src/skf-campaign/references/step-05-skill-loop.md +67 -48
- package/src/skf-campaign/references/step-06-batch.md +41 -34
- package/src/skf-campaign/references/step-07-capstone.md +40 -25
- package/src/skf-campaign/references/step-08-verify.md +28 -29
- package/src/skf-campaign/references/step-09-refine.md +29 -30
- package/src/skf-campaign/references/step-10-export.md +48 -36
- package/src/skf-campaign/references/step-11-maintenance.md +31 -34
- package/src/skf-campaign/references/step-resume.md +37 -68
- package/src/skf-campaign/scripts/campaign-deps.py +57 -18
- package/src/skf-campaign/scripts/campaign-parse-manifest.py +361 -54
- package/src/skf-campaign/scripts/campaign-provenance.py +96 -53
- package/src/skf-campaign/scripts/campaign-quality-gate.py +592 -0
- package/src/skf-campaign/scripts/campaign-render-batch.py +257 -49
- package/src/skf-campaign/scripts/campaign-render-kickoff.py +343 -50
- package/src/skf-campaign/scripts/campaign-report.py +204 -37
- package/src/skf-campaign/scripts/campaign-state.py +1128 -0
- package/src/skf-campaign/scripts/campaign-status.py +10 -74
- package/src/skf-campaign/scripts/campaign-validate-pins.py +30 -3
- package/src/skf-campaign/scripts/campaign-validate-state.py +34 -4
- package/src/skf-campaign/templates/campaign-brief-template.yaml +4 -5
- package/src/skf-campaign/templates/campaign-report-template.md +5 -1
- package/src/skf-campaign/templates/kickoff-template.md +0 -14
- package/src/skf-create-skill/SKILL.md +27 -20
- package/src/skf-create-skill/assets/compile-assembly-rules.md +11 -8
- package/src/skf-create-skill/assets/skill-sections.md +21 -11
- package/src/skf-create-skill/customize.toml +20 -8
- package/src/skf-create-skill/references/authoritative-files-protocol.md +36 -58
- package/src/skf-create-skill/references/batch-mode.md +81 -0
- package/src/skf-create-skill/references/ccc-index-check.md +27 -0
- package/src/skf-create-skill/references/compile.md +119 -70
- package/src/skf-create-skill/references/component-extraction.md +113 -91
- package/src/skf-create-skill/references/enrich.md +41 -23
- package/src/skf-create-skill/references/entry-points-by-hand.md +37 -0
- package/src/skf-create-skill/references/extract.md +204 -162
- package/src/skf-create-skill/references/extraction-patterns-by-hand.md +141 -0
- package/src/skf-create-skill/references/extraction-patterns-tracing.md +11 -11
- package/src/skf-create-skill/references/extraction-patterns.md +588 -201
- package/src/skf-create-skill/references/generate-artifacts.md +145 -93
- package/src/skf-create-skill/references/health-check.md +1 -1
- package/src/skf-create-skill/references/load-brief.md +53 -36
- package/src/skf-create-skill/references/relabel-rule.md +12 -0
- package/src/skf-create-skill/references/report.md +37 -57
- package/src/skf-create-skill/references/source-resolution-protocols.md +58 -169
- package/src/skf-create-skill/references/step-auto-shard.md +20 -93
- package/src/skf-create-skill/references/step-doc-rot.md +44 -60
- package/src/skf-create-skill/references/step-doc-sources.md +28 -19
- package/src/skf-create-skill/references/sub/ccc-discover.md +18 -7
- package/src/skf-create-skill/references/sub/fetch-docs.md +106 -59
- package/src/skf-create-skill/references/sub/fetch-temporal.md +53 -166
- package/src/skf-create-skill/references/tier-degradation-rules.md +18 -9
- package/src/skf-create-skill/references/validate.md +173 -99
- package/src/skf-create-skill/scripts/scan-doc-rot.py +232 -99
- package/src/skf-create-stack-skill/SKILL.md +29 -56
- package/src/skf-create-stack-skill/assets/metadata-contract.md +51 -0
- package/src/skf-create-stack-skill/assets/provenance-map-schema.md +15 -15
- package/src/skf-create-stack-skill/assets/stack-skill-template.md +23 -57
- package/src/skf-create-stack-skill/customize.toml +23 -12
- package/src/skf-create-stack-skill/references/compile-stack.md +57 -66
- package/src/skf-create-stack-skill/references/compose-mode-rules.md +24 -44
- package/src/skf-create-stack-skill/references/detect-integrations.md +92 -95
- package/src/skf-create-stack-skill/references/detect-manifests.md +46 -71
- package/src/skf-create-stack-skill/references/generate-output.md +138 -128
- package/src/skf-create-stack-skill/references/init.md +90 -32
- package/src/skf-create-stack-skill/references/integration-patterns.md +1 -17
- package/src/skf-create-stack-skill/references/invocation-contract.md +34 -0
- package/src/skf-create-stack-skill/references/manifest-patterns.md +32 -20
- package/src/skf-create-stack-skill/references/parallel-extract.md +112 -38
- package/src/skf-create-stack-skill/references/rank-and-confirm.md +57 -75
- package/src/skf-create-stack-skill/references/report.md +39 -33
- package/src/skf-create-stack-skill/references/validate.md +20 -39
- package/src/skf-drop-skill/SKILL.md +28 -51
- package/src/skf-drop-skill/customize.toml +44 -30
- package/src/skf-drop-skill/references/execute.md +83 -146
- package/src/skf-drop-skill/references/health-check.md +2 -0
- package/src/skf-drop-skill/references/invocation-contract.md +64 -0
- package/src/skf-drop-skill/references/report.md +46 -30
- package/src/skf-drop-skill/references/select.md +122 -124
- package/src/skf-drop-skill/scripts/dir-sizes.py +54 -26
- package/src/skf-drop-skill/scripts/drop-roster.py +322 -0
- package/src/skf-export-skill/SKILL.md +40 -62
- package/src/skf-export-skill/assets/managed-section-format.md +14 -50
- package/src/skf-export-skill/assets/snippet-format.md +3 -1
- package/src/skf-export-skill/customize.toml +42 -13
- package/src/skf-export-skill/references/generate-snippet.md +54 -46
- package/src/skf-export-skill/references/health-check.md +2 -0
- package/src/skf-export-skill/references/invocation-contract.md +15 -0
- package/src/skf-export-skill/references/load-skill.md +88 -62
- package/src/skf-export-skill/references/manifest-rebuild.md +9 -10
- package/src/skf-export-skill/references/multi-skill-mode.md +4 -4
- package/src/skf-export-skill/references/orphan-context-detection.md +13 -5
- package/src/skf-export-skill/references/orphan-row-detection.md +31 -22
- package/src/skf-export-skill/references/package.md +15 -73
- package/src/skf-export-skill/references/preflight-snippet-root-probe.md +51 -70
- package/src/skf-export-skill/references/result-envelope.md +31 -16
- package/src/skf-export-skill/references/summary.md +41 -17
- package/src/skf-export-skill/references/token-report.md +4 -12
- package/src/skf-export-skill/references/update-context.md +118 -237
- package/src/skf-forger/SKILL.md +61 -37
- package/src/skf-forger/bmad-skill-manifest.yaml +4 -1
- package/src/skf-forger/customize.toml +43 -0
- package/src/skf-forger/references/pipeline-mode.md +73 -18
- package/src/skf-forger/scripts/forge-status.py +299 -0
- package/src/skf-forger/scripts/parse-pipeline.py +322 -54
- package/src/skf-forger/scripts/pipeline-gate.py +428 -0
- package/src/skf-forger/scripts/pipeline-journal.py +1052 -0
- package/src/skf-quick-skill/SKILL.md +54 -53
- package/src/skf-quick-skill/assets/skill-template.md +4 -2
- package/src/skf-quick-skill/customize.toml +51 -20
- package/src/skf-quick-skill/references/batch-mode.md +74 -21
- package/src/skf-quick-skill/references/compile.md +30 -31
- package/src/skf-quick-skill/references/ecosystem-check.md +7 -57
- package/src/skf-quick-skill/references/finalize.md +69 -18
- package/src/skf-quick-skill/references/halt-contract.md +41 -29
- package/src/skf-quick-skill/references/health-check.md +5 -3
- package/src/skf-quick-skill/references/quick-extract.md +114 -45
- package/src/skf-quick-skill/references/registry-resolution.md +9 -64
- package/src/skf-quick-skill/references/resolve-target.md +116 -66
- package/src/skf-quick-skill/references/write-and-validate.md +79 -40
- package/src/skf-refine-architecture/SKILL.md +43 -53
- package/src/skf-refine-architecture/customize.toml +49 -20
- package/src/skf-refine-architecture/references/compile.md +97 -54
- package/src/skf-refine-architecture/references/exit-codes.md +30 -0
- package/src/skf-refine-architecture/references/finding-storage.md +21 -0
- package/src/skf-refine-architecture/references/gap-analysis.md +101 -38
- package/src/skf-refine-architecture/references/improvements.md +14 -20
- package/src/skf-refine-architecture/references/init.md +98 -57
- package/src/skf-refine-architecture/references/issue-detection.md +32 -39
- package/src/skf-refine-architecture/references/refinement-rules.md +37 -48
- package/src/skf-refine-architecture/references/report.md +65 -54
- package/src/skf-refine-architecture/scripts/skf-check-preservation.py +1619 -0
- package/src/skf-rename-skill/SKILL.md +19 -54
- package/src/skf-rename-skill/customize.toml +32 -26
- package/src/skf-rename-skill/references/execute.md +230 -184
- package/src/skf-rename-skill/references/exit-codes.md +7 -7
- package/src/skf-rename-skill/references/health-check.md +1 -7
- package/src/skf-rename-skill/references/invocation-contract.md +25 -0
- package/src/skf-rename-skill/references/report.md +63 -52
- package/src/skf-rename-skill/references/select.md +197 -115
- package/src/skf-rename-skill/scripts/skf-validate-rename-name.py +161 -21
- package/src/skf-setup/SKILL.md +26 -26
- package/src/skf-setup/customize.toml +14 -10
- package/src/skf-setup/references/auto-index.md +50 -63
- package/src/skf-setup/references/ccc-index.md +29 -90
- package/src/skf-setup/references/detect-and-tier.md +58 -67
- package/src/skf-setup/references/health-check.md +2 -0
- package/src/skf-setup/references/invocation-contract.md +14 -0
- package/src/skf-setup/references/report.md +43 -166
- package/src/skf-setup/references/tier-rules.md +5 -5
- package/src/skf-setup/references/write-config.md +34 -57
- package/src/skf-test-skill/SKILL.md +17 -57
- package/src/skf-test-skill/assets/output-section-formats.md +33 -34
- package/src/skf-test-skill/customize.toml +27 -13
- package/src/skf-test-skill/references/coherence-check.md +134 -62
- package/src/skf-test-skill/references/coverage-check-tiers.md +35 -0
- package/src/skf-test-skill/references/coverage-check.md +209 -264
- package/src/skf-test-skill/references/detect-mode.md +5 -5
- package/src/skf-test-skill/references/external-validators.md +94 -82
- package/src/skf-test-skill/references/health-check.md +6 -7
- package/src/skf-test-skill/references/init.md +164 -91
- package/src/skf-test-skill/references/invocation-contract.md +66 -0
- package/src/skf-test-skill/references/migration-section-rules.md +5 -2
- package/src/skf-test-skill/references/report.md +164 -142
- package/src/skf-test-skill/references/score.md +80 -107
- package/src/skf-test-skill/references/scoring-rules.md +42 -155
- package/src/skf-test-skill/references/source-access-protocol.md +18 -94
- package/src/skf-test-skill/references/step-hard-gate.md +51 -33
- package/src/skf-test-skill/scripts/aggregate-coherence.py +218 -30
- package/src/skf-test-skill/scripts/build-result-context.py +380 -0
- package/src/skf-test-skill/scripts/check-metadata-coherence.py +37 -4
- package/src/skf-test-skill/scripts/combine-external-scores.py +23 -6
- package/src/skf-test-skill/scripts/compute-score.py +223 -45
- package/src/skf-test-skill/scripts/gap-ledger.py +1666 -0
- package/src/skf-test-skill/scripts/hard-gate.py +203 -0
- package/src/skf-test-skill/scripts/load-coverage-inputs.py +1198 -0
- package/src/skf-test-skill/scripts/locate-export-segments.py +835 -0
- package/src/skf-test-skill/scripts/reconcile-coverage.py +547 -79
- package/src/skf-test-skill/scripts/score-signatures.py +479 -0
- package/src/skf-test-skill/scripts/stage-helper-payload.py +148 -0
- package/src/skf-test-skill/scripts/validate-inventory.py +311 -16
- package/src/skf-test-skill/scripts/verify-declared-numerator.py +133 -56
- package/src/skf-test-skill/templates/test-report-template.md +21 -15
- package/src/skf-update-skill/SKILL.md +38 -25
- package/src/skf-update-skill/customize.toml +27 -12
- package/src/skf-update-skill/references/detect-changes.md +253 -223
- package/src/skf-update-skill/references/gap-driven.md +336 -0
- package/src/skf-update-skill/references/health-check.md +31 -5
- package/src/skf-update-skill/references/init.md +254 -84
- package/src/skf-update-skill/references/invocation-contract.md +12 -0
- package/src/skf-update-skill/references/manual-section-rules.md +6 -23
- package/src/skf-update-skill/references/merge.md +182 -49
- package/src/skf-update-skill/references/re-extract.md +53 -262
- package/src/skf-update-skill/references/report.md +102 -35
- package/src/skf-update-skill/references/write.md +357 -155
- package/src/skf-update-skill/scripts/skf-new-file-diff.py +183 -46
- package/src/skf-verify-stack/SKILL.md +23 -42
- package/src/skf-verify-stack/assets/feasibility-report-template.md +11 -5
- package/src/skf-verify-stack/customize.toml +55 -22
- package/src/skf-verify-stack/references/coverage-patterns.md +17 -8
- package/src/skf-verify-stack/references/coverage.md +67 -64
- package/src/skf-verify-stack/references/exit-codes.md +21 -8
- package/src/skf-verify-stack/references/init.md +107 -62
- package/src/skf-verify-stack/references/integration-verification-rules.md +12 -13
- package/src/skf-verify-stack/references/integrations.md +106 -65
- package/src/skf-verify-stack/references/report.md +59 -42
- package/src/skf-verify-stack/references/requirements.md +32 -9
- package/src/skf-verify-stack/references/synthesize.md +53 -39
- package/src/skf-verify-stack/scripts/skf-coverage-tally.py +398 -52
- package/src/skf-verify-stack/scripts/skf-previous-report.py +330 -0
- package/src/skf-verify-stack/scripts/skf-report-delta.py +453 -49
- package/src/skf-verify-stack/scripts/skf-verdict-rollup.py +240 -27
- package/tools/cli/commands/install.js +11 -4
- package/tools/cli/commands/status.js +15 -3
- package/tools/cli/commands/update.js +11 -4
- package/tools/cli/lib/tool-check.js +320 -0
- package/tools/cli/lib/version-check.js +22 -9
- package/tools/cli/skf-cli.js +4 -1
- package/.gitattributes +0 -16
- package/.nvmrc +0 -1
- package/docs/_internal/RELEASING.md +0 -679
- package/release-audits/v1.0.0-launch-audit.md +0 -728
- package/src/skf-audit-skill/references/step-doc-drift.md +0 -177
- package/src/skf-brief-skill/references/headless-source-authority-detection.md +0 -26
- package/src/skf-campaign/manifest.yaml +0 -15
- package/src/skf-create-skill/assets/tessl-dismissal-rules.md +0 -106
- package/src/skf-create-skill/references/ecosystem-check.md +0 -74
- package/src/skf-drop-skill/references/headless-contract.md +0 -18
- package/src/skf-update-skill/references/merge-conflict-rules.md +0 -15
- package/src/skf-update-skill/references/remote-source-resolution.md +0 -98
- package/src/skf-update-skill/references/validate.md +0 -124
- package/tools/validate-docs-drift.js +0 -233
- package/tools/validate-docs-links.js +0 -665
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
# /// script
|
|
3
|
-
# requires-python = ">=3.
|
|
3
|
+
# requires-python = ">=3.11"
|
|
4
4
|
# dependencies = []
|
|
5
5
|
# ///
|
|
6
6
|
"""Deterministic overall-feasibility verdict rollup for skf-verify-stack (synthesize.md §1).
|
|
@@ -16,7 +16,8 @@ which co-occurring problems to name, how to phrase the recommendation — stays
|
|
|
16
16
|
prompt; this script emits only the token plus the stable condition codes the prompt
|
|
17
17
|
cites when it writes that rationale.
|
|
18
18
|
|
|
19
|
-
The ladder (
|
|
19
|
+
The ladder (synthesize.md §1 runs it and cites its condition codes; evaluate top-to-bottom,
|
|
20
|
+
first match wins):
|
|
20
21
|
|
|
21
22
|
1. Zero-coverage short-circuit — coveragePercentage == 0 -> NOT_FEASIBLE
|
|
22
23
|
(no live coverage: analysis is vacuous; the remainder of the ladder is skipped).
|
|
@@ -29,23 +30,48 @@ The ladder (exactly mirrors synthesize.md §1; evaluate top-to-bottom, first mat
|
|
|
29
30
|
CONDITIONALLY_FEASIBLE.
|
|
30
31
|
|
|
31
32
|
Post-verdict zero-integration-pairs guard (applied after ANY verdict): when all four
|
|
32
|
-
integration counts are 0 AND
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
integration counts are 0 AND two or more live technologies are Covered
|
|
34
|
+
(coveredCount >= 2), no integration was verified although one could have been, so
|
|
35
|
+
the guard fires: a FEASIBLE verdict is overridden to CONDITIONALLY_FEASIBLE, and
|
|
36
|
+
regardless of verdict the prompt appends the "no integration pair found" note. With
|
|
37
|
+
one Covered technology there is no pair to find, so the guard stays off.
|
|
35
38
|
|
|
36
39
|
Note that coveragePercentage and missingCount are independent inputs on purpose: half-up
|
|
37
40
|
rounding means a stack with covered=199, missing=1 rounds to coveragePercentage == 100
|
|
38
41
|
while missingCount is still > 0, so the Missing trigger reads missingCount directly.
|
|
42
|
+
coveredCount is checked against coveragePercentage only where they cannot disagree: no
|
|
43
|
+
Covered technology means 0% coverage, so coveredCount 0 with a percentage above 0 is
|
|
44
|
+
rejected (a caller that left the count at 0 would silently switch the guard off).
|
|
39
45
|
|
|
40
46
|
CLI usage:
|
|
47
|
+
uv run skf-verdict-rollup.py --report <feasibility-report.md> # the report's frontmatter
|
|
41
48
|
uv run skf-verdict-rollup.py '<JSON>' # JSON literal positional
|
|
42
49
|
uv run skf-verdict-rollup.py --json-input '<JSON>' # explicit flag form
|
|
43
50
|
cat input.json | uv run skf-verdict-rollup.py --stdin # piped input
|
|
44
51
|
|
|
52
|
+
Reading the report (--report, the call synthesize.md section 1 makes): the
|
|
53
|
+
input below comes from the report's frontmatter, read with split_frontmatter()
|
|
54
|
+
of the shared skf-validate-feasibility-report.py, the reader the report's
|
|
55
|
+
consumers use. coveragePercentage and the four pair counts keep their names;
|
|
56
|
+
coverageCovered gives coveredCount, coverageMissing missingCount and
|
|
57
|
+
coverageReplaced replacedCount; requirementsPass gives requirementsEvaluated
|
|
58
|
+
(true for `completed`, false for `skipped`), and with `completed`,
|
|
59
|
+
requirementsNotAddressed and requirementsPartial come in too. Every one of
|
|
60
|
+
those keys must hold a whole number (requirementsPass one of its two
|
|
61
|
+
values): a key missing from the frontmatter, or still at the template's null
|
|
62
|
+
or empty value, fails the run (INVALID_REPORT) rather than reading as 0 or
|
|
63
|
+
false, so the defaults below serve only the callers that pass JSON. The
|
|
64
|
+
template starts coveragePercentage and the four pair counts at 0, a value
|
|
65
|
+
no stage need have written, so the frontmatter's stepsCompleted list
|
|
66
|
+
(read with the shared reader's frontmatter_list()) must name coverage,
|
|
67
|
+
integrations and requirements, the three stages that write the counts: a
|
|
68
|
+
report whose list lacks one fails the same way.
|
|
69
|
+
|
|
45
70
|
Input schema (one object; counts come straight from the report frontmatter/tables):
|
|
46
71
|
{
|
|
47
72
|
"coveragePercentage": <int 0..100>, # from coverage.md (coverage-tally)
|
|
48
73
|
"missingCount": <int>, # Missing technologies (Replaced excluded)
|
|
74
|
+
"coveredCount": <int>, # Covered technologies (the coverage tally's covered_count)
|
|
49
75
|
"pairsBlocked": <int>, # from integrations.md
|
|
50
76
|
"pairsRisky": <int>,
|
|
51
77
|
"pairsPlausible": <int>, # includes Check-4-missing caps
|
|
@@ -53,45 +79,111 @@ Input schema (one object; counts come straight from the report frontmatter/table
|
|
|
53
79
|
"requirementsEvaluated": <bool>, # optional (default false): requirementsPass == "completed"
|
|
54
80
|
"requirementsNotAddressed": <int>, # optional (default 0); ignored unless evaluated
|
|
55
81
|
"requirementsPartial": <int>, # optional (default 0); ignored unless evaluated
|
|
56
|
-
"
|
|
82
|
+
"replacedCount": <int> # optional (default 0): Replaced technologies (the coverage tally's replaced_count)
|
|
57
83
|
}
|
|
58
84
|
|
|
59
85
|
Output (stdout, one object):
|
|
60
86
|
{
|
|
61
87
|
"overallVerdict": "FEASIBLE" | "CONDITIONALLY_FEASIBLE" | "NOT_FEASIBLE",
|
|
62
88
|
"matchedConditions": [<condition codes, in ladder order>],
|
|
63
|
-
"zeroPairsGuardFired": <bool
|
|
89
|
+
"zeroPairsGuardFired": <bool>,
|
|
90
|
+
"recommendationCount": <int>, # the sum of `recommendations`
|
|
91
|
+
"recommendations": {"blocked": <int>, "missing": <int>, "replaced": <int>,
|
|
92
|
+
"risky": <int>, "plausible": <int>, "zeroPairs": 0 | 1,
|
|
93
|
+
"notAddressed": <int>, "partial": <int>}
|
|
64
94
|
}
|
|
65
95
|
|
|
96
|
+
Recommendation count: synthesize.md section 2 writes one recommendation per
|
|
97
|
+
finding that is not verified, so their number has one correct answer per input
|
|
98
|
+
too: one per Blocked, Missing, Replaced, Risky (each circular-dependency row
|
|
99
|
+
included, as the Risky count holds it) and Plausible item, one when the
|
|
100
|
+
zero-integration-pairs guard fired, and, only when the requirements pass ran, one
|
|
101
|
+
per Not Addressed or Partially Fulfilled requirement. `recommendations` gives each
|
|
102
|
+
kind's share in the order synthesize.md lists them, and `recommendationCount` is
|
|
103
|
+
their sum: the report's recommendationCount and the envelope's
|
|
104
|
+
recommendation_count.
|
|
105
|
+
|
|
66
106
|
Condition codes (stable; the prompt cites these when synthesizing the rationale):
|
|
67
107
|
zero-coverage · blocked-integration · missing-coverage · risky-integration ·
|
|
68
108
|
requirements-not-addressed · requirements-partial · plausible-cap · zero-integration-pairs
|
|
69
109
|
|
|
110
|
+
Errors (stdout, one object): {"error": "<why>", "code": "<code>"}:
|
|
111
|
+
INVALID_INPUT the counts do not fit the input schema above
|
|
112
|
+
INVALID_REPORT --report: the report cannot be read, a key above is
|
|
113
|
+
missing or holds no count, or stepsCompleted does not
|
|
114
|
+
list a stage that writes them
|
|
115
|
+
HELPER_MISSING --report: the shared skf-validate-feasibility-report.py is
|
|
116
|
+
not in the shared scripts folder beside this skill's folder
|
|
117
|
+
|
|
70
118
|
Exit codes:
|
|
71
|
-
0
|
|
72
|
-
1
|
|
73
|
-
2
|
|
119
|
+
0 verdict emitted successfully
|
|
120
|
+
1 no input, input that could not be parsed as JSON, or HELPER_MISSING
|
|
121
|
+
2 input parsed but schema/semantics invalid: INVALID_INPUT or
|
|
122
|
+
INVALID_REPORT (error object emitted as JSON)
|
|
74
123
|
"""
|
|
75
124
|
|
|
76
125
|
from __future__ import annotations
|
|
77
126
|
|
|
78
127
|
import argparse
|
|
128
|
+
import importlib.util
|
|
79
129
|
import json
|
|
130
|
+
import re
|
|
80
131
|
import sys
|
|
132
|
+
from pathlib import Path
|
|
81
133
|
|
|
82
134
|
VERDICTS = ("FEASIBLE", "CONDITIONALLY_FEASIBLE", "NOT_FEASIBLE")
|
|
83
135
|
REQUIRED_COUNTS = (
|
|
84
136
|
"missingCount",
|
|
137
|
+
"coveredCount",
|
|
85
138
|
"pairsBlocked",
|
|
86
139
|
"pairsRisky",
|
|
87
140
|
"pairsPlausible",
|
|
88
141
|
"pairsVerified",
|
|
89
142
|
)
|
|
90
|
-
OPTIONAL_COUNTS = ("requirementsNotAddressed", "requirementsPartial")
|
|
143
|
+
OPTIONAL_COUNTS = ("requirementsNotAddressed", "requirementsPartial", "replacedCount")
|
|
144
|
+
|
|
145
|
+
# --report: each frontmatter key the stages persist, with the input it fills.
|
|
146
|
+
REPORT_COUNTS = (
|
|
147
|
+
("coveragePercentage", "coveragePercentage"),
|
|
148
|
+
("coverageMissing", "missingCount"),
|
|
149
|
+
("coverageCovered", "coveredCount"),
|
|
150
|
+
("coverageReplaced", "replacedCount"),
|
|
151
|
+
("pairsBlocked", "pairsBlocked"),
|
|
152
|
+
("pairsRisky", "pairsRisky"),
|
|
153
|
+
("pairsPlausible", "pairsPlausible"),
|
|
154
|
+
("pairsVerified", "pairsVerified"),
|
|
155
|
+
)
|
|
156
|
+
# --report: the stages that write those counts, each of which the frontmatter's
|
|
157
|
+
# stepsCompleted must list: the template's 0 is no count until its stage ran.
|
|
158
|
+
STEPS_COMPLETED_KEY = "stepsCompleted"
|
|
159
|
+
REPORT_STAGES = ("coverage", "integrations", "requirements")
|
|
160
|
+
REQUIREMENTS_PASS_KEY = "requirementsPass"
|
|
161
|
+
REQUIREMENTS_PASSES = ("completed", "skipped")
|
|
162
|
+
# Read only when requirementsPass is `completed`.
|
|
163
|
+
REQUIREMENT_COUNTS = ("requirementsNotAddressed", "requirementsPartial")
|
|
164
|
+
INVALID_REPORT = "INVALID_REPORT"
|
|
165
|
+
HELPER_MISSING = "HELPER_MISSING"
|
|
166
|
+
# A frontmatter count: a whole number, a trailing comment allowed.
|
|
167
|
+
_COUNT_RE = re.compile(r"([0-9]+)(?:\s+#.*)?")
|
|
168
|
+
|
|
169
|
+
# The shared feasibility-report reader. Installed (under _bmad/skf/ or an
|
|
170
|
+
# IDE's skills folder) and in a dev checkout (src/), shared/ sits beside
|
|
171
|
+
# this skill's folder.
|
|
172
|
+
SHARED_READER = (
|
|
173
|
+
Path(__file__).resolve().parent.parent.parent
|
|
174
|
+
/ "shared"
|
|
175
|
+
/ "scripts"
|
|
176
|
+
/ "skf-validate-feasibility-report.py"
|
|
177
|
+
)
|
|
91
178
|
|
|
179
|
+
# The zero-pairs guard needs a pair that could have been found: two Covered
|
|
180
|
+
# technologies (the schema's "two or more live technologies", all of them Covered
|
|
181
|
+
# whenever the verdict could otherwise be FEASIBLE).
|
|
182
|
+
ZERO_PAIRS_MIN_COVERED = 2
|
|
92
183
|
|
|
93
|
-
|
|
94
|
-
|
|
184
|
+
|
|
185
|
+
def make_error(message, code="INVALID_INPUT"):
|
|
186
|
+
return {"error": message, "code": code}
|
|
95
187
|
|
|
96
188
|
|
|
97
189
|
def _is_nonneg_int(value):
|
|
@@ -117,9 +209,11 @@ def _validate(inp):
|
|
|
117
209
|
if field in inp and inp[field] is not None and not _is_nonneg_int(inp[field]):
|
|
118
210
|
return f"{field} must be a non-negative integer when present"
|
|
119
211
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
212
|
+
if "requirementsEvaluated" in inp and not isinstance(inp["requirementsEvaluated"], bool):
|
|
213
|
+
return "requirementsEvaluated must be a boolean when present"
|
|
214
|
+
|
|
215
|
+
if inp["coveredCount"] == 0 and pct > 0:
|
|
216
|
+
return "coveredCount is 0 but coveragePercentage is above 0: pass the coverage tally's covered_count"
|
|
123
217
|
|
|
124
218
|
return None
|
|
125
219
|
|
|
@@ -139,7 +233,8 @@ def rollup(inp):
|
|
|
139
233
|
req_eval = bool(inp.get("requirementsEvaluated", False))
|
|
140
234
|
not_addressed = inp.get("requirementsNotAddressed") or 0
|
|
141
235
|
partial = inp.get("requirementsPartial") or 0
|
|
142
|
-
|
|
236
|
+
replaced = inp.get("replacedCount") or 0
|
|
237
|
+
covered = inp["coveredCount"]
|
|
143
238
|
|
|
144
239
|
matched: list[str] = []
|
|
145
240
|
|
|
@@ -179,43 +274,135 @@ def rollup(inp):
|
|
|
179
274
|
|
|
180
275
|
# Post-verdict zero-integration-pairs guard.
|
|
181
276
|
zero_pairs = blocked == 0 and risky == 0 and plausible == 0 and verified == 0
|
|
182
|
-
guard_fired = zero_pairs and
|
|
277
|
+
guard_fired = zero_pairs and covered >= ZERO_PAIRS_MIN_COVERED
|
|
183
278
|
if guard_fired:
|
|
184
279
|
if verdict == "FEASIBLE":
|
|
185
280
|
verdict = "CONDITIONALLY_FEASIBLE"
|
|
186
281
|
matched.append("zero-integration-pairs")
|
|
187
282
|
|
|
283
|
+
# One recommendation per finding that is not verified (synthesize.md section 2),
|
|
284
|
+
# in the order its recommendation list gives them.
|
|
285
|
+
recommendations = {
|
|
286
|
+
"blocked": blocked,
|
|
287
|
+
"missing": missing,
|
|
288
|
+
"replaced": replaced,
|
|
289
|
+
"risky": risky,
|
|
290
|
+
"plausible": plausible,
|
|
291
|
+
"zeroPairs": 1 if guard_fired else 0,
|
|
292
|
+
"notAddressed": not_addressed if req_eval else 0,
|
|
293
|
+
"partial": partial if req_eval else 0,
|
|
294
|
+
}
|
|
188
295
|
return {
|
|
189
296
|
"overallVerdict": verdict,
|
|
190
297
|
"matchedConditions": matched,
|
|
191
298
|
"zeroPairsGuardFired": guard_fired,
|
|
299
|
+
"recommendationCount": sum(recommendations.values()),
|
|
300
|
+
"recommendations": recommendations,
|
|
192
301
|
}
|
|
193
302
|
|
|
194
303
|
|
|
304
|
+
# --- Reading the report -------------------------------------------------------
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
def load_reader():
|
|
308
|
+
"""Import the shared feasibility-report reader, or None when it is missing."""
|
|
309
|
+
try:
|
|
310
|
+
if not SHARED_READER.is_file():
|
|
311
|
+
return None
|
|
312
|
+
except OSError:
|
|
313
|
+
return None # a folder on the way that cannot be searched
|
|
314
|
+
spec = importlib.util.spec_from_file_location("skf_validate_feasibility_report", SHARED_READER)
|
|
315
|
+
if spec is None or spec.loader is None:
|
|
316
|
+
return None
|
|
317
|
+
module = importlib.util.module_from_spec(spec)
|
|
318
|
+
spec.loader.exec_module(module)
|
|
319
|
+
return module
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
def _report_count(frontmatter, key):
|
|
323
|
+
"""A frontmatter key's whole number, or (None, why it holds none)."""
|
|
324
|
+
if key not in frontmatter:
|
|
325
|
+
return None, f"the report frontmatter has no {key}: the stage that writes it did not record it"
|
|
326
|
+
match = _COUNT_RE.fullmatch(frontmatter[key].strip())
|
|
327
|
+
if match is None:
|
|
328
|
+
return None, (f"the report frontmatter's {key} is {frontmatter[key]!r}, not a whole number: "
|
|
329
|
+
"the stage that writes it did not record it")
|
|
330
|
+
return int(match.group(1)), None
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
def counts_from_report(frontmatter, steps):
|
|
334
|
+
"""The rollup input from a report's frontmatter scalars (split_frontmatter())
|
|
335
|
+
and its stepsCompleted items (frontmatter_list(), None when it has none).
|
|
336
|
+
|
|
337
|
+
Returns (input, None), or (None, error) when a stage that writes the counts
|
|
338
|
+
is not in `steps`, or a key the stages persist is missing or holds no count.
|
|
339
|
+
Unlike rollup(), nothing defaults: requirementsPass must be `completed` or
|
|
340
|
+
`skipped`, and `completed` needs both requirement counts.
|
|
341
|
+
"""
|
|
342
|
+
for stage in REPORT_STAGES:
|
|
343
|
+
if stage not in (steps or ()):
|
|
344
|
+
return None, (f"the report frontmatter's {STEPS_COMPLETED_KEY} does not list {stage}: "
|
|
345
|
+
"that stage did not finish, so its counts are the template's")
|
|
346
|
+
inp = {}
|
|
347
|
+
for key, field in REPORT_COUNTS:
|
|
348
|
+
inp[field], err = _report_count(frontmatter, key)
|
|
349
|
+
if err:
|
|
350
|
+
return None, err
|
|
351
|
+
passed = frontmatter.get(REQUIREMENTS_PASS_KEY, "").split("#", 1)[0].strip()
|
|
352
|
+
if passed not in REQUIREMENTS_PASSES:
|
|
353
|
+
return None, (f"the report frontmatter's {REQUIREMENTS_PASS_KEY} is {passed!r}, not completed or "
|
|
354
|
+
"skipped: the requirements stage did not record its pass")
|
|
355
|
+
inp["requirementsEvaluated"] = passed == "completed"
|
|
356
|
+
if inp["requirementsEvaluated"]:
|
|
357
|
+
for key in REQUIREMENT_COUNTS:
|
|
358
|
+
inp[key], err = _report_count(frontmatter, key)
|
|
359
|
+
if err:
|
|
360
|
+
return None, err
|
|
361
|
+
return inp, None
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
def rollup_report(path, reader):
|
|
365
|
+
"""(result, exit code) of the rollup over the report at `path`, read with `reader`."""
|
|
366
|
+
try:
|
|
367
|
+
# utf-8-sig drops a byte order mark, as the shared reader does.
|
|
368
|
+
content = Path(path).read_text(encoding="utf-8-sig")
|
|
369
|
+
except (OSError, UnicodeDecodeError) as exc:
|
|
370
|
+
return make_error(f"cannot read the report {path}: {exc}", INVALID_REPORT), 2
|
|
371
|
+
frontmatter, _body = reader.split_frontmatter(content)
|
|
372
|
+
inp, err = counts_from_report(frontmatter, reader.frontmatter_list(content, STEPS_COMPLETED_KEY))
|
|
373
|
+
if err:
|
|
374
|
+
return make_error(err, INVALID_REPORT), 2
|
|
375
|
+
result = rollup(inp)
|
|
376
|
+
return result, 2 if result.get("code") == "INVALID_INPUT" else 0
|
|
377
|
+
|
|
378
|
+
|
|
195
379
|
# --- CLI --------------------------------------------------------------------
|
|
196
380
|
|
|
197
381
|
|
|
198
382
|
def _build_parser():
|
|
199
383
|
parser = argparse.ArgumentParser(
|
|
200
384
|
prog="skf-verdict-rollup",
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
"emits the FEASIBLE / CONDITIONALLY_FEASIBLE / NOT_FEASIBLE token plus the "
|
|
205
|
-
"condition codes the prompt cites in its rationale."
|
|
206
|
-
),
|
|
385
|
+
# The module docstring is the contract synthesize.md points --help at:
|
|
386
|
+
# the ladder, the input and output schemas and the condition codes.
|
|
387
|
+
description=__doc__,
|
|
207
388
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
208
389
|
epilog=(
|
|
209
|
-
"
|
|
390
|
+
"Examples:\n"
|
|
391
|
+
" uv run skf-verdict-rollup.py --report feasibility-report-my-app-20261002-101500.md\n"
|
|
210
392
|
" uv run skf-verdict-rollup.py "
|
|
211
|
-
"'{\"coveragePercentage\":100,\"missingCount\":0,\"
|
|
212
|
-
"\"pairsRisky\":0,\"pairsPlausible\":0,\"pairsVerified\":3}'"
|
|
393
|
+
"'{\"coveragePercentage\":100,\"missingCount\":0,\"coveredCount\":4,"
|
|
394
|
+
"\"pairsBlocked\":0,\"pairsRisky\":0,\"pairsPlausible\":0,\"pairsVerified\":3}'"
|
|
213
395
|
),
|
|
214
396
|
)
|
|
215
397
|
src = parser.add_mutually_exclusive_group()
|
|
216
398
|
src.add_argument("json_input", nargs="?", help="JSON object as a positional argument.")
|
|
217
399
|
src.add_argument("--json-input", dest="json_input_flag", help="JSON object passed via flag.")
|
|
218
400
|
src.add_argument("--stdin", action="store_true", help="Read the JSON object from stdin.")
|
|
401
|
+
src.add_argument(
|
|
402
|
+
"--report",
|
|
403
|
+
metavar="REPORT",
|
|
404
|
+
help="Read the counts from this feasibility report's frontmatter; a missing count fails.",
|
|
405
|
+
)
|
|
219
406
|
return parser
|
|
220
407
|
|
|
221
408
|
|
|
@@ -232,6 +419,15 @@ def _resolve_input(args):
|
|
|
232
419
|
def main(argv=None):
|
|
233
420
|
parser = _build_parser()
|
|
234
421
|
args = parser.parse_args(argv)
|
|
422
|
+
if args.report is not None:
|
|
423
|
+
reader = load_reader()
|
|
424
|
+
if reader is None:
|
|
425
|
+
message = f"the shared report reader {SHARED_READER.name} is not installed beside this skill"
|
|
426
|
+
print(json.dumps(make_error(message, HELPER_MISSING), indent=2))
|
|
427
|
+
return 1
|
|
428
|
+
result, code = rollup_report(args.report, reader)
|
|
429
|
+
print(json.dumps(result, indent=2))
|
|
430
|
+
return code
|
|
235
431
|
raw = _resolve_input(args)
|
|
236
432
|
if not raw.strip():
|
|
237
433
|
parser.print_usage(file=sys.stderr)
|
|
@@ -254,5 +450,22 @@ def main(argv=None):
|
|
|
254
450
|
return 0
|
|
255
451
|
|
|
256
452
|
|
|
453
|
+
def _force_utf8(*streams) -> None:
|
|
454
|
+
"""Reconfigure the given streams to UTF-8, keeping each stream's error handler.
|
|
455
|
+
|
|
456
|
+
A Windows console pipes them as cp1252, which cannot print every character
|
|
457
|
+
of the --help text this module docstring supplies, and which garbles UTF-8
|
|
458
|
+
JSON piped into --stdin.
|
|
459
|
+
"""
|
|
460
|
+
for stream in streams:
|
|
461
|
+
if hasattr(stream, "reconfigure"):
|
|
462
|
+
errors = getattr(stream, "errors", None)
|
|
463
|
+
if errors is None:
|
|
464
|
+
stream.reconfigure(encoding="utf-8")
|
|
465
|
+
else:
|
|
466
|
+
stream.reconfigure(encoding="utf-8", errors=errors)
|
|
467
|
+
|
|
468
|
+
|
|
257
469
|
if __name__ == "__main__":
|
|
470
|
+
_force_utf8(sys.stdin, sys.stdout, sys.stderr)
|
|
258
471
|
raise SystemExit(main())
|
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
const chalk = require('chalk');
|
|
2
2
|
const { Installer } = require('../lib/installer');
|
|
3
|
+
const { startToolCheck } = require('../lib/tool-check');
|
|
3
4
|
const { UI } = require('../lib/ui');
|
|
4
5
|
|
|
5
6
|
module.exports = {
|
|
6
7
|
command: 'install',
|
|
7
8
|
description: 'Install SKF skills into your project',
|
|
8
9
|
options: [],
|
|
10
|
+
// Returns rather than exiting, so skf-cli.js prints the update notice after
|
|
11
|
+
// it; a failure sets process.exitCode.
|
|
9
12
|
action: async () => {
|
|
13
|
+
// The tool probes run while the user answers the prompts and the files copy.
|
|
14
|
+
const printToolReport = startToolCheck();
|
|
10
15
|
try {
|
|
11
16
|
const ui = new UI();
|
|
12
17
|
const config = await ui.promptInstall();
|
|
13
18
|
|
|
14
19
|
if (config.cancelled) {
|
|
15
20
|
console.log(chalk.yellow('\nInstallation cancelled.'));
|
|
16
|
-
process.exit(0);
|
|
17
21
|
return;
|
|
18
22
|
}
|
|
19
23
|
|
|
@@ -22,15 +26,18 @@ module.exports = {
|
|
|
22
26
|
|
|
23
27
|
if (result && result.success) {
|
|
24
28
|
ui.displaySuccess(config.skfFolder, config.ides, config._action);
|
|
25
|
-
|
|
29
|
+
await printToolReport();
|
|
26
30
|
} else {
|
|
27
31
|
console.error(chalk.red('\nInstallation failed.'));
|
|
28
|
-
process.
|
|
32
|
+
process.exitCode = 1;
|
|
29
33
|
}
|
|
30
34
|
} catch (error) {
|
|
31
35
|
console.error(chalk.red('\nInstallation failed:'), error.message);
|
|
32
36
|
console.error(chalk.dim(error.stack));
|
|
33
|
-
process.
|
|
37
|
+
process.exitCode = 1;
|
|
38
|
+
} finally {
|
|
39
|
+
// A cancelled or failed install prints no report: stop the probes still running.
|
|
40
|
+
printToolReport.stop();
|
|
34
41
|
}
|
|
35
42
|
},
|
|
36
43
|
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* SKF Status Command
|
|
3
|
-
* Shows installation state, version, tier, configured IDEs, and sidecar status
|
|
3
|
+
* Shows installation state, version, tier, configured IDEs, and sidecar status,
|
|
4
|
+
* and under the tier the last setup recorded, the tools installed now.
|
|
4
5
|
*/
|
|
5
6
|
|
|
6
7
|
const chalk = require('chalk');
|
|
@@ -9,6 +10,7 @@ const fs = require('fs-extra');
|
|
|
9
10
|
const yaml = require('js-yaml');
|
|
10
11
|
const { readManifest } = require('../lib/manifest');
|
|
11
12
|
const { getAvailablePlatforms } = require('../lib/ide-skills');
|
|
13
|
+
const { startToolCheck } = require('../lib/tool-check');
|
|
12
14
|
|
|
13
15
|
const SKF_FOLDER = '_bmad/skf';
|
|
14
16
|
const SIDECAR_FOLDER = '_bmad/_memory/forger-sidecar';
|
|
@@ -94,7 +96,11 @@ async function getStatus(projectDir) {
|
|
|
94
96
|
};
|
|
95
97
|
}
|
|
96
98
|
|
|
97
|
-
|
|
99
|
+
/**
|
|
100
|
+
* Print the status. `printToolReport` prints what is installed now, below
|
|
101
|
+
* the tier and tools the last setup recorded.
|
|
102
|
+
*/
|
|
103
|
+
async function displayStatus(status, version, printToolReport) {
|
|
98
104
|
console.log('');
|
|
99
105
|
console.log(chalk.hex('#F59E0B').bold(' Skill Forge — Status'));
|
|
100
106
|
console.log(chalk.dim(` v${version}`));
|
|
@@ -104,6 +110,7 @@ function displayStatus(status, version) {
|
|
|
104
110
|
console.log(chalk.yellow(' Not installed.'));
|
|
105
111
|
console.log(chalk.dim(' Run: npx bmad-module-skill-forge install'));
|
|
106
112
|
console.log('');
|
|
113
|
+
await printToolReport();
|
|
107
114
|
return;
|
|
108
115
|
}
|
|
109
116
|
|
|
@@ -157,6 +164,8 @@ function displayStatus(status, version) {
|
|
|
157
164
|
console.log(` QMD: ${formatTool(tools.qmd)}`);
|
|
158
165
|
console.log('');
|
|
159
166
|
|
|
167
|
+
await printToolReport();
|
|
168
|
+
|
|
160
169
|
// Output Folders
|
|
161
170
|
console.log(chalk.white.bold(' Output Folders'));
|
|
162
171
|
console.log(` Skills: ${status.skillsFolder}/ ${status.skillsFolderExists ? chalk.green('✓') : chalk.yellow('missing')}`);
|
|
@@ -181,12 +190,15 @@ module.exports = {
|
|
|
181
190
|
description: 'Show SKF installation state, version, tier, and configuration',
|
|
182
191
|
options: [],
|
|
183
192
|
action: async () => {
|
|
193
|
+
const printToolReport = startToolCheck();
|
|
184
194
|
try {
|
|
185
195
|
const projectDir = process.cwd();
|
|
186
196
|
const packageJson = require('../../../package.json');
|
|
187
197
|
const status = await getStatus(projectDir);
|
|
188
|
-
displayStatus(status, packageJson.version);
|
|
198
|
+
await displayStatus(status, packageJson.version, printToolReport);
|
|
189
199
|
} catch (error) {
|
|
200
|
+
// No report follows: stop the probes still running, so none outlives the exit.
|
|
201
|
+
printToolReport.stop();
|
|
190
202
|
console.error(chalk.red('\nFailed to read status:'), error.message);
|
|
191
203
|
process.exit(1);
|
|
192
204
|
}
|
|
@@ -9,6 +9,7 @@ const path = require('node:path');
|
|
|
9
9
|
const fs = require('fs-extra');
|
|
10
10
|
const yaml = require('js-yaml');
|
|
11
11
|
const { Installer } = require('../lib/installer');
|
|
12
|
+
const { startToolCheck } = require('../lib/tool-check');
|
|
12
13
|
const { UI } = require('../lib/ui');
|
|
13
14
|
|
|
14
15
|
const SKF_FOLDER = '_bmad/skf';
|
|
@@ -17,7 +18,11 @@ module.exports = {
|
|
|
17
18
|
command: 'update',
|
|
18
19
|
description: 'Update SKF files and reinstall agent skill (preserves config and sidecar)',
|
|
19
20
|
options: [],
|
|
21
|
+
// Returns rather than exiting, so skf-cli.js prints the update notice after
|
|
22
|
+
// it; a failure sets process.exitCode.
|
|
20
23
|
action: async () => {
|
|
24
|
+
// The tool probes run while the files copy.
|
|
25
|
+
const printToolReport = startToolCheck();
|
|
21
26
|
try {
|
|
22
27
|
const projectDir = process.cwd();
|
|
23
28
|
const skfDir = path.join(projectDir, SKF_FOLDER);
|
|
@@ -25,7 +30,6 @@ module.exports = {
|
|
|
25
30
|
if (!(await fs.pathExists(skfDir))) {
|
|
26
31
|
console.log(chalk.yellow('\n SKF is not installed in this directory.'));
|
|
27
32
|
console.log(chalk.dim(' Run: npx bmad-module-skill-forge install\n'));
|
|
28
|
-
process.exit(0);
|
|
29
33
|
return;
|
|
30
34
|
}
|
|
31
35
|
|
|
@@ -52,14 +56,17 @@ module.exports = {
|
|
|
52
56
|
}
|
|
53
57
|
const ui = new UI();
|
|
54
58
|
ui.displaySuccess(SKF_FOLDER, ides, 'update');
|
|
55
|
-
|
|
59
|
+
await printToolReport();
|
|
56
60
|
} else {
|
|
57
61
|
console.error(chalk.red('\nUpdate failed.'));
|
|
58
|
-
process.
|
|
62
|
+
process.exitCode = 1;
|
|
59
63
|
}
|
|
60
64
|
} catch (error) {
|
|
61
65
|
console.error(chalk.red('\nUpdate failed:'), error.message);
|
|
62
|
-
process.
|
|
66
|
+
process.exitCode = 1;
|
|
67
|
+
} finally {
|
|
68
|
+
// An update that fails, or finds no SKF here, prints no report: stop the probes still running.
|
|
69
|
+
printToolReport.stop();
|
|
63
70
|
}
|
|
64
71
|
},
|
|
65
72
|
};
|