@kendoo.agentdesk/agentdesk 0.26.0 → 0.28.0
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/CHANGELOG.md +32 -1
- package/bin/agentdesk.mjs +35 -45
- package/cli/agents.mjs +4 -256
- package/cli/bootstrap.mjs +40 -59
- package/cli/config.mjs +29 -4
- package/cli/daemon.mjs +148 -66
- package/cli/dotenv.mjs +96 -13
- package/cli/engine/agents/index.mjs +151 -0
- package/cli/engine/claude-auth.mjs +72 -0
- package/cli/engine/env.mjs +56 -0
- package/cli/engine/events.mjs +214 -0
- package/cli/engine/hooks.mjs +112 -0
- package/cli/engine/phases/EXECUTION.md +45 -0
- package/cli/engine/phases/INTAKE.md +34 -0
- package/cli/engine/phases/PLAN.md +26 -0
- package/cli/engine/phases/REVIEW.md +21 -0
- package/cli/engine/phases/SOLO.md +115 -0
- package/cli/engine/phases/SUMMARY.md +23 -0
- package/cli/engine/prompts.mjs +181 -0
- package/cli/engine/query.mjs +63 -0
- package/cli/engine/schemas.mjs +180 -0
- package/cli/engine/session.mjs +285 -0
- package/cli/engine/spawn.mjs +83 -0
- package/cli/engine/tracker/github.md +19 -0
- package/cli/engine/tracker/jira.md +23 -0
- package/cli/engine/tracker/linear.md +24 -0
- package/cli/engine/verdict.mjs +83 -0
- package/cli/init.mjs +295 -149
- package/cli/login.mjs +52 -6
- package/cli/phase-loop.mjs +78 -0
- package/cli/proc.mjs +131 -0
- package/cli/project-key.mjs +56 -0
- package/cli/projects.mjs +41 -6
- package/cli/prompt.mjs +9 -503
- package/cli/prompts.mjs +20 -1
- package/cli/security-check.mjs +1 -1
- package/cli/session-isolation.mjs +65 -9
- package/cli/session-sandbox.mjs +13 -1
- package/cli/setup-helpers.mjs +83 -36
- package/cli/team.mjs +41 -34
- package/cli/tracker-check.mjs +12 -2
- package/cli/tracker-project.mjs +93 -0
- package/cli/update-check.mjs +62 -0
- package/package.json +12 -3
- package/cli/orchestrator.mjs +0 -461
- package/cli/stream-parser.mjs +0 -216
- package/prompts/phased.md +0 -549
- package/prompts/team.md +0 -505
package/cli/stream-parser.mjs
DELETED
|
@@ -1,216 +0,0 @@
|
|
|
1
|
-
// Shared Claude stream-json parser — used by both `agentdesk team` and `agentdesk daemon`
|
|
2
|
-
|
|
3
|
-
const PHASE_NAMES = ["INTAKE", "PLAN", "EXECUTION", "REVIEW", "SUMMARY"];
|
|
4
|
-
|
|
5
|
-
function escapeRegex(s) {
|
|
6
|
-
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export function createStreamParser({ teamNames, callbacks }) {
|
|
10
|
-
// callbacks: { onAgentMessage, onPhaseChange, onToolUse, onToolResult, onSessionEnd, onSessionUpdate, onTokenUsage }
|
|
11
|
-
|
|
12
|
-
const agentPattern = teamNames.map(escapeRegex).join("|");
|
|
13
|
-
const agentRegex = new RegExp(`^(${agentPattern})\\s*[●◆▲■◈☾✦*]*\\s*:?\\s*`, "i");
|
|
14
|
-
|
|
15
|
-
let lastAgent = teamNames[0] || "Jane";
|
|
16
|
-
let stepCount = 0;
|
|
17
|
-
const startTime = Date.now();
|
|
18
|
-
let totalInputTokens = 0;
|
|
19
|
-
let totalOutputTokens = 0;
|
|
20
|
-
|
|
21
|
-
function detectAgent(text) {
|
|
22
|
-
const stripped = text.replace(/^[●◆▲■◈☾✦*\-─—\s]+/, "");
|
|
23
|
-
const match = stripped.match(agentRegex);
|
|
24
|
-
if (match) {
|
|
25
|
-
const raw = match[1];
|
|
26
|
-
const name = teamNames.find(n => n.toLowerCase() === raw.toLowerCase()) || raw;
|
|
27
|
-
return { name, rest: stripped.slice(match[0].length) };
|
|
28
|
-
}
|
|
29
|
-
return null;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function detectTag(text) {
|
|
33
|
-
if (/\[ARGUE\]/i.test(text)) return "ARGUE";
|
|
34
|
-
if (/\[AGREE\]/i.test(text)) return "AGREE";
|
|
35
|
-
if (/\[THINK\]/i.test(text)) return "THINK";
|
|
36
|
-
if (/\[ACT\]/i.test(text)) return "ACT";
|
|
37
|
-
return "SAY";
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function detectPhase(text) {
|
|
41
|
-
const upper = text.trim().toUpperCase();
|
|
42
|
-
if (!upper) return null;
|
|
43
|
-
|
|
44
|
-
const phaseAlt = PHASE_NAMES.join("|");
|
|
45
|
-
|
|
46
|
-
// Canonical signal: "# PHASE: EXECUTION" / "PHASE EXECUTION" / "## EXECUTION".
|
|
47
|
-
// Accepted anywhere in a line so it survives leading bullets, emoji, etc.
|
|
48
|
-
const explicit = upper.match(new RegExp(`(?:^|\\s)#*\\s*PHASE\\s*[:\\-]?\\s*(${phaseAlt})\\b`));
|
|
49
|
-
if (explicit) return explicit[1];
|
|
50
|
-
|
|
51
|
-
// Markdown heading line that names a phase: "# EXECUTION", "## PLAN", etc.
|
|
52
|
-
const headingMatch = upper.match(new RegExp(`^#+\\s*(${phaseAlt})\\b`));
|
|
53
|
-
if (headingMatch) return headingMatch[1];
|
|
54
|
-
|
|
55
|
-
// Divider followed by a phase name: "--- EXECUTION".
|
|
56
|
-
const dividerMatch = upper.match(new RegExp(`^-{3,}\\s*(${phaseAlt})\\b`));
|
|
57
|
-
if (dividerMatch) return dividerMatch[1];
|
|
58
|
-
|
|
59
|
-
// Natural-language transitions ("entering EXECUTION", "moving to PLAN",
|
|
60
|
-
// "now in REVIEW", "EXECUTION phase begins"). Defensive fallback for when
|
|
61
|
-
// the canonical marker is missed.
|
|
62
|
-
const verbMatch = upper.match(new RegExp(`\\b(?:ENTERING|MOVING\\s+TO|STARTING|BEGIN(?:NING)?|NOW\\s+IN|TRANSITION(?:ING)?\\s+TO|PROCEED(?:ING)?\\s+TO|ADVANCING\\s+TO)\\s+(?:THE\\s+)?(?:PHASE\\s+)?(${phaseAlt})\\b(?:\\s+PHASE)?`));
|
|
63
|
-
if (verbMatch) return verbMatch[1];
|
|
64
|
-
|
|
65
|
-
const phaseSuffixMatch = upper.match(new RegExp(`\\b(${phaseAlt})\\s+PHASE\\s+(?:BEGINS?|STARTS?|HAS\\s+BEGUN|IS\\s+UNDERWAY)\\b`));
|
|
66
|
-
if (phaseSuffixMatch) return phaseSuffixMatch[1];
|
|
67
|
-
|
|
68
|
-
return null;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function timestamp() {
|
|
72
|
-
const d = new Date();
|
|
73
|
-
return [d.getHours(), d.getMinutes(), d.getSeconds()]
|
|
74
|
-
.map(n => String(n).padStart(2, "0")).join(":");
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
let reportedModel = null;
|
|
78
|
-
|
|
79
|
-
function captureModel(candidate) {
|
|
80
|
-
if (!candidate || typeof candidate !== "string") return;
|
|
81
|
-
if (reportedModel === candidate) return;
|
|
82
|
-
reportedModel = candidate;
|
|
83
|
-
callbacks.onModel?.({ model: candidate, timestamp: timestamp() });
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
function parseLine(line) {
|
|
87
|
-
try {
|
|
88
|
-
const event = JSON.parse(line);
|
|
89
|
-
|
|
90
|
-
// Claude's stream-json exposes the actual model it picked — capture it
|
|
91
|
-
// so the UI can show the real model instead of a vague "default" label.
|
|
92
|
-
if (event.type === "system" && event.model) captureModel(event.model);
|
|
93
|
-
if (event.message?.model) captureModel(event.message.model);
|
|
94
|
-
|
|
95
|
-
// Track token usage
|
|
96
|
-
if (event.usage) {
|
|
97
|
-
if (event.usage.input_tokens) totalInputTokens = Math.max(totalInputTokens, event.usage.input_tokens);
|
|
98
|
-
if (event.usage.output_tokens) totalOutputTokens += (event.usage.output_tokens_delta || 0);
|
|
99
|
-
}
|
|
100
|
-
if (event.message?.usage) {
|
|
101
|
-
if (event.message.usage.input_tokens) totalInputTokens = Math.max(totalInputTokens, event.message.usage.input_tokens);
|
|
102
|
-
if (event.message.usage.output_tokens) totalOutputTokens = Math.max(totalOutputTokens, event.message.usage.output_tokens);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
if (event.type === "assistant" && event.message?.content) {
|
|
106
|
-
for (const block of event.message.content) {
|
|
107
|
-
if (block.type === "text" && block.text.trim()) {
|
|
108
|
-
const text = block.text.trim().replace(/\*+/g, "");
|
|
109
|
-
|
|
110
|
-
// Detect TASK_ID announcement
|
|
111
|
-
const taskIdMatch = text.match(/TASK_ID:\s*(\S+)/);
|
|
112
|
-
if (taskIdMatch) {
|
|
113
|
-
callbacks.onSessionUpdate?.({ taskId: taskIdMatch[1] });
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// Detect short title from Jane
|
|
117
|
-
const titleMatch = text.match(/SESSION_TITLE:\s*(.+)/);
|
|
118
|
-
if (titleMatch) {
|
|
119
|
-
callbacks.onSessionUpdate?.({ title: titleMatch[1].trim().slice(0, 60) });
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
const textLines = text.split("\n");
|
|
123
|
-
let i = 0;
|
|
124
|
-
while (i < textLines.length) {
|
|
125
|
-
const currentLine = textLines[i].trim();
|
|
126
|
-
if (!currentLine) { i++; continue; }
|
|
127
|
-
|
|
128
|
-
const phase = detectPhase(currentLine);
|
|
129
|
-
if (phase) {
|
|
130
|
-
callbacks.onPhaseChange?.({ phase, timestamp: timestamp() });
|
|
131
|
-
i++;
|
|
132
|
-
continue;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
const detected = detectAgent(currentLine);
|
|
136
|
-
if (detected) {
|
|
137
|
-
let msg = detected.rest;
|
|
138
|
-
while (i + 1 < textLines.length && !detectAgent(textLines[i + 1].trim())) {
|
|
139
|
-
i++;
|
|
140
|
-
const next = textLines[i].trim();
|
|
141
|
-
if (next) msg += "\n" + next;
|
|
142
|
-
}
|
|
143
|
-
const tag = detectTag(msg);
|
|
144
|
-
const cleanMsg = msg.replace(/\[(SAY|ACT|THINK|AGREE|ARGUE)\]\s*/gi, "");
|
|
145
|
-
lastAgent = detected.name;
|
|
146
|
-
callbacks.onAgentMessage?.({ agent: detected.name, tag, message: cleanMsg, timestamp: timestamp() });
|
|
147
|
-
} else {
|
|
148
|
-
callbacks.onAgentMessage?.({ agent: lastAgent, tag: "SAY", message: currentLine, timestamp: timestamp() });
|
|
149
|
-
}
|
|
150
|
-
i++;
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
if (event.type === "tool_use") {
|
|
157
|
-
const name = event.name || event.tool_name;
|
|
158
|
-
stepCount++;
|
|
159
|
-
|
|
160
|
-
let description = "Running command...";
|
|
161
|
-
if (name === "Bash") {
|
|
162
|
-
const cmd = event.input?.command || "";
|
|
163
|
-
if (cmd.includes("curl") && cmd.includes("linear")) description = "Calling Linear API...";
|
|
164
|
-
else if (cmd.includes("curl")) description = "Making API request...";
|
|
165
|
-
else {
|
|
166
|
-
const shortCmd = cmd.length > 80 ? cmd.slice(0, 80) + "..." : cmd;
|
|
167
|
-
description = `$ ${shortCmd}`;
|
|
168
|
-
}
|
|
169
|
-
} else if (name === "Read") {
|
|
170
|
-
const shortPath = (event.input?.file_path || "").split("/").slice(-3).join("/");
|
|
171
|
-
description = `Reading ${shortPath}`;
|
|
172
|
-
} else if (name === "Edit" || name === "Write") {
|
|
173
|
-
const shortPath = (event.input?.file_path || "").split("/").slice(-3).join("/");
|
|
174
|
-
description = `${name === "Edit" ? "Editing" : "Writing"} ${shortPath}`;
|
|
175
|
-
} else if (name === "Glob" || name === "Grep") {
|
|
176
|
-
description = `Searching ${event.input?.pattern || ""}`;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
callbacks.onToolUse?.({ agent: lastAgent, tool: name, description, timestamp: timestamp() });
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
if (event.type === "tool_result") {
|
|
183
|
-
const output = event.content || event.output;
|
|
184
|
-
let text = "";
|
|
185
|
-
if (typeof output === "string") text = output.trim();
|
|
186
|
-
else if (Array.isArray(output)) {
|
|
187
|
-
text = output.filter(b => b.type === "text").map(b => b.text.trim()).join("\n");
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
const hasError = text && (text.toLowerCase().includes("error") || text.toLowerCase().includes("failed"));
|
|
191
|
-
const summary = text?.length > 300 ? `Done (${text.length} chars)` : text || "Done";
|
|
192
|
-
|
|
193
|
-
callbacks.onToolResult?.({ success: !hasError, summary, timestamp: timestamp() });
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
if (event.type === "result") {
|
|
197
|
-
if (event.usage) {
|
|
198
|
-
if (event.usage.input_tokens) totalInputTokens = Math.max(totalInputTokens, event.usage.input_tokens);
|
|
199
|
-
if (event.usage.output_tokens) totalOutputTokens = Math.max(totalOutputTokens, event.usage.output_tokens);
|
|
200
|
-
}
|
|
201
|
-
const totalTime = ((Date.now() - startTime) / 1000).toFixed(1);
|
|
202
|
-
callbacks.onSessionEnd?.({
|
|
203
|
-
duration: `${totalTime}s`,
|
|
204
|
-
steps: stepCount,
|
|
205
|
-
inputTokens: totalInputTokens,
|
|
206
|
-
outputTokens: totalOutputTokens,
|
|
207
|
-
timestamp: timestamp(),
|
|
208
|
-
});
|
|
209
|
-
}
|
|
210
|
-
} catch {
|
|
211
|
-
// skip non-JSON lines
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
return { parseLine, getLastAgent: () => lastAgent, getStepCount: () => stepCount };
|
|
216
|
-
}
|