rex-claude 4.0.0 → 6.0.0

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 (38) hide show
  1. package/dist/agents-JIZXXASP.js +853 -0
  2. package/dist/app-3VWDSH5F.js +248 -0
  3. package/dist/audio-US2J627E.js +196 -0
  4. package/dist/audit-ZVTGE4L4.js +8 -0
  5. package/dist/call-AQZ3Z5SE.js +143 -0
  6. package/dist/chunk-5ND7JYY3.js +62 -0
  7. package/dist/chunk-6SRV2I2H.js +56 -0
  8. package/dist/{setup-AO3MW46W.js → chunk-A7ZLQUOX.js} +93 -16
  9. package/dist/chunk-E5UYN3W7.js +105 -0
  10. package/dist/chunk-HAHJD3QH.js +147 -0
  11. package/dist/{init-DLFEGD6O.js → chunk-KR7ISYZH.js} +328 -29
  12. package/dist/chunk-LTOM55UV.js +154 -0
  13. package/dist/chunk-PDX44BCA.js +11 -0
  14. package/dist/chunk-PPGYFMU5.js +67 -0
  15. package/dist/{chunk-7AGI43F5.js → chunk-WBMVBMWB.js} +4 -2
  16. package/dist/{context-FN5O5YBI.js → context-XNCG2M5Q.js} +2 -1
  17. package/dist/daemon-5KNSNFTD.js +208 -0
  18. package/dist/gateway-YLP66MCQ.js +2273 -0
  19. package/dist/hammerspoon/rex-call-watcher.lua +186 -0
  20. package/dist/index.js +309 -15
  21. package/dist/init-RDZFIBLA.js +30 -0
  22. package/dist/install-63JBDPRU.js +41 -0
  23. package/dist/{llm-YRORUH7E.js → llm-RALIPIMI.js} +2 -1
  24. package/dist/mcp_registry-DX4GGSP6.js +514 -0
  25. package/dist/migrate-GDO37TI5.js +87 -0
  26. package/dist/{optimize-UKMAGQQE.js → optimize-5TE5RKZV.js} +2 -1
  27. package/dist/paths-4SECM6E6.js +38 -0
  28. package/dist/preload-I3MYBVNU.js +78 -0
  29. package/dist/projects-V6TSLO7E.js +17 -0
  30. package/dist/{prune-2PPIVDXK.js → prune-B7F5B5OF.js} +2 -1
  31. package/dist/recategorize-YXYIMQLZ.js +155 -0
  32. package/dist/router-2JD34COX.js +12 -0
  33. package/dist/self-improve-YK7RCYF4.js +197 -0
  34. package/dist/setup-KNDTVFO6.js +8 -0
  35. package/dist/skills-AIWFY5NH.js +374 -0
  36. package/dist/voice-RITC3EVC.js +248 -0
  37. package/package.json +12 -3
  38. package/dist/gateway-EKMU5D7J.js +0 -784
