mustflow 1.18.16 → 1.31.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/README.md +28 -8
- package/dist/cli/commands/adapters.js +90 -0
- package/dist/cli/commands/classify.js +3 -4
- package/dist/cli/commands/contract-lint.js +26 -6
- package/dist/cli/commands/dashboard.js +103 -4
- package/dist/cli/commands/explain-verify.js +213 -0
- package/dist/cli/commands/explain.js +48 -4
- package/dist/cli/commands/handoff.js +136 -0
- package/dist/cli/commands/run.js +302 -168
- package/dist/cli/commands/update.js +91 -61
- package/dist/cli/commands/upgrade.js +65 -0
- package/dist/cli/commands/verify.js +304 -139
- package/dist/cli/i18n/en.js +77 -4
- package/dist/cli/i18n/es.js +77 -4
- package/dist/cli/i18n/fr.js +77 -4
- package/dist/cli/i18n/hi.js +77 -4
- package/dist/cli/i18n/ko.js +77 -4
- package/dist/cli/i18n/zh.js +77 -4
- package/dist/cli/index.js +34 -42
- package/dist/cli/lib/command-registry.js +15 -0
- package/dist/cli/lib/dashboard-export.js +775 -0
- package/dist/cli/lib/dashboard-html.js +1 -1
- package/dist/cli/lib/local-index.js +33 -14
- package/dist/cli/lib/reporter.js +6 -0
- package/dist/cli/lib/run-plan.js +239 -0
- package/dist/cli/lib/templates.js +18 -3
- package/dist/cli/lib/update-diff-preview.js +163 -0
- package/dist/cli/lib/validation.js +132 -1
- package/dist/core/adapter-compatibility.js +235 -0
- package/dist/core/bounded-output.js +38 -0
- package/dist/core/change-classification.js +14 -1
- package/dist/core/change-verification.js +246 -5
- package/dist/core/check-issues.js +10 -0
- package/dist/core/command-contract-validation.js +34 -0
- package/dist/core/command-cwd.js +18 -6
- package/dist/core/command-effects.js +13 -0
- package/dist/core/command-env.js +91 -0
- package/dist/core/contract-lint.js +260 -4
- package/dist/core/contract-models.js +172 -0
- package/dist/core/dashboard-verification.js +10 -0
- package/dist/core/doc-review-triage.js +1 -0
- package/dist/core/handoff-record.js +376 -0
- package/dist/core/public-json-contracts.js +23 -0
- package/dist/core/run-performance-history.js +307 -0
- package/dist/core/run-profile.js +87 -0
- package/dist/core/run-receipt.js +217 -11
- package/dist/core/run-write-drift.js +196 -0
- package/dist/core/secret-redaction.js +39 -0
- package/dist/core/skill-route-alignment.js +90 -0
- package/dist/core/source-anchors.js +3 -5
- package/dist/core/test-selection.js +224 -0
- package/dist/core/verification-decision-graph.js +290 -0
- package/dist/core/verification-scheduler.js +96 -2
- package/package.json +3 -1
- package/schemas/README.md +15 -4
- package/schemas/adapter-compatibility-report.schema.json +184 -0
- package/schemas/change-verification-report.schema.json +286 -4
- package/schemas/commands.schema.json +55 -2
- package/schemas/contract-lint-report.schema.json +99 -0
- package/schemas/dashboard-export.schema.json +273 -0
- package/schemas/explain-report.schema.json +267 -2
- package/schemas/handoff-validation-report.schema.json +68 -0
- package/schemas/run-receipt.schema.json +183 -1
- package/templates/default/common/.mustflow/config/commands.toml +3 -1
- package/templates/default/i18n.toml +78 -234
- package/templates/default/locales/en/.mustflow/skills/INDEX.md +7 -3
- package/templates/default/locales/en/.mustflow/skills/architecture-deepening-review/SKILL.md +154 -0
- package/templates/default/locales/en/.mustflow/skills/behavior-preserving-refactor/SKILL.md +8 -3
- package/templates/default/locales/en/.mustflow/skills/code-review/SKILL.md +9 -4
- package/templates/default/locales/en/.mustflow/skills/date-number-audit/SKILL.md +19 -4
- package/templates/default/locales/en/.mustflow/skills/diff-risk-review/SKILL.md +4 -2
- package/templates/default/locales/en/.mustflow/skills/external-skill-intake/SKILL.md +141 -0
- package/templates/default/locales/en/.mustflow/skills/release-notes-authoring/SKILL.md +143 -0
- package/templates/default/locales/en/.mustflow/skills/repro-first-debug/SKILL.md +22 -8
- package/templates/default/locales/en/.mustflow/skills/skill-authoring/SKILL.md +3 -3
- package/templates/default/locales/en/.mustflow/skills/source-freshness-check/SKILL.md +22 -9
- package/templates/default/locales/en/.mustflow/skills/ui-quality-gate/SKILL.md +21 -13
- package/templates/default/locales/en/.mustflow/skills/vertical-slice-tdd/SKILL.md +167 -0
- package/templates/default/manifest.toml +16 -1
- package/templates/default/locales/es/.mustflow/skills/INDEX.md +0 -75
- package/templates/default/locales/es/.mustflow/skills/adapter-boundary/SKILL.md +0 -193
- package/templates/default/locales/es/.mustflow/skills/artifact-integrity-check/SKILL.md +0 -114
- package/templates/default/locales/es/.mustflow/skills/behavior-preserving-refactor/SKILL.md +0 -182
- package/templates/default/locales/es/.mustflow/skills/code-review/SKILL.md +0 -115
- package/templates/default/locales/es/.mustflow/skills/codebase-orientation/SKILL.md +0 -115
- package/templates/default/locales/es/.mustflow/skills/command-pattern/SKILL.md +0 -247
- package/templates/default/locales/es/.mustflow/skills/composition-over-inheritance/SKILL.md +0 -176
- package/templates/default/locales/es/.mustflow/skills/contract-sync-check/SKILL.md +0 -116
- package/templates/default/locales/es/.mustflow/skills/database-change-safety/SKILL.md +0 -155
- package/templates/default/locales/es/.mustflow/skills/date-number-audit/SKILL.md +0 -116
- package/templates/default/locales/es/.mustflow/skills/dependency-injection/SKILL.md +0 -161
- package/templates/default/locales/es/.mustflow/skills/dependency-reality-check/SKILL.md +0 -115
- package/templates/default/locales/es/.mustflow/skills/diff-risk-review/SKILL.md +0 -136
- package/templates/default/locales/es/.mustflow/skills/docs-prose-review/SKILL.md +0 -119
- package/templates/default/locales/es/.mustflow/skills/docs-update/SKILL.md +0 -97
- package/templates/default/locales/es/.mustflow/skills/external-prompt-injection-defense/SKILL.md +0 -116
- package/templates/default/locales/es/.mustflow/skills/facade-pattern/SKILL.md +0 -210
- package/templates/default/locales/es/.mustflow/skills/failure-triage/SKILL.md +0 -97
- package/templates/default/locales/es/.mustflow/skills/instruction-conflict-scope-check/SKILL.md +0 -118
- package/templates/default/locales/es/.mustflow/skills/line-ending-hygiene/SKILL.md +0 -111
- package/templates/default/locales/es/.mustflow/skills/migration-safety-check/SKILL.md +0 -117
- package/templates/default/locales/es/.mustflow/skills/multi-agent-work-coordination/SKILL.md +0 -260
- package/templates/default/locales/es/.mustflow/skills/null-object-pattern/SKILL.md +0 -196
- package/templates/default/locales/es/.mustflow/skills/pattern-scout/SKILL.md +0 -110
- package/templates/default/locales/es/.mustflow/skills/performance-budget-check/SKILL.md +0 -121
- package/templates/default/locales/es/.mustflow/skills/project-context-authoring/SKILL.md +0 -107
- package/templates/default/locales/es/.mustflow/skills/pure-core-imperative-shell/SKILL.md +0 -212
- package/templates/default/locales/es/.mustflow/skills/readme-authoring/SKILL.md +0 -115
- package/templates/default/locales/es/.mustflow/skills/repo-improvement-loop/SKILL.md +0 -150
- package/templates/default/locales/es/.mustflow/skills/repro-first-debug/SKILL.md +0 -112
- package/templates/default/locales/es/.mustflow/skills/requirement-regression-guard/SKILL.md +0 -152
- package/templates/default/locales/es/.mustflow/skills/result-option/SKILL.md +0 -186
- package/templates/default/locales/es/.mustflow/skills/security-privacy-review/SKILL.md +0 -116
- package/templates/default/locales/es/.mustflow/skills/security-regression-tests/SKILL.md +0 -131
- package/templates/default/locales/es/.mustflow/skills/skill-authoring/SKILL.md +0 -110
- package/templates/default/locales/es/.mustflow/skills/source-freshness-check/SKILL.md +0 -111
- package/templates/default/locales/es/.mustflow/skills/state-machine-pattern/SKILL.md +0 -214
- package/templates/default/locales/es/.mustflow/skills/strategy-pattern/SKILL.md +0 -215
- package/templates/default/locales/es/.mustflow/skills/structure-discovery-gate/SKILL.md +0 -159
- package/templates/default/locales/es/.mustflow/skills/test-design-guard/SKILL.md +0 -162
- package/templates/default/locales/es/.mustflow/skills/test-maintenance/SKILL.md +0 -122
- package/templates/default/locales/es/.mustflow/skills/ui-quality-gate/SKILL.md +0 -117
- package/templates/default/locales/es/.mustflow/skills/visual-review-artifact/SKILL.md +0 -127
- package/templates/default/locales/es/.mustflow/skills/visual-review-artifact/assets/review-template.html +0 -286
- package/templates/default/locales/es/.mustflow/skills/visual-review-artifact/resources.toml +0 -7
- package/templates/default/locales/es/.mustflow/skills/web-asset-optimization/SKILL.md +0 -108
- package/templates/default/locales/fr/.mustflow/skills/INDEX.md +0 -75
- package/templates/default/locales/fr/.mustflow/skills/adapter-boundary/SKILL.md +0 -193
- package/templates/default/locales/fr/.mustflow/skills/artifact-integrity-check/SKILL.md +0 -114
- package/templates/default/locales/fr/.mustflow/skills/behavior-preserving-refactor/SKILL.md +0 -182
- package/templates/default/locales/fr/.mustflow/skills/code-review/SKILL.md +0 -115
- package/templates/default/locales/fr/.mustflow/skills/codebase-orientation/SKILL.md +0 -115
- package/templates/default/locales/fr/.mustflow/skills/command-pattern/SKILL.md +0 -247
- package/templates/default/locales/fr/.mustflow/skills/composition-over-inheritance/SKILL.md +0 -176
- package/templates/default/locales/fr/.mustflow/skills/contract-sync-check/SKILL.md +0 -116
- package/templates/default/locales/fr/.mustflow/skills/database-change-safety/SKILL.md +0 -155
- package/templates/default/locales/fr/.mustflow/skills/date-number-audit/SKILL.md +0 -116
- package/templates/default/locales/fr/.mustflow/skills/dependency-injection/SKILL.md +0 -161
- package/templates/default/locales/fr/.mustflow/skills/dependency-reality-check/SKILL.md +0 -115
- package/templates/default/locales/fr/.mustflow/skills/diff-risk-review/SKILL.md +0 -136
- package/templates/default/locales/fr/.mustflow/skills/docs-prose-review/SKILL.md +0 -119
- package/templates/default/locales/fr/.mustflow/skills/docs-update/SKILL.md +0 -97
- package/templates/default/locales/fr/.mustflow/skills/external-prompt-injection-defense/SKILL.md +0 -116
- package/templates/default/locales/fr/.mustflow/skills/facade-pattern/SKILL.md +0 -210
- package/templates/default/locales/fr/.mustflow/skills/failure-triage/SKILL.md +0 -97
- package/templates/default/locales/fr/.mustflow/skills/instruction-conflict-scope-check/SKILL.md +0 -118
- package/templates/default/locales/fr/.mustflow/skills/line-ending-hygiene/SKILL.md +0 -111
- package/templates/default/locales/fr/.mustflow/skills/migration-safety-check/SKILL.md +0 -117
- package/templates/default/locales/fr/.mustflow/skills/multi-agent-work-coordination/SKILL.md +0 -260
- package/templates/default/locales/fr/.mustflow/skills/null-object-pattern/SKILL.md +0 -196
- package/templates/default/locales/fr/.mustflow/skills/pattern-scout/SKILL.md +0 -110
- package/templates/default/locales/fr/.mustflow/skills/performance-budget-check/SKILL.md +0 -121
- package/templates/default/locales/fr/.mustflow/skills/project-context-authoring/SKILL.md +0 -107
- package/templates/default/locales/fr/.mustflow/skills/pure-core-imperative-shell/SKILL.md +0 -212
- package/templates/default/locales/fr/.mustflow/skills/readme-authoring/SKILL.md +0 -115
- package/templates/default/locales/fr/.mustflow/skills/repo-improvement-loop/SKILL.md +0 -150
- package/templates/default/locales/fr/.mustflow/skills/repro-first-debug/SKILL.md +0 -112
- package/templates/default/locales/fr/.mustflow/skills/requirement-regression-guard/SKILL.md +0 -152
- package/templates/default/locales/fr/.mustflow/skills/result-option/SKILL.md +0 -186
- package/templates/default/locales/fr/.mustflow/skills/security-privacy-review/SKILL.md +0 -116
- package/templates/default/locales/fr/.mustflow/skills/security-regression-tests/SKILL.md +0 -131
- package/templates/default/locales/fr/.mustflow/skills/skill-authoring/SKILL.md +0 -110
- package/templates/default/locales/fr/.mustflow/skills/source-freshness-check/SKILL.md +0 -111
- package/templates/default/locales/fr/.mustflow/skills/state-machine-pattern/SKILL.md +0 -214
- package/templates/default/locales/fr/.mustflow/skills/strategy-pattern/SKILL.md +0 -215
- package/templates/default/locales/fr/.mustflow/skills/structure-discovery-gate/SKILL.md +0 -159
- package/templates/default/locales/fr/.mustflow/skills/test-design-guard/SKILL.md +0 -162
- package/templates/default/locales/fr/.mustflow/skills/test-maintenance/SKILL.md +0 -122
- package/templates/default/locales/fr/.mustflow/skills/ui-quality-gate/SKILL.md +0 -117
- package/templates/default/locales/fr/.mustflow/skills/visual-review-artifact/SKILL.md +0 -127
- package/templates/default/locales/fr/.mustflow/skills/visual-review-artifact/assets/review-template.html +0 -286
- package/templates/default/locales/fr/.mustflow/skills/visual-review-artifact/resources.toml +0 -7
- package/templates/default/locales/fr/.mustflow/skills/web-asset-optimization/SKILL.md +0 -108
- package/templates/default/locales/hi/.mustflow/skills/INDEX.md +0 -75
- package/templates/default/locales/hi/.mustflow/skills/adapter-boundary/SKILL.md +0 -193
- package/templates/default/locales/hi/.mustflow/skills/artifact-integrity-check/SKILL.md +0 -114
- package/templates/default/locales/hi/.mustflow/skills/behavior-preserving-refactor/SKILL.md +0 -182
- package/templates/default/locales/hi/.mustflow/skills/code-review/SKILL.md +0 -115
- package/templates/default/locales/hi/.mustflow/skills/codebase-orientation/SKILL.md +0 -115
- package/templates/default/locales/hi/.mustflow/skills/command-pattern/SKILL.md +0 -247
- package/templates/default/locales/hi/.mustflow/skills/composition-over-inheritance/SKILL.md +0 -176
- package/templates/default/locales/hi/.mustflow/skills/contract-sync-check/SKILL.md +0 -116
- package/templates/default/locales/hi/.mustflow/skills/database-change-safety/SKILL.md +0 -155
- package/templates/default/locales/hi/.mustflow/skills/date-number-audit/SKILL.md +0 -116
- package/templates/default/locales/hi/.mustflow/skills/dependency-injection/SKILL.md +0 -161
- package/templates/default/locales/hi/.mustflow/skills/dependency-reality-check/SKILL.md +0 -115
- package/templates/default/locales/hi/.mustflow/skills/diff-risk-review/SKILL.md +0 -136
- package/templates/default/locales/hi/.mustflow/skills/docs-prose-review/SKILL.md +0 -119
- package/templates/default/locales/hi/.mustflow/skills/docs-update/SKILL.md +0 -97
- package/templates/default/locales/hi/.mustflow/skills/external-prompt-injection-defense/SKILL.md +0 -116
- package/templates/default/locales/hi/.mustflow/skills/facade-pattern/SKILL.md +0 -210
- package/templates/default/locales/hi/.mustflow/skills/failure-triage/SKILL.md +0 -97
- package/templates/default/locales/hi/.mustflow/skills/instruction-conflict-scope-check/SKILL.md +0 -118
- package/templates/default/locales/hi/.mustflow/skills/line-ending-hygiene/SKILL.md +0 -111
- package/templates/default/locales/hi/.mustflow/skills/migration-safety-check/SKILL.md +0 -117
- package/templates/default/locales/hi/.mustflow/skills/multi-agent-work-coordination/SKILL.md +0 -260
- package/templates/default/locales/hi/.mustflow/skills/null-object-pattern/SKILL.md +0 -196
- package/templates/default/locales/hi/.mustflow/skills/pattern-scout/SKILL.md +0 -110
- package/templates/default/locales/hi/.mustflow/skills/performance-budget-check/SKILL.md +0 -121
- package/templates/default/locales/hi/.mustflow/skills/project-context-authoring/SKILL.md +0 -107
- package/templates/default/locales/hi/.mustflow/skills/pure-core-imperative-shell/SKILL.md +0 -212
- package/templates/default/locales/hi/.mustflow/skills/readme-authoring/SKILL.md +0 -115
- package/templates/default/locales/hi/.mustflow/skills/repo-improvement-loop/SKILL.md +0 -150
- package/templates/default/locales/hi/.mustflow/skills/repro-first-debug/SKILL.md +0 -112
- package/templates/default/locales/hi/.mustflow/skills/requirement-regression-guard/SKILL.md +0 -152
- package/templates/default/locales/hi/.mustflow/skills/result-option/SKILL.md +0 -186
- package/templates/default/locales/hi/.mustflow/skills/security-privacy-review/SKILL.md +0 -116
- package/templates/default/locales/hi/.mustflow/skills/security-regression-tests/SKILL.md +0 -131
- package/templates/default/locales/hi/.mustflow/skills/skill-authoring/SKILL.md +0 -110
- package/templates/default/locales/hi/.mustflow/skills/source-freshness-check/SKILL.md +0 -111
- package/templates/default/locales/hi/.mustflow/skills/state-machine-pattern/SKILL.md +0 -214
- package/templates/default/locales/hi/.mustflow/skills/strategy-pattern/SKILL.md +0 -215
- package/templates/default/locales/hi/.mustflow/skills/structure-discovery-gate/SKILL.md +0 -159
- package/templates/default/locales/hi/.mustflow/skills/test-design-guard/SKILL.md +0 -162
- package/templates/default/locales/hi/.mustflow/skills/test-maintenance/SKILL.md +0 -122
- package/templates/default/locales/hi/.mustflow/skills/ui-quality-gate/SKILL.md +0 -117
- package/templates/default/locales/hi/.mustflow/skills/visual-review-artifact/SKILL.md +0 -127
- package/templates/default/locales/hi/.mustflow/skills/visual-review-artifact/assets/review-template.html +0 -286
- package/templates/default/locales/hi/.mustflow/skills/visual-review-artifact/resources.toml +0 -7
- package/templates/default/locales/hi/.mustflow/skills/web-asset-optimization/SKILL.md +0 -108
- package/templates/default/locales/ko/.mustflow/skills/INDEX.md +0 -80
- package/templates/default/locales/ko/.mustflow/skills/adapter-boundary/SKILL.md +0 -193
- package/templates/default/locales/ko/.mustflow/skills/artifact-integrity-check/SKILL.md +0 -114
- package/templates/default/locales/ko/.mustflow/skills/behavior-preserving-refactor/SKILL.md +0 -182
- package/templates/default/locales/ko/.mustflow/skills/code-review/SKILL.md +0 -118
- package/templates/default/locales/ko/.mustflow/skills/codebase-orientation/SKILL.md +0 -115
- package/templates/default/locales/ko/.mustflow/skills/command-pattern/SKILL.md +0 -247
- package/templates/default/locales/ko/.mustflow/skills/composition-over-inheritance/SKILL.md +0 -176
- package/templates/default/locales/ko/.mustflow/skills/contract-sync-check/SKILL.md +0 -116
- package/templates/default/locales/ko/.mustflow/skills/database-change-safety/SKILL.md +0 -155
- package/templates/default/locales/ko/.mustflow/skills/date-number-audit/SKILL.md +0 -116
- package/templates/default/locales/ko/.mustflow/skills/dependency-injection/SKILL.md +0 -161
- package/templates/default/locales/ko/.mustflow/skills/dependency-reality-check/SKILL.md +0 -115
- package/templates/default/locales/ko/.mustflow/skills/diff-risk-review/SKILL.md +0 -136
- package/templates/default/locales/ko/.mustflow/skills/docs-prose-review/SKILL.md +0 -119
- package/templates/default/locales/ko/.mustflow/skills/docs-update/SKILL.md +0 -107
- package/templates/default/locales/ko/.mustflow/skills/external-prompt-injection-defense/SKILL.md +0 -116
- package/templates/default/locales/ko/.mustflow/skills/facade-pattern/SKILL.md +0 -210
- package/templates/default/locales/ko/.mustflow/skills/failure-triage/SKILL.md +0 -119
- package/templates/default/locales/ko/.mustflow/skills/instruction-conflict-scope-check/SKILL.md +0 -118
- package/templates/default/locales/ko/.mustflow/skills/line-ending-hygiene/SKILL.md +0 -111
- package/templates/default/locales/ko/.mustflow/skills/migration-safety-check/SKILL.md +0 -117
- package/templates/default/locales/ko/.mustflow/skills/multi-agent-work-coordination/SKILL.md +0 -259
- package/templates/default/locales/ko/.mustflow/skills/null-object-pattern/SKILL.md +0 -196
- package/templates/default/locales/ko/.mustflow/skills/pattern-scout/SKILL.md +0 -110
- package/templates/default/locales/ko/.mustflow/skills/performance-budget-check/SKILL.md +0 -121
- package/templates/default/locales/ko/.mustflow/skills/project-context-authoring/SKILL.md +0 -107
- package/templates/default/locales/ko/.mustflow/skills/pure-core-imperative-shell/SKILL.md +0 -212
- package/templates/default/locales/ko/.mustflow/skills/readme-authoring/SKILL.md +0 -115
- package/templates/default/locales/ko/.mustflow/skills/repo-improvement-loop/SKILL.md +0 -150
- package/templates/default/locales/ko/.mustflow/skills/repro-first-debug/SKILL.md +0 -112
- package/templates/default/locales/ko/.mustflow/skills/requirement-regression-guard/SKILL.md +0 -152
- package/templates/default/locales/ko/.mustflow/skills/result-option/SKILL.md +0 -186
- package/templates/default/locales/ko/.mustflow/skills/security-privacy-review/SKILL.md +0 -116
- package/templates/default/locales/ko/.mustflow/skills/security-regression-tests/SKILL.md +0 -131
- package/templates/default/locales/ko/.mustflow/skills/skill-authoring/SKILL.md +0 -110
- package/templates/default/locales/ko/.mustflow/skills/source-freshness-check/SKILL.md +0 -111
- package/templates/default/locales/ko/.mustflow/skills/state-machine-pattern/SKILL.md +0 -214
- package/templates/default/locales/ko/.mustflow/skills/strategy-pattern/SKILL.md +0 -215
- package/templates/default/locales/ko/.mustflow/skills/structure-discovery-gate/SKILL.md +0 -159
- package/templates/default/locales/ko/.mustflow/skills/test-design-guard/SKILL.md +0 -162
- package/templates/default/locales/ko/.mustflow/skills/test-maintenance/SKILL.md +0 -130
- package/templates/default/locales/ko/.mustflow/skills/ui-quality-gate/SKILL.md +0 -117
- package/templates/default/locales/ko/.mustflow/skills/visual-review-artifact/SKILL.md +0 -127
- package/templates/default/locales/ko/.mustflow/skills/visual-review-artifact/assets/review-template.html +0 -286
- package/templates/default/locales/ko/.mustflow/skills/visual-review-artifact/resources.toml +0 -7
- package/templates/default/locales/ko/.mustflow/skills/web-asset-optimization/SKILL.md +0 -108
- package/templates/default/locales/zh/.mustflow/skills/INDEX.md +0 -74
- package/templates/default/locales/zh/.mustflow/skills/adapter-boundary/SKILL.md +0 -193
- package/templates/default/locales/zh/.mustflow/skills/artifact-integrity-check/SKILL.md +0 -114
- package/templates/default/locales/zh/.mustflow/skills/behavior-preserving-refactor/SKILL.md +0 -182
- package/templates/default/locales/zh/.mustflow/skills/code-review/SKILL.md +0 -115
- package/templates/default/locales/zh/.mustflow/skills/codebase-orientation/SKILL.md +0 -115
- package/templates/default/locales/zh/.mustflow/skills/command-pattern/SKILL.md +0 -247
- package/templates/default/locales/zh/.mustflow/skills/composition-over-inheritance/SKILL.md +0 -176
- package/templates/default/locales/zh/.mustflow/skills/contract-sync-check/SKILL.md +0 -116
- package/templates/default/locales/zh/.mustflow/skills/database-change-safety/SKILL.md +0 -155
- package/templates/default/locales/zh/.mustflow/skills/date-number-audit/SKILL.md +0 -116
- package/templates/default/locales/zh/.mustflow/skills/dependency-injection/SKILL.md +0 -161
- package/templates/default/locales/zh/.mustflow/skills/dependency-reality-check/SKILL.md +0 -115
- package/templates/default/locales/zh/.mustflow/skills/diff-risk-review/SKILL.md +0 -136
- package/templates/default/locales/zh/.mustflow/skills/docs-prose-review/SKILL.md +0 -119
- package/templates/default/locales/zh/.mustflow/skills/docs-update/SKILL.md +0 -97
- package/templates/default/locales/zh/.mustflow/skills/external-prompt-injection-defense/SKILL.md +0 -116
- package/templates/default/locales/zh/.mustflow/skills/facade-pattern/SKILL.md +0 -210
- package/templates/default/locales/zh/.mustflow/skills/failure-triage/SKILL.md +0 -96
- package/templates/default/locales/zh/.mustflow/skills/instruction-conflict-scope-check/SKILL.md +0 -118
- package/templates/default/locales/zh/.mustflow/skills/line-ending-hygiene/SKILL.md +0 -111
- package/templates/default/locales/zh/.mustflow/skills/migration-safety-check/SKILL.md +0 -117
- package/templates/default/locales/zh/.mustflow/skills/multi-agent-work-coordination/SKILL.md +0 -260
- package/templates/default/locales/zh/.mustflow/skills/null-object-pattern/SKILL.md +0 -196
- package/templates/default/locales/zh/.mustflow/skills/pattern-scout/SKILL.md +0 -110
- package/templates/default/locales/zh/.mustflow/skills/performance-budget-check/SKILL.md +0 -121
- package/templates/default/locales/zh/.mustflow/skills/project-context-authoring/SKILL.md +0 -107
- package/templates/default/locales/zh/.mustflow/skills/pure-core-imperative-shell/SKILL.md +0 -212
- package/templates/default/locales/zh/.mustflow/skills/readme-authoring/SKILL.md +0 -115
- package/templates/default/locales/zh/.mustflow/skills/repo-improvement-loop/SKILL.md +0 -150
- package/templates/default/locales/zh/.mustflow/skills/repro-first-debug/SKILL.md +0 -112
- package/templates/default/locales/zh/.mustflow/skills/requirement-regression-guard/SKILL.md +0 -152
- package/templates/default/locales/zh/.mustflow/skills/result-option/SKILL.md +0 -186
- package/templates/default/locales/zh/.mustflow/skills/security-privacy-review/SKILL.md +0 -116
- package/templates/default/locales/zh/.mustflow/skills/security-regression-tests/SKILL.md +0 -131
- package/templates/default/locales/zh/.mustflow/skills/skill-authoring/SKILL.md +0 -110
- package/templates/default/locales/zh/.mustflow/skills/source-freshness-check/SKILL.md +0 -111
- package/templates/default/locales/zh/.mustflow/skills/state-machine-pattern/SKILL.md +0 -214
- package/templates/default/locales/zh/.mustflow/skills/strategy-pattern/SKILL.md +0 -215
- package/templates/default/locales/zh/.mustflow/skills/structure-discovery-gate/SKILL.md +0 -159
- package/templates/default/locales/zh/.mustflow/skills/test-design-guard/SKILL.md +0 -162
- package/templates/default/locales/zh/.mustflow/skills/test-maintenance/SKILL.md +0 -122
- package/templates/default/locales/zh/.mustflow/skills/ui-quality-gate/SKILL.md +0 -117
- package/templates/default/locales/zh/.mustflow/skills/visual-review-artifact/SKILL.md +0 -127
- package/templates/default/locales/zh/.mustflow/skills/visual-review-artifact/assets/review-template.html +0 -286
- package/templates/default/locales/zh/.mustflow/skills/visual-review-artifact/resources.toml +0 -7
- package/templates/default/locales/zh/.mustflow/skills/web-asset-optimization/SKILL.md +0 -108
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import { lstatSync, readlinkSync, readdirSync } from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { normalizeCommandEffects } from './command-effects.js';
|
|
5
|
+
const MAX_SNAPSHOT_FILES = 20_000;
|
|
6
|
+
const MAX_REPORTED_PATHS = 200;
|
|
7
|
+
const RECURSIVE_SNAPSHOT_ENV = 'MUSTFLOW_WRITE_DRIFT_SNAPSHOT';
|
|
8
|
+
const EXCLUDED_DIRECTORY_NAMES = new Set(['.git', 'node_modules']);
|
|
9
|
+
const EXCLUDED_RELATIVE_DIRECTORY_PATHS = new Set(['.mustflow/state/runs']);
|
|
10
|
+
function isRecursiveSnapshotEnabled() {
|
|
11
|
+
const value = process.env[RECURSIVE_SNAPSHOT_ENV];
|
|
12
|
+
return value === '1' || value?.toLowerCase() === 'true';
|
|
13
|
+
}
|
|
14
|
+
function toPosixPath(value) {
|
|
15
|
+
return value.split(path.sep).join('/');
|
|
16
|
+
}
|
|
17
|
+
function normalizeRelativePath(value) {
|
|
18
|
+
return toPosixPath(value).replace(/^\.\/+/u, '').replace(/\/+$/u, '') || '.';
|
|
19
|
+
}
|
|
20
|
+
function pathKey(value) {
|
|
21
|
+
const normalized = normalizeRelativePath(value);
|
|
22
|
+
return process.platform === 'win32' ? normalized.toLowerCase() : normalized;
|
|
23
|
+
}
|
|
24
|
+
function isExcludedDirectory(relativePath, basename) {
|
|
25
|
+
const normalized = normalizeRelativePath(relativePath);
|
|
26
|
+
return EXCLUDED_DIRECTORY_NAMES.has(basename) || EXCLUDED_RELATIVE_DIRECTORY_PATHS.has(normalized);
|
|
27
|
+
}
|
|
28
|
+
function signatureForPath(fullPath) {
|
|
29
|
+
const stat = lstatSync(fullPath);
|
|
30
|
+
if (stat.isSymbolicLink()) {
|
|
31
|
+
return `symlink:${readlinkSync(fullPath)}`;
|
|
32
|
+
}
|
|
33
|
+
const type = stat.isDirectory() ? 'directory' : stat.isFile() ? 'file' : 'other';
|
|
34
|
+
return `${type}:${stat.size}:${stat.mtimeMs}`;
|
|
35
|
+
}
|
|
36
|
+
function collectSnapshotEntries(projectRoot, currentPath, entries) {
|
|
37
|
+
const names = readdirSync(currentPath).sort((left, right) => left.localeCompare(right));
|
|
38
|
+
for (const name of names) {
|
|
39
|
+
const fullPath = path.join(currentPath, name);
|
|
40
|
+
const relativePath = normalizeRelativePath(path.relative(projectRoot, fullPath));
|
|
41
|
+
const stat = lstatSync(fullPath);
|
|
42
|
+
if (stat.isDirectory()) {
|
|
43
|
+
if (isExcludedDirectory(relativePath, name)) {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
collectSnapshotEntries(projectRoot, fullPath, entries);
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
if (entries.size >= MAX_SNAPSHOT_FILES) {
|
|
50
|
+
throw new Error('snapshot_file_limit_exceeded');
|
|
51
|
+
}
|
|
52
|
+
entries.set(relativePath, signatureForPath(fullPath));
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function captureSnapshot(projectRoot) {
|
|
56
|
+
const gitSnapshot = captureGitStatusSnapshot(projectRoot);
|
|
57
|
+
if (gitSnapshot) {
|
|
58
|
+
return gitSnapshot;
|
|
59
|
+
}
|
|
60
|
+
if (!isRecursiveSnapshotEnabled()) {
|
|
61
|
+
return {
|
|
62
|
+
ok: false,
|
|
63
|
+
entries: new Map(),
|
|
64
|
+
reason: 'git_status_unavailable_recursive_snapshot_disabled',
|
|
65
|
+
source: 'unavailable',
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
try {
|
|
69
|
+
const entries = new Map();
|
|
70
|
+
collectSnapshotEntries(projectRoot, projectRoot, entries);
|
|
71
|
+
return { ok: true, entries, reason: null, source: 'recursive_snapshot' };
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
return {
|
|
75
|
+
ok: false,
|
|
76
|
+
entries: new Map(),
|
|
77
|
+
reason: error instanceof Error && error.message.length > 0 ? error.message : 'snapshot_unavailable',
|
|
78
|
+
source: 'unavailable',
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
function captureGitStatusSnapshot(projectRoot) {
|
|
83
|
+
const result = spawnSync('git', ['-C', projectRoot, 'status', '--porcelain=v1', '-z', '--untracked-files=all'], {
|
|
84
|
+
encoding: 'utf8',
|
|
85
|
+
input: '',
|
|
86
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
87
|
+
windowsHide: true,
|
|
88
|
+
});
|
|
89
|
+
if (result.error || result.status !== 0) {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
const entries = new Map();
|
|
93
|
+
const parts = result.stdout.split('\0').filter((part) => part.length > 0);
|
|
94
|
+
for (let index = 0; index < parts.length; index += 1) {
|
|
95
|
+
const entry = parts[index] ?? '';
|
|
96
|
+
const status = entry.slice(0, 2);
|
|
97
|
+
const filePath = normalizeRelativePath(entry.slice(3));
|
|
98
|
+
if (filePath.length === 0) {
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
entries.set(filePath, `git:${status}`);
|
|
102
|
+
if (status.includes('R') || status.includes('C')) {
|
|
103
|
+
index += 1;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return {
|
|
107
|
+
ok: true,
|
|
108
|
+
entries,
|
|
109
|
+
reason: null,
|
|
110
|
+
source: 'git_status',
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
function listDeclaredWritePaths(projectRoot, contract, intentName) {
|
|
114
|
+
const paths = normalizeCommandEffects(projectRoot, contract, intentName)
|
|
115
|
+
.filter((effect) => effect.access === 'write')
|
|
116
|
+
.map((effect) => effect.path)
|
|
117
|
+
.filter((effectPath) => typeof effectPath === 'string');
|
|
118
|
+
return [...new Set(paths.map(normalizeRelativePath))].sort((left, right) => left.localeCompare(right));
|
|
119
|
+
}
|
|
120
|
+
function listObservedChangedPaths(before, after) {
|
|
121
|
+
const paths = new Set([...before.keys(), ...after.keys()]);
|
|
122
|
+
const changed = [];
|
|
123
|
+
for (const filePath of paths) {
|
|
124
|
+
if (before.get(filePath) !== after.get(filePath)) {
|
|
125
|
+
changed.push(filePath);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return changed.sort((left, right) => left.localeCompare(right));
|
|
129
|
+
}
|
|
130
|
+
function declaredPathCoversObservedPath(declaredPath, observedPath) {
|
|
131
|
+
const declaredKey = pathKey(declaredPath);
|
|
132
|
+
const observedKey = pathKey(observedPath);
|
|
133
|
+
return declaredKey === '.' || observedKey === declaredKey || observedKey.startsWith(`${declaredKey}/`);
|
|
134
|
+
}
|
|
135
|
+
function truncatePaths(paths) {
|
|
136
|
+
if (paths.length <= MAX_REPORTED_PATHS) {
|
|
137
|
+
return { paths, truncated: false };
|
|
138
|
+
}
|
|
139
|
+
return { paths: paths.slice(0, MAX_REPORTED_PATHS), truncated: true };
|
|
140
|
+
}
|
|
141
|
+
export function startRunWriteTracking(projectRoot, contract, intentName) {
|
|
142
|
+
return {
|
|
143
|
+
projectRoot,
|
|
144
|
+
declaredPaths: listDeclaredWritePaths(projectRoot, contract, intentName),
|
|
145
|
+
before: captureSnapshot(projectRoot),
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
export function finishRunWriteTracking(tracker) {
|
|
149
|
+
if (!tracker.before.ok) {
|
|
150
|
+
return {
|
|
151
|
+
status: 'unavailable',
|
|
152
|
+
declared_paths: tracker.declaredPaths,
|
|
153
|
+
observed_paths: [],
|
|
154
|
+
declared_observed_paths: [],
|
|
155
|
+
undeclared_paths: [],
|
|
156
|
+
observed_count: 0,
|
|
157
|
+
undeclared_count: 0,
|
|
158
|
+
has_undeclared_changes: false,
|
|
159
|
+
truncated: false,
|
|
160
|
+
reason: tracker.before.reason,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
const after = captureSnapshot(tracker.projectRoot);
|
|
164
|
+
if (!after.ok) {
|
|
165
|
+
return {
|
|
166
|
+
status: 'unavailable',
|
|
167
|
+
declared_paths: tracker.declaredPaths,
|
|
168
|
+
observed_paths: [],
|
|
169
|
+
declared_observed_paths: [],
|
|
170
|
+
undeclared_paths: [],
|
|
171
|
+
observed_count: 0,
|
|
172
|
+
undeclared_count: 0,
|
|
173
|
+
has_undeclared_changes: false,
|
|
174
|
+
truncated: false,
|
|
175
|
+
reason: after.reason,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
const observedPaths = listObservedChangedPaths(tracker.before.entries, after.entries);
|
|
179
|
+
const declaredObservedPaths = observedPaths.filter((observedPath) => tracker.declaredPaths.some((declaredPath) => declaredPathCoversObservedPath(declaredPath, observedPath)));
|
|
180
|
+
const undeclaredPaths = observedPaths.filter((observedPath) => !tracker.declaredPaths.some((declaredPath) => declaredPathCoversObservedPath(declaredPath, observedPath)));
|
|
181
|
+
const observed = truncatePaths(observedPaths);
|
|
182
|
+
const declaredObserved = truncatePaths(declaredObservedPaths);
|
|
183
|
+
const undeclared = truncatePaths(undeclaredPaths);
|
|
184
|
+
return {
|
|
185
|
+
status: 'checked',
|
|
186
|
+
declared_paths: tracker.declaredPaths,
|
|
187
|
+
observed_paths: observed.paths,
|
|
188
|
+
declared_observed_paths: declaredObserved.paths,
|
|
189
|
+
undeclared_paths: undeclared.paths,
|
|
190
|
+
observed_count: observedPaths.length,
|
|
191
|
+
undeclared_count: undeclaredPaths.length,
|
|
192
|
+
has_undeclared_changes: undeclaredPaths.length > 0,
|
|
193
|
+
truncated: observed.truncated || declaredObserved.truncated || undeclared.truncated,
|
|
194
|
+
reason: null,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export const REDACTED_SECRET_MARKER = '[REDACTED_SECRET]';
|
|
2
|
+
const SECRET_VALUE_PATTERN = '[A-Za-z0-9_./+=:-]{8,}';
|
|
3
|
+
const SECRET_REDACTION_RULES = [
|
|
4
|
+
{
|
|
5
|
+
kind: 'secret_key_value',
|
|
6
|
+
pattern: new RegExp(`\\b((?:api[_-]?key|api[_-]?token|access[_-]?token|auth[_-]?token|secret|password|passwd|private[_-]?key)\\b\\s*[:=]\\s*["']?)(${SECRET_VALUE_PATTERN})`, 'giu'),
|
|
7
|
+
replace: (_match, prefix) => `${prefix}${REDACTED_SECRET_MARKER}`,
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
kind: 'secret_token',
|
|
11
|
+
pattern: /\b(?:sk-[A-Za-z0-9]{16,}|ghp_[A-Za-z0-9]{20,}|xox[baprs]-[A-Za-z0-9-]{20,}|AKIA[0-9A-Z]{16})\b/gu,
|
|
12
|
+
replace: () => REDACTED_SECRET_MARKER,
|
|
13
|
+
},
|
|
14
|
+
];
|
|
15
|
+
export const SECRET_LIKE_PATTERNS = SECRET_REDACTION_RULES.map((rule) => {
|
|
16
|
+
const flags = rule.pattern.flags.replace('g', '');
|
|
17
|
+
return new RegExp(rule.pattern.source, flags);
|
|
18
|
+
});
|
|
19
|
+
export function textContainsSecretLike(value) {
|
|
20
|
+
return SECRET_LIKE_PATTERNS.some((pattern) => pattern.test(value));
|
|
21
|
+
}
|
|
22
|
+
export function redactSecretLikeText(value) {
|
|
23
|
+
let text = value;
|
|
24
|
+
let redactionCount = 0;
|
|
25
|
+
const redactionKinds = new Set();
|
|
26
|
+
for (const rule of SECRET_REDACTION_RULES) {
|
|
27
|
+
text = text.replace(rule.pattern, (...match) => {
|
|
28
|
+
redactionCount += 1;
|
|
29
|
+
redactionKinds.add(rule.kind);
|
|
30
|
+
return rule.replace(...match.map(String));
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
return {
|
|
34
|
+
text,
|
|
35
|
+
redacted: redactionCount > 0,
|
|
36
|
+
redactionCount,
|
|
37
|
+
redactionKinds: [...redactionKinds].sort(),
|
|
38
|
+
};
|
|
39
|
+
}
|
|
@@ -5,6 +5,17 @@ const SKILL_ROUTE_SOURCE_FILES = [
|
|
|
5
5
|
'.mustflow/docs/agent-workflow.md',
|
|
6
6
|
];
|
|
7
7
|
const MARKDOWN_TABLE_SEPARATOR_PATTERN = /^:?-{3,}:?$/u;
|
|
8
|
+
const BROAD_CATCH_ALL_TRIGGERS = new Set([
|
|
9
|
+
'any request',
|
|
10
|
+
'any task',
|
|
11
|
+
'any change',
|
|
12
|
+
'all requests',
|
|
13
|
+
'all tasks',
|
|
14
|
+
'all changes',
|
|
15
|
+
'every request',
|
|
16
|
+
'every task',
|
|
17
|
+
'everything',
|
|
18
|
+
]);
|
|
8
19
|
export const SKILL_INDEX_ROUTE_COLUMN_COUNT = 7;
|
|
9
20
|
export const SKILL_INDEX_SKILL_PATH_COLUMN_INDEX = 1;
|
|
10
21
|
export const SKILL_INDEX_VERIFICATION_INTENTS_COLUMN_INDEX = 5;
|
|
@@ -56,11 +67,90 @@ export function parseSkillIndexRoutes(content) {
|
|
|
56
67
|
}
|
|
57
68
|
return routes;
|
|
58
69
|
}
|
|
70
|
+
function normalizeRouteText(value) {
|
|
71
|
+
return value
|
|
72
|
+
.toLowerCase()
|
|
73
|
+
.replace(/`[^`]+`/gu, ' ')
|
|
74
|
+
.replace(/<[^>]+>/gu, ' ')
|
|
75
|
+
.replace(/[^a-z0-9]+/gu, ' ')
|
|
76
|
+
.trim()
|
|
77
|
+
.replace(/\s+/gu, ' ');
|
|
78
|
+
}
|
|
79
|
+
function collectRoutePairs(routes) {
|
|
80
|
+
const pairs = [];
|
|
81
|
+
for (let leftIndex = 0; leftIndex < routes.length; leftIndex += 1) {
|
|
82
|
+
for (let rightIndex = leftIndex + 1; rightIndex < routes.length; rightIndex += 1) {
|
|
83
|
+
const left = routes[leftIndex];
|
|
84
|
+
const right = routes[rightIndex];
|
|
85
|
+
if (left.skillPath !== right.skillPath) {
|
|
86
|
+
pairs.push({ left, right });
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return pairs;
|
|
91
|
+
}
|
|
92
|
+
function routePairLabel(pair) {
|
|
93
|
+
return `${pair.left.skillPath} and ${pair.right.skillPath}`;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* mf:anchor core.skill-route-conflict-lint
|
|
97
|
+
* purpose: Keep skill routing warnings deterministic so broad or duplicate route rows are review candidates, not LLM guesses.
|
|
98
|
+
* search: skill route conflict, duplicate trigger, broad catch-all route
|
|
99
|
+
* invariant: Route conflict warnings are heuristic warnings only; missing routes and command-intent drift remain strict errors.
|
|
100
|
+
* risk: config
|
|
101
|
+
*/
|
|
102
|
+
export function findSkillRouteConflictWarnings(routes) {
|
|
103
|
+
const warnings = [];
|
|
104
|
+
const triggerToRoutes = new Map();
|
|
105
|
+
const surfaceToRoutes = new Map();
|
|
106
|
+
for (const route of routes) {
|
|
107
|
+
const trigger = normalizeRouteText(route.trigger);
|
|
108
|
+
const editScope = normalizeRouteText(route.editScope);
|
|
109
|
+
const risk = normalizeRouteText(route.risk);
|
|
110
|
+
const expectedOutput = normalizeRouteText(route.expectedOutput);
|
|
111
|
+
if (BROAD_CATCH_ALL_TRIGGERS.has(trigger)) {
|
|
112
|
+
warnings.push(`${route.skillPath} route uses broad catch-all trigger "${route.trigger}" that can shadow narrower skills`);
|
|
113
|
+
}
|
|
114
|
+
if (trigger) {
|
|
115
|
+
triggerToRoutes.set(trigger, [...(triggerToRoutes.get(trigger) ?? []), route]);
|
|
116
|
+
}
|
|
117
|
+
if (editScope && risk && expectedOutput) {
|
|
118
|
+
const surfaceKey = `${editScope}\n${risk}\n${expectedOutput}`;
|
|
119
|
+
surfaceToRoutes.set(surfaceKey, [...(surfaceToRoutes.get(surfaceKey) ?? []), route]);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
for (const pair of collectRoutePairs([...triggerToRoutes.values()].flatMap((matchingRoutes) => {
|
|
123
|
+
return matchingRoutes.length > 1 ? matchingRoutes : [];
|
|
124
|
+
}))) {
|
|
125
|
+
if (normalizeRouteText(pair.left.trigger) === normalizeRouteText(pair.right.trigger)) {
|
|
126
|
+
warnings.push(`${routePairLabel(pair)} have identical skill route trigger text`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
for (const pair of collectRoutePairs([...surfaceToRoutes.values()].flatMap((matchingRoutes) => {
|
|
130
|
+
return matchingRoutes.length > 1 ? matchingRoutes : [];
|
|
131
|
+
}))) {
|
|
132
|
+
const leftSurface = [
|
|
133
|
+
normalizeRouteText(pair.left.editScope),
|
|
134
|
+
normalizeRouteText(pair.left.risk),
|
|
135
|
+
normalizeRouteText(pair.left.expectedOutput),
|
|
136
|
+
].join('\n');
|
|
137
|
+
const rightSurface = [
|
|
138
|
+
normalizeRouteText(pair.right.editScope),
|
|
139
|
+
normalizeRouteText(pair.right.risk),
|
|
140
|
+
normalizeRouteText(pair.right.expectedOutput),
|
|
141
|
+
].join('\n');
|
|
142
|
+
if (leftSurface === rightSurface) {
|
|
143
|
+
warnings.push(`${routePairLabel(pair)} have duplicate edit scope, risk, and expected output route surface`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return [...new Set(warnings)].sort((left, right) => left.localeCompare(right));
|
|
147
|
+
}
|
|
59
148
|
function pluralize(count, singular, plural) {
|
|
60
149
|
return count === 1 ? singular : plural;
|
|
61
150
|
}
|
|
62
151
|
export function isSkillRouteAlignmentIssue(issue) {
|
|
63
152
|
return (issue.includes('.mustflow/skills/INDEX.md route') ||
|
|
153
|
+
issue.includes('.mustflow/skills/INDEX.md .mustflow/skills/') ||
|
|
64
154
|
issue.includes('.mustflow/skills/INDEX.md has duplicate route') ||
|
|
65
155
|
issue.endsWith(' is not listed in .mustflow/skills/INDEX.md'));
|
|
66
156
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
+
import { SECRET_LIKE_PATTERNS, textContainsSecretLike } from './secret-redaction.js';
|
|
3
4
|
export const SOURCE_ANCHOR_EXTENSIONS = new Set(['.cjs', '.go', '.js', '.jsx', '.mjs', '.py', '.rs', '.ts', '.tsx']);
|
|
4
5
|
export const SOURCE_ANCHOR_DEFAULT_EXCLUDED_PATH_PARTS = new Set([
|
|
5
6
|
'.git',
|
|
@@ -44,10 +45,7 @@ export const SOURCE_ANCHOR_ALLOWED_RISKS = new Set([
|
|
|
44
45
|
'xss',
|
|
45
46
|
]);
|
|
46
47
|
export const SOURCE_ANCHOR_ID_PATTERN = /^[a-z0-9][a-z0-9.-]{0,95}$/u;
|
|
47
|
-
export const SOURCE_ANCHOR_SECRET_LIKE_PATTERNS =
|
|
48
|
-
/\b(?:api[_-]?key|api[_-]?token|access[_-]?token|auth[_-]?token|secret|password|passwd|private[_-]?key)\b\s*[:=]\s*["']?[A-Za-z0-9_./+=:-]{8,}/iu,
|
|
49
|
-
/\b(?:sk-[A-Za-z0-9]{16,}|ghp_[A-Za-z0-9]{20,}|xox[baprs]-[A-Za-z0-9-]{20,}|AKIA[0-9A-Z]{16})\b/u,
|
|
50
|
-
];
|
|
48
|
+
export const SOURCE_ANCHOR_SECRET_LIKE_PATTERNS = SECRET_LIKE_PATTERNS;
|
|
51
49
|
function toPosixPath(value) {
|
|
52
50
|
return value.split(path.sep).join('/');
|
|
53
51
|
}
|
|
@@ -189,7 +187,7 @@ export function sourceAnchorPathIsGeneratedOrVendor(relativePath) {
|
|
|
189
187
|
return parts.some((part) => SOURCE_ANCHOR_GENERATED_PATH_PARTS.has(part));
|
|
190
188
|
}
|
|
191
189
|
export function sourceAnchorTextContainsSecretLike(value) {
|
|
192
|
-
return
|
|
190
|
+
return textContainsSecretLike(value);
|
|
193
191
|
}
|
|
194
192
|
export function parseSourceAnchorsInContent(relativePath, content) {
|
|
195
193
|
const lines = content.split(/\r?\n/);
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { isRecord, readStringArray, resolveMustflowConfigPath, } from './config-loading.js';
|
|
3
|
+
import { readTomlFile } from './toml.js';
|
|
4
|
+
import { classifyVerificationCandidate, } from './verification-plan.js';
|
|
5
|
+
export const TEST_SELECTION_CONFIG_RELATIVE_PATH = '.mustflow/config/test-selection.toml';
|
|
6
|
+
function uniqueSorted(values) {
|
|
7
|
+
return [...new Set(values)].sort((left, right) => left.localeCompare(right));
|
|
8
|
+
}
|
|
9
|
+
function toPosixPath(value) {
|
|
10
|
+
return value.replace(/\\/g, '/');
|
|
11
|
+
}
|
|
12
|
+
function escapeRegExp(value) {
|
|
13
|
+
return value.replace(/[\\^$+?.()|[\]{}]/g, '\\$&');
|
|
14
|
+
}
|
|
15
|
+
function globToRegExp(pattern) {
|
|
16
|
+
const normalized = toPosixPath(pattern).replace(/^\/+/u, '');
|
|
17
|
+
let source = '^';
|
|
18
|
+
for (let index = 0; index < normalized.length;) {
|
|
19
|
+
if (normalized.startsWith('**', index)) {
|
|
20
|
+
source += '.*';
|
|
21
|
+
index += 2;
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
const char = normalized[index];
|
|
25
|
+
if (char === '*') {
|
|
26
|
+
source += '[^/]*';
|
|
27
|
+
index += 1;
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
source += escapeRegExp(char ?? '');
|
|
31
|
+
index += 1;
|
|
32
|
+
}
|
|
33
|
+
return new RegExp(`${source}$`, 'u');
|
|
34
|
+
}
|
|
35
|
+
function readStringField(table, key) {
|
|
36
|
+
const value = table[key];
|
|
37
|
+
return typeof value === 'string' && value.trim().length > 0 ? value : null;
|
|
38
|
+
}
|
|
39
|
+
function readRule(value) {
|
|
40
|
+
if (!isRecord(value) || !isRecord(value.match) || !isRecord(value.select)) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
const id = readStringField(value, 'id');
|
|
44
|
+
const risk = readStringField(value, 'risk');
|
|
45
|
+
const reason = readStringField(value, 'reason');
|
|
46
|
+
const paths = readStringArray(value.match, 'paths');
|
|
47
|
+
const surfaces = readStringArray(value.match, 'surfaces');
|
|
48
|
+
const intent = readStringField(value.select, 'intent');
|
|
49
|
+
const fallbackIntent = readStringField(value.select, 'fallback_intent');
|
|
50
|
+
const testTargets = readStringArray(value.select, 'test_targets') ?? [];
|
|
51
|
+
if (!id || !risk || !reason || !paths || paths.length === 0 || !surfaces || surfaces.length === 0 || !intent || !fallbackIntent) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
id,
|
|
56
|
+
risk,
|
|
57
|
+
reason,
|
|
58
|
+
paths: uniqueSorted(paths),
|
|
59
|
+
surfaces: uniqueSorted(surfaces),
|
|
60
|
+
intent,
|
|
61
|
+
fallbackIntent,
|
|
62
|
+
testTargets: uniqueSorted(testTargets),
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
function readRules(projectRoot) {
|
|
66
|
+
const configPath = resolveMustflowConfigPath(projectRoot, TEST_SELECTION_CONFIG_RELATIVE_PATH);
|
|
67
|
+
if (!existsSync(configPath)) {
|
|
68
|
+
return {
|
|
69
|
+
status: 'missing',
|
|
70
|
+
rules: [],
|
|
71
|
+
note: 'No project-declared test selection manifest exists; mustflow will not infer a user-project test subset.',
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
try {
|
|
75
|
+
const parsed = readTomlFile(configPath);
|
|
76
|
+
if (!isRecord(parsed) || parsed.schema_version !== '1' || !Array.isArray(parsed.rules)) {
|
|
77
|
+
return {
|
|
78
|
+
status: 'invalid',
|
|
79
|
+
rules: [],
|
|
80
|
+
note: 'Project-declared test selection manifest is invalid; run mf check --strict before relying on it.',
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
const rules = parsed.rules.map(readRule);
|
|
84
|
+
if (!rules.every((rule) => rule !== null)) {
|
|
85
|
+
return {
|
|
86
|
+
status: 'invalid',
|
|
87
|
+
rules: [],
|
|
88
|
+
note: 'Project-declared test selection manifest has invalid rule shapes; run mf check --strict before relying on it.',
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
return { status: 'loaded', rules };
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
return {
|
|
95
|
+
status: 'invalid',
|
|
96
|
+
rules: [],
|
|
97
|
+
note: 'Project-declared test selection manifest cannot be read; run mf check --strict before relying on it.',
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
function ruleMatchesClassification(rule, classification) {
|
|
102
|
+
const pathMatches = rule.paths.some((pattern) => globToRegExp(pattern).test(toPosixPath(classification.path)));
|
|
103
|
+
const surfaceMatches = rule.surfaces.includes(classification.surface.kind);
|
|
104
|
+
return pathMatches && surfaceMatches;
|
|
105
|
+
}
|
|
106
|
+
function matchingClassifications(rule, report) {
|
|
107
|
+
return report.classifications.filter((classification) => ruleMatchesClassification(rule, classification));
|
|
108
|
+
}
|
|
109
|
+
function selectReason(classifications) {
|
|
110
|
+
return (classifications.flatMap((classification) => classification.surface.validationReasons)[0] ??
|
|
111
|
+
'unknown_change');
|
|
112
|
+
}
|
|
113
|
+
function withDetail(candidate, detail) {
|
|
114
|
+
return {
|
|
115
|
+
...candidate,
|
|
116
|
+
detail: candidate.detail ?? detail,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
function commandAcceptsTestTargets(commandContract, intent) {
|
|
120
|
+
const rawIntent = commandContract.intents[intent];
|
|
121
|
+
return isRecord(rawIntent) && isRecord(rawIntent.selection) && rawIntent.selection.accepts_test_targets === true;
|
|
122
|
+
}
|
|
123
|
+
function appliedTestTargetsForCandidate(rule, commandContract, candidate) {
|
|
124
|
+
return candidate.status === 'runnable' && commandAcceptsTestTargets(commandContract, candidate.intent)
|
|
125
|
+
? rule.testTargets
|
|
126
|
+
: [];
|
|
127
|
+
}
|
|
128
|
+
function toReportCandidate(rule, reason, role, candidate, appliedTestTargets) {
|
|
129
|
+
return {
|
|
130
|
+
ruleId: rule.id,
|
|
131
|
+
reason,
|
|
132
|
+
intent: candidate.intent,
|
|
133
|
+
role,
|
|
134
|
+
status: candidate.status,
|
|
135
|
+
skipReason: candidate.reason,
|
|
136
|
+
detail: candidate.detail,
|
|
137
|
+
testTargets: rule.testTargets,
|
|
138
|
+
appliedTestTargets,
|
|
139
|
+
testTargetsApplied: appliedTestTargets.length > 0,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
export function createProjectTestSelectionPlan(projectRoot, classificationReport, commandContract) {
|
|
143
|
+
const loaded = readRules(projectRoot);
|
|
144
|
+
if (loaded.status !== 'loaded') {
|
|
145
|
+
const status = loaded.status;
|
|
146
|
+
return {
|
|
147
|
+
report: {
|
|
148
|
+
source: 'test-selection.toml',
|
|
149
|
+
status,
|
|
150
|
+
configPath: TEST_SELECTION_CONFIG_RELATIVE_PATH,
|
|
151
|
+
authority: TEST_SELECTION_CONFIG_RELATIVE_PATH,
|
|
152
|
+
commandAuthority: '.mustflow/config/commands.toml',
|
|
153
|
+
grantsCommandAuthority: false,
|
|
154
|
+
matches: [],
|
|
155
|
+
selected: [],
|
|
156
|
+
notes: [loaded.note],
|
|
157
|
+
},
|
|
158
|
+
candidates: [],
|
|
159
|
+
selectedCandidates: [],
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
const matches = [];
|
|
163
|
+
const candidates = [];
|
|
164
|
+
const selectedCandidates = [];
|
|
165
|
+
const selectedReportCandidates = [];
|
|
166
|
+
for (const rule of loaded.rules) {
|
|
167
|
+
const classifications = matchingClassifications(rule, classificationReport);
|
|
168
|
+
if (classifications.length === 0) {
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
const reason = selectReason(classifications);
|
|
172
|
+
matches.push({
|
|
173
|
+
ruleId: rule.id,
|
|
174
|
+
risk: rule.risk,
|
|
175
|
+
reason: rule.reason,
|
|
176
|
+
files: uniqueSorted(classifications.map((classification) => classification.path)),
|
|
177
|
+
surfaces: uniqueSorted(classifications.map((classification) => classification.surface.kind)),
|
|
178
|
+
intent: rule.intent,
|
|
179
|
+
fallbackIntent: rule.fallbackIntent,
|
|
180
|
+
testTargets: rule.testTargets,
|
|
181
|
+
});
|
|
182
|
+
const primary = withDetail(classifyVerificationCandidate(rule.intent, commandContract.intents[rule.intent]), `Project-declared test selection rule "${rule.id}".`);
|
|
183
|
+
const primaryTestTargets = appliedTestTargetsForCandidate(rule, commandContract, primary);
|
|
184
|
+
const primaryCandidate = { reason, candidate: primary, testTargets: primaryTestTargets };
|
|
185
|
+
candidates.push(primaryCandidate);
|
|
186
|
+
if (primary.status === 'runnable') {
|
|
187
|
+
selectedCandidates.push(primaryCandidate);
|
|
188
|
+
selectedReportCandidates.push(toReportCandidate(rule, reason, 'primary', primary, primaryTestTargets));
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
selectedReportCandidates.push(toReportCandidate(rule, reason, 'primary', primary, primaryTestTargets));
|
|
192
|
+
const fallback = withDetail(classifyVerificationCandidate(rule.fallbackIntent, commandContract.intents[rule.fallbackIntent]), `Fallback for project-declared test selection rule "${rule.id}".`);
|
|
193
|
+
const fallbackTestTargets = appliedTestTargetsForCandidate(rule, commandContract, fallback);
|
|
194
|
+
const fallbackCandidate = { reason, candidate: fallback, testTargets: fallbackTestTargets };
|
|
195
|
+
candidates.push(fallbackCandidate);
|
|
196
|
+
if (fallback.status === 'runnable') {
|
|
197
|
+
selectedCandidates.push(fallbackCandidate);
|
|
198
|
+
selectedReportCandidates.push(toReportCandidate(rule, reason, 'fallback', fallback, fallbackTestTargets));
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
const status = matches.length > 0 ? 'matched' : 'unmatched';
|
|
202
|
+
const notes = [
|
|
203
|
+
matches.length > 0
|
|
204
|
+
? 'Matched project-declared test selection rules are treated as a minimum selected set.'
|
|
205
|
+
: 'No project-declared test selection rules matched the current changed files.',
|
|
206
|
+
'Local index data and performance history may add suggestions later, but must not remove manifest-selected tests.',
|
|
207
|
+
'Test targets are passed only when the selected command intent declares selection.accepts_test_targets = true.',
|
|
208
|
+
];
|
|
209
|
+
return {
|
|
210
|
+
report: {
|
|
211
|
+
source: 'test-selection.toml',
|
|
212
|
+
status,
|
|
213
|
+
configPath: TEST_SELECTION_CONFIG_RELATIVE_PATH,
|
|
214
|
+
authority: TEST_SELECTION_CONFIG_RELATIVE_PATH,
|
|
215
|
+
commandAuthority: '.mustflow/config/commands.toml',
|
|
216
|
+
grantsCommandAuthority: false,
|
|
217
|
+
matches,
|
|
218
|
+
selected: selectedReportCandidates,
|
|
219
|
+
notes,
|
|
220
|
+
},
|
|
221
|
+
candidates,
|
|
222
|
+
selectedCandidates,
|
|
223
|
+
};
|
|
224
|
+
}
|