@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.
Files changed (48) hide show
  1. package/CHANGELOG.md +32 -1
  2. package/bin/agentdesk.mjs +35 -45
  3. package/cli/agents.mjs +4 -256
  4. package/cli/bootstrap.mjs +40 -59
  5. package/cli/config.mjs +29 -4
  6. package/cli/daemon.mjs +148 -66
  7. package/cli/dotenv.mjs +96 -13
  8. package/cli/engine/agents/index.mjs +151 -0
  9. package/cli/engine/claude-auth.mjs +72 -0
  10. package/cli/engine/env.mjs +56 -0
  11. package/cli/engine/events.mjs +214 -0
  12. package/cli/engine/hooks.mjs +112 -0
  13. package/cli/engine/phases/EXECUTION.md +45 -0
  14. package/cli/engine/phases/INTAKE.md +34 -0
  15. package/cli/engine/phases/PLAN.md +26 -0
  16. package/cli/engine/phases/REVIEW.md +21 -0
  17. package/cli/engine/phases/SOLO.md +115 -0
  18. package/cli/engine/phases/SUMMARY.md +23 -0
  19. package/cli/engine/prompts.mjs +181 -0
  20. package/cli/engine/query.mjs +63 -0
  21. package/cli/engine/schemas.mjs +180 -0
  22. package/cli/engine/session.mjs +285 -0
  23. package/cli/engine/spawn.mjs +83 -0
  24. package/cli/engine/tracker/github.md +19 -0
  25. package/cli/engine/tracker/jira.md +23 -0
  26. package/cli/engine/tracker/linear.md +24 -0
  27. package/cli/engine/verdict.mjs +83 -0
  28. package/cli/init.mjs +295 -149
  29. package/cli/login.mjs +52 -6
  30. package/cli/phase-loop.mjs +78 -0
  31. package/cli/proc.mjs +131 -0
  32. package/cli/project-key.mjs +56 -0
  33. package/cli/projects.mjs +41 -6
  34. package/cli/prompt.mjs +9 -503
  35. package/cli/prompts.mjs +20 -1
  36. package/cli/security-check.mjs +1 -1
  37. package/cli/session-isolation.mjs +65 -9
  38. package/cli/session-sandbox.mjs +13 -1
  39. package/cli/setup-helpers.mjs +83 -36
  40. package/cli/team.mjs +41 -34
  41. package/cli/tracker-check.mjs +12 -2
  42. package/cli/tracker-project.mjs +93 -0
  43. package/cli/update-check.mjs +62 -0
  44. package/package.json +12 -3
  45. package/cli/orchestrator.mjs +0 -461
  46. package/cli/stream-parser.mjs +0 -216
  47. package/prompts/phased.md +0 -549
  48. package/prompts/team.md +0 -505
@@ -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
- }