trismegistus 1.1.4 → 1.1.6

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 +34 -17
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -23,6 +23,7 @@ var CONFIG_FILE = "config";
23
23
  var TASKS_FILE = "tasks.md";
24
24
  var NOTES_FILE = "notes.md";
25
25
  var HANDOFF_FILE = "handoff";
26
+ var PROMPT_FILE = "prompt-instructions.md";
26
27
  var TASKS_TEMPLATE = `# Tasks \u2014 one per line
27
28
  # - [ ] = pending - [x] = done
28
29
  # - [!] = failed once (retrying) - [!!] = twice - [!!!] = gave up
@@ -31,6 +32,13 @@ var TASKS_TEMPLATE = `# Tasks \u2014 one per line
31
32
  `;
32
33
  var NOTES_TEMPLATE = `# Notes for Claude \u2014 write here, cleared after each read
33
34
  `;
35
+ var PROMPT_TEMPLATE = `# Prompt Instructions
36
+ # These instructions are injected into every Claude session started by the daemon.
37
+ # Customize this file to control how Claude approaches your tasks.
38
+
39
+ - Commit your changes with a clear commit message when done.
40
+ - Follow existing code style and conventions in the project.
41
+ `;
34
42
  var CONFIG_TEMPLATE = `# Trismegistus Configuration
35
43
  MAX_RETRIES=3
36
44
  TIMEOUT_MINUTES=30
@@ -58,6 +66,7 @@ npx tmg start # runs tasks autonomously
58
66
  | File | Tracked | Purpose |
59
67
  |------|---------|---------|
60
68
  | \`config\` | Yes | Daemon settings (retries, timeouts) \u2014 shared with team |
69
+ | \`prompt-instructions.md\` | Yes | Instructions injected into every Claude session \u2014 shared with team |
61
70
  | \`README.md\` | Yes | This file |
62
71
  | \`tasks.md\` | No | Personal task queue (gitignored) |
63
72
  | \`notes.md\` | No | Notes for Claude, cleared after each read (gitignored) |
@@ -155,7 +164,8 @@ function initProject(projectDir) {
155
164
  }
156
165
  const userFiles = [
157
166
  { name: TASKS_FILE, content: TASKS_TEMPLATE },
158
- { name: NOTES_FILE, content: NOTES_TEMPLATE }
167
+ { name: NOTES_FILE, content: NOTES_TEMPLATE },
168
+ { name: PROMPT_FILE, content: PROMPT_TEMPLATE }
159
169
  ];
160
170
  for (const file of userFiles) {
161
171
  const path = join(tmgDir, file.name);
@@ -345,6 +355,12 @@ function addTask(projectDir, text) {
345
355
  const separator = content.length > 0 && !content.endsWith("\n") ? "\n" : "";
346
356
  writeFileSync2(path, content + separator + line);
347
357
  }
358
+ function readPromptInstructions(projectDir) {
359
+ const path = join2(projectDir, DIR_NAME, PROMPT_FILE);
360
+ if (!existsSync2(path)) return "";
361
+ const content = readFileSync2(path, "utf-8");
362
+ return content.split("\n").filter((l) => !l.startsWith("#") && l.trim() !== "").join("\n").trim();
363
+ }
348
364
  function getFailureStatus(attempt) {
349
365
  if (attempt === 1) return "!";
350
366
  if (attempt === 2) return "!!";
@@ -499,16 +515,21 @@ function preflight(projectDir) {
499
515
  }
500
516
  return { ok: errors.length === 0, errors, warnings };
501
517
  }
502
- function buildPrompt(taskText, attempt, maxRetries, notes, handoff) {
518
+ function buildPrompt(taskText, attempt, maxRetries, notes, handoff, instructions) {
503
519
  let prompt = `You are an autonomous agent (attempt ${attempt}/${maxRetries}). Complete this task fully without asking questions.
504
520
 
505
521
  WORKFLOW:
506
522
  1. Break the task into subtasks. Use the Agent tool to spawn sub-agents for independent work when it makes sense.
507
523
  2. Plan before coding \u2014 understand the codebase first, then implement.
508
524
  3. Run tests and verify your work before finishing.
509
- 4. Commit your changes with a clear commit message.
510
525
 
511
526
  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.`;
527
+ if (instructions) {
528
+ prompt += `
529
+
530
+ PROJECT INSTRUCTIONS:
531
+ ${instructions}`;
532
+ }
512
533
  if (notes) {
513
534
  prompt += `
514
535
 
@@ -550,6 +571,7 @@ async function runDaemon(opts) {
550
571
  const attempt = getAttemptFromStatus(task.status);
551
572
  const notes = readAndClearNotes(projectDir);
552
573
  const handoff = readHandoff(projectDir);
574
+ const instructions = readPromptInstructions(projectDir);
553
575
  log("");
554
576
  log("\u2501".repeat(47));
555
577
  log(`\u25B6 [${time()}] ${task.text} (attempt ${attempt}/${config.maxRetries})`);
@@ -560,7 +582,8 @@ async function runDaemon(opts) {
560
582
  attempt,
561
583
  config.maxRetries,
562
584
  notes,
563
- handoff
585
+ handoff,
586
+ instructions
564
587
  );
565
588
  const result = await runClaude({
566
589
  prompt,
@@ -634,10 +657,11 @@ function startTunnel(name, options = {}) {
634
657
  )
635
658
  );
636
659
  }
660
+ const safeName = name.replace(/[^\w-=]/g, "").slice(0, 50) || "tmg-tunnel";
637
661
  return new Promise((resolve, reject) => {
638
662
  const child = spawn2(
639
663
  "code",
640
- ["tunnel", "--name", name, "--accept-server-license-terms"],
664
+ ["tunnel", "--name", safeName, "--accept-server-license-terms"],
641
665
  { stdio: ["ignore", "pipe", "pipe"] }
642
666
  );
643
667
  let stderr = "";
@@ -655,17 +679,8 @@ function startTunnel(name, options = {}) {
655
679
  ${stderr}` : `Timed out waiting for tunnel URL. You may need to authenticate \u2014 run \`code tunnel\` manually first.`;
656
680
  settle(() => reject(new Error(msg)));
657
681
  }, TIMEOUT_MS);
658
- child.stderr?.on("data", (chunk) => {
659
- const text = chunk.toString();
660
- stderr += text;
661
- if (options.onOutput) {
662
- for (const line of text.split("\n").filter(Boolean)) {
663
- options.onOutput(line);
664
- }
665
- }
666
- });
667
- child.stdout?.on("data", (chunk) => {
668
- const text = chunk.toString();
682
+ function handleOutput(text, isStderr) {
683
+ if (isStderr) stderr += text;
669
684
  if (options.onOutput) {
670
685
  for (const line of text.split("\n").filter(Boolean)) {
671
686
  options.onOutput(line);
@@ -676,7 +691,9 @@ ${stderr}` : `Timed out waiting for tunnel URL. You may need to authenticate \u2
676
691
  clearTimeout(timer);
677
692
  settle(() => resolve({ url: match[1], process: child }));
678
693
  }
679
- });
694
+ }
695
+ child.stderr?.on("data", (chunk) => handleOutput(chunk.toString(), true));
696
+ child.stdout?.on("data", (chunk) => handleOutput(chunk.toString(), false));
680
697
  child.on("error", (err) => {
681
698
  clearTimeout(timer);
682
699
  settle(() => reject(err));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "trismegistus",
3
- "version": "1.1.4",
3
+ "version": "1.1.6",
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": {