mustflow 2.103.15 → 2.103.20

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 (33) hide show
  1. package/README.md +2 -0
  2. package/dist/cli/commands/run/args.js +83 -0
  3. package/dist/cli/commands/run/execution.js +334 -0
  4. package/dist/cli/commands/run/preview.js +29 -0
  5. package/dist/cli/commands/run/profile.js +6 -0
  6. package/dist/cli/commands/run.js +19 -425
  7. package/dist/cli/commands/script-pack.js +1 -0
  8. package/dist/cli/commands/verify.js +15 -18
  9. package/dist/cli/i18n/en.js +27 -0
  10. package/dist/cli/i18n/es.js +27 -0
  11. package/dist/cli/i18n/fr.js +27 -0
  12. package/dist/cli/i18n/hi.js +27 -0
  13. package/dist/cli/i18n/ko.js +27 -0
  14. package/dist/cli/i18n/zh.js +27 -0
  15. package/dist/cli/lib/command-registry.js +92 -0
  16. package/dist/cli/lib/script-pack-registry.js +39 -0
  17. package/dist/cli/script-packs/code-module-boundary.js +210 -0
  18. package/dist/core/module-boundary.js +523 -0
  19. package/dist/core/public-json-contracts.js +50 -0
  20. package/dist/core/script-pack-suggestions.js +5 -0
  21. package/package.json +1 -1
  22. package/schemas/README.md +12 -0
  23. package/schemas/check-report.schema.json +52 -0
  24. package/schemas/index-report.schema.json +103 -0
  25. package/schemas/module-boundary-report.schema.json +210 -0
  26. package/schemas/search-report.schema.json +102 -0
  27. package/schemas/status-report.schema.json +50 -0
  28. package/templates/default/i18n.toml +4 -4
  29. package/templates/default/locales/en/.mustflow/skills/database-migration-change/SKILL.md +16 -2
  30. package/templates/default/locales/en/.mustflow/skills/module-boundary-review/SKILL.md +12 -1
  31. package/templates/default/locales/en/.mustflow/skills/next-action-menu/SKILL.md +8 -6
  32. package/templates/default/locales/en/.mustflow/skills/payment-integrity-review/SKILL.md +17 -10
  33. package/templates/default/manifest.toml +1 -1
