trismegistus 1.1.3 → 1.1.5

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 (2) hide show
  1. package/dist/cli.js +59 -46
  2. package/package.json +2 -2
package/dist/cli.js CHANGED
@@ -14,6 +14,7 @@ import { join } from "path";
14
14
  var DEFAULT_CONFIG = {
15
15
  maxRetries: 3,
16
16
  timeoutMinutes: 30,
17
+ idleTimeoutSeconds: 15,
17
18
  idlePollSeconds: 10,
18
19
  taskDelaySeconds: 5
19
20
  };
@@ -33,6 +34,7 @@ var NOTES_TEMPLATE = `# Notes for Claude \u2014 write here, cleared after each r
33
34
  var CONFIG_TEMPLATE = `# Trismegistus Configuration
34
35
  MAX_RETRIES=3
35
36
  TIMEOUT_MINUTES=30
37
+ IDLE_TIMEOUT_SECONDS=15
36
38
  IDLE_POLL_SECONDS=10
37
39
  TASK_DELAY_SECONDS=5
38
40
  `;
@@ -360,6 +362,7 @@ import { join as join3 } from "path";
360
362
  var KEY_MAP = {
361
363
  MAX_RETRIES: "maxRetries",
362
364
  TIMEOUT_MINUTES: "timeoutMinutes",
365
+ IDLE_TIMEOUT_SECONDS: "idleTimeoutSeconds",
363
366
  IDLE_POLL_SECONDS: "idlePollSeconds",
364
367
  TASK_DELAY_SECONDS: "taskDelaySeconds"
365
368
  };
@@ -393,16 +396,24 @@ function loadConfig(projectDir) {
393
396
 
394
397
  // src/runner.ts
395
398
  import * as pty from "node-pty";
396
- function stripAnsi(str) {
397
- return str.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, "").replace(/\x1b\][^\x07]*\x07/g, "");
398
- }
399
399
  function runClaude(opts) {
400
- const { prompt, timeoutMs, projectDir, spawnFn = pty.spawn } = opts;
400
+ const {
401
+ prompt,
402
+ timeoutMs,
403
+ projectDir,
404
+ idleTimeoutMs = 6e4,
405
+ spawnFn = pty.spawn,
406
+ startupDelayMs = 5e3
407
+ } = opts;
401
408
  return new Promise((resolve) => {
402
409
  let timedOut = false;
410
+ let idledOut = false;
403
411
  let settled = false;
404
- let outputBuffer = "";
405
412
  let promptSent = false;
413
+ let gotFirstData = false;
414
+ let idleTimer = null;
415
+ let outputAfterPrompt = 0;
416
+ const MIN_OUTPUT_FOR_SUCCESS = 500;
406
417
  const child = spawnFn("claude", ["--dangerously-skip-permissions"], {
407
418
  cols: 120,
408
419
  rows: 40,
@@ -417,43 +428,43 @@ function runClaude(opts) {
417
428
  if (!settled) {
418
429
  settled = true;
419
430
  clearTimeout(timer);
431
+ if (idleTimer) clearTimeout(idleTimer);
420
432
  resolve(result);
421
433
  }
422
434
  }
435
+ function resetIdleTimer() {
436
+ if (!promptSent || settled) return;
437
+ if (idleTimer) clearTimeout(idleTimer);
438
+ idleTimer = setTimeout(() => {
439
+ idledOut = true;
440
+ child.kill();
441
+ }, idleTimeoutMs);
442
+ }
423
443
  child.onData((data) => {
424
- outputBuffer += data;
425
- const clean = stripAnsi(outputBuffer);
426
- if (!promptSent && isReadyForInput(clean)) {
427
- promptSent = true;
428
- outputBuffer = "";
429
- child.write(prompt + "\r");
430
- return;
431
- }
432
- if (promptSent) {
433
- if (clean.length > 100 && isTaskComplete(clean)) {
434
- child.kill();
435
- }
444
+ process.stdout.write(data);
445
+ if (promptSent) outputAfterPrompt += data.length;
446
+ resetIdleTimer();
447
+ if (!gotFirstData) {
448
+ gotFirstData = true;
449
+ setTimeout(() => {
450
+ if (!promptSent && !settled) {
451
+ promptSent = true;
452
+ child.write(prompt);
453
+ setTimeout(() => child.write("\r"), 500);
454
+ }
455
+ }, startupDelayMs);
436
456
  }
437
457
  });
438
458
  child.onExit(({ exitCode }) => {
459
+ const idleSuccess = idledOut && outputAfterPrompt >= MIN_OUTPUT_FOR_SUCCESS;
439
460
  settle({
440
- success: !timedOut && exitCode === 0,
461
+ success: idleSuccess || !timedOut && !idledOut && exitCode === 0,
441
462
  exitCode: timedOut ? 124 : exitCode,
442
463
  timedOut
443
464
  });
444
465
  });
445
466
  });
446
467
  }
447
- function isReadyForInput(output) {
448
- const lines = output.split("\n");
449
- const tail = lines.slice(-5).join("\n");
450
- return /[>❯]\s*$/.test(tail) || /╰─\s*$/.test(tail) || /\$\s*$/.test(tail);
451
- }
452
- function isTaskComplete(output) {
453
- const lines = output.split("\n");
454
- const tail = lines.slice(-5).join("\n");
455
- return /[>❯]\s*$/.test(tail) || /╰─\s*$/.test(tail);
456
- }
457
468
 
458
469
  // src/daemon.ts
459
470
  import { createRequire } from "module";
@@ -489,9 +500,15 @@ function preflight(projectDir) {
489
500
  return { ok: errors.length === 0, errors, warnings };
490
501
  }
491
502
  function buildPrompt(taskText, attempt, maxRetries, notes, handoff) {
492
- let prompt = `You are running autonomously overnight (attempt ${attempt}/${maxRetries}). Complete this task fully, commit your work, don't ask questions.
503
+ let prompt = `You are an autonomous agent (attempt ${attempt}/${maxRetries}). Complete this task fully without asking questions.
504
+
505
+ WORKFLOW:
506
+ 1. Break the task into subtasks. Use the Agent tool to spawn sub-agents for independent work when it makes sense.
507
+ 2. Plan before coding \u2014 understand the codebase first, then implement.
508
+ 3. Run tests and verify your work before finishing.
509
+ 4. Commit your changes with a clear commit message.
493
510
 
494
- If you are running low on time, write a summary of what you did and what remains to .trismegistus/handoff so the next session can continue.`;
511
+ If you cannot finish in time, write a summary of what you did and what remains to .trismegistus/handoff so the next session can continue.`;
495
512
  if (notes) {
496
513
  prompt += `
