mustflow 2.103.3 → 2.103.12

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.
Files changed (50) hide show
  1. package/dist/cli/commands/run.js +11 -0
  2. package/dist/cli/commands/script-pack.js +2 -0
  3. package/dist/cli/i18n/en.js +35 -0
  4. package/dist/cli/i18n/es.js +35 -0
  5. package/dist/cli/i18n/fr.js +35 -0
  6. package/dist/cli/i18n/hi.js +35 -0
  7. package/dist/cli/i18n/ko.js +35 -0
  8. package/dist/cli/i18n/zh.js +35 -0
  9. package/dist/cli/lib/external-skill-import.js +78 -14
  10. package/dist/cli/lib/local-index/sql.js +9 -1
  11. package/dist/cli/lib/run-plan.js +37 -0
  12. package/dist/cli/lib/script-pack-registry.js +57 -0
  13. package/dist/cli/script-packs/repo-deploy-surface.js +98 -0
  14. package/dist/cli/script-packs/repo-security-pattern-scan.js +150 -0
  15. package/dist/core/change-impact.js +16 -0
  16. package/dist/core/code-outline.js +3 -13
  17. package/dist/core/command-env.js +26 -8
  18. package/dist/core/config-chain.js +3 -13
  19. package/dist/core/dependency-graph.js +3 -13
  20. package/dist/core/docs-link-integrity.js +23 -4
  21. package/dist/core/env-contract.js +3 -13
  22. package/dist/core/export-diff.js +3 -3
  23. package/dist/core/ignored-directories.js +40 -0
  24. package/dist/core/public-json-contracts.js +18 -0
  25. package/dist/core/reference-drift.js +4 -2
  26. package/dist/core/related-files.js +3 -13
  27. package/dist/core/repo-deploy-surface.js +428 -0
  28. package/dist/core/repo-merge-conflict-scan.js +3 -9
  29. package/dist/core/route-outline.js +3 -13
  30. package/dist/core/script-pack-suggestions.js +52 -14
  31. package/dist/core/secret-risk-scan.js +3 -13
  32. package/dist/core/security-pattern-scan.js +518 -0
  33. package/dist/core/skill-route-resolution.js +21 -1
  34. package/package.json +2 -2
  35. package/schemas/README.md +7 -0
  36. package/schemas/link-integrity-report.schema.json +1 -0
  37. package/schemas/reference-drift-report.schema.json +1 -0
  38. package/schemas/repo-deploy-surface-report.schema.json +190 -0
  39. package/schemas/security-pattern-scan-report.schema.json +196 -0
  40. package/templates/default/i18n.toml +20 -8
  41. package/templates/default/locales/en/.mustflow/skills/ai-generated-code-hardening/SKILL.md +30 -7
  42. package/templates/default/locales/en/.mustflow/skills/api-contract-change/SKILL.md +18 -9
  43. package/templates/default/locales/en/.mustflow/skills/api-request-performance-review/SKILL.md +12 -6
  44. package/templates/default/locales/en/.mustflow/skills/completion-evidence-gate/SKILL.md +20 -9
  45. package/templates/default/locales/en/.mustflow/skills/hot-path-performance-review/SKILL.md +20 -15
  46. package/templates/default/locales/en/.mustflow/skills/next-action-menu/SKILL.md +22 -7
  47. package/templates/default/locales/en/.mustflow/skills/quadratic-scan-review/SKILL.md +21 -19
  48. package/templates/default/locales/en/.mustflow/skills/react-code-change/SKILL.md +54 -8
  49. package/templates/default/locales/en/.mustflow/skills/vertical-slice-tdd/SKILL.md +22 -8
  50. package/templates/default/manifest.toml +1 -1
@@ -79,6 +79,14 @@ function reportRunPlanFailure(plan, reporter, lang) {
79
79
  detail: getRunPlanDetail(plan, lang, 'run.error.blockedLongRunningCommandDetail'),
80
80
  });
81
81
  break;
82
+ case 'network_requires_approval':
83
+ case 'destructive_requires_approval':
84
+ case 'approval_policy_unreadable':
85
+ message = t(lang, 'run.error.approvalRequired', {
86
+ intent: plan.intentName,
87
+ detail: getRunPlanDetail(plan, lang, 'run.error.approvalRequiredDetail'),
88
+ });
89
+ break;
82
90
  case 'cwd_outside_project':
