cabloy 5.1.193 → 5.1.195

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 (108) hide show
  1. package/.cabloy-version +1 -1
  2. package/.github/workflows/agent-governance.yml +1 -0
  3. package/CHANGELOG.md +30 -0
  4. package/README.md +13 -24
  5. package/package.json +2 -1
  6. package/repo-agent-governance/adapters/codex/contract-loop-gate.mjs +1 -1
  7. package/repo-agent-governance/adapters/cursor/contract-loop-gate.mjs +1 -1
  8. package/repo-agent-governance/managed-assets.json +140 -35
  9. package/repo-agent-governance/scripts/pack-check.mjs +17 -0
  10. package/repo-agent-governance/skills/cabloy-backend-scaffold/references/follow-up-checklist.md +1 -0
  11. package/repo-agent-governance/skills/cabloy-domain-planning/SKILL.md +5 -3
  12. package/repo-agent-governance/skills/cabloy-spec-execution/SKILL.md +13 -8
  13. package/repo-agent-governance/skills/cabloy-spec-execution/evals/evals.json +108 -12
  14. package/repo-agent-governance/skills/cabloy-spec-execution/evals/files/scenarios.json +84 -0
  15. package/repo-agent-governance/skills/cabloy-spec-execution/evals/protocol.md +9 -0
  16. package/repo-agent-governance/skills/cabloy-spec-execution/references/execution-protocol.md +21 -5
  17. package/repo-agent-governance/skills/cabloy-spec-execution/references/status-and-evidence.md +5 -3
  18. package/repo-agent-governance/skills/cabloy-spec-generation/SKILL.md +78 -168
  19. package/repo-agent-governance/skills/cabloy-spec-generation/evals/evals.json +165 -17
  20. package/repo-agent-governance/skills/cabloy-spec-generation/evals/files/scenarios.json +114 -0
  21. package/repo-agent-governance/skills/cabloy-spec-generation/evals/protocol.md +44 -0
  22. package/repo-agent-governance/skills/cabloy-spec-generation/references/canonical-spec-input.md +145 -0
  23. package/repo-agent-governance/skills/cabloy-spec-generation/references/repo-aware-discovery.md +55 -57
  24. package/repo-agent-governance/skills/cabloy-spec-generation/references/repo-specs-document-set.md +6 -4
  25. package/repo-agent-governance/skills/cabloy-spec-generation/references/traceability-and-status-rules.md +12 -8
  26. package/repo-agent-governance/tests/governance.test.mjs +42 -1
  27. package/repo-agent-governance/tests/spec-audit.test.mjs +255 -0
  28. package/repo-agent-governance/tools/contract-loop/hook-runtime.mjs +4 -3
  29. package/repo-agent-governance/tools/spec-audit/audit.mjs +427 -0
  30. package/repo-agent-governance/tools/spec-charts/generate-implementation-charts.mjs +97 -408
  31. package/repo-agent-governance/tools/spec-charts/generate-implementation-charts.test.mjs +462 -1
  32. package/repo-agent-governance/tools/spec-charts/spec-parser.mjs +561 -0
  33. package/repo-docs/.vitepress/config.mjs +54 -29
  34. package/repo-docs/ai/playbook-spec-execution.md +10 -4
  35. package/repo-docs/ai/playbook-spec-generation.md +40 -3
  36. package/repo-docs/ai/skills.md +3 -3
  37. package/repo-docs/backend/controller-aop-guide.md +10 -0
  38. package/repo-docs/backend/field-indexes.md +25 -0
  39. package/repo-docs/backend/menu-authorization.md +1 -0
  40. package/repo-docs/backend/menu-guide.md +18 -7
  41. package/repo-docs/blogs/cabloy-fullstack-resource-addressing/index.md +2 -1
  42. package/repo-docs/fullstack/contract-loop-playbook.md +1 -1
  43. package/repo-docs/fullstack/development-history.md +24 -0
  44. package/repo-docs/fullstack/introduction.md +25 -156
  45. package/repo-docs/fullstack/quickstart.md +1 -1
  46. package/repo-docs/fullstack/ssr-entry-modes.md +52 -0
  47. package/repo-docs/fullstack/ssr-site-and-flavor-setup.md +3 -1
  48. package/repo-docs/fullstack/tutorial-5-backend-contract-sharing.md +13 -7
  49. package/repo-docs/fullstack/tutorial-6-one-contract-four-uses.md +11 -15
  50. package/repo-docs/fullstack/tutorials-overview.md +2 -2
  51. package/repo-docs/index.md +23 -45
  52. package/repo-docs/public/cabloy.png +0 -0
  53. package/repo-docs/public/cabloy.svg +3 -0
  54. package/repo-docs/public/favicon.svg +3 -0
  55. package/repo-docs/reference/repo-scripts.md +6 -1
  56. package/repo-e2e/config/playwright.config.ts +1 -1
  57. package/repo-e2e/scripts/startE2eVona.ts +6 -2
  58. package/repo-e2e/specs/controller-boundary.spec.ts +76 -28
  59. package/scripts/bootstrapAgentGovernance.mjs +1 -0
  60. package/scripts/upgrade.ts +1 -0
  61. package/vona/packages-cli/cli/package.json +1 -1
  62. package/vona/packages-cli/cli-set-api/cli/templates/tools/crudBasic/snippets/2-meta.index.ts +4 -10
  63. package/vona/packages-cli/cli-set-api/cli/templates/tools/crudStart/snippets/2-meta.index.ts +4 -10
  64. package/vona/packages-cli/cli-set-api/package.json +8 -2
  65. package/vona/packages-cli/cli-set-api/src/index.ts +1 -0
  66. package/vona/packages-cli/cli-set-api/src/lib/bean/cli.tools.masterDetail.ts +9 -12
  67. package/vona/packages-cli/cli-set-api/src/lib/mergeMetaIndex.ts +121 -0
  68. package/vona/packages-cli/cli-set-api/test/indexSnippets.test.ts +41 -0
  69. package/vona/packages-cli/cli-set-api/test/mergeMetaIndex.test.ts +78 -0
  70. package/vona/packages-vona/vona/package.json +1 -1
  71. package/vona/pnpm-lock.yaml +5 -5
  72. package/vona/src/suite/a-commerce/modules/commerce-catalog/src/service/sku.ts +25 -4
  73. package/vona/src/suite/a-commerce/modules/commerce-catalog/test/skuUniqueness.test.ts +161 -0
  74. package/vona/src/suite/a-commerce/modules/commerce-payment/src/bean/meta.index.ts +21 -20
  75. package/vona/src/suite/a-commerce/modules/commerce-payment/test/paymentIndexes.test.ts +165 -0
  76. package/vona/src/suite/a-commerce/modules/commerce-promotion/src/bean/meta.index.ts +16 -13
  77. package/vona/src/suite/a-commerce/modules/commerce-promotion/test/promotionIndexes.test.ts +82 -0
  78. package/vona/src/suite/a-commerce/modules/commerce-trade/src/bean/meta.index.ts +23 -21
  79. package/vona/src/suite/a-commerce/modules/commerce-trade/test/tradeIndexes.test.ts +86 -0
  80. package/vona/src/suite-vendor/a-cabloy/modules/a-rbac/package.json +1 -1
  81. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/package.json +1 -1
  82. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/src/lib/beanSsrSiteBase.ts +2 -0
  83. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/src/lib/ssrMenu.ts +9 -3
  84. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/src/lib/ssrMenuEligibility.ts +14 -0
  85. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/src/lib/ssrMenuGroup.ts +9 -3
  86. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/src/lib/ssrMenuSite.ts +10 -0
  87. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/src/types/ssrMenu.ts +1 -1
  88. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/src/types/ssrMenuGroup.ts +1 -1
  89. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/test/ssrMenuEligibility.test.ts +73 -13
  90. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/test/ssrMenuSite.test.ts +90 -0
  91. package/vona/src/suite-vendor/a-cabloy/package.json +2 -2
  92. package/vona/src/suite-vendor/a-pay/modules/a-pay/package.json +1 -1
  93. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/bean/meta.index.ts +35 -26
  94. package/vona/src/suite-vendor/a-pay/modules/pay-mock/package.json +1 -1
  95. package/vona/src/suite-vendor/a-pay/modules/pay-paypal/package.json +1 -1
  96. package/vona/src/suite-vendor/a-pay/modules/pay-stripe/package.json +1 -1
  97. package/vona/src/suite-vendor/a-pay/package.json +5 -5
  98. package/vona/src/suite-vendor/a-vona/modules/a-error/package.json +1 -1
  99. package/vona/src/suite-vendor/a-vona/modules/a-error/src/lib/onerror.ts +9 -0
  100. package/vona/src/suite-vendor/a-vona/modules/a-error/test/onerror.test.ts +92 -0
  101. package/vona/src/suite-vendor/a-vona/modules/a-orm/package.json +1 -1
  102. package/vona/src/suite-vendor/a-vona/modules/a-orm/src/service/transactionFiber_.ts +6 -2
  103. package/vona/src/suite-vendor/a-vona/modules/a-orm/src/service/transaction_.ts +4 -1
  104. package/vona/src/suite-vendor/a-vona/modules/a-ormutils/package.json +1 -1
  105. package/vona/src/suite-vendor/a-vona/modules/a-ormutils/src/lib/columns.ts +3 -1
  106. package/vona/src/suite-vendor/a-vona/modules/a-permission/package.json +1 -1
  107. package/vona/src/suite-vendor/a-vona/package.json +1 -1
  108. /package/repo-docs/{.vitepress/public → public}/CNAME +0 -0
