aoaoe 0.1.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 (47) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +250 -0
  3. package/dist/config.d.ts +10 -0
  4. package/dist/config.d.ts.map +1 -0
  5. package/dist/config.js +149 -0
  6. package/dist/config.js.map +1 -0
  7. package/dist/dashboard.d.ts +4 -0
  8. package/dist/dashboard.d.ts.map +1 -0
  9. package/dist/dashboard.js +49 -0
  10. package/dist/dashboard.js.map +1 -0
  11. package/dist/executor.d.ts +26 -0
  12. package/dist/executor.d.ts.map +1 -0
  13. package/dist/executor.js +138 -0
  14. package/dist/executor.js.map +1 -0
  15. package/dist/index.d.ts +3 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +153 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/poller.d.ts +14 -0
  20. package/dist/poller.d.ts.map +1 -0
  21. package/dist/poller.js +167 -0
  22. package/dist/poller.js.map +1 -0
  23. package/dist/reasoner/claude-code.d.ts +15 -0
  24. package/dist/reasoner/claude-code.d.ts.map +1 -0
  25. package/dist/reasoner/claude-code.js +119 -0
  26. package/dist/reasoner/claude-code.js.map +1 -0
  27. package/dist/reasoner/index.d.ts +5 -0
  28. package/dist/reasoner/index.d.ts.map +1 -0
  29. package/dist/reasoner/index.js +15 -0
  30. package/dist/reasoner/index.js.map +1 -0
  31. package/dist/reasoner/opencode.d.ts +17 -0
  32. package/dist/reasoner/opencode.d.ts.map +1 -0
  33. package/dist/reasoner/opencode.js +193 -0
  34. package/dist/reasoner/opencode.js.map +1 -0
  35. package/dist/reasoner/prompt.d.ts +4 -0
  36. package/dist/reasoner/prompt.d.ts.map +1 -0
  37. package/dist/reasoner/prompt.js +68 -0
  38. package/dist/reasoner/prompt.js.map +1 -0
  39. package/dist/shell.d.ts +8 -0
  40. package/dist/shell.d.ts.map +1 -0
  41. package/dist/shell.js +27 -0
  42. package/dist/shell.js.map +1 -0
  43. package/dist/types.d.ts +85 -0
  44. package/dist/types.d.ts.map +1 -0
  45. package/dist/types.js +2 -0
  46. package/dist/types.js.map +1 -0
  47. package/package.json +54 -0
