Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion LifeOS/install/LIFEOS/TOOLS/Inference.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,32 @@
*/

import { spawn } from "child_process";
import { existsSync } from "fs";

// Resolve `claude` to an absolute path. Bare `spawn('claude')` relies on PATH,
// which is fine interactively but ENOENTs under launchd/cron (minimal PATH lacks
// ~/.local/bin) — the failure that silently wedges any scheduled inference caller.
let cachedClaudeBin: string | null = null;
export function resolveClaudeBin(): string {
if (cachedClaudeBin) return cachedClaudeBin;
const explicit = process.env.CLAUDE_BIN;
if (explicit && existsSync(explicit)) return (cachedClaudeBin = explicit);
const home = process.env.HOME ?? "";
const candidates = [
home ? `${home}/.local/bin/claude` : "",
home ? `${home}/.claude/local/claude` : "",
"/opt/homebrew/bin/claude",
"/usr/local/bin/claude",
"/usr/bin/claude",
].filter(Boolean);
for (const p of candidates) {
if (existsSync(p)) return (cachedClaudeBin = p);
}
for (const dir of (process.env.PATH ?? "").split(":")) {
if (dir && existsSync(`${dir}/claude`)) return (cachedClaudeBin = `${dir}/claude`);
}
return (cachedClaudeBin = "claude"); // last resort: preserve prior behavior
}

/** The four run levels — mirrors models.ts EffortLevel. */
export type InferenceLevel = 'low' | 'medium' | 'high' | 'max';
Expand Down Expand Up @@ -182,7 +208,7 @@ async function inferenceAttempt(options: InferenceOptions, modelOverride?: strin
let stdout = '';
let stderr = '';

const proc = spawn('claude', args, {
const proc = spawn(resolveClaudeBin(), args, {
env,
stdio: ['pipe', 'pipe', 'pipe'],
});
Expand Down
7 changes: 4 additions & 3 deletions LifeOS/install/LIFEOS/TOOLS/algorithm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ import { resolve, basename, join, dirname } from "path";
import { spawnSync, spawn } from "child_process";
import { randomUUID } from "crypto";
import { generateISATemplate } from "../../../.claude/hooks/lib/isa-template";
import { resolveClaudeBin } from "./Inference";

// Normalize env path vars that Claude Code injects without shell expansion (LifeOS#1404)
for (const k of ["LIFEOS_DIR", "LIFEOS_CONFIG_DIR", "PROJECTS_DIR"]) {
Expand Down Expand Up @@ -842,7 +843,7 @@ async function runParallelIteration(
const processes = assignments.map(assignment => {
const criterion = assignment.criteriaDetails[0]; // One criterion per agent
const prompt = buildWorkerPrompt(isaPath, assignment.agentId, criterion, iteration);
const proc = Bun.spawn(["claude", "-p", prompt,
const proc = Bun.spawn([resolveClaudeBin(), "-p", prompt,
"--allowedTools", "Edit,Write,Bash,Read,Glob,Grep,WebFetch,WebSearch,NotebookEdit",
], {
cwd: dirname(isaPath),
Expand Down Expand Up @@ -1437,7 +1438,7 @@ function runInteractive(isaPath: string): void {
console.log(` Launching claude...\n`);

// Launch interactive claude session with ISA context
const child = spawn("claude", [
const child = spawn(resolveClaudeBin(), [
prompt,
"--allowedTools", "Edit,Write,Bash,Read,Glob,Grep,WebFetch,WebSearch,Task,TaskCreate,TaskUpdate,TaskList,NotebookEdit",
], {
Expand Down Expand Up @@ -1503,7 +1504,7 @@ function runIdeate(
console.log(` Launching claude...\n`);

// Launch interactive claude session with ideate context
const child = spawn("claude", [
const child = spawn(resolveClaudeBin(), [
prompt,
"--allowedTools", "Edit,Write,Bash,Read,Glob,Grep,WebFetch,WebSearch,Task,TaskCreate,TaskUpdate,TaskList,NotebookEdit",
], {
Expand Down
Loading