@@ -0,0 +1,255 @@
1
+ import assert from 'node:assert/strict';
2
+ import { spawnSync } from 'node:child_process';
3
+ import { mkdir, mkdtemp, readFile, readdir, rm, writeFile } from 'node:fs/promises';
4
+ import { tmpdir } from 'node:os';
5
+ import { dirname, resolve } from 'node:path';
6
+ import { test } from 'node:test';
7
+ import { fileURLToPath } from 'node:url';
8
+
9
+ import { auditPlanning } from '../tools/spec-audit/audit.mjs';
10
+
11
+ const CLI = fileURLToPath(new URL('../tools/spec-audit/audit.mjs', import.meta.url));
12
+ const BASELINE = {
13
+ 'README.md': '# Synthetic Planning\n\n[Product](./prd.md#requirements)\n',
14
+ 'prd.md':
15
+ '# Product\n\n## Requirements\n\n### PRD-DEMO-01: Product outcome\n\nTraceability: `SRS-DEMO-01`\n',
16
+ 'srs.md':
17
+ '# Technical Contracts\n\n### SRS-DEMO-01: Contract\n\nTraceability: `PRD-DEMO-01`, `WBS-DEMO-10-01`\n',
18
+ 'pdp-wbs.md':
19
+ '# Delivery\n\n### Phase 10: Baseline\n\nDependencies: none.\n\n#### WBS-DEMO-10-01: Implement contract\n\nTraceability: `SRS-DEMO-01`, `ATP-DEMO-01`\n\nCompletion checks: inspect synthetic output.\n',
20
+ 'test-plan.md':
21
+ '# Acceptance\n\n## Acceptance Scenarios\n\n### ATP-DEMO-01: Verify contract\n\nSetup: synthetic state.\n\nProcedure: inspect output.\n\nExpected result: contract holds.\n\nMinimum proof: redacted observation.\n\nTraceability: `WBS-DEMO-10-01`\n',
22
+ 'progress.md':
23
+ '# Progress\n\n| WBS ID | Work item | Status |\n| --- | --- | --- |\n| WBS-DEMO-10-01 | Implement contract | not-started |\n',
24
+ 'decisions/0001-boundary.md': '# Boundary\n\nStatus: Proposed\n',
25
+ };
26
+
27
+ async function fixture(t, records = BASELINE) {
28
+ const directory = await mkdtemp(resolve(tmpdir(), 'cabloy-spec-audit-'));
29
+ t.after(() => rm(directory, { recursive: true, force: true }));
30
+ for (const [file, markdown] of Object.entries(records)) {
31
+ await mkdir(dirname(resolve(directory, file)), { recursive: true });
32
+ await writeFile(resolve(directory, file), markdown);
33
+ }
34
+ return directory;
35
+ }
36
+
37
+ async function snapshot(directory, prefix = '') {
38
+ const result = {};
39
+ for (const entry of await readdir(resolve(directory, prefix), { withFileTypes: true })) {
40
+ const file = prefix ? `${prefix}/${entry.name}` : entry.name;
41
+ if (entry.isDirectory()) Object.assign(result, await snapshot(directory, file));
42
+ else result[file] = await readFile(resolve(directory, file), 'utf8');
43
+ }
44
+ return result;
45
+ }
46
+
47
+ function codes(result) {
48
+ return result.diagnostics.map(diagnostic => diagnostic.code);
49
+ }
50
+
51
+ test('complete baseline checks exact traceability without treating a Proposed ADR as accepted', async t => {
52
+ const directory = await fixture(t);
53
+ const result = await auditPlanning(directory);
54
+ assert.deepEqual(result.diagnostics, []);
55
+ assert.equal(result.definitions, 4);
56
+ assert.equal(result.ok, true);
57
+ assert.match(result.limitations, /approval.*evidence authenticity/);
58
+ });
59
+
60
+ test('canonical atomic bullets carry their own inline traceability', async t => {
61
+ const records = {
62
+ ...BASELINE,
63
+ 'prd.md':
64
+ '# Product\n\n## Requirements\n\n- **PRD-DEMO-01**: Product outcome. Traceability: `SRS-DEMO-01`.\n',
65
+ 'srs.md':
66
+ '# Technical Contracts\n\n- **SRS-DEMO-01**: Contract. Traceability: `PRD-DEMO-01`, `WBS-DEMO-10-01`.\n',
67
+ };
68
+ const result = await auditPlanning(await fixture(t, records));
69
+ assert.equal(result.ok, true, JSON.stringify(result.diagnostics));
70
+ });
71
+
72
+ test('explicit four-column matrix supplies exact adjacent traceability', async t => {
73
+ const records = Object.fromEntries(
74
+ Object.entries(BASELINE).map(([file, value]) => [
75
+ file,
76
+ value.replace(/^Traceability:.*\n/gm, ''),
77
+ ]),
78
+ );
79
+ records['README.md'] +=
80
+ '\n| PRD | SRS | WBS | ATP |\n| --- | --- | --- | --- |\n| PRD-DEMO-01 | SRS-DEMO-01 | WBS-DEMO-10-01 | ATP-DEMO-01 |\n';
81
+ const result = await auditPlanning(await fixture(t, records));
82
+ assert.equal(result.ok, true, JSON.stringify(result.diagnostics));
83
+ });
84
+
85
+ test('ordinary co-occurrence and wildcard matrices cannot manufacture traceability', async t => {
86
+ const records = Object.fromEntries(
87
+ Object.entries(BASELINE).map(([file, value]) => [
88
+ file,
89
+ value.replace(/^Traceability:.*\n/gm, ''),
90
+ ]),
91
+ );
92
+ records['README.md'] +=
93
+ '\nPRD-DEMO-01 SRS-DEMO-01 WBS-DEMO-10-01 ATP-DEMO-01\n\n| PRD | SRS | WBS | ATP |\n| --- | --- | --- | --- |\n| PRD-DEMO-* | SRS-DEMO-* | WBS-DEMO-* | ATP-DEMO-* |\n';
94
+ const result = await auditPlanning(await fixture(t, records));
95
+ assert.equal(result.ok, false);
96
+ assert.equal(
97
+ result.diagnostics.filter(diagnostic => diagnostic.code.startsWith('traceability-')).length,
98
+ 6,
99
+ );
100
+ });
101
+
102
+ test('lightweight records do not require absent baseline files or charts', async t => {
103
+ const directory = await fixture(t, {
104
+ 'README.md': '# Lightweight\n',
105
+ 'prd.md': '### PRD-DEMO-01: Bounded outcome\n',
106
+ });
107
+ const before = await snapshot(directory);
108
+ const result = await auditPlanning(directory, { lightweight: true });
109
+ assert.equal(result.ok, true);
110
+ assert.equal(result.mode, 'lightweight');
111
+ assert.match(result.coverage, /full-chain coverage not checked/);
112
+ assert.deepEqual(result.omittedRecords, ['srs.md', 'pdp-wbs.md', 'test-plan.md', 'progress.md']);
113
+ assert.deepEqual(await snapshot(directory), before);
114
+ const complete = await auditPlanning(directory);
115
+ assert.ok(codes(complete).includes('missing-record'));
116
+ assert.ok(codes(complete).includes('missing-adr'));
117
+ });
118
+
119
+ test('optional records are audited; fences, comments and evidence files are not authority', async t => {
120
+ const records = {
121
+ ...BASELINE,
122
+ 'rollout.md':
123
+ '# Rollout\n\nUse `ATP-MISSING-01`.\n\n```md\n### ATP-EXAMPLE-01: Example\n```\n<!-- PRD-COMMENT-01 -->\n',
124
+ 'evidence/run.md': 'PRD-EVIDENCE-01\n',
125
+ };
126
+ const result = await auditPlanning(await fixture(t, records));
127
+ assert.deepEqual(
128
+ result.diagnostics
129
+ .filter(diagnostic => diagnostic.code === 'undefined-reference')
130
+ .map(diagnostic => [diagnostic.file, diagnostic.line, diagnostic.id]),
131
+ [['rollout.md', 3, 'ATP-MISSING-01']],
132
+ );
133
+ });
134
+
135
+ test('a mention or evidence table is not a formal ATP declaration', async t => {
136
+ const records = {
137
+ ...BASELINE,
138
+ 'test-plan.md':
139
+ '# Proof\n\n| ATP ID | Evidence | Status |\n| --- | --- | --- |\n| ATP-DEMO-01 | synthetic | verified |\n',
140
+ };
141
+ const result = await auditPlanning(await fixture(t, records));
142
+ assert.ok(codes(result).includes('missing-definition'));
143
+ assert.ok(
144
+ result.diagnostics.some(
145
+ diagnostic => diagnostic.id === 'ATP-DEMO-01' && diagnostic.code === 'undefined-reference',
146
+ ),
147
+ );
148
+ });
149
+
150
+ test('duplicate formal definitions and duplicate progress rows retain diagnostic provenance', async t => {
151
+ const records = {
152
+ ...BASELINE,
153
+ 'prd.md': `${BASELINE['prd.md']}\n### PRD-DEMO-01: Duplicate\n`,
154
+ 'progress.md': `${BASELINE['progress.md']}| WBS-DEMO-10-01 | Duplicate | verified |\n`,
155
+ };
156
+ const result = await auditPlanning(await fixture(t, records));
157
+ assert.ok(
158
+ result.diagnostics.some(
159
+ diagnostic => diagnostic.file === 'prd.md' && /Duplicate.*line/.test(diagnostic.message),
160
+ ),
161
+ );
162
+ assert.ok(
163
+ result.diagnostics.some(
164
+ diagnostic => diagnostic.file === 'progress.md' && /Duplicate.*line/.test(diagnostic.message),
165
+ ),
166
+ );
167
+ });
168
+
169
+ test('progress omissions, extras and explicit WBS status conflicts are structural failures', async t => {
170
+ const records = {
171
+ ...BASELINE,
172
+ 'pdp-wbs.md': `${BASELINE['pdp-wbs.md']}\nStatus: in-progress\n\n#### WBS-DEMO-10-02: Additional task\n\nDependencies: none.\n`,
173
+ 'progress.md': `${BASELINE['progress.md']}| WBS-DEMO-99-01 | Unknown | not-started |\n`,
174
+ };
175
+ const result = await auditPlanning(await fixture(t, records));
176
+ for (const code of ['missing-progress', 'unknown-progress', 'status-conflict'])
177
+ assert.ok(codes(result).includes(code), code);
178
+ });
179
+
180
+ test('completed tasks cannot hide unfinished recorded prerequisites', async t => {
181
+ const records = {
182
+ ...BASELINE,
183
+ 'pdp-wbs.md': `${BASELINE['pdp-wbs.md']}\n#### WBS-DEMO-10-02: Dependent task\n\nDependencies: WBS-DEMO-10-01.\n`,
184
+ 'progress.md': `${BASELINE['progress.md']}| WBS-DEMO-10-02 | Dependent | verified |\n`,
185
+ };
186
+ const result = await auditPlanning(await fixture(t, records), { lightweight: true });
187
+ assert.ok(
188
+ result.diagnostics.some(
189
+ diagnostic => diagnostic.id === 'WBS-DEMO-10-02' && diagnostic.code === 'dependency-state',
190
+ ),
191
+ );
192
+ });
193
+
194
+ test('local Markdown destinations, fragments and reference links are checked', async t => {
195
+ const records = {
196
+ ...BASELINE,
197
+ 'rollout.md':
198
+ '# Rollout\n\n[Missing](./absent.md)\n[Bad fragment](./prd.md#absent)\n[Good][product]\n[Unknown][missing]\n\n[product]: ./prd.md#requirements\n\nPlanned source: `zova/src/target/new.ts`.\n',
199
+ };
200
+ const result = await auditPlanning(await fixture(t, records));
201
+ assert.deepEqual(
202
+ result.diagnostics.map(diagnostic => [diagnostic.code, diagnostic.line]),
203
+ [
204
+ ['link-target', 3],
205
+ ['link-fragment', 4],
206
+ ['link-reference', 6],
207
+ ],
208
+ );
209
+ });
210
+
211
+ test('exact range endpoints do not masquerade as exact traceability', async t => {
212
+ const records = {
213
+ ...BASELINE,
214
+ 'prd.md': BASELINE['prd.md'].replace('`SRS-DEMO-01`', '`SRS-DEMO-01` through `SRS-DEMO-01`'),
215
+ };
216
+ // Remove the reverse PRD mapping so the ambiguous range is the sole proposed edge.
217
+ records['srs.md'] = records['srs.md'].replace('`PRD-DEMO-01`, ', '');
218
+ const result = await auditPlanning(await fixture(t, records));
219
+ assert.ok(
220
+ result.diagnostics.some(
221
+ diagnostic => diagnostic.code === 'traceability-outgoing' && diagnostic.id === 'PRD-DEMO-01',
222
+ ),
223
+ );
224
+ });
225
+
226
+ test('definition body boundaries prevent another requirement from supplying an earlier mapping', async t => {
227
+ const records = {
228
+ ...BASELINE,
229
+ 'prd.md':
230
+ '# Product\n\n## Requirements\n\n### PRD-DEMO-02: Unmapped\n\n### PRD-DEMO-01: Mapped\n\nTraceability: `SRS-DEMO-01`\n',
231
+ };
232
+ const result = await auditPlanning(await fixture(t, records));
233
+ assert.ok(
234
+ result.diagnostics.some(
235
+ diagnostic => diagnostic.id === 'PRD-DEMO-02' && diagnostic.code === 'traceability-outgoing',
236
+ ),
237
+ );
238
+ assert.ok(!result.diagnostics.some(diagnostic => diagnostic.id === 'PRD-DEMO-01'));
239
+ });
240
+
241
+ test('CLI emits deterministic JSON, exits nonzero on gaps and never writes planning files', async t => {
242
+ const directory = await fixture(t, { 'README.md': '# Bounded\n\nATP-MISSING-01\n' });
243
+ const before = await snapshot(directory);
244
+ const args = [CLI, directory, '--lightweight', '--format', 'json'];
245
+ const first = spawnSync(process.execPath, args, { encoding: 'utf8' });
246
+ const second = spawnSync(process.execPath, args, { encoding: 'utf8' });
247
+ assert.equal(first.status, 1);
248
+ assert.equal(first.stdout, second.stdout);
249
+ const result = JSON.parse(first.stdout);
250
+ assert.equal(result.diagnostics[0].id, 'ATP-MISSING-01');
251
+ assert.deepEqual(await snapshot(directory), before);
252
+ const invalid = spawnSync(process.execPath, [CLI, '--fix'], { encoding: 'utf8' });
253
+ assert.equal(invalid.status, 1);
254
+ assert.match(invalid.stderr, /Unknown argument/);
255
+ });
@@ -176,15 +176,16 @@ export function extractCodexEditedFiles(payload) {
176
176
  if (typeof command !== 'string') return [];
177
177
  const entries = [];
178
178
  let pendingUpdate = null;
179
- const headerPattern =
180
- /^\*\*\* (Add|Update|Delete) File:\s*(.+?)\s*$|^\*\*\* Move to:\s*(.+?)\s*$/gm;
179
+ const headerPattern = /^\*\*\* (Add|Update|Delete) File:(.+)$|^\*\*\* Move to:(.+)$/;
181
180
 
182
181
  const flushUpdate = () => {
183
182
  if (pendingUpdate) entries.push(pendingUpdate);
184
183
  pendingUpdate = null;
185
184
  };
186
185
 
187
- for (const match of command.matchAll(headerPattern)) {
186
+ for (const line of command.split(/\r?\n/)) {
187
+ const match = headerPattern.exec(line);
188
+ if (!match) continue;
188
189
  const [, verb, sourcePath, destinationPath] = match;
189
190
  if (destinationPath !== undefined) {
190
191
  if (pendingUpdate) pendingUpdate.filePath = destinationPath.trim();