mustflow 2.75.1 → 2.84.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.
- package/README.md +31 -3
- package/dist/cli/commands/docs.js +86 -2
- package/dist/cli/commands/script-pack.js +5 -0
- package/dist/cli/i18n/en.js +101 -2
- package/dist/cli/i18n/es.js +101 -2
- package/dist/cli/i18n/fr.js +101 -2
- package/dist/cli/i18n/hi.js +101 -2
- package/dist/cli/i18n/ko.js +101 -2
- package/dist/cli/i18n/zh.js +101 -2
- package/dist/cli/lib/script-pack-registry.js +162 -7
- 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-related-files.js +161 -0
- package/dist/core/code-outline.js +527 -80
- package/dist/core/config-chain.js +595 -0
- package/dist/core/export-diff.js +359 -0
- package/dist/core/public-json-contracts.js +75 -0
- package/dist/core/reference-drift.js +388 -0
- package/dist/core/related-files.js +493 -0
- package/dist/core/route-outline.js +912 -0
- package/dist/core/script-pack-suggestions.js +111 -5
- package/dist/core/source-anchors.js +13 -1
- package/package.json +1 -1
- package/schemas/README.md +28 -5
- package/schemas/code-outline-report.schema.json +47 -1
- package/schemas/code-symbol-read-report.schema.json +64 -4
- package/schemas/config-chain-report.schema.json +187 -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/templates/default/common/.mustflow/config/commands.toml +21 -0
- package/templates/default/i18n.toml +7 -1
- package/templates/default/locales/en/.mustflow/docs/agent-workflow.md +1 -1
- package/templates/default/locales/en/.mustflow/skills/INDEX.md +2 -1
- package/templates/default/locales/en/.mustflow/skills/cross-agent-session-reference/SKILL.md +131 -0
- package/templates/default/locales/en/.mustflow/skills/routes.toml +6 -0
- package/templates/default/manifest.toml +8 -1
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import { createHash } from 'node:crypto';
|
|
3
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { extractSymbols, languageForPath } from './code-outline.js';
|
|
6
|
+
import { ensureInsideWithoutSymlinks, readFileInsideWithoutSymlinks } from './safe-filesystem.js';
|
|
7
|
+
export const CODE_EXPORT_DIFF_SCRIPT_ID = 'export-diff';
|
|
8
|
+
export const CODE_EXPORT_DIFF_SCRIPT_REF = `code/${CODE_EXPORT_DIFF_SCRIPT_ID}`;
|
|
9
|
+
const DEFAULT_BASE_REF = 'HEAD';
|
|
10
|
+
const DEFAULT_MAX_FILES = 100;
|
|
11
|
+
const DEFAULT_MAX_FILE_BYTES = 1024 * 1024;
|
|
12
|
+
const SUPPORTED_EXTENSIONS = ['.ts', '.tsx', '.mts', '.cts', '.js', '.jsx', '.mjs', '.cjs'];
|
|
13
|
+
const IGNORED_DIRECTORIES = ['.git', 'node_modules', 'dist', 'build', 'coverage', '.mustflow/cache', '.mustflow/state'];
|
|
14
|
+
const ERROR_CODES = new Set([
|
|
15
|
+
'export_diff_git_unavailable',
|
|
16
|
+
'export_diff_invalid_ref',
|
|
17
|
+
'export_diff_unreadable_path',
|
|
18
|
+
'export_diff_file_too_large',
|
|
19
|
+
'export_diff_max_files_exceeded',
|
|
20
|
+
]);
|
|
21
|
+
function toPosixPath(value) {
|
|
22
|
+
return value.replace(/\\/gu, '/');
|
|
23
|
+
}
|
|
24
|
+
function normalizeRelativePath(value) {
|
|
25
|
+
return toPosixPath(value).replace(/^\.\/+/u, '') || '.';
|
|
26
|
+
}
|
|
27
|
+
function sha256Tagged(value) {
|
|
28
|
+
return `sha256:${createHash('sha256').update(value).digest('hex')}`;
|
|
29
|
+
}
|
|
30
|
+
function runGit(root, args) {
|
|
31
|
+
const result = spawnSync('git', [...args], {
|
|
32
|
+
cwd: root,
|
|
33
|
+
encoding: 'utf8',
|
|
34
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
35
|
+
windowsHide: true,
|
|
36
|
+
maxBuffer: 16 * 1024 * 1024,
|
|
37
|
+
});
|
|
38
|
+
return {
|
|
39
|
+
ok: result.status === 0,
|
|
40
|
+
stdout: result.stdout ?? '',
|
|
41
|
+
stderr: result.stderr ?? '',
|
|
42
|
+
status: result.status,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function makeFinding(code, severity, pathValue, message) {
|
|
46
|
+
return { code, severity, path: pathValue, message };
|
|
47
|
+
}
|
|
48
|
+
function isIgnoredPath(relativePath) {
|
|
49
|
+
const normalized = normalizeRelativePath(relativePath);
|
|
50
|
+
return IGNORED_DIRECTORIES.some((directory) => normalized === directory || normalized.startsWith(`${directory}/`));
|
|
51
|
+
}
|
|
52
|
+
function isSupportedPath(relativePath) {
|
|
53
|
+
return SUPPORTED_EXTENSIONS.includes(path.extname(relativePath).toLowerCase());
|
|
54
|
+
}
|
|
55
|
+
function splitGitPaths(stdout) {
|
|
56
|
+
return stdout
|
|
57
|
+
.split(/\r?\n/u)
|
|
58
|
+
.map((entry) => normalizeRelativePath(entry.trim()))
|
|
59
|
+
.filter((entry) => entry.length > 0 && entry !== '.');
|
|
60
|
+
}
|
|
61
|
+
function collectChangedPaths(root, policy, findings, issues) {
|
|
62
|
+
const diffArgs = policy.head_ref
|
|
63
|
+
? ['diff', '--name-only', '--diff-filter=ACMRTD', policy.base_ref, policy.head_ref, '--', ...policy.path_filters]
|
|
64
|
+
: ['diff', '--name-only', '--diff-filter=ACMRTD', policy.base_ref, '--', ...policy.path_filters];
|
|
65
|
+
const diff = runGit(root, diffArgs);
|
|
66
|
+
if (!diff.ok) {
|
|
67
|
+
const detail = diff.stderr.trim() || diff.stdout.trim() || `git diff exited with ${diff.status}`;
|
|
68
|
+
issues.push(detail);
|
|
69
|
+
findings.push(makeFinding('export_diff_invalid_ref', 'high', '.', detail));
|
|
70
|
+
return [];
|
|
71
|
+
}
|
|
72
|
+
const changed = new Set(splitGitPaths(diff.stdout));
|
|
73
|
+
if (!policy.head_ref) {
|
|
74
|
+
const untracked = runGit(root, ['ls-files', '--others', '--exclude-standard', '--', ...policy.path_filters]);
|
|
75
|
+
if (untracked.ok) {
|
|
76
|
+
for (const entry of splitGitPaths(untracked.stdout)) {
|
|
77
|
+
changed.add(entry);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
const paths = [...changed]
|
|
82
|
+
.filter((entry) => isSupportedPath(entry) && !isIgnoredPath(entry))
|
|
83
|
+
.sort((left, right) => left.localeCompare(right));
|
|
84
|
+
if (paths.length > policy.max_files) {
|
|
85
|
+
const message = `Export diff matched ${paths.length} supported files; max_files is ${policy.max_files}.`;
|
|
86
|
+
issues.push(message);
|
|
87
|
+
findings.push(makeFinding('export_diff_max_files_exceeded', 'high', '.', message));
|
|
88
|
+
}
|
|
89
|
+
return paths.slice(0, policy.max_files);
|
|
90
|
+
}
|
|
91
|
+
function readGitSnapshot(root, ref, relativePath, maxFileBytes) {
|
|
92
|
+
const result = runGit(root, ['show', `${ref}:${relativePath}`]);
|
|
93
|
+
if (!result.ok) {
|
|
94
|
+
return { exists: false, text: null, sha256: null };
|
|
95
|
+
}
|
|
96
|
+
const bytes = Buffer.byteLength(result.stdout, 'utf8');
|
|
97
|
+
if (bytes > maxFileBytes) {
|
|
98
|
+
throw new Error(`${relativePath} at ${ref} has ${bytes} bytes; max_file_bytes is ${maxFileBytes}.`);
|
|
99
|
+
}
|
|
100
|
+
return { exists: true, text: result.stdout, sha256: sha256Tagged(result.stdout) };
|
|
101
|
+
}
|
|
102
|
+
function readWorktreeSnapshot(root, relativePath, maxFileBytes) {
|
|
103
|
+
const absolutePath = path.join(root, ...relativePath.split('/'));
|
|
104
|
+
if (!existsSync(absolutePath)) {
|
|
105
|
+
return { exists: false, text: null, sha256: null };
|
|
106
|
+
}
|
|
107
|
+
ensureInsideWithoutSymlinks(root, absolutePath);
|
|
108
|
+
const buffer = readFileInsideWithoutSymlinks(root, absolutePath, { maxBytes: maxFileBytes });
|
|
109
|
+
return { exists: true, text: buffer.toString('utf8'), sha256: sha256Tagged(buffer) };
|
|
110
|
+
}
|
|
111
|
+
function snapshotSymbols(relativePath, snapshot) {
|
|
112
|
+
if (!snapshot.exists || snapshot.text === null || snapshot.sha256 === null) {
|
|
113
|
+
return [];
|
|
114
|
+
}
|
|
115
|
+
const language = languageForPath(relativePath);
|
|
116
|
+
if (!language) {
|
|
117
|
+
return [];
|
|
118
|
+
}
|
|
119
|
+
return extractSymbols(relativePath, language, snapshot.sha256, snapshot.text)
|
|
120
|
+
.filter((symbol) => symbol.exported)
|
|
121
|
+
.map((symbol) => ({
|
|
122
|
+
path: symbol.path,
|
|
123
|
+
name: symbol.name,
|
|
124
|
+
kind: symbol.kind,
|
|
125
|
+
language: symbol.language,
|
|
126
|
+
signature: symbol.signature,
|
|
127
|
+
return_type: symbol.return_type,
|
|
128
|
+
return_behavior: symbol.return_behavior,
|
|
129
|
+
async: symbol.async,
|
|
130
|
+
line: symbol.start_line,
|
|
131
|
+
}));
|
|
132
|
+
}
|
|
133
|
+
function symbolKey(symbol) {
|
|
134
|
+
return `${symbol.path}:${symbol.name}`;
|
|
135
|
+
}
|
|
136
|
+
function buildSymbolMap(symbols, findings, issues) {
|
|
137
|
+
const map = new Map();
|
|
138
|
+
for (const symbol of symbols) {
|
|
139
|
+
const key = symbolKey(symbol);
|
|
140
|
+
if (map.has(key)) {
|
|
141
|
+
const message = `Multiple exported symbols named ${symbol.name} were found in ${symbol.path}; export diff kept the first match.`;
|
|
142
|
+
issues.push(message);
|
|
143
|
+
findings.push(makeFinding('export_diff_duplicate_export', 'medium', symbol.path, message));
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
map.set(key, symbol);
|
|
147
|
+
}
|
|
148
|
+
return map;
|
|
149
|
+
}
|
|
150
|
+
function hasUnresolvedReexport(text) {
|
|
151
|
+
if (text === null) {
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
return /^\s*export\s+(?:type\s+)?\{[^}]+\}\s+from\s+['"][^'"]+['"]/mu.test(text) || /^\s*export\s+\*\s+from\s+['"][^'"]+['"]/mu.test(text);
|
|
155
|
+
}
|
|
156
|
+
function compareSymbols(before, after) {
|
|
157
|
+
if (before === null && after !== null) {
|
|
158
|
+
return { change: 'added', compatibility: 'additive', kind: after.kind };
|
|
159
|
+
}
|
|
160
|
+
if (before !== null && after === null) {
|
|
161
|
+
return { change: 'removed', compatibility: 'removed_export', kind: before.kind };
|
|
162
|
+
}
|
|
163
|
+
if (before === null || after === null) {
|
|
164
|
+
return { change: 'changed', compatibility: 'unknown', kind: 'function' };
|
|
165
|
+
}
|
|
166
|
+
if (before.kind === after.kind &&
|
|
167
|
+
before.signature === after.signature &&
|
|
168
|
+
before.return_type === after.return_type &&
|
|
169
|
+
before.return_behavior === after.return_behavior &&
|
|
170
|
+
before.async === after.async) {
|
|
171
|
+
return { change: 'unchanged', compatibility: 'unchanged', kind: after.kind };
|
|
172
|
+
}
|
|
173
|
+
if (before.return_type !== after.return_type || before.return_behavior !== after.return_behavior) {
|
|
174
|
+
return { change: 'changed', compatibility: 'return_changed', kind: after.kind };
|
|
175
|
+
}
|
|
176
|
+
return { change: 'changed', compatibility: 'signature_changed', kind: after.kind };
|
|
177
|
+
}
|
|
178
|
+
function packageSurface(root) {
|
|
179
|
+
const packageJsonPath = path.join(root, 'package.json');
|
|
180
|
+
if (!existsSync(packageJsonPath)) {
|
|
181
|
+
return { package_json_present: false, type: null, exports: [], bin: [], types: null };
|
|
182
|
+
}
|
|
183
|
+
try {
|
|
184
|
+
const parsed = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
|
|
185
|
+
const exportsValue = parsed.exports;
|
|
186
|
+
const binValue = parsed.bin;
|
|
187
|
+
return {
|
|
188
|
+
package_json_present: true,
|
|
189
|
+
type: typeof parsed.type === 'string' ? parsed.type : null,
|
|
190
|
+
exports: typeof exportsValue === 'string'
|
|
191
|
+
? ['.']
|
|
192
|
+
: exportsValue && typeof exportsValue === 'object' && !Array.isArray(exportsValue)
|
|
193
|
+
? Object.keys(exportsValue).sort((left, right) => left.localeCompare(right))
|
|
194
|
+
: [],
|
|
195
|
+
bin: typeof binValue === 'string'
|
|
196
|
+
? ['.']
|
|
197
|
+
: binValue && typeof binValue === 'object' && !Array.isArray(binValue)
|
|
198
|
+
? Object.keys(binValue).sort((left, right) => left.localeCompare(right))
|
|
199
|
+
: [],
|
|
200
|
+
types: typeof parsed.types === 'string' ? parsed.types : typeof parsed.typings === 'string' ? parsed.typings : null,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
catch {
|
|
204
|
+
return { package_json_present: true, type: null, exports: [], bin: [], types: null };
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
function createInputHash(policy, files, entries) {
|
|
208
|
+
return sha256Tagged(JSON.stringify({
|
|
209
|
+
policy,
|
|
210
|
+
files: files.map((file) => ({
|
|
211
|
+
path: file.path,
|
|
212
|
+
base_sha256: file.base_sha256,
|
|
213
|
+
head_sha256: file.head_sha256,
|
|
214
|
+
exported_count_before: file.exported_count_before,
|
|
215
|
+
exported_count_after: file.exported_count_after,
|
|
216
|
+
})),
|
|
217
|
+
exports: entries.map((entry) => ({
|
|
218
|
+
id: entry.id,
|
|
219
|
+
change: entry.change,
|
|
220
|
+
compatibility: entry.compatibility,
|
|
221
|
+
before: entry.before?.signature ?? null,
|
|
222
|
+
after: entry.after?.signature ?? null,
|
|
223
|
+
})),
|
|
224
|
+
}));
|
|
225
|
+
}
|
|
226
|
+
function statusFromFindings(findings) {
|
|
227
|
+
return findings.some((finding) => ERROR_CODES.has(finding.code)) ? 'error' : 'passed';
|
|
228
|
+
}
|
|
229
|
+
export function inspectExportDiff(projectRoot, options = {}) {
|
|
230
|
+
const root = path.resolve(projectRoot);
|
|
231
|
+
const policy = {
|
|
232
|
+
base_ref: options.baseRef ?? DEFAULT_BASE_REF,
|
|
233
|
+
head_ref: options.headRef ?? null,
|
|
234
|
+
compare_worktree: !options.headRef,
|
|
235
|
+
max_files: options.maxFiles ?? DEFAULT_MAX_FILES,
|
|
236
|
+
max_file_bytes: options.maxFileBytes ?? DEFAULT_MAX_FILE_BYTES,
|
|
237
|
+
extensions: [...SUPPORTED_EXTENSIONS],
|
|
238
|
+
ignored_directories: [...IGNORED_DIRECTORIES],
|
|
239
|
+
path_filters: [...(options.paths ?? [])].map(normalizeRelativePath),
|
|
240
|
+
};
|
|
241
|
+
const findings = [];
|
|
242
|
+
const issues = [];
|
|
243
|
+
const gitProbe = runGit(root, ['rev-parse', '--is-inside-work-tree']);
|
|
244
|
+
if (!gitProbe.ok || gitProbe.stdout.trim() !== 'true') {
|
|
245
|
+
const detail = gitProbe.stderr.trim() || gitProbe.stdout.trim() || 'Not inside a git work tree.';
|
|
246
|
+
findings.push(makeFinding('export_diff_git_unavailable', 'high', '.', detail));
|
|
247
|
+
issues.push(detail);
|
|
248
|
+
const status = statusFromFindings(findings);
|
|
249
|
+
return {
|
|
250
|
+
schema_version: '1',
|
|
251
|
+
command: 'script-pack',
|
|
252
|
+
pack_id: 'code',
|
|
253
|
+
script_id: CODE_EXPORT_DIFF_SCRIPT_ID,
|
|
254
|
+
script_ref: CODE_EXPORT_DIFF_SCRIPT_REF,
|
|
255
|
+
action: 'compare',
|
|
256
|
+
status,
|
|
257
|
+
ok: false,
|
|
258
|
+
mustflow_root: root,
|
|
259
|
+
policy,
|
|
260
|
+
input_hash: sha256Tagged(JSON.stringify({ policy, findings })),
|
|
261
|
+
package_surface: packageSurface(root),
|
|
262
|
+
files: [],
|
|
263
|
+
exports: [],
|
|
264
|
+
summary: { files_changed: 0, added: 0, removed: 0, changed: 0, unchanged: 0 },
|
|
265
|
+
findings,
|
|
266
|
+
issues,
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
const changedPaths = collectChangedPaths(root, policy, findings, issues);
|
|
270
|
+
const files = [];
|
|
271
|
+
const entries = [];
|
|
272
|
+
for (const relativePath of changedPaths) {
|
|
273
|
+
const language = languageForPath(relativePath);
|
|
274
|
+
if (!language) {
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
277
|
+
let baseSnapshot;
|
|
278
|
+
let headSnapshot;
|
|
279
|
+
try {
|
|
280
|
+
baseSnapshot = readGitSnapshot(root, policy.base_ref, relativePath, policy.max_file_bytes);
|
|
281
|
+
headSnapshot = policy.head_ref
|
|
282
|
+
? readGitSnapshot(root, policy.head_ref, relativePath, policy.max_file_bytes)
|
|
283
|
+
: readWorktreeSnapshot(root, relativePath, policy.max_file_bytes);
|
|
284
|
+
}
|
|
285
|
+
catch (error) {
|
|
286
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
287
|
+
const code = message.includes('max_file_bytes') ? 'export_diff_file_too_large' : 'export_diff_unreadable_path';
|
|
288
|
+
findings.push(makeFinding(code, 'high', relativePath, message));
|
|
289
|
+
issues.push(`${relativePath}: ${message}`);
|
|
290
|
+
continue;
|
|
291
|
+
}
|
|
292
|
+
if (hasUnresolvedReexport(baseSnapshot.text) || hasUnresolvedReexport(headSnapshot.text)) {
|
|
293
|
+
const message = `${relativePath} contains re-export declarations; export diff does not resolve barrel targets.`;
|
|
294
|
+
findings.push(makeFinding('export_diff_reexport_unresolved', 'low', relativePath, message));
|
|
295
|
+
}
|
|
296
|
+
const beforeSymbols = snapshotSymbols(relativePath, baseSnapshot);
|
|
297
|
+
const afterSymbols = snapshotSymbols(relativePath, headSnapshot);
|
|
298
|
+
const beforeMap = buildSymbolMap(beforeSymbols, findings, issues);
|
|
299
|
+
const afterMap = buildSymbolMap(afterSymbols, findings, issues);
|
|
300
|
+
const keys = [...new Set([...beforeMap.keys(), ...afterMap.keys()])].sort((left, right) => left.localeCompare(right));
|
|
301
|
+
for (const key of keys) {
|
|
302
|
+
const before = beforeMap.get(key) ?? null;
|
|
303
|
+
const after = afterMap.get(key) ?? null;
|
|
304
|
+
const comparison = compareSymbols(before, after);
|
|
305
|
+
const current = after ?? before;
|
|
306
|
+
if (!current) {
|
|
307
|
+
continue;
|
|
308
|
+
}
|
|
309
|
+
entries.push({
|
|
310
|
+
id: key,
|
|
311
|
+
path: current.path,
|
|
312
|
+
name: current.name,
|
|
313
|
+
kind: comparison.kind,
|
|
314
|
+
change: comparison.change,
|
|
315
|
+
before,
|
|
316
|
+
after,
|
|
317
|
+
compatibility: comparison.compatibility,
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
files.push({
|
|
321
|
+
kind: 'source_file',
|
|
322
|
+
path: relativePath,
|
|
323
|
+
language,
|
|
324
|
+
base_sha256: baseSnapshot.sha256,
|
|
325
|
+
head_sha256: headSnapshot.sha256,
|
|
326
|
+
base_exists: baseSnapshot.exists,
|
|
327
|
+
head_exists: headSnapshot.exists,
|
|
328
|
+
exported_count_before: beforeSymbols.length,
|
|
329
|
+
exported_count_after: afterSymbols.length,
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
const summary = {
|
|
333
|
+
files_changed: files.length,
|
|
334
|
+
added: entries.filter((entry) => entry.change === 'added').length,
|
|
335
|
+
removed: entries.filter((entry) => entry.change === 'removed').length,
|
|
336
|
+
changed: entries.filter((entry) => entry.change === 'changed').length,
|
|
337
|
+
unchanged: entries.filter((entry) => entry.change === 'unchanged').length,
|
|
338
|
+
};
|
|
339
|
+
const status = statusFromFindings(findings);
|
|
340
|
+
return {
|
|
341
|
+
schema_version: '1',
|
|
342
|
+
command: 'script-pack',
|
|
343
|
+
pack_id: 'code',
|
|
344
|
+
script_id: CODE_EXPORT_DIFF_SCRIPT_ID,
|
|
345
|
+
script_ref: CODE_EXPORT_DIFF_SCRIPT_REF,
|
|
346
|
+
action: 'compare',
|
|
347
|
+
status,
|
|
348
|
+
ok: status !== 'error',
|
|
349
|
+
mustflow_root: root,
|
|
350
|
+
policy,
|
|
351
|
+
input_hash: createInputHash(policy, files, entries),
|
|
352
|
+
package_surface: packageSurface(root),
|
|
353
|
+
files,
|
|
354
|
+
exports: entries.sort((left, right) => left.path.localeCompare(right.path) || left.name.localeCompare(right.name)),
|
|
355
|
+
summary,
|
|
356
|
+
findings,
|
|
357
|
+
issues,
|
|
358
|
+
};
|
|
359
|
+
}
|
|
@@ -268,6 +268,47 @@ const PUBLIC_JSON_SCHEMA_CONTRACTS = [
|
|
|
268
268
|
'--json',
|
|
269
269
|
],
|
|
270
270
|
},
|
|
271
|
+
{
|
|
272
|
+
id: 'route-outline-report',
|
|
273
|
+
schemaFile: 'route-outline-report.schema.json',
|
|
274
|
+
producer: 'mf script-pack run code/route-outline scan <path...> --json',
|
|
275
|
+
packaged: true,
|
|
276
|
+
documented: true,
|
|
277
|
+
installedCommand: [
|
|
278
|
+
'mf',
|
|
279
|
+
'script-pack',
|
|
280
|
+
'run',
|
|
281
|
+
'code/route-outline',
|
|
282
|
+
'scan',
|
|
283
|
+
'node_modules/mustflow/dist/cli/index.js',
|
|
284
|
+
'--json',
|
|
285
|
+
],
|
|
286
|
+
},
|
|
287
|
+
{
|
|
288
|
+
id: 'export-diff-report',
|
|
289
|
+
schemaFile: 'export-diff-report.schema.json',
|
|
290
|
+
producer: 'mf script-pack run code/export-diff compare --json',
|
|
291
|
+
packaged: true,
|
|
292
|
+
documented: true,
|
|
293
|
+
},
|
|
294
|
+
{
|
|
295
|
+
id: 'reference-drift-report',
|
|
296
|
+
schemaFile: 'reference-drift-report.schema.json',
|
|
297
|
+
producer: 'mf script-pack run docs/reference-drift check [path...] --json',
|
|
298
|
+
packaged: true,
|
|
299
|
+
documented: true,
|
|
300
|
+
installedCommand: [
|
|
301
|
+
'mf',
|
|
302
|
+
'script-pack',
|
|
303
|
+
'run',
|
|
304
|
+
'docs/reference-drift',
|
|
305
|
+
'check',
|
|
306
|
+
'node_modules/mustflow/README.md',
|
|
307
|
+
'node_modules/mustflow/schemas/README.md',
|
|
308
|
+
'--json',
|
|
309
|
+
],
|
|
310
|
+
expectedExitCodes: [0, 1],
|
|
311
|
+
},
|
|
271
312
|
{
|
|
272
313
|
id: 'text-budget-report',
|
|
273
314
|
schemaFile: 'text-budget-report.schema.json',
|
|
@@ -305,6 +346,40 @@ const PUBLIC_JSON_SCHEMA_CONTRACTS = [
|
|
|
305
346
|
],
|
|
306
347
|
expectedExitCodes: [0, 1],
|
|
307
348
|
},
|
|
349
|
+
{
|
|
350
|
+
id: 'config-chain-report',
|
|
351
|
+
schemaFile: 'config-chain-report.schema.json',
|
|
352
|
+
producer: 'mf script-pack run repo/config-chain inspect <path...> --json',
|
|
353
|
+
packaged: true,
|
|
354
|
+
documented: true,
|
|
355
|
+
installedCommand: [
|
|
356
|
+
'mf',
|
|
357
|
+
'script-pack',
|
|
358
|
+
'run',
|
|
359
|
+
'repo/config-chain',
|
|
360
|
+
'inspect',
|
|
361
|
+
'node_modules/mustflow/dist/cli/index.js',
|
|
362
|
+
'--json',
|
|
363
|
+
],
|
|
364
|
+
expectedExitCodes: [0, 1],
|
|
365
|
+
},
|
|
366
|
+
{
|
|
367
|
+
id: 'related-files-report',
|
|
368
|
+
schemaFile: 'related-files-report.schema.json',
|
|
369
|
+
producer: 'mf script-pack run repo/related-files map <path...> --json',
|
|
370
|
+
packaged: true,
|
|
371
|
+
documented: true,
|
|
372
|
+
installedCommand: [
|
|
373
|
+
'mf',
|
|
374
|
+
'script-pack',
|
|
375
|
+
'run',
|
|
376
|
+
'repo/related-files',
|
|
377
|
+
'map',
|
|
378
|
+
'node_modules/mustflow/dist/cli/index.js',
|
|
379
|
+
'--json',
|
|
380
|
+
],
|
|
381
|
+
expectedExitCodes: [0, 1],
|
|
382
|
+
},
|
|
308
383
|
{
|
|
309
384
|
id: 'skill-route-report',
|
|
310
385
|
schemaFile: 'skill-route-report.schema.json',
|