@xandout/libra-harness 0.1.10 → 0.1.13

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 (49) hide show
  1. package/dist/agent.d.ts.map +1 -1
  2. package/dist/agent.js +20 -9
  3. package/dist/agent.js.map +1 -1
  4. package/dist/extras/extensions/code-tools/extension.d.ts +69 -0
  5. package/dist/extras/extensions/code-tools/extension.d.ts.map +1 -0
  6. package/dist/extras/extensions/code-tools/extension.js +83 -0
  7. package/dist/extras/extensions/code-tools/extension.js.map +1 -0
  8. package/dist/extras/extensions/code-tools/extension.json +7 -0
  9. package/dist/extras/extensions/code-tools/extension.test.d.ts +2 -0
  10. package/dist/extras/extensions/code-tools/extension.test.d.ts.map +1 -0
  11. package/dist/extras/extensions/code-tools/extension.test.js +656 -0
  12. package/dist/extras/extensions/code-tools/extension.test.js.map +1 -0
  13. package/dist/extras/extensions/code-tools/index.d.ts +3 -0
  14. package/dist/extras/extensions/code-tools/index.d.ts.map +1 -0
  15. package/dist/extras/extensions/code-tools/index.js +2 -0
  16. package/dist/extras/extensions/code-tools/index.js.map +1 -0
  17. package/dist/extras/extensions/code-tools/tools/find.d.ts +3 -0
  18. package/dist/extras/extensions/code-tools/tools/find.d.ts.map +1 -0
  19. package/dist/extras/extensions/code-tools/tools/find.js +160 -0
  20. package/dist/extras/extensions/code-tools/tools/find.js.map +1 -0
  21. package/dist/extras/extensions/code-tools/tools/grep.d.ts +3 -0
  22. package/dist/extras/extensions/code-tools/tools/grep.d.ts.map +1 -0
  23. package/dist/extras/extensions/code-tools/tools/grep.js +306 -0
  24. package/dist/extras/extensions/code-tools/tools/grep.js.map +1 -0
  25. package/dist/extras/extensions/code-tools/tools/read.d.ts +3 -0
  26. package/dist/extras/extensions/code-tools/tools/read.d.ts.map +1 -0
  27. package/dist/extras/extensions/code-tools/tools/read.js +97 -0
  28. package/dist/extras/extensions/code-tools/tools/read.js.map +1 -0
  29. package/dist/extras/extensions/code-tools/tools/shared.d.ts +14 -0
  30. package/dist/extras/extensions/code-tools/tools/shared.d.ts.map +1 -0
  31. package/dist/extras/extensions/code-tools/tools/shared.js +53 -0
  32. package/dist/extras/extensions/code-tools/tools/shared.js.map +1 -0
  33. package/dist/extras/extensions/code-tools/tools/shell.d.ts +47 -0
  34. package/dist/extras/extensions/code-tools/tools/shell.d.ts.map +1 -0
  35. package/dist/extras/extensions/code-tools/tools/shell.js +492 -0
  36. package/dist/extras/extensions/code-tools/tools/shell.js.map +1 -0
  37. package/dist/extras/extensions/code-tools/tools/todo.d.ts +24 -0
  38. package/dist/extras/extensions/code-tools/tools/todo.d.ts.map +1 -0
  39. package/dist/extras/extensions/code-tools/tools/todo.js +131 -0
  40. package/dist/extras/extensions/code-tools/tools/todo.js.map +1 -0
  41. package/dist/extras/extensions/code-tools/tools/write.d.ts +4 -0
  42. package/dist/extras/extensions/code-tools/tools/write.d.ts.map +1 -0
  43. package/dist/extras/extensions/code-tools/tools/write.js +179 -0
  44. package/dist/extras/extensions/code-tools/tools/write.js.map +1 -0
  45. package/dist/extras/extensions/disk-session/extension.d.ts +5 -0
  46. package/dist/extras/extensions/disk-session/extension.d.ts.map +1 -1
  47. package/dist/extras/extensions/disk-session/extension.js +141 -23
  48. package/dist/extras/extensions/disk-session/extension.js.map +1 -1
  49. package/package.json +5 -1
