sisyphi 1.1.18 → 1.1.19
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/README.md +195 -75
- package/dist/chunk-36VJ7ZBD.js +1898 -0
- package/dist/chunk-36VJ7ZBD.js.map +1 -0
- package/dist/{chunk-C2XKXERJ.js → chunk-M6Z3KHOH.js} +159 -46
- package/dist/chunk-M6Z3KHOH.js.map +1 -0
- package/dist/chunk-O4ZHSQ5R.js +544 -0
- package/dist/chunk-O4ZHSQ5R.js.map +1 -0
- package/dist/chunk-P2HHTIPM.js +478 -0
- package/dist/chunk-P2HHTIPM.js.map +1 -0
- package/dist/{chunk-TMBAVPHH.js → chunk-PNDCVKBN.js} +73 -1
- package/dist/chunk-PNDCVKBN.js.map +1 -0
- package/dist/chunk-SVGIQ2G4.js +1076 -0
- package/dist/chunk-SVGIQ2G4.js.map +1 -0
- package/dist/cli.js +4405 -892
- package/dist/cli.js.map +1 -1
- package/dist/daemon.js +4340 -1990
- package/dist/daemon.js.map +1 -1
- package/dist/{paths-XRDEEJ5R.js → paths-JXFLR5BN.js} +38 -2
- package/dist/single-ask-6G4BIVY2.js +132 -0
- package/dist/single-ask-6G4BIVY2.js.map +1 -0
- package/dist/templates/CLAUDE.md +1 -56
- package/dist/templates/agent-plugin/agents/CLAUDE.md +2 -65
- package/dist/templates/agent-plugin/agents/debug.md +43 -6
- package/dist/templates/agent-plugin/agents/debug.settings.json +57 -0
- package/dist/templates/agent-plugin/agents/explore.md +28 -1
- package/dist/templates/agent-plugin/agents/explore.settings.json +57 -0
- package/dist/templates/agent-plugin/agents/implementor.md +94 -0
- package/dist/templates/agent-plugin/agents/implementor.settings.json +57 -0
- package/dist/templates/agent-plugin/agents/operator.md +43 -1
- package/dist/templates/agent-plugin/agents/operator.settings.json +57 -0
- package/dist/templates/agent-plugin/agents/plan/sub-planner.md +75 -0
- package/dist/templates/agent-plugin/agents/plan.md +176 -86
- package/dist/templates/agent-plugin/agents/plan.settings.json +57 -0
- package/dist/templates/agent-plugin/agents/problem/adversarial.md +26 -0
- package/dist/templates/agent-plugin/agents/problem/contrarian.md +26 -0
- package/dist/templates/agent-plugin/agents/problem/first-principles.md +26 -0
- package/dist/templates/agent-plugin/agents/problem/precedent.md +25 -0
- package/dist/templates/agent-plugin/agents/problem/simplifier.md +26 -0
- package/dist/templates/agent-plugin/agents/problem/systems-thinker.md +26 -0
- package/dist/templates/agent-plugin/agents/problem/time-traveler.md +26 -0
- package/dist/templates/agent-plugin/agents/problem/user-empathy.md +26 -0
- package/dist/templates/agent-plugin/agents/problem.md +334 -79
- package/dist/templates/agent-plugin/agents/problem.settings.json +57 -0
- package/dist/templates/agent-plugin/agents/research-lead/CLAUDE.md +26 -0
- package/dist/templates/agent-plugin/agents/research-lead/critic.md +61 -0
- package/dist/templates/agent-plugin/agents/research-lead/researcher.md +60 -0
- package/dist/templates/agent-plugin/agents/research-lead.md +184 -0
- package/dist/templates/agent-plugin/agents/research-lead.settings.json +57 -0
- package/dist/templates/agent-plugin/agents/review/CLAUDE.md +3 -29
- package/dist/templates/agent-plugin/agents/review/compliance.md +14 -3
- package/dist/templates/agent-plugin/agents/review/efficiency.md +15 -4
- package/dist/templates/agent-plugin/agents/review/quality.md +20 -6
- package/dist/templates/agent-plugin/agents/review/reuse.md +17 -5
- package/dist/templates/agent-plugin/agents/review/security.md +10 -3
- package/dist/templates/agent-plugin/agents/review/tests.md +58 -0
- package/dist/templates/agent-plugin/agents/review-plan/CLAUDE.md +28 -0
- package/dist/templates/agent-plugin/agents/review-plan/code-smells.md +4 -2
- package/dist/templates/agent-plugin/agents/review-plan/pattern-consistency.md +4 -2
- package/dist/templates/agent-plugin/agents/review-plan/requirements-coverage.md +3 -1
- package/dist/templates/agent-plugin/agents/review-plan/security.md +5 -2
- package/dist/templates/agent-plugin/agents/review-plan.md +52 -5
- package/dist/templates/agent-plugin/agents/review-plan.settings.json +57 -0
- package/dist/templates/agent-plugin/agents/review.md +89 -16
- package/dist/templates/agent-plugin/agents/review.settings.json +57 -0
- package/dist/templates/agent-plugin/agents/spec/engineer.md +175 -0
- package/dist/templates/agent-plugin/agents/spec/requirements-writer.md +149 -0
- package/dist/templates/agent-plugin/agents/spec.md +444 -0
- package/dist/templates/agent-plugin/agents/spec.settings.json +57 -0
- package/dist/templates/agent-plugin/agents/test-spec.md +58 -2
- package/dist/templates/agent-plugin/agents/test-spec.settings.json +57 -0
- package/dist/templates/agent-plugin/hooks/CLAUDE.md +9 -57
- package/dist/templates/agent-plugin/hooks/ask-background-guard.sh +57 -0
- package/dist/templates/agent-plugin/hooks/intercept-send-message.sh +1 -1
- package/dist/templates/agent-plugin/hooks/plan-user-prompt.sh +8 -7
- package/dist/templates/agent-plugin/hooks/plan-validate.sh +97 -0
- package/dist/templates/agent-plugin/hooks/plan-write-path.sh +55 -0
- package/dist/templates/agent-plugin/hooks/problem-user-prompt.sh +26 -0
- package/dist/templates/agent-plugin/hooks/register-bg-task.sh +37 -0
- package/dist/templates/agent-plugin/hooks/require-submit.sh +51 -42
- package/dist/templates/agent-plugin/hooks/review-user-prompt.sh +6 -2
- package/dist/templates/agent-plugin/hooks/spec-user-prompt.sh +43 -0
- package/dist/templates/agent-plugin/skills/humanloop/SKILL.md +147 -0
- package/dist/templates/agent-plugin/skills/perspective-fanout/SKILL.md +115 -0
- package/dist/templates/agent-plugin/skills/problem-document/SKILL.md +105 -0
- package/dist/templates/agent-plugin/skills/problem-plateau-breakers/SKILL.md +83 -0
- package/dist/templates/agent-suffix.md +7 -4
- package/dist/templates/baleia.lua +42 -0
- package/dist/templates/companion-plugin/hooks/user-prompt-context.sh +1 -1
- package/dist/templates/dashboard-claude.md +7 -3
- package/dist/templates/orchestrator-base.md +89 -52
- package/dist/templates/orchestrator-completion.md +47 -24
- package/dist/templates/orchestrator-discovery.md +183 -0
- package/dist/templates/orchestrator-impl.md +47 -18
- package/dist/templates/orchestrator-planning.md +109 -20
- package/dist/templates/orchestrator-plugin/commands/sisyphus/scratch.md +19 -0
- package/dist/templates/orchestrator-plugin/commands/sisyphus/spec.md +11 -0
- package/dist/templates/orchestrator-plugin/commands/sisyphus/strategize.md +5 -5
- package/dist/templates/orchestrator-plugin/hooks/hooks.json +0 -10
- package/dist/templates/orchestrator-plugin/skills/humanloop/SKILL.md +149 -0
- package/dist/templates/orchestrator-plugin/skills/orchestration/CLAUDE.md +1 -0
- package/dist/templates/orchestrator-plugin/skills/orchestration/SKILL.md +2 -1
- package/dist/templates/orchestrator-plugin/skills/orchestration/strategy.md +160 -0
- package/dist/templates/orchestrator-plugin/skills/orchestration/task-patterns.md +26 -28
- package/dist/templates/orchestrator-plugin/skills/orchestration/workflow-examples.md +133 -25
- package/dist/templates/orchestrator-settings.json +55 -0
- package/dist/templates/orchestrator-validation.md +17 -14
- package/dist/templates/sisyphus-init.lua +30 -0
- package/dist/templates/sisyphus-tmux-plugin/hooks/hooks.json +54 -0
- package/dist/templates/sisyphus-tmux-plugin/hooks/tmux-state.sh +19 -0
- package/dist/templates/termrender-haiku-system.md +82 -0
- package/dist/templates/whip-animation.sh +345 -0
- package/dist/tui.js +3242 -2189
- package/dist/tui.js.map +1 -1
- package/native/SisyphusNotify/main.swift +15 -5
- package/package.json +8 -6
- package/templates/CLAUDE.md +1 -56
- package/templates/agent-plugin/agents/CLAUDE.md +2 -65
- package/templates/agent-plugin/agents/debug.md +43 -6
- package/templates/agent-plugin/agents/debug.settings.json +57 -0
- package/templates/agent-plugin/agents/explore.md +28 -1
- package/templates/agent-plugin/agents/explore.settings.json +57 -0
- package/templates/agent-plugin/agents/implementor.md +94 -0
- package/templates/agent-plugin/agents/implementor.settings.json +57 -0
- package/templates/agent-plugin/agents/operator.md +43 -1
- package/templates/agent-plugin/agents/operator.settings.json +57 -0
- package/templates/agent-plugin/agents/plan/sub-planner.md +75 -0
- package/templates/agent-plugin/agents/plan.md +176 -86
- package/templates/agent-plugin/agents/plan.settings.json +57 -0
- package/templates/agent-plugin/agents/problem/adversarial.md +26 -0
- package/templates/agent-plugin/agents/problem/contrarian.md +26 -0
- package/templates/agent-plugin/agents/problem/first-principles.md +26 -0
- package/templates/agent-plugin/agents/problem/precedent.md +25 -0
- package/templates/agent-plugin/agents/problem/simplifier.md +26 -0
- package/templates/agent-plugin/agents/problem/systems-thinker.md +26 -0
- package/templates/agent-plugin/agents/problem/time-traveler.md +26 -0
- package/templates/agent-plugin/agents/problem/user-empathy.md +26 -0
- package/templates/agent-plugin/agents/problem.md +334 -79
- package/templates/agent-plugin/agents/problem.settings.json +57 -0
- package/templates/agent-plugin/agents/research-lead/CLAUDE.md +26 -0
- package/templates/agent-plugin/agents/research-lead/critic.md +61 -0
- package/templates/agent-plugin/agents/research-lead/researcher.md +60 -0
- package/templates/agent-plugin/agents/research-lead.md +184 -0
- package/templates/agent-plugin/agents/research-lead.settings.json +57 -0
- package/templates/agent-plugin/agents/review/CLAUDE.md +3 -29
- package/templates/agent-plugin/agents/review/compliance.md +14 -3
- package/templates/agent-plugin/agents/review/efficiency.md +15 -4
- package/templates/agent-plugin/agents/review/quality.md +20 -6
- package/templates/agent-plugin/agents/review/reuse.md +17 -5
- package/templates/agent-plugin/agents/review/security.md +10 -3
- package/templates/agent-plugin/agents/review/tests.md +58 -0
- package/templates/agent-plugin/agents/review-plan/CLAUDE.md +28 -0
- package/templates/agent-plugin/agents/review-plan/code-smells.md +4 -2
- package/templates/agent-plugin/agents/review-plan/pattern-consistency.md +4 -2
- package/templates/agent-plugin/agents/review-plan/requirements-coverage.md +3 -1
- package/templates/agent-plugin/agents/review-plan/security.md +5 -2
- package/templates/agent-plugin/agents/review-plan.md +52 -5
- package/templates/agent-plugin/agents/review-plan.settings.json +57 -0
- package/templates/agent-plugin/agents/review.md +89 -16
- package/templates/agent-plugin/agents/review.settings.json +57 -0
- package/templates/agent-plugin/agents/spec/engineer.md +175 -0
- package/templates/agent-plugin/agents/spec/requirements-writer.md +149 -0
- package/templates/agent-plugin/agents/spec.md +444 -0
- package/templates/agent-plugin/agents/spec.settings.json +57 -0
- package/templates/agent-plugin/agents/test-spec.md +58 -2
- package/templates/agent-plugin/agents/test-spec.settings.json +57 -0
- package/templates/agent-plugin/hooks/CLAUDE.md +9 -57
- package/templates/agent-plugin/hooks/ask-background-guard.sh +57 -0
- package/templates/agent-plugin/hooks/intercept-send-message.sh +1 -1
- package/templates/agent-plugin/hooks/plan-user-prompt.sh +8 -7
- package/templates/agent-plugin/hooks/plan-validate.sh +97 -0
- package/templates/agent-plugin/hooks/plan-write-path.sh +55 -0
- package/templates/agent-plugin/hooks/problem-user-prompt.sh +26 -0
- package/templates/agent-plugin/hooks/register-bg-task.sh +37 -0
- package/templates/agent-plugin/hooks/require-submit.sh +51 -42
- package/templates/agent-plugin/hooks/review-user-prompt.sh +6 -2
- package/templates/agent-plugin/hooks/spec-user-prompt.sh +43 -0
- package/templates/agent-plugin/skills/humanloop/SKILL.md +147 -0
- package/templates/agent-plugin/skills/perspective-fanout/SKILL.md +115 -0
- package/templates/agent-plugin/skills/problem-document/SKILL.md +105 -0
- package/templates/agent-plugin/skills/problem-plateau-breakers/SKILL.md +83 -0
- package/templates/agent-suffix.md +7 -4
- package/templates/baleia.lua +42 -0
- package/templates/companion-plugin/hooks/user-prompt-context.sh +1 -1
- package/templates/dashboard-claude.md +7 -3
- package/templates/orchestrator-base.md +89 -52
- package/templates/orchestrator-completion.md +47 -24
- package/templates/orchestrator-discovery.md +183 -0
- package/templates/orchestrator-impl.md +47 -18
- package/templates/orchestrator-planning.md +109 -20
- package/templates/orchestrator-plugin/commands/sisyphus/scratch.md +19 -0
- package/templates/orchestrator-plugin/commands/sisyphus/spec.md +11 -0
- package/templates/orchestrator-plugin/commands/sisyphus/strategize.md +5 -5
- package/templates/orchestrator-plugin/hooks/hooks.json +0 -10
- package/templates/orchestrator-plugin/skills/humanloop/SKILL.md +149 -0
- package/templates/orchestrator-plugin/skills/orchestration/CLAUDE.md +1 -0
- package/templates/orchestrator-plugin/skills/orchestration/SKILL.md +2 -1
- package/templates/orchestrator-plugin/skills/orchestration/strategy.md +160 -0
- package/templates/orchestrator-plugin/skills/orchestration/task-patterns.md +26 -28
- package/templates/orchestrator-plugin/skills/orchestration/workflow-examples.md +133 -25
- package/templates/orchestrator-settings.json +55 -0
- package/templates/orchestrator-validation.md +17 -14
- package/templates/sisyphus-init.lua +30 -0
- package/templates/sisyphus-tmux-plugin/hooks/hooks.json +54 -0
- package/templates/sisyphus-tmux-plugin/hooks/tmux-state.sh +19 -0
- package/templates/termrender-haiku-system.md +82 -0
- package/templates/whip-animation.sh +345 -0
- package/dist/chunk-22ZGZTGY.js +0 -67
- package/dist/chunk-22ZGZTGY.js.map +0 -1
- package/dist/chunk-6PJVJEYQ.js +0 -46
- package/dist/chunk-6PJVJEYQ.js.map +0 -1
- package/dist/chunk-C2XKXERJ.js.map +0 -1
- package/dist/chunk-TMBAVPHH.js.map +0 -1
- package/dist/chunk-V36NXMHP.js +0 -299
- package/dist/chunk-V36NXMHP.js.map +0 -1
- package/dist/templates/agent-plugin/agents/design.md +0 -134
- package/dist/templates/agent-plugin/agents/requirements.md +0 -138
- package/dist/templates/begin.md +0 -22
- package/dist/templates/nvim-tutorial.txt +0 -68
- package/dist/templates/orchestrator-plugin/commands/sisyphus/design.md +0 -13
- package/dist/templates/orchestrator-plugin/commands/sisyphus/requirements.md +0 -13
- package/dist/templates/orchestrator-plugin/hooks/idle-notify.sh +0 -71
- package/dist/templates/orchestrator-strategy.md +0 -238
- package/templates/agent-plugin/agents/design.md +0 -134
- package/templates/agent-plugin/agents/requirements.md +0 -138
- package/templates/begin.md +0 -22
- package/templates/nvim-tutorial.txt +0 -68
- package/templates/orchestrator-plugin/commands/sisyphus/design.md +0 -13
- package/templates/orchestrator-plugin/commands/sisyphus/requirements.md +0 -13
- package/templates/orchestrator-plugin/hooks/idle-notify.sh +0 -71
- package/templates/orchestrator-strategy.md +0 -238
- /package/dist/{paths-XRDEEJ5R.js.map → paths-JXFLR5BN.js.map} +0 -0
|
@@ -0,0 +1,1076 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
askDecisionsPath,
|
|
4
|
+
askDir,
|
|
5
|
+
askMetaPath,
|
|
6
|
+
askOutputPath,
|
|
7
|
+
askProgressPath,
|
|
8
|
+
askVisualsDir,
|
|
9
|
+
contextDir,
|
|
10
|
+
globalConfigPath,
|
|
11
|
+
goalPath,
|
|
12
|
+
historyBaseDir,
|
|
13
|
+
historyEventsPath,
|
|
14
|
+
historySessionDir,
|
|
15
|
+
historySessionSummaryPath,
|
|
16
|
+
initialPromptPath,
|
|
17
|
+
legacyLogsPath,
|
|
18
|
+
logsDir,
|
|
19
|
+
projectConfigPath,
|
|
20
|
+
promptsDir,
|
|
21
|
+
roadmapPath,
|
|
22
|
+
sessionDir,
|
|
23
|
+
snapshotDir,
|
|
24
|
+
snapshotsDir,
|
|
25
|
+
statePath,
|
|
26
|
+
strategyPath
|
|
27
|
+
} from "./chunk-PNDCVKBN.js";
|
|
28
|
+
|
|
29
|
+
// src/shared/config.ts
|
|
30
|
+
import { readFileSync } from "fs";
|
|
31
|
+
var DEFAULT_CONFIG = {
|
|
32
|
+
model: "claude-opus-4-7[1m]",
|
|
33
|
+
pollIntervalMs: 5e3,
|
|
34
|
+
orchestratorEffort: "xhigh",
|
|
35
|
+
agentEffort: "medium",
|
|
36
|
+
notifications: {
|
|
37
|
+
enabled: true,
|
|
38
|
+
sound: "/System/Library/Sounds/Hero.aiff"
|
|
39
|
+
},
|
|
40
|
+
companionPopup: true,
|
|
41
|
+
requiredPlugins: [
|
|
42
|
+
{ name: "devcore", marketplace: "crouton-kit" }
|
|
43
|
+
]
|
|
44
|
+
};
|
|
45
|
+
function readJsonFile(filePath) {
|
|
46
|
+
try {
|
|
47
|
+
const content = readFileSync(filePath, "utf-8");
|
|
48
|
+
return JSON.parse(content);
|
|
49
|
+
} catch {
|
|
50
|
+
return {};
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function loadConfig(cwd) {
|
|
54
|
+
const globalConfig = readJsonFile(globalConfigPath());
|
|
55
|
+
const projectConfig = readJsonFile(projectConfigPath(cwd));
|
|
56
|
+
if (projectConfig.upload !== void 0) {
|
|
57
|
+
console.warn(
|
|
58
|
+
"ignoring `upload` block from project-local .sisyphus/config.json \u2014 only the global config can set upload credentials"
|
|
59
|
+
);
|
|
60
|
+
delete projectConfig.upload;
|
|
61
|
+
}
|
|
62
|
+
const merged = { ...DEFAULT_CONFIG, ...globalConfig, ...projectConfig };
|
|
63
|
+
if (globalConfig.statusBar || projectConfig.statusBar) {
|
|
64
|
+
merged.statusBar = {
|
|
65
|
+
...merged.statusBar,
|
|
66
|
+
...globalConfig.statusBar,
|
|
67
|
+
...projectConfig.statusBar,
|
|
68
|
+
colors: {
|
|
69
|
+
...merged.statusBar?.colors,
|
|
70
|
+
...globalConfig.statusBar?.colors,
|
|
71
|
+
...projectConfig.statusBar?.colors
|
|
72
|
+
},
|
|
73
|
+
segments: {
|
|
74
|
+
...merged.statusBar?.segments,
|
|
75
|
+
...globalConfig.statusBar?.segments,
|
|
76
|
+
...projectConfig.statusBar?.segments
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
return merged;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// src/daemon/state.ts
|
|
84
|
+
import { copyFileSync, cpSync, existsSync as existsSync2, mkdirSync, readFileSync as readFileSync3, readdirSync, rmSync, statSync, writeFileSync as writeFileSync3 } from "fs";
|
|
85
|
+
import { join as join3 } from "path";
|
|
86
|
+
|
|
87
|
+
// src/daemon/lib/atomic.ts
|
|
88
|
+
import { randomUUID } from "crypto";
|
|
89
|
+
import { dirname, join } from "path";
|
|
90
|
+
import { renameSync, writeFileSync } from "fs";
|
|
91
|
+
function atomicWrite(filePath, data) {
|
|
92
|
+
const dir = dirname(filePath);
|
|
93
|
+
const tmpPath = join(dir, `.atomic.${randomUUID()}.tmp`);
|
|
94
|
+
writeFileSync(tmpPath, data, "utf-8");
|
|
95
|
+
renameSync(tmpPath, filePath);
|
|
96
|
+
}
|
|
97
|
+
var locks = /* @__PURE__ */ new Map();
|
|
98
|
+
async function withLock(key, fn) {
|
|
99
|
+
const prev = locks.get(key) ?? Promise.resolve();
|
|
100
|
+
let resolve;
|
|
101
|
+
const next = new Promise((r) => {
|
|
102
|
+
resolve = r;
|
|
103
|
+
});
|
|
104
|
+
locks.set(key, next);
|
|
105
|
+
await prev;
|
|
106
|
+
try {
|
|
107
|
+
return fn();
|
|
108
|
+
} finally {
|
|
109
|
+
resolve();
|
|
110
|
+
if (locks.get(key) === next) {
|
|
111
|
+
locks.delete(key);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// src/shared/gitignore.ts
|
|
117
|
+
import { existsSync, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
118
|
+
import { join as join2 } from "path";
|
|
119
|
+
var SISYPHUS_ENTRIES = [".sisyphus"];
|
|
120
|
+
var SISYPHUS_HEADER = "# Sisyphus";
|
|
121
|
+
function ensureSisyphusGitignore(cwd) {
|
|
122
|
+
if (!existsSync(join2(cwd, ".git"))) return;
|
|
123
|
+
const gitignorePath = join2(cwd, ".gitignore");
|
|
124
|
+
let content = "";
|
|
125
|
+
if (existsSync(gitignorePath)) {
|
|
126
|
+
content = readFileSync2(gitignorePath, "utf-8");
|
|
127
|
+
}
|
|
128
|
+
const lines = content.split("\n");
|
|
129
|
+
const missing = SISYPHUS_ENTRIES.filter((entry) => !lines.some((line) => line.trim() === entry));
|
|
130
|
+
if (missing.length === 0) return;
|
|
131
|
+
const block = [SISYPHUS_HEADER, ...missing].join("\n");
|
|
132
|
+
const separator = content.length > 0 && !content.endsWith("\n\n") ? content.endsWith("\n") ? "\n" : "\n\n" : "";
|
|
133
|
+
writeFileSync2(gitignorePath, content + separator + block + "\n", "utf-8");
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// src/shared/types.ts
|
|
137
|
+
var ORCHESTRATOR_ASKED_BY = "orchestrator";
|
|
138
|
+
|
|
139
|
+
// src/daemon/state.ts
|
|
140
|
+
var ROADMAP_SEED = `---
|
|
141
|
+
description: >
|
|
142
|
+
Living document tracking development phases and outstanding work.
|
|
143
|
+
---
|
|
144
|
+
`;
|
|
145
|
+
var CONTEXT_CLAUDE_MD = `# context/
|
|
146
|
+
|
|
147
|
+
Agents save exploration findings, architectural notes, and reference material here for use across cycles.
|
|
148
|
+
`;
|
|
149
|
+
function withSessionLock(sessionId, fn) {
|
|
150
|
+
return withLock(sessionId, fn);
|
|
151
|
+
}
|
|
152
|
+
function createSession(id, task, cwd, context, name, effort) {
|
|
153
|
+
ensureSisyphusGitignore(cwd);
|
|
154
|
+
const dir = sessionDir(cwd, id);
|
|
155
|
+
mkdirSync(dir, { recursive: true });
|
|
156
|
+
mkdirSync(contextDir(cwd, id), { recursive: true });
|
|
157
|
+
mkdirSync(promptsDir(cwd, id), { recursive: true });
|
|
158
|
+
writeFileSync3(roadmapPath(cwd, id), ROADMAP_SEED, "utf-8");
|
|
159
|
+
mkdirSync(logsDir(cwd, id), { recursive: true });
|
|
160
|
+
writeFileSync3(goalPath(cwd, id), task, "utf-8");
|
|
161
|
+
writeFileSync3(initialPromptPath(cwd, id), task, "utf-8");
|
|
162
|
+
writeFileSync3(join3(contextDir(cwd, id), "CLAUDE.md"), CONTEXT_CLAUDE_MD, "utf-8");
|
|
163
|
+
if (context) {
|
|
164
|
+
writeFileSync3(join3(contextDir(cwd, id), "initial-context.md"), context, "utf-8");
|
|
165
|
+
}
|
|
166
|
+
const createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
167
|
+
const created = new Date(createdAt);
|
|
168
|
+
const session = {
|
|
169
|
+
id,
|
|
170
|
+
...name ? { name } : {},
|
|
171
|
+
task,
|
|
172
|
+
...context ? { context } : {},
|
|
173
|
+
cwd,
|
|
174
|
+
status: "active",
|
|
175
|
+
createdAt,
|
|
176
|
+
activeMs: 0,
|
|
177
|
+
userBlockedMs: 0,
|
|
178
|
+
agents: [],
|
|
179
|
+
orchestratorCycles: [],
|
|
180
|
+
messages: [],
|
|
181
|
+
startHour: created.getHours(),
|
|
182
|
+
startDayOfWeek: created.getDay(),
|
|
183
|
+
orphaned: false,
|
|
184
|
+
...effort ? { effort } : {}
|
|
185
|
+
};
|
|
186
|
+
atomicWrite(statePath(cwd, id), JSON.stringify(session, null, 2));
|
|
187
|
+
return session;
|
|
188
|
+
}
|
|
189
|
+
function getSession(cwd, sessionId) {
|
|
190
|
+
const content = readFileSync3(statePath(cwd, sessionId), "utf-8");
|
|
191
|
+
const session = JSON.parse(content);
|
|
192
|
+
if (session.activeMs == null) session.activeMs = 0;
|
|
193
|
+
if (session.userBlockedMs == null) session.userBlockedMs = 0;
|
|
194
|
+
for (const agent of session.agents) {
|
|
195
|
+
if (!agent.repo) agent.repo = ".";
|
|
196
|
+
if (agent.activeMs == null) agent.activeMs = 0;
|
|
197
|
+
if (agent.orphaned == null) agent.orphaned = false;
|
|
198
|
+
}
|
|
199
|
+
if (session.orphaned == null) session.orphaned = false;
|
|
200
|
+
for (const cycle of session.orchestratorCycles) {
|
|
201
|
+
if (cycle.activeMs == null) cycle.activeMs = 0;
|
|
202
|
+
if (cycle.userBlockedMs == null) cycle.userBlockedMs = 0;
|
|
203
|
+
}
|
|
204
|
+
return session;
|
|
205
|
+
}
|
|
206
|
+
function saveSession(session) {
|
|
207
|
+
atomicWrite(statePath(session.cwd, session.id), JSON.stringify(session, null, 2));
|
|
208
|
+
}
|
|
209
|
+
async function addAgent(cwd, sessionId, agent) {
|
|
210
|
+
return withSessionLock(sessionId, () => {
|
|
211
|
+
const session = getSession(cwd, sessionId);
|
|
212
|
+
session.agents.push(agent);
|
|
213
|
+
saveSession(session);
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
async function updateAgent(cwd, sessionId, agentId, updates) {
|
|
217
|
+
return withSessionLock(sessionId, () => {
|
|
218
|
+
const session = getSession(cwd, sessionId);
|
|
219
|
+
const agent = session.agents.slice().reverse().find((a) => a.id === agentId);
|
|
220
|
+
if (!agent) throw new Error(`Agent ${agentId} not found in session ${sessionId}`);
|
|
221
|
+
Object.assign(agent, updates);
|
|
222
|
+
saveSession(session);
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
async function markAgentOrphan(cwd, sessionId, agentId, opts) {
|
|
226
|
+
return withSessionLock(sessionId, () => {
|
|
227
|
+
const session = getSession(cwd, sessionId);
|
|
228
|
+
const agent = session.agents.slice().reverse().find((a) => a.id === agentId);
|
|
229
|
+
if (!agent) throw new Error(`Agent ${agentId} not found in session ${sessionId}`);
|
|
230
|
+
agent.orphaned = true;
|
|
231
|
+
agent.status = opts.status !== void 0 ? opts.status : "lost";
|
|
232
|
+
agent.killedReason = opts.reason;
|
|
233
|
+
agent.completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
234
|
+
if (opts.activeMs !== void 0) agent.activeMs = opts.activeMs;
|
|
235
|
+
delete agent.pid;
|
|
236
|
+
delete agent.pidLstart;
|
|
237
|
+
saveSession(session);
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
async function markSessionOrphan(cwd, sessionId, opts) {
|
|
241
|
+
return withSessionLock(sessionId, () => {
|
|
242
|
+
const session = getSession(cwd, sessionId);
|
|
243
|
+
session.orphaned = true;
|
|
244
|
+
session.orphanReason = opts.reason;
|
|
245
|
+
saveSession(session);
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
async function clearSessionOrphan(cwd, sessionId) {
|
|
249
|
+
return withSessionLock(sessionId, () => {
|
|
250
|
+
const session = getSession(cwd, sessionId);
|
|
251
|
+
if (!session.orphaned && session.orphanReason == null) return;
|
|
252
|
+
session.orphaned = false;
|
|
253
|
+
delete session.orphanReason;
|
|
254
|
+
saveSession(session);
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
async function setAgentPid(cwd, sessionId, agentId, pid, pidLstart) {
|
|
258
|
+
return withSessionLock(sessionId, () => {
|
|
259
|
+
const session = getSession(cwd, sessionId);
|
|
260
|
+
const agent = session.agents.slice().reverse().find((a) => a.id === agentId);
|
|
261
|
+
if (!agent) return;
|
|
262
|
+
agent.pid = pid;
|
|
263
|
+
agent.pidLstart = pidLstart;
|
|
264
|
+
saveSession(session);
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
async function setAgentConsumedInline(cwd, sessionId, agentId) {
|
|
268
|
+
return withSessionLock(sessionId, () => {
|
|
269
|
+
const session = getSession(cwd, sessionId);
|
|
270
|
+
const agent = session.agents.slice().reverse().find((a) => a.id === agentId);
|
|
271
|
+
if (!agent) throw new Error(`Agent ${agentId} not found in session ${sessionId}`);
|
|
272
|
+
if (agent.consumedInline) return;
|
|
273
|
+
agent.consumedInline = true;
|
|
274
|
+
saveSession(session);
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
async function addOrchestratorCycle(cwd, sessionId, cycle) {
|
|
278
|
+
return withSessionLock(sessionId, () => {
|
|
279
|
+
const session = getSession(cwd, sessionId);
|
|
280
|
+
session.orchestratorCycles.push(cycle);
|
|
281
|
+
saveSession(session);
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
async function updateSessionStatus(cwd, sessionId, status, completionReport) {
|
|
285
|
+
return withSessionLock(sessionId, () => {
|
|
286
|
+
const session = getSession(cwd, sessionId);
|
|
287
|
+
session.status = status;
|
|
288
|
+
if (completionReport !== void 0) {
|
|
289
|
+
session.completionReport = completionReport;
|
|
290
|
+
}
|
|
291
|
+
saveSession(session);
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
async function appendAgentToLastCycle(cwd, sessionId, agentId) {
|
|
295
|
+
return withSessionLock(sessionId, () => {
|
|
296
|
+
const session = getSession(cwd, sessionId);
|
|
297
|
+
const cycles = session.orchestratorCycles;
|
|
298
|
+
if (cycles.length === 0) return;
|
|
299
|
+
cycles[cycles.length - 1].agentsSpawned.push(agentId);
|
|
300
|
+
saveSession(session);
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
async function completeSession(cwd, sessionId, report) {
|
|
304
|
+
return withSessionLock(sessionId, () => {
|
|
305
|
+
const session = getSession(cwd, sessionId);
|
|
306
|
+
session.status = "completed";
|
|
307
|
+
session.completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
308
|
+
session.completionReport = report;
|
|
309
|
+
saveSession(session);
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
async function continueSession(cwd, sessionId) {
|
|
313
|
+
return withSessionLock(sessionId, () => {
|
|
314
|
+
const session = getSession(cwd, sessionId);
|
|
315
|
+
if (session.status !== "completed") {
|
|
316
|
+
throw new Error(`Session ${sessionId} is not completed (status: ${session.status})`);
|
|
317
|
+
}
|
|
318
|
+
session.status = "active";
|
|
319
|
+
session.completedAt = void 0;
|
|
320
|
+
session.completionReport = void 0;
|
|
321
|
+
const cycles = session.orchestratorCycles;
|
|
322
|
+
if (cycles.length > 0) {
|
|
323
|
+
cycles[cycles.length - 1].completedAt = void 0;
|
|
324
|
+
}
|
|
325
|
+
saveSession(session);
|
|
326
|
+
writeFileSync3(roadmapPath(cwd, sessionId), "", "utf-8");
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
async function appendAgentReport(cwd, sessionId, agentId, entry) {
|
|
330
|
+
return withSessionLock(sessionId, () => {
|
|
331
|
+
const session = getSession(cwd, sessionId);
|
|
332
|
+
const agent = session.agents.slice().reverse().find((a) => a.id === agentId);
|
|
333
|
+
if (!agent) throw new Error(`Agent ${agentId} not found in session ${sessionId}`);
|
|
334
|
+
agent.reports.push(entry);
|
|
335
|
+
saveSession(session);
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
async function updateReportSummary(cwd, sessionId, agentId, filePath, summary) {
|
|
339
|
+
return withSessionLock(sessionId, () => {
|
|
340
|
+
const session = getSession(cwd, sessionId);
|
|
341
|
+
const agent = session.agents.slice().reverse().find((a) => a.id === agentId);
|
|
342
|
+
if (!agent) return;
|
|
343
|
+
const report = agent.reports.find((r) => r.filePath === filePath);
|
|
344
|
+
if (report) {
|
|
345
|
+
report.summary = summary;
|
|
346
|
+
saveSession(session);
|
|
347
|
+
}
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
async function updateSessionName(cwd, sessionId, name) {
|
|
351
|
+
return withSessionLock(sessionId, () => {
|
|
352
|
+
const session = getSession(cwd, sessionId);
|
|
353
|
+
session.name = name;
|
|
354
|
+
saveSession(session);
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
async function updateSessionTmux(cwd, sessionId, tmuxSessionName, tmuxWindowId, tmuxSessionId) {
|
|
358
|
+
return withSessionLock(sessionId, () => {
|
|
359
|
+
const session = getSession(cwd, sessionId);
|
|
360
|
+
session.tmuxSessionName = tmuxSessionName;
|
|
361
|
+
session.tmuxSessionId = tmuxSessionId;
|
|
362
|
+
session.tmuxWindowId = tmuxWindowId;
|
|
363
|
+
saveSession(session);
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
async function updateSession(cwd, sessionId, updates) {
|
|
367
|
+
return withSessionLock(sessionId, () => {
|
|
368
|
+
const session = getSession(cwd, sessionId);
|
|
369
|
+
Object.assign(session, updates);
|
|
370
|
+
saveSession(session);
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
async function drainMessages(cwd, sessionId, count) {
|
|
374
|
+
return withSessionLock(sessionId, () => {
|
|
375
|
+
const session = getSession(cwd, sessionId);
|
|
376
|
+
if (!session.messages || count <= 0) return;
|
|
377
|
+
session.messages = session.messages.slice(count);
|
|
378
|
+
saveSession(session);
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
async function appendMessage(cwd, sessionId, message) {
|
|
382
|
+
return withSessionLock(sessionId, () => {
|
|
383
|
+
const session = getSession(cwd, sessionId);
|
|
384
|
+
if (!session.messages) session.messages = [];
|
|
385
|
+
session.messages.push(message);
|
|
386
|
+
saveSession(session);
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
async function updateTask(cwd, sessionId, task) {
|
|
390
|
+
return withSessionLock(sessionId, () => {
|
|
391
|
+
const session = getSession(cwd, sessionId);
|
|
392
|
+
session.task = task;
|
|
393
|
+
saveSession(session);
|
|
394
|
+
writeFileSync3(goalPath(cwd, sessionId), task, "utf-8");
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
async function completeOrchestratorCycle(cwd, sessionId, nextPrompt, mode, activeMs) {
|
|
398
|
+
return withSessionLock(sessionId, () => {
|
|
399
|
+
const session = getSession(cwd, sessionId);
|
|
400
|
+
const cycles = session.orchestratorCycles;
|
|
401
|
+
if (cycles.length === 0) return;
|
|
402
|
+
const cycle = cycles[cycles.length - 1];
|
|
403
|
+
if (cycle.completedAt) return;
|
|
404
|
+
cycle.completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
405
|
+
if (nextPrompt) cycle.nextPrompt = nextPrompt;
|
|
406
|
+
if (mode) cycle.mode = mode;
|
|
407
|
+
if (activeMs != null) cycle.activeMs += activeMs;
|
|
408
|
+
saveSession(session);
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
async function incrementUserBlockedMs(cwd, sessionId, deltaMs, askedAt, askedBy) {
|
|
412
|
+
if (deltaMs <= 0) return;
|
|
413
|
+
return withSessionLock(sessionId, () => {
|
|
414
|
+
const session = getSession(cwd, sessionId);
|
|
415
|
+
session.userBlockedMs = (session.userBlockedMs ?? 0) + deltaMs;
|
|
416
|
+
if (askedAt) {
|
|
417
|
+
const askedAtMs = new Date(askedAt).getTime();
|
|
418
|
+
const cycle = session.orchestratorCycles.find((c) => {
|
|
419
|
+
const startMs = new Date(c.timestamp).getTime();
|
|
420
|
+
const endMs = c.completedAt ? new Date(c.completedAt).getTime() : Infinity;
|
|
421
|
+
return startMs <= askedAtMs && askedAtMs < endMs;
|
|
422
|
+
});
|
|
423
|
+
if (cycle) cycle.userBlockedMs = (cycle.userBlockedMs ?? 0) + deltaMs;
|
|
424
|
+
}
|
|
425
|
+
if (askedBy && askedBy !== ORCHESTRATOR_ASKED_BY) {
|
|
426
|
+
const agent = session.agents.slice().reverse().find((a) => a.id === askedBy);
|
|
427
|
+
if (agent) agent.userBlockedMs = (agent.userBlockedMs ?? 0) + deltaMs;
|
|
428
|
+
}
|
|
429
|
+
saveSession(session);
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
async function incrementActiveTime(cwd, sessionId, sessionDelta, agentDeltas, cycleDeltas) {
|
|
433
|
+
return withSessionLock(sessionId, () => {
|
|
434
|
+
const session = getSession(cwd, sessionId);
|
|
435
|
+
session.activeMs += sessionDelta;
|
|
436
|
+
for (const [agentId, delta] of agentDeltas) {
|
|
437
|
+
const agent = session.agents.slice().reverse().find((a) => a.id === agentId);
|
|
438
|
+
if (agent) agent.activeMs += delta;
|
|
439
|
+
}
|
|
440
|
+
for (const [cycleNum, delta] of cycleDeltas) {
|
|
441
|
+
const cycle = session.orchestratorCycles.find((c) => c.cycle === cycleNum);
|
|
442
|
+
if (cycle) cycle.activeMs += delta;
|
|
443
|
+
}
|
|
444
|
+
saveSession(session);
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
function createSnapshot(cwd, sessionId, cycleNumber) {
|
|
448
|
+
const dir = snapshotDir(cwd, sessionId, cycleNumber);
|
|
449
|
+
mkdirSync(dir, { recursive: true });
|
|
450
|
+
copyFileSync(statePath(cwd, sessionId), join3(dir, "state.json"));
|
|
451
|
+
const roadmap = roadmapPath(cwd, sessionId);
|
|
452
|
+
if (existsSync2(roadmap)) copyFileSync(roadmap, join3(dir, "roadmap.md"));
|
|
453
|
+
const strategy = strategyPath(cwd, sessionId);
|
|
454
|
+
if (existsSync2(strategy)) copyFileSync(strategy, join3(dir, "strategy.md"));
|
|
455
|
+
const ld = logsDir(cwd, sessionId);
|
|
456
|
+
if (existsSync2(ld)) cpSync(ld, join3(dir, "logs"), { recursive: true });
|
|
457
|
+
const legacyLogs = legacyLogsPath(cwd, sessionId);
|
|
458
|
+
if (existsSync2(legacyLogs)) copyFileSync(legacyLogs, join3(dir, "logs.md"));
|
|
459
|
+
}
|
|
460
|
+
async function restoreSnapshot(cwd, sessionId, toCycle) {
|
|
461
|
+
return withSessionLock(sessionId, () => {
|
|
462
|
+
const dir = snapshotDir(cwd, sessionId, toCycle);
|
|
463
|
+
if (!existsSync2(dir)) throw new Error(`No snapshot found for cycle ${toCycle}`);
|
|
464
|
+
const snapshotState = readFileSync3(join3(dir, "state.json"), "utf-8");
|
|
465
|
+
const session = JSON.parse(snapshotState);
|
|
466
|
+
session.status = "paused";
|
|
467
|
+
session.completedAt = void 0;
|
|
468
|
+
session.completionReport = void 0;
|
|
469
|
+
session.tmuxSessionName = void 0;
|
|
470
|
+
session.tmuxSessionId = void 0;
|
|
471
|
+
session.tmuxWindowId = void 0;
|
|
472
|
+
atomicWrite(statePath(cwd, sessionId), JSON.stringify(session, null, 2));
|
|
473
|
+
const snapshotRoadmap = join3(dir, "roadmap.md");
|
|
474
|
+
if (existsSync2(snapshotRoadmap)) copyFileSync(snapshotRoadmap, roadmapPath(cwd, sessionId));
|
|
475
|
+
const snapshotStrategy = join3(dir, "strategy.md");
|
|
476
|
+
if (existsSync2(snapshotStrategy)) copyFileSync(snapshotStrategy, strategyPath(cwd, sessionId));
|
|
477
|
+
const snapshotLogsDir = join3(dir, "logs");
|
|
478
|
+
if (existsSync2(snapshotLogsDir)) {
|
|
479
|
+
const currentLogsDir = logsDir(cwd, sessionId);
|
|
480
|
+
if (existsSync2(currentLogsDir)) rmSync(currentLogsDir, { recursive: true, force: true });
|
|
481
|
+
cpSync(snapshotLogsDir, currentLogsDir, { recursive: true });
|
|
482
|
+
} else {
|
|
483
|
+
const snapshotLogs = join3(dir, "logs.md");
|
|
484
|
+
if (existsSync2(snapshotLogs)) copyFileSync(snapshotLogs, legacyLogsPath(cwd, sessionId));
|
|
485
|
+
}
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
function listSnapshots(cwd, sessionId) {
|
|
489
|
+
const dir = snapshotsDir(cwd, sessionId);
|
|
490
|
+
if (!existsSync2(dir)) return [];
|
|
491
|
+
return readdirSync(dir, { withFileTypes: true }).filter((e) => e.isDirectory() && e.name.startsWith("cycle-")).map((e) => parseInt(e.name.replace("cycle-", ""), 10)).filter((n) => !isNaN(n)).sort((a, b) => a - b);
|
|
492
|
+
}
|
|
493
|
+
function deleteSnapshotsAfter(cwd, sessionId, afterCycle) {
|
|
494
|
+
const dir = snapshotsDir(cwd, sessionId);
|
|
495
|
+
if (!existsSync2(dir)) return;
|
|
496
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
497
|
+
if (!entry.isDirectory() || !entry.name.startsWith("cycle-")) continue;
|
|
498
|
+
const num = parseInt(entry.name.replace("cycle-", ""), 10);
|
|
499
|
+
if (!isNaN(num) && num > afterCycle) {
|
|
500
|
+
rmSync(join3(dir, entry.name), { recursive: true, force: true });
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
function replaceIdInDir(dir, sourceId, cloneId) {
|
|
505
|
+
if (!existsSync2(dir)) return;
|
|
506
|
+
const entries = readdirSync(dir, { recursive: true });
|
|
507
|
+
for (const rel of entries) {
|
|
508
|
+
const fullPath = join3(dir, rel);
|
|
509
|
+
if (!statSync(fullPath).isFile()) continue;
|
|
510
|
+
const buf = readFileSync3(fullPath);
|
|
511
|
+
const sample = buf.subarray(0, 8192);
|
|
512
|
+
if (sample.includes(0)) continue;
|
|
513
|
+
const text = buf.toString("utf-8");
|
|
514
|
+
if (text.includes(sourceId)) {
|
|
515
|
+
writeFileSync3(fullPath, text.replaceAll(sourceId, cloneId), "utf-8");
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
function cloneSessionDir(sourceCwd, sourceId, cloneId, goal, context, strategy) {
|
|
520
|
+
const srcDir = sessionDir(sourceCwd, sourceId);
|
|
521
|
+
const dstDir = sessionDir(sourceCwd, cloneId);
|
|
522
|
+
mkdirSync(dstDir, { recursive: true });
|
|
523
|
+
const dirsToCopy = ["context", "prompts", "reports", "snapshots"];
|
|
524
|
+
for (const sub of dirsToCopy) {
|
|
525
|
+
const src = join3(srcDir, sub);
|
|
526
|
+
const dst = join3(dstDir, sub);
|
|
527
|
+
if (existsSync2(src)) {
|
|
528
|
+
cpSync(src, dst, { recursive: true });
|
|
529
|
+
} else {
|
|
530
|
+
mkdirSync(dst, { recursive: true });
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
if (strategy) {
|
|
534
|
+
const srcStrategy = strategyPath(sourceCwd, sourceId);
|
|
535
|
+
if (existsSync2(srcStrategy)) {
|
|
536
|
+
const text = readFileSync3(srcStrategy, "utf-8");
|
|
537
|
+
writeFileSync3(strategyPath(sourceCwd, cloneId), text.replaceAll(sourceId, cloneId), "utf-8");
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
for (const sub of dirsToCopy) {
|
|
541
|
+
replaceIdInDir(join3(dstDir, sub), sourceId, cloneId);
|
|
542
|
+
}
|
|
543
|
+
writeFileSync3(goalPath(sourceCwd, cloneId), goal, "utf-8");
|
|
544
|
+
writeFileSync3(initialPromptPath(sourceCwd, cloneId), goal, "utf-8");
|
|
545
|
+
writeFileSync3(roadmapPath(sourceCwd, cloneId), ROADMAP_SEED, "utf-8");
|
|
546
|
+
mkdirSync(logsDir(sourceCwd, cloneId), { recursive: true });
|
|
547
|
+
writeFileSync3(join3(contextDir(sourceCwd, cloneId), "CLAUDE.md"), CONTEXT_CLAUDE_MD, "utf-8");
|
|
548
|
+
if (context) {
|
|
549
|
+
writeFileSync3(join3(contextDir(sourceCwd, cloneId), "initial-context.md"), context, "utf-8");
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
async function createCloneState(sourceCwd, sourceId, cloneId, goal, context, configModel, configOrchestratorPrompt) {
|
|
553
|
+
return withSessionLock(cloneId, () => {
|
|
554
|
+
const source = getSession(sourceCwd, sourceId);
|
|
555
|
+
const createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
556
|
+
const created = new Date(createdAt);
|
|
557
|
+
const agents = structuredClone(source.agents);
|
|
558
|
+
const orchestratorCycles = structuredClone(source.orchestratorCycles);
|
|
559
|
+
const messages = structuredClone(source.messages);
|
|
560
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
561
|
+
for (const agent of agents) {
|
|
562
|
+
if (agent.status === "running") {
|
|
563
|
+
agent.status = "killed";
|
|
564
|
+
agent.completedAt = now;
|
|
565
|
+
agent.killedReason = "inherited from source session";
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
const model = source.model ?? configModel;
|
|
569
|
+
const launchConfig = source.launchConfig ? structuredClone(source.launchConfig) : {
|
|
570
|
+
model,
|
|
571
|
+
context,
|
|
572
|
+
orchestratorPrompt: configOrchestratorPrompt
|
|
573
|
+
};
|
|
574
|
+
const clone = {
|
|
575
|
+
id: cloneId,
|
|
576
|
+
task: goal,
|
|
577
|
+
...context ? { context } : {},
|
|
578
|
+
cwd: sourceCwd,
|
|
579
|
+
status: "active",
|
|
580
|
+
createdAt,
|
|
581
|
+
activeMs: 0,
|
|
582
|
+
agents,
|
|
583
|
+
orchestratorCycles,
|
|
584
|
+
messages,
|
|
585
|
+
startHour: created.getHours(),
|
|
586
|
+
startDayOfWeek: created.getDay(),
|
|
587
|
+
parentSessionId: sourceId,
|
|
588
|
+
...model ? { model } : {},
|
|
589
|
+
launchConfig,
|
|
590
|
+
...source.effort != null ? { effort: source.effort } : {}
|
|
591
|
+
};
|
|
592
|
+
atomicWrite(statePath(sourceCwd, cloneId), JSON.stringify(clone, null, 2));
|
|
593
|
+
return clone;
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
// src/daemon/ask-store.ts
|
|
598
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync4, readFileSync as readFileSync5, readdirSync as readdirSync3 } from "fs";
|
|
599
|
+
|
|
600
|
+
// src/daemon/history.ts
|
|
601
|
+
import { appendFileSync, mkdirSync as mkdirSync2, writeFileSync as writeFileSync4, renameSync as renameSync2, readdirSync as readdirSync2, readFileSync as readFileSync4, rmSync as rmSync2, statSync as statSync2 } from "fs";
|
|
602
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
603
|
+
import { dirname as dirname2, join as join4 } from "path";
|
|
604
|
+
var knownDirs = /* @__PURE__ */ new Set();
|
|
605
|
+
function ensureDir(sessionId) {
|
|
606
|
+
if (knownDirs.has(sessionId)) return;
|
|
607
|
+
mkdirSync2(historySessionDir(sessionId), { recursive: true });
|
|
608
|
+
knownDirs.add(sessionId);
|
|
609
|
+
}
|
|
610
|
+
function emitHistoryEvent(sessionId, event, data) {
|
|
611
|
+
try {
|
|
612
|
+
ensureDir(sessionId);
|
|
613
|
+
const line = JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), event, sessionId, data }) + "\n";
|
|
614
|
+
appendFileSync(historyEventsPath(sessionId), line, "utf-8");
|
|
615
|
+
} catch {
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
function writeSessionSummary(session, extra) {
|
|
619
|
+
try {
|
|
620
|
+
ensureDir(session.id);
|
|
621
|
+
const summary = {
|
|
622
|
+
sessionId: session.id,
|
|
623
|
+
name: session.name ?? null,
|
|
624
|
+
task: session.task,
|
|
625
|
+
cwd: session.cwd,
|
|
626
|
+
model: session.model ?? null,
|
|
627
|
+
status: session.status,
|
|
628
|
+
startedAt: session.createdAt,
|
|
629
|
+
completedAt: session.completedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
630
|
+
activeMs: session.activeMs,
|
|
631
|
+
wallClockMs: session.wallClockMs ?? null,
|
|
632
|
+
userBlockedMs: session.userBlockedMs ?? 0,
|
|
633
|
+
agentCount: session.agents.length,
|
|
634
|
+
crashCount: session.agents.filter((a) => a.status === "crashed").length,
|
|
635
|
+
lostCount: session.agents.filter((a) => a.status === "lost").length,
|
|
636
|
+
killedAgentCount: session.agents.filter((a) => a.status === "killed").length,
|
|
637
|
+
rollbackCount: session.rollbackCount ?? 0,
|
|
638
|
+
efficiency: session.wallClockMs ? Math.max(0, session.activeMs - (session.userBlockedMs ?? 0)) / Math.max(1, session.wallClockMs - (session.userBlockedMs ?? 0)) : null,
|
|
639
|
+
cycleCount: session.orchestratorCycles.length,
|
|
640
|
+
context: session.context ?? null,
|
|
641
|
+
completionReport: session.completionReport ?? null,
|
|
642
|
+
agents: session.agents.map((a) => ({
|
|
643
|
+
id: a.id,
|
|
644
|
+
name: a.name,
|
|
645
|
+
nickname: a.nickname ?? null,
|
|
646
|
+
agentType: a.agentType,
|
|
647
|
+
status: a.status,
|
|
648
|
+
activeMs: a.activeMs,
|
|
649
|
+
userBlockedMs: a.userBlockedMs ?? 0,
|
|
650
|
+
spawnedAt: a.spawnedAt,
|
|
651
|
+
completedAt: a.completedAt,
|
|
652
|
+
restartCount: a.restartCount ?? 0
|
|
653
|
+
})),
|
|
654
|
+
cycles: session.orchestratorCycles.map((c) => ({
|
|
655
|
+
cycle: c.cycle,
|
|
656
|
+
mode: c.mode ?? null,
|
|
657
|
+
agentsSpawned: c.agentsSpawned.length,
|
|
658
|
+
activeMs: c.activeMs,
|
|
659
|
+
userBlockedMs: c.userBlockedMs ?? 0,
|
|
660
|
+
startedAt: c.timestamp,
|
|
661
|
+
completedAt: c.completedAt ?? null
|
|
662
|
+
})),
|
|
663
|
+
messages: session.messages.map((m) => ({
|
|
664
|
+
id: m.id,
|
|
665
|
+
source: typeof m.source === "string" ? m.source : m.source.type,
|
|
666
|
+
content: m.content,
|
|
667
|
+
timestamp: m.timestamp
|
|
668
|
+
})),
|
|
669
|
+
finalMoodSignals: extra?.finalSignals ?? null,
|
|
670
|
+
achievements: extra?.achievements ?? [],
|
|
671
|
+
xpGained: extra?.xpGained ?? 0,
|
|
672
|
+
sentiment: extra?.sentiment ?? null
|
|
673
|
+
};
|
|
674
|
+
const filePath = historySessionSummaryPath(session.id);
|
|
675
|
+
const tmp = join4(dirname2(filePath), `.session-${randomUUID2()}.tmp`);
|
|
676
|
+
writeFileSync4(tmp, JSON.stringify(summary, null, 2), "utf-8");
|
|
677
|
+
renameSync2(tmp, filePath);
|
|
678
|
+
} catch (err) {
|
|
679
|
+
console.error(`[history] Failed to write session summary for ${session.id}:`, err);
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
function getRecentSentiments(count = 5, scanLimit = 30, overrideBaseDir) {
|
|
683
|
+
try {
|
|
684
|
+
const base = overrideBaseDir ?? historyBaseDir();
|
|
685
|
+
let entries;
|
|
686
|
+
try {
|
|
687
|
+
entries = readdirSync2(base);
|
|
688
|
+
} catch {
|
|
689
|
+
return [];
|
|
690
|
+
}
|
|
691
|
+
const withMtime = [];
|
|
692
|
+
for (const name of entries) {
|
|
693
|
+
try {
|
|
694
|
+
const st = statSync2(join4(base, name));
|
|
695
|
+
if (st.isDirectory()) withMtime.push({ name, mtime: st.mtimeMs });
|
|
696
|
+
} catch {
|
|
697
|
+
continue;
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
withMtime.sort((a, b) => b.mtime - a.mtime);
|
|
701
|
+
const results = [];
|
|
702
|
+
const limit = Math.min(withMtime.length, scanLimit);
|
|
703
|
+
for (let i = 0; i < limit && results.length < count; i++) {
|
|
704
|
+
try {
|
|
705
|
+
const raw = readFileSync4(join4(base, withMtime[i].name, "session.json"), "utf-8");
|
|
706
|
+
const summary = JSON.parse(raw);
|
|
707
|
+
if (summary.sentiment && summary.completedAt) {
|
|
708
|
+
results.push({
|
|
709
|
+
sentiment: summary.sentiment,
|
|
710
|
+
task: summary.task.slice(0, 100),
|
|
711
|
+
completedAt: summary.completedAt
|
|
712
|
+
});
|
|
713
|
+
}
|
|
714
|
+
} catch {
|
|
715
|
+
continue;
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
return results;
|
|
719
|
+
} catch {
|
|
720
|
+
return [];
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
var PRUNE_KEEP_COUNT = 200;
|
|
724
|
+
var PRUNE_KEEP_DAYS = 90;
|
|
725
|
+
function pruneHistory() {
|
|
726
|
+
try {
|
|
727
|
+
const base = historyBaseDir();
|
|
728
|
+
let entries;
|
|
729
|
+
try {
|
|
730
|
+
entries = readdirSync2(base);
|
|
731
|
+
} catch {
|
|
732
|
+
return;
|
|
733
|
+
}
|
|
734
|
+
const sessions = [];
|
|
735
|
+
for (const name of entries) {
|
|
736
|
+
const dir = join4(base, name);
|
|
737
|
+
try {
|
|
738
|
+
const summaryPath = join4(dir, "session.json");
|
|
739
|
+
const raw = readFileSync4(summaryPath, "utf-8");
|
|
740
|
+
const summary = JSON.parse(raw);
|
|
741
|
+
sessions.push({ dir, startedAt: new Date(summary.startedAt ?? 0).getTime() });
|
|
742
|
+
} catch {
|
|
743
|
+
try {
|
|
744
|
+
const eventsPath = join4(dir, "events.jsonl");
|
|
745
|
+
const firstLine = readFileSync4(eventsPath, "utf-8").split("\n")[0];
|
|
746
|
+
const firstEvent = JSON.parse(firstLine);
|
|
747
|
+
sessions.push({ dir, startedAt: new Date(firstEvent.ts ?? 0).getTime() });
|
|
748
|
+
} catch {
|
|
749
|
+
try {
|
|
750
|
+
const st = statSync2(dir);
|
|
751
|
+
sessions.push({ dir, startedAt: st.mtimeMs });
|
|
752
|
+
} catch {
|
|
753
|
+
continue;
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
if (sessions.length <= PRUNE_KEEP_COUNT) return;
|
|
759
|
+
sessions.sort((a, b) => b.startedAt - a.startedAt);
|
|
760
|
+
const cutoff = Date.now() - PRUNE_KEEP_DAYS * 24 * 60 * 60 * 1e3;
|
|
761
|
+
for (let i = PRUNE_KEEP_COUNT; i < sessions.length; i++) {
|
|
762
|
+
if (sessions[i].startedAt < cutoff) {
|
|
763
|
+
rmSync2(sessions[i].dir, { recursive: true, force: true });
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
} catch {
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
// src/daemon/notify.ts
|
|
771
|
+
import { spawn, execFile } from "child_process";
|
|
772
|
+
import { writeFileSync as writeFileSync5, mkdirSync as mkdirSync3, existsSync as existsSync3 } from "fs";
|
|
773
|
+
import { join as join5 } from "path";
|
|
774
|
+
import { homedir } from "os";
|
|
775
|
+
|
|
776
|
+
// src/shared/shell.ts
|
|
777
|
+
function shellQuote(s) {
|
|
778
|
+
return `'${s.replace(/'/g, "'\\''")}'`;
|
|
779
|
+
}
|
|
780
|
+
var SESSION_ID_PATTERN = /^[a-zA-Z0-9_-]+$/;
|
|
781
|
+
function validateSessionId(id) {
|
|
782
|
+
return SESSION_ID_PATTERN.test(id) && !id.includes("..");
|
|
783
|
+
}
|
|
784
|
+
function validateRepoName(repo) {
|
|
785
|
+
return !repo.includes("/") && !repo.includes("\\") && !repo.includes("..");
|
|
786
|
+
}
|
|
787
|
+
function escapeAppleScript(s) {
|
|
788
|
+
return s.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
// src/daemon/notify.ts
|
|
792
|
+
var TMUX_SOCKET = `/tmp/tmux-${process.getuid?.() ?? 0}/default`;
|
|
793
|
+
var SWITCH_SCRIPT = [
|
|
794
|
+
"#!/bin/bash",
|
|
795
|
+
'SESSION="$1"',
|
|
796
|
+
`TMUX_SOCKET="${TMUX_SOCKET}"`,
|
|
797
|
+
"TMUX=/opt/homebrew/bin/tmux",
|
|
798
|
+
"",
|
|
799
|
+
"# Find any attached client (user is likely on a different session)",
|
|
800
|
+
`CLIENT_TTY=$("$TMUX" -S "$TMUX_SOCKET" list-clients -F '#{client_tty}' 2>/dev/null | head -1)`,
|
|
801
|
+
'[ -z "$CLIENT_TTY" ] && exit 0',
|
|
802
|
+
"",
|
|
803
|
+
"# Switch that client to the target session",
|
|
804
|
+
'"$TMUX" -S "$TMUX_SOCKET" switch-client -c "$CLIENT_TTY" -t "$SESSION" 2>/dev/null',
|
|
805
|
+
'"$TMUX" -S "$TMUX_SOCKET" select-window -t "$SESSION" 2>/dev/null',
|
|
806
|
+
"",
|
|
807
|
+
"# Bring iTerm2 to front and select the tab with this client",
|
|
808
|
+
`TTY_SHORT=$(echo "$CLIENT_TTY" | sed 's|/dev/||')`,
|
|
809
|
+
'osascript -e "',
|
|
810
|
+
' tell application \\"iTerm2\\"',
|
|
811
|
+
" activate",
|
|
812
|
+
" repeat with w in windows",
|
|
813
|
+
" tell w",
|
|
814
|
+
" repeat with t in tabs",
|
|
815
|
+
" tell t",
|
|
816
|
+
" repeat with s in sessions",
|
|
817
|
+
" tell s",
|
|
818
|
+
' if tty contains \\"$TTY_SHORT\\" then',
|
|
819
|
+
" select t",
|
|
820
|
+
" return",
|
|
821
|
+
" end if",
|
|
822
|
+
" end tell",
|
|
823
|
+
" end repeat",
|
|
824
|
+
" end tell",
|
|
825
|
+
" end repeat",
|
|
826
|
+
" end tell",
|
|
827
|
+
" end repeat",
|
|
828
|
+
" end tell",
|
|
829
|
+
`" 2>/dev/null || osascript -e 'tell application "iTerm2" to activate' 2>/dev/null`,
|
|
830
|
+
""
|
|
831
|
+
].join("\n");
|
|
832
|
+
function ensureSwitchScript() {
|
|
833
|
+
const dir = join5(homedir(), ".sisyphus");
|
|
834
|
+
const scriptPath = join5(dir, "notify-switch.sh");
|
|
835
|
+
try {
|
|
836
|
+
mkdirSync3(dir, { recursive: true });
|
|
837
|
+
writeFileSync5(scriptPath, SWITCH_SCRIPT, { mode: 493 });
|
|
838
|
+
} catch {
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
var notifyProcess = null;
|
|
842
|
+
function getNotifyBinary() {
|
|
843
|
+
return join5(homedir(), ".sisyphus", "SisyphusNotify.app", "Contents", "MacOS", "sisyphus-notify");
|
|
844
|
+
}
|
|
845
|
+
function ensureNotifyProcess() {
|
|
846
|
+
if (notifyProcess && !notifyProcess.killed && notifyProcess.stdin?.writable) {
|
|
847
|
+
return notifyProcess;
|
|
848
|
+
}
|
|
849
|
+
const binary = getNotifyBinary();
|
|
850
|
+
if (!existsSync3(binary)) {
|
|
851
|
+
return null;
|
|
852
|
+
}
|
|
853
|
+
notifyProcess = spawn(binary, [], {
|
|
854
|
+
stdio: ["pipe", "ignore", "pipe"]
|
|
855
|
+
});
|
|
856
|
+
notifyProcess.stderr?.on("data", (data) => {
|
|
857
|
+
const msg = data.toString().trim();
|
|
858
|
+
if (msg) console.error(`[sisyphus-notify] ${msg}`);
|
|
859
|
+
});
|
|
860
|
+
notifyProcess.on("close", () => {
|
|
861
|
+
notifyProcess = null;
|
|
862
|
+
});
|
|
863
|
+
notifyProcess.unref();
|
|
864
|
+
notifyProcess.stdin?.unref();
|
|
865
|
+
notifyProcess.stderr?.unref();
|
|
866
|
+
return notifyProcess;
|
|
867
|
+
}
|
|
868
|
+
function sendTerminalNotification(titleOrOpts, message, tmuxSession, level) {
|
|
869
|
+
let title;
|
|
870
|
+
let msg;
|
|
871
|
+
let tmuxSess;
|
|
872
|
+
let lvl;
|
|
873
|
+
if (typeof titleOrOpts === "object") {
|
|
874
|
+
title = titleOrOpts.title;
|
|
875
|
+
msg = titleOrOpts.message;
|
|
876
|
+
tmuxSess = titleOrOpts.tmuxSession;
|
|
877
|
+
lvl = titleOrOpts.level ?? "urgent";
|
|
878
|
+
} else {
|
|
879
|
+
title = titleOrOpts;
|
|
880
|
+
msg = message;
|
|
881
|
+
tmuxSess = tmuxSession;
|
|
882
|
+
lvl = level ?? "urgent";
|
|
883
|
+
}
|
|
884
|
+
if (tmuxSess) ensureSwitchScript();
|
|
885
|
+
const proc = ensureNotifyProcess();
|
|
886
|
+
if (proc?.stdin?.writable) {
|
|
887
|
+
const payload = { title, message: msg, level: lvl };
|
|
888
|
+
if (tmuxSess) payload.tmuxSession = tmuxSess;
|
|
889
|
+
proc.stdin.write(JSON.stringify(payload) + "\n");
|
|
890
|
+
return;
|
|
891
|
+
}
|
|
892
|
+
const tnArgs = ["-title", title, "-message", msg];
|
|
893
|
+
if (lvl === "urgent") tnArgs.push("-sound", "default");
|
|
894
|
+
execFile("terminal-notifier", tnArgs, (err) => {
|
|
895
|
+
if (err) {
|
|
896
|
+
const soundClause = lvl === "urgent" ? ' sound name "default"' : "";
|
|
897
|
+
execFile("osascript", [
|
|
898
|
+
"-e",
|
|
899
|
+
`display notification "${escapeAppleScript(msg)}" with title "${escapeAppleScript(title)}"${soundClause}`
|
|
900
|
+
], () => {
|
|
901
|
+
});
|
|
902
|
+
}
|
|
903
|
+
});
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
// src/daemon/ask-store.ts
|
|
907
|
+
var ACTIONABLE_KINDS = /* @__PURE__ */ new Set([
|
|
908
|
+
"validation",
|
|
909
|
+
"decision",
|
|
910
|
+
"context",
|
|
911
|
+
"error"
|
|
912
|
+
]);
|
|
913
|
+
var HEARTBEAT_ASKED_BY = "system:heartbeat";
|
|
914
|
+
function maybeNotifyOnAskCreated(cwd, sessionId, meta) {
|
|
915
|
+
const isActionable = meta.kind !== void 0 && ACTIONABLE_KINDS.has(meta.kind);
|
|
916
|
+
const isHeartbeat = meta.askedBy === HEARTBEAT_ASKED_BY;
|
|
917
|
+
if (!isActionable && !isHeartbeat) return;
|
|
918
|
+
try {
|
|
919
|
+
const config = loadConfig(cwd);
|
|
920
|
+
if (config.notifications?.enabled === false) return;
|
|
921
|
+
const session = getSession(cwd, sessionId);
|
|
922
|
+
const label = session.name ?? sessionId.slice(0, 8);
|
|
923
|
+
const body = meta.title ?? "Question pending";
|
|
924
|
+
sendTerminalNotification(label, body, session.tmuxSessionName, "urgent");
|
|
925
|
+
} catch {
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
function createAsk(cwd, sessionId, params) {
|
|
929
|
+
mkdirSync4(askVisualsDir(cwd, sessionId, params.askId), { recursive: true });
|
|
930
|
+
const askedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
931
|
+
const meta = {
|
|
932
|
+
askId: params.askId,
|
|
933
|
+
askedBy: params.askedBy,
|
|
934
|
+
askedAt,
|
|
935
|
+
status: "pending",
|
|
936
|
+
blocking: params.blocking,
|
|
937
|
+
cwd: params.cwd,
|
|
938
|
+
...params.pid !== void 0 ? { pid: params.pid, startedAt: askedAt } : {},
|
|
939
|
+
...params.claudeSessionId !== void 0 ? { claudeSessionId: params.claudeSessionId } : {},
|
|
940
|
+
...params.title !== void 0 ? { title: params.title } : {},
|
|
941
|
+
...params.subtitle !== void 0 ? { subtitle: params.subtitle } : {},
|
|
942
|
+
...params.kind !== void 0 ? { kind: params.kind } : {},
|
|
943
|
+
...params.orphanTarget !== void 0 ? { orphanTarget: params.orphanTarget } : {},
|
|
944
|
+
...params.modeTransition !== void 0 ? { modeTransition: params.modeTransition } : {}
|
|
945
|
+
};
|
|
946
|
+
atomicWrite(askMetaPath(cwd, sessionId, params.askId), JSON.stringify(meta, null, 2));
|
|
947
|
+
emitHistoryEvent(sessionId, "ask-issued", {
|
|
948
|
+
askId: params.askId,
|
|
949
|
+
askedBy: params.askedBy,
|
|
950
|
+
blocking: params.blocking,
|
|
951
|
+
askedAt
|
|
952
|
+
});
|
|
953
|
+
maybeNotifyOnAskCreated(cwd, sessionId, meta);
|
|
954
|
+
return meta;
|
|
955
|
+
}
|
|
956
|
+
function writeDecisions(cwd, sessionId, askId, deck) {
|
|
957
|
+
atomicWrite(askDecisionsPath(cwd, sessionId, askId), JSON.stringify(deck, null, 2));
|
|
958
|
+
}
|
|
959
|
+
function readDecisions(cwd, sessionId, askId) {
|
|
960
|
+
const p = askDecisionsPath(cwd, sessionId, askId);
|
|
961
|
+
try {
|
|
962
|
+
return JSON.parse(readFileSync5(p, { encoding: "utf-8" }));
|
|
963
|
+
} catch (_e) {
|
|
964
|
+
return null;
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
function readProgress(cwd, sessionId, askId) {
|
|
968
|
+
const p = askProgressPath(cwd, sessionId, askId);
|
|
969
|
+
try {
|
|
970
|
+
const data = JSON.parse(readFileSync5(p, { encoding: "utf-8" }));
|
|
971
|
+
if (!Array.isArray(data["responses"])) return null;
|
|
972
|
+
return { responses: data["responses"], savedAt: data["savedAt"] };
|
|
973
|
+
} catch (_e) {
|
|
974
|
+
return null;
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
function writeOutput(cwd, sessionId, askId, responses, completedAt) {
|
|
978
|
+
atomicWrite(askOutputPath(cwd, sessionId, askId), JSON.stringify({
|
|
979
|
+
responses,
|
|
980
|
+
completedAt: completedAt ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
981
|
+
}, null, 2));
|
|
982
|
+
}
|
|
983
|
+
function readMeta(cwd, sessionId, askId) {
|
|
984
|
+
const p = askMetaPath(cwd, sessionId, askId);
|
|
985
|
+
if (!existsSync4(p)) {
|
|
986
|
+
return null;
|
|
987
|
+
}
|
|
988
|
+
return JSON.parse(readFileSync5(p, "utf-8"));
|
|
989
|
+
}
|
|
990
|
+
async function updateMeta(cwd, sessionId, askId, patch) {
|
|
991
|
+
return withLock(askId, () => {
|
|
992
|
+
const cur = readMeta(cwd, sessionId, askId);
|
|
993
|
+
if (!cur) {
|
|
994
|
+
throw new Error(`updateMeta: askId ${askId} not found`);
|
|
995
|
+
}
|
|
996
|
+
const next = { ...cur, ...patch };
|
|
997
|
+
atomicWrite(askMetaPath(cwd, sessionId, askId), JSON.stringify(next, null, 2));
|
|
998
|
+
return next;
|
|
999
|
+
});
|
|
1000
|
+
}
|
|
1001
|
+
function listAsks(cwd, sessionId) {
|
|
1002
|
+
const dir = askDir(cwd, sessionId);
|
|
1003
|
+
if (!existsSync4(dir)) {
|
|
1004
|
+
return [];
|
|
1005
|
+
}
|
|
1006
|
+
return readdirSync3(dir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
1007
|
+
}
|
|
1008
|
+
function listOpenAsksFor(cwd, sessionId, askedBy) {
|
|
1009
|
+
const out = [];
|
|
1010
|
+
for (const askId of listAsks(cwd, sessionId)) {
|
|
1011
|
+
const meta = readMeta(cwd, sessionId, askId);
|
|
1012
|
+
if (!meta) continue;
|
|
1013
|
+
if (meta.askedBy !== askedBy) continue;
|
|
1014
|
+
if (meta.orphaned) continue;
|
|
1015
|
+
if (!meta.blocking) continue;
|
|
1016
|
+
if (meta.status !== "pending" && meta.status !== "in-progress") continue;
|
|
1017
|
+
if (existsSync4(askOutputPath(cwd, sessionId, askId))) continue;
|
|
1018
|
+
out.push({ askId, status: meta.status, ...meta.title !== void 0 ? { title: meta.title } : {} });
|
|
1019
|
+
}
|
|
1020
|
+
return out;
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
export {
|
|
1024
|
+
loadConfig,
|
|
1025
|
+
shellQuote,
|
|
1026
|
+
validateSessionId,
|
|
1027
|
+
validateRepoName,
|
|
1028
|
+
atomicWrite,
|
|
1029
|
+
ORCHESTRATOR_ASKED_BY,
|
|
1030
|
+
createSession,
|
|
1031
|
+
getSession,
|
|
1032
|
+
addAgent,
|
|
1033
|
+
updateAgent,
|
|
1034
|
+
markAgentOrphan,
|
|
1035
|
+
markSessionOrphan,
|
|
1036
|
+
clearSessionOrphan,
|
|
1037
|
+
setAgentPid,
|
|
1038
|
+
setAgentConsumedInline,
|
|
1039
|
+
addOrchestratorCycle,
|
|
1040
|
+
updateSessionStatus,
|
|
1041
|
+
appendAgentToLastCycle,
|
|
1042
|
+
completeSession,
|
|
1043
|
+
continueSession,
|
|
1044
|
+
appendAgentReport,
|
|
1045
|
+
updateReportSummary,
|
|
1046
|
+
updateSessionName,
|
|
1047
|
+
updateSessionTmux,
|
|
1048
|
+
updateSession,
|
|
1049
|
+
drainMessages,
|
|
1050
|
+
appendMessage,
|
|
1051
|
+
updateTask,
|
|
1052
|
+
completeOrchestratorCycle,
|
|
1053
|
+
incrementUserBlockedMs,
|
|
1054
|
+
incrementActiveTime,
|
|
1055
|
+
createSnapshot,
|
|
1056
|
+
restoreSnapshot,
|
|
1057
|
+
listSnapshots,
|
|
1058
|
+
deleteSnapshotsAfter,
|
|
1059
|
+
cloneSessionDir,
|
|
1060
|
+
createCloneState,
|
|
1061
|
+
emitHistoryEvent,
|
|
1062
|
+
writeSessionSummary,
|
|
1063
|
+
getRecentSentiments,
|
|
1064
|
+
pruneHistory,
|
|
1065
|
+
sendTerminalNotification,
|
|
1066
|
+
createAsk,
|
|
1067
|
+
writeDecisions,
|
|
1068
|
+
readDecisions,
|
|
1069
|
+
readProgress,
|
|
1070
|
+
writeOutput,
|
|
1071
|
+
readMeta,
|
|
1072
|
+
updateMeta,
|
|
1073
|
+
listAsks,
|
|
1074
|
+
listOpenAsksFor
|
|
1075
|
+
};
|
|
1076
|
+
//# sourceMappingURL=chunk-SVGIQ2G4.js.map
|