mustflow 2.107.9 → 2.108.2

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 (26) hide show
  1. package/dist/cli/commands/api/serve.js +73 -10
  2. package/dist/core/run-receipt-state.js +23 -2
  3. package/dist/core/secret-redaction.js +6 -1
  4. package/package.json +1 -1
  5. package/schemas/api-serve-response.schema.json +1 -0
  6. package/templates/default/i18n.toml +48 -12
  7. package/templates/default/locales/en/.mustflow/docs/agent-workflow.md +24 -1
  8. package/templates/default/locales/en/.mustflow/skills/INDEX.md +52 -14
  9. package/templates/default/locales/en/.mustflow/skills/admin-control-plane-safety-review/SKILL.md +200 -0
  10. package/templates/default/locales/en/.mustflow/skills/ai-product-readiness-review/SKILL.md +158 -0
  11. package/templates/default/locales/en/.mustflow/skills/auth-permission-change/SKILL.md +91 -28
  12. package/templates/default/locales/en/.mustflow/skills/browser-automation-reliability-review/SKILL.md +279 -0
  13. package/templates/default/locales/en/.mustflow/skills/ci-pipeline-triage/SKILL.md +39 -11
  14. package/templates/default/locales/en/.mustflow/skills/cloud-cost-guardrail-review/SKILL.md +4 -1
  15. package/templates/default/locales/en/.mustflow/skills/database-change-safety/SKILL.md +21 -2
  16. package/templates/default/locales/en/.mustflow/skills/database-migration-change/SKILL.md +25 -7
  17. package/templates/default/locales/en/.mustflow/skills/deployment-rollout-safety-review/SKILL.md +117 -43
  18. package/templates/default/locales/en/.mustflow/skills/frontend-component-library-review/SKILL.md +299 -0
  19. package/templates/default/locales/en/.mustflow/skills/frontend-localization-review/SKILL.md +128 -36
  20. package/templates/default/locales/en/.mustflow/skills/notification-delivery-integrity-review/SKILL.md +226 -0
  21. package/templates/default/locales/en/.mustflow/skills/payment-integrity-review/SKILL.md +34 -14
  22. package/templates/default/locales/en/.mustflow/skills/routes.toml +36 -0
  23. package/templates/default/locales/en/.mustflow/skills/small-service-platform-architecture-review/SKILL.md +273 -0
  24. package/templates/default/locales/en/.mustflow/skills/tauri-code-change/SKILL.md +41 -3
  25. package/templates/default/locales/en/.mustflow/skills/wails-code-change/SKILL.md +34 -4
  26. package/templates/default/manifest.toml +43 -1
@@ -1,10 +1,10 @@
1
- import { createInterface } from 'node:readline';
2
1
  import { apiReportActionSpec, isApiReportAction } from './actions.js';
3
2
  import { printUsageError } from '../../lib/cli-output.js';
4
3
  import { formatCliOptionParseError, hasParsedCliOption, parseCliOptions, } from '../../lib/option-parser.js';
5
4
  import { isRecord } from '../../lib/command-contract.js';
6
5
  import { t } from '../../lib/i18n.js';
7
6
  const API_SERVE_SCHEMA_VERSION = '1';
7
+ const API_SERVE_MAX_LINE_CHARS = 1024 * 1024;
8
8
  const API_SERVE_OPTIONS = [
9
9
  { name: '--stdio', kind: 'boolean' },
10
10
  { name: '--help', kind: 'boolean', aliases: ['-h'] },
@@ -53,6 +53,15 @@ function readApiServeId(request) {
53
53
  }
54
54
  return null;
55
55
  }