@@ -0,0 +1,210 @@
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 { inspectModuleBoundaries, MODULE_BOUNDARY_SCRIPT_REF, } from '../../core/module-boundary.js';
6
+ const MODULE_BOUNDARY_OPTIONS = [
7
+ { name: '--json', kind: 'boolean' },
8
+ { name: '--config', kind: 'string' },
9
+ { name: '--max-files', kind: 'string' },
10
+ { name: '--max-file-bytes', kind: 'string' },
11
+ { name: '--max-depth', kind: 'string' },
12
+ { name: '--max-nodes', kind: 'string' },
13
+ { name: '--max-edges', kind: 'string' },
14
+ { name: '--max-cycles', kind: 'string' },
15
+ { name: '--max-shared-files', kind: 'string' },
16
+ ];
17
+ function parsePositiveInteger(value, option, lang) {
18
+ if (value === null) {
19
+ return { value: null };
20
+ }
21
+ if (!/^[1-9]\d*$/u.test(value)) {
22
+ return { value: null, error: t(lang, 'moduleBoundary.error.invalidPositiveInteger', { option, value }) };
23
+ }
24
+ const parsed = Number(value);
25
+ if (!Number.isSafeInteger(parsed)) {
26
+ return { value: null, error: t(lang, 'moduleBoundary.error.invalidPositiveInteger', { option, value }) };
27
+ }
28
+ return { value: parsed };
29
+ }
30
+ export function getCodeModuleBoundaryHelp(lang = 'en') {
31
+ return renderHelp({
32
+ usage: 'mf script-pack run code/module-boundary check <path...> [options]',
33
+ summary: t(lang, 'moduleBoundary.help.summary'),
34
+ options: [
35
+ { label: '--config <path>', description: t(lang, 'moduleBoundary.help.option.config') },
36
+ { label: '--max-depth <count>', description: t(lang, 'moduleBoundary.help.option.maxDepth') },
37
+ { label: '--max-files <count>', description: t(lang, 'moduleBoundary.help.option.maxFiles') },
38
+ { label: '--max-file-bytes <bytes>', description: t(lang, 'moduleBoundary.help.option.maxFileBytes') },
39
+ { label: '--max-nodes <count>', description: t(lang, 'moduleBoundary.help.option.maxNodes') },
40
+ { label: '--max-edges <count>', description: t(lang, 'moduleBoundary.help.option.maxEdges') },
41
+ { label: '--max-cycles <count>', description: t(lang, 'moduleBoundary.help.option.maxCycles') },
42
+ { label: '--max-shared-files <count>', description: t(lang, 'moduleBoundary.help.option.maxSharedFiles') },
43
+ { label: '--json', description: t(lang, 'cli.option.json') },
44
+ { label: '-h, --help', description: t(lang, 'cli.option.help') },
45
+ ],
46
+ examples: [
47
+ 'mf script-pack run code/module-boundary check src --json',
48
+ 'mf script-pack run code/module-boundary check src --config .mustflow/config/module-boundaries.toml --json',
49
+ 'mf script-pack run code/module-boundary check src/features --max-depth 30 --json',
50
+ ],
51
+ exitCodes: [
52
+ { label: '0', description: t(lang, 'moduleBoundary.help.exit.ok') },
53
+ { label: '1', description: t(lang, 'moduleBoundary.help.exit.fail') },
54
+ ],
55
+ }, lang);
56
+ }
57
+ function parseModuleBoundaryOptions(args, lang) {
58
+ const [action, ...rest] = args;
59
+ const parsed = parseCliOptions(rest, MODULE_BOUNDARY_OPTIONS, { allowPositionals: true });
60
+ const json = hasParsedCliOption(parsed, '--json');
61
+ const configPath = getParsedCliStringOption(parsed, '--config');
62
+ const maxFiles = parsePositiveInteger(getParsedCliStringOption(parsed, '--max-files'), '--max-files', lang);
63
+ const maxFileBytes = parsePositiveInteger(getParsedCliStringOption(parsed, '--max-file-bytes'), '--max-file-bytes', lang);
64
+ const maxDepth = parsePositiveInteger(getParsedCliStringOption(parsed, '--max-depth'), '--max-depth', lang);
65
+ const maxNodes = parsePositiveInteger(getParsedCliStringOption(parsed, '--max-nodes'), '--max-nodes', lang);
66
+ const maxEdges = parsePositiveInteger(getParsedCliStringOption(parsed, '--max-edges'), '--max-edges', lang);
67
+ const maxCycles = parsePositiveInteger(getParsedCliStringOption(parsed, '--max-cycles'), '--max-cycles', lang);
68
+ const maxSharedFiles = parsePositiveInteger(getParsedCliStringOption(parsed, '--max-shared-files'), '--max-shared-files', lang);
69
+ const positiveOptions = [maxFiles, maxFileBytes, maxDepth, maxNodes, maxEdges, maxCycles, maxSharedFiles];
70
+ if (action !== 'check') {
71
+ return {
72
+ action: 'check',
73
+ json,
74
+ paths: parsed.positionals,
75
+ configPath,
76
+ maxFiles: maxFiles.value,
77
+ maxFileBytes: maxFileBytes.value,
78
+ maxDepth: maxDepth.value,
79
+ maxNodes: maxNodes.value,
80
+ maxEdges: maxEdges.value,
81
+ maxCycles: maxCycles.value,
82
+ maxSharedFiles: maxSharedFiles.value,
83
+ error: action ? t(lang, 'moduleBoundary.error.unknownAction', { action }) : t(lang, 'moduleBoundary.error.missingAction'),
84
+ };
85
+ }
86
+ if (parsed.error) {
87
+ return {
88
+ action,
89
+ json,
90
+ paths: parsed.positionals,
91
+ configPath,
92
+ maxFiles: maxFiles.value,
93
+ maxFileBytes: maxFileBytes.value,
94
+ maxDepth: maxDepth.value,
95
+ maxNodes: maxNodes.value,
96
+ maxEdges: maxEdges.value,
97
+ maxCycles: maxCycles.value,
98
+ maxSharedFiles: maxSharedFiles.value,
99
+ error: formatCliOptionParseError(parsed.error, lang),
100
+ };
101
+ }
102
+ for (const candidate of positiveOptions) {
103
+ if (candidate.error) {
104
+ return {
105
+ action,
106
+ json,
107
+ paths: parsed.positionals,
108
+ configPath,
109
+ maxFiles: maxFiles.value,
110
+ maxFileBytes: maxFileBytes.value,
111
+ maxDepth: maxDepth.value,
112
+ maxNodes: maxNodes.value,
113
+ maxEdges: maxEdges.value,
114
+ maxCycles: maxCycles.value,
115
+ maxSharedFiles: maxSharedFiles.value,
116
+ error: candidate.error,
117
+ };
118
+ }
119
+ }
120
+ if (parsed.positionals.length === 0) {
121
+ return {
122
+ action,
123
+ json,
124
+ paths: parsed.positionals,
125
+ configPath,
126
+ maxFiles: maxFiles.value,
127
+ maxFileBytes: maxFileBytes.value,
128
+ maxDepth: maxDepth.value,
129
+ maxNodes: maxNodes.value,
130
+ maxEdges: maxEdges.value,
131
+ maxCycles: maxCycles.value,
132
+ maxSharedFiles: maxSharedFiles.value,
133
+ error: t(lang, 'moduleBoundary.error.missingPath'),
134
+ };
135
+ }
136
+ return {
137
+ action,
138
+ json,
139
+ paths: parsed.positionals,
140
+ configPath,
141
+ maxFiles: maxFiles.value,
142
+ maxFileBytes: maxFileBytes.value,
143
+ maxDepth: maxDepth.value,
144
+ maxNodes: maxNodes.value,
145
+ maxEdges: maxEdges.value,
146
+ maxCycles: maxCycles.value,
147
+ maxSharedFiles: maxSharedFiles.value,
148
+ };
149
+ }
150
+ function renderModuleBoundarySummary(report, lang) {
151
+ const lines = [
152
+ t(lang, 'moduleBoundary.title'),
153
+ `${t(lang, 'scriptPack.label.script')}: ${MODULE_BOUNDARY_SCRIPT_REF}`,
154
+ `${t(lang, 'label.status')}: ${report.status}`,
155
+ `${t(lang, 'moduleBoundary.label.config')}: ${report.config.path} (${report.config.status})`,
156
+ `${t(lang, 'moduleBoundary.label.targets')}: ${report.targets.length}`,
157
+ `${t(lang, 'moduleBoundary.label.edges')}: ${report.graph.edge_count}`,
158
+ `${t(lang, 'moduleBoundary.label.rules')}: ${report.rules.length}`,
159
+ `${t(lang, 'moduleBoundary.label.findings')}: ${report.findings.length}`,
160
+ `${t(lang, 'moduleBoundary.label.truncated')}: ${report.truncated ? t(lang, 'value.yes') : t(lang, 'value.no')}`,
161
+ ];
162
+ if (report.findings.length > 0) {
163
+ lines.push(t(lang, 'moduleBoundary.label.findingList'));
164
+ for (const finding of report.findings.slice(0, 80)) {
165
+ const location = finding.line ? `${finding.path}:${finding.line}` : finding.path;
166
+ lines.push(`- ${location}: ${finding.code} (${finding.message})`);
167
+ }
168
+ }
169
+ if (report.shared_metrics.length > 0) {
170
+ lines.push(t(lang, 'moduleBoundary.label.sharedMetrics'));
171
+ for (const metric of report.shared_metrics) {
172
+ lines.push(`- ${metric.path}: ${metric.file_count} files, ${metric.export_count} exports`);
173
+ }
174
+ }
175
+ if (report.issues.length > 0) {
176
+ lines.push(t(lang, 'moduleBoundary.label.issues'), ...report.issues.map((issue) => `- ${issue}`));
177
+ }
178
+ if (report.findings.length === 0 && report.issues.length === 0) {
179
+ lines.push(t(lang, 'moduleBoundary.clean'));
180
+ }
181
+ return lines.join('\n');
182
+ }
183
+ export function runCodeModuleBoundaryScript(args, reporter, lang = 'en') {
184
+ if (hasCliOptionToken(args, '--help', ['-h'])) {
185
+ reporter.stdout(getCodeModuleBoundaryHelp(lang));
186
+ return 0;
187
+ }
188
+ const options = parseModuleBoundaryOptions(args, lang);
189
+ if (options.error) {
190
+ printUsageError(reporter, options.error, 'mf script-pack run code/module-boundary --help', getCodeModuleBoundaryHelp(lang), lang);
191
+ return 1;
192
+ }
193
+ const report = inspectModuleBoundaries(resolveMustflowRoot(), {
194
+ paths: options.paths,
195
+ configPath: options.configPath ?? undefined,
196
+ maxFiles: options.maxFiles ?? undefined,
197
+ maxFileBytes: options.maxFileBytes ?? undefined,
198
+ maxDepth: options.maxDepth ?? undefined,
199
+ maxNodes: options.maxNodes ?? undefined,
200
+ maxEdges: options.maxEdges ?? undefined,
201
+ maxCycles: options.maxCycles ?? undefined,
202
+ maxSharedFiles: options.maxSharedFiles ?? undefined,
203
+ });
204
+ if (options.json) {
205
+ reporter.stdout(JSON.stringify(report, null, 2));
206
+ return report.ok ? 0 : 1;
207
+ }
208
+ reporter.stdout(renderModuleBoundarySummary(report, lang));
209
+ return report.ok ? 0 : 1;
210
+ }