@adhdev/daemon-standalone 1.0.41-rc.2 → 1.0.41-rc.4
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/dist/index.js +57 -15
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/vendor/mcp-server/index.js +19 -0
- package/vendor/mcp-server/index.js.map +1 -1
package/dist/index.js
CHANGED
|
@@ -36539,10 +36539,10 @@ var require_dist3 = __commonJS({
|
|
|
36539
36539
|
}
|
|
36540
36540
|
function getDaemonBuildInfo() {
|
|
36541
36541
|
if (cached2) return cached2;
|
|
36542
|
-
const commit = readInjected(true ? "
|
|
36543
|
-
const commitShort = readInjected(true ? "
|
|
36544
|
-
const version2 = readInjected(true ? "1.0.41-rc.
|
|
36545
|
-
const builtAt = readInjected(true ? "2026-08-
|
|
36542
|
+
const commit = readInjected(true ? "da2ff48eebaf23f8f0ebc61f9e17fd9fd317d0d0" : void 0) ?? "unknown";
|
|
36543
|
+
const commitShort = readInjected(true ? "da2ff48e" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
|
|
36544
|
+
const version2 = readInjected(true ? "1.0.41-rc.4" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
|
|
36545
|
+
const builtAt = readInjected(true ? "2026-08-09T07:23:13.303Z" : void 0);
|
|
36546
36546
|
cached2 = builtAt ? { commit, commitShort, version: version2, builtAt } : { commit, commitShort, version: version2 };
|
|
36547
36547
|
return cached2;
|
|
36548
36548
|
}
|
|
@@ -51760,7 +51760,7 @@ Default branch: \`${mesh.defaultBranch}\`` : ""}`);
|
|
|
51760
51760
|
}[policy.dirtyWorkspaceBehavior] || "";
|
|
51761
51761
|
if (dirtyBehavior) rules.push(dirtyBehavior);
|
|
51762
51762
|
rules.push(`- Maximum **${policy.maxParallelTasks}** concurrent WRITE tasks; **${resolveMaxReadonlyParallelTasks(policy.maxParallelTasks)}** concurrent READ-ONLY tasks (\`live_debug_readonly\`) \u2014 read-only work runs under its own, larger cap`);
|
|
51763
|
-
rules.push("- Write tasks are limited to **one active task per node**, so N parallel write tasks need N
|
|
51763
|
+
rules.push("- Write tasks are limited to **one active task per node**, so N parallel write tasks need N *separate branch workspaces* \u2014 clone a worktree per task. **Having N nodes in the mesh does not satisfy this**: the constraint is branch isolation, not node count. Base nodes all share one checkout, so two write tasks on two base nodes still collide on the branch. Read-only tasks are exempt and may stack on a node that is already busy. Both caps are ceilings, not targets");
|
|
51764
51764
|
if (policy.coordinatorIdlePushPolicy === "auto_silent_on_dispatch") {
|
|
51765
51765
|
rules.push("- Delegated-worker completions are **auto-silenced**: the routine idle/completion push for a task you dispatch is suppressed once (approval-needed, failure, and long-running alerts still notify the owner normally)");
|
|
51766
51766
|
}
|
|
@@ -51776,13 +51776,14 @@ ${rules.join("\n")}`;
|
|
|
51776
51776
|
- **Never use local sub-agents.** Do NOT spawn your runtime's own sub-agents (e.g. Claude Code's Task/Explore/Agent tools, or any equivalent in-process agent-spawning tool) to read code, investigate, run RCA, or implement. Such sub-agents execute on the coordinator's machine, outside the mesh \u2014 they escape mesh parallelism, the ledger/audit trail, node capability profiles, and worktree isolation, and leave no \`mesh_task_history\` record. ALL code reading, analysis, RCA, and implementation must be delegated to mesh nodes via \`mesh_enqueue_task\` / \`mesh_send_task\` (use \`task_mode: "live_debug_readonly"\` for read-only investigation), or cross-verified via \`mesh_magi_review\` for read-only fan-out. The coordinator's own actions are limited to \`mesh_*\` tool orchestration and synthesizing results.
|
|
51777
51777
|
- **Front-load task messages.** Include everything the agent needs (files, problem, expected fix) in \`mesh_enqueue_task\` / \`mesh_send_task\`. Append a structured result request at the end: ask the worker to conclude with a JSON block containing \`status\`, \`changedFiles\`, \`gitStatus\`, \`validationResults\`, \`errors\`, \`nextAction\`. The daemon parses this automatically; you can read it from \`mesh_task_history\`.
|
|
51778
51778
|
- **Reuse idle sessions.** For follow-up, retry, commit/push, or cleanup on the same issue, send only the delta to the existing idle session. Start a fresh session only when: (a) branch/worktree isolation is required, (b) the existing session had a dispatch failure or provider mismatch, (c) the transcript/runtime is contaminated or interrupted, (d) the user explicitly asks for a different provider/session, or (e) **the delta is a genuinely NEW subject rather than a continuation** \u2014 a new topic appended to an existing session can be dropped or re-run as the previous task, so give it its own task even when a session sits idle. Continuation of the same issue in an already-idle session is allowed and preferred \u2014 this rule blocks concurrent unrelated work interleaved into a live (still-generating) session, not sequential same-issue follow-ups. The test is subject continuity, not timing: carrying an investigation forward into its own fix is the SAME subject and belongs in that session (Workflow 3f), while an unrelated bug is a new subject even if the same session just went idle.
|
|
51779
|
+
- **Base nodes are reserved for environment-specific testing.** Do NOT use a base node for a general code change. If a task does not strictly test OS- or machine-specific physical behavior (win32 PATH/registry, clean install/uninstall on one OS, that machine's package-manager state, OS-dependent runtime behavior), you MUST clone a worktree with \`mesh_clone_node\` and assign the task there; pin genuine environment tasks to the base with \`required_tags\`/\`target_node_id\` instead. Having several nodes available is NOT branch isolation \u2014 every base node is one shared checkout of the same branch \u2014 and cloning is ~10s with auto-launch starting the session, so there is no dispatch-cost reason to skip it.
|
|
51779
51780
|
- **Worktree affinity.** A worktree is a durable per-branch workspace; keep all of a branch's code_change/fix/review work on its worktree node. Target it with \`required_tags: ["worktree=<branch>"]\` or \`target_node_id\`. Get the id/branch from the \`mesh_clone_node\` result or a live \`mesh_status\` \u2014 the Configured Nodes snapshot won't list a worktree cloned after launch. Untargeted same-branch follow-ups drift to the base node. Only \`convergence\` (merge/push) runs on the base, never pinned to the worktree.
|
|
51780
51781
|
- **Classify task difficulty to save tokens.** For each task you enqueue, judge its execution difficulty and pass \`difficulty\`: \`easy\` (extraction, renames, doc tweaks, trivial fixes), \`medium\` (ordinary feature/bugfix work), \`difficult\` (architecture, tricky debugging, multi-file refactors, subtle reasoning), or \`freeform\`. The mesh's per-difficulty brain preset then runs easy tasks on a cheaper model at low reasoning effort and hard tasks on a stronger model at high effort \u2014 real token savings on simple work. The current presets are shown in the "Brain presets" section below. You may still pass an explicit \`model\`/\`thinkingLevel\` to override the preset for one task.
|
|
51781
51782
|
- **Retune node profiles when routing is a poor fit \u2014 but only with approval.** A node's capability slots (its provider/model/thinking + difficulty range + capability tags, seen via \`mesh_node_slots_list\`) are what task\u2192node fitness routing matches against. If you notice a persistent mismatch \u2014 e.g. every \`difficult\` task lands on a node whose only slot is a cheap model, or a capability a node clearly has isn't declared \u2014 you MAY propose a slot change with \`mesh_node_slots_set\` (write=false). That returns current-vs-proposed; present that diff to the user with a one-line reason and apply (write=true) ONLY after they approve. It is a WHOLESALE replacement of the node's slots, so include the slots you want to keep. Never rewrite a node's profile silently or without a clear routing reason.
|
|
51782
51783
|
- **Bootstrap a node's slots from what's actually installed.** When a node has NO slots configured (routing then falls back to "first available provider"), or CLI agents were newly installed on it, call \`mesh_node_slots_propose({ node_id })\` instead of hand-writing a profile. It detects the node's installed CLI agents and drafts a slot list from them \u2014 read-only, it never writes. Present its \`proposedSlots\` with the \`droppedSlots\` / \`destructive\` fields it reports (a wholesale write would delete any existing hand-tuned slot the draft doesn't reproduce, including providers not currently on PATH), then apply with \`mesh_node_slots_set({ slots: proposedSlots, write: true })\` after approval. It flags \`unknownProvider\` / \`provisional\` slots whose placement is a conservative guess rather than an attested one \u2014 call those out rather than presenting them as settled.
|
|
51783
51784
|
- **Respect explicit provider requests.** Map: Hermes \u2192 \`hermes-cli\`, Claude/Claude Code \u2192 \`claude-cli\`, Codex \u2192 \`codex-cli\`, Gemini \u2192 \`gemini-cli\`, Antigravity \u2192 \`antigravity-cli\`. Never substitute the coordinator's own runtime.
|
|
51784
51785
|
- **Verify via git, not source.** Use \`mesh_git_status\` to confirm side effects. Treat agent summaries as self-reports, not verification.
|
|
51785
|
-
- **Match concurrency to task kind.** Read-only investigation (\`live_debug_readonly\`) carries no isolation or merge cost and is exempt from the one-write-per-node limit: dispatch every independent read-only task at once, up to the read-only cap shown in Policy \u2014 they need neither a free node nor a worktree. Write tasks (\`code_change\`) are limited to ONE active task per node
|
|
51786
|
+
- **Match concurrency to task kind.** Read-only investigation (\`live_debug_readonly\`) carries no isolation or merge cost and is exempt from the one-write-per-node limit: dispatch every independent read-only task at once, up to the read-only cap shown in Policy \u2014 they need neither a free node nor a worktree. Write tasks (\`code_change\`) are limited to ONE active task per node and each needs its OWN branch workspace, so clone a worktree per write task rather than queueing them onto one node \u2014 **or spreading them across base nodes, which is NOT a substitute**: a mesh with four base nodes still has zero branch isolation, because each base node is one shared checkout of the same branch. Ramp up cautiously only when tasks share a base branch or a submodule pointer, where landing order actually matters. Never launch a second session onto work already in flight for the same issue, and never duplicate a session because \`mesh_read_chat\` shows no final message while tool/terminal activity is ongoing. All of this is about *unrelated* work running side by side; it does not mean splitting one line of work across sessions \u2014 successive stages of the same investigation belong in the session that already has the context (see Workflow 3f).
|
|
51786
51787
|
- **Check history first.** Call \`mesh_task_history\` at session start to avoid duplicate work and inform recovery. On failure, read task history before retrying.
|
|
51787
51788
|
- **Don't reopen already-done work after a resume.** Before reopening a reported issue after context compaction or session resume, check current git state and recent session context. If another session has already completed the work, continue from the existing diff/commit instead of starting a duplicate investigation.
|
|
51788
51789
|
- **Sequence shared-base-moving merges.** Parallel dispatch is encouraged, but merging one worktree can advance another in-flight worktree's base \u2014 especially a shared submodule pointer \u2014 turning a clean fast-forward into a diverged rebase (patch-equivalence correctly blocks this). Before merging an in-flight worktree while siblings are also in flight, land in an intentional order, re-clone long-running worktrees from the advanced base, or expect to manually rebase + ff-only the laggards; merging an independent fix mid-flight can strand siblings into a rebase.
|
|
@@ -51894,6 +51895,11 @@ Before doing any coordinator work, confirm that the actual callable tool list in
|
|
|
51894
51895
|
3. **Queue / Delegate** \u2014 The Mesh uses an autonomous pull-based Work Queue:
|
|
51895
51896
|
a. **General Tasks**: Enqueue tasks using \`mesh_enqueue_task\`. Idle node agents will automatically pull tasks from the queue and begin working.
|
|
51896
51897
|
b. **Node Preparation**: Reuse an existing idle session on the correct node/provider before launching a new chat/session. Call \`mesh_launch_session\` only when no suitable session exists, when the user explicitly asks for a fresh provider/session, or when branch/worktree isolation requires it. **A node is not limited to one live session for read-only work** \u2014 \`readonly\`/\`live_debug_readonly\` tasks are exempt from the one-active-per-node invariant, so the SAME node can auto-launch multiple concurrent read-only sessions with no worktree needed. Cloning a worktree costs roughly 10 seconds, so it is cheap enough to create one whenever write work needs a free node; use it for branch isolation, for parallel write tasks (one active write per node), or when a node's read-only queue is deep enough that a second node would clearly finish faster \u2014 call \`mesh_clone_node\` to create the worktree node first.
|
|
51898
|
+
b0. **Base nodes are for environment-specific testing, not for general code changes.** Before dispatching any write task, answer ONE question: *does this task verify the physical environment of a specific machine or OS, or does it only change code?*
|
|
51899
|
+
- **Physical-environment task \u2192 base node, targeted.** Pin it with \`required_tags\` (e.g. \`["os=win32"]\`) or \`target_node_id\`. Examples that genuinely require the real machine: verifying a win32 \`PATH\`/registry/installer layout, a clean-install or uninstall on a specific OS, Homebrew or package-manager state on one particular machine, an OS-dependent runtime behavior (path separators, process spawn, native bindings), or reproducing a bug reported only on that node. A worktree CANNOT substitute for these \u2014 the point is the machine itself.
|
|
51900
|
+
- **Everything else (ordinary \`code_change\`) \u2192 clone a worktree and assign the task there.** Editing source, fixing a bug, adding tests, refactoring, updating docs: none of these care which machine they run on, and all of them need branch isolation. **Do NOT send these to a base node.**
|
|
51901
|
+
- **A mesh with several nodes does not remove this requirement.** Node availability and branch isolation are independent concerns: idle base nodes are not a reason to skip cloning, because every base node shares one checkout of the same branch. "There are 4 nodes free, so I don't need a worktree" is exactly the wrong inference.
|
|
51902
|
+
- **Cloning is nearly free and does NOT cost you an extra dispatch step.** \`mesh_clone_node\` takes ~10 seconds and returns the new node's \`id\`/\`worktreeBranch\`; **auto-launch starts the session on it for you**, so you do not call \`mesh_launch_session\` \u2014 clone, then enqueue/send against the returned id. Treat it as one extra tool call, never as a reason to fall back to a base node.
|
|
51897
51903
|
b1. **Keep a branch's work on its worktree (worktree affinity).** This is about routing a branch's follow-ups back to its OWN worktree \u2014 it is never a reason to avoid creating a NEW worktree for independent work. A worktree node is a durable per-branch workspace, not a one-task throwaway \u2014 implement, review, and fix for the same branch all belong on the SAME worktree, and it lives until its work is converged (merged/pushed) and it is cleaned up. So once you clone a worktree for a branch, route every subsequent \`code_change\`/\`validation\`/fix task for that branch back to that same node: pass \`required_tags: ["worktree=<branch>"]\` or \`target_node_id: <that worktree node's id>\`. **Where to get the node id / tag:** the \`mesh_clone_node\` result returns the new node's \`id\` and \`worktreeBranch\` directly \u2014 use them immediately. The Configured Nodes list in this prompt is a launch-time snapshot and will NOT list a worktree you cloned after this session started, so do not rely on it for freshly-cloned worktrees; take the id/branch from the \`mesh_clone_node\` result, or call \`mesh_status\` to re-list the live nodes (each worktree there advertises its \`worktree=<branch>\` tag). Do NOT leave same-branch follow-ups untargeted \u2014 an untargeted task is claimed by whichever node polls first (usually the base machine node), which strands the work off the branch's worktree. The ONE exception is a \`convergence\` task (merge/push): that is base-only and must NOT be pinned to the worktree.
|
|
51898
51904
|
c. **Targeted Tasks**: Use \`mesh_send_task\` only when you need to bypass the queue and force a specific node to execute a task immediately.
|
|
51899
51905
|
d. For the first dispatch of a new task, provide a **complete, self-contained** instruction that includes all context the agent needs (file paths, line numbers, what to change, why). Do not send partial instructions expecting future follow-up.
|
|
@@ -108379,7 +108385,7 @@ ${formatManifestValidationIssues2(validation2.issues)}`);
|
|
|
108379
108385
|
normalizeRepoMeshDeclarativeConfig: normalizeRepoMeshDeclarativeConfig2,
|
|
108380
108386
|
MESH_JSON_CONFIG_LOCATIONS: MESH_JSON_CONFIG_LOCATIONS2
|
|
108381
108387
|
} = await Promise.resolve().then(() => (init_mesh_json_config(), mesh_json_config_exports));
|
|
108382
|
-
const { mkdirSync:
|
|
108388
|
+
const { mkdirSync: mkdirSync29, writeFileSync: writeFileSync31 } = await import("fs");
|
|
108383
108389
|
const { dirname: dirname22, join: join62 } = await import("path");
|
|
108384
108390
|
const scaffold = buildMeshJsonConfigScaffold2(mesh);
|
|
108385
108391
|
const scaffoldJson = serializeMeshJsonConfigScaffold2(scaffold);
|
|
@@ -108420,8 +108426,8 @@ ${formatManifestValidationIssues2(validation2.issues)}`);
|
|
|
108420
108426
|
note: "Dry-run: nothing written. Re-run with write=true to persist to the repo (commit target). meshes.json is untouched."
|
|
108421
108427
|
};
|
|
108422
108428
|
}
|
|
108423
|
-
|
|
108424
|
-
|
|
108429
|
+
mkdirSync29(dirname22(absolutePath), { recursive: true });
|
|
108430
|
+
writeFileSync31(absolutePath, `${scaffoldJson}
|
|
108425
108431
|
`, "utf-8");
|
|
108426
108432
|
return {
|
|
108427
108433
|
success: true,
|
|
@@ -108490,7 +108496,7 @@ ${formatManifestValidationIssues2(validation2.issues)}`);
|
|
|
108490
108496
|
normalizeRepoMeshDeclarativeConfig: normalizeRepoMeshDeclarativeConfig2,
|
|
108491
108497
|
MESH_JSON_CONFIG_LOCATIONS: MESH_JSON_CONFIG_LOCATIONS2
|
|
108492
108498
|
} = await Promise.resolve().then(() => (init_mesh_json_config(), mesh_json_config_exports));
|
|
108493
|
-
const { existsSync: existsSync62, readFileSync: readFileSync53, mkdirSync:
|
|
108499
|
+
const { existsSync: existsSync62, readFileSync: readFileSync53, mkdirSync: mkdirSync29, writeFileSync: writeFileSync31 } = await import("fs");
|
|
108494
108500
|
const { dirname: dirname22, join: join62 } = await import("path");
|
|
108495
108501
|
const yaml6 = await Promise.resolve().then(() => (init_js_yaml(), js_yaml_exports));
|
|
108496
108502
|
const relativePath = MESH_JSON_CONFIG_LOCATIONS2[0];
|
|
@@ -108555,8 +108561,8 @@ ${formatManifestValidationIssues2(validation2.issues)}`);
|
|
|
108555
108561
|
note: "Dry-run: nothing written. Re-run with write=true to persist. Only the providerDefaults zone is merged; other repo zones are preserved."
|
|
108556
108562
|
};
|
|
108557
108563
|
}
|
|
108558
|
-
|
|
108559
|
-
|
|
108564
|
+
mkdirSync29(dirname22(absolutePath), { recursive: true });
|
|
108565
|
+
writeFileSync31(absolutePath, serialized, "utf-8");
|
|
108560
108566
|
return {
|
|
108561
108567
|
success: true,
|
|
108562
108568
|
written: true,
|
|
@@ -110688,7 +110694,7 @@ ${ptyResult.output.slice(-2e3)}`);
|
|
|
110688
110694
|
workspace
|
|
110689
110695
|
};
|
|
110690
110696
|
}
|
|
110691
|
-
const { existsSync: existsSync62, readFileSync: readFileSync53, writeFileSync:
|
|
110697
|
+
const { existsSync: existsSync62, readFileSync: readFileSync53, writeFileSync: writeFileSync31, copyFileSync: copyFileSync4, mkdirSync: mkdirSync29 } = await import("fs");
|
|
110692
110698
|
const { dirname: dirname22 } = await import("path");
|
|
110693
110699
|
const mcpConfigPath = coordinatorSetup.configPath;
|
|
110694
110700
|
const hermesManualFallback = cliType === "hermes-cli" && configFormat === "hermes_config_yaml" ? createHermesManualMeshCoordinatorSetup(meshId, workspace) : null;
|
|
@@ -110724,7 +110730,7 @@ ${ptyResult.output.slice(-2e3)}`);
|
|
|
110724
110730
|
};
|
|
110725
110731
|
}
|
|
110726
110732
|
try {
|
|
110727
|
-
|
|
110733
|
+
mkdirSync29(dirname22(mcpConfigPath), { recursive: true });
|
|
110728
110734
|
} catch (error48) {
|
|
110729
110735
|
const message = `Could not prepare MCP config path for automatic setup: ${error48?.message || error48}`;
|
|
110730
110736
|
LOG2.error("MeshCoordinator", message);
|
|
@@ -110761,7 +110767,7 @@ ${ptyResult.output.slice(-2e3)}`);
|
|
|
110761
110767
|
}
|
|
110762
110768
|
};
|
|
110763
110769
|
try {
|
|
110764
|
-
|
|
110770
|
+
writeFileSync31(mcpConfigPath, serializeMeshCoordinatorMcpConfig(mcpConfig, configFormat), "utf-8");
|
|
110765
110771
|
} catch (error48) {
|
|
110766
110772
|
const message = `Could not write MCP config for automatic setup: ${error48?.message || error48}`;
|
|
110767
110773
|
LOG2.error("MeshCoordinator", message);
|
|
@@ -111961,6 +111967,35 @@ ${ptyResult.output.slice(-2e3)}`);
|
|
|
111961
111967
|
[... ${omitted} chars omitted ...]
|
|
111962
111968
|
${tail}`;
|
|
111963
111969
|
}
|
|
111970
|
+
var REFINE_VALIDATION_LOG_DIR = (0, import_path18.join)(".adhdev", "logs");
|
|
111971
|
+
function writeValidationFailureLog(workspace, index, candidate, streams, now = () => /* @__PURE__ */ new Date()) {
|
|
111972
|
+
try {
|
|
111973
|
+
const dir = (0, import_path18.join)(workspace, REFINE_VALIDATION_LOG_DIR);
|
|
111974
|
+
fs43.mkdirSync(dir, { recursive: true });
|
|
111975
|
+
const stamp = now().toISOString().replace(/[:.]/g, "-");
|
|
111976
|
+
const file2 = (0, import_path18.join)(dir, `refine-${stamp}-${index}.log`);
|
|
111977
|
+
const asText = (v) => typeof v === "string" ? v : v == null ? "" : String(v);
|
|
111978
|
+
const shown = candidate.displayCommand || [candidate.command, ...candidate.args || []].join(" ");
|
|
111979
|
+
fs43.writeFileSync(
|
|
111980
|
+
file2,
|
|
111981
|
+
`# refine validation failure
|
|
111982
|
+
# command: ${shown}
|
|
111983
|
+
# cwd: ${candidate.cwd || workspace}
|
|
111984
|
+
# recorded: ${now().toISOString()}
|
|
111985
|
+
|
|
111986
|
+
=== stdout ===
|
|
111987
|
+
${asText(streams.stdout)}
|
|
111988
|
+
|
|
111989
|
+
=== stderr ===
|
|
111990
|
+
${asText(streams.stderr)}
|
|
111991
|
+
`,
|
|
111992
|
+
"utf8"
|
|
111993
|
+
);
|
|
111994
|
+
return file2;
|
|
111995
|
+
} catch {
|
|
111996
|
+
return void 0;
|
|
111997
|
+
}
|
|
111998
|
+
}
|
|
111964
111999
|
function isSpawnResolutionError(error48) {
|
|
111965
112000
|
if (!error48) return false;
|
|
111966
112001
|
if (error48.code === "ENOENT" && typeof error48.syscall === "string" && error48.syscall.startsWith("spawn")) return true;
|
|
@@ -113361,10 +113396,17 @@ ${mergeTreeErr?.stderr || ""}`;
|
|
|
113361
113396
|
const spawnResolutionFailed = isSpawnResolutionError(error48);
|
|
113362
113397
|
const stderr = truncateValidationOutput(error48?.stderr || error48?.message);
|
|
113363
113398
|
const missingDependencyFailure = !spawnResolutionFailed && /Cannot find module|MODULE_NOT_FOUND|node_modules|command not found|not found/i.test(stderr);
|
|
113399
|
+
const failureLogPath = writeValidationFailureLog(
|
|
113400
|
+
workspace,
|
|
113401
|
+
summary.commandsRun.length,
|
|
113402
|
+
{ command: candidate.command, args: candidate.args, displayCommand: candidate.displayCommand, cwd },
|
|
113403
|
+
{ stdout: error48?.stdout, stderr: error48?.stderr || error48?.message }
|
|
113404
|
+
);
|
|
113364
113405
|
summary.commandsRun.push(commandRecord(candidate, cwd, startedAt, error48, false, {
|
|
113365
113406
|
exitCode: typeof error48?.code === "number" ? error48.code : null,
|
|
113366
113407
|
signal: typeof error48?.signal === "string" ? error48.signal : null,
|
|
113367
113408
|
timedOut: error48?.killed === true || /timed out/i.test(String(error48?.message || "")),
|
|
113409
|
+
...failureLogPath ? { failureLogPath } : {},
|
|
113368
113410
|
...spawnResolutionFailed ? { failureKind: "spawn_resolution_failed", resolvedCommand } : missingDependencyFailure ? { failureKind: "missing_dependencies" } : {}
|
|
113369
113411
|
}));
|
|
113370
113412
|
summary.status = "failed";
|