@@ -0,0 +1,492 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync, appendFileSync, openSync, closeSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ import { makeToolName } from './shared.js';
5
+ export class ShellRegistry {
6
+ shells = new Map();
7
+ counter = 0;
8
+ shellsDir;
9
+ constructor(shellsDir) {
10
+ this.shellsDir = shellsDir ?? join(process.cwd(), '.libra-shells');
11
+ }
12
+ nextId() {
13
+ return `shell_${++this.counter}`;
14
+ }
15
+ metaPath(id) {
16
+ return join(this.shellsDir, `${id}.json`);
17
+ }
18
+ /**
19
+ * Create a foreground shell (pipes connected, parent waits).
20
+ * If background is true, the process is detached and output goes
21
+ * to a file. Metadata is persisted to disk so a future process
22
+ * can reconnect.
23
+ */
24
+ create(command, cwd, env, background) {
25
+ const id = this.nextId();
26
+ const shellsDir = this.shellsDir;
27
+ if (background) {
28
+ // ── Detached: output to file at OS level, process survives parent exit ──
29
+ // The child's stdout/stderr are connected directly to a file via
30
+ // file descriptors, not Node pipes. This means output continues
31
+ // flowing to the file even after the parent process exits.
32
+ //
33
+ // A wrapper shell command records the exit code to a file after
34
+ // the command finishes, so a future process can see whether it
35
+ // succeeded — without relying on the parent's event handlers.
36
+ mkdirSync(shellsDir, { recursive: true });
37
+ const outputFile = join(shellsDir, `${id}.output`);
38
+ const exitFile = join(shellsDir, `${id}.exit`);
39
+ const metaFile = this.metaPath(id);
40
+ // Create the output file so it exists even if the command produces no output.
41
+ writeFileSync(outputFile, '');
42
+ // Wrap the command so the exit code is recorded to a file.
43
+ // The wrapper runs in the detached shell, not in Node.
44
+ const wrappedCommand = `${command}; echo $? > "${exitFile}" 2>/dev/null`;
45
+ // Open file descriptors for stdout/stderr redirection.
46
+ const outFd = openSync(outputFile, 'a');
47
+ const errFd = outFd;
48
+ const child = spawn(wrappedCommand, {
49
+ shell: true,
50
+ cwd,
51
+ env: { ...process.env, ...env },
52
+ stdio: ['pipe', outFd, errFd],
53
+ detached: true,
54
+ });
55
+ closeSync(outFd);
56
+ const entry = {
57
+ id,
58
+ process: child,
59
+ pid: child.pid ?? -1,
60
+ output: '',
61
+ done: false,
62
+ exitCode: null,
63
+ startedAt: Date.now(),
64
+ command,
65
+ cwd,
66
+ outputFile,
67
+ detached: true,
68
+ };
69
+ // While the parent is alive, track exit for in-memory queries.
70
+ child.on('exit', (code) => {
71
+ entry.done = true;
72
+ entry.exitCode = code;
73
+ });
74
+ child.on('error', (err) => {
75
+ try {
76
+ appendFileSync(outputFile, `\nError: ${err.message}\n`);
77
+ }
78
+ catch { }
79
+ entry.done = true;
80
+ entry.exitCode = -1;
81
+ });
82
+ child.unref();
83
+ const meta = {
84
+ id,
85
+ pid: child.pid ?? -1,
86
+ command,
87
+ cwd,
88
+ startedAt: entry.startedAt,
89
+ outputFile,
90
+ };
91
+ try {
92
+ writeFileSync(metaFile, JSON.stringify(meta, null, 2));
93
+ }
94
+ catch { }
95
+ this.shells.set(id, entry);
96
+ return entry;
97
+ }
98
+ // ── Foreground: pipes connected, parent owns the process ──
99
+ const child = spawn(command, {
100
+ shell: true,
101
+ cwd,
102
+ env: { ...process.env, ...env },
103
+ stdio: ['pipe', 'pipe', 'pipe'],
104
+ });
105
+ const entry = {
106
+ id,
107
+ process: child,
108
+ pid: child.pid ?? -1,
109
+ output: '',
110
+ done: false,
111
+ exitCode: null,
112
+ startedAt: Date.now(),
113
+ command,
114
+ cwd,
115
+ detached: false,
116
+ };
117
+ child.stdout?.on('data', (data) => {
118
+ entry.output += data.toString();
119
+ });
120
+ child.stderr?.on('data', (data) => {
121
+ entry.output += data.toString();
122
+ });
123
+ child.on('exit', (code) => {
124
+ entry.done = true;
125
+ entry.exitCode = code;
126
+ });
127
+ child.on('error', (err) => {
128
+ entry.output += `\nError: ${err.message}\n`;
129
+ entry.done = true;
130
+ entry.exitCode = -1;
131
+ });
132
+ this.shells.set(id, entry);
133
+ return entry;
134
+ }
135
+ /**
136
+ * Get a shell by ID. If it's not in the in-memory map, try to
137
+ * reconnect from persisted metadata on disk.
138
+ */
139
+ get(id) {
140
+ const cached = this.shells.get(id);
141
+ if (cached)
142
+ return cached;
143
+ // Try to reconnect from disk.
144
+ const metaFile = this.metaPath(id);
145
+ if (!existsSync(metaFile))
146
+ return undefined;
147
+ try {
148
+ const meta = JSON.parse(readFileSync(metaFile, 'utf-8'));
149
+ const outputFile = meta.outputFile;
150
+ const exitFile = join(this.shellsDir, `${id}.exit`);
151
+ // Check if the process is still alive (signal 0 = probe).
152
+ let alive = false;
153
+ try {
154
+ process.kill(meta.pid, 0);
155
+ alive = true;
156
+ }
157
+ catch {
158
+ alive = false;
159
+ }
160
+ // Read accumulated output from file.
161
+ let output = '';
162
+ if (existsSync(outputFile)) {
163
+ output = readFileSync(outputFile, 'utf-8');
164
+ }
165
+ // Check if exit code was recorded.
166
+ let exitCode = null;
167
+ let done = false;
168
+ if (existsSync(exitFile)) {
169
+ exitCode = Number(readFileSync(exitFile, 'utf-8').trim());
170
+ done = true;
171
+ }
172
+ else if (!alive) {
173
+ // Process died without recording exit code.
174
+ done = true;
175
+ exitCode = -1;
176
+ }
177
+ const entry = {
178
+ id,
179
+ pid: meta.pid,
180
+ output,
181
+ done,
182
+ exitCode,
183
+ startedAt: meta.startedAt,
184
+ command: meta.command,
185
+ cwd: meta.cwd,
186
+ outputFile,
187
+ detached: true,
188
+ // No process handle — can't write to stdin or use ChildProcess.kill.
189
+ };
190
+ this.shells.set(id, entry);
191
+ return entry;
192
+ }
193
+ catch {
194
+ return undefined;
195
+ }
196
+ }
197
+ delete(id) {
198
+ const removed = this.shells.delete(id);
199
+ // Clean up disk artifacts for detached shells.
200
+ const metaFile = this.metaPath(id);
201
+ const outputFile = join(this.shellsDir, `${id}.output`);
202
+ const exitFile = join(this.shellsDir, `${id}.exit`);
203
+ for (const f of [metaFile, outputFile, exitFile]) {
204
+ try {
205
+ unlinkSync(f);
206
+ }
207
+ catch { }
208
+ }
209
+ return removed;
210
+ }
211
+ /** Kill all in-memory backgrounded shells. Called on extension close. */
212
+ close() {
213
+ for (const [id, entry] of this.shells) {
214
+ if (!entry.done && entry.process) {
215
+ try {
216
+ entry.process.kill('SIGKILL');
217
+ }
218
+ catch {
219
+ // already dead
220
+ }
221
+ }
222
+ // Detached shells survive — that's the point.
223
+ // Only clean up disk artifacts for non-detached shells.
224
+ if (!entry.detached) {
225
+ this.delete(id);
226
+ }
227
+ this.shells.delete(id);
228
+ }
229
+ }
230
+ }
231
+ // ── exec ─────────────────────────────────────────────────────────────
232
+ export const execTool = (cfg) => ({
233
+ name: makeToolName(cfg.toolPrefix, 'exec'),
234
+ description: 'Execute a shell command. By default, runs the command and waits for it to complete, ' +
235
+ 'returning stdout and stderr. Set timeout (ms) to limit execution time — if the command ' +
236
+ 'is still running when the timeout elapses, it is detached and backgrounded with a shell_id ' +
237
+ 'so you can retrieve output later with get_output. ' +
238
+ 'Set timeout to 0 to background immediately. ' +
239
+ 'Detached shells survive process exit — you can check on them in a later session. ' +
240
+ 'Commands run in the current working directory. Do not use this for file operations — ' +
241
+ 'use the dedicated file tools instead.',
242
+ parameters: {
243
+ type: 'object',
244
+ properties: {
245
+ command: {
246
+ type: 'string',
247
+ description: 'The shell command to execute.',
248
+ },
249
+ timeout: {
250
+ type: 'integer',
251
+ description: 'Timeout in milliseconds. Default: 30000 (30s). Set to 0 to background immediately.',
252
+ },
253
+ cwd: {
254
+ type: 'string',
255
+ description: 'Working directory for the command. Default: current working directory.',
256
+ },
257
+ env: {
258
+ type: 'object',
259
+ description: 'Additional environment variables (merged with process.env).',
260
+ },
261
+ },
262
+ required: ['command'],
263
+ },
264
+ async execute(args) {
265
+ const command = String(args.command ?? '');
266
+ if (!command) {
267
+ return { toolCallId: '', content: 'Error: command is required' };
268
+ }
269
+ const timeout = Number(args.timeout ?? 30000);
270
+ const cwd = String(args.cwd ?? process.cwd());
271
+ const extraEnv = (args.env ?? {});
272
+ // Background immediately — detach the process.
273
+ if (timeout === 0) {
274
+ const entry = cfg.registry.create(command, cwd, extraEnv, true);
275
+ return {
276
+ toolCallId: '',
277
+ content: `Backgrounded: ${entry.id} (pid ${entry.pid})\nUse get_output with shell_id "${entry.id}" to read output, or kill_shell to terminate.`,
278
+ };
279
+ }
280
+ // Foreground — wait for completion or timeout.
281
+ const entry = cfg.registry.create(command, cwd, extraEnv, false);
282
+ const elapsed = await new Promise((resolve) => {
283
+ const start = Date.now();
284
+ const check = setInterval(() => {
285
+ if (entry.done) {
286
+ clearInterval(check);
287
+ resolve(Date.now() - start);
288
+ }
289
+ else if (Date.now() - start >= timeout) {
290
+ clearInterval(check);
291
+ resolve(Date.now() - start);
292
+ }
293
+ }, 50);
294
+ });
295
+ if (entry.done) {
296
+ const output = entry.output || '(no output)';
297
+ const truncated = output.length > 50000
298
+ ? output.slice(0, 50000) + '\n[output truncated]'
299
+ : output;
300
+ cfg.registry.delete(entry.id);
301
+ return {
302
+ toolCallId: '',
303
+ content: `Exit code: ${entry.exitCode}\n${truncated}`,
304
+ };
305
+ }
306
+ // Timed out — re-spawn as detached so it survives.
307
+ // Kill the foreground process first.
308
+ try {
309
+ entry.process?.kill('SIGKILL');
310
+ }
311
+ catch { }
312
+ cfg.registry.delete(entry.id);
313
+ // Re-create as detached, capturing output from the start.
314
+ const bgEntry = cfg.registry.create(command, cwd, extraEnv, true);
315
+ return {
316
+ toolCallId: '',
317
+ content: `Timed out after ${elapsed}ms. Detached and backgrounded as ${bgEntry.id} (pid ${bgEntry.pid}).\nUse get_output with shell_id "${bgEntry.id}" to read output, or kill_shell to terminate.`,
318
+ };
319
+ },
320
+ });
321
+ // ── get_output ───────────────────────────────────────────────────────
322
+ export const getOutputTool = (cfg) => ({
323
+ name: makeToolName(cfg.toolPrefix, 'get_output'),
324
+ description: 'Read output from a backgrounded shell process. Returns accumulated stdout and stderr. ' +
325
+ 'If the process has exited, includes the exit code and the shell is cleaned up. ' +
326
+ 'Works across sessions — if the shell was started in a previous invocation, it is ' +
327
+ 'reconnected from persisted metadata. Use the shell_id returned by exec.',
328
+ parameters: {
329
+ type: 'object',
330
+ properties: {
331
+ shell_id: {
332
+ type: 'string',
333
+ description: 'The shell ID returned by exec when the command was backgrounded.',
334
+ },
335
+ },
336
+ required: ['shell_id'],
337
+ },
338
+ async execute(args) {
339
+ const shellId = String(args.shell_id ?? '');
340
+ if (!shellId) {
341
+ return { toolCallId: '', content: 'Error: shell_id is required' };
342
+ }
343
+ // get() auto-reconnects from disk if the shell isn't in memory.
344
+ const entry = cfg.registry.get(shellId);
345
+ if (!entry) {
346
+ return { toolCallId: '', content: `Error: no shell with id "${shellId}"` };
347
+ }
348
+ // For reconnected shells, re-read the output file to get latest.
349
+ let output = entry.output;
350
+ if (entry.detached && entry.outputFile && existsSync(entry.outputFile)) {
351
+ try {
352
+ output = readFileSync(entry.outputFile, 'utf-8');
353
+ entry.output = output;
354
+ }
355
+ catch { }
356
+ }
357
+ const truncated = output.length > 50000
358
+ ? output.slice(0, 50000) + '\n[output truncated]'
359
+ : output;
360
+ if (entry.done) {
361
+ cfg.registry.delete(shellId);
362
+ return {
363
+ toolCallId: '',
364
+ content: `Process exited (code: ${entry.exitCode}).\n${truncated}`,
365
+ };
366
+ }
367
+ return {
368
+ toolCallId: '',
369
+ content: `Still running (pid ${entry.pid}).\n${truncated}`,
370
+ };
371
+ },
372
+ });
373
+ // ── kill_shell ───────────────────────────────────────────────────────
374
+ export const killShellTool = (cfg) => ({
375
+ name: makeToolName(cfg.toolPrefix, 'kill_shell'),
376
+ description: 'Kill a backgrounded shell process by its shell_id. Sends SIGKILL. ' +
377
+ 'Returns the final accumulated output. Works across sessions.',
378
+ parameters: {
379
+ type: 'object',
380
+ properties: {
381
+ shell_id: {
382
+ type: 'string',
383
+ description: 'The shell ID returned by exec.',
384
+ },
385
+ },
386
+ required: ['shell_id'],
387
+ },
388
+ async execute(args) {
389
+ const shellId = String(args.shell_id ?? '');
390
+ if (!shellId) {
391
+ return { toolCallId: '', content: 'Error: shell_id is required' };
392
+ }
393
+ const entry = cfg.registry.get(shellId);
394
+ if (!entry) {
395
+ return { toolCallId: '', content: `Error: no shell with id "${shellId}"` };
396
+ }
397
+ // Kill via PID (works for both in-memory and reconnected shells).
398
+ try {
399
+ process.kill(entry.pid, 'SIGKILL');
400
+ }
401
+ catch {
402
+ // already dead
403
+ }
404
+ // Give it a moment to flush output.
405
+ await new Promise((r) => setTimeout(r, 100));
406
+ // Re-read output file for detached shells.
407
+ let output = entry.output;
408
+ if (entry.detached && entry.outputFile && existsSync(entry.outputFile)) {
409
+ try {
410
+ output = readFileSync(entry.outputFile, 'utf-8');
411
+ }
412
+ catch { }
413
+ }
414
+ const truncated = output.length > 50000
415
+ ? output.slice(0, 50000) + '\n[output truncated]'
416
+ : output;
417
+ cfg.registry.delete(shellId);
418
+ return {
419
+ toolCallId: '',
420
+ content: `Killed ${shellId} (pid ${entry.pid}).\n${truncated}`,
421
+ };
422
+ },
423
+ });
424
+ // ── write_to_process ─────────────────────────────────────────────────
425
+ export const writeToProcessTool = (cfg) => ({
426
+ name: makeToolName(cfg.toolPrefix, 'write_to_process'),
427
+ description: 'Write input to the stdin of a backgrounded interactive shell process. ' +
428
+ 'Use this for shells running TUI programs, REPLs, or commands that wait for input. ' +
429
+ 'For a simple Enter keypress, send "\\n". ' +
430
+ 'Only works for shells started in the current session (not reconnected).',
431
+ parameters: {
432
+ type: 'object',
433
+ properties: {
434
+ shell_id: {
435
+ type: 'string',
436
+ description: 'The shell ID returned by exec.',
437
+ },
438
+ input: {
439
+ type: 'string',
440
+ description: 'The text to write to the process stdin.',
441
+ },
442
+ },
443
+ required: ['shell_id', 'input'],
444
+ },
445
+ async execute(args) {
446
+ const shellId = String(args.shell_id ?? '');
447
+ const input = String(args.input ?? '');
448
+ if (!shellId) {
449
+ return { toolCallId: '', content: 'Error: shell_id is required' };
450
+ }
451
+ if (!input) {
452
+ return { toolCallId: '', content: 'Error: input is required' };
453
+ }
454
+ const entry = cfg.registry.get(shellId);
455
+ if (!entry) {
456
+ return { toolCallId: '', content: `Error: no shell with id "${shellId}"` };
457
+ }
458
+ if (entry.done) {
459
+ return { toolCallId: '', content: `Error: process ${shellId} has already exited` };
460
+ }
461
+ // write_to_process only works for in-memory shells with a live stdin.
462
+ if (!entry.process) {
463
+ return {
464
+ toolCallId: '',
465
+ content: `Error: shell ${shellId} was reconnected from a previous session and has no writable stdin. Only shells started in the current session accept input.`,
466
+ isError: true,
467
+ };
468
+ }
469
+ const stdin = entry.process.stdin;
470
+ if (!stdin || stdin.destroyed) {
471
+ return { toolCallId: '', content: `Error: process ${shellId} has no writable stdin` };
472
+ }
473
+ return new Promise((resolve) => {
474
+ stdin.write(input, (err) => {
475
+ if (err) {
476
+ resolve({
477
+ toolCallId: '',
478
+ content: `Error writing to ${shellId}: ${err.message}`,
479
+ isError: true,
480
+ });
481
+ }
482
+ else {
483
+ resolve({
484
+ toolCallId: '',
485
+ content: `Wrote ${input.length} bytes to ${shellId}`,
486
+ });
487
+ }
488
+ });
489
+ });
490
+ },
491
+ });
492
+ //# sourceMappingURL=shell.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shell.js","sourceRoot":"","sources":["../../../../../src/extras/extensions/code-tools/tools/shell.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ export type TodoStatus = 'pending' | 'in_progress' | 'completed';
2
+ export interface Todo {
3
+ content: string;
4
+ status: TodoStatus;
5
+ }
6
+ export declare class TodoStore {
7
+ private todos;
8
+ private filePath?;
9
+ constructor(filePath?: string);
10
+ /** Load todos from disk. */
11
+ private load;
12
+ /** Save todos to disk. */
13
+ private save;
14
+ /** Replace the entire todo list. */
15
+ setAll(todos: Todo[]): void;
16
+ /** Get a snapshot of the current todos. */
17
+ getAll(): Todo[];
18
+ clear(): void;
19
+ }
20
+ export declare const todoWriteTool: (cfg: {
21
+ toolPrefix: string;
22
+ store: TodoStore;
23
+ }) => any;
24
+ //# sourceMappingURL=todo.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"todo.d.ts","sourceRoot":"","sources":["../../../../../src/extras/extensions/code-tools/tools/todo.ts"],"names":[],"mappings":"AAUA,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,aAAa,GAAG,WAAW,CAAC;AAEjE,MAAM,WAAW,IAAI;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,UAAU,CAAC;CACpB;AAED,qBAAa,SAAS;IACpB,OAAO,CAAC,KAAK,CAAc;IAC3B,OAAO,CAAC,QAAQ,CAAC,CAAS;IAE1B,YAAY,QAAQ,CAAC,EAAE,MAAM,EAK5B;IAED,4BAA4B;IAC5B,OAAO,CAAC,IAAI;IAmBZ,0BAA0B;IAC1B,OAAO,CAAC,IAAI;IAUZ,oCAAoC;IACpC,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,IAAI,CAG1B;IAED,2CAA2C;IAC3C,MAAM,IAAI,IAAI,EAAE,CAEf;IAED,KAAK,IAAI,IAAI,CAGZ;CACF;AAGD,eAAO,MAAM,aAAa,EAAE,CAAC,GAAG,EAAE;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,SAAS,CAAA;CAAE,KAAK,GA4E7E,CAAC"}
@@ -0,0 +1,131 @@
1
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
2
+ import { dirname } from 'node:path';
3
+ import { makeToolName } from './shared.js';
4
+ export class TodoStore {
5
+ todos = [];
6
+ filePath;
7
+ constructor(filePath) {
8
+ this.filePath = filePath;
9
+ if (filePath) {
10
+ this.load();
11
+ }
12
+ }
13
+ /** Load todos from disk. */
14
+ load() {
15
+ if (!this.filePath)
16
+ return;
17
+ try {
18
+ if (existsSync(this.filePath)) {
19
+ const data = JSON.parse(readFileSync(this.filePath, 'utf-8'));
20
+ if (Array.isArray(data)) {
21
+ this.todos = data.map((t) => ({
22
+ content: String(t.content ?? ''),
23
+ status: (t.status === 'in_progress' || t.status === 'completed')
24
+ ? t.status
25
+ : 'pending',
26
+ }));
27
+ }
28
+ }
29
+ }
30
+ catch {
31
+ // ignore corrupt file
32
+ }
33
+ }
34
+ /** Save todos to disk. */
35
+ save() {
36
+ if (!this.filePath)
37
+ return;
38
+ try {
39
+ mkdirSync(dirname(this.filePath), { recursive: true });
40
+ writeFileSync(this.filePath, JSON.stringify(this.todos, null, 2) + '\n', 'utf-8');
41
+ }
42
+ catch {
43
+ // ignore write errors
44
+ }
45
+ }
46
+ /** Replace the entire todo list. */
47
+ setAll(todos) {
48
+ this.todos = todos;
49
+ this.save();
50
+ }
51
+ /** Get a snapshot of the current todos. */
52
+ getAll() {
53
+ return [...this.todos];
54
+ }
55
+ clear() {
56
+ this.todos = [];
57
+ this.save();
58
+ }
59
+ }
60
+ // ── todo_write tool ──────────────────────────────────────────────────
61
+ export const todoWriteTool = (cfg) => ({
62
+ name: makeToolName(cfg.toolPrefix, 'todo_write'),
63
+ description: 'Create and manage a structured task list for tracking multi-step work. ' +
64
+ 'Use this for tasks with 3 or more distinct steps. Skip it for trivial single-step work. ' +
65
+ 'Each todo has a status: "pending", "in_progress", or "completed". ' +
66
+ 'Keep exactly ONE todo as "in_progress" at a time — mark it in_progress when starting, ' +
67
+ 'and mark it "completed" immediately when done. ' +
68
+ 'The entire list is replaced on each call (not appended). ' +
69
+ 'Add follow-up tasks discovered during work. Remove tasks that are no longer relevant. ' +
70
+ 'Todos persist across sessions, so you can pick up where you left off.',
71
+ parameters: {
72
+ type: 'object',
73
+ properties: {
74
+ todos: {
75
+ type: 'array',
76
+ description: 'The complete todo list. Replaces the previous list on each call.',
77
+ items: {
78
+ type: 'object',
79
+ properties: {
80
+ content: {
81
+ type: 'string',
82
+ description: 'The task description.',
83
+ },
84
+ status: {
85
+ type: 'string',
86
+ enum: ['pending', 'in_progress', 'completed'],
87
+ description: 'Current status of the todo.',
88
+ },
89
+ },
90
+ required: ['content', 'status'],
91
+ },
92
+ },
93
+ },
94
+ required: ['todos'],
95
+ },
96
+ async execute(args) {
97
+ const input = args.todos;
98
+ if (!Array.isArray(input)) {
99
+ return { toolCallId: '', content: 'Error: todos must be an array' };
100
+ }
101
+ const todos = input.map((t) => ({
102
+ content: String(t.content ?? ''),
103
+ status: (t.status === 'in_progress' || t.status === 'completed')
104
+ ? t.status
105
+ : 'pending',
106
+ }));
107
+ // Validate: at most one in_progress.
108
+ const inProgressCount = todos.filter((t) => t.status === 'in_progress').length;
109
+ if (inProgressCount > 1) {
110
+ return {
111
+ toolCallId: '',
112
+ content: 'Error: only one todo can be "in_progress" at a time. Mark the others "pending" or "completed".',
113
+ isError: true,
114
+ };
115
+ }
116
+ cfg.store.setAll(todos);
117
+ // Format the response so the model sees the current state.
118
+ if (todos.length === 0) {
119
+ return { toolCallId: '', content: 'Todo list cleared.' };
120
+ }
121
+ const lines = todos.map((t, i) => {
122
+ const marker = t.status === 'completed' ? '[x]' : t.status === 'in_progress' ? '[~]' : '[ ]';
123
+ return `${i + 1}. ${marker} ${t.content}`;
124
+ });
125
+ return {
126
+ toolCallId: '',
127
+ content: `Updated todo list:\n${lines.join('\n')}`,
128
+ };
129
+ },
130
+ });
131
+ //# sourceMappingURL=todo.js.map