mustflow 2.75.2 → 2.85.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +40 -3
- package/dist/cli/commands/docs.js +86 -2
- package/dist/cli/commands/script-pack.js +9 -0
- package/dist/cli/i18n/en.js +180 -2
- package/dist/cli/i18n/es.js +180 -2
- package/dist/cli/i18n/fr.js +180 -2
- package/dist/cli/i18n/hi.js +180 -2
- package/dist/cli/i18n/ko.js +180 -2
- package/dist/cli/i18n/zh.js +180 -2
- package/dist/cli/lib/repo-map.js +27 -6
- package/dist/cli/lib/run-root-trust.js +15 -1
- package/dist/cli/lib/script-pack-registry.js +275 -6
- package/dist/cli/lib/validation/index.js +2 -2
- package/dist/cli/lib/validation/primitives.js +4 -1
- package/dist/cli/script-packs/code-change-impact.js +172 -0
- package/dist/cli/script-packs/code-dependency-graph.js +181 -0
- package/dist/cli/script-packs/code-export-diff.js +160 -0
- package/dist/cli/script-packs/code-outline.js +33 -5
- package/dist/cli/script-packs/code-route-outline.js +155 -0
- package/dist/cli/script-packs/docs-reference-drift.js +150 -0
- package/dist/cli/script-packs/repo-config-chain.js +163 -0
- package/dist/cli/script-packs/repo-env-contract.js +156 -0
- package/dist/cli/script-packs/repo-related-files.js +161 -0
- package/dist/cli/script-packs/repo-secret-risk-scan.js +147 -0
- package/dist/core/change-impact.js +383 -0
- package/dist/core/change-verification.js +32 -5
- package/dist/core/code-outline.js +460 -79
- package/dist/core/config-chain.js +595 -0
- package/dist/core/config-loading.js +121 -4
- package/dist/core/dependency-graph.js +490 -0
- package/dist/core/env-contract.js +450 -0
- package/dist/core/export-diff.js +359 -0
- package/dist/core/line-endings.js +26 -13
- package/dist/core/public-json-contracts.js +126 -0
- package/dist/core/reference-drift.js +388 -0
- package/dist/core/related-files.js +493 -0
- package/dist/core/route-outline.js +964 -0
- package/dist/core/script-pack-suggestions.js +131 -5
- package/dist/core/secret-risk-scan.js +440 -0
- package/dist/core/source-anchors.js +13 -1
- package/package.json +1 -1
- package/schemas/README.md +44 -6
- package/schemas/change-impact-report.schema.json +150 -0
- package/schemas/code-outline-report.schema.json +1 -1
- package/schemas/code-symbol-read-report.schema.json +64 -4
- package/schemas/commands.schema.json +12 -0
- package/schemas/config-chain-report.schema.json +187 -0
- package/schemas/dependency-graph-report.schema.json +149 -0
- package/schemas/env-contract-report.schema.json +203 -0
- package/schemas/export-diff-report.schema.json +220 -0
- package/schemas/reference-drift-report.schema.json +166 -0
- package/schemas/related-files-report.schema.json +145 -0
- package/schemas/route-outline-report.schema.json +200 -0
- package/schemas/secret-risk-scan-report.schema.json +152 -0
- package/templates/default/common/.mustflow/config/commands.toml +21 -0
- package/templates/default/i18n.toml +21 -9
- package/templates/default/locales/en/.mustflow/docs/agent-workflow.md +1 -1
- package/templates/default/locales/en/.mustflow/skills/INDEX.md +8 -2
- package/templates/default/locales/en/.mustflow/skills/architecture-deepening-review/SKILL.md +28 -11
- package/templates/default/locales/en/.mustflow/skills/astro-code-change/SKILL.md +71 -27
- package/templates/default/locales/en/.mustflow/skills/cross-agent-session-reference/SKILL.md +146 -0
- package/templates/default/locales/en/.mustflow/skills/dependency-upgrade-review/SKILL.md +3 -1
- package/templates/default/locales/en/.mustflow/skills/github-contribution-quality-gate/SKILL.md +48 -11
- package/templates/default/locales/en/.mustflow/skills/javascript-code-change/SKILL.md +15 -13
- package/templates/default/locales/en/.mustflow/skills/node-code-change/SKILL.md +16 -14
- package/templates/default/locales/en/.mustflow/skills/routes.toml +21 -9
- package/templates/default/locales/en/.mustflow/skills/security-privacy-review/SKILL.md +3 -1
- package/templates/default/locales/en/.mustflow/skills/test-suite-performance-review/SKILL.md +314 -0
- package/templates/default/locales/en/.mustflow/skills/typescript-code-change/SKILL.md +13 -10
- package/templates/default/manifest.toml +15 -1
|
@@ -14,8 +14,9 @@ export const SCRIPT_PACKS = [
|
|
|
14
14
|
summaryKey: 'scriptPack.script.codeOutline.summary',
|
|
15
15
|
actions: ['scan'],
|
|
16
16
|
useWhen: [
|
|
17
|
-
'Scan
|
|
18
|
-
'Build a bounded source outline with file paths, line ranges, signatures,
|
|
17
|
+
'Scan supported source files for symbol headers and source anchors before reading large files chunk by chunk.',
|
|
18
|
+
'Build a bounded source outline with file paths, language metadata, line ranges, signatures, ' +
|
|
19
|
+
'export flags, source-anchor metadata, and content hashes for codebase orientation.',
|
|
19
20
|
],
|
|
20
21
|
phases: ['before_change', 'during_change', 'review'],
|
|
21
22
|
readOnly: true,
|
|
@@ -35,22 +36,80 @@ export const SCRIPT_PACKS = [
|
|
|
35
36
|
reportSchemaFile: 'code-outline-report.schema.json',
|
|
36
37
|
loadRunner: async () => (await import('../script-packs/code-outline.js')).runCodeOutlineScript,
|
|
37
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: 'change-impact',
|
|
71
|
+
ref: scriptRef('code', 'change-impact'),
|
|
72
|
+
usage: 'mf script-pack run code/change-impact analyze [path...] [options]',
|
|
73
|
+
summaryKey: 'scriptPack.script.codeChangeImpact.summary',
|
|
74
|
+
actions: ['analyze'],
|
|
75
|
+
useWhen: [
|
|
76
|
+
'Analyze current git changes and produce bounded file, script-pack, and verification hints after a change.',
|
|
77
|
+
'Classify changed source, test, docs, schema, package, template, and workflow surfaces before choosing follow-up checks.',
|
|
78
|
+
],
|
|
79
|
+
phases: ['after_change', 'review'],
|
|
80
|
+
readOnly: true,
|
|
81
|
+
mutates: false,
|
|
82
|
+
network: false,
|
|
83
|
+
inputs: ['path', 'base_ref', 'head_ref', 'max_files', 'max_impacts', 'max_file_bytes'],
|
|
84
|
+
outputs: ['human_summary', 'json_report', 'changed_files', 'impact_candidates', 'verification_hints'],
|
|
85
|
+
relatedSkills: [
|
|
86
|
+
'change-blast-radius-review',
|
|
87
|
+
'completion-evidence-gate',
|
|
88
|
+
'javascript-code-change',
|
|
89
|
+
'public-json-contract-change',
|
|
90
|
+
'typescript-code-change',
|
|
91
|
+
],
|
|
92
|
+
riskLevel: 'low',
|
|
93
|
+
cost: 'low',
|
|
94
|
+
reportSchemaFile: 'change-impact-report.schema.json',
|
|
95
|
+
loadRunner: async () => (await import('../script-packs/code-change-impact.js')).runCodeChangeImpactScript,
|
|
96
|
+
},
|
|
38
97
|
{
|
|
39
98
|
packId: 'code',
|
|
40
99
|
id: 'symbol-read',
|
|
41
100
|
ref: scriptRef('code', 'symbol-read'),
|
|
42
|
-
usage: 'mf script-pack run code/symbol-read read <path> --start-line <line> [options]',
|
|
101
|
+
usage: 'mf script-pack run code/symbol-read read (<path> --start-line <line> | --anchor <id>) [options]',
|
|
43
102
|
summaryKey: 'scriptPack.script.codeSymbolRead.summary',
|
|
44
103
|
actions: ['read'],
|
|
45
104
|
useWhen: [
|
|
46
|
-
'Read only the resolved symbol range or
|
|
47
|
-
'Fetch a focused source snippet with path, line, symbol, and content-hash metadata instead of repeatedly paging through a whole file.',
|
|
105
|
+
'Read only the resolved symbol range, source-anchor target, or explicit bounded line range after a code outline identifies the relevant location.',
|
|
106
|
+
'Fetch a focused source snippet with path, anchor, line, symbol, and content-hash metadata instead of repeatedly paging through a whole file.',
|
|
48
107
|
],
|
|
49
108
|
phases: ['before_change', 'during_change', 'review'],
|
|
50
109
|
readOnly: true,
|
|
51
110
|
mutates: false,
|
|
52
111
|
network: false,
|
|
53
|
-
inputs: ['path', 'start_line', 'end_line', 'context_lines', 'max_file_bytes', 'max_snippet_lines'],
|
|
112
|
+
inputs: ['path', 'anchor_id', 'start_line', 'end_line', 'context_lines', 'max_file_bytes', 'max_snippet_lines'],
|
|
54
113
|
outputs: ['human_summary', 'json_report', 'source_snippet'],
|
|
55
114
|
relatedSkills: [
|
|
56
115
|
'codebase-orientation',
|
|
@@ -64,6 +123,66 @@ export const SCRIPT_PACKS = [
|
|
|
64
123
|
reportSchemaFile: 'code-symbol-read-report.schema.json',
|
|
65
124
|
loadRunner: async () => (await import('../script-packs/code-outline.js')).runCodeSymbolReadScript,
|
|
66
125
|
},
|
|
126
|
+
{
|
|
127
|
+
packId: 'code',
|
|
128
|
+
id: 'route-outline',
|
|
129
|
+
ref: scriptRef('code', 'route-outline'),
|
|
130
|
+
usage: 'mf script-pack run code/route-outline scan <path...> [options]',
|
|
131
|
+
summaryKey: 'scriptPack.script.codeRouteOutline.summary',
|
|
132
|
+
actions: ['scan'],
|
|
133
|
+
useWhen: [
|
|
134
|
+
'Scan Hono, Elysia, Axum, and NestJS route source files for method, path, framework, lifecycle, handler, and line metadata before reading whole modules.',
|
|
135
|
+
'Build a bounded first-pass route outline with paths, line ranges, lifecycle calls, and content hashes for HTTP route orientation.',
|
|
136
|
+
],
|
|
137
|
+
phases: ['before_change', 'during_change', 'after_change', 'review'],
|
|
138
|
+
readOnly: true,
|
|
139
|
+
mutates: false,
|
|
140
|
+
network: false,
|
|
141
|
+
inputs: ['path', 'max_files', 'max_file_bytes'],
|
|
142
|
+
outputs: ['human_summary', 'json_report', 'route_outline', 'route_lifecycle'],
|
|
143
|
+
relatedSkills: [
|
|
144
|
+
'api-contract-change',
|
|
145
|
+
'backend-reliability-change',
|
|
146
|
+
'axum-code-change',
|
|
147
|
+
'elysia-code-change',
|
|
148
|
+
'hono-code-change',
|
|
149
|
+
'nestjs-code-change',
|
|
150
|
+
'http-delivery-streaming',
|
|
151
|
+
],
|
|
152
|
+
riskLevel: 'low',
|
|
153
|
+
cost: 'low',
|
|
154
|
+
reportSchemaFile: 'route-outline-report.schema.json',
|
|
155
|
+
loadRunner: async () => (await import('../script-packs/code-route-outline.js')).runCodeRouteOutlineScript,
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
packId: 'code',
|
|
159
|
+
id: 'export-diff',
|
|
160
|
+
ref: scriptRef('code', 'export-diff'),
|
|
161
|
+
usage: 'mf script-pack run code/export-diff compare [path...] [options]',
|
|
162
|
+
summaryKey: 'scriptPack.script.codeExportDiff.summary',
|
|
163
|
+
actions: ['compare'],
|
|
164
|
+
useWhen: [
|
|
165
|
+
'Compare exported TypeScript and JavaScript declaration signatures, return metadata, and package surface hints across a git base and head.',
|
|
166
|
+
'Review public-ish API changes after source edits without reading every changed file from top to bottom.',
|
|
167
|
+
],
|
|
168
|
+
phases: ['after_change', 'review'],
|
|
169
|
+
readOnly: true,
|
|
170
|
+
mutates: false,
|
|
171
|
+
network: false,
|
|
172
|
+
inputs: ['base_ref', 'head_ref', 'path', 'max_files', 'max_file_bytes'],
|
|
173
|
+
outputs: ['human_summary', 'json_report', 'export_diff', 'return_type_changes', 'package_surface'],
|
|
174
|
+
relatedSkills: [
|
|
175
|
+
'api-contract-change',
|
|
176
|
+
'dependency-upgrade-review',
|
|
177
|
+
'javascript-code-change',
|
|
178
|
+
'public-json-contract-change',
|
|
179
|
+
'typescript-code-change',
|
|
180
|
+
],
|
|
181
|
+
riskLevel: 'low',
|
|
182
|
+
cost: 'low',
|
|
183
|
+
reportSchemaFile: 'export-diff-report.schema.json',
|
|
184
|
+
loadRunner: async () => (await import('../script-packs/code-export-diff.js')).runCodeExportDiffScript,
|
|
185
|
+
},
|
|
67
186
|
],
|
|
68
187
|
},
|
|
69
188
|
{
|
|
@@ -100,10 +219,130 @@ export const SCRIPT_PACKS = [
|
|
|
100
219
|
},
|
|
101
220
|
],
|
|
102
221
|
},
|
|
222
|
+
{
|
|
223
|
+
id: 'docs',
|
|
224
|
+
summaryKey: 'scriptPack.pack.docs.summary',
|
|
225
|
+
scripts: [
|
|
226
|
+
{
|
|
227
|
+
packId: 'docs',
|
|
228
|
+
id: 'reference-drift',
|
|
229
|
+
ref: scriptRef('docs', 'reference-drift'),
|
|
230
|
+
usage: 'mf script-pack run docs/reference-drift check [path...] [options]',
|
|
231
|
+
summaryKey: 'scriptPack.script.referenceDrift.summary',
|
|
232
|
+
actions: ['check'],
|
|
233
|
+
useWhen: [
|
|
234
|
+
'Check documentation references to mf commands, script-pack refs, schema files, and repository paths against current local surfaces.',
|
|
235
|
+
'Review docs after CLI, schema, script-pack, or repository structure changes before claiming references are synchronized.',
|
|
236
|
+
],
|
|
237
|
+
phases: ['after_change', 'review'],
|
|
238
|
+
readOnly: true,
|
|
239
|
+
mutates: false,
|
|
240
|
+
network: false,
|
|
241
|
+
inputs: ['path', 'max_files', 'max_file_bytes'],
|
|
242
|
+
outputs: ['human_summary', 'json_report', 'reference_drift', 'stale_reference_findings'],
|
|
243
|
+
relatedSkills: [
|
|
244
|
+
'cli-output-contract-review',
|
|
245
|
+
'docs-prose-review',
|
|
246
|
+
'public-json-contract-change',
|
|
247
|
+
'readme-authoring',
|
|
248
|
+
'release-notes-authoring',
|
|
249
|
+
],
|
|
250
|
+
riskLevel: 'low',
|
|
251
|
+
cost: 'low',
|
|
252
|
+
reportSchemaFile: 'reference-drift-report.schema.json',
|
|
253
|
+
loadRunner: async () => (await import('../script-packs/docs-reference-drift.js')).runDocsReferenceDriftScript,
|
|
254
|
+
},
|
|
255
|
+
],
|
|
256
|
+
},
|
|
103
257
|
{
|
|
104
258
|
id: 'repo',
|
|
105
259
|
summaryKey: 'scriptPack.pack.repo.summary',
|
|
106
260
|
scripts: [
|
|
261
|
+
{
|
|
262
|
+
packId: 'repo',
|
|
263
|
+
id: 'config-chain',
|
|
264
|
+
ref: scriptRef('repo', 'config-chain'),
|
|
265
|
+
usage: 'mf script-pack run repo/config-chain inspect <path...> [options]',
|
|
266
|
+
summaryKey: 'scriptPack.script.configChain.summary',
|
|
267
|
+
actions: ['inspect'],
|
|
268
|
+
useWhen: [
|
|
269
|
+
'Inspect nearby tsconfig, package, ESLint, Prettier, Vite, Vitest, Tailwind, Jest, and Playwright config files before assuming effective rules.',
|
|
270
|
+
'Build a read-only config chain with extends, references, workspaces, dynamic-config findings, and source path context.',
|
|
271
|
+
],
|
|
272
|
+
phases: ['before_change', 'during_change', 'after_change', 'review'],
|
|
273
|
+
readOnly: true,
|
|
274
|
+
mutates: false,
|
|
275
|
+
network: false,
|
|
276
|
+
inputs: ['path', 'max_configs', 'max_file_bytes'],
|
|
277
|
+
outputs: ['human_summary', 'json_report', 'config_chain', 'config_edges'],
|
|
278
|
+
relatedSkills: [
|
|
279
|
+
'bun-code-change',
|
|
280
|
+
'config-env-change',
|
|
281
|
+
'dependency-reality-check',
|
|
282
|
+
'tailwind-code-change',
|
|
283
|
+
'typescript-code-change',
|
|
284
|
+
],
|
|
285
|
+
riskLevel: 'low',
|
|
286
|
+
cost: 'low',
|
|
287
|
+
reportSchemaFile: 'config-chain-report.schema.json',
|
|
288
|
+
loadRunner: async () => (await import('../script-packs/repo-config-chain.js')).runRepoConfigChainScript,
|
|
289
|
+
},
|
|
290
|
+
{
|
|
291
|
+
packId: 'repo',
|
|
292
|
+
id: 'env-contract',
|
|
293
|
+
ref: scriptRef('repo', 'env-contract'),
|
|
294
|
+
usage: 'mf script-pack run repo/env-contract scan [path...] [options]',
|
|
295
|
+
summaryKey: 'scriptPack.script.envContract.summary',
|
|
296
|
+
actions: ['scan'],
|
|
297
|
+
useWhen: [
|
|
298
|
+
'Scan code, CI, docs, and env example files for environment variable contract drift without reading real secret env files.',
|
|
299
|
+
'Review env keys that are used in code, declared in examples, referenced in CI, documented, missing from examples, or public-prefix secret-like names.',
|
|
300
|
+
],
|
|
301
|
+
phases: ['before_change', 'during_change', 'after_change', 'review'],
|
|
302
|
+
readOnly: true,
|
|
303
|
+
mutates: false,
|
|
304
|
+
network: false,
|
|
305
|
+
inputs: ['path', 'max_files', 'max_file_bytes', 'max_keys'],
|
|
306
|
+
outputs: ['human_summary', 'json_report', 'env_keys', 'env_contract_findings'],
|
|
307
|
+
relatedSkills: [
|
|
308
|
+
'config-env-change',
|
|
309
|
+
'public-json-contract-change',
|
|
310
|
+
'security-privacy-review',
|
|
311
|
+
'typescript-code-change',
|
|
312
|
+
],
|
|
313
|
+
riskLevel: 'low',
|
|
314
|
+
cost: 'low',
|
|
315
|
+
reportSchemaFile: 'env-contract-report.schema.json',
|
|
316
|
+
loadRunner: async () => (await import('../script-packs/repo-env-contract.js')).runRepoEnvContractScript,
|
|
317
|
+
},
|
|
318
|
+
{
|
|
319
|
+
packId: 'repo',
|
|
320
|
+
id: 'secret-risk-scan',
|
|
321
|
+
ref: scriptRef('repo', 'secret-risk-scan'),
|
|
322
|
+
usage: 'mf script-pack run repo/secret-risk-scan scan [path...] [options]',
|
|
323
|
+
summaryKey: 'scriptPack.script.secretRiskScan.summary',
|
|
324
|
+
actions: ['scan'],
|
|
325
|
+
useWhen: [
|
|
326
|
+
'Scan code, docs, config, CI, and examples for plausible hardcoded secrets without printing secret values.',
|
|
327
|
+
'Review provider token prefixes, Bearer tokens, private key markers, secret-like assignments, and realistic env-example values.',
|
|
328
|
+
],
|
|
329
|
+
phases: ['before_change', 'during_change', 'after_change', 'review'],
|
|
330
|
+
readOnly: true,
|
|
331
|
+
mutates: false,
|
|
332
|
+
network: false,
|
|
333
|
+
inputs: ['path', 'max_files', 'max_file_bytes', 'max_findings'],
|
|
334
|
+
outputs: ['human_summary', 'json_report', 'secret_risk_findings', 'redacted_fingerprints'],
|
|
335
|
+
relatedSkills: [
|
|
336
|
+
'config-env-change',
|
|
337
|
+
'public-json-contract-change',
|
|
338
|
+
'security-privacy-review',
|
|
339
|
+
'typescript-code-change',
|
|
340
|
+
],
|
|
341
|
+
riskLevel: 'medium',
|
|
342
|
+
cost: 'low',
|
|
343
|
+
reportSchemaFile: 'secret-risk-scan-report.schema.json',
|
|
344
|
+
loadRunner: async () => (await import('../script-packs/repo-secret-risk-scan.js')).runRepoSecretRiskScanScript,
|
|
345
|
+
},
|
|
107
346
|
{
|
|
108
347
|
packId: 'repo',
|
|
109
348
|
id: 'generated-boundary',
|
|
@@ -133,6 +372,36 @@ export const SCRIPT_PACKS = [
|
|
|
133
372
|
reportSchemaFile: 'generated-boundary-report.schema.json',
|
|
134
373
|
loadRunner: async () => (await import('../script-packs/repo-generated-boundary.js')).runRepoGeneratedBoundaryScript,
|
|
135
374
|
},
|
|
375
|
+
{
|
|
376
|
+
packId: 'repo',
|
|
377
|
+
id: 'related-files',
|
|
378
|
+
ref: scriptRef('repo', 'related-files'),
|
|
379
|
+
usage: 'mf script-pack run repo/related-files map <path...> [options]',
|
|
380
|
+
summaryKey: 'scriptPack.script.relatedFiles.summary',
|
|
381
|
+
actions: ['map'],
|
|
382
|
+
useWhen: [
|
|
383
|
+
'Map direct imports, importers, sibling tests, sibling docs, sibling styles, type siblings, ' +
|
|
384
|
+
'and nearby config files for a source path before widening context reads.',
|
|
385
|
+
'Review likely adjacent files after a partial implementation without treating the result as verification scope or completeness proof.',
|
|
386
|
+
],
|
|
387
|
+
phases: ['before_change', 'during_change', 'after_change', 'review'],
|
|
388
|
+
readOnly: true,
|
|
389
|
+
mutates: false,
|
|
390
|
+
network: false,
|
|
391
|
+
inputs: ['path', 'max_files', 'max_file_bytes', 'max_candidates'],
|
|
392
|
+
outputs: ['human_summary', 'json_report', 'related_file_candidates'],
|
|
393
|
+
relatedSkills: [
|
|
394
|
+
'codebase-orientation',
|
|
395
|
+
'heuristic-candidate-selection',
|
|
396
|
+
'module-boundary-review',
|
|
397
|
+
'typescript-code-change',
|
|
398
|
+
'javascript-code-change',
|
|
399
|
+
],
|
|
400
|
+
riskLevel: 'low',
|
|
401
|
+
cost: 'low',
|
|
402
|
+
reportSchemaFile: 'related-files-report.schema.json',
|
|
403
|
+
loadRunner: async () => (await import('../script-packs/repo-related-files.js')).runRepoRelatedFilesScript,
|
|
404
|
+
},
|
|
136
405
|
],
|
|
137
406
|
},
|
|
138
407
|
];
|
|
@@ -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 {
|
|
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 =
|
|
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 =
|
|
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,172 @@
|
|
|
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
|
+
}
|
|
130
|
+
}
|
|
131
|
+
if (report.verification_hints.length > 0) {
|
|
132
|
+
lines.push(t(lang, 'changeImpact.label.verificationHints'));
|
|
133
|
+
for (const hint of report.verification_hints) {
|
|
134
|
+
lines.push(`- ${hint.intent}: ${hint.reason}`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
if (report.findings.length > 0) {
|
|
138
|
+
lines.push(t(lang, 'changeImpact.label.findings'), ...report.findings.map((finding) => `- ${finding.path}: ${finding.code} (${finding.message})`));
|
|
139
|
+
}
|
|
140
|
+
if (report.issues.length > 0) {
|
|
141
|
+
lines.push(t(lang, 'changeImpact.label.issues'), ...report.issues.map((issue) => `- ${issue}`));
|
|
142
|
+
}
|
|
143
|
+
if (report.changed_files.length === 0 && report.findings.length === 0 && report.issues.length === 0) {
|
|
144
|
+
lines.push(t(lang, 'changeImpact.clean'));
|
|
145
|
+
}
|
|
146
|
+
return lines.join('\n');
|
|
147
|
+
}
|
|
148
|
+
export function runCodeChangeImpactScript(args, reporter, lang = 'en') {
|
|
149
|
+
if (hasCliOptionToken(args, '--help', ['-h'])) {
|
|
150
|
+
reporter.stdout(getCodeChangeImpactHelp(lang));
|
|
151
|
+
return 0;
|
|
152
|
+
}
|
|
153
|
+
const options = parseChangeImpactOptions(args, lang);
|
|
154
|
+
if (options.error) {
|
|
155
|
+
printUsageError(reporter, options.error, 'mf script-pack run code/change-impact --help', getCodeChangeImpactHelp(lang), lang);
|
|
156
|
+
return 1;
|
|
157
|
+
}
|
|
158
|
+
const report = inspectChangeImpact(resolveMustflowRoot(), {
|
|
159
|
+
baseRef: options.baseRef ?? undefined,
|
|
160
|
+
headRef: options.headRef,
|
|
161
|
+
paths: options.paths,
|
|
162
|
+
maxFiles: options.maxFiles ?? undefined,
|
|
163
|
+
maxImpacts: options.maxImpacts ?? undefined,
|
|
164
|
+
maxFileBytes: options.maxFileBytes ?? undefined,
|
|
165
|
+
});
|
|
166
|
+
if (options.json) {
|
|
167
|
+
reporter.stdout(JSON.stringify(report, null, 2));
|
|
168
|
+
return report.ok ? 0 : 1;
|
|
169
|
+
}
|
|
170
|
+
reporter.stdout(renderChangeImpactSummary(report, lang));
|
|
171
|
+
return report.ok ? 0 : 1;
|
|
172
|
+
}
|