sisyphi 0.1.18 → 0.1.22
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.
- package/dist/cli.js +32 -21
- package/dist/cli.js.map +1 -1
- package/dist/daemon.js +168 -102
- package/dist/daemon.js.map +1 -1
- package/dist/templates/agent-plugin/agents/review-plan.md +94 -68
- package/dist/templates/agent-plugin/agents/spec-draft.md +27 -51
- package/dist/templates/agent-plugin/hooks/hooks.json +1 -13
- package/dist/templates/agent-plugin/hooks/intercept-send-message.sh +4 -55
- package/dist/templates/agent-suffix.md +10 -6
- package/dist/templates/orchestrator-plugin/scripts/block-task.sh +8 -1
- package/dist/templates/orchestrator.md +22 -18
- package/package.json +1 -1
- package/templates/agent-plugin/agents/review-plan.md +94 -68
- package/templates/agent-plugin/agents/spec-draft.md +27 -51
- package/templates/agent-plugin/hooks/hooks.json +1 -13
- package/templates/agent-plugin/hooks/intercept-send-message.sh +4 -55
- package/templates/agent-suffix.md +10 -6
- package/templates/orchestrator-plugin/scripts/block-task.sh +8 -1
- package/templates/orchestrator.md +22 -18
package/dist/daemon.js
CHANGED
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
} from "./chunk-LTAW6OWS.js";
|
|
22
22
|
|
|
23
23
|
// src/daemon/index.ts
|
|
24
|
-
import { mkdirSync as mkdirSync5, readFileSync as readFileSync9, writeFileSync as writeFileSync6, unlinkSync as unlinkSync3, existsSync as
|
|
24
|
+
import { mkdirSync as mkdirSync5, readFileSync as readFileSync9, writeFileSync as writeFileSync6, unlinkSync as unlinkSync3, existsSync as existsSync7 } from "fs";
|
|
25
25
|
import { execSync as execSync4 } from "child_process";
|
|
26
26
|
import { setTimeout as sleep } from "timers/promises";
|
|
27
27
|
|
|
@@ -46,12 +46,12 @@ function loadConfig(cwd) {
|
|
|
46
46
|
|
|
47
47
|
// src/daemon/server.ts
|
|
48
48
|
import { createServer } from "net";
|
|
49
|
-
import { unlinkSync, existsSync as
|
|
49
|
+
import { unlinkSync, existsSync as existsSync6, writeFileSync as writeFileSync4, readFileSync as readFileSync7, mkdirSync as mkdirSync4 } from "fs";
|
|
50
50
|
import { join as join4 } from "path";
|
|
51
51
|
|
|
52
52
|
// src/daemon/session-manager.ts
|
|
53
53
|
import { v4 as uuidv4 } from "uuid";
|
|
54
|
-
import { existsSync as
|
|
54
|
+
import { existsSync as existsSync5, readdirSync as readdirSync4, rmSync as rmSync2 } from "fs";
|
|
55
55
|
|
|
56
56
|
// src/daemon/state.ts
|
|
57
57
|
import { randomUUID } from "crypto";
|
|
@@ -95,7 +95,7 @@ function atomicWrite(filePath, data) {
|
|
|
95
95
|
writeFileSync(tmpPath, data, "utf-8");
|
|
96
96
|
renameSync(tmpPath, filePath);
|
|
97
97
|
}
|
|
98
|
-
function createSession(id, task, cwd) {
|
|
98
|
+
function createSession(id, task, cwd, context) {
|
|
99
99
|
const dir = sessionDir(cwd, id);
|
|
100
100
|
mkdirSync(dir, { recursive: true });
|
|
101
101
|
mkdirSync(contextDir(cwd, id), { recursive: true });
|
|
@@ -106,6 +106,7 @@ function createSession(id, task, cwd) {
|
|
|
106
106
|
const session = {
|
|
107
107
|
id,
|
|
108
108
|
task,
|
|
109
|
+
...context ? { context } : {},
|
|
109
110
|
cwd,
|
|
110
111
|
status: "active",
|
|
111
112
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -203,13 +204,10 @@ async function completeOrchestratorCycle(cwd, sessionId, nextPrompt) {
|
|
|
203
204
|
}
|
|
204
205
|
|
|
205
206
|
// src/daemon/orchestrator.ts
|
|
206
|
-
import { existsSync, readdirSync, readFileSync as
|
|
207
|
+
import { existsSync, readdirSync, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
207
208
|
import { resolve } from "path";
|
|
208
209
|
|
|
209
210
|
// src/daemon/colors.ts
|
|
210
|
-
import { readFileSync as readFileSync3 } from "fs";
|
|
211
|
-
import { homedir } from "os";
|
|
212
|
-
import { join as join2 } from "path";
|
|
213
211
|
var ORCHESTRATOR_COLOR = "yellow";
|
|
214
212
|
var AGENT_PALETTE = ["blue", "green", "magenta", "cyan", "red", "white"];
|
|
215
213
|
var TMUX_COLOR_MAP = {
|
|
@@ -229,56 +227,6 @@ function getNextColor(sessionId) {
|
|
|
229
227
|
function resetColors(sessionId) {
|
|
230
228
|
sessionColorIndex.delete(sessionId);
|
|
231
229
|
}
|
|
232
|
-
function extractFrontmatterColor(content) {
|
|
233
|
-
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
234
|
-
if (!match) return null;
|
|
235
|
-
const colorMatch = match[1].match(/^color:\s*(.+)$/m);
|
|
236
|
-
return colorMatch ? colorMatch[1].trim() : null;
|
|
237
|
-
}
|
|
238
|
-
function findPluginInstallPath(namespace) {
|
|
239
|
-
try {
|
|
240
|
-
const registryPath2 = join2(homedir(), ".claude", "plugins", "installed_plugins.json");
|
|
241
|
-
const registry = JSON.parse(readFileSync3(registryPath2, "utf-8"));
|
|
242
|
-
for (const key of Object.keys(registry)) {
|
|
243
|
-
if (key.startsWith(`${namespace}@`)) {
|
|
244
|
-
return registry[key].installPath ?? null;
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
} catch {
|
|
248
|
-
}
|
|
249
|
-
return null;
|
|
250
|
-
}
|
|
251
|
-
function resolveAgentTypeColor(agentType, pluginDir, cwd) {
|
|
252
|
-
if (!agentType) return null;
|
|
253
|
-
let namespace;
|
|
254
|
-
let name;
|
|
255
|
-
if (agentType.includes(":")) {
|
|
256
|
-
[namespace, name] = agentType.split(":", 2);
|
|
257
|
-
} else {
|
|
258
|
-
name = agentType;
|
|
259
|
-
}
|
|
260
|
-
const searchPaths = [];
|
|
261
|
-
if (namespace) {
|
|
262
|
-
searchPaths.push(join2(pluginDir, "agents", `${name}.md`));
|
|
263
|
-
const installPath = findPluginInstallPath(namespace);
|
|
264
|
-
if (installPath) {
|
|
265
|
-
searchPaths.push(join2(installPath, "agents", `${name}.md`));
|
|
266
|
-
}
|
|
267
|
-
} else {
|
|
268
|
-
searchPaths.push(join2(cwd, ".claude", "agents", `${name}.md`));
|
|
269
|
-
searchPaths.push(join2(homedir(), ".claude", "agents", `${name}.md`));
|
|
270
|
-
searchPaths.push(join2(pluginDir, "agents", `${name}.md`));
|
|
271
|
-
}
|
|
272
|
-
for (const path of searchPaths) {
|
|
273
|
-
try {
|
|
274
|
-
const content = readFileSync3(path, "utf-8");
|
|
275
|
-
const color = extractFrontmatterColor(content);
|
|
276
|
-
if (color) return normalizeTmuxColor(color);
|
|
277
|
-
} catch {
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
return null;
|
|
281
|
-
}
|
|
282
230
|
|
|
283
231
|
// src/daemon/tmux.ts
|
|
284
232
|
import { execSync } from "child_process";
|
|
@@ -296,9 +244,13 @@ function execSafe(cmd) {
|
|
|
296
244
|
return null;
|
|
297
245
|
}
|
|
298
246
|
}
|
|
299
|
-
function createPane(windowTarget, cwd) {
|
|
247
|
+
function createPane(windowTarget, cwd, position = "right") {
|
|
300
248
|
const cwdFlag = cwd ? ` -c ${shellQuote(cwd)}` : "";
|
|
301
|
-
const
|
|
249
|
+
const panes = listPanes(windowTarget);
|
|
250
|
+
const target = position === "left" ? panes[0]?.paneId : panes[panes.length - 1]?.paneId;
|
|
251
|
+
const targetFlag = target ? ` -t "${target}"` : ` -t "${windowTarget}"`;
|
|
252
|
+
const beforeFlag = position === "left" ? "b" : "";
|
|
253
|
+
const paneId = exec(`tmux split-window -h${beforeFlag}${targetFlag}${cwdFlag} -P -F "#{pane_id}"`);
|
|
302
254
|
execSafe(`tmux select-layout -t "${windowTarget}" even-horizontal`);
|
|
303
255
|
return paneId;
|
|
304
256
|
}
|
|
@@ -381,10 +333,10 @@ function setOrchestratorPaneId(sessionId, paneId) {
|
|
|
381
333
|
function loadOrchestratorPrompt(cwd) {
|
|
382
334
|
const projectPath = projectOrchestratorPromptPath(cwd);
|
|
383
335
|
if (existsSync(projectPath)) {
|
|
384
|
-
return
|
|
336
|
+
return readFileSync3(projectPath, "utf-8");
|
|
385
337
|
}
|
|
386
338
|
const bundledPath = resolve(import.meta.dirname, "../templates/orchestrator.md");
|
|
387
|
-
return
|
|
339
|
+
return readFileSync3(bundledPath, "utf-8");
|
|
388
340
|
}
|
|
389
341
|
function formatStateForOrchestrator(session) {
|
|
390
342
|
const shortId = session.id.slice(0, 8);
|
|
@@ -436,11 +388,15 @@ function formatStateForOrchestrator(session) {
|
|
|
436
388
|
${wtLines}`;
|
|
437
389
|
}
|
|
438
390
|
const worktreeHint = existsSync(worktreeConfigPath(session.cwd)) ? "Worktree config active (`.sisyphus/worktree.json`). Use `--worktree` flag with `sisyphus spawn` to isolate agents in their own worktrees. Recommended for feature work, especially with potential file overlap." : "No worktree configuration found. If this session involves parallel work where agents may edit overlapping files, use the `git-management` skill to set up `.sisyphus/worktree.json` and enable worktree isolation.";
|
|
391
|
+
const contextSection = session.context ? `
|
|
392
|
+
## Background Context
|
|
393
|
+
${session.context}
|
|
394
|
+
` : "";
|
|
439
395
|
return `<state>
|
|
440
396
|
session: ${shortId} (cycle ${cycleNum})
|
|
441
397
|
task: ${session.task}
|
|
442
398
|
status: ${session.status}
|
|
443
|
-
|
|
399
|
+
${contextSection}
|
|
444
400
|
## Plan
|
|
445
401
|
${planRef}
|
|
446
402
|
|
|
@@ -468,9 +424,12 @@ async function spawnOrchestrator(sessionId, cwd, windowId, message) {
|
|
|
468
424
|
const promptFilePath = `${promptsDir(cwd, sessionId)}/orchestrator-system-${cycleNum}.md`;
|
|
469
425
|
writeFileSync2(promptFilePath, basePrompt, "utf-8");
|
|
470
426
|
sessionWindowMap.set(sessionId, windowId);
|
|
427
|
+
const cliBin = resolve(import.meta.dirname, "cli.js");
|
|
428
|
+
const npmBinDir = resolve(import.meta.dirname, "../../.bin");
|
|
471
429
|
const envExports = [
|
|
472
430
|
`export SISYPHUS_SESSION_ID='${sessionId}'`,
|
|
473
|
-
`export SISYPHUS_AGENT_ID='orchestrator'
|
|
431
|
+
`export SISYPHUS_AGENT_ID='orchestrator'`,
|
|
432
|
+
`export PATH="${npmBinDir}:$PATH"`
|
|
474
433
|
].join(" && ");
|
|
475
434
|
let userPrompt;
|
|
476
435
|
if (message) {
|
|
@@ -494,15 +453,15 @@ Review the current session and delegate the next cycle of work.`;
|
|
|
494
453
|
writeFileSync2(userPromptFilePath, userPrompt, "utf-8");
|
|
495
454
|
const pluginPath = resolve(import.meta.dirname, "../templates/orchestrator-plugin");
|
|
496
455
|
const settingsPath = resolve(import.meta.dirname, "../templates/orchestrator-settings.json");
|
|
497
|
-
const claudeCmd = `claude --dangerously-skip-permissions --
|
|
498
|
-
const paneId = createPane(windowId, cwd);
|
|
456
|
+
const claudeCmd = `claude --dangerously-skip-permissions --settings "${settingsPath}" --plugin-dir "${pluginPath}" --append-system-prompt "$(cat '${promptFilePath}')" "$(cat '${userPromptFilePath}')"`;
|
|
457
|
+
const paneId = createPane(windowId, cwd, "left");
|
|
499
458
|
sessionOrchestratorPane.set(sessionId, paneId);
|
|
500
459
|
registerPane(paneId, sessionId, "orchestrator");
|
|
501
460
|
setPaneTitle(paneId, `Sisyphus`);
|
|
502
461
|
setPaneStyle(paneId, ORCHESTRATOR_COLOR);
|
|
503
462
|
const bannerPath = resolve(import.meta.dirname, "../templates/banner.txt");
|
|
504
463
|
const bannerCmd = existsSync(bannerPath) ? `cat '${bannerPath}' &&` : "";
|
|
505
|
-
const notifyCmd = `
|
|
464
|
+
const notifyCmd = `node "${cliBin}" notify pane-exited --pane-id ${paneId}`;
|
|
506
465
|
sendKeys(paneId, `${bannerCmd} ${envExports} && ${claudeCmd}; ${notifyCmd}`);
|
|
507
466
|
await addOrchestratorCycle(cwd, sessionId, {
|
|
508
467
|
cycle: cycleNum,
|
|
@@ -546,13 +505,13 @@ function cleanupSessionMaps(sessionId) {
|
|
|
546
505
|
}
|
|
547
506
|
|
|
548
507
|
// src/daemon/agent.ts
|
|
549
|
-
import { readFileSync as readFileSync6, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, readdirSync as readdirSync3, existsSync as
|
|
508
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, readdirSync as readdirSync3, existsSync as existsSync4 } from "fs";
|
|
550
509
|
import { resolve as resolve2 } from "path";
|
|
551
510
|
|
|
552
511
|
// src/daemon/worktree.ts
|
|
553
512
|
import { execSync as execSync2 } from "child_process";
|
|
554
|
-
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as
|
|
555
|
-
import { dirname as
|
|
513
|
+
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync4, readdirSync as readdirSync2, rmSync } from "fs";
|
|
514
|
+
import { dirname as dirname3, join as join2 } from "path";
|
|
556
515
|
var EXEC_ENV2 = {
|
|
557
516
|
...process.env,
|
|
558
517
|
PATH: `/opt/homebrew/bin:/usr/local/bin:${process.env["PATH"] ?? "/usr/bin:/bin"}`
|
|
@@ -572,7 +531,7 @@ function shellQuote2(s) {
|
|
|
572
531
|
}
|
|
573
532
|
function loadWorktreeConfig(cwd) {
|
|
574
533
|
try {
|
|
575
|
-
const content =
|
|
534
|
+
const content = readFileSync4(worktreeConfigPath(cwd), "utf-8");
|
|
576
535
|
return JSON.parse(content);
|
|
577
536
|
} catch {
|
|
578
537
|
return null;
|
|
@@ -580,8 +539,8 @@ function loadWorktreeConfig(cwd) {
|
|
|
580
539
|
}
|
|
581
540
|
function createWorktreeShell(cwd, sessionId, agentId) {
|
|
582
541
|
const branchName = `sisyphus/${sessionId.slice(0, 8)}/${agentId}`;
|
|
583
|
-
const worktreePath =
|
|
584
|
-
mkdirSync2(
|
|
542
|
+
const worktreePath = join2(worktreeBaseDir(cwd), sessionId.slice(0, 8), agentId);
|
|
543
|
+
mkdirSync2(dirname3(worktreePath), { recursive: true });
|
|
585
544
|
execSafe2(`git -C ${shellQuote2(cwd)} worktree prune`);
|
|
586
545
|
if (existsSync2(worktreePath)) {
|
|
587
546
|
execSafe2(`git -C ${shellQuote2(cwd)} worktree remove --force ${shellQuote2(worktreePath)}`);
|
|
@@ -594,16 +553,16 @@ function createWorktreeShell(cwd, sessionId, agentId) {
|
|
|
594
553
|
function bootstrapWorktree(cwd, worktreePath, config) {
|
|
595
554
|
if (config.copy) {
|
|
596
555
|
for (const entry of config.copy) {
|
|
597
|
-
const dest =
|
|
598
|
-
mkdirSync2(
|
|
599
|
-
execSafe2(`cp -r ${shellQuote2(
|
|
556
|
+
const dest = join2(worktreePath, entry);
|
|
557
|
+
mkdirSync2(dirname3(dest), { recursive: true });
|
|
558
|
+
execSafe2(`cp -r ${shellQuote2(join2(cwd, entry))} ${shellQuote2(dest)}`);
|
|
600
559
|
}
|
|
601
560
|
}
|
|
602
561
|
if (config.clone) {
|
|
603
562
|
for (const entry of config.clone) {
|
|
604
|
-
const dest =
|
|
605
|
-
mkdirSync2(
|
|
606
|
-
const src = shellQuote2(
|
|
563
|
+
const dest = join2(worktreePath, entry);
|
|
564
|
+
mkdirSync2(dirname3(dest), { recursive: true });
|
|
565
|
+
const src = shellQuote2(join2(cwd, entry));
|
|
607
566
|
const dstQ = shellQuote2(dest);
|
|
608
567
|
if (execSafe2(`cp -Rc ${src} ${dstQ}`) === null) {
|
|
609
568
|
execSafe2(`cp -r ${src} ${dstQ}`);
|
|
@@ -612,9 +571,9 @@ function bootstrapWorktree(cwd, worktreePath, config) {
|
|
|
612
571
|
}
|
|
613
572
|
if (config.symlink) {
|
|
614
573
|
for (const entry of config.symlink) {
|
|
615
|
-
const dest =
|
|
616
|
-
mkdirSync2(
|
|
617
|
-
execSafe2(`ln -s ${shellQuote2(
|
|
574
|
+
const dest = join2(worktreePath, entry);
|
|
575
|
+
mkdirSync2(dirname3(dest), { recursive: true });
|
|
576
|
+
execSafe2(`ln -s ${shellQuote2(join2(cwd, entry))} ${shellQuote2(dest)}`);
|
|
618
577
|
}
|
|
619
578
|
}
|
|
620
579
|
if (config.init) {
|
|
@@ -684,7 +643,7 @@ function mergeWorktrees(cwd, agents) {
|
|
|
684
643
|
function cleanupWorktree(cwd, worktreePath, branchName) {
|
|
685
644
|
execSafe2(`git -C ${shellQuote2(cwd)} worktree remove ${shellQuote2(worktreePath)} --force`);
|
|
686
645
|
execSafe2(`git -C ${shellQuote2(cwd)} branch -D ${shellQuote2(branchName)}`);
|
|
687
|
-
const baseDir =
|
|
646
|
+
const baseDir = dirname3(worktreePath);
|
|
688
647
|
try {
|
|
689
648
|
const entries = readdirSync2(baseDir);
|
|
690
649
|
if (entries.length === 0) {
|
|
@@ -697,6 +656,93 @@ function countWorktreeAgents(agents) {
|
|
|
697
656
|
return agents.filter((a) => a.worktreePath && a.status === "running").length;
|
|
698
657
|
}
|
|
699
658
|
|
|
659
|
+
// src/daemon/frontmatter.ts
|
|
660
|
+
import { readFileSync as readFileSync5, existsSync as existsSync3 } from "fs";
|
|
661
|
+
import { homedir } from "os";
|
|
662
|
+
import { join as join3 } from "path";
|
|
663
|
+
function detectProvider(model) {
|
|
664
|
+
if (!model) return "anthropic";
|
|
665
|
+
if (/^(gpt-|codex-)/.test(model)) return "openai";
|
|
666
|
+
return "anthropic";
|
|
667
|
+
}
|
|
668
|
+
function parseAgentFrontmatter(content) {
|
|
669
|
+
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
670
|
+
if (!match) return {};
|
|
671
|
+
const block = match[1];
|
|
672
|
+
const fm = {};
|
|
673
|
+
const str = (key) => {
|
|
674
|
+
const m = block.match(new RegExp(`^${key}:\\s*(.+)$`, "m"));
|
|
675
|
+
return m ? m[1].trim() : void 0;
|
|
676
|
+
};
|
|
677
|
+
fm.name = str("name");
|
|
678
|
+
fm.model = str("model");
|
|
679
|
+
fm.color = str("color");
|
|
680
|
+
fm.description = str("description");
|
|
681
|
+
fm.permissionMode = str("permissionMode");
|
|
682
|
+
const skillsMatch = block.match(/^skills:\s*\n((?:\s+-\s+.+\n?)*)/m);
|
|
683
|
+
if (skillsMatch) {
|
|
684
|
+
fm.skills = skillsMatch[1].split("\n").map((line) => line.replace(/^\s+-\s+/, "").trim()).filter(Boolean);
|
|
685
|
+
}
|
|
686
|
+
return fm;
|
|
687
|
+
}
|
|
688
|
+
function extractAgentBody(content) {
|
|
689
|
+
const match = content.match(/^---\n[\s\S]*?\n---\n?([\s\S]*)$/);
|
|
690
|
+
return match ? match[1].trim() : content.trim();
|
|
691
|
+
}
|
|
692
|
+
function findPluginInstallPath(namespace) {
|
|
693
|
+
try {
|
|
694
|
+
const registryPath2 = join3(homedir(), ".claude", "plugins", "installed_plugins.json");
|
|
695
|
+
const registry = JSON.parse(readFileSync5(registryPath2, "utf-8"));
|
|
696
|
+
for (const key of Object.keys(registry)) {
|
|
697
|
+
if (key.startsWith(`${namespace}@`)) {
|
|
698
|
+
return registry[key].installPath ?? null;
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
} catch {
|
|
702
|
+
}
|
|
703
|
+
return null;
|
|
704
|
+
}
|
|
705
|
+
function resolveAgentTypePath(agentType, pluginDir, cwd) {
|
|
706
|
+
if (!agentType) return null;
|
|
707
|
+
let namespace;
|
|
708
|
+
let name;
|
|
709
|
+
if (agentType.includes(":")) {
|
|
710
|
+
[namespace, name] = agentType.split(":", 2);
|
|
711
|
+
} else {
|
|
712
|
+
name = agentType;
|
|
713
|
+
}
|
|
714
|
+
const searchPaths = [];
|
|
715
|
+
if (namespace) {
|
|
716
|
+
searchPaths.push(join3(pluginDir, "agents", `${name}.md`));
|
|
717
|
+
const installPath = findPluginInstallPath(namespace);
|
|
718
|
+
if (installPath) {
|
|
719
|
+
searchPaths.push(join3(installPath, "agents", `${name}.md`));
|
|
720
|
+
}
|
|
721
|
+
} else {
|
|
722
|
+
searchPaths.push(join3(cwd, ".claude", "agents", `${name}.md`));
|
|
723
|
+
searchPaths.push(join3(homedir(), ".claude", "agents", `${name}.md`));
|
|
724
|
+
searchPaths.push(join3(pluginDir, "agents", `${name}.md`));
|
|
725
|
+
}
|
|
726
|
+
for (const path of searchPaths) {
|
|
727
|
+
if (existsSync3(path)) return path;
|
|
728
|
+
}
|
|
729
|
+
return null;
|
|
730
|
+
}
|
|
731
|
+
function resolveAgentConfig(agentType, pluginDir, cwd) {
|
|
732
|
+
const filePath = resolveAgentTypePath(agentType, pluginDir, cwd);
|
|
733
|
+
if (!filePath) return null;
|
|
734
|
+
try {
|
|
735
|
+
const content = readFileSync5(filePath, "utf-8");
|
|
736
|
+
return {
|
|
737
|
+
frontmatter: parseAgentFrontmatter(content),
|
|
738
|
+
body: extractAgentBody(content),
|
|
739
|
+
filePath
|
|
740
|
+
};
|
|
741
|
+
} catch {
|
|
742
|
+
return null;
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
|
|
700
746
|
// src/daemon/agent.ts
|
|
701
747
|
var agentCounters = /* @__PURE__ */ new Map();
|
|
702
748
|
function resetAgentCounterFromState(sessionId, agents) {
|
|
@@ -736,7 +782,9 @@ async function spawnAgent(opts) {
|
|
|
736
782
|
agentCounters.set(sessionId, count);
|
|
737
783
|
const agentId = `agent-${String(count).padStart(3, "0")}`;
|
|
738
784
|
const pluginPath = resolve2(import.meta.dirname, "../templates/agent-plugin");
|
|
739
|
-
const
|
|
785
|
+
const agentConfig = resolveAgentConfig(agentType, pluginPath, cwd);
|
|
786
|
+
const provider = detectProvider(agentConfig?.frontmatter.model);
|
|
787
|
+
const color = (agentConfig?.frontmatter.color ? normalizeTmuxColor(agentConfig.frontmatter.color) : null) ?? getNextColor(sessionId);
|
|
740
788
|
let paneCwd = cwd;
|
|
741
789
|
let worktreePath;
|
|
742
790
|
let branchName;
|
|
@@ -760,22 +808,40 @@ async function spawnAgent(opts) {
|
|
|
760
808
|
const suffixFilePath = `${promptsDir(cwd, sessionId)}/${agentId}-system.md`;
|
|
761
809
|
writeFileSync3(suffixFilePath, suffix, "utf-8");
|
|
762
810
|
const bannerPath = resolve2(import.meta.dirname, "../templates/banner.txt");
|
|
763
|
-
const bannerCmd =
|
|
811
|
+
const bannerCmd = existsSync4(bannerPath) ? `cat '${bannerPath}' &&` : "";
|
|
812
|
+
const cliBin = resolve2(import.meta.dirname, "cli.js");
|
|
813
|
+
const npmBinDir = resolve2(import.meta.dirname, "../../.bin");
|
|
764
814
|
const envExports = [
|
|
765
815
|
`export SISYPHUS_SESSION_ID='${sessionId}'`,
|
|
766
816
|
`export SISYPHUS_AGENT_ID='${agentId}'`,
|
|
767
|
-
...worktreeContext ? [`export SISYPHUS_PORT_OFFSET='${worktreeContext.offset}'`] : []
|
|
817
|
+
...worktreeContext ? [`export SISYPHUS_PORT_OFFSET='${worktreeContext.offset}'`] : [],
|
|
818
|
+
`export PATH="${npmBinDir}:$PATH"`
|
|
768
819
|
].join(" && ");
|
|
769
|
-
const
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
820
|
+
const notifyCmd = `node "${cliBin}" notify pane-exited --pane-id ${paneId}`;
|
|
821
|
+
let mainCmd;
|
|
822
|
+
if (provider === "openai") {
|
|
823
|
+
const codexPromptPath = `${promptsDir(cwd, sessionId)}/${agentId}-codex-prompt.md`;
|
|
824
|
+
const parts = [];
|
|
825
|
+
if (agentConfig?.body) {
|
|
826
|
+
parts.push(agentConfig.body);
|
|
827
|
+
}
|
|
828
|
+
parts.push(suffix);
|
|
829
|
+
parts.push(`## Task
|
|
830
|
+
|
|
831
|
+
${instruction}`);
|
|
832
|
+
writeFileSync3(codexPromptPath, parts.join("\n\n"), "utf-8");
|
|
833
|
+
const model = agentConfig?.frontmatter.model ?? "codex-mini";
|
|
834
|
+
mainCmd = `codex -m ${shellQuote3(model)} --dangerously-bypass-approvals-and-sandbox "$(cat '${codexPromptPath}')"`;
|
|
835
|
+
} else {
|
|
836
|
+
const agentFlag = agentType && agentType !== "worker" ? ` --agent ${shellQuote3(agentType)}` : "";
|
|
837
|
+
mainCmd = `claude --dangerously-skip-permissions --plugin-dir "${pluginPath}"${agentFlag} --append-system-prompt "$(cat '${suffixFilePath}')" ${shellQuote3(instruction)}`;
|
|
838
|
+
}
|
|
839
|
+
const fullCmd = `${bannerCmd} ${envExports} && ${mainCmd}; ${notifyCmd}`;
|
|
775
840
|
const agent = {
|
|
776
841
|
id: agentId,
|
|
777
842
|
name,
|
|
778
843
|
agentType,
|
|
844
|
+
provider,
|
|
779
845
|
color,
|
|
780
846
|
instruction,
|
|
781
847
|
status: "running",
|
|
@@ -970,9 +1036,9 @@ async function pollSession(sessionId, cwd, windowId) {
|
|
|
970
1036
|
}
|
|
971
1037
|
|
|
972
1038
|
// src/daemon/session-manager.ts
|
|
973
|
-
async function startSession(task, cwd, tmuxSession, windowId) {
|
|
1039
|
+
async function startSession(task, cwd, tmuxSession, windowId, context) {
|
|
974
1040
|
const sessionId = uuidv4();
|
|
975
|
-
const session = createSession(sessionId, task, cwd);
|
|
1041
|
+
const session = createSession(sessionId, task, cwd, context);
|
|
976
1042
|
await updateSessionTmux(cwd, sessionId, tmuxSession, windowId);
|
|
977
1043
|
trackSession(sessionId, cwd, tmuxSession);
|
|
978
1044
|
await spawnOrchestrator(sessionId, cwd, windowId);
|
|
@@ -985,7 +1051,7 @@ var PRUNE_KEEP_DAYS = 7;
|
|
|
985
1051
|
function pruneOldSessions(cwd) {
|
|
986
1052
|
try {
|
|
987
1053
|
const dir = sessionsDir(cwd);
|
|
988
|
-
if (!
|
|
1054
|
+
if (!existsSync5(dir)) return;
|
|
989
1055
|
const entries = readdirSync4(dir, { withFileTypes: true });
|
|
990
1056
|
const candidates = [];
|
|
991
1057
|
for (const entry of entries) {
|
|
@@ -1052,7 +1118,7 @@ function getSessionStatus(cwd, sessionId) {
|
|
|
1052
1118
|
}
|
|
1053
1119
|
function listSessions(cwd) {
|
|
1054
1120
|
const dir = sessionsDir(cwd);
|
|
1055
|
-
if (!
|
|
1121
|
+
if (!existsSync5(dir)) return [];
|
|
1056
1122
|
const entries = readdirSync4(dir, { withFileTypes: true });
|
|
1057
1123
|
const sessions = [];
|
|
1058
1124
|
for (const entry of entries) {
|
|
@@ -1223,7 +1289,7 @@ function persistSessionRegistry() {
|
|
|
1223
1289
|
}
|
|
1224
1290
|
function loadSessionRegistry() {
|
|
1225
1291
|
const p = registryPath();
|
|
1226
|
-
if (!
|
|
1292
|
+
if (!existsSync6(p)) return {};
|
|
1227
1293
|
try {
|
|
1228
1294
|
return JSON.parse(readFileSync7(p, "utf-8"));
|
|
1229
1295
|
} catch {
|
|
@@ -1242,7 +1308,7 @@ async function handleRequest(req) {
|
|
|
1242
1308
|
try {
|
|
1243
1309
|
switch (req.type) {
|
|
1244
1310
|
case "start": {
|
|
1245
|
-
const session = await startSession(req.task, req.cwd, req.tmuxSession, req.tmuxWindow);
|
|
1311
|
+
const session = await startSession(req.task, req.cwd, req.tmuxSession, req.tmuxWindow, req.context);
|
|
1246
1312
|
registerSessionCwd(session.id, req.cwd);
|
|
1247
1313
|
sessionTmuxMap.set(session.id, req.tmuxSession);
|
|
1248
1314
|
sessionWindowMap2.set(session.id, req.tmuxWindow);
|
|
@@ -1319,7 +1385,7 @@ async function handleRequest(req) {
|
|
|
1319
1385
|
let cwd = sessionCwdMap.get(req.sessionId);
|
|
1320
1386
|
if (!cwd) {
|
|
1321
1387
|
const stateFile = `${req.cwd}/.sisyphus/sessions/${req.sessionId}/state.json`;
|
|
1322
|
-
if (
|
|
1388
|
+
if (existsSync6(stateFile)) {
|
|
1323
1389
|
cwd = req.cwd;
|
|
1324
1390
|
registerSessionCwd(req.sessionId, cwd);
|
|
1325
1391
|
} else {
|
|
@@ -1370,7 +1436,7 @@ async function handleRequest(req) {
|
|
|
1370
1436
|
function startServer() {
|
|
1371
1437
|
return new Promise((resolve4, reject) => {
|
|
1372
1438
|
const sock = socketPath();
|
|
1373
|
-
if (
|
|
1439
|
+
if (existsSync6(sock)) {
|
|
1374
1440
|
unlinkSync(sock);
|
|
1375
1441
|
}
|
|
1376
1442
|
server = createServer((conn) => {
|
|
@@ -1412,7 +1478,7 @@ function stopServer() {
|
|
|
1412
1478
|
}
|
|
1413
1479
|
server.close(() => {
|
|
1414
1480
|
const sock = socketPath();
|
|
1415
|
-
if (
|
|
1481
|
+
if (existsSync6(sock)) {
|
|
1416
1482
|
unlinkSync(sock);
|
|
1417
1483
|
}
|
|
1418
1484
|
server = null;
|
|
@@ -1618,7 +1684,7 @@ async function recoverSessions() {
|
|
|
1618
1684
|
let recovered = 0;
|
|
1619
1685
|
for (const [sessionId, cwd] of entries) {
|
|
1620
1686
|
const stateFile = statePath(cwd, sessionId);
|
|
1621
|
-
if (!
|
|
1687
|
+
if (!existsSync7(stateFile)) {
|
|
1622
1688
|
continue;
|
|
1623
1689
|
}
|
|
1624
1690
|
try {
|