@@ -0,0 +1,138 @@
1
+ import { exec, execQuiet } from "./shell.js";
2
+ import { appendFileSync, mkdirSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ import { homedir } from "node:os";
5
+ const LOG_DIR = join(homedir(), ".aoaoe");
6
+ const LOG_FILE = join(LOG_DIR, "actions.log");
7
+ export class Executor {
8
+ config;
9
+ actionLog = [];
10
+ recentActions = new Map(); // session -> last action timestamp
11
+ constructor(config) {
12
+ this.config = config;
13
+ // ensure log dir exists
14
+ try {
15
+ mkdirSync(LOG_DIR, { recursive: true });
16
+ }
17
+ catch { }
18
+ }
19
+ async execute(actions, snapshots) {
20
+ const results = [];
21
+ for (const action of actions) {
22
+ // rate limit: don't hammer the same session
23
+ if ("session" in action && action.session) {
24
+ if (this.isRateLimited(action.session)) {
25
+ const entry = this.logAction(action, false, "rate limited (too soon)");
26
+ results.push(entry);
27
+ continue;
28
+ }
29
+ }
30
+ const entry = await this.executeOne(action, snapshots);
31
+ results.push(entry);
32
+ }
33
+ return results;
34
+ }
35
+ async executeOne(action, snapshots) {
36
+ switch (action.action) {
37
+ case "send_input":
38
+ return this.sendInput(action.session, action.text, snapshots);
39
+ case "start_session":
40
+ return this.startSession(action.session);
41
+ case "stop_session":
42
+ return this.stopSession(action.session);
43
+ case "create_agent":
44
+ return this.createAgent(action.path, action.title, action.tool);
45
+ case "remove_agent":
46
+ return this.removeAgent(action.session);
47
+ case "wait":
48
+ return this.logAction(action, true, action.reason ?? "no action needed");
49
+ default:
50
+ return this.logAction(action, false, "unknown action type");
51
+ }
52
+ }
53
+ async sendInput(sessionId, text, snapshots) {
54
+ // resolve tmux session name from session ID
55
+ const tmuxName = this.resolveTmuxName(sessionId, snapshots);
56
+ if (!tmuxName) {
57
+ return this.logAction({ action: "send_input", session: sessionId, text }, false, `could not resolve tmux name for session ${sessionId}`);
58
+ }
59
+ // safety: refuse empty or whitespace-only input
60
+ if (!text.trim()) {
61
+ return this.logAction({ action: "send_input", session: sessionId, text }, false, "refusing to send empty input");
62
+ }
63
+ // tmux send-keys sends literal text then Enter
64
+ const ok = await execQuiet("tmux", ["send-keys", "-t", tmuxName, text, "Enter"]);
65
+ this.markAction(sessionId);
66
+ return this.logAction({ action: "send_input", session: sessionId, text }, ok, ok ? `sent to ${tmuxName}` : `send-keys failed for ${tmuxName}`);
67
+ }
68
+ async startSession(sessionId) {
69
+ const result = await exec("aoe", ["session", "start", sessionId]);
70
+ this.markAction(sessionId);
71
+ return this.logAction({ action: "start_session", session: sessionId }, result.exitCode === 0, result.exitCode === 0 ? "started" : result.stderr.trim());
72
+ }
73
+ async stopSession(sessionId) {
74
+ const result = await exec("aoe", ["session", "stop", sessionId]);
75
+ this.markAction(sessionId);
76
+ return this.logAction({ action: "stop_session", session: sessionId }, result.exitCode === 0, result.exitCode === 0 ? "stopped" : result.stderr.trim());
77
+ }
78
+ async createAgent(path, title, tool) {
79
+ const args = ["add", path, "-t", title, "-c", tool, "-y"]; // -y for yolo mode
80
+ const result = await exec("aoe", args);
81
+ return this.logAction({ action: "create_agent", path, title, tool }, result.exitCode === 0, result.exitCode === 0 ? "created" : result.stderr.trim());
82
+ }
83
+ async removeAgent(sessionId) {
84
+ const result = await exec("aoe", ["remove", sessionId, "-y"]);
85
+ this.markAction(sessionId);
86
+ return this.logAction({ action: "remove_agent", session: sessionId }, result.exitCode === 0, result.exitCode === 0 ? "removed" : result.stderr.trim());
87
+ }
88
+ resolveTmuxName(sessionId, snapshots) {
89
+ // try exact match first
90
+ const exact = snapshots.find((s) => s.session.id === sessionId);
91
+ if (exact?.session.tmux_name)
92
+ return exact.session.tmux_name;
93
+ // try prefix match (reasoner might return truncated IDs)
94
+ const prefix = snapshots.find((s) => s.session.id.startsWith(sessionId));
95
+ if (prefix?.session.tmux_name)
96
+ return prefix.session.tmux_name;
97
+ // try title match
98
+ const byTitle = snapshots.find((s) => s.session.title.toLowerCase() === sessionId.toLowerCase());
99
+ if (byTitle?.session.tmux_name)
100
+ return byTitle.session.tmux_name;
101
+ return null;
102
+ }
103
+ // rate limiting: don't act on the same session more than once per 30s
104
+ isRateLimited(sessionId) {
105
+ const last = this.recentActions.get(sessionId);
106
+ if (!last)
107
+ return false;
108
+ return Date.now() - last < 30_000;
109
+ }
110
+ markAction(sessionId) {
111
+ this.recentActions.set(sessionId, Date.now());
112
+ }
113
+ logAction(action, success, detail) {
114
+ const entry = {
115
+ timestamp: Date.now(),
116
+ action,
117
+ success,
118
+ detail,
119
+ };
120
+ this.actionLog.push(entry);
121
+ // keep in-memory log bounded
122
+ if (this.actionLog.length > 1000) {
123
+ this.actionLog = this.actionLog.slice(-500);
124
+ }
125
+ // persist to ~/.aoaoe/actions.log (JSONL, one entry per line)
126
+ if (action.action !== "wait") {
127
+ try {
128
+ appendFileSync(LOG_FILE, JSON.stringify(entry) + "\n");
129
+ }
130
+ catch { } // best-effort, don't crash the daemon
131
+ }
132
+ return entry;
133
+ }
134
+ getRecentLog(n = 20) {
135
+ return this.actionLog.slice(-n);
136
+ }
137
+ }
138
+ //# sourceMappingURL=executor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"executor.js","sourceRoot":"","sources":["../src/executor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,153 @@
1
+ #!/usr/bin/env node
2
+ import { loadConfig, validateEnvironment, parseCliArgs, printHelp } from "./config.js";
3
+ import { Poller } from "./poller.js";
4
+ import { createReasoner } from "./reasoner/index.js";
5
+ import { Executor } from "./executor.js";
6
+ import { printDashboard } from "./dashboard.js";
7
+ import { readFileSync } from "node:fs";
8
+ import { resolve, dirname } from "node:path";
9
+ import { fileURLToPath } from "node:url";
10
+ const __dirname = dirname(fileURLToPath(import.meta.url));
11
+ async function main() {
12
+ const { overrides, help, version } = parseCliArgs(process.argv);
13
+ if (help) {
14
+ printHelp();
15
+ process.exit(0);
16
+ }
17
+ if (version) {
18
+ try {
19
+ const pkg = JSON.parse(readFileSync(resolve(__dirname, "..", "package.json"), "utf-8"));
20
+ console.log(`aoaoe ${pkg.version}`);
21
+ }
22
+ catch {
23
+ console.log("aoaoe (version unknown)");
24
+ }
25
+ process.exit(0);
26
+ }
27
+ const config = loadConfig(overrides);
28
+ log("starting aoaoe supervisor");
29
+ log(`reasoner: ${config.reasoner}`);
30
+ log(`poll interval: ${config.pollIntervalMs}ms`);
31
+ if (config.dryRun)
32
+ log("DRY RUN -- will observe and reason but not execute");
33
+ // validate tools are installed
34
+ await validateEnvironment(config);
35
+ const poller = new Poller(config);
36
+ const reasoner = createReasoner(config);
37
+ const executor = new Executor(config);
38
+ // init reasoner (starts opencode serve, verifies claude, etc)
39
+ log("initializing reasoner...");
40
+ await reasoner.init();
41
+ log("reasoner ready");
42
+ // graceful shutdown
43
+ let running = true;
44
+ const shutdown = async () => {
45
+ if (!running)
46
+ return;
47
+ running = false;
48
+ log("shutting down...");
49
+ await reasoner.shutdown();
50
+ process.exit(0);
51
+ };
52
+ process.on("SIGINT", shutdown);
53
+ process.on("SIGTERM", shutdown);
54
+ // main loop
55
+ let pollCount = 0;
56
+ log("entering main loop (Ctrl+C to stop)\n");
57
+ while (running) {
58
+ pollCount++;
59
+ try {
60
+ await tick(config, poller, reasoner, executor, pollCount);
61
+ }
62
+ catch (err) {
63
+ console.error(`[error] tick ${pollCount} failed: ${err}`);
64
+ }
65
+ if (running) {
66
+ await sleep(config.pollIntervalMs);
67
+ }
68
+ }
69
+ }
70
+ async function tick(config, poller, reasoner, executor, pollCount) {
71
+ // 1. poll
72
+ const observation = await poller.poll();
73
+ const sessionCount = observation.sessions.length;
74
+ const changeCount = observation.changes.length;
75
+ if (sessionCount === 0) {
76
+ if (pollCount % 6 === 1) {
77
+ // log every ~60s when no sessions
78
+ log("no active aoe sessions found");
79
+ }
80
+ return;
81
+ }
82
+ // dashboard every 6 polls (~60s at default interval)
83
+ if (pollCount % 6 === 1) {
84
+ printDashboard(observation, executor.getRecentLog(), pollCount, config);
85
+ }
86
+ // status line
87
+ const statuses = summarizeStatuses(observation);
88
+ process.stdout.write(`\r[poll #${pollCount}] ${sessionCount} sessions (${statuses}) | ${changeCount} changed`);
89
+ // 2. if nothing changed, skip reasoning (save tokens)
90
+ if (changeCount === 0) {
91
+ if (config.verbose) {
92
+ process.stdout.write(" | no changes, skipping reasoner\n");
93
+ }
94
+ return;
95
+ }
96
+ process.stdout.write(" | reasoning...");
97
+ // 3. reason (with timeout to avoid blocking the loop)
98
+ const result = await withTimeout(reasoner.decide(observation), 90_000, // 90s max for a single reasoning call
99
+ { actions: [{ action: "wait", reason: "reasoner timeout" }] });
100
+ const actionSummary = result.actions.map((a) => a.action).join(", ");
101
+ process.stdout.write(` -> ${actionSummary}\n`);
102
+ if (result.reasoning && config.verbose) {
103
+ log(`reasoning: ${result.reasoning}`);
104
+ }
105
+ // 4. execute (skip if all actions are "wait")
106
+ const nonWaitActions = result.actions.filter((a) => a.action !== "wait");
107
+ if (nonWaitActions.length === 0) {
108
+ return;
109
+ }
110
+ // dry-run: log what would happen, don't actually execute
111
+ if (config.dryRun) {
112
+ for (const action of nonWaitActions) {
113
+ log(`[dry-run] would ${action.action}: ${JSON.stringify(action)}`);
114
+ }
115
+ return;
116
+ }
117
+ const entries = await executor.execute(result.actions, observation.sessions);
118
+ for (const entry of entries) {
119
+ if (entry.action.action === "wait")
120
+ continue;
121
+ const icon = entry.success ? "+" : "!";
122
+ log(`[${icon}] ${entry.action.action}: ${entry.detail}`);
123
+ }
124
+ }
125
+ function summarizeStatuses(obs) {
126
+ const counts = new Map();
127
+ for (const snap of obs.sessions) {
128
+ const s = snap.session.status;
129
+ counts.set(s, (counts.get(s) ?? 0) + 1);
130
+ }
131
+ return [...counts.entries()].map(([k, v]) => `${v} ${k}`).join(", ");
132
+ }
133
+ function log(msg) {
134
+ const ts = new Date().toLocaleTimeString();
135
+ console.error(`[${ts}] ${msg}`);
136
+ }
137
+ function sleep(ms) {
138
+ return new Promise((resolve) => setTimeout(resolve, ms));
139
+ }
140
+ function withTimeout(promise, ms, fallback) {
141
+ return Promise.race([
142
+ promise,
143
+ new Promise((resolve) => setTimeout(() => {
144
+ log(`reasoner timed out after ${ms}ms, using fallback`);
145
+ resolve(fallback);
146
+ }, ms)),
147
+ ]);
148
+ }
149
+ main().catch((err) => {
150
+ console.error(`fatal: ${err}`);
151
+ process.exit(1);
152
+ });
153
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import type { AoaoeConfig, Observation } from "./types.js";
2
+ export declare class Poller {
3
+ private config;
4
+ private previousSnapshots;
5
+ constructor(config: AoaoeConfig);
6
+ poll(): Promise<Observation>;
7
+ private listSessions;
8
+ private getSessionStatus;
9
+ private captureSession;
10
+ private captureTmuxPane;
11
+ private diffSnapshots;
12
+ private log;
13
+ }
14
+ //# sourceMappingURL=poller.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"poller.d.ts","sourceRoot":"","sources":["../src/poller.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,WAAW,EAIX,WAAW,EACZ,MAAM,YAAY,CAAC;AAEpB,qBAAa,MAAM;IACjB,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,iBAAiB,CAA2C;gBAExD,MAAM,EAAE,WAAW;IAIzB,IAAI,IAAI,OAAO,CAAC,WAAW,CAAC;YAsBpB,YAAY;YAwCZ,gBAAgB;YAWhB,cAAc;YAYd,eAAe;IAqB7B,OAAO,CAAC,aAAa;IA6BrB,OAAO,CAAC,GAAG;CAKZ"}
package/dist/poller.js ADDED
@@ -0,0 +1,167 @@
1
+ import { createHash } from "node:crypto";
2
+ import { exec } from "./shell.js";
3
+ export class Poller {
4
+ config;
5
+ previousSnapshots = new Map();
6
+ constructor(config) {
7
+ this.config = config;
8
+ }
9
+ async poll() {
10
+ const sessions = await this.listSessions();
11
+ const snapshots = await Promise.all(sessions.map((s) => this.captureSession(s)));
12
+ const changes = this.diffSnapshots(snapshots);
13
+ const observation = {
14
+ timestamp: Date.now(),
15
+ sessions: snapshots,
16
+ changes,
17
+ };
18
+ // store current snapshots for next diff
19
+ this.previousSnapshots.clear();
20
+ for (const snap of snapshots) {
21
+ this.previousSnapshots.set(snap.session.id, snap);
22
+ }
23
+ return observation;
24
+ }
25
+ async listSessions() {
26
+ // aoe list --json returns array with id, title, path, tool, command, profile, created_at
27
+ // does NOT include status or tmux_name -- we derive both
28
+ const result = await exec("aoe", ["list", "--json"]);
29
+ if (result.exitCode !== 0) {
30
+ this.log(`aoe list failed: ${result.stderr}`);
31
+ return [];
32
+ }
33
+ let raw;
34
+ try {
35
+ const parsed = JSON.parse(result.stdout);
36
+ raw = Array.isArray(parsed) ? parsed : [];
37
+ }
38
+ catch (e) {
39
+ this.log(`failed to parse aoe list output: ${e}`);
40
+ return [];
41
+ }
42
+ // fetch per-session status in parallel
43
+ const sessions = await Promise.all(raw.map(async (s) => {
44
+ const id = String(s.id ?? "");
45
+ const title = String(s.title ?? "");
46
+ const status = await this.getSessionStatus(id);
47
+ return {
48
+ id,
49
+ title,
50
+ path: String(s.path ?? ""),
51
+ tool: String(s.tool ?? ""),
52
+ status,
53
+ tmux_name: computeTmuxName(id, title),
54
+ group: s.group ? String(s.group) : undefined,
55
+ created_at: s.created_at ? String(s.created_at) : undefined,
56
+ };
57
+ }));
58
+ return sessions;
59
+ }
60
+ async getSessionStatus(id) {
61
+ const result = await exec("aoe", ["session", "show", id, "--json"]);
62
+ if (result.exitCode !== 0)
63
+ return "unknown";
64
+ try {
65
+ const data = JSON.parse(result.stdout);
66
+ return String(data.status ?? "unknown");
67
+ }
68
+ catch {
69
+ return "unknown";
70
+ }
71
+ }
72
+ async captureSession(session) {
73
+ const output = await this.captureTmuxPane(session.tmux_name);
74
+ const outputHash = quickHash(output);
75
+ return {
76
+ session,
77
+ output,
78
+ outputHash,
79
+ capturedAt: Date.now(),
80
+ };
81
+ }
82
+ async captureTmuxPane(tmuxName) {
83
+ if (!tmuxName)
84
+ return "";
85
+ const result = await exec("tmux", [
86
+ "capture-pane",
87
+ "-t",
88
+ tmuxName,
89
+ "-p", // print to stdout
90
+ "-S",
91
+ `-${this.config.captureLinesCount}`, // last N lines
92
+ ]);
93
+ if (result.exitCode !== 0) {
94
+ // session might not exist in tmux (stopped, etc)
95
+ return "";
96
+ }
97
+ // trim trailing blank lines that tmux pads
98
+ return result.stdout.replace(/\n+$/, "");
99
+ }
100
+ diffSnapshots(current) {
101
+ const changes = [];
102
+ for (const snap of current) {
103
+ const prev = this.previousSnapshots.get(snap.session.id);
104
+ // no previous snapshot = first poll, skip to avoid spamming the reasoner
105
+ // with all existing output on startup
106
+ if (!prev)
107
+ continue;
108
+ // same hash = no change
109
+ if (snap.outputHash === prev.outputHash)
110
+ continue;
111
+ // find new lines by diffing output
112
+ const newLines = extractNewLines(prev.output, snap.output);
113
+ if (newLines.trim()) {
114
+ changes.push({
115
+ sessionId: snap.session.id,
116
+ title: snap.session.title,
117
+ tool: snap.session.tool,
118
+ status: snap.session.status,
119
+ newLines,
120
+ });
121
+ }
122
+ }
123
+ return changes;
124
+ }
125
+ log(msg) {
126
+ if (this.config.verbose) {
127
+ console.error(`[poller] ${msg}`);
128
+ }
129
+ }
130
+ }
131
+ // replicate AoE tmux naming: aoe_<sanitized_title_max20>_<first8_of_id>
132
+ // from agent-of-empires/src/tmux/session.rs:22-25
133
+ function computeTmuxName(id, title) {
134
+ const safeTitle = sanitizeTmuxName(title);
135
+ const shortId = id.slice(0, 8);
136
+ return `aoe_${safeTitle}_${shortId}`;
137
+ }
138
+ // AoE sanitization: only [a-zA-Z0-9_-], max 20 chars, everything else -> _
139
+ // from agent-of-empires/src/tmux/session.rs:220-231
140
+ function sanitizeTmuxName(name) {
141
+ const sanitized = name.replace(/[^a-zA-Z0-9_-]/g, "_");
142
+ return sanitized.slice(0, 20);
143
+ }
144
+ function quickHash(s) {
145
+ return createHash("sha256").update(s).digest("hex").slice(0, 16);
146
+ }
147
+ // extract lines in `current` that weren't in `previous`
148
+ function extractNewLines(previous, current) {
149
+ const prevLines = previous.split("\n");
150
+ const currLines = current.split("\n");
151
+ if (prevLines.length === 0)
152
+ return current;
153
+ // look for the last few non-empty lines of previous in current to find the overlap point
154
+ const anchorLines = prevLines.filter((l) => l.trim()).slice(-5);
155
+ if (anchorLines.length === 0)
156
+ return current;
157
+ const anchor = anchorLines.join("\n");
158
+ const currJoined = currLines.join("\n");
159
+ const anchorIdx = currJoined.lastIndexOf(anchor);
160
+ if (anchorIdx >= 0) {
161
+ const after = currJoined.slice(anchorIdx + anchor.length);
162
+ return after.replace(/^\n/, "");
163
+ }
164
+ // no overlap found (screen clear, etc) -- return last 20 lines
165
+ return currLines.slice(-20).join("\n");
166
+ }
167
+ //# sourceMappingURL=poller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"poller.js","sourceRoot":"","sources":["../src/poller.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import type { AoaoeConfig, Reasoner, Observation, ReasonerResult } from "../types.js";
2
+ export declare class ClaudeCodeReasoner implements Reasoner {
3
+ private config;
4
+ private sessionId;
5
+ constructor(config: AoaoeConfig);
6
+ init(): Promise<void>;
7
+ decide(observation: Observation): Promise<ReasonerResult>;
8
+ shutdown(): Promise<void>;
9
+ private buildArgs;
10
+ private tryExtractSessionId;
11
+ private parseResponse;
12
+ private validateResult;
13
+ private log;
14
+ }
15
+ //# sourceMappingURL=claude-code.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-code.d.ts","sourceRoot":"","sources":["../../src/reasoner/claude-code.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,WAAW,EAAE,cAAc,EAAU,MAAM,aAAa,CAAC;AAM9F,qBAAa,kBAAmB,YAAW,QAAQ;IACjD,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,SAAS,CAAuB;gBAE5B,MAAM,EAAE,WAAW;IAIzB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IASrB,MAAM,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,cAAc,CAAC;IAkBzD,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAI/B,OAAO,CAAC,SAAS;IA8BjB,OAAO,CAAC,mBAAmB;IAS3B,OAAO,CAAC,aAAa;IAkCrB,OAAO,CAAC,cAAc;IAsBtB,OAAO,CAAC,GAAG;CAGZ"}
@@ -0,0 +1,119 @@
1
+ import { exec } from "../shell.js";
2
+ import { SYSTEM_PROMPT, formatObservation } from "./prompt.js";
3
+ // Claude Code backend: uses `claude --print` subprocess for each decision.
4
+ // Optionally stateful via `--resume`.
5
+ export class ClaudeCodeReasoner {
6
+ config;
7
+ sessionId = null;
8
+ constructor(config) {
9
+ this.config = config;
10
+ }
11
+ async init() {
12
+ // verify claude is available
13
+ const result = await exec("claude", ["--version"]);
14
+ if (result.exitCode !== 0) {
15
+ throw new Error("claude CLI not found or not working");
16
+ }
17
+ this.log(`claude available: ${result.stdout.trim()}`);
18
+ }
19
+ async decide(observation) {
20
+ const prompt = formatObservation(observation);
21
+ const args = this.buildArgs(prompt);
22
+ const result = await exec("claude", args, 120_000);
23
+ if (result.exitCode !== 0) {
24
+ this.log(`claude failed: ${result.stderr}`);
25
+ return { actions: [{ action: "wait", reason: "reasoner error" }] };
26
+ }
27
+ // capture session ID from output for --resume on next call
28
+ if (this.config.claudeCode.resume) {
29
+ this.tryExtractSessionId(result.stderr + result.stdout);
30
+ }
31
+ return this.parseResponse(result.stdout);
32
+ }
33
+ async shutdown() {
34
+ // stateless subprocess, nothing to clean up
35
+ }
36
+ buildArgs(prompt) {
37
+ const args = ["--print"];
38
+ // output format
39
+ args.push("--output-format", "text");
40
+ // system prompt injection
41
+ args.push("--append-system-prompt", SYSTEM_PROMPT);
42
+ // model selection
43
+ if (this.config.claudeCode.model) {
44
+ args.push("--model", this.config.claudeCode.model);
45
+ }
46
+ // YOLO mode for unattended operation
47
+ if (this.config.claudeCode.yolo) {
48
+ args.push("--dangerously-skip-permissions");
49
+ }
50
+ // resume previous session for context continuity
51
+ if (this.config.claudeCode.resume && this.sessionId) {
52
+ args.push("--resume", this.sessionId);
53
+ }
54
+ // the actual prompt
55
+ args.push(prompt);
56
+ return args;
57
+ }
58
+ tryExtractSessionId(output) {
59
+ // claude prints session info to stderr, try to capture it
60
+ // format varies by version, look for common patterns
61
+ const match = output.match(/session[_\s]?(?:id)?[:\s]+([a-f0-9-]+)/i);
62
+ if (match) {
63
+ this.sessionId = match[1];
64
+ }
65
+ }
66
+ parseResponse(raw) {
67
+ const trimmed = raw.trim();
68
+ // try direct JSON parse
69
+ try {
70
+ return this.validateResult(JSON.parse(trimmed));
71
+ }
72
+ catch {
73
+ // not direct JSON
74
+ }
75
+ // extract from markdown code blocks
76
+ const jsonMatch = trimmed.match(/```(?:json)?\s*\n?([\s\S]*?)\n?```/);
77
+ if (jsonMatch) {
78
+ try {
79
+ return this.validateResult(JSON.parse(jsonMatch[1]));
80
+ }
81
+ catch {
82
+ // fall through
83
+ }
84
+ }
85
+ // find first { ... } block
86
+ const braceMatch = trimmed.match(/\{[\s\S]*\}/);
87
+ if (braceMatch) {
88
+ try {
89
+ return this.validateResult(JSON.parse(braceMatch[0]));
90
+ }
91
+ catch {
92
+ // give up
93
+ }
94
+ }
95
+ this.log(`failed to parse: ${trimmed.slice(0, 200)}`);
96
+ return { actions: [{ action: "wait", reason: "failed to parse reasoner response" }] };
97
+ }
98
+ validateResult(parsed) {
99
+ if (typeof parsed !== "object" || parsed === null) {
100
+ return { actions: [{ action: "wait", reason: "invalid response" }] };
101
+ }
102
+ const obj = parsed;
103
+ const actions = Array.isArray(obj.actions) ? obj.actions : [];
104
+ const validActions = actions
105
+ .filter((a) => typeof a === "object" && a !== null && "action" in a)
106
+ .map((a) => a);
107
+ if (validActions.length === 0) {
108
+ validActions.push({ action: "wait", reason: "no valid actions" });
109
+ }
110
+ return {
111
+ actions: validActions,
112
+ reasoning: typeof obj.reasoning === "string" ? obj.reasoning : undefined,
113
+ };
114
+ }
115
+ log(msg) {
116
+ console.error(`[reasoner:claude-code] ${msg}`);
117
+ }
118
+ }
119
+ //# sourceMappingURL=claude-code.js.map