83
91
  message = t(lang, 'run.error.cwdOutsideProject', {
84
92
  intent: plan.intentName,
@@ -327,6 +335,9 @@ export async function runRun(args, reporter, lang = 'en', options = {}) {
327
335
  return plan.ok ? 0 : 1;
328
336
  }
329
337
  if (!plan.ok) {
338
+ if (json) {
339
+ reporter.stdout(JSON.stringify(createRunPreview(plan, 'plan-only'), null, 2));
340
+ }
330
341
  reportRunPlanFailure(plan, reporter, lang);
331
342
  writeLatestProfile(profiler, options, {
332
343
  projectRoot,
@@ -76,6 +76,7 @@ export function getScriptPackHelp(lang = 'en') {
76
76
  'mf script-pack run repo/config-chain inspect src/cli/index.ts --json',
77
77
  'mf script-pack run repo/env-contract scan --json',
78
78
  'mf script-pack run repo/secret-risk-scan scan src README.md --json',
79
+ 'mf script-pack run repo/security-pattern-scan scan src .github/workflows --json',
79
80
  'mf script-pack run repo/generated-boundary check src/cli/index.ts --json',
80
81
  'mf script-pack run repo/merge-conflict-scan check --json',
81
82
  'mf script-pack run repo/git-ignore-audit audit .env.local dist/app.js --json',
@@ -83,6 +84,7 @@ export function getScriptPackHelp(lang = 'en') {
83
84
  'mf script-pack run repo/skill-route-audit audit --json',
84
85
  'mf script-pack run repo/version-source inspect --json',
85
86
  'mf script-pack run repo/approval-gate check --action git_commit --json',
87
+ 'mf script-pack run repo/deploy-surface inspect --json',
86
88
  'mf script-pack run repo/related-files map src/cli/index.ts --json',
87
89
  'mf script-pack run core/text-budget --help',
88
90
  ],
@@ -1123,9 +1123,29 @@ Read these files before working:
1123
1123
  "secretRiskScan.error.missingAction": "Specify a secret-risk-scan action: scan",
1124
1124
  "secretRiskScan.error.unknownAction": "Unknown secret-risk-scan action: {action}",
1125
1125
  "secretRiskScan.error.invalidPositiveInteger": "{option} must be a positive safe integer: {value}",
1126
+ "scriptPack.script.securityPatternScan.summary": "Scan repository files for high-signal security code patterns",
1127
+ "securityPatternScan.help.summary": "Scan source, CI, and config files for security code-pattern leads without printing matched source lines or secret values.",
1128
+ "securityPatternScan.help.option.maxFiles": "Maximum number of files to inspect. Default: 1000",
1129
+ "securityPatternScan.help.option.maxFileBytes": "Maximum bytes to read from each inspected file. Default: 262144",
1130
+ "securityPatternScan.help.option.maxFindings": "Maximum number of findings to report. Default: 300",
1131
+ "securityPatternScan.help.exit.ok": "The security-pattern scan completed without blocking findings",
1132
+ "securityPatternScan.help.exit.fail": "The security-pattern scan found invalid input, unreadable files, or security-pattern findings",
1133
+ "securityPatternScan.title": "mustflow security pattern scan",
1134
+ "securityPatternScan.label.files": "Files",
1135
+ "securityPatternScan.label.findings": "Findings",
1136
+ "securityPatternScan.label.categories": "Categories",
1137
+ "securityPatternScan.label.highOrCritical": "High or critical",
1138
+ "securityPatternScan.label.truncated": "Truncated",
1139
+ "securityPatternScan.label.reviewFocus": "Review focus",
1140
+ "securityPatternScan.label.issues": "Issues",
1141
+ "securityPatternScan.clean": "No high-signal security code patterns were found.",
1142
+ "securityPatternScan.error.missingAction": "Specify a security-pattern-scan action: scan",
1143
+ "securityPatternScan.error.unknownAction": "Unknown security-pattern-scan action: {action}",
1144
+ "securityPatternScan.error.invalidPositiveInteger": "{option} must be a positive safe integer: {value}",
1126
1145
  "scriptPack.script.skillRouteAudit.summary": "Audit skill routes, template skill copies, manifest profiles, and i18n metadata for drift",
1127
1146
  "scriptPack.script.versionSource.summary": "Inspect repository version sources before release metadata changes",
1128
1147
  "scriptPack.script.approvalGate.summary": "Check planned actions against repository approval policy gates",
1148
+ "scriptPack.script.deploySurface.summary": "Inspect repository deploy and release surfaces with verification gates",
1129
1149
  "scriptPack.script.mergeConflictScan.summary": "Scan repository files for unresolved Git merge conflict markers",
1130
1150
  "scriptPack.script.gitIgnoreAudit.summary": "Audit Git ignore rules and path visibility evidence",
1131
1151
  "scriptPack.script.manifestLockDrift.summary": "Check manifest-lock file hashes against current repository files",
@@ -1165,6 +1185,19 @@ Read these files before working:
1165
1185
  "approvalGate.error.missingAction": "Specify an approval-gate action: check",
1166
1186
  "approvalGate.error.unknownAction": "Unknown approval-gate action: {action}",
1167
1187
  "approvalGate.error.missingActionType": "Specify at least one --action <type>",
1188
+ "deploySurface.help.summary": "Inspect workflows, package metadata, package scripts, and deploy config files for local deploy or release surfaces.",
1189
+ "deploySurface.help.exit.ok": "The deploy-surface report was generated",
1190
+ "deploySurface.help.exit.fail": "The deploy-surface report found unreadable metadata or invalid input",
1191
+ "deploySurface.title": "mustflow deploy surface",
1192
+ "deploySurface.label.deploySurface": "Deploy surface detected",
1193
+ "deploySurface.label.surfaces": "Surfaces",
1194
+ "deploySurface.label.surfaceDetails": "Surface details",
1195
+ "deploySurface.label.requiredVerification": "Required verification",
1196
+ "deploySurface.label.manualGates": "Manual gates",
1197
+ "deploySurface.label.issues": "Issues",
1198
+ "deploySurface.noSurfaces": "No local deploy or release surface was detected.",
1199
+ "deploySurface.error.missingAction": "Specify a deploy-surface action: inspect",
1200
+ "deploySurface.error.unknownAction": "Unknown deploy-surface action: {action}",
1168
1201
  "mergeConflictScan.help.summary": "Scan changed files or explicit paths for unresolved Git merge conflict markers without printing file content.",
1169
1202
  "mergeConflictScan.help.option.maxFiles": "Maximum files to scan. Default: 1000",
1170
1203
  "mergeConflictScan.help.option.maxFileBytes": "Maximum bytes to read from each file. Default: 524288",
@@ -1238,6 +1271,8 @@ Read these files before working:
1238
1271
  "run.error.blockedShellBackgroundDetail": "Shell commands must not spawn background work.",
1239
1272
  "run.error.blockedLongRunningCommand": 'Intent "{intent}" is blocked. {detail}',
1240
1273
  "run.error.blockedLongRunningCommandDetail": "Command argv must describe a finite one-shot command, not a development server, watcher, shell wrapper, interpreter loop, or background process.",
1274
+ "run.error.approvalRequired": 'Intent "{intent}" requires approval. {detail}',
1275
+ "run.error.approvalRequiredDetail": "The repository approval policy requires explicit approval before this command intent can run.",
1241
1276
  "run.error.cwdOutsideProject": 'Command "{intent}" has an invalid cwd: {detail}',
1242
1277
  "run.error.cwdOutsideProjectDetail": "Intent cwd must stay inside the current root.",
1243
1278
  "run.error.invalidTestTarget": 'Command "{intent}" received an invalid test target. {detail}',
@@ -1123,9 +1123,29 @@ Lee estos archivos antes de trabajar:
1123
1123
  "secretRiskScan.error.missingAction": "Specify a secret-risk-scan action: scan",
1124
1124
  "secretRiskScan.error.unknownAction": "Unknown secret-risk-scan action: {action}",
1125
1125
  "secretRiskScan.error.invalidPositiveInteger": "{option} must be a positive safe integer: {value}",
1126
+ "scriptPack.script.securityPatternScan.summary": "Scan repository files for high-signal security code patterns",
1127
+ "securityPatternScan.help.summary": "Scan source, CI, and config files for security code-pattern leads without printing matched source lines or secret values.",
1128
+ "securityPatternScan.help.option.maxFiles": "Maximum number of files to inspect. Default: 1000",
1129
+ "securityPatternScan.help.option.maxFileBytes": "Maximum bytes to read from each inspected file. Default: 262144",
1130
+ "securityPatternScan.help.option.maxFindings": "Maximum number of findings to report. Default: 300",
1131
+ "securityPatternScan.help.exit.ok": "The security-pattern scan completed without blocking findings",
1132
+ "securityPatternScan.help.exit.fail": "The security-pattern scan found invalid input, unreadable files, or security-pattern findings",
1133
+ "securityPatternScan.title": "mustflow security pattern scan",
1134
+ "securityPatternScan.label.files": "Files",
1135
+ "securityPatternScan.label.findings": "Findings",
1136
+ "securityPatternScan.label.categories": "Categories",
1137
+ "securityPatternScan.label.highOrCritical": "High or critical",
1138
+ "securityPatternScan.label.truncated": "Truncated",
1139
+ "securityPatternScan.label.reviewFocus": "Review focus",
1140
+ "securityPatternScan.label.issues": "Issues",
1141
+ "securityPatternScan.clean": "No high-signal security code patterns were found.",
1142
+ "securityPatternScan.error.missingAction": "Specify a security-pattern-scan action: scan",
1143
+ "securityPatternScan.error.unknownAction": "Unknown security-pattern-scan action: {action}",
1144
+ "securityPatternScan.error.invalidPositiveInteger": "{option} must be a positive safe integer: {value}",
1126
1145
  "scriptPack.script.skillRouteAudit.summary": "Audit skill routes, template skill copies, manifest profiles, and i18n metadata for drift",
1127
1146
  "scriptPack.script.versionSource.summary": "Inspect repository version sources before release metadata changes",
1128
1147
  "scriptPack.script.approvalGate.summary": "Check planned actions against repository approval policy gates",
1148
+ "scriptPack.script.deploySurface.summary": "Inspect repository deploy and release surfaces with verification gates",
1129
1149
  "scriptPack.script.mergeConflictScan.summary": "Scan repository files for unresolved Git merge conflict markers",
1130
1150
  "scriptPack.script.gitIgnoreAudit.summary": "Audit Git ignore rules and path visibility evidence",
1131
1151
  "scriptPack.script.manifestLockDrift.summary": "Check manifest-lock file hashes against current repository files",
@@ -1165,6 +1185,19 @@ Lee estos archivos antes de trabajar:
1165
1185
  "approvalGate.error.missingAction": "Specify an approval-gate action: check",
1166
1186
  "approvalGate.error.unknownAction": "Unknown approval-gate action: {action}",
1167
1187
  "approvalGate.error.missingActionType": "Specify at least one --action <type>",
1188
+ "deploySurface.help.summary": "Inspect workflows, package metadata, package scripts, and deploy config files for local deploy or release surfaces.",
1189
+ "deploySurface.help.exit.ok": "The deploy-surface report was generated",
1190
+ "deploySurface.help.exit.fail": "The deploy-surface report found unreadable metadata or invalid input",
1191
+ "deploySurface.title": "mustflow deploy surface",
1192
+ "deploySurface.label.deploySurface": "Deploy surface detected",
1193
+ "deploySurface.label.surfaces": "Surfaces",
1194
+ "deploySurface.label.surfaceDetails": "Surface details",
1195
+ "deploySurface.label.requiredVerification": "Required verification",
1196
+ "deploySurface.label.manualGates": "Manual gates",
1197
+ "deploySurface.label.issues": "Issues",
1198
+ "deploySurface.noSurfaces": "No local deploy or release surface was detected.",
1199
+ "deploySurface.error.missingAction": "Specify a deploy-surface action: inspect",
1200
+ "deploySurface.error.unknownAction": "Unknown deploy-surface action: {action}",
1168
1201
  "mergeConflictScan.help.summary": "Scan changed files or explicit paths for unresolved Git merge conflict markers without printing file content.",
1169
1202
  "mergeConflictScan.help.option.maxFiles": "Maximum files to scan. Default: 1000",
1170
1203
  "mergeConflictScan.help.option.maxFileBytes": "Maximum bytes to read from each file. Default: 524288",
@@ -1238,6 +1271,8 @@ Lee estos archivos antes de trabajar:
1238
1271
  "run.error.blockedShellBackgroundDetail": "Los comandos de shell no deben iniciar trabajo en segundo plano.",
1239
1272
  "run.error.blockedLongRunningCommand": 'La intención "{intent}" está bloqueada. {detail}',
1240
1273
  "run.error.blockedLongRunningCommandDetail": "argv debe describir un comando finito de una sola ejecución, no un servidor de desarrollo, watcher, envoltorio de shell, bucle de intérprete o proceso en segundo plano.",
1274
+ "run.error.approvalRequired": 'La intención "{intent}" requiere aprobación. {detail}',
1275
+ "run.error.approvalRequiredDetail": "La política de aprobación del repositorio requiere aprobación explícita antes de ejecutar esta intención de comando.",
1241
1276
  "run.error.cwdOutsideProject": 'El comando "{intent}" tiene un cwd no válido: {detail}',
1242
1277
  "run.error.cwdOutsideProjectDetail": "El cwd de la intención debe permanecer dentro de la raíz actual.",
1243
1278
  "run.error.invalidTestTarget": 'El comando "{intent}" recibió un objetivo de prueba no válido. {detail}',
@@ -1123,9 +1123,29 @@ Lisez ces fichiers avant de travailler :
1123
1123
  "secretRiskScan.error.missingAction": "Specify a secret-risk-scan action: scan",
1124
1124
  "secretRiskScan.error.unknownAction": "Unknown secret-risk-scan action: {action}",
1125
1125
  "secretRiskScan.error.invalidPositiveInteger": "{option} must be a positive safe integer: {value}",
1126
+ "scriptPack.script.securityPatternScan.summary": "Scan repository files for high-signal security code patterns",
1127
+ "securityPatternScan.help.summary": "Scan source, CI, and config files for security code-pattern leads without printing matched source lines or secret values.",
1128
+ "securityPatternScan.help.option.maxFiles": "Maximum number of files to inspect. Default: 1000",
1129
+ "securityPatternScan.help.option.maxFileBytes": "Maximum bytes to read from each inspected file. Default: 262144",
1130
+ "securityPatternScan.help.option.maxFindings": "Maximum number of findings to report. Default: 300",
1131
+ "securityPatternScan.help.exit.ok": "The security-pattern scan completed without blocking findings",
1132
+ "securityPatternScan.help.exit.fail": "The security-pattern scan found invalid input, unreadable files, or security-pattern findings",
1133
+ "securityPatternScan.title": "mustflow security pattern scan",
1134
+ "securityPatternScan.label.files": "Files",
1135
+ "securityPatternScan.label.findings": "Findings",
1136
+ "securityPatternScan.label.categories": "Categories",
1137
+ "securityPatternScan.label.highOrCritical": "High or critical",
1138
+ "securityPatternScan.label.truncated": "Truncated",
1139
+ "securityPatternScan.label.reviewFocus": "Review focus",
1140
+ "securityPatternScan.label.issues": "Issues",
1141
+ "securityPatternScan.clean": "No high-signal security code patterns were found.",
1142
+ "securityPatternScan.error.missingAction": "Specify a security-pattern-scan action: scan",
1143
+ "securityPatternScan.error.unknownAction": "Unknown security-pattern-scan action: {action}",
1144
+ "securityPatternScan.error.invalidPositiveInteger": "{option} must be a positive safe integer: {value}",
1126
1145
  "scriptPack.script.skillRouteAudit.summary": "Audit skill routes, template skill copies, manifest profiles, and i18n metadata for drift",
1127
1146
  "scriptPack.script.versionSource.summary": "Inspect repository version sources before release metadata changes",
1128
1147
  "scriptPack.script.approvalGate.summary": "Check planned actions against repository approval policy gates",
1148
+ "scriptPack.script.deploySurface.summary": "Inspect repository deploy and release surfaces with verification gates",
1129
1149
  "scriptPack.script.mergeConflictScan.summary": "Scan repository files for unresolved Git merge conflict markers",
1130
1150
  "scriptPack.script.gitIgnoreAudit.summary": "Audit Git ignore rules and path visibility evidence",
1131
1151
  "scriptPack.script.manifestLockDrift.summary": "Check manifest-lock file hashes against current repository files",
@@ -1165,6 +1185,19 @@ Lisez ces fichiers avant de travailler :
1165
1185
  "approvalGate.error.missingAction": "Specify an approval-gate action: check",
1166
1186
  "approvalGate.error.unknownAction": "Unknown approval-gate action: {action}",
1167
1187
  "approvalGate.error.missingActionType": "Specify at least one --action <type>",
1188
+ "deploySurface.help.summary": "Inspect workflows, package metadata, package scripts, and deploy config files for local deploy or release surfaces.",
1189
+ "deploySurface.help.exit.ok": "The deploy-surface report was generated",
1190
+ "deploySurface.help.exit.fail": "The deploy-surface report found unreadable metadata or invalid input",
1191
+ "deploySurface.title": "mustflow deploy surface",
1192
+ "deploySurface.label.deploySurface": "Deploy surface detected",
1193
+ "deploySurface.label.surfaces": "Surfaces",
1194
+ "deploySurface.label.surfaceDetails": "Surface details",
1195
+ "deploySurface.label.requiredVerification": "Required verification",
1196
+ "deploySurface.label.manualGates": "Manual gates",
1197
+ "deploySurface.label.issues": "Issues",
1198
+ "deploySurface.noSurfaces": "No local deploy or release surface was detected.",
1199
+ "deploySurface.error.missingAction": "Specify a deploy-surface action: inspect",
1200
+ "deploySurface.error.unknownAction": "Unknown deploy-surface action: {action}",
1168
1201
  "mergeConflictScan.help.summary": "Scan changed files or explicit paths for unresolved Git merge conflict markers without printing file content.",
1169
1202
  "mergeConflictScan.help.option.maxFiles": "Maximum files to scan. Default: 1000",
1170
1203
  "mergeConflictScan.help.option.maxFileBytes": "Maximum bytes to read from each file. Default: 524288",
@@ -1238,6 +1271,8 @@ Lisez ces fichiers avant de travailler :
1238
1271
  "run.error.blockedShellBackgroundDetail": "Les commandes shell ne doivent pas lancer de travail en arrière-plan.",
1239
1272
  "run.error.blockedLongRunningCommand": 'L’intention "{intent}" est bloquée. {detail}',
1240
1273
  "run.error.blockedLongRunningCommandDetail": "argv doit décrire une commande ponctuelle finie, pas un serveur de développement, un watcher, un wrapper shell, une boucle d'interpréteur ou un processus en arrière-plan.",
1274
+ "run.error.approvalRequired": 'L’intention "{intent}" nécessite une approbation. {detail}',
1275
+ "run.error.approvalRequiredDetail": "La politique d’approbation du dépôt exige une approbation explicite avant d’exécuter cette intention de commande.",
1241
1276
  "run.error.cwdOutsideProject": 'La commande "{intent}" a un cwd non valide : {detail}',
1242
1277
  "run.error.cwdOutsideProjectDetail": "Le cwd de l’intention doit rester dans la racine actuelle.",
1243
1278
  "run.error.invalidTestTarget": 'La commande "{intent}" a reçu une cible de test invalide. {detail}',
@@ -1123,9 +1123,29 @@ export const hiMessages = {
1123
1123
  "secretRiskScan.error.missingAction": "Specify a secret-risk-scan action: scan",
1124
1124
  "secretRiskScan.error.unknownAction": "Unknown secret-risk-scan action: {action}",
1125
1125
  "secretRiskScan.error.invalidPositiveInteger": "{option} must be a positive safe integer: {value}",
1126
+ "scriptPack.script.securityPatternScan.summary": "Scan repository files for high-signal security code patterns",
1127
+ "securityPatternScan.help.summary": "Scan source, CI, and config files for security code-pattern leads without printing matched source lines or secret values.",
1128
+ "securityPatternScan.help.option.maxFiles": "Maximum number of files to inspect. Default: 1000",
1129
+ "securityPatternScan.help.option.maxFileBytes": "Maximum bytes to read from each inspected file. Default: 262144",
1130
+ "securityPatternScan.help.option.maxFindings": "Maximum number of findings to report. Default: 300",
1131
+ "securityPatternScan.help.exit.ok": "The security-pattern scan completed without blocking findings",
1132
+ "securityPatternScan.help.exit.fail": "The security-pattern scan found invalid input, unreadable files, or security-pattern findings",
1133
+ "securityPatternScan.title": "mustflow security pattern scan",
1134
+ "securityPatternScan.label.files": "Files",
1135
+ "securityPatternScan.label.findings": "Findings",
1136
+ "securityPatternScan.label.categories": "Categories",
1137
+ "securityPatternScan.label.highOrCritical": "High or critical",
1138
+ "securityPatternScan.label.truncated": "Truncated",
1139
+ "securityPatternScan.label.reviewFocus": "Review focus",
1140
+ "securityPatternScan.label.issues": "Issues",
1141
+ "securityPatternScan.clean": "No high-signal security code patterns were found.",
1142
+ "securityPatternScan.error.missingAction": "Specify a security-pattern-scan action: scan",
1143
+ "securityPatternScan.error.unknownAction": "Unknown security-pattern-scan action: {action}",
1144
+ "securityPatternScan.error.invalidPositiveInteger": "{option} must be a positive safe integer: {value}",
1126
1145
  "scriptPack.script.skillRouteAudit.summary": "Audit skill routes, template skill copies, manifest profiles, and i18n metadata for drift",
1127
1146
  "scriptPack.script.versionSource.summary": "Inspect repository version sources before release metadata changes",
1128
1147
  "scriptPack.script.approvalGate.summary": "Check planned actions against repository approval policy gates",
1148
+ "scriptPack.script.deploySurface.summary": "Inspect repository deploy and release surfaces with verification gates",
1129
1149
  "scriptPack.script.mergeConflictScan.summary": "Scan repository files for unresolved Git merge conflict markers",
1130
1150
  "scriptPack.script.gitIgnoreAudit.summary": "Audit Git ignore rules and path visibility evidence",
1131
1151
  "scriptPack.script.manifestLockDrift.summary": "Check manifest-lock file hashes against current repository files",
@@ -1165,6 +1185,19 @@ export const hiMessages = {
1165
1185
  "approvalGate.error.missingAction": "Specify an approval-gate action: check",
1166
1186
  "approvalGate.error.unknownAction": "Unknown approval-gate action: {action}",
1167
1187
  "approvalGate.error.missingActionType": "Specify at least one --action <type>",
1188
+ "deploySurface.help.summary": "Inspect workflows, package metadata, package scripts, and deploy config files for local deploy or release surfaces.",
1189
+ "deploySurface.help.exit.ok": "The deploy-surface report was generated",
1190
+ "deploySurface.help.exit.fail": "The deploy-surface report found unreadable metadata or invalid input",
1191
+ "deploySurface.title": "mustflow deploy surface",
1192
+ "deploySurface.label.deploySurface": "Deploy surface detected",
1193
+ "deploySurface.label.surfaces": "Surfaces",
1194
+ "deploySurface.label.surfaceDetails": "Surface details",
1195
+ "deploySurface.label.requiredVerification": "Required verification",
1196
+ "deploySurface.label.manualGates": "Manual gates",
1197
+ "deploySurface.label.issues": "Issues",
1198
+ "deploySurface.noSurfaces": "No local deploy or release surface was detected.",
1199
+ "deploySurface.error.missingAction": "Specify a deploy-surface action: inspect",
1200
+ "deploySurface.error.unknownAction": "Unknown deploy-surface action: {action}",
1168
1201
  "mergeConflictScan.help.summary": "Scan changed files or explicit paths for unresolved Git merge conflict markers without printing file content.",
1169
1202
  "mergeConflictScan.help.option.maxFiles": "Maximum files to scan. Default: 1000",
1170
1203
  "mergeConflictScan.help.option.maxFileBytes": "Maximum bytes to read from each file. Default: 524288",
@@ -1238,6 +1271,8 @@ export const hiMessages = {
1238
1271
  "run.error.blockedShellBackgroundDetail": "Shell commands background work शुरू नहीं कर सकतीं।",
1239
1272
  "run.error.blockedLongRunningCommand": 'इंटेंट "{intent}" अवरुद्ध है। {detail}',
1240
1273
  "run.error.blockedLongRunningCommandDetail": "argv में finite one-shot command होना चाहिए, development server, watcher, shell wrapper, interpreter loop, या background process नहीं।",
1274
+ "run.error.approvalRequired": 'इंटेंट "{intent}" को approval चाहिए। {detail}',
1275
+ "run.error.approvalRequiredDetail": "Repository approval policy के अनुसार इस command intent को चलाने से पहले explicit approval चाहिए।",
1241
1276
  "run.error.cwdOutsideProject": 'कमांड "{intent}" का cwd अमान्य है: {detail}',
1242
1277
  "run.error.cwdOutsideProjectDetail": "Intent cwd current root के अंदर रहना चाहिए।",
1243
1278
  "run.error.invalidTestTarget": 'कमांड "{intent}" को अमान्य test target मिला। {detail}',
@@ -1123,9 +1123,29 @@ export const koMessages = {
1123
1123
  "secretRiskScan.error.missingAction": "Specify a secret-risk-scan action: scan",
1124
1124
  "secretRiskScan.error.unknownAction": "Unknown secret-risk-scan action: {action}",
1125
1125
  "secretRiskScan.error.invalidPositiveInteger": "{option} must be a positive safe integer: {value}",
1126
+ "scriptPack.script.securityPatternScan.summary": "Scan repository files for high-signal security code patterns",
1127
+ "securityPatternScan.help.summary": "Scan source, CI, and config files for security code-pattern leads without printing matched source lines or secret values.",
1128
+ "securityPatternScan.help.option.maxFiles": "Maximum number of files to inspect. Default: 1000",
1129
+ "securityPatternScan.help.option.maxFileBytes": "Maximum bytes to read from each inspected file. Default: 262144",
1130
+ "securityPatternScan.help.option.maxFindings": "Maximum number of findings to report. Default: 300",
1131
+ "securityPatternScan.help.exit.ok": "The security-pattern scan completed without blocking findings",
1132
+ "securityPatternScan.help.exit.fail": "The security-pattern scan found invalid input, unreadable files, or security-pattern findings",
1133
+ "securityPatternScan.title": "mustflow security pattern scan",
1134
+ "securityPatternScan.label.files": "Files",
1135
+ "securityPatternScan.label.findings": "Findings",
1136
+ "securityPatternScan.label.categories": "Categories",
1137
+ "securityPatternScan.label.highOrCritical": "High or critical",
1138
+ "securityPatternScan.label.truncated": "Truncated",
1139
+ "securityPatternScan.label.reviewFocus": "Review focus",
1140
+ "securityPatternScan.label.issues": "Issues",
1141
+ "securityPatternScan.clean": "No high-signal security code patterns were found.",
1142
+ "securityPatternScan.error.missingAction": "Specify a security-pattern-scan action: scan",
1143
+ "securityPatternScan.error.unknownAction": "Unknown security-pattern-scan action: {action}",
1144
+ "securityPatternScan.error.invalidPositiveInteger": "{option} must be a positive safe integer: {value}",
1126
1145
  "scriptPack.script.skillRouteAudit.summary": "Audit skill routes, template skill copies, manifest profiles, and i18n metadata for drift",
1127
1146
  "scriptPack.script.versionSource.summary": "Inspect repository version sources before release metadata changes",
1128
1147
  "scriptPack.script.approvalGate.summary": "Check planned actions against repository approval policy gates",
1148
+ "scriptPack.script.deploySurface.summary": "Inspect repository deploy and release surfaces with verification gates",
1129
1149
  "scriptPack.script.mergeConflictScan.summary": "Scan repository files for unresolved Git merge conflict markers",
1130
1150
  "scriptPack.script.gitIgnoreAudit.summary": "Audit Git ignore rules and path visibility evidence",
1131
1151
  "scriptPack.script.manifestLockDrift.summary": "Check manifest-lock file hashes against current repository files",
@@ -1165,6 +1185,19 @@ export const koMessages = {
1165
1185
  "approvalGate.error.missingAction": "Specify an approval-gate action: check",
1166
1186
  "approvalGate.error.unknownAction": "Unknown approval-gate action: {action}",
1167
1187
  "approvalGate.error.missingActionType": "Specify at least one --action <type>",
1188
+ "deploySurface.help.summary": "Inspect workflows, package metadata, package scripts, and deploy config files for local deploy or release surfaces.",
1189
+ "deploySurface.help.exit.ok": "The deploy-surface report was generated",
1190
+ "deploySurface.help.exit.fail": "The deploy-surface report found unreadable metadata or invalid input",
1191
+ "deploySurface.title": "mustflow deploy surface",
1192
+ "deploySurface.label.deploySurface": "Deploy surface detected",
1193
+ "deploySurface.label.surfaces": "Surfaces",
1194
+ "deploySurface.label.surfaceDetails": "Surface details",
1195
+ "deploySurface.label.requiredVerification": "Required verification",
1196
+ "deploySurface.label.manualGates": "Manual gates",
1197
+ "deploySurface.label.issues": "Issues",
1198
+ "deploySurface.noSurfaces": "No local deploy or release surface was detected.",
1199
+ "deploySurface.error.missingAction": "Specify a deploy-surface action: inspect",
1200
+ "deploySurface.error.unknownAction": "Unknown deploy-surface action: {action}",
1168
1201
  "mergeConflictScan.help.summary": "Scan changed files or explicit paths for unresolved Git merge conflict markers without printing file content.",
1169
1202
  "mergeConflictScan.help.option.maxFiles": "Maximum files to scan. Default: 1000",
1170
1203
  "mergeConflictScan.help.option.maxFileBytes": "Maximum bytes to read from each file. Default: 524288",
@@ -1238,6 +1271,8 @@ export const koMessages = {
1238
1271
  "run.error.blockedShellBackgroundDetail": "셸 명령은 백그라운드 작업을 시작하면 안 됩니다.",
1239
1272
  "run.error.blockedLongRunningCommand": '명령 의도 "{intent}"가 차단되었습니다. {detail}',
1240
1273
  "run.error.blockedLongRunningCommandDetail": "argv는 개발 서버, 감시 명령, 셸 래퍼, 인터프리터 반복 작업, 백그라운드 프로세스가 아니라 끝나는 단발성 명령이어야 합니다.",
1274
+ "run.error.approvalRequired": '명령 의도 "{intent}"는 승인이 필요합니다. {detail}',
1275
+ "run.error.approvalRequiredDetail": "저장소 승인 정책상 이 명령 의도를 실행하기 전에 명시적 승인이 필요합니다.",
1241
1276
  "run.error.cwdOutsideProject": '명령 "{intent}"의 실행 위치(cwd)가 올바르지 않습니다: {detail}',
1242
1277
  "run.error.cwdOutsideProjectDetail": "명령 실행 위치(cwd)는 현재 루트 안에 있어야 합니다.",
1243
1278
  "run.error.invalidTestTarget": '명령 "{intent}"에 올바르지 않은 테스트 대상이 전달되었습니다. {detail}',
@@ -1123,9 +1123,29 @@ export const zhMessages = {
1123
1123
  "secretRiskScan.error.missingAction": "Specify a secret-risk-scan action: scan",
1124
1124
  "secretRiskScan.error.unknownAction": "Unknown secret-risk-scan action: {action}",
1125
1125
  "secretRiskScan.error.invalidPositiveInteger": "{option} must be a positive safe integer: {value}",
1126
+ "scriptPack.script.securityPatternScan.summary": "Scan repository files for high-signal security code patterns",
1127
+ "securityPatternScan.help.summary": "Scan source, CI, and config files for security code-pattern leads without printing matched source lines or secret values.",
1128
+ "securityPatternScan.help.option.maxFiles": "Maximum number of files to inspect. Default: 1000",
1129
+ "securityPatternScan.help.option.maxFileBytes": "Maximum bytes to read from each inspected file. Default: 262144",
1130
+ "securityPatternScan.help.option.maxFindings": "Maximum number of findings to report. Default: 300",
1131
+ "securityPatternScan.help.exit.ok": "The security-pattern scan completed without blocking findings",
1132
+ "securityPatternScan.help.exit.fail": "The security-pattern scan found invalid input, unreadable files, or security-pattern findings",
1133
+ "securityPatternScan.title": "mustflow security pattern scan",
1134
+ "securityPatternScan.label.files": "Files",
1135
+ "securityPatternScan.label.findings": "Findings",
1136
+ "securityPatternScan.label.categories": "Categories",
1137
+ "securityPatternScan.label.highOrCritical": "High or critical",
1138
+ "securityPatternScan.label.truncated": "Truncated",
1139
+ "securityPatternScan.label.reviewFocus": "Review focus",
1140
+ "securityPatternScan.label.issues": "Issues",
1141
+ "securityPatternScan.clean": "No high-signal security code patterns were found.",
1142
+ "securityPatternScan.error.missingAction": "Specify a security-pattern-scan action: scan",
1143
+ "securityPatternScan.error.unknownAction": "Unknown security-pattern-scan action: {action}",
1144
+ "securityPatternScan.error.invalidPositiveInteger": "{option} must be a positive safe integer: {value}",
1126
1145
  "scriptPack.script.skillRouteAudit.summary": "Audit skill routes, template skill copies, manifest profiles, and i18n metadata for drift",
1127
1146
  "scriptPack.script.versionSource.summary": "Inspect repository version sources before release metadata changes",
1128
1147
  "scriptPack.script.approvalGate.summary": "Check planned actions against repository approval policy gates",
1148
+ "scriptPack.script.deploySurface.summary": "Inspect repository deploy and release surfaces with verification gates",
1129
1149
  "scriptPack.script.mergeConflictScan.summary": "Scan repository files for unresolved Git merge conflict markers",
1130
1150
  "scriptPack.script.gitIgnoreAudit.summary": "Audit Git ignore rules and path visibility evidence",
1131
1151
  "scriptPack.script.manifestLockDrift.summary": "Check manifest-lock file hashes against current repository files",
@@ -1165,6 +1185,19 @@ export const zhMessages = {
1165
1185
  "approvalGate.error.missingAction": "Specify an approval-gate action: check",
1166
1186
  "approvalGate.error.unknownAction": "Unknown approval-gate action: {action}",
1167
1187
  "approvalGate.error.missingActionType": "Specify at least one --action <type>",
1188
+ "deploySurface.help.summary": "Inspect workflows, package metadata, package scripts, and deploy config files for local deploy or release surfaces.",
1189
+ "deploySurface.help.exit.ok": "The deploy-surface report was generated",
1190
+ "deploySurface.help.exit.fail": "The deploy-surface report found unreadable metadata or invalid input",
1191
+ "deploySurface.title": "mustflow deploy surface",
1192
+ "deploySurface.label.deploySurface": "Deploy surface detected",
1193
+ "deploySurface.label.surfaces": "Surfaces",
1194
+ "deploySurface.label.surfaceDetails": "Surface details",
1195
+ "deploySurface.label.requiredVerification": "Required verification",
1196
+ "deploySurface.label.manualGates": "Manual gates",
1197
+ "deploySurface.label.issues": "Issues",
1198
+ "deploySurface.noSurfaces": "No local deploy or release surface was detected.",
1199
+ "deploySurface.error.missingAction": "Specify a deploy-surface action: inspect",
1200
+ "deploySurface.error.unknownAction": "Unknown deploy-surface action: {action}",
1168
1201
  "mergeConflictScan.help.summary": "Scan changed files or explicit paths for unresolved Git merge conflict markers without printing file content.",
1169
1202
  "mergeConflictScan.help.option.maxFiles": "Maximum files to scan. Default: 1000",
1170
1203
  "mergeConflictScan.help.option.maxFileBytes": "Maximum bytes to read from each file. Default: 524288",
@@ -1238,6 +1271,8 @@ export const zhMessages = {
1238
1271
  "run.error.blockedShellBackgroundDetail": "Shell 命令不得启动后台工作。",
1239
1272
  "run.error.blockedLongRunningCommand": '意图 "{intent}" 已被阻止。{detail}',
1240
1273
  "run.error.blockedLongRunningCommandDetail": "argv 必须描述会结束的单次命令,而不是开发服务器、监听命令、shell 包装器、解释器循环或后台进程。",
1274
+ "run.error.approvalRequired": '意图 "{intent}" 需要审批。{detail}',
1275
+ "run.error.approvalRequiredDetail": "仓库审批策略要求在运行此命令意图前获得明确审批。",
1241
1276
  "run.error.cwdOutsideProject": '命令 "{intent}" 的 cwd 无效:{detail}',
1242
1277
  "run.error.cwdOutsideProjectDetail": "意图 cwd 必须位于当前根目录内。",
1243
1278
  "run.error.invalidTestTarget": '命令 "{intent}" 收到无效测试目标。{detail}',
@@ -1,4 +1,4 @@
1
- import { existsSync } from 'node:fs';
1
+ import { existsSync, renameSync, rmSync } from 'node:fs';
2
2
  import { createHash } from 'node:crypto';
3
3
  import path from 'node:path';
4
4
  import { ensureFileTargetInsideWithoutSymlinks, writeJsonFileInsideWithoutSymlinks, writeUtf8FileInsideWithoutSymlinks, } from '../../core/safe-filesystem.js';
@@ -185,6 +185,52 @@ function validateImportRelativeFilePath(relativePath) {
185
185
  function normalizeTextContent(content) {
186
186
  return content.replace(/\r\n?/gu, '\n');
187
187
  }
188
+ function readFrontmatterParts(content) {
189
+ if (!content.startsWith('---')) {
190
+ return {
191
+ name: null,
192
+ description: null,
193
+ body: content,
194
+ };
195
+ }
196
+ const firstLineEnd = content.indexOf('\n');
197
+ const end = firstLineEnd >= 0 ? content.indexOf('\n---', firstLineEnd + 1) : -1;
198
+ if (firstLineEnd < 0 || end < 0) {
199
+ return {
200
+ name: null,
201
+ description: null,
202
+ body: content,
203
+ };
204
+ }
205
+ const frontmatter = content.slice(firstLineEnd + 1, end).split(/\r?\n/u);
206
+ const bodyStart = content.indexOf('\n', end + 1);
207
+ return {
208
+ name: readFrontmatterScalar(content, 'name'),
209
+ description: readFrontmatterScalar(content, 'description'),
210
+ body: bodyStart >= 0 ? content.slice(bodyStart + 1) : '',
211
+ };
212
+ }
213
+ function renderYamlScalar(key, value) {
214
+ return `${key}: ${JSON.stringify(value)}`;
215
+ }
216
+ function sanitizeExternalSkillMarkdown(content) {
217
+ const frontmatter = readFrontmatterParts(content);
218
+ const lines = [
219
+ '---',
220
+ ...(frontmatter.name ? [renderYamlScalar('name', frontmatter.name)] : []),
221
+ ...(frontmatter.description ? [renderYamlScalar('description', frontmatter.description)] : []),
222
+ 'external_authority: untrusted',
223
+ '---',
224
+ frontmatter.body,
225
+ ];
226
+ return lines.join('\n');
227
+ }
228
+ function normalizeImportedSkillFiles(files) {
229
+ return files.map((file) => ({
230
+ ...file,
231
+ content: file.relativePath === 'SKILL.md' ? sanitizeExternalSkillMarkdown(file.content) : file.content,
232
+ }));
233
+ }
188
234
  function hashContent(content) {
189
235
  return `sha256:${createHash('sha256').update(content).digest('hex')}`;
190
236
  }
@@ -314,21 +360,38 @@ function createTarget(skillName) {
314
360
  };
315
361
  }
316
362
  function writeImportedSkillFiles(projectRoot, target, source, files, fileReport, warnings) {
317
- const skillPath = path.join(projectRoot, ...target.skill_dir.split('/'), 'SKILL.md');
318
- if (existsSync(skillPath)) {
363
+ const targetPath = path.join(projectRoot, ...target.skill_dir.split('/'));
364
+ const skillPath = path.join(targetPath, 'SKILL.md');
365
+ if (existsSync(targetPath)) {
319
366
  throw new Error(`External skill already exists: ${target.skill_dir}`);
320
367
  }
321
368
  ensureFileTargetInsideWithoutSymlinks(projectRoot, skillPath, { allowMissingLeaf: true });
322
- for (const file of files) {
323
- writeUtf8FileInsideWithoutSymlinks(projectRoot, path.join(projectRoot, ...target.skill_dir.split('/'), ...file.relativePath.split('/')), file.content);
369
+ const tempSkillDir = `${EXTERNAL_SKILL_ROOT}/.${target.skill_name}.tmp-${process.pid}-${Date.now()}`;
370
+ const tempTarget = {
371
+ ...target,
372
+ skill_dir: tempSkillDir,
373
+ provenance_path: `${tempSkillDir}/${PROVENANCE_FILE}`,
374
+ };
375
+ const tempPath = path.join(projectRoot, ...tempTarget.skill_dir.split('/'));
376
+ const tempSkillPath = path.join(tempPath, 'SKILL.md');
377
+ ensureFileTargetInsideWithoutSymlinks(projectRoot, tempSkillPath, { allowMissingLeaf: true });
378
+ try {
379
+ for (const file of files) {
380
+ writeUtf8FileInsideWithoutSymlinks(projectRoot, path.join(projectRoot, ...tempTarget.skill_dir.split('/'), ...file.relativePath.split('/')), file.content);
381
+ }
382
+ writeJsonFileInsideWithoutSymlinks(projectRoot, path.join(projectRoot, ...tempTarget.provenance_path.split('/')), {
383
+ schema_version: '1',
384
+ kind: 'external_skill_source',
385
+ source,
386
+ files: fileReport,
387
+ warnings,
388
+ });
389
+ renameSync(tempPath, targetPath);
390
+ }
391
+ catch (error) {
392
+ rmSync(tempPath, { recursive: true, force: true });
393
+ throw error;
324
394
  }
325
- writeJsonFileInsideWithoutSymlinks(projectRoot, path.join(projectRoot, ...target.provenance_path.split('/')), {
326
- schema_version: '1',
327
- kind: 'external_skill_source',
328
- source,
329
- files: fileReport,
330
- warnings,
331
- });
332
395
  }
333
396
  function rejectionReport(mode, issue) {
334
397
  return {
@@ -355,10 +418,11 @@ export async function createExternalSkillImportReport(projectRoot, inputUrl, opt
355
418
  if (typeof fetchImpl !== 'function') {
356
419
  throw new Error('This runtime does not provide fetch.');
357
420
  }
358
- const files = await loadExternalSkillFiles(fetchImpl, parsed);
359
- const skillName = targetNameForSkill(files, parsed, options.name);
421
+ const sourceFiles = await loadExternalSkillFiles(fetchImpl, parsed);
422
+ const skillName = targetNameForSkill(sourceFiles, parsed, options.name);
360
423
  const target = createTarget(skillName);
361
424
  const source = externalSkillSourceFromParsed(inputUrl, parsed);
425
+ const files = normalizeImportedSkillFiles(sourceFiles);
362
426
  const reports = fileReports(files);
363
427
  const warnings = [
364
428
  ...(reports.some((file) => file.kind === 'script')
@@ -1,5 +1,6 @@
1
1
  import { createRequire } from 'node:module';
2
- export async function loadSqlJs() {
2
+ let sqlJsPromise = null;
3
+ async function initializeSqlJs() {
3
4
  const require = createRequire(import.meta.url);
4
5
  const wasmPath = require.resolve('sql.js/dist/sql-wasm.wasm');
5
6
  const sqlJsModule = (await import('sql.js'));
@@ -13,3 +14,10 @@ export async function loadSqlJs() {
13
14
  },
14
15
  });
15
16
  }
17
+ export async function loadSqlJs() {
18
+ sqlJsPromise ??= initializeSqlJs().catch((error) => {
19
+ sqlJsPromise = null;
20
+ throw error;
21
+ });
22
+ return sqlJsPromise;
23
+ }
@@ -7,6 +7,7 @@ import { evaluateCommandIntentEligibility, } from '../../core/command-intent-eli
7
7
  import { inspectActiveRunLocks, } from '../../core/active-run-locks.js';
8
8
  import { isRecord, readPositiveInteger, readString, readStringArray, } from '../../core/config-loading.js';
9
9
  import { DEFAULT_COMMAND_MAX_OUTPUT_BYTES, COMMAND_OUTPUT_LIMIT_SCOPE, } from '../../core/command-output-limits.js';
10
+ import { checkRepoApprovalGate } from '../../core/repo-approval-gate.js';
10
11
  import { normalizeSuccessExitCodes } from '../../core/success-exit-codes.js';
11
12
  import { normalizeSafeTestTargetPath, TEST_TARGET_PATH_ERROR } from '../../core/test-target-paths.js';
12
13
  import { evaluateCommandPreconditions, } from '../../core/command-preconditions.js';
@@ -124,6 +125,38 @@ function readRunIntentMetadata(contract, intent) {
124
125
  relatedOneshotChecks: readStringArray(intent, 'related_oneshot_checks') ?? [],
125
126
  };
126
127
  }
128
+ function createApprovalBlock(projectRoot, metadata) {
129
+ const actionTypes = [];
130
+ if (metadata.network === true) {
131
+ actionTypes.push('network_access');
132
+ }
133
+ if (metadata.destructive === true) {
134
+ actionTypes.push('destructive_command');
135
+ }
136
+ if (actionTypes.length === 0) {
137
+ return null;
138
+ }
139
+ const approvalReport = checkRepoApprovalGate(projectRoot, actionTypes);
140
+ if (approvalReport.issues.length > 0) {
141
+ return {
142
+ reasonCode: 'approval_policy_unreadable',
143
+ detail: `Could not evaluate ${approvalReport.input.policy_path}: ${approvalReport.issues.join(' ')}`,
144
+ };
145
+ }
146
+ const requiredActions = approvalReport.decisions
147
+ .filter((decision) => decision.approval_required)
148
+ .map((decision) => decision.action_type);
149
+ if (requiredActions.length === 0) {
150
+ return null;
151
+ }
152
+ const reasonCode = requiredActions.includes('destructive_command')
153
+ ? 'destructive_requires_approval'
154
+ : 'network_requires_approval';
155
+ return {
156
+ reasonCode,
157
+ detail: `Action ${requiredActions.map((action) => JSON.stringify(action)).join(', ')} requires explicit approval before mf run can execute this intent.`,
158
+ };
159
+ }
127
160
  function createBlockedRunPlan(contract, intentName, intent, eligibility, reasonCode, detail, preconditions = []) {
128
161
  const metadata = intent ? readRunIntentMetadata(contract, intent) : null;
129
162
  return {
@@ -177,6 +210,10 @@ export function createRunPlan(projectRoot, contract, intentName, options = {}) {
177
210
  return createBlockedRunPlan(contract, intentName, rawIntent, eligibility, eligibility.code, eligibility.detail, preconditions);
178
211
  }
179
212
  const metadata = readRunIntentMetadata(contract, rawIntent);
213
+ const approvalBlock = createApprovalBlock(projectRoot, metadata);
214
+ if (approvalBlock) {
215
+ return createBlockedRunPlan(contract, intentName, rawIntent, eligibility, approvalBlock.reasonCode, approvalBlock.detail, preconditions);
216
+ }
180
217
  const maxOutputBytesLimitDetail = getCommandMaxOutputBytesLimitDetail(contract, rawIntent);
181
218
  if (maxOutputBytesLimitDetail) {
182
219
  return createBlockedRunPlan(contract, intentName, rawIntent, eligibility, 'max_output_bytes_exceeds_limit', maxOutputBytesLimitDetail, preconditions);