mustflow 2.84.0 → 2.99.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/README.md +11 -2
  2. package/dist/cli/commands/script-pack.js +14 -0
  3. package/dist/cli/i18n/en.js +262 -0
  4. package/dist/cli/i18n/es.js +262 -0
  5. package/dist/cli/i18n/fr.js +262 -0
  6. package/dist/cli/i18n/hi.js +262 -0
  7. package/dist/cli/i18n/ko.js +262 -0
  8. package/dist/cli/i18n/zh.js +262 -0
  9. package/dist/cli/lib/repo-map.js +27 -6
  10. package/dist/cli/lib/run-root-trust.js +15 -1
  11. package/dist/cli/lib/script-pack-registry.js +397 -0
  12. package/dist/cli/lib/validation/index.js +2 -2
  13. package/dist/cli/lib/validation/primitives.js +4 -1
  14. package/dist/cli/script-packs/code-change-impact.js +178 -0
  15. package/dist/cli/script-packs/code-dependency-graph.js +181 -0
  16. package/dist/cli/script-packs/code-import-cycle.js +193 -0
  17. package/dist/cli/script-packs/docs-link-integrity.js +145 -0
  18. package/dist/cli/script-packs/repo-approval-gate.js +100 -0
  19. package/dist/cli/script-packs/repo-env-contract.js +156 -0
  20. package/dist/cli/script-packs/repo-git-ignore-audit.js +119 -0
  21. package/dist/cli/script-packs/repo-manifest-lock-drift.js +122 -0
  22. package/dist/cli/script-packs/repo-merge-conflict-scan.js +123 -0
  23. package/dist/cli/script-packs/repo-secret-risk-scan.js +147 -0
  24. package/dist/cli/script-packs/repo-skill-route-audit.js +86 -0
  25. package/dist/cli/script-packs/repo-version-source.js +92 -0
  26. package/dist/cli/script-packs/test-performance-report.js +247 -0
  27. package/dist/cli/script-packs/test-regression-selector.js +167 -0
  28. package/dist/core/change-impact.js +355 -0
  29. package/dist/core/change-surface-classification.js +198 -0
  30. package/dist/core/change-verification.js +32 -5
  31. package/dist/core/config-loading.js +121 -4
  32. package/dist/core/dependency-graph.js +490 -0
  33. package/dist/core/docs-link-integrity.js +443 -0
  34. package/dist/core/env-contract.js +450 -0
  35. package/dist/core/import-cycle.js +152 -0
  36. package/dist/core/line-endings.js +26 -13
  37. package/dist/core/public-json-contracts.js +167 -0
  38. package/dist/core/repo-approval-gate.js +116 -0
  39. package/dist/core/repo-git-ignore-audit.js +302 -0
  40. package/dist/core/repo-manifest-lock-drift.js +321 -0
  41. package/dist/core/repo-merge-conflict-scan.js +335 -0
  42. package/dist/core/repo-version-source.js +82 -0
  43. package/dist/core/route-outline.js +57 -5
  44. package/dist/core/script-pack-suggestions.js +97 -1
  45. package/dist/core/secret-risk-scan.js +440 -0
  46. package/dist/core/skill-route-audit.js +354 -0
  47. package/dist/core/test-performance-report.js +697 -0
  48. package/dist/core/test-regression-selector.js +335 -0
  49. package/package.json +1 -1
  50. package/schemas/README.md +54 -0
  51. package/schemas/change-impact-report.schema.json +184 -0
  52. package/schemas/commands.schema.json +12 -0
  53. package/schemas/dependency-graph-report.schema.json +149 -0
  54. package/schemas/env-contract-report.schema.json +203 -0
  55. package/schemas/import-cycle-report.schema.json +157 -0
  56. package/schemas/link-integrity-report.schema.json +176 -0
  57. package/schemas/repo-approval-gate-report.schema.json +115 -0
  58. package/schemas/repo-git-ignore-audit-report.schema.json +201 -0
  59. package/schemas/repo-manifest-lock-drift-report.schema.json +202 -0
  60. package/schemas/repo-merge-conflict-scan-report.schema.json +169 -0
  61. package/schemas/repo-version-source-report.schema.json +127 -0
  62. package/schemas/secret-risk-scan-report.schema.json +152 -0
  63. package/schemas/skill-route-audit-report.schema.json +144 -0
  64. package/schemas/test-performance-report.schema.json +319 -0
  65. package/schemas/test-regression-selector-report.schema.json +187 -0
  66. package/templates/default/i18n.toml +80 -26
  67. package/templates/default/locales/en/.mustflow/skills/INDEX.md +51 -9
  68. package/templates/default/locales/en/.mustflow/skills/api-access-control-review/SKILL.md +48 -27
  69. package/templates/default/locales/en/.mustflow/skills/api-failure-triage/SKILL.md +270 -0
  70. package/templates/default/locales/en/.mustflow/skills/architecture-deepening-review/SKILL.md +28 -11
  71. package/templates/default/locales/en/.mustflow/skills/astro-code-change/SKILL.md +71 -27
  72. package/templates/default/locales/en/.mustflow/skills/auth-flow-triage/SKILL.md +192 -0
  73. package/templates/default/locales/en/.mustflow/skills/auth-permission-change/SKILL.md +59 -13
  74. package/templates/default/locales/en/.mustflow/skills/backend-log-evidence-review/SKILL.md +14 -5
  75. package/templates/default/locales/en/.mustflow/skills/cache-integrity-review/SKILL.md +30 -15
  76. package/templates/default/locales/en/.mustflow/skills/change-blast-radius-review/SKILL.md +45 -32
  77. package/templates/default/locales/en/.mustflow/skills/ci-pipeline-triage/SKILL.md +200 -0
  78. package/templates/default/locales/en/.mustflow/skills/clarifying-question-gate/SKILL.md +87 -13
  79. package/templates/default/locales/en/.mustflow/skills/cross-agent-session-reference/SKILL.md +23 -8
  80. package/templates/default/locales/en/.mustflow/skills/dependency-upgrade-review/SKILL.md +3 -1
  81. package/templates/default/locales/en/.mustflow/skills/docker-runtime-triage/SKILL.md +191 -0
  82. package/templates/default/locales/en/.mustflow/skills/github-contribution-quality-gate/SKILL.md +48 -11
  83. package/templates/default/locales/en/.mustflow/skills/go-code-change/SKILL.md +18 -13
  84. package/templates/default/locales/en/.mustflow/skills/javascript-code-change/SKILL.md +15 -13
  85. package/templates/default/locales/en/.mustflow/skills/line-ending-hygiene/SKILL.md +18 -10
  86. package/templates/default/locales/en/.mustflow/skills/llm-hallucination-control-review/SKILL.md +4 -1
  87. package/templates/default/locales/en/.mustflow/skills/motion-system-contract-review/SKILL.md +155 -0
  88. package/templates/default/locales/en/.mustflow/skills/next-action-menu/SKILL.md +177 -0
  89. package/templates/default/locales/en/.mustflow/skills/node-code-change/SKILL.md +16 -14
  90. package/templates/default/locales/en/.mustflow/skills/observability-debuggability-review/SKILL.md +15 -7
  91. package/templates/default/locales/en/.mustflow/skills/payment-integrity-review/SKILL.md +59 -35
  92. package/templates/default/locales/en/.mustflow/skills/powershell-code-change/SKILL.md +16 -6
  93. package/templates/default/locales/en/.mustflow/skills/prompt-contract-quality-review/SKILL.md +4 -1
  94. package/templates/default/locales/en/.mustflow/skills/python-code-change/SKILL.md +19 -10
  95. package/templates/default/locales/en/.mustflow/skills/rag-pipeline-triage/SKILL.md +206 -0
  96. package/templates/default/locales/en/.mustflow/skills/routes.toml +69 -9
  97. package/templates/default/locales/en/.mustflow/skills/rust-code-change/SKILL.md +10 -4
  98. package/templates/default/locales/en/.mustflow/skills/search-index-integrity-review/SKILL.md +181 -0
  99. package/templates/default/locales/en/.mustflow/skills/security-privacy-review/SKILL.md +3 -1
  100. package/templates/default/locales/en/.mustflow/skills/service-boundary-architecture/SKILL.md +37 -23
  101. package/templates/default/locales/en/.mustflow/skills/test-suite-performance-review/SKILL.md +323 -0
  102. package/templates/default/locales/en/.mustflow/skills/typescript-code-change/SKILL.md +18 -10
  103. package/templates/default/locales/en/.mustflow/skills/vector-search-integrity-review/SKILL.md +209 -0
  104. package/templates/default/locales/en/.mustflow/skills/version-freshness-check/SKILL.md +16 -14
  105. package/templates/default/manifest.toml +71 -1
