@xenonbyte/da-vinci-workflow 0.1.26 → 0.2.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.
- package/CHANGELOG.md +31 -0
- package/README.md +28 -65
- package/README.zh-CN.md +28 -65
- package/bin/da-vinci-tui.js +8 -0
- package/commands/claude/dv/continue.md +5 -0
- package/commands/codex/prompts/dv-continue.md +6 -1
- package/commands/gemini/dv/continue.toml +5 -0
- package/commands/templates/dv-continue.shared.md +33 -0
- package/docs/dv-command-reference.md +35 -0
- package/docs/execution-chain-migration.md +46 -0
- package/docs/execution-chain-plan.md +125 -0
- package/docs/prompt-entrypoints.md +8 -0
- package/docs/skill-usage.md +217 -0
- package/docs/workflow-examples.md +10 -0
- package/docs/workflow-overview.md +26 -0
- package/docs/zh-CN/dv-command-reference.md +35 -0
- package/docs/zh-CN/execution-chain-migration.md +46 -0
- package/docs/zh-CN/prompt-entrypoints.md +8 -0
- package/docs/zh-CN/skill-usage.md +217 -0
- package/docs/zh-CN/workflow-examples.md +10 -0
- package/docs/zh-CN/workflow-overview.md +26 -0
- package/lib/artifact-parsers.js +120 -0
- package/lib/audit.js +61 -0
- package/lib/cli.js +351 -13
- package/lib/diff-spec.js +242 -0
- package/lib/execution-signals.js +136 -0
- package/lib/lint-bindings.js +143 -0
- package/lib/lint-spec.js +408 -0
- package/lib/lint-tasks.js +176 -0
- package/lib/planning-parsers.js +567 -0
- package/lib/scaffold.js +193 -0
- package/lib/scope-check.js +603 -0
- package/lib/sidecars.js +369 -0
- package/lib/supervisor-review.js +28 -3
- package/lib/utils.js +10 -2
- package/lib/verify.js +652 -0
- package/lib/workflow-contract.js +107 -0
- package/lib/workflow-persisted-state.js +297 -0
- package/lib/workflow-state.js +785 -0
- package/package.json +13 -3
- package/references/artifact-templates.md +26 -0
- package/references/checkpoints.md +14 -0
- package/references/modes.md +10 -0
- package/tui/catalog.js +1190 -0
- package/tui/index.js +727 -0
package/lib/sidecars.js
ADDED
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
const { STATUS } = require("./workflow-contract");
|
|
4
|
+
const { pathExists, writeFileAtomic } = require("./utils");
|
|
5
|
+
const {
|
|
6
|
+
unique,
|
|
7
|
+
resolveChangeDir,
|
|
8
|
+
parseTasksArtifact,
|
|
9
|
+
parseBindingsArtifact,
|
|
10
|
+
parsePageMapArtifact,
|
|
11
|
+
parseRuntimeSpecs,
|
|
12
|
+
digestFile,
|
|
13
|
+
digestObject,
|
|
14
|
+
readChangeArtifacts,
|
|
15
|
+
readArtifactTexts
|
|
16
|
+
} = require("./planning-parsers");
|
|
17
|
+
|
|
18
|
+
const SCHEMA_VERSION = "1.0.0";
|
|
19
|
+
|
|
20
|
+
function buildEnvelope(projectRoot) {
|
|
21
|
+
return {
|
|
22
|
+
status: STATUS.PASS,
|
|
23
|
+
failures: [],
|
|
24
|
+
warnings: [],
|
|
25
|
+
notes: [],
|
|
26
|
+
projectRoot,
|
|
27
|
+
changeId: null,
|
|
28
|
+
sidecarsDir: null,
|
|
29
|
+
sidecars: {},
|
|
30
|
+
freshness: {}
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function normalizeCollectionItems(values) {
|
|
35
|
+
return unique((values || []).map((value) => String(value || "").trim()).filter(Boolean)).sort((a, b) =>
|
|
36
|
+
a.localeCompare(b)
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function buildSpecIndex(projectRoot, changeId, specRecords) {
|
|
41
|
+
const behaviors = [];
|
|
42
|
+
const states = [];
|
|
43
|
+
const inputs = [];
|
|
44
|
+
const outputs = [];
|
|
45
|
+
const acceptance = [];
|
|
46
|
+
const sourceRefs = [];
|
|
47
|
+
|
|
48
|
+
for (const record of specRecords) {
|
|
49
|
+
const sourcePath = record.path;
|
|
50
|
+
sourceRefs.push({
|
|
51
|
+
path: sourcePath,
|
|
52
|
+
digest: digestFile(path.join(projectRoot, sourcePath))
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
const parsed = record.parsed;
|
|
56
|
+
for (const item of parsed.sections.behavior.items || []) {
|
|
57
|
+
behaviors.push({ specPath: sourcePath, text: item });
|
|
58
|
+
}
|
|
59
|
+
for (const item of parsed.sections.states.items || []) {
|
|
60
|
+
states.push({ specPath: sourcePath, text: item });
|
|
61
|
+
}
|
|
62
|
+
for (const item of parsed.sections.inputs.items || []) {
|
|
63
|
+
inputs.push({ specPath: sourcePath, text: item });
|
|
64
|
+
}
|
|
65
|
+
for (const item of parsed.sections.outputs.items || []) {
|
|
66
|
+
outputs.push({ specPath: sourcePath, text: item });
|
|
67
|
+
}
|
|
68
|
+
for (const item of parsed.sections.acceptance.items || []) {
|
|
69
|
+
acceptance.push({ specPath: sourcePath, text: item });
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
schemaVersion: SCHEMA_VERSION,
|
|
75
|
+
sidecar: "spec.index.json",
|
|
76
|
+
changeId,
|
|
77
|
+
sourceRefs: sourceRefs.sort((a, b) => a.path.localeCompare(b.path)),
|
|
78
|
+
collections: {
|
|
79
|
+
behavior: behaviors
|
|
80
|
+
.map((item, index) => ({
|
|
81
|
+
id: `behavior-${String(index + 1).padStart(3, "0")}`,
|
|
82
|
+
specPath: item.specPath,
|
|
83
|
+
text: item.text
|
|
84
|
+
}))
|
|
85
|
+
.sort((a, b) => a.id.localeCompare(b.id)),
|
|
86
|
+
states: states
|
|
87
|
+
.map((item, index) => ({
|
|
88
|
+
id: `state-${String(index + 1).padStart(3, "0")}`,
|
|
89
|
+
specPath: item.specPath,
|
|
90
|
+
text: item.text
|
|
91
|
+
}))
|
|
92
|
+
.sort((a, b) => a.id.localeCompare(b.id)),
|
|
93
|
+
inputs: inputs
|
|
94
|
+
.map((item, index) => ({
|
|
95
|
+
id: `input-${String(index + 1).padStart(3, "0")}`,
|
|
96
|
+
specPath: item.specPath,
|
|
97
|
+
text: item.text
|
|
98
|
+
}))
|
|
99
|
+
.sort((a, b) => a.id.localeCompare(b.id)),
|
|
100
|
+
outputs: outputs
|
|
101
|
+
.map((item, index) => ({
|
|
102
|
+
id: `output-${String(index + 1).padStart(3, "0")}`,
|
|
103
|
+
specPath: item.specPath,
|
|
104
|
+
text: item.text
|
|
105
|
+
}))
|
|
106
|
+
.sort((a, b) => a.id.localeCompare(b.id)),
|
|
107
|
+
acceptance: acceptance
|
|
108
|
+
.map((item, index) => ({
|
|
109
|
+
id: `acceptance-${String(index + 1).padStart(3, "0")}`,
|
|
110
|
+
specPath: item.specPath,
|
|
111
|
+
text: item.text
|
|
112
|
+
}))
|
|
113
|
+
.sort((a, b) => a.id.localeCompare(b.id))
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function buildTasksIndex(changeId, tasksArtifact, bindingsArtifact, specIndex) {
|
|
119
|
+
const taskSections = Array.isArray(tasksArtifact.sections) ? tasksArtifact.sections : [];
|
|
120
|
+
const groupRecords = tasksArtifact.taskGroups.map((group) => {
|
|
121
|
+
const section =
|
|
122
|
+
taskSections.find((item) => String(item.id || "") === String(group.id || "")) || null;
|
|
123
|
+
const checklistItems = Array.isArray(section && section.checklistItems)
|
|
124
|
+
? section.checklistItems
|
|
125
|
+
: [];
|
|
126
|
+
return {
|
|
127
|
+
id: String(group.id || ""),
|
|
128
|
+
title: group.title,
|
|
129
|
+
checklistItems: checklistItems.map((item) => ({
|
|
130
|
+
checked: item.checked,
|
|
131
|
+
text: item.text
|
|
132
|
+
}))
|
|
133
|
+
};
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
const specTexts = [
|
|
137
|
+
...specIndex.collections.behavior.map((item) => item.text),
|
|
138
|
+
...specIndex.collections.states.map((item) => item.text),
|
|
139
|
+
...specIndex.collections.acceptance.map((item) => item.text)
|
|
140
|
+
]
|
|
141
|
+
.join(" ")
|
|
142
|
+
.toLowerCase();
|
|
143
|
+
|
|
144
|
+
const specLinks = groupRecords.map((group) => {
|
|
145
|
+
const tokens = String(group.title || "")
|
|
146
|
+
.toLowerCase()
|
|
147
|
+
.split(/\s+/)
|
|
148
|
+
.filter((token) => token.length >= 4);
|
|
149
|
+
const matchedTokenCount = tokens.filter((token) => specTexts.includes(token)).length;
|
|
150
|
+
return {
|
|
151
|
+
taskGroupId: group.id,
|
|
152
|
+
matchedTokenCount
|
|
153
|
+
};
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
const bindingLinks = bindingsArtifact.mappings.map((mapping, index) => ({
|
|
157
|
+
id: `binding-link-${String(index + 1).padStart(3, "0")}`,
|
|
158
|
+
implementation: mapping.implementation,
|
|
159
|
+
designPage: mapping.designPage,
|
|
160
|
+
designSource: mapping.designSource || ""
|
|
161
|
+
}));
|
|
162
|
+
|
|
163
|
+
const codeAreas = normalizeCollectionItems(
|
|
164
|
+
tasksArtifact.checklistItems
|
|
165
|
+
.map((item) => item.text)
|
|
166
|
+
.filter((text) => /src\/|pages\/|component|module|route|screen/i.test(text))
|
|
167
|
+
);
|
|
168
|
+
const verificationActions = normalizeCollectionItems(
|
|
169
|
+
tasksArtifact.checklistItems
|
|
170
|
+
.map((item) => item.text)
|
|
171
|
+
.filter((text) => /verify|verification|coverage|test/i.test(text))
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
return {
|
|
175
|
+
schemaVersion: SCHEMA_VERSION,
|
|
176
|
+
sidecar: "tasks.index.json",
|
|
177
|
+
changeId,
|
|
178
|
+
sourceRefs: [],
|
|
179
|
+
taskGroups: groupRecords,
|
|
180
|
+
specLinks,
|
|
181
|
+
bindingLinks,
|
|
182
|
+
codeAreas,
|
|
183
|
+
verificationActions
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function buildPageMapIndex(changeId, pageMapArtifact, pageMapPath, projectRoot) {
|
|
188
|
+
return {
|
|
189
|
+
schemaVersion: SCHEMA_VERSION,
|
|
190
|
+
sidecar: "page-map.index.json",
|
|
191
|
+
changeId,
|
|
192
|
+
sourceRefs: [
|
|
193
|
+
{
|
|
194
|
+
path: path.relative(projectRoot, pageMapPath) || pageMapPath,
|
|
195
|
+
digest: digestFile(pageMapPath)
|
|
196
|
+
}
|
|
197
|
+
],
|
|
198
|
+
pages: pageMapArtifact.pages,
|
|
199
|
+
statesPerPageItems: pageMapArtifact.statesPerPageItems
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function buildBindingsIndex(changeId, bindingsArtifact, bindingsPath, projectRoot) {
|
|
204
|
+
return {
|
|
205
|
+
schemaVersion: SCHEMA_VERSION,
|
|
206
|
+
sidecar: "bindings.index.json",
|
|
207
|
+
changeId,
|
|
208
|
+
sourceRefs: [
|
|
209
|
+
{
|
|
210
|
+
path: path.relative(projectRoot, bindingsPath) || bindingsPath,
|
|
211
|
+
digest: digestFile(bindingsPath)
|
|
212
|
+
}
|
|
213
|
+
],
|
|
214
|
+
mappings: bindingsArtifact.mappings
|
|
215
|
+
.map((mapping, index) => ({
|
|
216
|
+
id: `mapping-${String(index + 1).padStart(3, "0")}`,
|
|
217
|
+
implementation: mapping.implementation,
|
|
218
|
+
designPage: mapping.designPage,
|
|
219
|
+
designSource: mapping.designSource || "",
|
|
220
|
+
screenId: mapping.screenId || ""
|
|
221
|
+
}))
|
|
222
|
+
.sort((a, b) => a.id.localeCompare(b.id)),
|
|
223
|
+
malformed: bindingsArtifact.malformed.sort((a, b) => a.localeCompare(b))
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function attachTaskSources(tasksIndex, artifactPaths, projectRoot) {
|
|
228
|
+
tasksIndex.sourceRefs = [
|
|
229
|
+
{ path: path.relative(projectRoot, artifactPaths.tasksPath), digest: digestFile(artifactPaths.tasksPath) },
|
|
230
|
+
{
|
|
231
|
+
path: path.relative(projectRoot, artifactPaths.bindingsPath),
|
|
232
|
+
digest: digestFile(artifactPaths.bindingsPath)
|
|
233
|
+
}
|
|
234
|
+
];
|
|
235
|
+
return tasksIndex;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function writeSidecarFile(sidecarsDir, fileName, payload) {
|
|
239
|
+
const absolutePath = path.join(sidecarsDir, fileName);
|
|
240
|
+
writeFileAtomic(absolutePath, `${JSON.stringify(payload, null, 2)}\n`);
|
|
241
|
+
return absolutePath;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function generatePlanningSidecars(projectPathInput, options = {}) {
|
|
245
|
+
const projectRoot = path.resolve(projectPathInput || process.cwd());
|
|
246
|
+
const write = options.write !== false;
|
|
247
|
+
const requestedChangeId = options.changeId ? String(options.changeId).trim() : "";
|
|
248
|
+
const result = buildEnvelope(projectRoot);
|
|
249
|
+
|
|
250
|
+
const resolved = resolveChangeDir(projectRoot, requestedChangeId);
|
|
251
|
+
result.failures.push(...resolved.failures);
|
|
252
|
+
result.notes.push(...resolved.notes);
|
|
253
|
+
if (!resolved.changeDir) {
|
|
254
|
+
result.status = STATUS.BLOCK;
|
|
255
|
+
return result;
|
|
256
|
+
}
|
|
257
|
+
result.changeId = resolved.changeId;
|
|
258
|
+
|
|
259
|
+
const artifactPaths = readChangeArtifacts(projectRoot, resolved.changeId);
|
|
260
|
+
const artifacts = readArtifactTexts(artifactPaths);
|
|
261
|
+
const specRecords = parseRuntimeSpecs(resolved.changeDir, projectRoot);
|
|
262
|
+
if (!artifacts.tasks || !artifacts.bindings || !artifacts.pageMap || specRecords.length === 0) {
|
|
263
|
+
if (!artifacts.tasks) {
|
|
264
|
+
result.failures.push("Missing `tasks.md` for sidecar generation.");
|
|
265
|
+
}
|
|
266
|
+
if (!artifacts.bindings) {
|
|
267
|
+
result.failures.push("Missing `pencil-bindings.md` for sidecar generation.");
|
|
268
|
+
}
|
|
269
|
+
if (!artifacts.pageMap) {
|
|
270
|
+
result.failures.push("Missing `page-map.md` for sidecar generation.");
|
|
271
|
+
}
|
|
272
|
+
if (specRecords.length === 0) {
|
|
273
|
+
result.failures.push("Missing runtime spec files for sidecar generation.");
|
|
274
|
+
}
|
|
275
|
+
result.status = STATUS.BLOCK;
|
|
276
|
+
return result;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const tasksArtifact = parseTasksArtifact(artifacts.tasks);
|
|
280
|
+
const bindingsArtifact = parseBindingsArtifact(artifacts.bindings);
|
|
281
|
+
const pageMapArtifact = parsePageMapArtifact(artifacts.pageMap);
|
|
282
|
+
|
|
283
|
+
const specIndex = buildSpecIndex(projectRoot, resolved.changeId, specRecords);
|
|
284
|
+
const tasksIndex = attachTaskSources(
|
|
285
|
+
buildTasksIndex(resolved.changeId, tasksArtifact, bindingsArtifact, specIndex),
|
|
286
|
+
artifactPaths,
|
|
287
|
+
projectRoot
|
|
288
|
+
);
|
|
289
|
+
const pageMapIndex = buildPageMapIndex(resolved.changeId, pageMapArtifact, artifactPaths.pageMapPath, projectRoot);
|
|
290
|
+
const bindingsIndex = buildBindingsIndex(
|
|
291
|
+
resolved.changeId,
|
|
292
|
+
bindingsArtifact,
|
|
293
|
+
artifactPaths.bindingsPath,
|
|
294
|
+
projectRoot
|
|
295
|
+
);
|
|
296
|
+
|
|
297
|
+
const sidecars = {
|
|
298
|
+
"spec.index.json": specIndex,
|
|
299
|
+
"tasks.index.json": tasksIndex,
|
|
300
|
+
"page-map.index.json": pageMapIndex,
|
|
301
|
+
"bindings.index.json": bindingsIndex
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
result.sidecars = sidecars;
|
|
305
|
+
result.freshness = Object.fromEntries(
|
|
306
|
+
Object.entries(sidecars).map(([name, payload]) => [name, digestObject(payload)])
|
|
307
|
+
);
|
|
308
|
+
|
|
309
|
+
const sidecarsDir = path.join(resolved.changeDir, "sidecars");
|
|
310
|
+
result.sidecarsDir = sidecarsDir;
|
|
311
|
+
|
|
312
|
+
if (write) {
|
|
313
|
+
fs.mkdirSync(sidecarsDir, { recursive: true });
|
|
314
|
+
const written = {};
|
|
315
|
+
for (const [name, payload] of Object.entries(sidecars)) {
|
|
316
|
+
written[name] = writeSidecarFile(sidecarsDir, name, payload);
|
|
317
|
+
}
|
|
318
|
+
result.written = written;
|
|
319
|
+
result.notes.push("Sidecars are generated only through `generate-sidecars`; lint/status/verify do not auto-rewrite them.");
|
|
320
|
+
} else {
|
|
321
|
+
result.notes.push("Dry-run mode: sidecars computed in-memory only.");
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
return result;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function formatGenerateSidecarsReport(result) {
|
|
328
|
+
const lines = [
|
|
329
|
+
"Da Vinci generate-sidecars",
|
|
330
|
+
`Project: ${result.projectRoot}`,
|
|
331
|
+
`Change: ${result.changeId || "(not selected)"}`,
|
|
332
|
+
`Status: ${result.status}`
|
|
333
|
+
];
|
|
334
|
+
|
|
335
|
+
if (result.sidecarsDir) {
|
|
336
|
+
lines.push(`Sidecars dir: ${result.sidecarsDir}`);
|
|
337
|
+
}
|
|
338
|
+
if (result.failures.length > 0) {
|
|
339
|
+
lines.push("", "Failures:");
|
|
340
|
+
for (const failure of result.failures) {
|
|
341
|
+
lines.push(`- ${failure}`);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
if (result.warnings.length > 0) {
|
|
345
|
+
lines.push("", "Warnings:");
|
|
346
|
+
for (const warning of result.warnings) {
|
|
347
|
+
lines.push(`- ${warning}`);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
if (Object.keys(result.freshness || {}).length > 0) {
|
|
351
|
+
lines.push("", "Sidecar digests:");
|
|
352
|
+
for (const [name, digest] of Object.entries(result.freshness)) {
|
|
353
|
+
lines.push(`- ${name}: ${digest}`);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
if (result.notes.length > 0) {
|
|
357
|
+
lines.push("", "Notes:");
|
|
358
|
+
for (const note of result.notes) {
|
|
359
|
+
lines.push(`- ${note}`);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
return lines.join("\n");
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
module.exports = {
|
|
366
|
+
SCHEMA_VERSION,
|
|
367
|
+
generatePlanningSidecars,
|
|
368
|
+
formatGenerateSidecarsReport
|
|
369
|
+
};
|
package/lib/supervisor-review.js
CHANGED
|
@@ -111,13 +111,25 @@ function buildReviewerPrompt({ reviewer, projectRoot, changeId, pencilDesignPath
|
|
|
111
111
|
].join("\n");
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
+
function truncateForError(value, maxLength = 240) {
|
|
115
|
+
const text = String(value || "").replace(/\s+/g, " ").trim();
|
|
116
|
+
if (text.length <= maxLength) {
|
|
117
|
+
return text;
|
|
118
|
+
}
|
|
119
|
+
return `${text.slice(0, maxLength)}...`;
|
|
120
|
+
}
|
|
121
|
+
|
|
114
122
|
function parseReviewerPayload(rawText) {
|
|
115
123
|
const payload = parseJsonText(String(rawText || "").trim(), "reviewer JSON payload");
|
|
116
124
|
if (!isPlainObject(payload)) {
|
|
117
|
-
throw new Error(
|
|
125
|
+
throw new Error(
|
|
126
|
+
`Invalid reviewer JSON payload: expected a JSON object. Received: ${truncateForError(rawText)}`
|
|
127
|
+
);
|
|
118
128
|
}
|
|
119
129
|
if (!payload.status) {
|
|
120
|
-
throw new Error(
|
|
130
|
+
throw new Error(
|
|
131
|
+
`Invalid reviewer JSON payload: missing required \`status\`. Received: ${truncateForError(rawText)}`
|
|
132
|
+
);
|
|
121
133
|
}
|
|
122
134
|
const status = normalizeStatus(payload.status);
|
|
123
135
|
const issues = Array.isArray(payload.issues)
|
|
@@ -241,7 +253,20 @@ async function runReviewerWithCodexOnce(options = {}) {
|
|
|
241
253
|
const stdout = String(error.stdout || "").trim();
|
|
242
254
|
const stderr = String(error.stderr || "").trim();
|
|
243
255
|
const timeoutHint = error.killed ? " (timed out)" : "";
|
|
244
|
-
const
|
|
256
|
+
const diagnostics = [];
|
|
257
|
+
if (stderr) {
|
|
258
|
+
diagnostics.push(stderr);
|
|
259
|
+
}
|
|
260
|
+
if (stdout) {
|
|
261
|
+
diagnostics.push(`stdout: ${stdout}`);
|
|
262
|
+
}
|
|
263
|
+
if (error && error.code !== undefined && error.code !== null && !stderr && !stdout) {
|
|
264
|
+
diagnostics.push(`process code: ${String(error.code)}`);
|
|
265
|
+
}
|
|
266
|
+
if (error && error.signal && !stderr && !stdout) {
|
|
267
|
+
diagnostics.push(`signal: ${String(error.signal)}`);
|
|
268
|
+
}
|
|
269
|
+
const message = diagnostics[0] || error.message || "unclassified execution failure";
|
|
245
270
|
throw new Error(`Reviewer \`${reviewer}\` failed${timeoutHint}: ${message}`);
|
|
246
271
|
}
|
|
247
272
|
|
package/lib/utils.js
CHANGED
|
@@ -90,8 +90,16 @@ function writeFileAtomic(targetPath, content, options = {}) {
|
|
|
90
90
|
const encoding = options.encoding || "utf8";
|
|
91
91
|
const tempPath = buildAtomicTempPath(resolvedTargetPath);
|
|
92
92
|
fs.mkdirSync(path.dirname(resolvedTargetPath), { recursive: true });
|
|
93
|
-
|
|
94
|
-
|
|
93
|
+
let shouldCleanupTemp = true;
|
|
94
|
+
try {
|
|
95
|
+
fs.writeFileSync(tempPath, content, encoding);
|
|
96
|
+
fs.renameSync(tempPath, resolvedTargetPath);
|
|
97
|
+
shouldCleanupTemp = false;
|
|
98
|
+
} finally {
|
|
99
|
+
if (shouldCleanupTemp) {
|
|
100
|
+
fs.rmSync(tempPath, { force: true });
|
|
101
|
+
}
|
|
102
|
+
}
|
|
95
103
|
}
|
|
96
104
|
|
|
97
105
|
function writeFileExclusiveAtomic(targetPath, content, options = {}) {
|