bmad-module-skill-forge 2.2.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 +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 +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 +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 +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 +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 +1 -1
- 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 +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 +15 -5
- 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 +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 +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 -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 +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 -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
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SKF Tool Check
|
|
3
|
+
* Probes the tools SKF runs (the list in src/shared/tool-requirements.yaml)
|
|
4
|
+
* in parallel when a command starts, and prints one line per tool once the
|
|
5
|
+
* command's own work is done: the version found, its status against the
|
|
6
|
+
* tool's minimum and, when it is not ok, how to upgrade or install it.
|
|
7
|
+
*
|
|
8
|
+
* Each probe runs a binary resolved on PATH outside the project folder, with
|
|
9
|
+
* no shell and fixed arguments, and never npx, which can download. A .cmd or
|
|
10
|
+
* .bat shim on Windows runs through cmd.exe. A missing, hanging or failing
|
|
11
|
+
* probe reads as unknown: nothing here throws or changes an exit code.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const { spawn } = require('node:child_process');
|
|
15
|
+
const fs = require('node:fs');
|
|
16
|
+
const path = require('node:path');
|
|
17
|
+
const chalk = require('chalk');
|
|
18
|
+
const yaml = require('js-yaml');
|
|
19
|
+
const { TIMEOUT_MS, compareVersions, findVersion } = require('./version-check');
|
|
20
|
+
|
|
21
|
+
const PACKAGE_REQUIREMENTS = path.join(__dirname, '..', '..', '..', 'src', 'shared', 'tool-requirements.yaml');
|
|
22
|
+
// The copy the installer writes: the minimums of the SKF version the workflows run.
|
|
23
|
+
const PROJECT_REQUIREMENTS = path.join('_bmad', 'skf', 'shared', 'tool-requirements.yaml');
|
|
24
|
+
const MINIMUM = /^\d+(?:\.\d+)*$/;
|
|
25
|
+
// SKF runs these through npx when they are not installed, so a probe reads a
|
|
26
|
+
// binary on PATH only, and a missing one is no gap.
|
|
27
|
+
const NPX_TOOLS = new Set(['tessl', 'skill_check']);
|
|
28
|
+
// ccc has no --version: `uv tool list` names each package uv installed, with its version.
|
|
29
|
+
const CCC_PACKAGE = /^cocoindex-code v(\S+)/m;
|
|
30
|
+
const MAX_OUTPUT = 64 * 1024;
|
|
31
|
+
const STYLES = { ok: chalk.green, upgrade: chalk.yellow, unknown: chalk.yellow, missing: chalk.red, optional: chalk.dim };
|
|
32
|
+
|
|
33
|
+
/** The tools of a tool-requirements.yaml by key (mappings only), or null when it cannot be read. */
|
|
34
|
+
function readTools(file) {
|
|
35
|
+
try {
|
|
36
|
+
const tools = yaml.load(fs.readFileSync(file, 'utf8'))?.tools;
|
|
37
|
+
if (!tools || typeof tools !== 'object' || Array.isArray(tools)) return null;
|
|
38
|
+
return Object.fromEntries(Object.entries(tools).filter(([, tool]) => tool && typeof tool === 'object' && !Array.isArray(tool)));
|
|
39
|
+
} catch {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** An environment variable, its name matched case-insensitively on Windows. */
|
|
45
|
+
function envValue(env, name) {
|
|
46
|
+
if (process.platform !== 'win32') return env[name];
|
|
47
|
+
const key = Object.keys(env).find((candidate) => candidate.toUpperCase() === name.toUpperCase());
|
|
48
|
+
return key === undefined ? undefined : env[key];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function realpath(file) {
|
|
52
|
+
try {
|
|
53
|
+
return fs.realpathSync.native(file);
|
|
54
|
+
} catch {
|
|
55
|
+
return file;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** True when `target` is `folder` or lies inside it. */
|
|
60
|
+
function isInside(target, folder) {
|
|
61
|
+
const relative = path.relative(folder, target);
|
|
62
|
+
return !path.isAbsolute(relative) && relative.split(path.sep)[0] !== '..';
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function isRunnable(file) {
|
|
66
|
+
try {
|
|
67
|
+
if (!fs.statSync(file).isFile()) return false;
|
|
68
|
+
if (process.platform !== 'win32') fs.accessSync(file, fs.constants.X_OK);
|
|
69
|
+
return true;
|
|
70
|
+
} catch {
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* The absolute path of `name` on PATH, or null. A PATH folder in or under the
|
|
77
|
+
* project, a relative PATH entry (a shell reads it from the project folder)
|
|
78
|
+
* and a binary whose real path lies in the project are all skipped, so a
|
|
79
|
+
* binary planted in the repository never runs. On Windows each PATHEXT
|
|
80
|
+
* extension is tried, never the bare name.
|
|
81
|
+
*/
|
|
82
|
+
function resolveOutsideProject(name, { env, projectDir }) {
|
|
83
|
+
const project = [path.resolve(projectDir), realpath(path.resolve(projectDir))];
|
|
84
|
+
const inProject = (file) => project.some((folder) => isInside(file, folder));
|
|
85
|
+
const extensions = process.platform === 'win32' ? (envValue(env, 'PATHEXT') || '.COM;.EXE;.BAT;.CMD').split(';').filter(Boolean) : [''];
|
|
86
|
+
for (const entry of String(envValue(env, 'PATH') || '').split(path.delimiter)) {
|
|
87
|
+
const dir = entry.replaceAll('"', '');
|
|
88
|
+
if (!path.isAbsolute(dir) || inProject(dir) || inProject(realpath(dir))) continue;
|
|
89
|
+
for (const extension of extensions) {
|
|
90
|
+
const file = path.join(dir, name + extension);
|
|
91
|
+
if (isRunnable(file) && !inProject(realpath(file))) return file;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* How to run `file` with `args`: the file itself, or, for a .cmd or .bat shim
|
|
99
|
+
* on Windows (Node refuses to spawn one without a shell), cmd.exe with the
|
|
100
|
+
* command line fixed here. null when that line would not be safe: a path
|
|
101
|
+
* cmd.exe would expand (`%`, `!`) or an argument that is not a plain word.
|
|
102
|
+
*/
|
|
103
|
+
function commandFor(file, args, platform = process.platform, env = process.env) {
|
|
104
|
+
if (platform !== 'win32' || !/\.(?:cmd|bat)$/i.test(file)) return { file, args, options: {} };
|
|
105
|
+
if (/[%!"]/.test(file) || !args.every((arg) => /^[\w.-]+$/.test(arg))) return null;
|
|
106
|
+
const shell = envValue(env, 'ComSpec') || path.win32.join(envValue(env, 'SystemRoot') || String.raw`C:\Windows`, 'System32', 'cmd.exe');
|
|
107
|
+
return { file: shell, args: ['/d', '/s', '/c', `""${file}" ${args.join(' ')}"`], options: { windowsVerbatimArguments: true } };
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** Stop a probe that is still running, so it neither holds the command open nor outlives it. */
|
|
111
|
+
function stop(child) {
|
|
112
|
+
try {
|
|
113
|
+
child.kill('SIGKILL');
|
|
114
|
+
} catch {
|
|
115
|
+
// Already gone
|
|
116
|
+
}
|
|
117
|
+
child.stdout?.destroy();
|
|
118
|
+
child.unref();
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Run one probe. Resolves { ok, stdout } and never rejects: a probe that
|
|
123
|
+
* cannot start, exits non-zero or outlives the timeout (it is then killed)
|
|
124
|
+
* is not ok.
|
|
125
|
+
*/
|
|
126
|
+
function run(command, context) {
|
|
127
|
+
return new Promise((resolve) => {
|
|
128
|
+
let stdout = '';
|
|
129
|
+
let timer;
|
|
130
|
+
let child;
|
|
131
|
+
const finish = (ok) => {
|
|
132
|
+
clearTimeout(timer);
|
|
133
|
+
if (child) context.running.delete(child);
|
|
134
|
+
resolve({ ok, stdout });
|
|
135
|
+
};
|
|
136
|
+
// The report is already taken: a probe chained after it starts nothing.
|
|
137
|
+
if (context.stopped) {
|
|
138
|
+
finish(false);
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
try {
|
|
142
|
+
child = spawn(command.file, command.args, {
|
|
143
|
+
cwd: context.cwd,
|
|
144
|
+
env: context.env,
|
|
145
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
146
|
+
windowsHide: true,
|
|
147
|
+
...command.options,
|
|
148
|
+
});
|
|
149
|
+
} catch {
|
|
150
|
+
finish(false);
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
context.running.add(child);
|
|
154
|
+
timer = setTimeout(() => {
|
|
155
|
+
stop(child);
|
|
156
|
+
finish(false);
|
|
157
|
+
}, context.timeoutMs);
|
|
158
|
+
child.stdout.setEncoding('utf8');
|
|
159
|
+
child.stdout.on('data', (chunk) => {
|
|
160
|
+
if (stdout.length < MAX_OUTPUT) stdout += chunk;
|
|
161
|
+
});
|
|
162
|
+
child.on('error', () => finish(false));
|
|
163
|
+
child.on('close', (code) => finish(code === 0));
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/** The first x.y or x.y.z that `file` prints when run with `args`, or null. */
|
|
168
|
+
async function versionOf(file, args, context) {
|
|
169
|
+
const command = commandFor(file, args, process.platform, context.env);
|
|
170
|
+
if (!command) return null;
|
|
171
|
+
const { ok, stdout } = await run(command, context);
|
|
172
|
+
return ok ? findVersion(stdout) : null;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/** One tool: { found, version }, the version null when it cannot be read. */
|
|
176
|
+
async function probe(key, tool, context) {
|
|
177
|
+
if (key === 'node') return { found: true, version: process.versions.node };
|
|
178
|
+
if (key === 'python') {
|
|
179
|
+
// The interpreter uv runs SKF's helpers with. --system leaves out a
|
|
180
|
+
// virtual environment, whose interpreter would be the project's to run.
|
|
181
|
+
const uv = resolveOutsideProject('uv', context);
|
|
182
|
+
const version = uv ? await versionOf(uv, ['python', 'find', '--system', '--show-version'], context) : null;
|
|
183
|
+
if (version) return { found: true, version };
|
|
184
|
+
}
|
|
185
|
+
if (key === 'ccc') {
|
|
186
|
+
if (!resolveOutsideProject('ccc', context)) return { found: false };
|
|
187
|
+
const uv = resolveOutsideProject('uv', context);
|
|
188
|
+
const command = uv && commandFor(uv, ['tool', 'list'], process.platform, context.env);
|
|
189
|
+
const { ok, stdout } = command ? await run(command, context) : { ok: false, stdout: '' };
|
|
190
|
+
const match = ok ? CCC_PACKAGE.exec(stdout) : null;
|
|
191
|
+
return { found: true, version: match ? findVersion(match[1]) : null };
|
|
192
|
+
}
|
|
193
|
+
const [name, ...args] = Array.isArray(tool.version_command) ? tool.version_command.map(String) : [];
|
|
194
|
+
const file = name ? resolveOutsideProject(name, context) : null;
|
|
195
|
+
if (!file) return { found: false };
|
|
196
|
+
return { found: true, version: args.length > 0 ? await versionOf(file, args, context) : null };
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* The minimum of each tool, from the project's copy of the list when there is
|
|
201
|
+
* one (the SKF version its workflows run), else from the package's own.
|
|
202
|
+
*/
|
|
203
|
+
function minimums(projectDir, tools) {
|
|
204
|
+
const source = readTools(path.join(projectDir, PROJECT_REQUIREMENTS)) || tools;
|
|
205
|
+
return Object.fromEntries(
|
|
206
|
+
Object.keys(tools).map((key) => {
|
|
207
|
+
const value = source[key]?.minimum;
|
|
208
|
+
return [key, typeof value === 'string' && MINIMUM.test(value) ? value : null];
|
|
209
|
+
}),
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/** One report row per tool, in the list's order. A tool that has not answered yet was found and reads as unknown. */
|
|
214
|
+
function rowsFor(tools, answers, floors) {
|
|
215
|
+
return Object.entries(tools).map(([key, tool]) => {
|
|
216
|
+
const row = { key, name: String(tool.name || key), version: null, minimum: floors[key] };
|
|
217
|
+
const answer = answers.get(key) || { found: true, version: null };
|
|
218
|
+
if (!answer.found) {
|
|
219
|
+
const tiers = Array.isArray(tool.tiers) ? tool.tiers : [];
|
|
220
|
+
if (NPX_TOOLS.has(key)) return { ...row, status: 'optional', label: 'optional, runs through npx' };
|
|
221
|
+
if (tool.kind === 'tier' && tiers.length > 0)
|
|
222
|
+
return { ...row, status: 'optional', label: `optional, ${tiers[0]} tier`, hint: tool.install_url };
|
|
223
|
+
if (tool.kind === 'tier' || tool.kind === 'optional')
|
|
224
|
+
return { ...row, status: 'optional', label: 'optional', hint: tool.install_url };
|
|
225
|
+
return { ...row, status: 'missing', label: 'missing', hint: tool.install_url };
|
|
226
|
+
}
|
|
227
|
+
if (!answer.version) {
|
|
228
|
+
return { ...row, status: 'unknown', label: 'installed, version unknown', hint: row.minimum ? tool.upgrade : undefined };
|
|
229
|
+
}
|
|
230
|
+
// A minimum may name a major alone (Node.js 22); compareVersions reads x.y.
|
|
231
|
+
const floor = row.minimum && !row.minimum.includes('.') ? `${row.minimum}.0` : row.minimum;
|
|
232
|
+
if (floor && compareVersions(answer.version, floor)) {
|
|
233
|
+
return { ...row, version: answer.version, status: 'upgrade', label: `upgrade to >= ${row.minimum}`, hint: tool.upgrade };
|
|
234
|
+
}
|
|
235
|
+
return { ...row, version: answer.version, status: 'ok', label: 'ok' };
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/** The report: a heading, then one aligned line per tool. */
|
|
240
|
+
function formatReport(rows) {
|
|
241
|
+
const width = (pick) => Math.max(0, ...rows.map((row) => pick(row).length));
|
|
242
|
+
const nameWidth = width((row) => row.name);
|
|
243
|
+
const versionWidth = width((row) => row.version || '-');
|
|
244
|
+
const labelWidth = width((row) => (row.hint ? row.label : ''));
|
|
245
|
+
const lines = [chalk.white.bold(' Tools')];
|
|
246
|
+
for (const row of rows) {
|
|
247
|
+
const style = STYLES[row.status];
|
|
248
|
+
const status = row.hint ? `${style(row.label.padEnd(labelWidth))} ${chalk.dim(row.hint)}` : style(row.label);
|
|
249
|
+
lines.push(` ${row.name.padEnd(nameWidth)} ${(row.version || '-').padEnd(versionWidth)} ${status}`);
|
|
250
|
+
}
|
|
251
|
+
return lines;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Start every probe now. The returned rows() waits for them at most
|
|
256
|
+
* `timeoutMs` more, stops any still running and resolves the report rows,
|
|
257
|
+
* or null when the package's tool list cannot be read. rows.stop() stops
|
|
258
|
+
* the probes still running and starts no more, without a report.
|
|
259
|
+
*/
|
|
260
|
+
function beginCheck({ projectDir = process.cwd(), env = process.env, timeoutMs = TIMEOUT_MS, requirements = PACKAGE_REQUIREMENTS } = {}) {
|
|
261
|
+
const tools = readTools(requirements);
|
|
262
|
+
// Probes run from the filesystem root, so no tool reads the project's own
|
|
263
|
+
// configuration or runs its virtual environment.
|
|
264
|
+
const context = { env, projectDir, timeoutMs, cwd: path.parse(path.resolve(projectDir)).root, running: new Set(), stopped: false };
|
|
265
|
+
const answers = new Map();
|
|
266
|
+
const probes = Object.entries(tools || {}).map(([key, tool]) =>
|
|
267
|
+
probe(key, tool, context).then(
|
|
268
|
+
(answer) => answers.set(key, answer),
|
|
269
|
+
() => answers.set(key, { found: true, version: null }),
|
|
270
|
+
),
|
|
271
|
+
);
|
|
272
|
+
const stopAll = () => {
|
|
273
|
+
context.stopped = true;
|
|
274
|
+
for (const child of context.running) stop(child);
|
|
275
|
+
};
|
|
276
|
+
async function rows() {
|
|
277
|
+
if (!tools) return null;
|
|
278
|
+
let timer;
|
|
279
|
+
const waited = new Promise((resolve) => {
|
|
280
|
+
timer = setTimeout(resolve, timeoutMs);
|
|
281
|
+
});
|
|
282
|
+
await Promise.race([Promise.all(probes), waited]);
|
|
283
|
+
clearTimeout(timer);
|
|
284
|
+
stopAll();
|
|
285
|
+
return rowsFor(tools, answers, minimums(projectDir, tools));
|
|
286
|
+
}
|
|
287
|
+
return Object.assign(rows, { stop: stopAll });
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/** Probe every tool and resolve the report rows (see beginCheck). */
|
|
291
|
+
function checkTools(options) {
|
|
292
|
+
return beginCheck(options)();
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Start an async tool check, with the shape of startVersionCheck: call the
|
|
297
|
+
* returned function after the command's own work to print the report. It
|
|
298
|
+
* waits at most about 3 s for the probes and never throws. A command that
|
|
299
|
+
* ends without the report calls its stop() instead, so no probe outlives
|
|
300
|
+
* the CLI; after the report, stop() does nothing.
|
|
301
|
+
*/
|
|
302
|
+
function startToolCheck(options) {
|
|
303
|
+
let rows;
|
|
304
|
+
try {
|
|
305
|
+
rows = beginCheck(options);
|
|
306
|
+
} catch {
|
|
307
|
+
rows = Object.assign(async () => null, { stop() {} });
|
|
308
|
+
}
|
|
309
|
+
async function printIfReady() {
|
|
310
|
+
try {
|
|
311
|
+
const found = await rows();
|
|
312
|
+
console.log(found ? `${formatReport(found).join('\n')}\n` : chalk.dim(' Tools: not checked, the tool list could not be read\n'));
|
|
313
|
+
} catch {
|
|
314
|
+
// Never block or fail the CLI for a tool check
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
return Object.assign(printIfReady, { stop: rows.stop });
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
module.exports = { startToolCheck, checkTools, commandFor, formatReport, resolveOutsideProject };
|
|
@@ -10,6 +10,8 @@ const chalk = require('chalk');
|
|
|
10
10
|
const PACKAGE_NAME = 'bmad-module-skill-forge';
|
|
11
11
|
const REGISTRY_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/latest`;
|
|
12
12
|
const TIMEOUT_MS = 3000;
|
|
13
|
+
// The first x.y or x.y.z in a string: `0.45`, `v3.0.0-rc.1`, `ast-grep 0.42.2`.
|
|
14
|
+
const VERSION_IN_TEXT = /(\d+)\.(\d+)(?:\.(\d+))?/;
|
|
13
15
|
|
|
14
16
|
function fetchLatestVersion() {
|
|
15
17
|
return new Promise((resolve) => {
|
|
@@ -42,15 +44,26 @@ function fetchLatestVersion() {
|
|
|
42
44
|
});
|
|
43
45
|
}
|
|
44
46
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
const
|
|
48
|
-
|
|
47
|
+
/** The first x.y or x.y.z in `text`, as written there, or null. */
|
|
48
|
+
function findVersion(text) {
|
|
49
|
+
const match = VERSION_IN_TEXT.exec(String(text ?? ''));
|
|
50
|
+
return match ? match[0] : null;
|
|
51
|
+
}
|
|
49
52
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
53
|
+
/**
|
|
54
|
+
* True when `latest` is newer than `current`. Each is read as the first x.y
|
|
55
|
+
* or x.y.z it holds, a missing part as 0, so `0.45` and `ast-grep 0.42.2`
|
|
56
|
+
* both compare with `0.45.3`; a string with neither is never newer.
|
|
57
|
+
*/
|
|
58
|
+
function compareVersions(current, latest) {
|
|
59
|
+
const parse = (text) => {
|
|
60
|
+
const match = VERSION_IN_TEXT.exec(String(text ?? ''));
|
|
61
|
+
return match ? match.slice(1, 4).map((part) => Number(part ?? 0)) : null;
|
|
62
|
+
};
|
|
63
|
+
const [from, to] = [parse(current), parse(latest)];
|
|
64
|
+
if (!from || !to) return false;
|
|
65
|
+
const index = from.findIndex((part, position) => part !== to[position]);
|
|
66
|
+
return index !== -1 && to[index] > from[index];
|
|
54
67
|
}
|
|
55
68
|
|
|
56
69
|
/**
|
|
@@ -83,4 +96,4 @@ function startVersionCheck(currentVersion) {
|
|
|
83
96
|
};
|
|
84
97
|
}
|
|
85
98
|
|
|
86
|
-
module.exports = { startVersionCheck, compareVersions };
|
|
99
|
+
module.exports = { startVersionCheck, compareVersions, findVersion, TIMEOUT_MS };
|
package/tools/cli/skf-cli.js
CHANGED
|
@@ -30,11 +30,14 @@ for (const command of [installCommand, updateCommand, statusCommand, uninstallCo
|
|
|
30
30
|
for (const option of command.options || []) {
|
|
31
31
|
cmd.option(...option);
|
|
32
32
|
}
|
|
33
|
-
// Wrap action to print update notice after command completes
|
|
33
|
+
// Wrap action to print update notice after command completes. The actions
|
|
34
|
+
// return (setting process.exitCode on a failure) rather than exit, so the
|
|
35
|
+
// notice follows them; the exit ends the stdin that the fix above resumed.
|
|
34
36
|
const originalAction = command.action;
|
|
35
37
|
cmd.action(async (...args) => {
|
|
36
38
|
await originalAction(...args);
|
|
37
39
|
await printUpdateNotice();
|
|
40
|
+
process.exit(process.exitCode ?? 0);
|
|
38
41
|
});
|
|
39
42
|
}
|
|
40
43
|
|
package/.gitattributes
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
# Normalize all text files to LF in the repo and on checkout, regardless of
|
|
2
|
-
# the contributor's platform or core.autocrlf setting. SKF runs on Linux,
|
|
3
|
-
# macOS, and Windows (Git Bash / WSL2); LF-everywhere keeps prettier
|
|
4
|
-
# `endOfLine: 'lf'` clean and avoids spurious diffs from line-ending churn.
|
|
5
|
-
* text=auto eol=lf
|
|
6
|
-
|
|
7
|
-
# Binary blobs — never touch.
|
|
8
|
-
*.png binary
|
|
9
|
-
*.jpg binary
|
|
10
|
-
*.jpeg binary
|
|
11
|
-
*.gif binary
|
|
12
|
-
*.ico binary
|
|
13
|
-
*.pdf binary
|
|
14
|
-
*.zip binary
|
|
15
|
-
*.gz binary
|
|
16
|
-
*.tgz binary
|
package/.nvmrc
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
24
|