@@ -36,6 +36,93 @@ export const SCRIPT_PACKS = [
36
36
  reportSchemaFile: 'code-outline-report.schema.json',
37
37
  loadRunner: async () => (await import('../script-packs/code-outline.js')).runCodeOutlineScript,
38
38
  },
39
+ {
40
+ packId: 'code',
41
+ id: 'dependency-graph',
42
+ ref: scriptRef('code', 'dependency-graph'),
43
+ usage: 'mf script-pack run code/dependency-graph scan <path...> [options]',
44
+ summaryKey: 'scriptPack.script.codeDependencyGraph.summary',
45
+ actions: ['scan'],
46
+ useWhen: [
47
+ 'Trace relative TypeScript and JavaScript import, export, require, and dynamic import edges before changing a module.',
48
+ 'Build a bounded dependency graph with nodes, edges, depth, importer counts, import counts, and cycle hints for source impact orientation.',
49
+ ],
50
+ phases: ['before_change', 'during_change', 'review'],
51
+ readOnly: true,
52
+ mutates: false,
53
+ network: false,
54
+ inputs: ['path', 'max_files', 'max_file_bytes', 'max_depth', 'max_nodes', 'max_edges'],
55
+ outputs: ['human_summary', 'json_report', 'dependency_graph', 'cycle_hints'],
56
+ relatedSkills: [
57
+ 'change-blast-radius-review',
58
+ 'codebase-orientation',
59
+ 'javascript-code-change',
60
+ 'module-boundary-review',
61
+ 'typescript-code-change',
62
+ ],
63
+ riskLevel: 'low',
64
+ cost: 'low',
65
+ reportSchemaFile: 'dependency-graph-report.schema.json',
66
+ loadRunner: async () => (await import('../script-packs/code-dependency-graph.js')).runCodeDependencyGraphScript,
67
+ },
68
+ {
69
+ packId: 'code',
70
+ id: 'import-cycle',
71
+ ref: scriptRef('code', 'import-cycle'),
72
+ usage: 'mf script-pack run code/import-cycle check <path...> [options]',
73
+ summaryKey: 'scriptPack.script.codeImportCycle.summary',
74
+ actions: ['check'],
75
+ useWhen: [
76
+ 'Detect bounded relative TypeScript and JavaScript import cycles before changing module boundaries.',
77
+ 'Review exact cycle paths and import line evidence after dependency graph orientation finds cycle hints.',
78
+ ],
79
+ phases: ['before_change', 'after_change', 'review'],
80
+ readOnly: true,
81
+ mutates: false,
82
+ network: false,
83
+ inputs: ['path', 'max_files', 'max_file_bytes', 'max_depth', 'max_nodes', 'max_edges', 'max_cycles'],
84
+ outputs: ['human_summary', 'json_report', 'import_cycles', 'cycle_edge_evidence'],
85
+ relatedSkills: [
86
+ 'change-blast-radius-review',
87
+ 'codebase-orientation',
88
+ 'javascript-code-change',
89
+ 'module-boundary-review',
90
+ 'typescript-code-change',
91
+ ],
92
+ riskLevel: 'low',
93
+ cost: 'low',
94
+ reportSchemaFile: 'import-cycle-report.schema.json',
95
+ loadRunner: async () => (await import('../script-packs/code-import-cycle.js')).runCodeImportCycleScript,
96
+ },
97
+ {
98
+ packId: 'code',
99
+ id: 'change-impact',
100
+ ref: scriptRef('code', 'change-impact'),
101
+ usage: 'mf script-pack run code/change-impact analyze [path...] [options]',
102
+ summaryKey: 'scriptPack.script.codeChangeImpact.summary',
103
+ actions: ['analyze'],
104
+ useWhen: [
105
+ 'Analyze current git changes and produce bounded file, script-pack, and verification hints after a change.',
106
+ 'Classify changed source, test, docs, schema, package, template, and workflow surfaces before choosing follow-up checks.',
107
+ ],
108
+ phases: ['after_change', 'review'],
109
+ readOnly: true,
110
+ mutates: false,
111
+ network: false,
112
+ inputs: ['path', 'base_ref', 'head_ref', 'max_files', 'max_impacts', 'max_file_bytes'],
113
+ outputs: ['human_summary', 'json_report', 'changed_files', 'impact_candidates', 'script_hints', 'verification_hints'],
114
+ relatedSkills: [
115
+ 'change-blast-radius-review',
116
+ 'completion-evidence-gate',
117
+ 'javascript-code-change',
118
+ 'public-json-contract-change',
119
+ 'typescript-code-change',
120
+ ],
121
+ riskLevel: 'low',
122
+ cost: 'low',
123
+ reportSchemaFile: 'change-impact-report.schema.json',
124
+ loadRunner: async () => (await import('../script-packs/code-change-impact.js')).runCodeChangeImpactScript,
125
+ },
39
126
  {
40
127
  packId: 'code',
41
128
  id: 'symbol-read',
@@ -194,6 +281,87 @@ export const SCRIPT_PACKS = [
194
281
  reportSchemaFile: 'reference-drift-report.schema.json',
195
282
  loadRunner: async () => (await import('../script-packs/docs-reference-drift.js')).runDocsReferenceDriftScript,
196
283
  },
284
+ {
285
+ packId: 'docs',
286
+ id: 'link-integrity',
287
+ ref: scriptRef('docs', 'link-integrity'),
288
+ usage: 'mf script-pack run docs/link-integrity check [path...] [options]',
289
+ summaryKey: 'scriptPack.script.linkIntegrity.summary',
290
+ actions: ['check'],
291
+ useWhen: [
292
+ 'Check Markdown and MDX inline links for local file and anchor drift without fetching external URLs.',
293
+ 'Review docs after moving, renaming, or retitling documentation files before claiming navigation links still resolve.',
294
+ ],
295
+ phases: ['after_change', 'review'],
296
+ readOnly: true,
297
+ mutates: false,
298
+ network: false,
299
+ inputs: ['path', 'max_files', 'max_file_bytes'],
300
+ outputs: ['human_summary', 'json_report', 'link_integrity', 'missing_link_findings'],
301
+ relatedSkills: [
302
+ 'cli-output-contract-review',
303
+ 'docs-prose-review',
304
+ 'docs-update',
305
+ 'public-json-contract-change',
306
+ 'readme-authoring',
307
+ ],
308
+ riskLevel: 'low',
309
+ cost: 'low',
310
+ reportSchemaFile: 'link-integrity-report.schema.json',
311
+ loadRunner: async () => (await import('../script-packs/docs-link-integrity.js')).runDocsLinkIntegrityScript,
312
+ },
313
+ ],
314
+ },
315
+ {
316
+ id: 'test',
317
+ summaryKey: 'scriptPack.pack.test.summary',
318
+ scripts: [
319
+ {
320
+ packId: 'test',
321
+ id: 'performance-report',
322
+ ref: scriptRef('test', 'performance-report'),
323
+ usage: 'mf script-pack run test/performance-report summarize [options]',
324
+ summaryKey: 'scriptPack.script.testPerformanceReport.summary',
325
+ actions: ['summarize'],
326
+ useWhen: [
327
+ 'Summarize retained mf run performance evidence, selected-test fallbacks, slow intents, timeout pressure, and phase bottlenecks.',
328
+ 'Review local verification-loop performance before changing test scheduling, profiling, caching, or command-contract timeout policy.',
329
+ ],
330
+ phases: ['after_change', 'review'],
331
+ readOnly: true,
332
+ mutates: false,
333
+ network: false,
334
+ inputs: ['max_samples', 'max_intents', 'max_test_files', 'max_findings', 'slow_ms', 'timeout_ratio', 'phase_ms'],
335
+ outputs: ['human_summary', 'json_report', 'performance_samples', 'intent_timings', 'phase_timings', 'test_file_timings'],
336
+ relatedSkills: ['test-suite-performance-review', 'test-maintenance', 'completion-evidence-gate'],
337
+ riskLevel: 'low',
338
+ cost: 'low',
339
+ reportSchemaFile: 'test-performance-report.schema.json',
340
+ loadRunner: async () => (await import('../script-packs/test-performance-report.js')).runTestPerformanceReportScript,
341
+ },
342
+ {
343
+ packId: 'test',
344
+ id: 'regression-selector',
345
+ ref: scriptRef('test', 'regression-selector'),
346
+ usage: 'mf script-pack run test/regression-selector select [path...] [options]',
347
+ summaryKey: 'scriptPack.script.testRegressionSelector.summary',
348
+ actions: ['select'],
349
+ useWhen: [
350
+ 'Select likely regression tests from changed files while reporting unsafe surfaces that require fallback verification.',
351
+ 'Review source, test, package, schema, workflow, and config changes before relying on a related-test shortcut.',
352
+ ],
353
+ phases: ['after_change', 'review'],
354
+ readOnly: true,
355
+ mutates: false,
356
+ network: false,
357
+ inputs: ['path', 'base_ref', 'head_ref', 'max_files', 'max_tests'],
358
+ outputs: ['human_summary', 'json_report', 'changed_files', 'selected_tests', 'fallbacks'],
359
+ relatedSkills: ['test-suite-performance-review', 'test-maintenance', 'change-blast-radius-review'],
360
+ riskLevel: 'low',
361
+ cost: 'low',
362
+ reportSchemaFile: 'test-regression-selector-report.schema.json',
363
+ loadRunner: async () => (await import('../script-packs/test-regression-selector.js')).runTestRegressionSelectorScript,
364
+ },
197
365
  ],
198
366
  },