497
514
 
@@ -513,12 +530,12 @@ function sleep(ms) {
513
530
  return new Promise((resolve) => setTimeout(resolve, ms));
514
531
  }
515
532
  async function runDaemon(opts) {
516
- const { projectDir, spawnFn, maxIterations, onLog } = opts;
533
+ const { projectDir, spawnFn, maxIterations, onLog, startupDelayMs } = opts;
517
534
  const log = onLog ?? ((msg) => console.log(msg));
518
535
  const config = loadConfig(projectDir);
519
536
  log(` TMG v${VERSION} \u2014 ${(/* @__PURE__ */ new Date()).toLocaleString()}`);
520
537
  log(` Project: ${projectDir}`);
521
- log(` Timeout: ${config.timeoutMinutes}m | Max retries: ${config.maxRetries}`);
538
+ log(` Timeout: ${config.timeoutMinutes}m | Idle: ${config.idleTimeoutSeconds}s | Max retries: ${config.maxRetries}`);
522
539
  log("");
523
540
  let iterations = 0;
524
541
  while (true) {
@@ -548,8 +565,10 @@ async function runDaemon(opts) {
548
565
  const result = await runClaude({
549
566
  prompt,
550
567
  timeoutMs: config.timeoutMinutes * 60 * 1e3,
568
+ idleTimeoutMs: config.idleTimeoutSeconds * 1e3,
551
569
  projectDir,
552
- spawnFn
570
+ spawnFn,
571
+ startupDelayMs
553
572
  });
554
573
  if (result.success) {
555
574
  setTaskStatus(projectDir, task.text, "x");
@@ -615,10 +634,11 @@ function startTunnel(name, options = {}) {
615
634
  )
616
635
  );
617
636
  }
637
+ const safeName = name.replace(/[^\w-=]/g, "").slice(0, 50) || "tmg-tunnel";
618
638
  return new Promise((resolve, reject) => {
619
639
  const child = spawn2(
620
640
  "code",
621
- ["tunnel", "--name", name, "--accept-server-license-terms"],
641
+ ["tunnel", "--name", safeName, "--accept-server-license-terms"],
622
642
  { stdio: ["ignore", "pipe", "pipe"] }
623
643
  );
624
644
  let stderr = "";
@@ -636,17 +656,8 @@ function startTunnel(name, options = {}) {
636
656
  ${stderr}` : `Timed out waiting for tunnel URL. You may need to authenticate \u2014 run \`code tunnel\` manually first.`;
637
657
  settle(() => reject(new Error(msg)));
638
658
  }, TIMEOUT_MS);
639
- child.stderr?.on("data", (chunk) => {
640
- const text = chunk.toString();
641
- stderr += text;
642
- if (options.onOutput) {
643
- for (const line of text.split("\n").filter(Boolean)) {
644
- options.onOutput(line);
645
- }
646
- }
647
- });
648
- child.stdout?.on("data", (chunk) => {
649
- const text = chunk.toString();
659
+ function handleOutput(text, isStderr) {
660
+ if (isStderr) stderr += text;
650
661
  if (options.onOutput) {
651
662
  for (const line of text.split("\n").filter(Boolean)) {
652
663
  options.onOutput(line);
@@ -657,7 +668,9 @@ ${stderr}` : `Timed out waiting for tunnel URL. You may need to authenticate \u2
657
668
  clearTimeout(timer);
658
669
  settle(() => resolve({ url: match[1], process: child }));
659
670
  }
660
- });
671
+ }
672
+ child.stderr?.on("data", (chunk) => handleOutput(chunk.toString(), true));
673
+ child.stdout?.on("data", (chunk) => handleOutput(chunk.toString(), false));
661
674
  child.on("error", (err) => {
662
675
  clearTimeout(timer);
663
676
  settle(() => reject(err));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "trismegistus",
3
- "version": "1.1.3",
3
+ "version": "1.1.5",
4
4
  "description": "A local persistent daemon that runs AI sessions from a task queue, with mobile support.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -39,7 +39,7 @@
39
39
  ],
40
40
  "dependencies": {
41
41
  "commander": "^13.0.0",
42
- "node-pty": "^1.1.0",
42
+ "node-pty": "^1.2.0-beta.11",
43
43
  "qrcode-terminal": "^0.12.0"
44
44
  },
45
45
  "devDependencies": {