@@ -0,0 +1,853 @@
1
+ #!/usr/bin/env node
2
+ import "./chunk-PDX44BCA.js";
3
+
4
+ // src/agents.ts
5
+ import { homedir } from "os";
6
+ import { existsSync, mkdirSync, readFileSync, writeFileSync, appendFileSync } from "fs";
7
+ import { join } from "path";
8
+ import { spawn, execSync } from "child_process";
9
+ var HOME = homedir();
10
+ var ROOT_DIR = join(HOME, ".claude", "rex", "agents");
11
+ var STORE_FILE = join(ROOT_DIR, "agents.json");
12
+ var LOGS_DIR = join(ROOT_DIR, "logs");
13
+ var RUNTIME_DIR = join(ROOT_DIR, "runtime");
14
+ var OLLAMA_URL = process.env.OLLAMA_URL || "http://localhost:11434";
15
+ var COLORS = {
16
+ reset: "\x1B[0m",
17
+ bold: "\x1B[1m",
18
+ dim: "\x1B[2m",
19
+ green: "\x1B[32m",
20
+ yellow: "\x1B[33m",
21
+ red: "\x1B[31m",
22
+ cyan: "\x1B[36m",
23
+ magenta: "\x1B[35m"
24
+ };
25
+ var PROFILE_TEMPLATES = {
26
+ scout: {
27
+ prompt: "You are a codebase scout. Read files, analyze structure, identify issues, and report findings. Do NOT modify any files.",
28
+ allowedTools: ["Read", "Glob", "Grep", "Bash(ls *)", "Bash(git *)"],
29
+ maxTurns: 10,
30
+ model: "claude",
31
+ intervalSec: 0
32
+ // one-shot by default
33
+ },
34
+ reviewer: {
35
+ prompt: "You are a strict code reviewer. Analyze recent changes, find bugs, security issues, missing tests. Report with file:line references and severity. Do NOT modify files.",
36
+ allowedTools: ["Read", "Glob", "Grep", "Bash(git *)", "Bash(npm test *)", "Bash(pnpm test *)"],
37
+ maxTurns: 20,
38
+ model: "claude",
39
+ intervalSec: 0
40
+ },
41
+ fixer: {
42
+ prompt: "You are an autonomous bug fixer. Identify the bug, understand root cause, implement the fix, verify it compiles/passes tests. Create a git commit when done.",
43
+ allowedTools: ["Read", "Edit", "Write", "Glob", "Grep", "Bash"],
44
+ maxTurns: 30,
45
+ model: "claude",
46
+ intervalSec: 0
47
+ },
48
+ architect: {
49
+ prompt: "You are a senior architect. Analyze the full codebase, identify tech debt, propose refactoring plans, check for scalability issues. Write a report to docs/audit.md.",
50
+ allowedTools: ["Read", "Write", "Glob", "Grep", "Bash(git *)", "Bash(wc *)", "Bash(cloc *)"],
51
+ maxTurns: 40,
52
+ model: "claude",
53
+ intervalSec: 0
54
+ },
55
+ worker: {
56
+ prompt: "You are an autonomous worker. Execute the assigned task end-to-end: plan, implement, test, verify. If something fails, debug and retry. Do not stop until the task is complete or you have exhausted all approaches.",
57
+ allowedTools: ["Read", "Edit", "Write", "Glob", "Grep", "Bash"],
58
+ maxTurns: 50,
59
+ model: "claude",
60
+ intervalSec: 0
61
+ },
62
+ monitor: {
63
+ prompt: "You are a system monitor. Check health of services, verify builds pass, check for stale branches, report anomalies. Run periodically.",
64
+ allowedTools: ["Read", "Glob", "Grep", "Bash(git *)", "Bash(curl *)", "Bash(rex *)"],
65
+ maxTurns: 15,
66
+ model: "claude",
67
+ intervalSec: 3600
68
+ // hourly
69
+ },
70
+ watchdog: {
71
+ prompt: `You are the REX Watchdog. Monitor system health and fix issues automatically.
72
+ Check: 1) rex ingest runs and captures new sessions (delta ingest for growing files)
73
+ 2) Ollama is running for embeddings 3) LaunchAgents are loaded 4) Memory DB is not corrupted.
74
+ If ingest is behind, run "rex ingest" to catch up. If Ollama is down, try "ollama serve".
75
+ Report what you checked and any actions taken.`,
76
+ allowedTools: ["Read", "Bash(rex *)", "Bash(ollama *)", "Bash(launchctl *)", "Bash(ps *)", "Bash(curl *)"],
77
+ maxTurns: 15,
78
+ model: "claude",
79
+ intervalSec: 1800
80
+ // every 30 min
81
+ },
82
+ "local-analyst": {
83
+ prompt: "Analyze the project and provide insights on architecture, patterns, and potential improvements.",
84
+ allowedTools: [],
85
+ maxTurns: 1,
86
+ model: "local",
87
+ intervalSec: 0
88
+ },
89
+ orchestrator: {
90
+ prompt: `You are the REX Orchestrator (Opus). You supervise all agents, detect loops/crashes, and coordinate work.
91
+ You know all agent profiles and can create, start, stop, and monitor agents.
92
+ Use local LLM (Ollama) for simple tasks and Claude for complex ones.
93
+ If an agent fails 3 times consecutively, stop it and alert.
94
+ Always explain what you are doing and why.`,
95
+ allowedTools: ["Read", "Edit", "Write", "Glob", "Grep", "Bash"],
96
+ maxTurns: 100,
97
+ model: "claude",
98
+ intervalSec: 0
99
+ }
100
+ };
101
+ function ensureDirs() {
102
+ for (const d of [ROOT_DIR, LOGS_DIR, RUNTIME_DIR]) {
103
+ if (!existsSync(d)) mkdirSync(d, { recursive: true });
104
+ }
105
+ }
106
+ function readStore() {
107
+ ensureDirs();
108
+ try {
109
+ if (existsSync(STORE_FILE)) {
110
+ const parsed = JSON.parse(readFileSync(STORE_FILE, "utf-8"));
111
+ if (Array.isArray(parsed.agents)) return parsed;
112
+ }
113
+ } catch {
114
+ }
115
+ return { agents: [] };
116
+ }
117
+ function writeStore(store) {
118
+ ensureDirs();
119
+ writeFileSync(STORE_FILE, JSON.stringify(store, null, 2));
120
+ }
121
+ function runtimePath(id) {
122
+ return join(RUNTIME_DIR, `${id}.json`);
123
+ }
124
+ function readRuntime(id) {
125
+ try {
126
+ const p = runtimePath(id);
127
+ if (existsSync(p)) return JSON.parse(readFileSync(p, "utf-8"));
128
+ } catch {
129
+ }
130
+ return { id, pid: null, running: false, consecutiveErrors: 0, currentRetry: 0 };
131
+ }
132
+ function writeRuntime(id, data) {
133
+ ensureDirs();
134
+ writeFileSync(runtimePath(id), JSON.stringify(data, null, 2));
135
+ }
136
+ function isPidRunning(pid) {
137
+ if (!pid || pid <= 0) return false;
138
+ try {
139
+ process.kill(pid, 0);
140
+ return true;
141
+ } catch {
142
+ return false;
143
+ }
144
+ }
145
+ function logPath(id) {
146
+ return join(LOGS_DIR, `${id}.log`);
147
+ }
148
+ function appendLog(id, text) {
149
+ ensureDirs();
150
+ const now = (/* @__PURE__ */ new Date()).toISOString();
151
+ appendFileSync(logPath(id), `[${now}] ${text}
152
+ `);
153
+ }
154
+ function slug(text) {
155
+ return text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48) || "agent";
156
+ }
157
+ function parseFlag(args, name) {
158
+ const eqIdx = args.findIndex((a) => a.startsWith(`${name}=`));
159
+ if (eqIdx >= 0) return args[eqIdx].split("=").slice(1).join("=");
160
+ const idx = args.findIndex((a) => a === name);
161
+ if (idx < 0) return null;
162
+ return args[idx + 1] || null;
163
+ }
164
+ function findAgent(store, idOrName) {
165
+ return store.agents.find((a) => a.id === idOrName || a.name === idOrName) || null;
166
+ }
167
+ function buildAgentContext(agent) {
168
+ const parts = [];
169
+ parts.push("# Agent Context (auto-injected by REX)");
170
+ parts.push(`Agent: ${agent.name} (${agent.profile})`);
171
+ parts.push(`Time: ${(/* @__PURE__ */ new Date()).toISOString()}`);
172
+ parts.push(`Run #${agent.totalRuns + 1}`);
173
+ if (agent.cwd) {
174
+ const claudeMd = join(agent.cwd, "CLAUDE.md");
175
+ if (existsSync(claudeMd)) {
176
+ try {
177
+ const content = readFileSync(claudeMd, "utf-8").slice(0, 2e3);
178
+ parts.push(`
179
+ # Project Context (from CLAUDE.md)
180
+ ${content}`);
181
+ } catch {
182
+ }
183
+ }
184
+ }
185
+ const rt = readRuntime(agent.id);
186
+ if (rt.lastError) {
187
+ parts.push(`
188
+ # Previous Run Error (self-correct this)
189
+ ${rt.lastError.slice(0, 500)}`);
190
+ }
191
+ parts.push("\n# Instructions");
192
+ parts.push(agent.prompt);
193
+ parts.push("\n# Rules");
194
+ parts.push("- Be autonomous: plan, execute, verify, retry if needed.");
195
+ parts.push("- If a tool call fails, analyze the error and try a different approach.");
196
+ parts.push("- If stuck after 3 attempts at the same approach, stop and report what you tried.");
197
+ parts.push("- Always verify your work before declaring done (build, test, or read back).");
198
+ parts.push("- Be concise in your output. Focus on actions taken and results.");
199
+ return parts.join("\n");
200
+ }
201
+ function findClaudeCli() {
202
+ try {
203
+ const path = execSync("which claude", { encoding: "utf-8", timeout: 5e3 }).trim();
204
+ return path || null;
205
+ } catch {
206
+ return null;
207
+ }
208
+ }
209
+ async function runWithClaude(agent, task) {
210
+ const claudePath = findClaudeCli();
211
+ if (!claudePath) {
212
+ return { ok: false, output: "", exitCode: 1, duration: 0, toolsUsed: [], turns: 0, error: "Claude CLI not found. Install: npm i -g @anthropic-ai/claude-code" };
213
+ }
214
+ const systemPrompt = buildAgentContext(agent);
215
+ const prompt = task || agent.prompt;
216
+ const start = Date.now();
217
+ const args = [
218
+ "-p",
219
+ prompt,
220
+ "--append-system-prompt",
221
+ systemPrompt,
222
+ "--output-format",
223
+ "json",
224
+ "--max-turns",
225
+ String(agent.maxTurns)
226
+ ];
227
+ if (agent.allowedTools.length > 0) {
228
+ for (const tool of agent.allowedTools) {
229
+ args.push("--allowedTools", tool);
230
+ }
231
+ args.push("--permission-mode", "acceptEdits");
232
+ }
233
+ if (agent.mcpServers && agent.mcpServers.length > 0) {
234
+ for (const mcp of agent.mcpServers) {
235
+ args.push("--mcp-server", mcp);
236
+ }
237
+ }
238
+ return new Promise((resolve) => {
239
+ const cwd = agent.cwd || process.cwd();
240
+ const env = { ...process.env, CLAUDE_CODE_ENTRYPOINT: "rex-agent" };
241
+ delete env.CLAUDECODE;
242
+ delete env.CLAUDE_CODE_SESSION;
243
+ const agentConfigDir = join(HOME, `.claude-agent-${agent.id}`);
244
+ if (!existsSync(agentConfigDir)) mkdirSync(agentConfigDir, { recursive: true });
245
+ env.CLAUDE_CONFIG_DIR = agentConfigDir;
246
+ const child = spawn(claudePath, args, {
247
+ cwd,
248
+ env,
249
+ stdio: ["ignore", "pipe", "pipe"],
250
+ timeout: 6e5
251
+ // 10 min max
252
+ });
253
+ let stdout = "";
254
+ let stderr = "";
255
+ child.stdout.on("data", (d) => {
256
+ stdout += d.toString();
257
+ });
258
+ child.stderr.on("data", (d) => {
259
+ stderr += d.toString();
260
+ });
261
+ child.on("close", (code) => {
262
+ const duration = Date.now() - start;
263
+ let output = "";
264
+ let toolsUsed = [];
265
+ let turns = 0;
266
+ try {
267
+ const parsed = JSON.parse(stdout);
268
+ output = parsed.result || parsed.output || "";
269
+ turns = parsed.num_turns || 0;
270
+ if (parsed.tools_used) toolsUsed = parsed.tools_used;
271
+ } catch {
272
+ const lines = stdout.trim().split("\n");
273
+ for (const line of lines.reverse()) {
274
+ try {
275
+ const obj = JSON.parse(line);
276
+ if (obj.result) {
277
+ output = obj.result;
278
+ break;
279
+ }
280
+ if (obj.type === "result" && obj.result) {
281
+ output = obj.result;
282
+ break;
283
+ }
284
+ } catch {
285
+ }
286
+ }
287
+ if (!output) output = stdout || stderr;
288
+ }
289
+ resolve({
290
+ ok: code === 0,
291
+ output: output.slice(0, 1e4),
292
+ // cap log size
293
+ exitCode: code,
294
+ duration,
295
+ toolsUsed,
296
+ turns,
297
+ error: code !== 0 ? (stderr || `Exit code ${code}`).slice(0, 2e3) : void 0
298
+ });
299
+ });
300
+ child.on("error", (err) => {
301
+ resolve({
302
+ ok: false,
303
+ output: "",
304
+ exitCode: 1,
305
+ duration: Date.now() - start,
306
+ toolsUsed: [],
307
+ turns: 0,
308
+ error: err.message
309
+ });
310
+ });
311
+ });
312
+ }
313
+ async function runWithOllama(agent, task) {
314
+ const start = Date.now();
315
+ const prompt = buildAgentContext(agent) + "\n\nTask: " + (task || agent.prompt);
316
+ let model = agent.localModel || "qwen3.5:4b";
317
+ try {
318
+ const tagsRes = await fetch(`${OLLAMA_URL}/api/tags`, { signal: AbortSignal.timeout(3e3) });
319
+ if (tagsRes.ok) {
320
+ const tags = await tagsRes.json();
321
+ const names = (tags.models || []).map((m) => m.name || "").filter(Boolean);
322
+ if (!names.includes(model)) {
323
+ const base = model.split(":")[0];
324
+ model = names.find((n) => n.includes(base)) || names.find((n) => !n.includes("embed")) || model;
325
+ }
326
+ }
327
+ } catch {
328
+ }
329
+ try {
330
+ const res = await fetch(`${OLLAMA_URL}/api/generate`, {
331
+ method: "POST",
332
+ headers: { "Content-Type": "application/json" },
333
+ body: JSON.stringify({ model, prompt, stream: false, keep_alive: "30s" }),
334
+ signal: AbortSignal.timeout(3e5)
335
+ });
336
+ if (!res.ok) throw new Error(`Ollama ${res.status}`);
337
+ const data = await res.json();
338
+ const output = (data.response || "").trim();
339
+ return {
340
+ ok: true,
341
+ output: output.slice(0, 1e4),
342
+ exitCode: 0,
343
+ duration: Date.now() - start,
344
+ toolsUsed: [],
345
+ turns: 1
346
+ };
347
+ } catch (e) {
348
+ return {
349
+ ok: false,
350
+ output: "",
351
+ exitCode: 1,
352
+ duration: Date.now() - start,
353
+ toolsUsed: [],
354
+ turns: 0,
355
+ error: e.message
356
+ };
357
+ }
358
+ }
359
+ async function executeAgent(agent, task) {
360
+ const runner = agent.model === "claude" ? runWithClaude : runWithOllama;
361
+ let lastResult = { ok: false, output: "", exitCode: 1, duration: 0, toolsUsed: [], turns: 0 };
362
+ for (let attempt = 0; attempt <= agent.maxRetries; attempt++) {
363
+ if (attempt > 0) {
364
+ appendLog(agent.id, `RETRY ${attempt}/${agent.maxRetries} \u2014 self-correcting from: ${lastResult.error?.slice(0, 200)}`);
365
+ const rt = readRuntime(agent.id);
366
+ writeRuntime(agent.id, { ...rt, currentRetry: attempt, lastError: lastResult.error || lastResult.output.slice(0, 500) });
367
+ await new Promise((r) => setTimeout(r, 5e3 * Math.pow(3, attempt - 1)));
368
+ }
369
+ lastResult = await runner(agent, task);
370
+ if (lastResult.ok) {
371
+ return lastResult;
372
+ }
373
+ appendLog(agent.id, `ATTEMPT ${attempt + 1} FAILED (${lastResult.duration}ms): ${lastResult.error?.slice(0, 300)}`);
374
+ }
375
+ return lastResult;
376
+ }
377
+ function listAgents(jsonMode) {
378
+ const store = readStore();
379
+ const rows = store.agents.map((a) => {
380
+ const rt = readRuntime(a.id);
381
+ const running = rt.running && isPidRunning(rt.pid);
382
+ return { id: a.id, name: a.name, profile: a.profile, model: a.model, enabled: a.enabled, running, lastRunAt: a.lastRunAt || null, totalRuns: a.totalRuns, totalErrors: a.totalErrors, intervalSec: a.intervalSec };
383
+ });
384
+ if (jsonMode) {
385
+ console.log(JSON.stringify({ agents: rows }, null, 2));
386
+ return;
387
+ }
388
+ if (rows.length === 0) {
389
+ console.log(`No agents. Create one:
390
+ rex agents create <${Object.keys(PROFILE_TEMPLATES).join("|")}> [name] [--cwd path] [--task "..."]`);
391
+ return;
392
+ }
393
+ const line = "\u2500".repeat(60);
394
+ console.log(`
395
+ ${COLORS.bold} REX Agents${COLORS.reset}
396
+ ${line}`);
397
+ for (const r of rows) {
398
+ const status = r.running ? `${COLORS.green}RUNNING${COLORS.reset}` : r.enabled ? `${COLORS.yellow}IDLE${COLORS.reset}` : `${COLORS.dim}DISABLED${COLORS.reset}`;
399
+ const model = r.model === "claude" ? `${COLORS.magenta}claude${COLORS.reset}` : `${COLORS.cyan}local${COLORS.reset}`;
400
+ const schedule = r.intervalSec > 0 ? `every ${r.intervalSec}s` : "one-shot";
401
+ console.log(` ${status} ${COLORS.bold}${r.name}${COLORS.reset} (${r.profile}) ${model} \u2014 ${schedule}`);
402
+ console.log(` ${COLORS.dim} id=${r.id} runs=${r.totalRuns} errors=${r.totalErrors} last=${r.lastRunAt || "never"}${COLORS.reset}`);
403
+ }
404
+ console.log(line);
405
+ }
406
+ function createAgent(args) {
407
+ const profile = args[0];
408
+ if (!profile || !PROFILE_TEMPLATES[profile]) {
409
+ console.log(`Usage: rex agents create <${Object.keys(PROFILE_TEMPLATES).join("|")}> [name] [--cwd path] [--task "..."] [--model claude|local] [--interval sec] [--team name]`);
410
+ return;
411
+ }
412
+ const template = PROFILE_TEMPLATES[profile];
413
+ const store = readStore();
414
+ const nameArg = args[1] && !args[1].startsWith("--") ? args[1] : `${profile}-agent`;
415
+ const taskOverride = parseFlag(args, "--task");
416
+ const cwdOverride = parseFlag(args, "--cwd");
417
+ const modelOverride = parseFlag(args, "--model");
418
+ const intervalOverride = parseFlag(args, "--interval");
419
+ const maxTurnsOverride = parseFlag(args, "--max-turns");
420
+ const maxRetriesOverride = parseFlag(args, "--max-retries");
421
+ const teamFlag = parseFlag(args, "--team");
422
+ const mcpFlag = parseFlag(args, "--mcp");
423
+ const id = `${slug(nameArg)}-${Date.now().toString().slice(-6)}`;
424
+ const now = (/* @__PURE__ */ new Date()).toISOString();
425
+ const cwd = cwdOverride || process.cwd();
426
+ const agent = {
427
+ id,
428
+ name: nameArg,
429
+ profile,
430
+ prompt: taskOverride || template.prompt,
431
+ model: modelOverride || template.model,
432
+ localModel: modelOverride === "local" ? "qwen3.5:4b" : void 0,
433
+ cwd,
434
+ allowedTools: template.allowedTools,
435
+ maxTurns: parseInt(maxTurnsOverride || String(template.maxTurns), 10),
436
+ maxRetries: parseInt(maxRetriesOverride || "2", 10),
437
+ intervalSec: parseInt(intervalOverride || String(template.intervalSec), 10),
438
+ enabled: true,
439
+ createdAt: now,
440
+ updatedAt: now,
441
+ totalRuns: 0,
442
+ totalErrors: 0,
443
+ team: teamFlag || void 0,
444
+ mcpServers: mcpFlag ? mcpFlag.split(",").map((s) => s.trim()).filter(Boolean) : void 0
445
+ };
446
+ store.agents.push(agent);
447
+ writeStore(store);
448
+ writeRuntime(agent.id, { id: agent.id, pid: null, running: false, consecutiveErrors: 0, currentRetry: 0 });
449
+ console.log(`
450
+ ${COLORS.green}Agent created:${COLORS.reset} ${agent.name} (${agent.profile})`);
451
+ console.log(` ${COLORS.dim}id:${COLORS.reset} ${agent.id}`);
452
+ console.log(` ${COLORS.dim}model:${COLORS.reset} ${agent.model}`);
453
+ console.log(` ${COLORS.dim}cwd:${COLORS.reset} ${agent.cwd}`);
454
+ console.log(` ${COLORS.dim}tools:${COLORS.reset} ${agent.allowedTools.join(", ") || "none (local mode)"}`);
455
+ console.log(` ${COLORS.dim}turns:${COLORS.reset} ${agent.maxTurns} max, ${agent.maxRetries} retries`);
456
+ console.log(`
457
+ Run it: ${COLORS.cyan}rex agents run ${agent.name}${COLORS.reset}`);
458
+ }
459
+ async function runAgent(args) {
460
+ const idOrName = args[0];
461
+ const once = args.includes("--once") || !args.includes("--daemon");
462
+ const taskOverride = parseFlag(args, "--task");
463
+ if (!idOrName) {
464
+ console.log('Usage: rex agents run <id|name> [--task "..."] [--daemon]');
465
+ return;
466
+ }
467
+ const store = readStore();
468
+ const agent = findAgent(store, idOrName);
469
+ if (!agent) {
470
+ console.log(`Agent not found: ${idOrName}`);
471
+ return;
472
+ }
473
+ const rt = readRuntime(agent.id);
474
+ if (rt.pid && isPidRunning(rt.pid)) {
475
+ console.log(`${COLORS.yellow}Already running${COLORS.reset} (pid ${rt.pid}). Use: rex agents stop ${agent.name}`);
476
+ return;
477
+ }
478
+ if (once || agent.intervalSec === 0) {
479
+ console.log(`
480
+ ${COLORS.bold}Running ${agent.name}${COLORS.reset} (${agent.profile}, ${agent.model})...`);
481
+ console.log(`${COLORS.dim}cwd: ${agent.cwd || process.cwd()}${COLORS.reset}`);
482
+ console.log(`${COLORS.dim}tools: ${agent.allowedTools.join(", ") || "local LLM"}${COLORS.reset}`);
483
+ console.log(`${COLORS.dim}max turns: ${agent.maxTurns}, retries: ${agent.maxRetries}${COLORS.reset}
484
+ `);
485
+ writeRuntime(agent.id, { ...rt, pid: process.pid, running: true, startedAt: (/* @__PURE__ */ new Date()).toISOString(), lastHeartbeat: (/* @__PURE__ */ new Date()).toISOString(), consecutiveErrors: 0, currentRetry: 0 });
486
+ const result = await executeAgent(agent, taskOverride || void 0);
487
+ agent.lastRunAt = (/* @__PURE__ */ new Date()).toISOString();
488
+ agent.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
489
+ agent.totalRuns++;
490
+ if (!result.ok) agent.totalErrors++;
491
+ writeStore(store);
492
+ writeRuntime(agent.id, { ...readRuntime(agent.id), pid: null, running: false, lastRunAt: agent.lastRunAt, lastError: result.error, consecutiveErrors: result.ok ? 0 : readRuntime(agent.id).consecutiveErrors + 1, currentRetry: 0 });
493
+ const status = result.ok ? "OK" : "FAIL";
494
+ appendLog(agent.id, `RUN ${status} (${result.duration}ms, ${result.turns} turns, tools: ${result.toolsUsed.join(",") || "none"})`);
495
+ if (result.output && !result.output.startsWith('[{"type":"system"')) appendLog(agent.id, result.output.slice(0, 5e3));
496
+ if (result.error) appendLog(agent.id, `ERROR: ${result.error}`);
497
+ if (result.ok) {
498
+ console.log(`${COLORS.green}Done${COLORS.reset} (${Math.round(result.duration / 1e3)}s, ${result.turns} turns)
499
+ `);
500
+ console.log(result.output);
501
+ } else {
502
+ console.log(`${COLORS.red}Failed${COLORS.reset} (${Math.round(result.duration / 1e3)}s)
503
+ `);
504
+ console.log(result.error || result.output);
505
+ }
506
+ return;
507
+ }
508
+ const script = process.argv[1];
509
+ const child = spawn(process.execPath, [script, "agents", "_daemon-loop", agent.id], {
510
+ detached: true,
511
+ stdio: "ignore"
512
+ });
513
+ child.unref();
514
+ const pid = child.pid || null;
515
+ writeRuntime(agent.id, { id: agent.id, pid, running: pid !== null, startedAt: (/* @__PURE__ */ new Date()).toISOString(), lastHeartbeat: (/* @__PURE__ */ new Date()).toISOString(), consecutiveErrors: 0, currentRetry: 0 });
516
+ console.log(`${COLORS.green}Agent started in background${COLORS.reset} (pid ${pid})`);
517
+ console.log(` Logs: rex agents logs ${agent.name}`);
518
+ console.log(` Stop: rex agents stop ${agent.name}`);
519
+ }
520
+ async function daemonLoop(agentId) {
521
+ let alive = true;
522
+ process.on("SIGTERM", () => {
523
+ alive = false;
524
+ });
525
+ process.on("SIGINT", () => {
526
+ alive = false;
527
+ });
528
+ const MAX_CONSECUTIVE_ERRORS = 5;
529
+ while (alive) {
530
+ const store = readStore();
531
+ const agent = store.agents.find((a) => a.id === agentId);
532
+ if (!agent || !agent.enabled) break;
533
+ const runtime = readRuntime(agent.id);
534
+ if (runtime.consecutiveErrors >= MAX_CONSECUTIVE_ERRORS) {
535
+ appendLog(agent.id, `CIRCUIT BREAKER: ${runtime.consecutiveErrors} consecutive errors \u2014 stopping agent`);
536
+ break;
537
+ }
538
+ writeRuntime(agent.id, { ...runtime, id: agent.id, pid: process.pid, running: true, lastHeartbeat: (/* @__PURE__ */ new Date()).toISOString() });
539
+ const result = await executeAgent(agent);
540
+ agent.lastRunAt = (/* @__PURE__ */ new Date()).toISOString();
541
+ agent.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
542
+ agent.totalRuns++;
543
+ if (!result.ok) agent.totalErrors++;
544
+ writeStore(store);
545
+ const newConsecutive = result.ok ? 0 : runtime.consecutiveErrors + 1;
546
+ writeRuntime(agent.id, { ...readRuntime(agent.id), id: agent.id, pid: process.pid, running: true, lastRunAt: agent.lastRunAt, lastError: result.error, consecutiveErrors: newConsecutive, currentRetry: 0, lastHeartbeat: (/* @__PURE__ */ new Date()).toISOString() });
547
+ const status = result.ok ? "OK" : "FAIL";
548
+ appendLog(agent.id, `SCHEDULED RUN ${status} (${result.duration}ms, ${result.turns} turns)`);
549
+ if (result.output && !result.output.startsWith('[{"type":"system"')) appendLog(agent.id, result.output.slice(0, 3e3));
550
+ if (result.error) appendLog(agent.id, `ERROR: ${result.error}`);
551
+ const interval = Math.max(30, agent.intervalSec);
552
+ await new Promise((r) => setTimeout(r, interval * 1e3));
553
+ }
554
+ const finalState = readRuntime(agentId);
555
+ writeRuntime(agentId, { ...finalState, id: agentId, pid: null, running: false, lastHeartbeat: (/* @__PURE__ */ new Date()).toISOString() });
556
+ appendLog(agentId, "DAEMON STOPPED");
557
+ }
558
+ function stopAgent(args) {
559
+ const idOrName = args[0];
560
+ if (!idOrName) {
561
+ console.log("Usage: rex agents stop <id|name>");
562
+ return;
563
+ }
564
+ const store = readStore();
565
+ const agent = findAgent(store, idOrName);
566
+ if (!agent) {
567
+ console.log(`Agent not found: ${idOrName}`);
568
+ return;
569
+ }
570
+ const rt = readRuntime(agent.id);
571
+ if (rt.pid && isPidRunning(rt.pid)) {
572
+ try {
573
+ process.kill(rt.pid, "SIGTERM");
574
+ } catch {
575
+ }
576
+ console.log(`${COLORS.green}Stopped${COLORS.reset} ${agent.name} (pid ${rt.pid})`);
577
+ } else {
578
+ console.log(`${COLORS.dim}${agent.name} was not running${COLORS.reset}`);
579
+ }
580
+ writeRuntime(agent.id, { ...rt, running: false, pid: null, lastHeartbeat: (/* @__PURE__ */ new Date()).toISOString() });
581
+ }
582
+ function statusAgent(args, jsonMode) {
583
+ const idOrName = args[0];
584
+ if (!idOrName) {
585
+ listAgents(jsonMode);
586
+ return;
587
+ }
588
+ const store = readStore();
589
+ const agent = findAgent(store, idOrName);
590
+ if (!agent) {
591
+ console.log(`Agent not found: ${idOrName}`);
592
+ return;
593
+ }
594
+ const rt = readRuntime(agent.id);
595
+ const running = rt.running && isPidRunning(rt.pid);
596
+ if (jsonMode) {
597
+ console.log(JSON.stringify({ ...agent, runtime: { ...rt, running } }, null, 2));
598
+ return;
599
+ }
600
+ const status = running ? `${COLORS.green}RUNNING${COLORS.reset}` : agent.enabled ? `${COLORS.yellow}IDLE${COLORS.reset}` : `${COLORS.dim}DISABLED${COLORS.reset}`;
601
+ console.log(`
602
+ ${COLORS.bold}${agent.name}${COLORS.reset} ${status}`);
603
+ console.log(` Profile: ${agent.profile}`);
604
+ console.log(` Model: ${agent.model}${agent.localModel ? ` (${agent.localModel})` : ""}`);
605
+ console.log(` CWD: ${agent.cwd || "current dir"}`);
606
+ console.log(` Tools: ${agent.allowedTools.join(", ") || "none"}`);
607
+ console.log(` Turns: ${agent.maxTurns} max, ${agent.maxRetries} retries`);
608
+ console.log(` Schedule: ${agent.intervalSec > 0 ? `every ${agent.intervalSec}s` : "one-shot"}`);
609
+ console.log(` Runs: ${agent.totalRuns} total, ${agent.totalErrors} errors`);
610
+ console.log(` Last run: ${agent.lastRunAt || "never"}`);
611
+ if (rt.lastError) console.log(` Last err: ${COLORS.red}${rt.lastError.slice(0, 100)}${COLORS.reset}`);
612
+ if (rt.consecutiveErrors > 0) console.log(` Consec errors: ${COLORS.red}${rt.consecutiveErrors}${COLORS.reset}`);
613
+ console.log();
614
+ }
615
+ function showLogs(args) {
616
+ const idOrName = args[0];
617
+ const tailArg = parseFlag(args, "--tail");
618
+ const tail = Math.max(1, parseInt(tailArg || "50", 10));
619
+ if (!idOrName) {
620
+ console.log("Usage: rex agents logs <id|name> [--tail N]");
621
+ return;
622
+ }
623
+ const store = readStore();
624
+ const agent = findAgent(store, idOrName);
625
+ if (!agent) {
626
+ console.log(`Agent not found: ${idOrName}`);
627
+ return;
628
+ }
629
+ const p = logPath(agent.id);
630
+ if (!existsSync(p)) {
631
+ console.log("No logs yet.");
632
+ return;
633
+ }
634
+ const lines = readFileSync(p, "utf-8").split("\n").filter(Boolean);
635
+ console.log(lines.slice(-tail).join("\n"));
636
+ }
637
+ function setEnabled(args, enabled) {
638
+ const idOrName = args[0];
639
+ if (!idOrName) {
640
+ console.log(`Usage: rex agents ${enabled ? "enable" : "disable"} <id|name>`);
641
+ return;
642
+ }
643
+ const store = readStore();
644
+ const agent = findAgent(store, idOrName);
645
+ if (!agent) {
646
+ console.log(`Agent not found: ${idOrName}`);
647
+ return;
648
+ }
649
+ agent.enabled = enabled;
650
+ agent.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
651
+ writeStore(store);
652
+ console.log(`${enabled ? COLORS.green : COLORS.yellow}${agent.name} ${enabled ? "enabled" : "disabled"}${COLORS.reset}`);
653
+ }
654
+ function deleteAgent(args) {
655
+ const idOrName = args[0];
656
+ if (!idOrName) {
657
+ console.log("Usage: rex agents delete <id|name>");
658
+ return;
659
+ }
660
+ const store = readStore();
661
+ const idx = store.agents.findIndex((a) => a.id === idOrName || a.name === idOrName);
662
+ if (idx < 0) {
663
+ console.log(`Agent not found: ${idOrName}`);
664
+ return;
665
+ }
666
+ const agent = store.agents[idx];
667
+ const rt = readRuntime(agent.id);
668
+ if (rt.pid && isPidRunning(rt.pid)) {
669
+ try {
670
+ process.kill(rt.pid, "SIGTERM");
671
+ } catch {
672
+ }
673
+ }
674
+ store.agents.splice(idx, 1);
675
+ writeStore(store);
676
+ console.log(`${COLORS.green}Deleted${COLORS.reset} ${agent.name}`);
677
+ }
678
+ function profiles(jsonMode) {
679
+ const rows = Object.entries(PROFILE_TEMPLATES).map(([name, t]) => ({
680
+ name,
681
+ model: t.model,
682
+ allowedTools: t.allowedTools,
683
+ maxTurns: t.maxTurns,
684
+ intervalSec: t.intervalSec,
685
+ prompt: t.prompt.slice(0, 80) + "..."
686
+ }));
687
+ if (jsonMode) {
688
+ console.log(JSON.stringify({ profiles: rows }, null, 2));
689
+ return;
690
+ }
691
+ const line = "\u2500".repeat(60);
692
+ console.log(`
693
+ ${COLORS.bold} Agent Profiles${COLORS.reset}
694
+ ${line}`);
695
+ for (const r of rows) {
696
+ const model = r.model === "claude" ? `${COLORS.magenta}claude${COLORS.reset}` : `${COLORS.cyan}local${COLORS.reset}`;
697
+ const schedule = r.intervalSec > 0 ? `every ${r.intervalSec}s` : "one-shot";
698
+ console.log(` ${COLORS.bold}${r.name}${COLORS.reset} ${model} \u2014 ${r.maxTurns} turns \u2014 ${schedule}`);
699
+ console.log(` ${COLORS.dim}${r.prompt}${COLORS.reset}`);
700
+ if (r.allowedTools.length) console.log(` ${COLORS.dim}tools: ${r.allowedTools.join(", ")}${COLORS.reset}`);
701
+ console.log();
702
+ }
703
+ }
704
+ function teamCommand(args) {
705
+ const teamName = args[0];
706
+ if (!teamName) {
707
+ const store2 = readStore();
708
+ const teams = [...new Set(store2.agents.filter((a) => a.team).map((a) => a.team))];
709
+ if (teams.length === 0) {
710
+ console.log("No teams defined. Create agents with --team <name>");
711
+ return;
712
+ }
713
+ for (const t of teams) {
714
+ const members2 = store2.agents.filter((a) => a.team === t);
715
+ console.log(`
716
+ ${COLORS.bold}${t}${COLORS.reset} (${members2.length} agents)`);
717
+ for (const m of members2) {
718
+ const rt = readRuntime(m.id);
719
+ const running = rt.running && isPidRunning(rt.pid);
720
+ const status = running ? `${COLORS.green}RUN${COLORS.reset}` : `${COLORS.dim}IDLE${COLORS.reset}`;
721
+ console.log(` ${status} ${m.name} (${m.profile})`);
722
+ }
723
+ }
724
+ return;
725
+ }
726
+ const store = readStore();
727
+ const members = store.agents.filter((a) => a.team === teamName);
728
+ if (members.length === 0) {
729
+ console.log(`No agents in team "${teamName}"`);
730
+ return;
731
+ }
732
+ console.log(`
733
+ ${COLORS.bold}Team: ${teamName}${COLORS.reset}`);
734
+ for (const m of members) {
735
+ const rt = readRuntime(m.id);
736
+ const running = rt.running && isPidRunning(rt.pid);
737
+ const status = running ? `${COLORS.green}RUNNING${COLORS.reset}` : `${COLORS.dim}IDLE${COLORS.reset}`;
738
+ console.log(` ${status} ${m.name} (${m.profile})`);
739
+ }
740
+ }
741
+ async function chatWithOrchestrator(args) {
742
+ const message = args.join(" ");
743
+ if (!message) {
744
+ console.log("Usage: rex agents chat <message>");
745
+ console.log("Sends a message to the orchestrator agent.");
746
+ return;
747
+ }
748
+ const store = readStore();
749
+ let orch = store.agents.find((a) => a.profile === "orchestrator");
750
+ if (!orch) {
751
+ console.log(`${COLORS.cyan}Creating orchestrator agent...${COLORS.reset}`);
752
+ const template = PROFILE_TEMPLATES.orchestrator;
753
+ const id = `orchestrator-${Date.now().toString().slice(-6)}`;
754
+ const now = (/* @__PURE__ */ new Date()).toISOString();
755
+ orch = {
756
+ id,
757
+ name: "orchestrator",
758
+ profile: "orchestrator",
759
+ prompt: template.prompt,
760
+ model: template.model,
761
+ allowedTools: template.allowedTools,
762
+ maxTurns: template.maxTurns,
763
+ maxRetries: 2,
764
+ intervalSec: 0,
765
+ enabled: true,
766
+ createdAt: now,
767
+ updatedAt: now,
768
+ totalRuns: 0,
769
+ totalErrors: 0
770
+ };
771
+ store.agents.push(orch);
772
+ writeStore(store);
773
+ writeRuntime(orch.id, { id: orch.id, pid: null, running: false, consecutiveErrors: 0, currentRetry: 0 });
774
+ }
775
+ const agentList = store.agents.map((a) => {
776
+ const rt = readRuntime(a.id);
777
+ const running = rt.running && isPidRunning(rt.pid);
778
+ return `- ${a.name} (${a.profile}, ${a.model}) ${running ? "RUNNING" : a.enabled ? "IDLE" : "DISABLED"} runs=${a.totalRuns} errors=${a.totalErrors}`;
779
+ }).join("\n");
780
+ const enrichedTask = `Current agents:
781
+ ${agentList}
782
+
783
+ User request: ${message}`;
784
+ console.log(`
785
+ ${COLORS.bold}Orchestrator${COLORS.reset} thinking...
786
+ `);
787
+ const result = await executeAgent(orch, enrichedTask);
788
+ if (result.ok) {
789
+ console.log(result.output);
790
+ } else {
791
+ console.log(`${COLORS.red}Error:${COLORS.reset} ${result.error || result.output}`);
792
+ }
793
+ }
794
+ async function agents(args) {
795
+ ensureDirs();
796
+ const sub = args[0] || "list";
797
+ const rest = args.slice(1);
798
+ const jsonMode = args.includes("--json");
799
+ switch (sub) {
800
+ case "list":
801
+ listAgents(jsonMode);
802
+ return;
803
+ case "profiles":
804
+ profiles(jsonMode);
805
+ return;
806
+ case "create":
807
+ createAgent(rest);
808
+ return;
809
+ case "run":
810
+ await runAgent(rest);
811
+ return;
812
+ case "_daemon-loop":
813
+ await daemonLoop(rest[0]);
814
+ return;
815
+ case "stop":
816
+ stopAgent(rest);
817
+ return;
818
+ case "status":
819
+ statusAgent(rest, jsonMode);
820
+ return;
821
+ case "logs":
822
+ showLogs(rest);
823
+ return;
824
+ case "enable":
825
+ setEnabled(rest, true);
826
+ return;
827
+ case "disable":
828
+ setEnabled(rest, false);
829
+ return;
830
+ case "delete":
831
+ deleteAgent(rest);
832
+ return;
833
+ case "team":
834
+ teamCommand(rest);
835
+ return;
836
+ case "chat":
837
+ await chatWithOrchestrator(rest);
838
+ return;
839
+ default:
840
+ console.log(`Usage: rex agents <profiles|list|create|run|stop|status|logs|enable|disable|delete|team|chat>`);
841
+ console.log(`
842
+ Quick start:`);
843
+ console.log(` rex agents profiles # See available profiles`);
844
+ console.log(` rex agents create worker my-worker # Create an agent`);
845
+ console.log(` rex agents run my-worker --task "..." # Run with a task`);
846
+ console.log(` rex agents run my-worker --daemon # Run in background (scheduled)`);
847
+ console.log(` rex agents team [name] # List teams or team members`);
848
+ console.log(` rex agents chat <message> # Chat with the orchestrator`);
849
+ }
850
+ }
851
+ export {
852
+ agents
853
+ };