mustflow 2.74.3 → 2.74.5

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.
@@ -0,0 +1,199 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import path from 'node:path';
3
+ export function isScriptPackSuggestionPhase(value) {
4
+ return ['before_change', 'during_change', 'after_change', 'review'].includes(value);
5
+ }
6
+ function uniqueSortedStrings(values) {
7
+ return [...new Set(values)].sort((left, right) => left.localeCompare(right));
8
+ }
9
+ function uniqueSortedPhases(values) {
10
+ return uniqueSortedStrings(values);
11
+ }
12
+ function uniqueSortedSurfaces(values) {
13
+ return uniqueSortedStrings(values);
14
+ }
15
+ function normalizeReportPath(mustflowRoot, value) {
16
+ const absolute = path.resolve(mustflowRoot, value);
17
+ const relative = path.relative(mustflowRoot, absolute);
18
+ return relative.startsWith('..') || path.isAbsolute(relative)
19
+ ? value.replace(/\\/gu, '/')
20
+ : (relative.replace(/\\/gu, '/') || '.');
21
+ }
22
+ export function classifyScriptPackPathSurface(relativePath) {
23
+ const normalized = relativePath.replace(/\\/gu, '/').replace(/^\.\/+/u, '');
24
+ const surfaces = [];
25
+ if (normalized === 'REPO_MAP.md' ||
26
+ normalized === '.mustflow/config/manifest.lock.toml' ||
27
+ normalized.startsWith('dist/') ||
28
+ normalized.startsWith('build/') ||
29
+ normalized.startsWith('coverage/') ||
30
+ normalized.startsWith('.mustflow/cache/') ||
31
+ normalized.startsWith('.mustflow/state/')) {
32
+ surfaces.push('generated');
33
+ }
34
+ if (normalized.startsWith('.mustflow/config/') || normalized.startsWith('config/')) {
35
+ surfaces.push('config');
36
+ }
37
+ if (normalized.startsWith('.mustflow/skills/') || normalized.includes('/.mustflow/skills/')) {
38
+ surfaces.push('skill');
39
+ }
40
+ if (normalized.startsWith('templates/')) {
41
+ surfaces.push('template');
42
+ }
43
+ if (normalized.startsWith('schemas/') || normalized.endsWith('.schema.json')) {
44
+ surfaces.push('schema');
45
+ }
46
+ if (normalized === 'README.md' ||
47
+ normalized === 'CHANGELOG.md' ||
48
+ normalized.endsWith('.md') ||
49
+ normalized.startsWith('docs/') ||
50
+ normalized.startsWith('docs-site/')) {
51
+ surfaces.push('docs');
52
+ }
53
+ if (normalized.startsWith('tests/') || normalized.endsWith('.test.js') || normalized.endsWith('.test.ts')) {
54
+ surfaces.push('test');
55
+ }
56
+ if (normalized === 'package.json' ||
57
+ normalized === 'bun.lock' ||
58
+ normalized === 'package-lock.json' ||
59
+ normalized.startsWith('.github/workflows/')) {
60
+ surfaces.push('package');
61
+ }
62
+ if (normalized.startsWith('src/') || normalized.endsWith('.ts') || normalized.endsWith('.tsx')) {
63
+ surfaces.push('source');
64
+ }
65
+ return surfaces.length > 0 ? uniqueSortedSurfaces(surfaces) : ['unknown'];
66
+ }
67
+ function readChangedPaths(mustflowRoot, issues) {
68
+ const result = spawnSync('git', ['status', '--short'], {
69
+ cwd: mustflowRoot,
70
+ encoding: 'utf8',
71
+ stdio: ['ignore', 'pipe', 'pipe'],
72
+ windowsHide: true,
73
+ });
74
+ if (result.status !== 0) {
75
+ const detail = result.stderr.trim() || result.stdout.trim() || `git status exited with ${result.status}`;
76
+ issues.push(`Could not read changed paths: ${detail}`);
77
+ return [];
78
+ }
79
+ return result.stdout
80
+ .split(/\r?\n/u)
81
+ .map((line) => line.trimEnd())
82
+ .filter((line) => line.length > 0)
83
+ .map((line) => {
84
+ const renamed = /\s->\s(?<target>.+)$/u.exec(line);
85
+ return renamed?.groups?.target ?? line.slice(3).trim();
86
+ })
87
+ .filter((entry) => entry.length > 0);
88
+ }
89
+ function surfacesForScript(script) {
90
+ const surfaces = new Set();
91
+ const searchable = [
92
+ script.ref,
93
+ script.usage,
94
+ ...script.useWhen,
95
+ ...script.inputs,
96
+ ...script.outputs,
97
+ ...script.relatedSkills,
98
+ ].join(' ');
99
+ const addIf = (surface, pattern) => {
100
+ if (pattern.test(searchable)) {
101
+ surfaces.add(surface);
102
+ }
103
+ };
104
+ addIf('docs', /docs|readme|release|copy|text|prompt/u);
105
+ addIf('schema', /json|schema|contract/u);
106
+ addIf('template', /template|install|manifest/u);
107
+ addIf('skill', /skill|workflow/u);
108
+ addIf('generated', /generated|protected|vendor|cache|boundary/u);
109
+ addIf('config', /config|command/u);
110
+ addIf('package', /package|release/u);
111
+ addIf('source', /code|source|path/u);
112
+ return uniqueSortedSurfaces(surfaces);
113
+ }
114
+ function confidenceForScore(score) {
115
+ if (score >= 7) {
116
+ return 'high';
117
+ }
118
+ if (score >= 4) {
119
+ return 'medium';
120
+ }
121
+ return 'low';
122
+ }
123
+ export function createScriptPackSuggestionReport(mustflowRoot, options) {
124
+ const issues = [];
125
+ const changedPaths = options.changed ? readChangedPaths(mustflowRoot, issues) : [];
126
+ const inputPaths = uniqueSortedStrings([...options.paths, ...changedPaths].map((value) => normalizeReportPath(mustflowRoot, value)));
127
+ const analyzedPaths = inputPaths.map((entry) => ({ path: entry, surfaces: classifyScriptPackPathSurface(entry) }));
128
+ const requestedSurfaces = new Set(analyzedPaths.flatMap((entry) => entry.surfaces));
129
+ const suggestions = options.scripts
130
+ .map((script) => {
131
+ let score = 0;
132
+ const reasons = [];
133
+ const matchedPhases = options.phases.filter((phase) => script.phases.includes(phase));
134
+ if (matchedPhases.length > 0) {
135
+ score += matchedPhases.length * 3;
136
+ reasons.push(`Matches requested phase: ${matchedPhases.join(', ')}`);
137
+ }
138
+ const matchedSkills = options.skills.filter((skill) => script.relatedSkills.includes(skill));
139
+ if (matchedSkills.length > 0) {
140
+ score += matchedSkills.length * 4;
141
+ reasons.push(`Matches related skill: ${matchedSkills.join(', ')}`);
142
+ }
143
+ const scriptSurfaces = surfacesForScript(script);
144
+ const matchedSurfaces = scriptSurfaces.filter((surface) => requestedSurfaces.has(surface));
145
+ if (matchedSurfaces.length > 0) {
146
+ score += matchedSurfaces.length * 2;
147
+ reasons.push(`Matches changed surface: ${matchedSurfaces.join(', ')}`);
148
+ }
149
+ if (inputPaths.length > 0 && script.inputs.includes('path')) {
150
+ score += 1;
151
+ reasons.push('Accepts explicit path inputs.');
152
+ }
153
+ if (script.readOnly && !script.mutates && !script.network) {
154
+ score += 1;
155
+ reasons.push('Read-only, non-mutating, offline helper.');
156
+ }
157
+ if (score === 0) {
158
+ return null;
159
+ }
160
+ return {
161
+ script_ref: script.ref,
162
+ score,
163
+ confidence: confidenceForScore(score),
164
+ usage: script.usage,
165
+ phases: script.phases,
166
+ matched_phases: uniqueSortedPhases(matchedPhases),
167
+ matched_skills: uniqueSortedStrings(matchedSkills),
168
+ matched_surfaces: uniqueSortedSurfaces(matchedSurfaces),
169
+ reasons,
170
+ read_only: script.readOnly,
171
+ mutates: script.mutates,
172
+ network: script.network,
173
+ risk_level: script.riskLevel,
174
+ cost: script.cost,
175
+ report_schema_file: script.reportSchemaFile,
176
+ run_hint: script.usage,
177
+ };
178
+ })
179
+ .filter((suggestion) => suggestion !== null)
180
+ .sort((left, right) => right.score - left.score || left.script_ref.localeCompare(right.script_ref));
181
+ const status = issues.length > 0 ? 'partial' : suggestions.length > 0 ? 'suggested' : 'empty';
182
+ return {
183
+ schema_version: '1',
184
+ command: 'script-pack',
185
+ action: 'suggest',
186
+ status,
187
+ ok: true,
188
+ mustflow_root: mustflowRoot,
189
+ input: {
190
+ phases: uniqueSortedPhases(options.phases),
191
+ skills: uniqueSortedStrings(options.skills),
192
+ paths: inputPaths,
193
+ changed: options.changed,
194
+ },
195
+ analyzed_paths: analyzedPaths,
196
+ suggestions,
197
+ issues,
198
+ };
199
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mustflow",
3
- "version": "2.74.3",
3
+ "version": "2.74.5",
4
4
  "description": "Agent workflow documents and CLI for mustflow repository roots.",
5
5
  "type": "module",
6
6
  "license": "MIT-0",
package/schemas/README.md CHANGED
@@ -29,7 +29,8 @@ Current schemas:
29
29
  - `onboard-commands-report.schema.json`: output of `mf onboard commands --json`, containing
30
30
  review-only command-intent suggestions that do not grant command authority or write files
31
31
  - `next-report.schema.json`: output of `mf next --json`, containing the next safe action,
32
- changed-file verification gaps, and read-only command recommendations
32
+ changed-file verification gaps, read-only command recommendations, and optional read-only
33
+ script-pack helper suggestions
33
34
  - `evidence-report.schema.json`: output of `mf evidence --changed --json`, containing verification
34
35
  requirements, risk-priced evidence assessment, latest bounded evidence, failure replay capsules,
35
36
  conflict ledgers, receipts, remaining risks, and gaps without running commands
@@ -57,11 +58,18 @@ Current schemas:
57
58
  quality-gaming risks such as line stuffing, validation suppressions, test bypass markers, type
58
59
  escapes, generated/vendor logic, empty catch swallowing, and placeholder implementations
59
60
  - `script-pack-catalog.schema.json`: output of `mf script-pack list --json`, containing bundled
60
- script-pack ids, script refs, action names, usage strings, and associated report schemas
61
+ script-pack ids, script refs, action names, usage strings, workflow phases, read-only and
62
+ side-effect flags, input and output capability labels, related skill names, cost and risk hints,
63
+ and associated report schemas
64
+ - `script-pack-suggestion-report.schema.json`: output of `mf script-pack suggest --json`, containing
65
+ path, skill, phase, and changed-file evidence used to recommend optional script-pack helpers
61
66
  - `text-budget-report.schema.json`: output of
62
67
  `mf script-pack run core/text-budget check <path...> --json`, containing
63
68
  exact text-budget metrics, input content hashes, policy metadata, findings, and JSON Pointer field
64
69
  checks for bounded user-facing strings and generated text
70
+ - `generated-boundary-report.schema.json`: output of
71
+ `mf script-pack run repo/generated-boundary check <path...> --json`, containing candidate path
72
+ classifications for generated, ignored, protected, vendor, and cache boundaries before or after edits
65
73
  - `skill-route-report.schema.json`: output of `mf skill route --json`, containing compact route
66
74
  candidates, selected main and adjunct skills, score breakdowns, route read plans, and source
67
75
  route shards without granting command authority or replacing selected `SKILL.md` reads
@@ -0,0 +1,148 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://mustflow.github.io/schemas/generated-boundary-report.schema.json",
4
+ "title": "mustflow generated-boundary report",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "required": [
8
+ "schema_version",
9
+ "command",
10
+ "pack_id",
11
+ "script_id",
12
+ "script_ref",
13
+ "action",
14
+ "status",
15
+ "ok",
16
+ "mustflow_root",
17
+ "policy",
18
+ "input_hash",
19
+ "targets",
20
+ "findings",
21
+ "issues"
22
+ ],
23
+ "properties": {
24
+ "schema_version": { "const": "1" },
25
+ "command": { "const": "script-pack" },
26
+ "pack_id": { "const": "repo" },
27
+ "script_id": { "const": "generated-boundary" },
28
+ "script_ref": { "const": "repo/generated-boundary" },
29
+ "action": { "const": "check" },
30
+ "status": { "enum": ["passed", "failed", "error"] },
31
+ "ok": { "type": "boolean" },
32
+ "mustflow_root": { "type": "string" },
33
+ "policy": { "$ref": "#/$defs/policy" },
34
+ "input_hash": { "$ref": "#/$defs/sha256" },
35
+ "targets": {
36
+ "type": "array",
37
+ "items": { "$ref": "#/$defs/target" }
38
+ },
39
+ "findings": {
40
+ "type": "array",
41
+ "items": { "$ref": "#/$defs/finding" }
42
+ },
43
+ "issues": {
44
+ "type": "array",
45
+ "items": { "type": "string" }
46
+ }
47
+ },
48
+ "$defs": {
49
+ "sha256": {
50
+ "type": "string",
51
+ "pattern": "^sha256:[a-f0-9]{64}$"
52
+ },
53
+ "category": {
54
+ "enum": ["generated", "ignored", "protected", "vendor", "cache", "outside_root"]
55
+ },
56
+ "patternSource": {
57
+ "enum": ["mustflow_config", "builtin"]
58
+ },
59
+ "patternMatch": {
60
+ "type": "object",
61
+ "additionalProperties": false,
62
+ "required": ["category", "pattern", "source"],
63
+ "properties": {
64
+ "category": { "$ref": "#/$defs/category" },
65
+ "pattern": { "type": "string" },
66
+ "source": { "$ref": "#/$defs/patternSource" }
67
+ }
68
+ },
69
+ "policy": {
70
+ "type": "object",
71
+ "additionalProperties": false,
72
+ "required": [
73
+ "config_path",
74
+ "config_loaded",
75
+ "generated_patterns",
76
+ "ignored_patterns",
77
+ "protected_patterns",
78
+ "builtin_generated_patterns",
79
+ "builtin_vendor_patterns",
80
+ "builtin_cache_patterns",
81
+ "builtin_protected_patterns"
82
+ ],
83
+ "properties": {
84
+ "config_path": { "const": ".mustflow/config/mustflow.toml" },
85
+ "config_loaded": { "type": "boolean" },
86
+ "generated_patterns": { "$ref": "#/$defs/stringArray" },
87
+ "ignored_patterns": { "$ref": "#/$defs/stringArray" },
88
+ "protected_patterns": { "$ref": "#/$defs/stringArray" },
89
+ "builtin_generated_patterns": { "$ref": "#/$defs/stringArray" },
90
+ "builtin_vendor_patterns": { "$ref": "#/$defs/stringArray" },
91
+ "builtin_cache_patterns": { "$ref": "#/$defs/stringArray" },
92
+ "builtin_protected_patterns": { "$ref": "#/$defs/stringArray" }
93
+ }
94
+ },
95
+ "stringArray": {
96
+ "type": "array",
97
+ "items": { "type": "string" }
98
+ },
99
+ "target": {
100
+ "type": "object",
101
+ "additionalProperties": false,
102
+ "required": ["input", "path", "exists", "kind", "matched_boundaries", "matched_patterns"],
103
+ "properties": {
104
+ "input": { "type": "string" },
105
+ "path": { "type": "string" },
106
+ "exists": { "type": ["boolean", "null"] },
107
+ "kind": { "enum": ["file", "directory", "missing", "other", "unknown"] },
108
+ "matched_boundaries": {
109
+ "type": "array",
110
+ "items": { "$ref": "#/$defs/category" }
111
+ },
112
+ "matched_patterns": {
113
+ "type": "array",
114
+ "items": { "$ref": "#/$defs/patternMatch" }
115
+ }
116
+ }
117
+ },
118
+ "finding": {
119
+ "type": "object",
120
+ "additionalProperties": false,
121
+ "required": ["code", "severity", "message", "path", "boundary", "pattern", "source"],
122
+ "properties": {
123
+ "code": {
124
+ "enum": [
125
+ "generated_boundary_generated_path",
126
+ "generated_boundary_ignored_path",
127
+ "generated_boundary_protected_path",
128
+ "generated_boundary_vendor_path",
129
+ "generated_boundary_cache_path",
130
+ "generated_boundary_path_outside_root",
131
+ "generated_boundary_unreadable_path"
132
+ ]
133
+ },
134
+ "severity": { "enum": ["low", "medium", "high", "critical"] },
135
+ "message": { "type": "string" },
136
+ "path": { "type": "string" },
137
+ "boundary": { "$ref": "#/$defs/category" },
138
+ "pattern": { "type": ["string", "null"] },
139
+ "source": {
140
+ "anyOf": [
141
+ { "$ref": "#/$defs/patternSource" },
142
+ { "type": "null" }
143
+ ]
144
+ }
145
+ }
146
+ }
147
+ }
148
+ }
@@ -116,6 +116,146 @@
116
116
  }