199
367
  {
@@ -229,6 +397,62 @@ export const SCRIPT_PACKS = [
229
397
  reportSchemaFile: 'config-chain-report.schema.json',
230
398
  loadRunner: async () => (await import('../script-packs/repo-config-chain.js')).runRepoConfigChainScript,
231
399
  },
400
+ {
401
+ packId: 'repo',
402
+ id: 'env-contract',
403
+ ref: scriptRef('repo', 'env-contract'),
404
+ usage: 'mf script-pack run repo/env-contract scan [path...] [options]',
405
+ summaryKey: 'scriptPack.script.envContract.summary',
406
+ actions: ['scan'],
407
+ useWhen: [
408
+ 'Scan code, CI, docs, and env example files for environment variable contract drift without reading real secret env files.',
409
+ 'Review env keys that are used in code, declared in examples, referenced in CI, documented, missing from examples, or public-prefix secret-like names.',
410
+ ],
411
+ phases: ['before_change', 'during_change', 'after_change', 'review'],
412
+ readOnly: true,
413
+ mutates: false,
414
+ network: false,
415
+ inputs: ['path', 'max_files', 'max_file_bytes', 'max_keys'],
416
+ outputs: ['human_summary', 'json_report', 'env_keys', 'env_contract_findings'],
417
+ relatedSkills: [
418
+ 'config-env-change',
419
+ 'public-json-contract-change',
420
+ 'security-privacy-review',
421
+ 'typescript-code-change',
422
+ ],
423
+ riskLevel: 'low',
424
+ cost: 'low',
425
+ reportSchemaFile: 'env-contract-report.schema.json',
426
+ loadRunner: async () => (await import('../script-packs/repo-env-contract.js')).runRepoEnvContractScript,
427
+ },
428
+ {
429
+ packId: 'repo',
430
+ id: 'secret-risk-scan',
431
+ ref: scriptRef('repo', 'secret-risk-scan'),
432
+ usage: 'mf script-pack run repo/secret-risk-scan scan [path...] [options]',
433
+ summaryKey: 'scriptPack.script.secretRiskScan.summary',
434
+ actions: ['scan'],
435
+ useWhen: [
436
+ 'Scan code, docs, config, CI, and examples for plausible hardcoded secrets without printing secret values.',
437
+ 'Review provider token prefixes, Bearer tokens, private key markers, secret-like assignments, and realistic env-example values.',
438
+ ],
439
+ phases: ['before_change', 'during_change', 'after_change', 'review'],
440
+ readOnly: true,
441
+ mutates: false,
442
+ network: false,
443
+ inputs: ['path', 'max_files', 'max_file_bytes', 'max_findings'],
444
+ outputs: ['human_summary', 'json_report', 'secret_risk_findings', 'redacted_fingerprints'],
445
+ relatedSkills: [
446
+ 'config-env-change',
447
+ 'public-json-contract-change',
448
+ 'security-privacy-review',
449
+ 'typescript-code-change',
450
+ ],
451
+ riskLevel: 'medium',
452
+ cost: 'low',
453
+ reportSchemaFile: 'secret-risk-scan-report.schema.json',
454
+ loadRunner: async () => (await import('../script-packs/repo-secret-risk-scan.js')).runRepoSecretRiskScanScript,
455
+ },
232
456
  {
233
457
  packId: 'repo',
234
458
  id: 'generated-boundary',
@@ -258,6 +482,179 @@ export const SCRIPT_PACKS = [
258
482
  reportSchemaFile: 'generated-boundary-report.schema.json',
259
483
  loadRunner: async () => (await import('../script-packs/repo-generated-boundary.js')).runRepoGeneratedBoundaryScript,
260
484
  },
485
+ {
486
+ packId: 'repo',
487
+ id: 'merge-conflict-scan',
488
+ ref: scriptRef('repo', 'merge-conflict-scan'),
489
+ usage: 'mf script-pack run repo/merge-conflict-scan check [path...] [options]',
490
+ summaryKey: 'scriptPack.script.mergeConflictScan.summary',
491
+ actions: ['check'],
492
+ useWhen: [
493
+ 'Scan changed files or explicit paths for Git merge conflict markers without printing surrounding file content.',
494
+ 'Review dirty worktrees before final verification, packaging, commit preparation, or handoff to avoid shipping unresolved conflict markers.',
495
+ ],
496
+ phases: ['after_change', 'review'],
497
+ readOnly: true,
498
+ mutates: false,
499
+ network: false,
500
+ inputs: ['path', 'max_files', 'max_file_bytes'],
501
+ outputs: ['human_summary', 'json_report', 'merge_conflict_markers', 'marker_findings'],
502
+ relatedSkills: [
503
+ 'cli-output-contract-review',
504
+ 'completion-evidence-gate',
505
+ 'public-json-contract-change',
506
+ 'test-maintenance',
507
+ 'typescript-code-change',
508
+ ],
509
+ riskLevel: 'low',
510
+ cost: 'low',
511
+ reportSchemaFile: 'repo-merge-conflict-scan-report.schema.json',
512
+ loadRunner: async () => (await import('../script-packs/repo-merge-conflict-scan.js')).runRepoMergeConflictScanScript,
513
+ },
514
+ {
515
+ packId: 'repo',
516
+ id: 'git-ignore-audit',
517
+ ref: scriptRef('repo', 'git-ignore-audit'),
518
+ usage: 'mf script-pack run repo/git-ignore-audit audit [path...] [options]',
519
+ summaryKey: 'scriptPack.script.gitIgnoreAudit.summary',
520
+ actions: ['audit'],
521
+ useWhen: [
522
+ 'Inspect explicit paths or changed files against .gitignore, .git/info/exclude, core.excludesFile, and config visibility evidence.',
523
+ 'Review ignored, untracked, missing, and tracked-but-ignored paths before assuming Git visibility, package contents, or generated output coverage.',
524
+ ],
525
+ phases: ['before_change', 'after_change', 'review'],
526
+ readOnly: true,
527
+ mutates: false,
528
+ network: false,
529
+ inputs: ['path', 'max_paths'],
530
+ outputs: ['human_summary', 'json_report', 'ignore_sources', 'ignored_path_evidence'],
531
+ relatedSkills: [
532
+ 'cli-output-contract-review',
533
+ 'completion-evidence-gate',
534
+ 'file-path-cross-platform-change',
535
+ 'public-json-contract-change',
536
+ 'test-maintenance',
537
+ ],
538
+ riskLevel: 'low',
539
+ cost: 'low',
540
+ reportSchemaFile: 'repo-git-ignore-audit-report.schema.json',
541
+ loadRunner: async () => (await import('../script-packs/repo-git-ignore-audit.js')).runRepoGitIgnoreAuditScript,
542
+ },
543
+ {
544
+ packId: 'repo',
545
+ id: 'manifest-lock-drift',
546
+ ref: scriptRef('repo', 'manifest-lock-drift'),
547
+ usage: 'mf script-pack run repo/manifest-lock-drift check [path...] [options]',
548
+ summaryKey: 'scriptPack.script.manifestLockDrift.summary',
549
+ actions: ['check'],
550
+ useWhen: [
551
+ 'Compare .mustflow/config/manifest.lock.toml entries with the current repository files without rewriting the lock.',
552
+ 'Review missing, unsafe, unreadable, invalid-hash, and content-hash mismatch evidence before trusting template update or install state.',
553
+ ],
554
+ phases: ['before_change', 'after_change', 'review'],
555
+ readOnly: true,
556
+ mutates: false,
557
+ network: false,
558
+ inputs: ['path', 'max_entries'],
559
+ outputs: ['human_summary', 'json_report', 'manifest_lock_entries', 'hash_mismatch_findings'],
560
+ relatedSkills: [
561
+ 'cli-output-contract-review',
562
+ 'completion-evidence-gate',
563
+ 'contract-sync-check',
564
+ 'file-path-cross-platform-change',
565
+ 'public-json-contract-change',
566
+ 'template-install-surface-sync',
567
+ ],
568
+ riskLevel: 'low',
569
+ cost: 'low',
570
+ reportSchemaFile: 'repo-manifest-lock-drift-report.schema.json',
571
+ loadRunner: async () => (await import('../script-packs/repo-manifest-lock-drift.js')).runRepoManifestLockDriftScript,
572
+ },
573
+ {
574
+ packId: 'repo',
575
+ id: 'skill-route-audit',
576
+ ref: scriptRef('repo', 'skill-route-audit'),
577
+ usage: 'mf script-pack run repo/skill-route-audit audit [options]',
578
+ summaryKey: 'scriptPack.script.skillRouteAudit.summary',
579
+ actions: ['audit'],
580
+ useWhen: [
581
+ 'Audit source skill files, route metadata, skill index entries, default template skill copies, manifest profiles, and i18n metadata for drift.',
582
+ 'Review mustflow skill-route surfaces after adding, refreshing, or synchronizing skills before relying on installed template coverage.',
583
+ ],
584
+ phases: ['before_change', 'after_change', 'review'],
585
+ readOnly: true,
586
+ mutates: false,
587
+ network: false,
588
+ inputs: [],
589
+ outputs: ['human_summary', 'json_report', 'skill_route_findings', 'template_drift', 'manifest_drift', 'i18n_drift'],
590
+ relatedSkills: [
591
+ 'contract-sync-check',
592
+ 'public-json-contract-change',
593
+ 'skill-authoring',
594
+ 'skill-refresh',
595
+ 'template-install-surface-sync',
596
+ ],
597
+ riskLevel: 'low',
598
+ cost: 'low',
599
+ reportSchemaFile: 'skill-route-audit-report.schema.json',
600
+ loadRunner: async () => (await import('../script-packs/repo-skill-route-audit.js')).runRepoSkillRouteAuditScript,
601
+ },
602
+ {
603
+ packId: 'repo',
604
+ id: 'version-source',
605
+ ref: scriptRef('repo', 'version-source'),
606
+ usage: 'mf script-pack run repo/version-source inspect [options]',
607
+ summaryKey: 'scriptPack.script.versionSource.summary',
608
+ actions: ['inspect'],
609
+ useWhen: [
610
+ 'Inspect detected version source files before editing package, template, release, or versioning metadata.',
611
+ 'Review whether enabled release versioning preferences have at least one detected version source.',
612
+ ],
613
+ phases: ['before_change', 'after_change', 'review'],
614
+ readOnly: true,
615
+ mutates: false,
616
+ network: false,
617
+ inputs: [],
618
+ outputs: ['human_summary', 'json_report', 'version_sources', 'versioning_findings'],
619
+ relatedSkills: [
620
+ 'completion-evidence-gate',
621
+ 'public-json-contract-change',
622
+ 'release-publish-change',
623
+ 'version-freshness-check',
624
+ ],
625
+ riskLevel: 'low',
626
+ cost: 'low',
627
+ reportSchemaFile: 'repo-version-source-report.schema.json',
628
+ loadRunner: async () => (await import('../script-packs/repo-version-source.js')).runRepoVersionSourceScript,
629
+ },
630
+ {
631
+ packId: 'repo',
632
+ id: 'approval-gate',
633
+ ref: scriptRef('repo', 'approval-gate'),
634
+ usage: 'mf script-pack run repo/approval-gate check --action <type> [options]',
635
+ summaryKey: 'scriptPack.script.approvalGate.summary',
636
+ actions: ['check'],
637
+ useWhen: [
638
+ 'Check planned action types against .mustflow/config/mustflow.toml [approval].required_for before proceeding.',
639
+ 'Review whether git, dependency, network, database, destructive, secret, release, or cross-repository actions require human approval.',
640
+ ],
641
+ phases: ['before_change', 'review'],
642
+ readOnly: true,
643
+ mutates: false,
644
+ network: false,
645
+ inputs: ['action_type'],
646
+ outputs: ['human_summary', 'json_report', 'approval_decisions', 'approval_findings'],
647
+ relatedSkills: [
648
+ 'command-contract-authoring',
649
+ 'completion-evidence-gate',
650
+ 'public-json-contract-change',
651
+ 'release-publish-change',
652
+ ],
653
+ riskLevel: 'low',
654
+ cost: 'low',
655
+ reportSchemaFile: 'repo-approval-gate-report.schema.json',
656
+ loadRunner: async () => (await import('../script-packs/repo-approval-gate.js')).runRepoApprovalGateScript,
657
+ },
261
658
  {
262
659
  packId: 'repo',
263
660
  id: 'related-files',
@@ -11,7 +11,7 @@ import { validateSourceAnchorsInProject } from '../../../core/source-anchor-vali
11
11
  import { listFilesRecursive, toPosixPath } from '../filesystem.js';
12
12
  import { readGitChangedFiles } from '../git-changes.js';
13
13
  import { inspectManifestLock } from '../manifest-lock.js';
14
- import { generateRepoMap } from '../repo-map.js';
14
+ import { getExpectedRepoMapSourceFingerprint } from '../repo-map.js';
15
15
  import { parseTomlText, readMustflowTomlFile } from '../toml.js';
16
16
  import { MUSTFLOW_JSON_MAX_BYTES } from '../mustflow-read.js';
17
17
  import { getContractModelDefinitions, validateCandidateContractModelConfig, } from '../../../core/contract-models.js';
@@ -1464,7 +1464,7 @@ function validateStrictRepoMap(projectRoot, issues) {
1464
1464
  }
1465
1465
  else {
1466
1466
  const currentSourceFingerprint = frontmatter.source_fingerprint;
1467
- const expectedSourceFingerprint = parseSimpleFrontmatter(generateRepoMap(projectRoot)).source_fingerprint;
1467
+ const expectedSourceFingerprint = getExpectedRepoMapSourceFingerprint(projectRoot);
1468
1468
  if (expectedSourceFingerprint && currentSourceFingerprint !== expectedSourceFingerprint) {
1469
1469
  pushStrictIssue(issues, 'REPO_MAP.md source_fingerprint is stale; regenerate with mf map --write');
1470
1470
  }
@@ -2,6 +2,7 @@ import { existsSync } from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { isRecord } from '../command-contract.js';
4
4
  import { readMustflowTomlFile } from '../toml.js';
5
+ import { COMMANDS_CONFIG_RELATIVE_PATH, readResolvedCommandContractToml, } from '../../../core/config-loading.js';
5
6
  import { REQUIRED_FILES, } from './constants.js';
6
7
  import { TECHNOLOGY_CONFIG_RELATIVE_PATH } from '../../../core/technology-preferences.js';
7
8
  import { VERSIONING_CONFIG_PATH } from '../../../core/version-sources.js';
@@ -43,7 +44,9 @@ export function validateToml(projectRoot, issues) {
43
44
  continue;
44
45
  }
45
46
  try {
46
- const parsed = readMustflowTomlFile(projectRoot, relativePath);
47
+ const parsed = relativePath === COMMANDS_CONFIG_RELATIVE_PATH
48
+ ? readResolvedCommandContractToml(projectRoot)
49
+ : readMustflowTomlFile(projectRoot, relativePath);
47
50
  if (!isRecord(parsed)) {
48
51
  issues.push({ message: `${relativePath} must contain a TOML table` });
49
52
  continue;
@@ -0,0 +1,178 @@
1
+ import { printUsageError, renderHelp } from '../lib/cli-output.js';
2
+ import { t } from '../lib/i18n.js';
3
+ import { formatCliOptionParseError, getParsedCliStringOption, hasCliOptionToken, hasParsedCliOption, parseCliOptions, } from '../lib/option-parser.js';
4
+ import { resolveMustflowRoot } from '../lib/project-root.js';
5
+ import { CHANGE_IMPACT_SCRIPT_REF, inspectChangeImpact } from '../../core/change-impact.js';
6
+ const CHANGE_IMPACT_OPTIONS = [
7
+ { name: '--json', kind: 'boolean' },
8
+ { name: '--base', kind: 'string' },
9
+ { name: '--head', kind: 'string' },
10
+ { name: '--max-files', kind: 'string' },
11
+ { name: '--max-impacts', kind: 'string' },
12
+ { name: '--max-file-bytes', kind: 'string' },
13
+ ];
14
+ function parsePositiveInteger(value, option, lang) {
15
+ if (value === null) {
16
+ return { value: null };
17
+ }
18
+ if (!/^[1-9]\d*$/u.test(value)) {
19
+ return { value: null, error: t(lang, 'changeImpact.error.invalidPositiveInteger', { option, value }) };
20
+ }
21
+ const parsed = Number(value);
22
+ if (!Number.isSafeInteger(parsed)) {
23
+ return { value: null, error: t(lang, 'changeImpact.error.invalidPositiveInteger', { option, value }) };
24
+ }
25
+ return { value: parsed };
26
+ }
27
+ export function getCodeChangeImpactHelp(lang = 'en') {
28
+ return renderHelp({
29
+ usage: 'mf script-pack run code/change-impact analyze [path...] [options]',
30
+ summary: t(lang, 'changeImpact.help.summary'),
31
+ options: [
32
+ { label: '--base <ref>', description: t(lang, 'changeImpact.help.option.base') },
33
+ { label: '--head <ref>', description: t(lang, 'changeImpact.help.option.head') },
34
+ { label: '--max-files <count>', description: t(lang, 'changeImpact.help.option.maxFiles') },
35
+ { label: '--max-impacts <count>', description: t(lang, 'changeImpact.help.option.maxImpacts') },
36
+ { label: '--max-file-bytes <bytes>', description: t(lang, 'changeImpact.help.option.maxFileBytes') },
37
+ { label: '--json', description: t(lang, 'cli.option.json') },
38
+ { label: '-h, --help', description: t(lang, 'cli.option.help') },
39
+ ],
40
+ examples: [
41
+ 'mf script-pack run code/change-impact analyze --base HEAD --json',
42
+ 'mf script-pack run code/change-impact analyze src --base main --head HEAD --json',
43
+ 'mf script-pack run code/change-impact analyze src/core --max-impacts 80 --json',
44
+ ],
45
+ exitCodes: [
46
+ { label: '0', description: t(lang, 'changeImpact.help.exit.ok') },
47
+ { label: '1', description: t(lang, 'changeImpact.help.exit.fail') },
48
+ ],
49
+ }, lang);
50
+ }
51
+ function parseChangeImpactOptions(args, lang) {
52
+ const [action, ...rest] = args;
53
+ const parsed = parseCliOptions(rest, CHANGE_IMPACT_OPTIONS, { allowPositionals: true });
54
+ const json = hasParsedCliOption(parsed, '--json');
55
+ const baseRef = getParsedCliStringOption(parsed, '--base');
56
+ const headRef = getParsedCliStringOption(parsed, '--head');
57
+ const maxFiles = parsePositiveInteger(getParsedCliStringOption(parsed, '--max-files'), '--max-files', lang);
58
+ const maxImpacts = parsePositiveInteger(getParsedCliStringOption(parsed, '--max-impacts'), '--max-impacts', lang);
59
+ const maxFileBytes = parsePositiveInteger(getParsedCliStringOption(parsed, '--max-file-bytes'), '--max-file-bytes', lang);
60
+ const positiveOptions = [maxFiles, maxImpacts, maxFileBytes];
61
+ if (action !== 'analyze') {
62
+ return {
63
+ action: 'analyze',
64
+ json,
65
+ baseRef,
66
+ headRef,
67
+ paths: parsed.positionals,
68
+ maxFiles: maxFiles.value,
69
+ maxImpacts: maxImpacts.value,
70
+ maxFileBytes: maxFileBytes.value,
71
+ error: action ? t(lang, 'changeImpact.error.unknownAction', { action }) : t(lang, 'changeImpact.error.missingAction'),
72
+ };
73
+ }
74
+ if (parsed.error) {
75
+ return {
76
+ action,
77
+ json,
78
+ baseRef,
79
+ headRef,
80
+ paths: parsed.positionals,
81
+ maxFiles: maxFiles.value,
82
+ maxImpacts: maxImpacts.value,
83
+ maxFileBytes: maxFileBytes.value,
84
+ error: formatCliOptionParseError(parsed.error, lang),
85
+ };
86
+ }
87
+ for (const candidate of positiveOptions) {
88
+ if (candidate.error) {
89
+ return {
90
+ action,
91
+ json,
92
+ baseRef,
93
+ headRef,
94
+ paths: parsed.positionals,
95
+ maxFiles: maxFiles.value,
96
+ maxImpacts: maxImpacts.value,
97
+ maxFileBytes: maxFileBytes.value,
98
+ error: candidate.error,
99
+ };
100
+ }
101
+ }
102
+ return {
103
+ action,
104
+ json,
105
+ baseRef,
106
+ headRef,
107
+ paths: parsed.positionals,
108
+ maxFiles: maxFiles.value,
109
+ maxImpacts: maxImpacts.value,
110
+ maxFileBytes: maxFileBytes.value,
111
+ };
112
+ }
113
+ function renderChangeImpactSummary(report, lang) {
114
+ const lines = [
115
+ t(lang, 'changeImpact.title'),
116
+ `${t(lang, 'scriptPack.label.script')}: ${CHANGE_IMPACT_SCRIPT_REF}`,
117
+ `${t(lang, 'label.status')}: ${report.status}`,
118
+ `${t(lang, 'changeImpact.label.changedFiles')}: ${report.changed_files.length}`,
119
+ `${t(lang, 'changeImpact.label.impacts')}: ${report.impacts.length}`,
120
+ `${t(lang, 'changeImpact.label.truncated')}: ${report.truncated ? t(lang, 'value.yes') : t(lang, 'value.no')}`,
121
+ ];
122
+ for (const impact of report.impacts.slice(0, 40)) {
123
+ lines.push(`- ${impact.path}: ${impact.relationship} (${impact.reason})`);
124
+ }
125
+ if (report.script_hints.length > 0) {
126
+ lines.push(t(lang, 'changeImpact.label.scriptHints'));
127
+ for (const hint of report.script_hints) {
128
+ lines.push(`- ${hint.script_ref}: ${hint.command}`);
129
+ if (hint.related_intents && hint.related_intents.length > 0) {
130
+ lines.push(` related intents: ${hint.related_intents.join(', ')}`);
131
+ }
132
+ if (hint.expected_fallback_reasons && hint.expected_fallback_reasons.length > 0) {
133
+ lines.push(` expected fallback reasons: ${hint.expected_fallback_reasons.join(', ')}`);
134
+ }
135
+ }
136
+ }
137
+ if (report.verification_hints.length > 0) {
138
+ lines.push(t(lang, 'changeImpact.label.verificationHints'));
139
+ for (const hint of report.verification_hints) {
140
+ lines.push(`- ${hint.intent}: ${hint.reason}`);
141
+ }
142
+ }
143
+ if (report.findings.length > 0) {
144
+ lines.push(t(lang, 'changeImpact.label.findings'), ...report.findings.map((finding) => `- ${finding.path}: ${finding.code} (${finding.message})`));
145
+ }
146
+ if (report.issues.length > 0) {
147
+ lines.push(t(lang, 'changeImpact.label.issues'), ...report.issues.map((issue) => `- ${issue}`));
148
+ }
149
+ if (report.changed_files.length === 0 && report.findings.length === 0 && report.issues.length === 0) {
150
+ lines.push(t(lang, 'changeImpact.clean'));
151
+ }
152
+ return lines.join('\n');
153
+ }
154
+ export function runCodeChangeImpactScript(args, reporter, lang = 'en') {
155
+ if (hasCliOptionToken(args, '--help', ['-h'])) {
156
+ reporter.stdout(getCodeChangeImpactHelp(lang));
157
+ return 0;
158
+ }
159
+ const options = parseChangeImpactOptions(args, lang);
160
+ if (options.error) {
161
+ printUsageError(reporter, options.error, 'mf script-pack run code/change-impact --help', getCodeChangeImpactHelp(lang), lang);
162
+ return 1;
163
+ }
164
+ const report = inspectChangeImpact(resolveMustflowRoot(), {
165
+ baseRef: options.baseRef ?? undefined,
166
+ headRef: options.headRef,
167
+ paths: options.paths,
168
+ maxFiles: options.maxFiles ?? undefined,
169
+ maxImpacts: options.maxImpacts ?? undefined,
170
+ maxFileBytes: options.maxFileBytes ?? undefined,
171
+ });
172
+ if (options.json) {
173
+ reporter.stdout(JSON.stringify(report, null, 2));
174
+ return report.ok ? 0 : 1;
175
+ }
176
+ reporter.stdout(renderChangeImpactSummary(report, lang));
177
+ return report.ok ? 0 : 1;
178
+ }