mustflow 1.15.97
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/LICENSE +16 -0
- package/README.md +422 -0
- package/dist/cli/commands/check.js +73 -0
- package/dist/cli/commands/classify.js +104 -0
- package/dist/cli/commands/context.js +95 -0
- package/dist/cli/commands/contract-lint.js +74 -0
- package/dist/cli/commands/dashboard.js +654 -0
- package/dist/cli/commands/docs.js +382 -0
- package/dist/cli/commands/doctor.js +232 -0
- package/dist/cli/commands/explain.js +293 -0
- package/dist/cli/commands/help.js +148 -0
- package/dist/cli/commands/impact.js +120 -0
- package/dist/cli/commands/index.js +70 -0
- package/dist/cli/commands/init.js +986 -0
- package/dist/cli/commands/line-endings.js +102 -0
- package/dist/cli/commands/map.js +95 -0
- package/dist/cli/commands/run.js +442 -0
- package/dist/cli/commands/search.js +166 -0
- package/dist/cli/commands/status.js +65 -0
- package/dist/cli/commands/update.js +443 -0
- package/dist/cli/commands/verify.js +448 -0
- package/dist/cli/commands/version-sources.js +79 -0
- package/dist/cli/commands/version.js +57 -0
- package/dist/cli/i18n/en.js +702 -0
- package/dist/cli/i18n/es.js +702 -0
- package/dist/cli/i18n/fr.js +702 -0
- package/dist/cli/i18n/hi.js +702 -0
- package/dist/cli/i18n/ko.js +702 -0
- package/dist/cli/i18n/zh.js +702 -0
- package/dist/cli/index.js +218 -0
- package/dist/cli/lib/agent-context.js +342 -0
- package/dist/cli/lib/browser-open.js +58 -0
- package/dist/cli/lib/cli-output.js +36 -0
- package/dist/cli/lib/command-contract.js +1 -0
- package/dist/cli/lib/command-registry.js +107 -0
- package/dist/cli/lib/dashboard-html.js +1866 -0
- package/dist/cli/lib/dashboard-locale.js +309 -0
- package/dist/cli/lib/dashboard-preferences.js +405 -0
- package/dist/cli/lib/doc-review-ledger.js +226 -0
- package/dist/cli/lib/filesystem.js +125 -0
- package/dist/cli/lib/git-changes.js +13 -0
- package/dist/cli/lib/i18n.js +55 -0
- package/dist/cli/lib/local-index.js +1014 -0
- package/dist/cli/lib/locale-tags.js +4 -0
- package/dist/cli/lib/manifest-lock.js +131 -0
- package/dist/cli/lib/npm-version-check.js +97 -0
- package/dist/cli/lib/package-info.js +13 -0
- package/dist/cli/lib/preferences-options.js +8 -0
- package/dist/cli/lib/project-root.js +23 -0
- package/dist/cli/lib/repo-map.js +635 -0
- package/dist/cli/lib/reporter.js +8 -0
- package/dist/cli/lib/run-receipt.js +1 -0
- package/dist/cli/lib/template-i18n.js +265 -0
- package/dist/cli/lib/templates.js +188 -0
- package/dist/cli/lib/toml.js +1 -0
- package/dist/cli/lib/validation.js +1639 -0
- package/dist/cli/lib/version-sources.js +1 -0
- package/dist/core/authority-resolution.js +155 -0
- package/dist/core/change-classification.js +122 -0
- package/dist/core/change-verification.js +80 -0
- package/dist/core/check-issues.js +67 -0
- package/dist/core/command-classification.js +22 -0
- package/dist/core/command-contract-rules.js +27 -0
- package/dist/core/command-contract-validation.js +197 -0
- package/dist/core/command-cwd.js +12 -0
- package/dist/core/command-effects.js +182 -0
- package/dist/core/command-explanation.js +135 -0
- package/dist/core/command-intent-eligibility.js +76 -0
- package/dist/core/config-loading.js +54 -0
- package/dist/core/contract-lint.js +110 -0
- package/dist/core/contract-models.js +53 -0
- package/dist/core/dashboard-verification.js +132 -0
- package/dist/core/doc-review-triage.js +92 -0
- package/dist/core/line-endings.js +144 -0
- package/dist/core/public-json-contracts.js +112 -0
- package/dist/core/public-surface-explanation.js +49 -0
- package/dist/core/release-version-validation.js +53 -0
- package/dist/core/retention-explanation.js +74 -0
- package/dist/core/retention-policy.js +57 -0
- package/dist/core/run-receipt.js +77 -0
- package/dist/core/skill-route-alignment.js +100 -0
- package/dist/core/skill-route-explanation.js +117 -0
- package/dist/core/source-anchor-explanation.js +33 -0
- package/dist/core/source-anchor-status.js +269 -0
- package/dist/core/source-anchor-symbols.js +181 -0
- package/dist/core/source-anchor-validation.js +158 -0
- package/dist/core/source-anchors.js +194 -0
- package/dist/core/surface-decision-model.js +18 -0
- package/dist/core/toml.js +11 -0
- package/dist/core/verification-plan.js +41 -0
- package/dist/core/verification-scheduler.js +92 -0
- package/dist/core/version-impact.js +54 -0
- package/dist/core/version-sources.js +235 -0
- package/dist/core/version-sync-policy.js +85 -0
- package/examples/README.md +13 -0
- package/examples/docs-only/README.md +72 -0
- package/examples/host-instruction-conflicts/README.md +47 -0
- package/examples/minimal-js/README.md +98 -0
- package/examples/missing-command-contracts/README.md +70 -0
- package/examples/nested-repos/README.md +62 -0
- package/package.json +80 -0
- package/schemas/README.md +32 -0
- package/schemas/change-verification-report.schema.json +319 -0
- package/schemas/classify-report.schema.json +113 -0
- package/schemas/commands.schema.json +116 -0
- package/schemas/context-report.schema.json +341 -0
- package/schemas/contract-lint-report.schema.json +61 -0
- package/schemas/docs-review-list.schema.json +72 -0
- package/schemas/doctor-report.schema.json +175 -0
- package/schemas/explain-report.schema.json +471 -0
- package/schemas/impact-report.schema.json +121 -0
- package/schemas/line-endings-report.schema.json +63 -0
- package/schemas/run-receipt.schema.json +75 -0
- package/schemas/verify-report.schema.json +67 -0
- package/schemas/version-sources-report.schema.json +42 -0
- package/templates/default/common/.mustflow/config/commands.toml +251 -0
- package/templates/default/common/.mustflow/config/mustflow.toml +424 -0
- package/templates/default/common/.mustflow/config/preferences.toml +125 -0
- package/templates/default/common/gitignore.mustflow +9 -0
- package/templates/default/i18n.toml +483 -0
- package/templates/default/locales/en/.mustflow/context/INDEX.md +39 -0
- package/templates/default/locales/en/.mustflow/context/PROJECT.md +66 -0
- package/templates/default/locales/en/.mustflow/docs/agent-workflow.md +345 -0
- package/templates/default/locales/en/.mustflow/skills/INDEX.md +78 -0
- package/templates/default/locales/en/.mustflow/skills/adapter-boundary/SKILL.md +193 -0
- package/templates/default/locales/en/.mustflow/skills/artifact-integrity-check/SKILL.md +121 -0
- package/templates/default/locales/en/.mustflow/skills/behavior-preserving-refactor/SKILL.md +182 -0
- package/templates/default/locales/en/.mustflow/skills/code-review/SKILL.md +115 -0
- package/templates/default/locales/en/.mustflow/skills/codebase-orientation/SKILL.md +115 -0
- package/templates/default/locales/en/.mustflow/skills/command-pattern/SKILL.md +247 -0
- package/templates/default/locales/en/.mustflow/skills/composition-over-inheritance/SKILL.md +176 -0
- package/templates/default/locales/en/.mustflow/skills/contract-sync-check/SKILL.md +116 -0
- package/templates/default/locales/en/.mustflow/skills/date-number-audit/SKILL.md +116 -0
- package/templates/default/locales/en/.mustflow/skills/dependency-injection/SKILL.md +161 -0
- package/templates/default/locales/en/.mustflow/skills/dependency-reality-check/SKILL.md +115 -0
- package/templates/default/locales/en/.mustflow/skills/diff-risk-review/SKILL.md +143 -0
- package/templates/default/locales/en/.mustflow/skills/docs-prose-review/SKILL.md +119 -0
- package/templates/default/locales/en/.mustflow/skills/docs-update/SKILL.md +100 -0
- package/templates/default/locales/en/.mustflow/skills/external-prompt-injection-defense/SKILL.md +124 -0
- package/templates/default/locales/en/.mustflow/skills/facade-pattern/SKILL.md +210 -0
- package/templates/default/locales/en/.mustflow/skills/failure-triage/SKILL.md +97 -0
- package/templates/default/locales/en/.mustflow/skills/instruction-conflict-scope-check/SKILL.md +118 -0
- package/templates/default/locales/en/.mustflow/skills/line-ending-hygiene/SKILL.md +111 -0
- package/templates/default/locales/en/.mustflow/skills/migration-safety-check/SKILL.md +117 -0
- package/templates/default/locales/en/.mustflow/skills/multi-agent-work-coordination/SKILL.md +260 -0
- package/templates/default/locales/en/.mustflow/skills/null-object-pattern/SKILL.md +196 -0
- package/templates/default/locales/en/.mustflow/skills/pattern-scout/SKILL.md +110 -0
- package/templates/default/locales/en/.mustflow/skills/performance-budget-check/SKILL.md +121 -0
- package/templates/default/locales/en/.mustflow/skills/project-context-authoring/SKILL.md +107 -0
- package/templates/default/locales/en/.mustflow/skills/pure-core-imperative-shell/SKILL.md +212 -0
- package/templates/default/locales/en/.mustflow/skills/readme-authoring/SKILL.md +115 -0
- package/templates/default/locales/en/.mustflow/skills/repo-improvement-loop/SKILL.md +150 -0
- package/templates/default/locales/en/.mustflow/skills/repro-first-debug/SKILL.md +112 -0
- package/templates/default/locales/en/.mustflow/skills/requirement-regression-guard/SKILL.md +152 -0
- package/templates/default/locales/en/.mustflow/skills/result-option/SKILL.md +186 -0
- package/templates/default/locales/en/.mustflow/skills/security-privacy-review/SKILL.md +130 -0
- package/templates/default/locales/en/.mustflow/skills/security-regression-tests/SKILL.md +157 -0
- package/templates/default/locales/en/.mustflow/skills/skill-authoring/SKILL.md +110 -0
- package/templates/default/locales/en/.mustflow/skills/source-freshness-check/SKILL.md +111 -0
- package/templates/default/locales/en/.mustflow/skills/state-machine-pattern/SKILL.md +214 -0
- package/templates/default/locales/en/.mustflow/skills/strategy-pattern/SKILL.md +215 -0
- package/templates/default/locales/en/.mustflow/skills/structure-discovery-gate/SKILL.md +159 -0
- package/templates/default/locales/en/.mustflow/skills/test-maintenance/SKILL.md +122 -0
- package/templates/default/locales/en/.mustflow/skills/ui-quality-gate/SKILL.md +119 -0
- package/templates/default/locales/en/.mustflow/skills/visual-review-artifact/SKILL.md +127 -0
- package/templates/default/locales/en/.mustflow/skills/visual-review-artifact/assets/review-template.html +286 -0
- package/templates/default/locales/en/.mustflow/skills/visual-review-artifact/resources.toml +7 -0
- package/templates/default/locales/en/.mustflow/skills/web-asset-optimization/SKILL.md +108 -0
- package/templates/default/locales/en/AGENTS.md +114 -0
- package/templates/default/locales/es/.mustflow/context/INDEX.md +39 -0
- package/templates/default/locales/es/.mustflow/context/PROJECT.md +63 -0
- package/templates/default/locales/es/.mustflow/docs/agent-workflow.md +365 -0
- package/templates/default/locales/es/.mustflow/skills/INDEX.md +78 -0
- package/templates/default/locales/es/.mustflow/skills/adapter-boundary/SKILL.md +193 -0
- package/templates/default/locales/es/.mustflow/skills/artifact-integrity-check/SKILL.md +114 -0
- package/templates/default/locales/es/.mustflow/skills/behavior-preserving-refactor/SKILL.md +182 -0
- package/templates/default/locales/es/.mustflow/skills/code-review/SKILL.md +115 -0
- package/templates/default/locales/es/.mustflow/skills/codebase-orientation/SKILL.md +115 -0
- package/templates/default/locales/es/.mustflow/skills/command-pattern/SKILL.md +247 -0
- package/templates/default/locales/es/.mustflow/skills/composition-over-inheritance/SKILL.md +176 -0
- package/templates/default/locales/es/.mustflow/skills/contract-sync-check/SKILL.md +116 -0
- package/templates/default/locales/es/.mustflow/skills/date-number-audit/SKILL.md +116 -0
- package/templates/default/locales/es/.mustflow/skills/dependency-injection/SKILL.md +161 -0
- package/templates/default/locales/es/.mustflow/skills/dependency-reality-check/SKILL.md +115 -0
- package/templates/default/locales/es/.mustflow/skills/diff-risk-review/SKILL.md +136 -0
- package/templates/default/locales/es/.mustflow/skills/docs-prose-review/SKILL.md +119 -0
- package/templates/default/locales/es/.mustflow/skills/docs-update/SKILL.md +97 -0
- package/templates/default/locales/es/.mustflow/skills/external-prompt-injection-defense/SKILL.md +116 -0
- package/templates/default/locales/es/.mustflow/skills/facade-pattern/SKILL.md +210 -0
- package/templates/default/locales/es/.mustflow/skills/failure-triage/SKILL.md +97 -0
- package/templates/default/locales/es/.mustflow/skills/instruction-conflict-scope-check/SKILL.md +118 -0
- package/templates/default/locales/es/.mustflow/skills/line-ending-hygiene/SKILL.md +111 -0
- package/templates/default/locales/es/.mustflow/skills/migration-safety-check/SKILL.md +117 -0
- package/templates/default/locales/es/.mustflow/skills/multi-agent-work-coordination/SKILL.md +260 -0
- package/templates/default/locales/es/.mustflow/skills/null-object-pattern/SKILL.md +196 -0
- package/templates/default/locales/es/.mustflow/skills/pattern-scout/SKILL.md +110 -0
- package/templates/default/locales/es/.mustflow/skills/performance-budget-check/SKILL.md +121 -0
- package/templates/default/locales/es/.mustflow/skills/project-context-authoring/SKILL.md +107 -0
- package/templates/default/locales/es/.mustflow/skills/pure-core-imperative-shell/SKILL.md +212 -0
- package/templates/default/locales/es/.mustflow/skills/readme-authoring/SKILL.md +115 -0
- package/templates/default/locales/es/.mustflow/skills/repo-improvement-loop/SKILL.md +150 -0
- package/templates/default/locales/es/.mustflow/skills/repro-first-debug/SKILL.md +112 -0
- package/templates/default/locales/es/.mustflow/skills/requirement-regression-guard/SKILL.md +152 -0
- package/templates/default/locales/es/.mustflow/skills/result-option/SKILL.md +186 -0
- package/templates/default/locales/es/.mustflow/skills/security-privacy-review/SKILL.md +116 -0
- package/templates/default/locales/es/.mustflow/skills/security-regression-tests/SKILL.md +131 -0
- package/templates/default/locales/es/.mustflow/skills/skill-authoring/SKILL.md +110 -0
- package/templates/default/locales/es/.mustflow/skills/source-freshness-check/SKILL.md +111 -0
- package/templates/default/locales/es/.mustflow/skills/state-machine-pattern/SKILL.md +214 -0
- package/templates/default/locales/es/.mustflow/skills/strategy-pattern/SKILL.md +215 -0
- package/templates/default/locales/es/.mustflow/skills/structure-discovery-gate/SKILL.md +159 -0
- package/templates/default/locales/es/.mustflow/skills/test-maintenance/SKILL.md +122 -0
- package/templates/default/locales/es/.mustflow/skills/ui-quality-gate/SKILL.md +117 -0
- package/templates/default/locales/es/.mustflow/skills/visual-review-artifact/SKILL.md +127 -0
- package/templates/default/locales/es/.mustflow/skills/visual-review-artifact/assets/review-template.html +286 -0
- package/templates/default/locales/es/.mustflow/skills/visual-review-artifact/resources.toml +7 -0
- package/templates/default/locales/es/.mustflow/skills/web-asset-optimization/SKILL.md +108 -0
- package/templates/default/locales/es/AGENTS.md +83 -0
- package/templates/default/locales/fr/.mustflow/context/INDEX.md +39 -0
- package/templates/default/locales/fr/.mustflow/context/PROJECT.md +63 -0
- package/templates/default/locales/fr/.mustflow/docs/agent-workflow.md +368 -0
- package/templates/default/locales/fr/.mustflow/skills/INDEX.md +78 -0
- package/templates/default/locales/fr/.mustflow/skills/adapter-boundary/SKILL.md +193 -0
- package/templates/default/locales/fr/.mustflow/skills/artifact-integrity-check/SKILL.md +114 -0
- package/templates/default/locales/fr/.mustflow/skills/behavior-preserving-refactor/SKILL.md +182 -0
- package/templates/default/locales/fr/.mustflow/skills/code-review/SKILL.md +115 -0
- package/templates/default/locales/fr/.mustflow/skills/codebase-orientation/SKILL.md +115 -0
- package/templates/default/locales/fr/.mustflow/skills/command-pattern/SKILL.md +247 -0
- package/templates/default/locales/fr/.mustflow/skills/composition-over-inheritance/SKILL.md +176 -0
- package/templates/default/locales/fr/.mustflow/skills/contract-sync-check/SKILL.md +116 -0
- package/templates/default/locales/fr/.mustflow/skills/date-number-audit/SKILL.md +116 -0
- package/templates/default/locales/fr/.mustflow/skills/dependency-injection/SKILL.md +161 -0
- package/templates/default/locales/fr/.mustflow/skills/dependency-reality-check/SKILL.md +115 -0
- package/templates/default/locales/fr/.mustflow/skills/diff-risk-review/SKILL.md +136 -0
- package/templates/default/locales/fr/.mustflow/skills/docs-prose-review/SKILL.md +119 -0
- package/templates/default/locales/fr/.mustflow/skills/docs-update/SKILL.md +97 -0
- package/templates/default/locales/fr/.mustflow/skills/external-prompt-injection-defense/SKILL.md +116 -0
- package/templates/default/locales/fr/.mustflow/skills/facade-pattern/SKILL.md +210 -0
- package/templates/default/locales/fr/.mustflow/skills/failure-triage/SKILL.md +97 -0
- package/templates/default/locales/fr/.mustflow/skills/instruction-conflict-scope-check/SKILL.md +118 -0
- package/templates/default/locales/fr/.mustflow/skills/line-ending-hygiene/SKILL.md +111 -0
- package/templates/default/locales/fr/.mustflow/skills/migration-safety-check/SKILL.md +117 -0
- package/templates/default/locales/fr/.mustflow/skills/multi-agent-work-coordination/SKILL.md +260 -0
- package/templates/default/locales/fr/.mustflow/skills/null-object-pattern/SKILL.md +196 -0
- package/templates/default/locales/fr/.mustflow/skills/pattern-scout/SKILL.md +110 -0
- package/templates/default/locales/fr/.mustflow/skills/performance-budget-check/SKILL.md +121 -0
- package/templates/default/locales/fr/.mustflow/skills/project-context-authoring/SKILL.md +107 -0
- package/templates/default/locales/fr/.mustflow/skills/pure-core-imperative-shell/SKILL.md +212 -0
- package/templates/default/locales/fr/.mustflow/skills/readme-authoring/SKILL.md +115 -0
- package/templates/default/locales/fr/.mustflow/skills/repo-improvement-loop/SKILL.md +150 -0
- package/templates/default/locales/fr/.mustflow/skills/repro-first-debug/SKILL.md +112 -0
- package/templates/default/locales/fr/.mustflow/skills/requirement-regression-guard/SKILL.md +152 -0
- package/templates/default/locales/fr/.mustflow/skills/result-option/SKILL.md +186 -0
- package/templates/default/locales/fr/.mustflow/skills/security-privacy-review/SKILL.md +116 -0
- package/templates/default/locales/fr/.mustflow/skills/security-regression-tests/SKILL.md +131 -0
- package/templates/default/locales/fr/.mustflow/skills/skill-authoring/SKILL.md +110 -0
- package/templates/default/locales/fr/.mustflow/skills/source-freshness-check/SKILL.md +111 -0
- package/templates/default/locales/fr/.mustflow/skills/state-machine-pattern/SKILL.md +214 -0
- package/templates/default/locales/fr/.mustflow/skills/strategy-pattern/SKILL.md +215 -0
- package/templates/default/locales/fr/.mustflow/skills/structure-discovery-gate/SKILL.md +159 -0
- package/templates/default/locales/fr/.mustflow/skills/test-maintenance/SKILL.md +122 -0
- package/templates/default/locales/fr/.mustflow/skills/ui-quality-gate/SKILL.md +117 -0
- package/templates/default/locales/fr/.mustflow/skills/visual-review-artifact/SKILL.md +127 -0
- package/templates/default/locales/fr/.mustflow/skills/visual-review-artifact/assets/review-template.html +286 -0
- package/templates/default/locales/fr/.mustflow/skills/visual-review-artifact/resources.toml +7 -0
- package/templates/default/locales/fr/.mustflow/skills/web-asset-optimization/SKILL.md +108 -0
- package/templates/default/locales/fr/AGENTS.md +84 -0
- package/templates/default/locales/hi/.mustflow/context/INDEX.md +39 -0
- package/templates/default/locales/hi/.mustflow/context/PROJECT.md +65 -0
- package/templates/default/locales/hi/.mustflow/docs/agent-workflow.md +359 -0
- package/templates/default/locales/hi/.mustflow/skills/INDEX.md +78 -0
- package/templates/default/locales/hi/.mustflow/skills/adapter-boundary/SKILL.md +193 -0
- package/templates/default/locales/hi/.mustflow/skills/artifact-integrity-check/SKILL.md +114 -0
- package/templates/default/locales/hi/.mustflow/skills/behavior-preserving-refactor/SKILL.md +182 -0
- package/templates/default/locales/hi/.mustflow/skills/code-review/SKILL.md +115 -0
- package/templates/default/locales/hi/.mustflow/skills/codebase-orientation/SKILL.md +115 -0
- package/templates/default/locales/hi/.mustflow/skills/command-pattern/SKILL.md +247 -0
- package/templates/default/locales/hi/.mustflow/skills/composition-over-inheritance/SKILL.md +176 -0
- package/templates/default/locales/hi/.mustflow/skills/contract-sync-check/SKILL.md +116 -0
- package/templates/default/locales/hi/.mustflow/skills/date-number-audit/SKILL.md +116 -0
- package/templates/default/locales/hi/.mustflow/skills/dependency-injection/SKILL.md +161 -0
- package/templates/default/locales/hi/.mustflow/skills/dependency-reality-check/SKILL.md +115 -0
- package/templates/default/locales/hi/.mustflow/skills/diff-risk-review/SKILL.md +136 -0
- package/templates/default/locales/hi/.mustflow/skills/docs-prose-review/SKILL.md +119 -0
- package/templates/default/locales/hi/.mustflow/skills/docs-update/SKILL.md +97 -0
- package/templates/default/locales/hi/.mustflow/skills/external-prompt-injection-defense/SKILL.md +116 -0
- package/templates/default/locales/hi/.mustflow/skills/facade-pattern/SKILL.md +210 -0
- package/templates/default/locales/hi/.mustflow/skills/failure-triage/SKILL.md +97 -0
- package/templates/default/locales/hi/.mustflow/skills/instruction-conflict-scope-check/SKILL.md +118 -0
- package/templates/default/locales/hi/.mustflow/skills/line-ending-hygiene/SKILL.md +111 -0
- package/templates/default/locales/hi/.mustflow/skills/migration-safety-check/SKILL.md +117 -0
- package/templates/default/locales/hi/.mustflow/skills/multi-agent-work-coordination/SKILL.md +260 -0
- package/templates/default/locales/hi/.mustflow/skills/null-object-pattern/SKILL.md +196 -0
- package/templates/default/locales/hi/.mustflow/skills/pattern-scout/SKILL.md +110 -0
- package/templates/default/locales/hi/.mustflow/skills/performance-budget-check/SKILL.md +121 -0
- package/templates/default/locales/hi/.mustflow/skills/project-context-authoring/SKILL.md +107 -0
- package/templates/default/locales/hi/.mustflow/skills/pure-core-imperative-shell/SKILL.md +212 -0
- package/templates/default/locales/hi/.mustflow/skills/readme-authoring/SKILL.md +115 -0
- package/templates/default/locales/hi/.mustflow/skills/repo-improvement-loop/SKILL.md +150 -0
- package/templates/default/locales/hi/.mustflow/skills/repro-first-debug/SKILL.md +112 -0
- package/templates/default/locales/hi/.mustflow/skills/requirement-regression-guard/SKILL.md +152 -0
- package/templates/default/locales/hi/.mustflow/skills/result-option/SKILL.md +186 -0
- package/templates/default/locales/hi/.mustflow/skills/security-privacy-review/SKILL.md +116 -0
- package/templates/default/locales/hi/.mustflow/skills/security-regression-tests/SKILL.md +131 -0
- package/templates/default/locales/hi/.mustflow/skills/skill-authoring/SKILL.md +110 -0
- package/templates/default/locales/hi/.mustflow/skills/source-freshness-check/SKILL.md +111 -0
- package/templates/default/locales/hi/.mustflow/skills/state-machine-pattern/SKILL.md +214 -0
- package/templates/default/locales/hi/.mustflow/skills/strategy-pattern/SKILL.md +215 -0
- package/templates/default/locales/hi/.mustflow/skills/structure-discovery-gate/SKILL.md +159 -0
- package/templates/default/locales/hi/.mustflow/skills/test-maintenance/SKILL.md +122 -0
- package/templates/default/locales/hi/.mustflow/skills/ui-quality-gate/SKILL.md +117 -0
- package/templates/default/locales/hi/.mustflow/skills/visual-review-artifact/SKILL.md +127 -0
- package/templates/default/locales/hi/.mustflow/skills/visual-review-artifact/assets/review-template.html +286 -0
- package/templates/default/locales/hi/.mustflow/skills/visual-review-artifact/resources.toml +7 -0
- package/templates/default/locales/hi/.mustflow/skills/web-asset-optimization/SKILL.md +108 -0
- package/templates/default/locales/hi/AGENTS.md +83 -0
- package/templates/default/locales/ko/.mustflow/context/INDEX.md +39 -0
- package/templates/default/locales/ko/.mustflow/context/PROJECT.md +66 -0
- package/templates/default/locales/ko/.mustflow/docs/agent-workflow.md +506 -0
- package/templates/default/locales/ko/.mustflow/skills/INDEX.md +78 -0
- package/templates/default/locales/ko/.mustflow/skills/adapter-boundary/SKILL.md +193 -0
- package/templates/default/locales/ko/.mustflow/skills/artifact-integrity-check/SKILL.md +114 -0
- package/templates/default/locales/ko/.mustflow/skills/behavior-preserving-refactor/SKILL.md +182 -0
- package/templates/default/locales/ko/.mustflow/skills/code-review/SKILL.md +118 -0
- package/templates/default/locales/ko/.mustflow/skills/codebase-orientation/SKILL.md +115 -0
- package/templates/default/locales/ko/.mustflow/skills/command-pattern/SKILL.md +247 -0
- package/templates/default/locales/ko/.mustflow/skills/composition-over-inheritance/SKILL.md +176 -0
- package/templates/default/locales/ko/.mustflow/skills/contract-sync-check/SKILL.md +116 -0
- package/templates/default/locales/ko/.mustflow/skills/date-number-audit/SKILL.md +116 -0
- package/templates/default/locales/ko/.mustflow/skills/dependency-injection/SKILL.md +161 -0
- package/templates/default/locales/ko/.mustflow/skills/dependency-reality-check/SKILL.md +115 -0
- package/templates/default/locales/ko/.mustflow/skills/diff-risk-review/SKILL.md +136 -0
- package/templates/default/locales/ko/.mustflow/skills/docs-prose-review/SKILL.md +119 -0
- package/templates/default/locales/ko/.mustflow/skills/docs-update/SKILL.md +107 -0
- package/templates/default/locales/ko/.mustflow/skills/external-prompt-injection-defense/SKILL.md +116 -0
- package/templates/default/locales/ko/.mustflow/skills/facade-pattern/SKILL.md +210 -0
- package/templates/default/locales/ko/.mustflow/skills/failure-triage/SKILL.md +119 -0
- package/templates/default/locales/ko/.mustflow/skills/instruction-conflict-scope-check/SKILL.md +118 -0
- package/templates/default/locales/ko/.mustflow/skills/line-ending-hygiene/SKILL.md +111 -0
- package/templates/default/locales/ko/.mustflow/skills/migration-safety-check/SKILL.md +117 -0
- package/templates/default/locales/ko/.mustflow/skills/multi-agent-work-coordination/SKILL.md +259 -0
- package/templates/default/locales/ko/.mustflow/skills/null-object-pattern/SKILL.md +196 -0
- package/templates/default/locales/ko/.mustflow/skills/pattern-scout/SKILL.md +110 -0
- package/templates/default/locales/ko/.mustflow/skills/performance-budget-check/SKILL.md +121 -0
- package/templates/default/locales/ko/.mustflow/skills/project-context-authoring/SKILL.md +107 -0
- package/templates/default/locales/ko/.mustflow/skills/pure-core-imperative-shell/SKILL.md +212 -0
- package/templates/default/locales/ko/.mustflow/skills/readme-authoring/SKILL.md +115 -0
- package/templates/default/locales/ko/.mustflow/skills/repo-improvement-loop/SKILL.md +150 -0
- package/templates/default/locales/ko/.mustflow/skills/repro-first-debug/SKILL.md +112 -0
- package/templates/default/locales/ko/.mustflow/skills/requirement-regression-guard/SKILL.md +152 -0
- package/templates/default/locales/ko/.mustflow/skills/result-option/SKILL.md +186 -0
- package/templates/default/locales/ko/.mustflow/skills/security-privacy-review/SKILL.md +116 -0
- package/templates/default/locales/ko/.mustflow/skills/security-regression-tests/SKILL.md +131 -0
- package/templates/default/locales/ko/.mustflow/skills/skill-authoring/SKILL.md +110 -0
- package/templates/default/locales/ko/.mustflow/skills/source-freshness-check/SKILL.md +111 -0
- package/templates/default/locales/ko/.mustflow/skills/state-machine-pattern/SKILL.md +214 -0
- package/templates/default/locales/ko/.mustflow/skills/strategy-pattern/SKILL.md +215 -0
- package/templates/default/locales/ko/.mustflow/skills/structure-discovery-gate/SKILL.md +159 -0
- package/templates/default/locales/ko/.mustflow/skills/test-maintenance/SKILL.md +130 -0
- package/templates/default/locales/ko/.mustflow/skills/ui-quality-gate/SKILL.md +117 -0
- package/templates/default/locales/ko/.mustflow/skills/visual-review-artifact/SKILL.md +127 -0
- package/templates/default/locales/ko/.mustflow/skills/visual-review-artifact/assets/review-template.html +286 -0
- package/templates/default/locales/ko/.mustflow/skills/visual-review-artifact/resources.toml +7 -0
- package/templates/default/locales/ko/.mustflow/skills/web-asset-optimization/SKILL.md +108 -0
- package/templates/default/locales/ko/AGENTS.md +85 -0
- package/templates/default/locales/zh/.mustflow/context/INDEX.md +39 -0
- package/templates/default/locales/zh/.mustflow/context/PROJECT.md +64 -0
- package/templates/default/locales/zh/.mustflow/docs/agent-workflow.md +310 -0
- package/templates/default/locales/zh/.mustflow/skills/INDEX.md +78 -0
- package/templates/default/locales/zh/.mustflow/skills/adapter-boundary/SKILL.md +193 -0
- package/templates/default/locales/zh/.mustflow/skills/artifact-integrity-check/SKILL.md +114 -0
- package/templates/default/locales/zh/.mustflow/skills/behavior-preserving-refactor/SKILL.md +182 -0
- package/templates/default/locales/zh/.mustflow/skills/code-review/SKILL.md +115 -0
- package/templates/default/locales/zh/.mustflow/skills/codebase-orientation/SKILL.md +115 -0
- package/templates/default/locales/zh/.mustflow/skills/command-pattern/SKILL.md +247 -0
- package/templates/default/locales/zh/.mustflow/skills/composition-over-inheritance/SKILL.md +176 -0
- package/templates/default/locales/zh/.mustflow/skills/contract-sync-check/SKILL.md +116 -0
- package/templates/default/locales/zh/.mustflow/skills/date-number-audit/SKILL.md +116 -0
- package/templates/default/locales/zh/.mustflow/skills/dependency-injection/SKILL.md +161 -0
- package/templates/default/locales/zh/.mustflow/skills/dependency-reality-check/SKILL.md +115 -0
- package/templates/default/locales/zh/.mustflow/skills/diff-risk-review/SKILL.md +136 -0
- package/templates/default/locales/zh/.mustflow/skills/docs-prose-review/SKILL.md +119 -0
- package/templates/default/locales/zh/.mustflow/skills/docs-update/SKILL.md +97 -0
- package/templates/default/locales/zh/.mustflow/skills/external-prompt-injection-defense/SKILL.md +116 -0
- package/templates/default/locales/zh/.mustflow/skills/facade-pattern/SKILL.md +210 -0
- package/templates/default/locales/zh/.mustflow/skills/failure-triage/SKILL.md +96 -0
- package/templates/default/locales/zh/.mustflow/skills/instruction-conflict-scope-check/SKILL.md +118 -0
- package/templates/default/locales/zh/.mustflow/skills/line-ending-hygiene/SKILL.md +111 -0
- package/templates/default/locales/zh/.mustflow/skills/migration-safety-check/SKILL.md +117 -0
- package/templates/default/locales/zh/.mustflow/skills/multi-agent-work-coordination/SKILL.md +260 -0
- package/templates/default/locales/zh/.mustflow/skills/null-object-pattern/SKILL.md +196 -0
- package/templates/default/locales/zh/.mustflow/skills/pattern-scout/SKILL.md +110 -0
- package/templates/default/locales/zh/.mustflow/skills/performance-budget-check/SKILL.md +121 -0
- package/templates/default/locales/zh/.mustflow/skills/project-context-authoring/SKILL.md +107 -0
- package/templates/default/locales/zh/.mustflow/skills/pure-core-imperative-shell/SKILL.md +212 -0
- package/templates/default/locales/zh/.mustflow/skills/readme-authoring/SKILL.md +115 -0
- package/templates/default/locales/zh/.mustflow/skills/repo-improvement-loop/SKILL.md +150 -0
- package/templates/default/locales/zh/.mustflow/skills/repro-first-debug/SKILL.md +112 -0
- package/templates/default/locales/zh/.mustflow/skills/requirement-regression-guard/SKILL.md +152 -0
- package/templates/default/locales/zh/.mustflow/skills/result-option/SKILL.md +186 -0
- package/templates/default/locales/zh/.mustflow/skills/security-privacy-review/SKILL.md +116 -0
- package/templates/default/locales/zh/.mustflow/skills/security-regression-tests/SKILL.md +131 -0
- package/templates/default/locales/zh/.mustflow/skills/skill-authoring/SKILL.md +110 -0
- package/templates/default/locales/zh/.mustflow/skills/source-freshness-check/SKILL.md +111 -0
- package/templates/default/locales/zh/.mustflow/skills/state-machine-pattern/SKILL.md +214 -0
- package/templates/default/locales/zh/.mustflow/skills/strategy-pattern/SKILL.md +215 -0
- package/templates/default/locales/zh/.mustflow/skills/structure-discovery-gate/SKILL.md +159 -0
- package/templates/default/locales/zh/.mustflow/skills/test-maintenance/SKILL.md +122 -0
- package/templates/default/locales/zh/.mustflow/skills/ui-quality-gate/SKILL.md +117 -0
- package/templates/default/locales/zh/.mustflow/skills/visual-review-artifact/SKILL.md +127 -0
- package/templates/default/locales/zh/.mustflow/skills/visual-review-artifact/assets/review-template.html +286 -0
- package/templates/default/locales/zh/.mustflow/skills/visual-review-artifact/resources.toml +7 -0
- package/templates/default/locales/zh/.mustflow/skills/web-asset-optimization/SKILL.md +108 -0
- package/templates/default/locales/zh/AGENTS.md +86 -0
- package/templates/default/manifest.toml +339 -0
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { COMMAND_LIFECYCLES, COMMAND_RUN_POLICIES, LONG_RUNNING_LIFECYCLES, isRecord, } from './config-loading.js';
|
|
2
|
+
import { COMMAND_EFFECT_CONCURRENCY, COMMAND_EFFECT_MODES, COMMAND_EFFECT_TYPES, validateCommandEffectLockWarnings, validateCommandEffects, } from './command-effects.js';
|
|
3
|
+
import { commandIntentHasBlockedShellBackgroundPattern, commandIntentHasCommandSource, commandIntentNameIsSafe, } from './command-contract-rules.js';
|
|
4
|
+
function commandContractIssue(message) {
|
|
5
|
+
return { message };
|
|
6
|
+
}
|
|
7
|
+
function hasOwn(table, key) {
|
|
8
|
+
return Object.prototype.hasOwnProperty.call(table, key);
|
|
9
|
+
}
|
|
10
|
+
function isPositiveInteger(value) {
|
|
11
|
+
return Number.isInteger(value) && Number(value) > 0;
|
|
12
|
+
}
|
|
13
|
+
function validateTable(config, tableName, issues) {
|
|
14
|
+
if (!hasOwn(config, tableName)) {
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
const table = config[tableName];
|
|
18
|
+
if (!isRecord(table)) {
|
|
19
|
+
issues.push(commandContractIssue(`[${tableName}] must be a TOML table`));
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
return table;
|
|
23
|
+
}
|
|
24
|
+
function validateBooleanField(table, key, label, issues) {
|
|
25
|
+
if (hasOwn(table, key) && typeof table[key] !== 'boolean') {
|
|
26
|
+
issues.push(commandContractIssue(`${label} must be a boolean`));
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function validateStringField(table, key, label, issues) {
|
|
30
|
+
if (hasOwn(table, key) && (typeof table[key] !== 'string' || table[key].trim().length === 0)) {
|
|
31
|
+
issues.push(commandContractIssue(`${label} must be a string`));
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function validatePositiveIntegerField(table, key, label, issues) {
|
|
35
|
+
if (hasOwn(table, key) && !isPositiveInteger(table[key])) {
|
|
36
|
+
issues.push(commandContractIssue(`${label} must be a positive integer`));
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function validateAllowedStringField(table, key, label, allowedValues, issues) {
|
|
40
|
+
if (!hasOwn(table, key)) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if (typeof table[key] !== 'string' || !allowedValues.has(table[key])) {
|
|
44
|
+
issues.push(commandContractIssue(`${label} must be ${Array.from(allowedValues).map((value) => `"${value}"`).join(' or ')}`));
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function validateCommandDefaults(commandsToml, issues) {
|
|
48
|
+
const defaults = validateTable(commandsToml, 'defaults', issues);
|
|
49
|
+
if (!defaults) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
validateStringField(defaults, 'missing_behavior', '[commands.defaults].missing_behavior', issues);
|
|
53
|
+
validateBooleanField(defaults, 'allow_inferred_commands', '[commands.defaults].allow_inferred_commands', issues);
|
|
54
|
+
validateBooleanField(defaults, 'require_lifecycle', '[commands.defaults].require_lifecycle', issues);
|
|
55
|
+
validateBooleanField(defaults, 'require_timeout_for_oneshot', '[commands.defaults].require_timeout_for_oneshot', issues);
|
|
56
|
+
validateBooleanField(defaults, 'deny_unmanaged_long_running', '[commands.defaults].deny_unmanaged_long_running', issues);
|
|
57
|
+
validateStringField(defaults, 'default_cwd', '[commands.defaults].default_cwd', issues);
|
|
58
|
+
validateStringField(defaults, 'stdin', '[commands.defaults].stdin', issues);
|
|
59
|
+
validateStringField(defaults, 'on_timeout', '[commands.defaults].on_timeout', issues);
|
|
60
|
+
validatePositiveIntegerField(defaults, 'default_timeout_seconds', '[commands.defaults].default_timeout_seconds', issues);
|
|
61
|
+
validatePositiveIntegerField(defaults, 'max_output_bytes', '[commands.defaults].max_output_bytes', issues);
|
|
62
|
+
validatePositiveIntegerField(defaults, 'kill_after_seconds', '[commands.defaults].kill_after_seconds', issues);
|
|
63
|
+
}
|
|
64
|
+
function validateCommandResources(commandsToml, issues) {
|
|
65
|
+
if (!hasOwn(commandsToml, 'resources')) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
if (!isRecord(commandsToml.resources)) {
|
|
69
|
+
issues.push(commandContractIssue('[commands.resources] must be a TOML table'));
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
for (const [resourceName, resource] of Object.entries(commandsToml.resources)) {
|
|
73
|
+
if (!isRecord(resource)) {
|
|
74
|
+
issues.push(commandContractIssue(`[commands.resources.${resourceName}] must be a TOML table`));
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
validateStringField(resource, 'type', `[commands.resources.${resourceName}].type`, issues);
|
|
78
|
+
if (hasOwn(resource, 'paths')) {
|
|
79
|
+
const paths = resource.paths;
|
|
80
|
+
if (!Array.isArray(paths) || paths.some((entry) => typeof entry !== 'string' || entry.trim().length === 0)) {
|
|
81
|
+
issues.push(commandContractIssue(`[commands.resources.${resourceName}].paths must be a string array`));
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
validateAllowedStringField(resource, 'concurrency', `[commands.resources.${resourceName}].concurrency`, new Set(['shared_reader', 'exclusive_writer', 'exclusive']), issues);
|
|
85
|
+
validateStringField(resource, 'description', `[commands.resources.${resourceName}].description`, issues);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
function validateCommandIntentEffects(intentName, intent, issues) {
|
|
89
|
+
if (!hasOwn(intent, 'effects')) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
if (!Array.isArray(intent.effects) || intent.effects.some((entry) => !isRecord(entry))) {
|
|
93
|
+
issues.push(commandContractIssue(`[commands.intents.${intentName}].effects must be an array of tables`));
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
for (const effect of intent.effects) {
|
|
97
|
+
const effectTable = effect;
|
|
98
|
+
validateAllowedStringField(effectTable, 'type', `[commands.intents.${intentName}].effects.type`, COMMAND_EFFECT_TYPES, issues);
|
|
99
|
+
validateAllowedStringField(effectTable, 'mode', `[commands.intents.${intentName}].effects.mode`, COMMAND_EFFECT_MODES, issues);
|
|
100
|
+
validateAllowedStringField(effectTable, 'concurrency', `[commands.intents.${intentName}].effects.concurrency`, COMMAND_EFFECT_CONCURRENCY, issues);
|
|
101
|
+
validateStringField(effectTable, 'path', `[commands.intents.${intentName}].effects.path`, issues);
|
|
102
|
+
validateStringField(effectTable, 'lock', `[commands.intents.${intentName}].effects.lock`, issues);
|
|
103
|
+
if (hasOwn(effectTable, 'paths')) {
|
|
104
|
+
const paths = effectTable.paths;
|
|
105
|
+
if (!Array.isArray(paths) || paths.some((entry) => typeof entry !== 'string' || entry.trim().length === 0)) {
|
|
106
|
+
issues.push(commandContractIssue(`[commands.intents.${intentName}].effects.paths must be a string array`));
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
function validateCommandIntent(intentName, intent, issues) {
|
|
112
|
+
if (!commandIntentNameIsSafe(intentName)) {
|
|
113
|
+
issues.push(commandContractIssue(`Intent ${intentName} name must contain only letters, numbers, underscores, and hyphens`));
|
|
114
|
+
}
|
|
115
|
+
validateStringField(intent, 'status', `[commands.intents.${intentName}].status`, issues);
|
|
116
|
+
validateAllowedStringField(intent, 'lifecycle', `[commands.intents.${intentName}].lifecycle`, COMMAND_LIFECYCLES, issues);
|
|
117
|
+
validateAllowedStringField(intent, 'run_policy', `[commands.intents.${intentName}].run_policy`, COMMAND_RUN_POLICIES, issues);
|
|
118
|
+
if (intent.status !== 'configured') {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
const lifecycle = typeof intent.lifecycle === 'string' ? intent.lifecycle : undefined;
|
|
122
|
+
const runPolicy = typeof intent.run_policy === 'string' ? intent.run_policy : undefined;
|
|
123
|
+
if (!lifecycle) {
|
|
124
|
+
issues.push(commandContractIssue(`Configured intent ${intentName} must define lifecycle`));
|
|
125
|
+
}
|
|
126
|
+
if (!runPolicy) {
|
|
127
|
+
issues.push(commandContractIssue(`Configured intent ${intentName} must define run_policy`));
|
|
128
|
+
}
|
|
129
|
+
if (lifecycle === 'oneshot') {
|
|
130
|
+
validatePositiveIntegerField(intent, 'timeout_seconds', `[commands.intents.${intentName}].timeout_seconds`, issues);
|
|
131
|
+
if (!hasOwn(intent, 'timeout_seconds')) {
|
|
132
|
+
issues.push(commandContractIssue(`Oneshot intent ${intentName} must define timeout_seconds`));
|
|
133
|
+
}
|
|
134
|
+
if (intent.stdin !== 'closed') {
|
|
135
|
+
issues.push(commandContractIssue(`Oneshot intent ${intentName} must set stdin = "closed"`));
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
if (lifecycle && LONG_RUNNING_LIFECYCLES.has(lifecycle) && runPolicy === 'agent_allowed') {
|
|
139
|
+
issues.push(commandContractIssue(`Long-running intent ${intentName} must not use run_policy = "agent_allowed"`));
|
|
140
|
+
}
|
|
141
|
+
if (!commandIntentHasCommandSource(intent)) {
|
|
142
|
+
issues.push(commandContractIssue(`Configured intent ${intentName} must define argv or mode = "shell" with cmd`));
|
|
143
|
+
}
|
|
144
|
+
if (commandIntentHasBlockedShellBackgroundPattern(intent)) {
|
|
145
|
+
issues.push(commandContractIssue(`Shell intent ${intentName} contains a blocked long-running or background pattern`));
|
|
146
|
+
}
|
|
147
|
+
if (hasOwn(intent, 'success_exit_codes')) {
|
|
148
|
+
const value = intent.success_exit_codes;
|
|
149
|
+
if (!Array.isArray(value) || value.length === 0 || value.some((entry) => !Number.isInteger(entry))) {
|
|
150
|
+
issues.push(commandContractIssue(`[commands.intents.${intentName}].success_exit_codes must be an integer array`));
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
validateCommandIntentEffects(intentName, intent, issues);
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* mf:anchor core.command-contract-validation
|
|
157
|
+
* purpose: Validate command intent declarations that gate agent-executable repository commands.
|
|
158
|
+
* search: commands.toml, intent status, lifecycle, run policy, timeout
|
|
159
|
+
* invariant: Agent-executable commands need configured status, oneshot lifecycle, timeout, and explicit command source.
|
|
160
|
+
* risk: config, security
|
|
161
|
+
*/
|
|
162
|
+
export function validateCommandContractConfig(commandsToml) {
|
|
163
|
+
const issues = [];
|
|
164
|
+
if (!commandsToml) {
|
|
165
|
+
return issues;
|
|
166
|
+
}
|
|
167
|
+
validateStringField(commandsToml, 'schema_version', '[commands].schema_version', issues);
|
|
168
|
+
validateCommandDefaults(commandsToml, issues);
|
|
169
|
+
validateCommandResources(commandsToml, issues);
|
|
170
|
+
if (!isRecord(commandsToml.intents)) {
|
|
171
|
+
issues.push(commandContractIssue('Missing [intents] table in .mustflow/config/commands.toml'));
|
|
172
|
+
return issues;
|
|
173
|
+
}
|
|
174
|
+
for (const [intentName, intent] of Object.entries(commandsToml.intents)) {
|
|
175
|
+
if (!isRecord(intent)) {
|
|
176
|
+
issues.push(commandContractIssue(`Intent ${intentName} must be a TOML table`));
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
validateCommandIntent(intentName, intent, issues);
|
|
180
|
+
}
|
|
181
|
+
return issues;
|
|
182
|
+
}
|
|
183
|
+
export function validateCommandContractStrictDefaults(projectRoot, commandsToml) {
|
|
184
|
+
const issues = [];
|
|
185
|
+
if (!commandsToml || !isRecord(commandsToml.defaults)) {
|
|
186
|
+
return issues;
|
|
187
|
+
}
|
|
188
|
+
if (!hasOwn(commandsToml.defaults, 'max_output_bytes')) {
|
|
189
|
+
issues.push(commandContractIssue('[commands.defaults].max_output_bytes is required'));
|
|
190
|
+
}
|
|
191
|
+
if (!hasOwn(commandsToml.defaults, 'on_timeout')) {
|
|
192
|
+
issues.push(commandContractIssue('[commands.defaults].on_timeout is required'));
|
|
193
|
+
}
|
|
194
|
+
issues.push(...validateCommandEffects(projectRoot, commandsToml));
|
|
195
|
+
issues.push(...validateCommandEffectLockWarnings(commandsToml));
|
|
196
|
+
return issues;
|
|
197
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
export function resolveSafeProjectCwd(projectRoot, rawCwd) {
|
|
3
|
+
const cwd = rawCwd ?? '.';
|
|
4
|
+
const resolved = path.resolve(projectRoot, cwd);
|
|
5
|
+
const root = path.resolve(projectRoot);
|
|
6
|
+
const resolvedLower = resolved.toLowerCase();
|
|
7
|
+
const rootLower = root.toLowerCase();
|
|
8
|
+
if (resolvedLower !== rootLower && !resolvedLower.startsWith(`${rootLower}${path.sep}`)) {
|
|
9
|
+
throw new Error(`Intent cwd must stay inside the current root: ${cwd}`);
|
|
10
|
+
}
|
|
11
|
+
return resolved;
|
|
12
|
+
}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { isRecord, readString, readStringArray, } from './config-loading.js';
|
|
3
|
+
import { resolveSafeProjectCwd } from './command-cwd.js';
|
|
4
|
+
export const COMMAND_EFFECT_MODES = new Set(['read', 'write', 'append', 'replace', 'delete_recreate']);
|
|
5
|
+
export const COMMAND_EFFECT_TYPES = new Set(['read', 'write']);
|
|
6
|
+
export const COMMAND_EFFECT_CONCURRENCY = new Set(['shared', 'exclusive']);
|
|
7
|
+
function hasOwn(table, key) {
|
|
8
|
+
return Object.prototype.hasOwnProperty.call(table, key);
|
|
9
|
+
}
|
|
10
|
+
function normalizeRelativePath(rawPath) {
|
|
11
|
+
return rawPath.replaceAll('\\', '/').replace(/^\.\/+/u, '').replace(/\/+$/u, '') || '.';
|
|
12
|
+
}
|
|
13
|
+
function pathLockKey(relativePath) {
|
|
14
|
+
return `path:${normalizeRelativePath(relativePath)}`;
|
|
15
|
+
}
|
|
16
|
+
function validateEffectPath(projectRoot, intent, rawPath) {
|
|
17
|
+
const cwd = resolveSafeProjectCwd(projectRoot, readString(intent, 'cwd'));
|
|
18
|
+
const resolved = path.resolve(cwd, rawPath);
|
|
19
|
+
const root = path.resolve(projectRoot);
|
|
20
|
+
const resolvedLower = resolved.toLowerCase();
|
|
21
|
+
const rootLower = root.toLowerCase();
|
|
22
|
+
if (resolvedLower !== rootLower && !resolvedLower.startsWith(`${rootLower}${path.sep}`)) {
|
|
23
|
+
throw new Error(`Command effect path must stay inside the current root: ${rawPath}`);
|
|
24
|
+
}
|
|
25
|
+
return normalizeRelativePath(path.relative(projectRoot, resolved));
|
|
26
|
+
}
|
|
27
|
+
function readResourcePaths(commandContract, lock) {
|
|
28
|
+
const resource = commandContract.resources[lock];
|
|
29
|
+
if (!isRecord(resource)) {
|
|
30
|
+
return [];
|
|
31
|
+
}
|
|
32
|
+
return readStringArray(resource, 'paths') ?? [];
|
|
33
|
+
}
|
|
34
|
+
function readEffectMode(effect) {
|
|
35
|
+
const mode = readString(effect, 'mode');
|
|
36
|
+
if (mode && COMMAND_EFFECT_MODES.has(mode)) {
|
|
37
|
+
return mode;
|
|
38
|
+
}
|
|
39
|
+
const type = readString(effect, 'type');
|
|
40
|
+
return type === 'read' ? 'read' : 'write';
|
|
41
|
+
}
|
|
42
|
+
function effectAccess(mode, effect) {
|
|
43
|
+
const type = readString(effect, 'type');
|
|
44
|
+
if (type && COMMAND_EFFECT_TYPES.has(type)) {
|
|
45
|
+
return type;
|
|
46
|
+
}
|
|
47
|
+
return mode === 'read' ? 'read' : 'write';
|
|
48
|
+
}
|
|
49
|
+
function readEffectConcurrency(effect, mode, access) {
|
|
50
|
+
const concurrency = readString(effect, 'concurrency');
|
|
51
|
+
if (concurrency && COMMAND_EFFECT_CONCURRENCY.has(concurrency)) {
|
|
52
|
+
return concurrency;
|
|
53
|
+
}
|
|
54
|
+
if (mode === 'read' && access === 'read') {
|
|
55
|
+
return 'shared';
|
|
56
|
+
}
|
|
57
|
+
return 'exclusive';
|
|
58
|
+
}
|
|
59
|
+
function normalizeDeclaredEffect(projectRoot, commandContract, intentName, intent, effect) {
|
|
60
|
+
const mode = readEffectMode(effect);
|
|
61
|
+
const access = effectAccess(mode, effect);
|
|
62
|
+
const concurrency = readEffectConcurrency(effect, mode, access);
|
|
63
|
+
const lock = readString(effect, 'lock');
|
|
64
|
+
const rawPaths = [
|
|
65
|
+
...(readStringArray(effect, 'paths') ?? []),
|
|
66
|
+
...(readString(effect, 'path') ? [readString(effect, 'path')] : []),
|
|
67
|
+
];
|
|
68
|
+
const fallbackPaths = lock ? readResourcePaths(commandContract, lock) : [];
|
|
69
|
+
const paths = rawPaths.length > 0 ? rawPaths : fallbackPaths;
|
|
70
|
+
if (!lock && paths.length === 0) {
|
|
71
|
+
throw new Error(`Command effect for intent ${intentName} must define path, paths, or lock`);
|
|
72
|
+
}
|
|
73
|
+
return paths.map((rawPath) => {
|
|
74
|
+
const normalizedPath = validateEffectPath(projectRoot, intent, rawPath);
|
|
75
|
+
return {
|
|
76
|
+
intent: intentName,
|
|
77
|
+
source: 'effects',
|
|
78
|
+
access,
|
|
79
|
+
mode,
|
|
80
|
+
path: normalizedPath,
|
|
81
|
+
lock: lock ?? pathLockKey(normalizedPath),
|
|
82
|
+
concurrency,
|
|
83
|
+
};
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
function normalizeWritesEffect(projectRoot, intentName, intent) {
|
|
87
|
+
const writes = readStringArray(intent, 'writes') ?? [];
|
|
88
|
+
return writes.map((rawPath) => {
|
|
89
|
+
const normalizedPath = validateEffectPath(projectRoot, intent, rawPath);
|
|
90
|
+
return {
|
|
91
|
+
intent: intentName,
|
|
92
|
+
source: 'writes',
|
|
93
|
+
access: 'write',
|
|
94
|
+
mode: 'write',
|
|
95
|
+
path: normalizedPath,
|
|
96
|
+
lock: pathLockKey(normalizedPath),
|
|
97
|
+
concurrency: 'exclusive',
|
|
98
|
+
};
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
export function normalizeCommandEffects(projectRoot, commandContract, intentName) {
|
|
102
|
+
const intent = commandContract.intents[intentName];
|
|
103
|
+
if (!isRecord(intent)) {
|
|
104
|
+
return [];
|
|
105
|
+
}
|
|
106
|
+
const rawEffects = intent.effects;
|
|
107
|
+
if (Array.isArray(rawEffects) && rawEffects.length > 0) {
|
|
108
|
+
return rawEffects.flatMap((effect) => isRecord(effect) ? normalizeDeclaredEffect(projectRoot, commandContract, intentName, intent, effect) : []);
|
|
109
|
+
}
|
|
110
|
+
return normalizeWritesEffect(projectRoot, intentName, intent);
|
|
111
|
+
}
|
|
112
|
+
export function commandEffectsConflict(left, right) {
|
|
113
|
+
if (left.lock !== right.lock) {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
if (left.mode === 'delete_recreate' || right.mode === 'delete_recreate') {
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
if (left.concurrency === 'exclusive' || right.concurrency === 'exclusive') {
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
return left.access === 'write' || right.access === 'write';
|
|
123
|
+
}
|
|
124
|
+
export function validateCommandEffects(projectRoot, commandsToml) {
|
|
125
|
+
const issues = [];
|
|
126
|
+
if (!commandsToml || !isRecord(commandsToml.intents)) {
|
|
127
|
+
return issues;
|
|
128
|
+
}
|
|
129
|
+
const commandContract = {
|
|
130
|
+
defaults: isRecord(commandsToml.defaults) ? commandsToml.defaults : {},
|
|
131
|
+
intents: commandsToml.intents,
|
|
132
|
+
resources: isRecord(commandsToml.resources) ? commandsToml.resources : {},
|
|
133
|
+
};
|
|
134
|
+
for (const [intentName, intent] of Object.entries(commandContract.intents)) {
|
|
135
|
+
if (!isRecord(intent)) {
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
if (hasOwn(intent, 'effects') && (!Array.isArray(intent.effects) || intent.effects.some((effect) => !isRecord(effect)))) {
|
|
139
|
+
issues.push({ message: `[commands.intents.${intentName}].effects must be an array of tables` });
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
try {
|
|
143
|
+
normalizeCommandEffects(projectRoot, commandContract, intentName);
|
|
144
|
+
}
|
|
145
|
+
catch (error) {
|
|
146
|
+
issues.push({ message: error instanceof Error ? error.message : String(error) });
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return issues;
|
|
150
|
+
}
|
|
151
|
+
export function validateCommandEffectLockWarnings(commandsToml) {
|
|
152
|
+
const issues = [];
|
|
153
|
+
if (!commandsToml || !isRecord(commandsToml.intents)) {
|
|
154
|
+
return issues;
|
|
155
|
+
}
|
|
156
|
+
const lockToIntents = new Map();
|
|
157
|
+
for (const [intentName, intent] of Object.entries(commandsToml.intents)) {
|
|
158
|
+
if (!isRecord(intent)) {
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
if (intent.status !== 'configured' ||
|
|
162
|
+
intent.lifecycle !== 'oneshot' ||
|
|
163
|
+
intent.run_policy !== 'agent_allowed' ||
|
|
164
|
+
Array.isArray(intent.effects)) {
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
for (const rawPath of readStringArray(intent, 'writes') ?? []) {
|
|
168
|
+
const lock = pathLockKey(rawPath);
|
|
169
|
+
lockToIntents.set(lock, [...(lockToIntents.get(lock) ?? []), intentName]);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
for (const [lock, intents] of lockToIntents) {
|
|
173
|
+
if (intents.length < 2) {
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
issues.push({
|
|
177
|
+
severity: 'warning',
|
|
178
|
+
message: `configured agent-runnable intents ${intents.sort((left, right) => left.localeCompare(right)).join(', ')} share ${lock} through writes without explicit effects or resource locks`,
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
return issues;
|
|
182
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { isRecord, readPositiveInteger, readString, readStringArray, } from './config-loading.js';
|
|
2
|
+
const COMMAND_CONTRACT_SOURCE_FILES = [
|
|
3
|
+
'AGENTS.md',
|
|
4
|
+
'.mustflow/docs/agent-workflow.md',
|
|
5
|
+
'.mustflow/config/commands.toml',
|
|
6
|
+
'.mustflow/config/mustflow.toml',
|
|
7
|
+
];
|
|
8
|
+
const ASSET_OPTIMIZATION_SOURCE_FILES = [
|
|
9
|
+
'.mustflow/skills/INDEX.md',
|
|
10
|
+
'.mustflow/skills/web-asset-optimization/SKILL.md',
|
|
11
|
+
'.mustflow/config/commands.toml',
|
|
12
|
+
'.mustflow/config/mustflow.toml',
|
|
13
|
+
];
|
|
14
|
+
function readOptionalStringArray(table, key) {
|
|
15
|
+
return readStringArray(table, key) ?? [];
|
|
16
|
+
}
|
|
17
|
+
function readOptionalBoolean(table, key) {
|
|
18
|
+
const value = table[key];
|
|
19
|
+
return typeof value === 'boolean' ? value : null;
|
|
20
|
+
}
|
|
21
|
+
function readOptionalIntegerArray(table, key) {
|
|
22
|
+
const value = table[key];
|
|
23
|
+
if (!Array.isArray(value) || value.some((entry) => !Number.isInteger(entry))) {
|
|
24
|
+
return [];
|
|
25
|
+
}
|
|
26
|
+
return value.map((entry) => Number(entry));
|
|
27
|
+
}
|
|
28
|
+
function resolveCommandMode(intent) {
|
|
29
|
+
const argv = readStringArray(intent, 'argv');
|
|
30
|
+
const shellCommand = intent.mode === 'shell' ? readString(intent, 'cmd') : undefined;
|
|
31
|
+
if (argv && argv.length > 0) {
|
|
32
|
+
return 'argv';
|
|
33
|
+
}
|
|
34
|
+
if (shellCommand) {
|
|
35
|
+
return 'shell';
|
|
36
|
+
}
|
|
37
|
+
return 'missing';
|
|
38
|
+
}
|
|
39
|
+
function summarizeIntent(name, intent) {
|
|
40
|
+
return {
|
|
41
|
+
name,
|
|
42
|
+
status: readString(intent, 'status') ?? null,
|
|
43
|
+
lifecycle: readString(intent, 'lifecycle') ?? null,
|
|
44
|
+
runPolicy: readString(intent, 'run_policy') ?? null,
|
|
45
|
+
stdin: readString(intent, 'stdin') ?? null,
|
|
46
|
+
timeoutSeconds: readPositiveInteger(intent, 'timeout_seconds') ?? null,
|
|
47
|
+
mode: resolveCommandMode(intent),
|
|
48
|
+
cwd: readString(intent, 'cwd') ?? null,
|
|
49
|
+
writes: readOptionalStringArray(intent, 'writes'),
|
|
50
|
+
network: readOptionalBoolean(intent, 'network'),
|
|
51
|
+
destructive: readOptionalBoolean(intent, 'destructive'),
|
|
52
|
+
successExitCodes: readOptionalIntegerArray(intent, 'success_exit_codes'),
|
|
53
|
+
requiredAfter: readOptionalStringArray(intent, 'required_after'),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function collectBlockingReasons(summary) {
|
|
57
|
+
const reasons = [];
|
|
58
|
+
if (summary.status !== 'configured') {
|
|
59
|
+
reasons.push(`status is ${summary.status ?? 'missing'}, not configured`);
|
|
60
|
+
}
|
|
61
|
+
if (summary.lifecycle !== 'oneshot') {
|
|
62
|
+
reasons.push(`lifecycle is ${summary.lifecycle ?? 'missing'}, not oneshot`);
|
|
63
|
+
}
|
|
64
|
+
if (summary.runPolicy !== 'agent_allowed') {
|
|
65
|
+
reasons.push(`run_policy is ${summary.runPolicy ?? 'missing'}, not agent_allowed`);
|
|
66
|
+
}
|
|
67
|
+
if (summary.stdin !== 'closed') {
|
|
68
|
+
reasons.push(`stdin is ${summary.stdin ?? 'missing'}, not closed`);
|
|
69
|
+
}
|
|
70
|
+
if (summary.timeoutSeconds === null) {
|
|
71
|
+
reasons.push('timeout_seconds is missing or invalid');
|
|
72
|
+
}
|
|
73
|
+
if (summary.mode === 'missing') {
|
|
74
|
+
reasons.push('no argv command or shell cmd is declared');
|
|
75
|
+
}
|
|
76
|
+
return reasons;
|
|
77
|
+
}
|
|
78
|
+
export function explainCommandIntent(contract, commandName) {
|
|
79
|
+
const intentCandidate = contract.intents[commandName];
|
|
80
|
+
if (!isRecord(intentCandidate)) {
|
|
81
|
+
return {
|
|
82
|
+
kind: 'unknown',
|
|
83
|
+
inputCommand: commandName,
|
|
84
|
+
decision: `command intent "${commandName}" is not declared`,
|
|
85
|
+
reason: 'mustflow does not infer commands from package manager files or repository conventions.',
|
|
86
|
+
effectiveAction: 'Do not run a guessed command; add a configured intent or report the missing command contract.',
|
|
87
|
+
countsAsMustflowVerification: false,
|
|
88
|
+
sourceFiles: COMMAND_CONTRACT_SOURCE_FILES,
|
|
89
|
+
intent: null,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
const intent = summarizeIntent(commandName, intentCandidate);
|
|
93
|
+
const blockingReasons = collectBlockingReasons(intent);
|
|
94
|
+
if (blockingReasons.length === 0) {
|
|
95
|
+
return {
|
|
96
|
+
kind: 'allowed',
|
|
97
|
+
inputCommand: commandName,
|
|
98
|
+
decision: `command intent "${commandName}" is allowed through mf run`,
|
|
99
|
+
reason: 'the intent is configured, one-shot, agent-allowed, closed-stdin, bounded by a timeout, and has an explicit command source.',
|
|
100
|
+
effectiveAction: `Run mf run ${commandName} when this intent is required for the changed behavior.`,
|
|
101
|
+
countsAsMustflowVerification: true,
|
|
102
|
+
sourceFiles: COMMAND_CONTRACT_SOURCE_FILES,
|
|
103
|
+
intent,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
return {
|
|
107
|
+
kind: 'blocked',
|
|
108
|
+
inputCommand: commandName,
|
|
109
|
+
decision: `command intent "${commandName}" is not agent-runnable`,
|
|
110
|
+
reason: blockingReasons.join('; '),
|
|
111
|
+
effectiveAction: 'Do not run this command as verification; report the command-contract reason instead.',
|
|
112
|
+
countsAsMustflowVerification: false,
|
|
113
|
+
sourceFiles: COMMAND_CONTRACT_SOURCE_FILES,
|
|
114
|
+
intent,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
export function explainAssetOptimization(contract) {
|
|
118
|
+
const decision = explainCommandIntent(contract, 'asset_optimize');
|
|
119
|
+
if (decision.kind === 'allowed') {
|
|
120
|
+
return {
|
|
121
|
+
...decision,
|
|
122
|
+
decision: 'web asset optimization has a configured command intent',
|
|
123
|
+
reason: 'the web-asset-optimization skill is available and asset_optimize is configured as an agent-runnable command intent.',
|
|
124
|
+
effectiveAction: 'Read .mustflow/skills/web-asset-optimization/SKILL.md, then run mf run asset_optimize when image asset changes require optimization evidence.',
|
|
125
|
+
sourceFiles: ASSET_OPTIMIZATION_SOURCE_FILES,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
return {
|
|
129
|
+
...decision,
|
|
130
|
+
decision: 'web asset optimization is skill-guided but command-gated',
|
|
131
|
+
reason: 'the web-asset-optimization skill applies to web image asset work, but asset_optimize is not currently an agent-runnable configured intent.',
|
|
132
|
+
effectiveAction: 'Read .mustflow/skills/web-asset-optimization/SKILL.md, do not guess external converters or package commands, and report the missing asset_optimize contract when optimization evidence is required.',
|
|
133
|
+
sourceFiles: ASSET_OPTIMIZATION_SOURCE_FILES,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { isRecord, readString } from './config-loading.js';
|
|
2
|
+
import { commandIntentHasBlockedShellBackgroundPattern, commandIntentHasCommandSource, commandIntentNameIsSafe, } from './command-contract-rules.js';
|
|
3
|
+
export function evaluateCommandIntentEligibility(intentName, rawIntent) {
|
|
4
|
+
if (!commandIntentNameIsSafe(intentName)) {
|
|
5
|
+
return {
|
|
6
|
+
ok: false,
|
|
7
|
+
code: 'unsafe_intent_name',
|
|
8
|
+
detail: 'Intent name can contain only ASCII letters, numbers, underscores, and hyphens.',
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
if (!isRecord(rawIntent)) {
|
|
12
|
+
return {
|
|
13
|
+
ok: false,
|
|
14
|
+
code: 'intent_not_table',
|
|
15
|
+
detail: 'Intent is not a TOML table.',
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
const status = readString(rawIntent, 'status') ?? 'unknown';
|
|
19
|
+
if (status !== 'configured') {
|
|
20
|
+
const reason = readString(rawIntent, 'reason');
|
|
21
|
+
return {
|
|
22
|
+
ok: false,
|
|
23
|
+
code: 'status_not_configured',
|
|
24
|
+
detail: reason ?? `Intent status is ${status}.`,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
const lifecycle = readString(rawIntent, 'lifecycle');
|
|
28
|
+
if (lifecycle !== 'oneshot') {
|
|
29
|
+
return {
|
|
30
|
+
ok: false,
|
|
31
|
+
code: 'lifecycle_not_oneshot',
|
|
32
|
+
detail: `Intent lifecycle is ${lifecycle ?? 'unknown'}.`,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
const runPolicy = readString(rawIntent, 'run_policy');
|
|
36
|
+
if (runPolicy !== 'agent_allowed') {
|
|
37
|
+
return {
|
|
38
|
+
ok: false,
|
|
39
|
+
code: 'run_policy_not_agent_allowed',
|
|
40
|
+
detail: `Intent run_policy is ${runPolicy ?? 'unknown'}.`,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
if (rawIntent.stdin !== 'closed') {
|
|
44
|
+
return {
|
|
45
|
+
ok: false,
|
|
46
|
+
code: 'stdin_not_closed',
|
|
47
|
+
detail: 'Intent stdin is not closed.',
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
if (!Number.isInteger(rawIntent.timeout_seconds) || Number(rawIntent.timeout_seconds) <= 0) {
|
|
51
|
+
return {
|
|
52
|
+
ok: false,
|
|
53
|
+
code: 'missing_timeout',
|
|
54
|
+
detail: 'Intent timeout_seconds is missing or invalid.',
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
if (!commandIntentHasCommandSource(rawIntent)) {
|
|
58
|
+
return {
|
|
59
|
+
ok: false,
|
|
60
|
+
code: 'missing_command_source',
|
|
61
|
+
detail: 'Intent does not define argv or shell cmd.',
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
if (commandIntentHasBlockedShellBackgroundPattern(rawIntent)) {
|
|
65
|
+
return {
|
|
66
|
+
ok: false,
|
|
67
|
+
code: 'blocked_shell_background_pattern',
|
|
68
|
+
detail: 'Shell command contains a blocked long-running or background pattern.',
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
ok: true,
|
|
73
|
+
code: 'ok',
|
|
74
|
+
detail: null,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { readTomlFile } from './toml.js';
|
|
4
|
+
export const COMMAND_LIFECYCLES = new Set(['oneshot', 'server', 'watch', 'interactive', 'browser', 'background']);
|
|
5
|
+
export const LONG_RUNNING_LIFECYCLES = new Set(['server', 'watch', 'interactive', 'browser', 'background']);
|
|
6
|
+
export const COMMAND_RUN_POLICIES = new Set(['agent_allowed', 'requires_explicit_user_request', 'manual_only']);
|
|
7
|
+
export const COMMANDS_CONFIG_RELATIVE_PATH = '.mustflow/config/commands.toml';
|
|
8
|
+
export const MUSTFLOW_CONFIG_RELATIVE_PATH = '.mustflow/config/mustflow.toml';
|
|
9
|
+
export function isRecord(value) {
|
|
10
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
11
|
+
}
|
|
12
|
+
export function resolveMustflowConfigPath(projectRoot, relativePath) {
|
|
13
|
+
return path.join(projectRoot, ...relativePath.split('/'));
|
|
14
|
+
}
|
|
15
|
+
export function readMustflowConfig(projectRoot) {
|
|
16
|
+
const parsed = readTomlFile(resolveMustflowConfigPath(projectRoot, MUSTFLOW_CONFIG_RELATIVE_PATH));
|
|
17
|
+
if (!isRecord(parsed)) {
|
|
18
|
+
throw new Error(`${MUSTFLOW_CONFIG_RELATIVE_PATH} must contain a TOML table`);
|
|
19
|
+
}
|
|
20
|
+
return parsed;
|
|
21
|
+
}
|
|
22
|
+
export function readMustflowConfigIfExists(projectRoot) {
|
|
23
|
+
const configPath = resolveMustflowConfigPath(projectRoot, MUSTFLOW_CONFIG_RELATIVE_PATH);
|
|
24
|
+
return existsSync(configPath) ? readMustflowConfig(projectRoot) : undefined;
|
|
25
|
+
}
|
|
26
|
+
export function readCommandContract(projectRoot) {
|
|
27
|
+
const parsed = readTomlFile(resolveMustflowConfigPath(projectRoot, COMMANDS_CONFIG_RELATIVE_PATH));
|
|
28
|
+
if (!isRecord(parsed)) {
|
|
29
|
+
throw new Error(`${COMMANDS_CONFIG_RELATIVE_PATH} must contain a TOML table`);
|
|
30
|
+
}
|
|
31
|
+
if (!isRecord(parsed.intents)) {
|
|
32
|
+
throw new Error(`Missing [intents] table in ${COMMANDS_CONFIG_RELATIVE_PATH}`);
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
defaults: isRecord(parsed.defaults) ? parsed.defaults : {},
|
|
36
|
+
intents: parsed.intents,
|
|
37
|
+
resources: isRecord(parsed.resources) ? parsed.resources : {},
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
export function readString(table, key) {
|
|
41
|
+
const value = table[key];
|
|
42
|
+
return typeof value === 'string' && value.trim().length > 0 ? value : undefined;
|
|
43
|
+
}
|
|
44
|
+
export function readPositiveInteger(table, key) {
|
|
45
|
+
const value = table[key];
|
|
46
|
+
return Number.isInteger(value) && Number(value) > 0 ? Number(value) : undefined;
|
|
47
|
+
}
|
|
48
|
+
export function readStringArray(table, key) {
|
|
49
|
+
const value = table[key];
|
|
50
|
+
if (!Array.isArray(value) || value.some((entry) => typeof entry !== 'string')) {
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
return [...value];
|
|
54
|
+
}
|