117
117
  }
118
118
  },
119
+ "script_pack_suggestions": {
120
+ "type": "object",
121
+ "additionalProperties": false,
122
+ "required": ["status", "input", "analyzed_paths", "suggestions", "issues"],
123
+ "properties": {
124
+ "status": { "type": "string", "enum": ["suggested", "empty", "partial"] },
125
+ "input": { "$ref": "#/$defs/scriptPackSuggestionInput" },
126
+ "analyzed_paths": {
127
+ "type": "array",
128
+ "items": { "$ref": "#/$defs/scriptPackAnalyzedPath" }
129
+ },
130
+ "suggestions": {
131
+ "type": "array",
132
+ "items": { "$ref": "#/$defs/scriptPackSuggestion" }
133
+ },
134
+ "issues": {
135
+ "type": "array",
136
+ "items": { "type": "string" }
137
+ }
138
+ }
139
+ },
119
140
  "verification_plan_id": { "type": ["string", "null"] }
141
+ },
142
+ "$defs": {
143
+ "scriptPackPhase": {
144
+ "type": "string",
145
+ "enum": ["before_change", "during_change", "after_change", "review"]
146
+ },
147
+ "scriptPackSurface": {
148
+ "type": "string",
149
+ "enum": [
150
+ "config",
151
+ "docs",
152
+ "generated",
153
+ "package",
154
+ "schema",
155
+ "skill",
156
+ "source",
157
+ "template",
158
+ "test",
159
+ "unknown"
160
+ ]
161
+ },
162
+ "scriptPackSuggestionInput": {
163
+ "type": "object",
164
+ "additionalProperties": false,
165
+ "required": ["phases", "skills", "paths", "changed"],
166
+ "properties": {
167
+ "phases": {
168
+ "type": "array",
169
+ "items": { "$ref": "#/$defs/scriptPackPhase" }
170
+ },
171
+ "skills": {
172
+ "type": "array",
173
+ "items": { "type": "string", "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$" }
174
+ },
175
+ "paths": {
176
+ "type": "array",
177
+ "items": { "type": "string" }
178
+ },
179
+ "changed": { "type": "boolean" }
180
+ }
181
+ },
182
+ "scriptPackAnalyzedPath": {
183
+ "type": "object",
184
+ "additionalProperties": false,
185
+ "required": ["path", "surfaces"],
186
+ "properties": {
187
+ "path": { "type": "string" },
188
+ "surfaces": {
189
+ "type": "array",
190
+ "items": { "$ref": "#/$defs/scriptPackSurface" },
191
+ "minItems": 1
192
+ }
193
+ }
194
+ },
195
+ "scriptPackSuggestion": {
196
+ "type": "object",
197
+ "additionalProperties": false,
198
+ "required": [
199
+ "script_ref",
200
+ "score",
201
+ "confidence",
202
+ "usage",
203
+ "phases",
204
+ "matched_phases",
205
+ "matched_skills",
206
+ "matched_surfaces",
207
+ "reasons",
208
+ "read_only",
209
+ "mutates",
210
+ "network",
211
+ "risk_level",
212
+ "cost",
213
+ "report_schema_file",
214
+ "run_hint"
215
+ ],
216
+ "properties": {
217
+ "script_ref": {
218
+ "type": "string",
219
+ "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*/[a-z0-9]+(?:-[a-z0-9]+)*$"
220
+ },
221
+ "score": { "type": "integer", "minimum": 0 },
222
+ "confidence": { "type": "string", "enum": ["low", "medium", "high"] },
223
+ "usage": { "type": "string" },
224
+ "phases": {
225
+ "type": "array",
226
+ "items": { "$ref": "#/$defs/scriptPackPhase" },
227
+ "minItems": 1
228
+ },
229
+ "matched_phases": {
230
+ "type": "array",
231
+ "items": { "$ref": "#/$defs/scriptPackPhase" }
232
+ },
233
+ "matched_skills": {
234
+ "type": "array",
235
+ "items": { "type": "string", "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$" }
236
+ },
237
+ "matched_surfaces": {
238
+ "type": "array",
239
+ "items": { "$ref": "#/$defs/scriptPackSurface" }
240
+ },
241
+ "reasons": {
242
+ "type": "array",
243
+ "items": { "type": "string" },
244
+ "minItems": 1
245
+ },
246
+ "read_only": { "type": "boolean" },
247
+ "mutates": { "type": "boolean" },
248
+ "network": { "type": "boolean" },
249
+ "risk_level": { "type": "string", "enum": ["low", "medium", "high"] },
250
+ "cost": { "type": "string", "enum": ["low", "medium", "high"] },
251
+ "report_schema_file": {
252
+ "anyOf": [
253
+ { "type": "string", "pattern": "^[a-z0-9-]+\\.schema\\.json$" },
254
+ { "type": "null" }
255
+ ]
256
+ },
257
+ "run_hint": { "type": "string" }
258
+ }
259
+ }
120
260
  }