56
+ function readApiServeChanged(request, id) {
57
+ if (!Object.hasOwn(request, 'changed')) {
58
+ return undefined;
59
+ }
60
+ if (typeof request.changed === 'boolean') {
61
+ return request.changed;
62
+ }
63
+ return createApiServeError(id, 'invalid_request', 'Request field "changed" must be a boolean when provided.');
64
+ }
56
65
  function parseApiServeRequestLine(line) {
57
66
  let parsed;
58
67
  try {
@@ -72,11 +81,18 @@ function parseApiServeRequestLine(line) {
72
81
  error: createApiServeError(id, 'invalid_request', 'Request must be a JSON object.'),
73
82
  };
74
83
  }
84
+ const changed = readApiServeChanged(parsed, id);
85
+ if (typeof changed !== 'boolean' && changed !== undefined) {
86
+ return {
87
+ request: null,
88
+ error: changed,
89
+ };
90
+ }
75
91
  return {
76
92
  request: {
77
93
  id,
78
94
  action: parsed.action,
79
- changed: parsed.changed,
95
+ changed,
80
96
  },
81
97
  error: null,
82
98
  };
@@ -99,8 +115,8 @@ function createApiServeResponse(request, runtime) {
99
115
  try {
100
116
  return createApiServeSuccess(id, runtime.createReport(request.action));
101
117
  }
102
- catch (error) {
103
- return createApiServeError(id, 'report_unavailable', error instanceof Error ? error.message : String(error));
118
+ catch {
119
+ return createApiServeError(id, 'report_unavailable', 'Report is unavailable for this action.');
104
120
  }
105
121
  }
106
122
  function writeApiServeResponse(response, reporter) {
@@ -111,6 +127,53 @@ function writeApiServeResponse(response, reporter) {
111
127
  }
112
128
  reporter.stdout(line.trimEnd());
113
129
  }
130
+ async function* readApiServeInputLines(input) {
131
+ input.setEncoding('utf8');
132
+ let buffer = '';
133
+ let discardingOversizedLine = false;
134
+ for await (const chunk of input) {
135
+ const text = typeof chunk === 'string' ? chunk : String(chunk);
136
+ let start = 0;
137
+ while (start < text.length) {
138
+ const newlineIndex = text.indexOf('\n', start);
139
+ const segmentEnd = newlineIndex === -1 ? text.length : newlineIndex;
140
+ const segment = text.slice(start, segmentEnd);
141
+ if (discardingOversizedLine) {
142
+ if (newlineIndex !== -1) {
143
+ yield { oversized: true };
144
+ discardingOversizedLine = false;
145
+ }
146
+ }
147
+ else if (buffer.length + segment.length > API_SERVE_MAX_LINE_CHARS) {
148
+ buffer = '';
149
+ if (newlineIndex === -1) {
150
+ discardingOversizedLine = true;
151
+ }
152
+ else {
153
+ yield { oversized: true };
154
+ }
155
+ }
156
+ else {
157
+ buffer += segment;
158
+ if (newlineIndex !== -1) {
159
+ yield { line: buffer.endsWith('\r') ? buffer.slice(0, -1) : buffer };
160
+ buffer = '';
161
+ }
162
+ }
163
+ if (newlineIndex === -1) {
164
+ break;
165
+ }
166
+ start = newlineIndex + 1;
167
+ }
168
+ }
169
+ if (discardingOversizedLine) {
170
+ yield { oversized: true };
171
+ return;
172
+ }
173
+ if (buffer.length > 0) {
174
+ yield { line: buffer.endsWith('\r') ? buffer.slice(0, -1) : buffer };
175
+ }
176
+ }
114
177
  export async function runApiServe(args, reporter, lang, runtime) {
115
178
  const parsed = parseCliOptions(args, API_SERVE_OPTIONS);
116
179
  if (hasParsedCliOption(parsed, '--help')) {
@@ -125,12 +188,12 @@ export async function runApiServe(args, reporter, lang, runtime) {
125
188
  printUsageError(reporter, t(lang, 'api.error.serveRequiresStdio'), 'mf api --help', runtime.getHelp(lang), lang);
126
189
  return 1;
127
190
  }
128
- const input = createInterface({
129
- input: process.stdin,
130
- crlfDelay: Infinity,
131
- });
132
- for await (const rawLine of input) {
133
- const line = rawLine.trim();
191
+ for await (const inputLine of readApiServeInputLines(process.stdin)) {
192
+ if (inputLine.oversized) {
193
+ writeApiServeResponse(createApiServeError(null, 'request_too_large', `Request line exceeds ${API_SERVE_MAX_LINE_CHARS} characters.`), reporter);
194
+ continue;
195
+ }
196
+ const line = inputLine.line?.trim() ?? '';
134
197
  if (line.length === 0) {
135
198
  continue;
136
199
  }
@@ -3,6 +3,7 @@ import path from 'node:path';
3
3
  import { ensureInside, writeJsonFileInsideWithoutSymlinks } from './safe-filesystem.js';
4
4
  const RUN_RECEIPT_SCHEMA_VERSION = '1';
5
5
  const RUN_RECEIPT_DIR = path.join('.mustflow', 'state', 'runs');
6
+ const RUN_RECEIPT_DIR_POSIX = '.mustflow/state/runs';
6
7
  const LATEST_RUN_RECEIPT_INDEX = 'latest.index.json';
7
8
  const STATE_DIR_PREFIXES = ['run-', 'verify-'];
8
9
  const MIN_RETAINED_RUN_DIRS = 1;
@@ -131,6 +132,24 @@ function stringField(value) {
131
132
  function stringArrayField(value) {
132
133
  return Array.isArray(value) && value.every((entry) => typeof entry === 'string') ? value : undefined;
133
134
  }
135
+ function resolveReceiptPathInsideRunsDir(runsDir, receiptPath) {
136
+ if (path.isAbsolute(receiptPath)) {
137
+ return null;
138
+ }
139
+ const normalizedReceiptPath = receiptPath.replace(/\\/gu, '/');
140
+ const runsPrefix = `${RUN_RECEIPT_DIR_POSIX}/`;
141
+ if (!normalizedReceiptPath.startsWith(runsPrefix)) {
142
+ return null;
143
+ }
144
+ const relativeToRunsDir = normalizedReceiptPath.slice(runsPrefix.length);
145
+ const parts = relativeToRunsDir.split('/');
146
+ if (parts.length === 0 || parts.some((part) => part.length === 0 || part === '.' || part === '..')) {
147
+ return null;
148
+ }
149
+ const receiptAbsolutePath = path.resolve(runsDir, ...parts);
150
+ ensureInside(runsDir, receiptAbsolutePath);
151
+ return receiptAbsolutePath;
152
+ }
134
153
  function createRunEntry(directory) {
135
154
  const receipt = readJsonObject(path.join(directory.absolutePath, 'receipt.json'));
136
155
  if (!receipt || receipt.command !== 'run' || typeof receipt.receipt_path !== 'string') {
@@ -154,8 +173,10 @@ function readVerifyIntentEntry(directory, manifest, manifestPath, manifestReceip
154
173
  return null;
155
174
  }
156
175
  const runsDir = path.dirname(directory.absolutePath);
157
- const receiptAbsolutePath = path.resolve(runsDir, ...receiptPath.split('/').slice(3));
158
- ensureInside(runsDir, receiptAbsolutePath);
176
+ const receiptAbsolutePath = resolveReceiptPathInsideRunsDir(runsDir, receiptPath);
177
+ if (!receiptAbsolutePath) {
178
+ return null;
179
+ }
159
180
  const receipt = readJsonObject(receiptAbsolutePath);
160
181
  return {
161
182
  command: 'verify',
@@ -8,9 +8,14 @@ const SECRET_REDACTION_RULES = [
8
8
  },
9
9
  {
10
10
  kind: 'secret_token',
11
- pattern: /\b(?:sk-[A-Za-z0-9]{16,}|ghp_[A-Za-z0-9]{20,}|xox[baprs]-[A-Za-z0-9-]{20,}|AKIA[0-9A-Z]{16})\b/gu,
11
+ pattern: /\b(?:sk-[A-Za-z0-9_-]{16,}|ghp_[A-Za-z0-9_]{20,}|github_pat_[A-Za-z0-9_]{20,}|xox[baprs]-[A-Za-z0-9-]{20,}|AKIA[0-9A-Z]{16})\b/gu,
12
12
  replace: () => REDACTED_SECRET_MARKER,
13
13
  },
14
+ {
15
+ kind: 'secret_bearer_token',
16
+ pattern: /\b(Bearer\s+)([A-Za-z0-9._~+/=-]{24,})\b/gu,
17
+ replace: (_match, prefix) => `${prefix}${REDACTED_SECRET_MARKER}`,
18
+ },
14
19
  ];
15
20
  export const SECRET_LIKE_PATTERNS = SECRET_REDACTION_RULES.map((rule) => {
16
21
  const flags = rule.pattern.flags.replace('g', '');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mustflow",
3
- "version": "2.107.9",
3
+ "version": "2.108.2",
4
4
  "description": "Agent workflow documents and CLI for mustflow repository roots.",
5
5
  "type": "module",
6
6
  "license": "MIT-0",
@@ -56,6 +56,7 @@
56
56
  "enum": [
57
57
  "invalid_json",
58
58
  "invalid_request",
59
+ "request_too_large",
59
60
  "unknown_action",
60
61
  "action_requires_changed",
61
62
  "action_does_not_accept_changed",
@@ -40,7 +40,7 @@ translations.hi = { path = "locales/hi/.mustflow/context/PROJECT.md", source_rev
40
40
  [documents."docs.agent-workflow"]
41
41
  source = "locales/en/.mustflow/docs/agent-workflow.md"
42
42
  source_locale = "en"
43
- revision = 27
43
+ revision = 28
44
44
  translations.ko = { path = "locales/ko/.mustflow/docs/agent-workflow.md", source_revision = 23, status = "needs_review" }
45
45
  translations.zh = { path = "locales/zh/.mustflow/docs/agent-workflow.md", source_revision = 18, status = "needs_review" }
46
46
  translations.es = { path = "locales/es/.mustflow/docs/agent-workflow.md", source_revision = 18, status = "needs_review" }
@@ -62,7 +62,7 @@ translations = {}
62
62
  [documents."skills.index"]
63
63
  source = "locales/en/.mustflow/skills/INDEX.md"
64
64
  source_locale = "en"
65
- revision = 200
65
+ revision = 207
66
66
  translations = {}
67
67
 
68
68
  [documents."skill.adapter-boundary"]
@@ -152,7 +152,7 @@ translations = {}
152
152
  [documents."skill.payment-integrity-review"]
153
153
  source = "locales/en/.mustflow/skills/payment-integrity-review/SKILL.md"
154
154
  source_locale = "en"
155
- revision = 3
155
+ revision = 4
156
156
  translations = {}
157
157
 
158
158
  [documents."skill.credit-ledger-integrity-review"]
@@ -161,6 +161,24 @@ source_locale = "en"
161
161
  revision = 1
162
162
  translations = {}
163
163
 
164
+ [documents."skill.notification-delivery-integrity-review"]
165
+ source = "locales/en/.mustflow/skills/notification-delivery-integrity-review/SKILL.md"
166
+ source_locale = "en"
167
+ revision = 1
168
+ translations = {}
169
+
170
+ [documents."skill.admin-control-plane-safety-review"]
171
+ source = "locales/en/.mustflow/skills/admin-control-plane-safety-review/SKILL.md"
172
+ source_locale = "en"
173
+ revision = 1
174
+ translations = {}
175
+
176
+ [documents."skill.small-service-platform-architecture-review"]
177
+ source = "locales/en/.mustflow/skills/small-service-platform-architecture-review/SKILL.md"
178
+ source_locale = "en"
179
+ revision = 1
180
+ translations = {}
181
+
164
182
  [documents."skill.api-misuse-resistance-review"]
165
183
  source = "locales/en/.mustflow/skills/api-misuse-resistance-review/SKILL.md"
166
184
  source_locale = "en"
@@ -224,7 +242,7 @@ translations = {}
224
242
  [documents."skill.ci-pipeline-triage"]
225
243
  source = "locales/en/.mustflow/skills/ci-pipeline-triage/SKILL.md"
226
244
  source_locale = "en"
227
- revision = 1
245
+ revision = 2
228
246
  translations = {}
229
247
 
230
248
  [documents."skill.auth-flow-triage"]
@@ -323,10 +341,16 @@ source_locale = "en"
323
341
  revision = 1
324
342
  translations = {}
325
343
 
344
+ [documents."skill.frontend-component-library-review"]
345
+ source = "locales/en/.mustflow/skills/frontend-component-library-review/SKILL.md"
346
+ source_locale = "en"
347
+ revision = 1
348
+ translations = {}
349
+
326
350
  [documents."skill.frontend-localization-review"]
327
351
  source = "locales/en/.mustflow/skills/frontend-localization-review/SKILL.md"
328
352
  source_locale = "en"
329
- revision = 1
353
+ revision = 2
330
354
  translations = {}
331
355
 
332
356
  [documents."skill.website-task-friction-review"]
@@ -398,13 +422,13 @@ translations = {}
398
422
  [documents."skill.deployment-rollout-safety-review"]
399
423
  source = "locales/en/.mustflow/skills/deployment-rollout-safety-review/SKILL.md"
400
424
  source_locale = "en"
401
- revision = 2
425
+ revision = 3
402
426
  translations = {}
403
427
 
404
428
  [documents."skill.cloud-cost-guardrail-review"]
405
429
  source = "locales/en/.mustflow/skills/cloud-cost-guardrail-review/SKILL.md"
406
430
  source_locale = "en"
407
- revision = 1
431
+ revision = 2
408
432
  translations = {}
409
433
 
410
434
  [documents."skill.rate-limit-integrity-review"]
@@ -464,13 +488,13 @@ translations = {}
464
488
  [documents."skill.database-change-safety"]
465
489
  source = "locales/en/.mustflow/skills/database-change-safety/SKILL.md"
466
490
  source_locale = "en"
467
- revision = 16
491
+ revision = 17
468
492
  translations = {}
469
493
 
470
494
  [documents."skill.database-migration-change"]
471
495
  source = "locales/en/.mustflow/skills/database-migration-change/SKILL.md"
472
496
  source_locale = "en"
473
- revision = 3
497
+ revision = 4
474
498
  translations = {}
475
499
 
476
500
  [documents."skill.database-query-bottleneck-review"]
@@ -619,7 +643,7 @@ translations = {}
619
643
  [documents."skill.auth-permission-change"]
620
644
  source = "locales/en/.mustflow/skills/auth-permission-change/SKILL.md"
621
645
  source_locale = "en"
622
- revision = 3
646
+ revision = 4
623
647
  translations = {}
624
648
 
625
649
  [documents."skill.security-flow-review"]
@@ -799,13 +823,13 @@ translations = {}
799
823
  [documents."skill.tauri-code-change"]
800
824
  source = "locales/en/.mustflow/skills/tauri-code-change/SKILL.md"
801
825
  source_locale = "en"
802
- revision = 3
826
+ revision = 4
803
827
  translations = {}
804
828
 
805
829
  [documents."skill.wails-code-change"]
806
830
  source = "locales/en/.mustflow/skills/wails-code-change/SKILL.md"
807
831
  source_locale = "en"
808
- revision = 1
832
+ revision = 2
809
833
  translations = {}
810
834
 
811
835
  [documents."skill.typescript-code-change"]
@@ -1167,6 +1191,12 @@ source_locale = "en"
1167
1191
  revision = 2
1168
1192
  translations = {}
1169
1193
 
1194
+ [documents."skill.ai-product-readiness-review"]
1195
+ source = "locales/en/.mustflow/skills/ai-product-readiness-review/SKILL.md"
1196
+ source_locale = "en"
1197
+ revision = 1
1198
+ translations = {}
1199
+
1170
1200
  [documents."skill.prompt-contract-quality-review"]
1171
1201
  source = "locales/en/.mustflow/skills/prompt-contract-quality-review/SKILL.md"
1172
1202
  source_locale = "en"
@@ -1197,6 +1227,12 @@ source_locale = "en"
1197
1227
  revision = 1
1198
1228
  translations = {}
1199
1229
 
1230
+ [documents."skill.browser-automation-reliability-review"]
1231
+ source = "locales/en/.mustflow/skills/browser-automation-reliability-review/SKILL.md"
1232
+ source_locale = "en"
1233
+ revision = 1
1234
+ translations = {}
1235
+
1200
1236
  [documents."skill.agent-eval-integrity-review"]
1201
1237
  source = "locales/en/.mustflow/skills/agent-eval-integrity-review/SKILL.md"
1202
1238
  source_locale = "en"
@@ -2,7 +2,7 @@
2
2
  mustflow_doc: docs.agent-workflow
3
3
  locale: en
4
4
  canonical: true
5
- revision: 27
5
+ revision: 28
6
6
  lifecycle: mustflow-owned
7
7
  authority: workflow-policy
8
8
  ---
@@ -68,6 +68,29 @@ When multiple skills apply, follow the most specific skill for each affected sco
68
68
 
69
69
  When a skill is used, report the skill name and selection reason briefly in the next user-facing update or final report. When files were created or modified, the final report must include a concise skill-selection note: list the skills used, say that no matching installed skill was found, or report that a plausible skill is missing from the installed profile. Do not create a versioned worklog solely to record skill selection.
70
70
 
71
+ ### Script-Pack Selection
72
+
73
+ Script-pack suggestions are optional evidence helpers, not skill procedures or command authority.
74
+ Skills remain the primary procedure owner: select and read the matching `SKILL.md` first, then use
75
+ script-pack metadata only to decide whether a bounded helper would improve orientation,
76
+ synchronization, or review evidence.
77
+
78
+ When the command contract exposes `script_pack_list`, use it for catalog discovery only. When the
79
+ command contract exposes `script_pack_suggest_changed`, use it after changed files exist to get
80
+ optional helper suggestions from current path evidence. These discovery commands do not run helper
81
+ scripts, authorize new commands, or replace the skill-selection gate.
82
+
83
+ Prefer helper candidates whose `related_skills` include the selected skill, whose `phases` match the
84
+ current phase (`before_change`, `during_change`, `after_change`, or `review`), and whose `use_when`
85
+ matches the changed paths and risk. Discard a candidate when `read_only` is false, `mutates` is true,
86
+ network, destructive, interactive, or long-running behavior appears, required inputs are unavailable,
87
+ the phase does not match, or no configured oneshot command intent authorizes that exact action.
88
+
89
+ Script-pack output can focus what to inspect, but it does not replace reading `SKILL.md`, relevant
90
+ source files, configured verification intents, documentation review requirements, or final completion
91
+ evidence. Suggested helpers are not mandatory. Report skipped useful suggestions only when they were
92
+ relevant to the current risk but not run.
93
+
71
94
  ## Input Stability
72
95
 
73
96
  Treat user instructions, local files, command contracts, and generated reports as distinct sources. Avoid conflating these sources.