121
261
  }
@@ -56,6 +56,38 @@
56
56
  "items": { "type": "string", "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$" },
57
57
  "minItems": 1
58
58
  },
59
+ "use_when": {
60
+ "type": "array",
61
+ "items": { "type": "string" },
62
+ "minItems": 1
63
+ },
64
+ "phases": {
65
+ "type": "array",
66
+ "items": {
67
+ "type": "string",
68
+ "enum": ["before_change", "during_change", "after_change", "review"]
69
+ },
70
+ "minItems": 1
71
+ },
72
+ "read_only": { "type": "boolean" },
73
+ "mutates": { "type": "boolean" },
74
+ "network": { "type": "boolean" },
75
+ "inputs": {
76
+ "type": "array",
77
+ "items": { "type": "string", "pattern": "^[a-z0-9]+(?:_[a-z0-9]+)*$" },
78
+ "minItems": 1
79
+ },
80
+ "outputs": {
81
+ "type": "array",
82
+ "items": { "type": "string", "pattern": "^[a-z0-9]+(?:_[a-z0-9]+)*$" },
83
+ "minItems": 1
84
+ },
85
+ "related_skills": {
86
+ "type": "array",
87
+ "items": { "type": "string", "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$" }
88
+ },
89
+ "risk_level": { "type": "string", "enum": ["low", "medium", "high"] },
90
+ "cost": { "type": "string", "enum": ["low", "medium", "high"] },
59
91
  "report_schema_file": {
60
92
  "anyOf": [
61
93
  { "type": "string", "pattern": "^[a-z0-9-]+\\.schema\\.json$" },