@kendoo.agentdesk/agentdesk 0.8.5 → 0.9.1

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 CHANGED
@@ -79,7 +79,7 @@ agentdesk update Update to the latest version
79
79
  |------|-------------|
80
80
  | `--description`, `-d` | Task description or requirements |
81
81
  | `--cwd` | Working directory (defaults to current) |
82
- | `--lite` | Lite mode — faster and cheaper, agents share one conversation |
82
+ | `--lite` | Lite mode — all agents share one process (faster, cheaper) |
83
83
 
84
84
  ## Configuration
85
85
 
@@ -116,7 +116,7 @@ AgentDesk also auto-discovers agents from `.claude/agents/`, `.claude/commands/`
116
116
 
117
117
  ## How It Works
118
118
 
119
- Each agent runs as its own independent Claude session — not one model role-playing multiple personas. This produces genuine independent reasoning, real disagreements, and better output.
119
+ Each agent runs in its own Claude process — not one model role-playing multiple personas. This produces genuine disagreements and better output because each agent reasons separately.
120
120
 
121
121
  1. You run `agentdesk team TASK-ID` in your project directory
122
122
  2. AgentDesk detects your project type, reads `CLAUDE.md`, and discovers existing agents
@@ -130,8 +130,8 @@ Each agent runs as its own independent Claude session — not one model role-pla
130
130
 
131
131
  | Mode | Command | Description |
132
132
  |------|---------|-------------|
133
- | Full (default) | `agentdesk team -d "..."` | Each agent thinks independently in its own Claude session. Better output. |
134
- | Lite | `agentdesk team -d "..." --lite` | All agents share one conversation. Faster and cheaper. |
133
+ | Full (default) | `agentdesk team -d "..."` | Each agent runs in its own process. Better output. |
134
+ | Lite | `agentdesk team -d "..." --lite` | All agents share one process. Faster and cheaper. |
135
135
 
136
136
  ## Daemon (Remote Sessions)
137
137
 
package/bin/agentdesk.mjs CHANGED
@@ -65,7 +65,7 @@ if (!command || command === "help" || command === "--help") {
65
65
  Options:
66
66
  --description, -d Task description or requirements
67
67
  --cwd Working directory (defaults to current)
68
- --lite Lite mode — faster and cheaper, agents share one conversation
68
+ --lite Lite mode — all agents share one process (faster, cheaper)
69
69
 
70
70
  How it works:
71
71
  With a tracker (Linear, Jira, GitHub Issues):
@@ -115,6 +115,8 @@ else if (command === "team") {
115
115
  cwd = remaining[++i];
116
116
  } else if (remaining[i] === "--lite" || remaining[i] === "--legacy") {
117
117
  lite = true;
118
+ } else if (remaining[i] === "--full") {
119
+ lite = false;
118
120
  } else if (!taskId && !remaining[i].startsWith("-")) {
119
121
  taskId = remaining[i];
120
122
  }
package/cli/daemon.mjs CHANGED
@@ -313,7 +313,7 @@ export async function runDaemon() {
313
313
 
314
314
  // 4. Session handling
315
315
 
316
- async function handleStartSession({ sessionId, projectId, prompt }) {
316
+ async function handleStartSession({ sessionId, projectId, prompt, mode }) {
317
317
  // Validate project against local allowlist
318
318
  const project = projects.find(p => p.id === projectId);
319
319
  if (!project) {
@@ -360,7 +360,7 @@ export async function runDaemon() {
360
360
  const inboxUrl = `${agentdeskServer}/api/sessions/${sessionId}/inbox`;
361
361
  const sessionUrl = `${agentdeskServer}/sessions/${sessionId}`;
362
362
 
363
- // Run orchestrator (sub-agent mode)
363
+ // Run orchestrator
364
364
  const result = await runOrchestrator({
365
365
  taskId, taskLink,
366
366
  description: prompt || "",
@@ -369,6 +369,7 @@ export async function runDaemon() {
369
369
  project: detected, team, teamSections,
370
370
  inboxUrl, sessionUrl,
371
371
  cwd: project.path,
372
+ mode: mode || "full",
372
373
  onEvent(event) {
373
374
  sendBuffered(sessionId, event);
374
375
  // Track file paths for metadata logging
@@ -1,9 +1,13 @@
1
1
  // Orchestrator — manages agent sub-sessions across phases
2
2
 
3
+ import { spawn } from "child_process";
3
4
  import { existsSync, readFileSync } from "fs";
5
+ import { createInterface } from "readline";
4
6
  import { join, dirname } from "path";
5
7
  import { fileURLToPath } from "url";
6
8
  import { runAgent } from "./agent-runner.mjs";
9
+ import { buildPrompt } from "./prompt.mjs";
10
+ import { createStreamParser } from "./stream-parser.mjs";
7
11
 
8
12
  const __dirname = dirname(fileURLToPath(import.meta.url));
9
13
  const CLI_VERSION = JSON.parse(readFileSync(join(__dirname, "../package.json"), "utf-8")).version;
@@ -33,8 +37,14 @@ function timestamp() {
33
37
  export async function runOrchestrator({
34
38
  taskId, taskLink, description, createTask, tracker, config,
35
39
  project, team, teamSections, inboxUrl, sessionUrl, cwd,
36
- onEvent,
40
+ onEvent, mode = "full",
37
41
  }) {
42
+ // --- Lite mode: single process, legacy behavior ---
43
+ if (mode === "lite") {
44
+ return runLiteMode({ taskId, taskLink, description, createTask, tracker, config, project, teamSections, inboxUrl, sessionUrl, cwd, onEvent });
45
+ }
46
+
47
+ // --- Full mode: independent sub-agents ---
38
48
  const state = {
39
49
  task: { id: taskId, link: taskLink, description: description || "" },
40
50
  conversationLog: [], // { type: "message"|"phase", agent?, tag?, message?, phase? }
@@ -290,3 +300,72 @@ export async function runOrchestrator({
290
300
  outputTokens: state.totalOutputTokens,
291
301
  };
292
302
  }
303
+
304
+ // --- Lite mode: single Claude process with all agents as personas ---
305
+
306
+ async function runLiteMode({ taskId, taskLink, description, createTask, tracker, config, project, teamSections, inboxUrl, sessionUrl, cwd, onEvent }) {
307
+ const env = { ...process.env, ...loadDotEnv(cwd) };
308
+ const startTime = Date.now();
309
+ let totalInputTokens = 0;
310
+ let totalOutputTokens = 0;
311
+ let totalSteps = 0;
312
+
313
+ function emit(event) {
314
+ onEvent?.({ ...event, timestamp: timestamp() });
315
+ }
316
+
317
+ const fullPrompt = buildPrompt({
318
+ taskId, taskLink, description, createTask, tracker, config,
319
+ project, teamSections, inboxUrl, sessionUrl,
320
+ });
321
+
322
+ emit({
323
+ type: "session:start",
324
+ taskId, taskLink,
325
+ title: description || taskId,
326
+ project: project?.name || null,
327
+ sessionNumber: 1,
328
+ agents: teamSections.names,
329
+ cliVersion: CLI_VERSION,
330
+ });
331
+
332
+ emit({ type: "phase:change", phase: "INTAKE" });
333
+
334
+ const child = spawn(
335
+ "claude",
336
+ ["-p", fullPrompt, "--allowedTools", "Bash,Read,Edit,Write,Glob,Grep", "--verbose", "--output-format", "stream-json"],
337
+ { stdio: ["pipe", "pipe", "inherit"], shell: false, env, cwd }
338
+ );
339
+ child.stdin.end();
340
+
341
+ const { parseLine } = createStreamParser({
342
+ teamNames: teamSections.names,
343
+ callbacks: {
344
+ onPhaseChange({ phase }) { emit({ type: "phase:change", phase }); },
345
+ onAgentMessage({ agent, tag, message }) { emit({ type: "agent:message", agent, tag, message }); },
346
+ onToolUse({ agent, tool, description }) { totalSteps++; emit({ type: "tool:use", agent, tool, description }); },
347
+ onToolResult({ success, summary }) { emit({ type: "tool:result", success, summary }); },
348
+ onSessionUpdate({ taskId: newTaskId, title }) {
349
+ if (newTaskId) emit({ type: "session:update", taskId: newTaskId });
350
+ if (title) emit({ type: "session:update", title });
351
+ },
352
+ onSessionEnd({ duration, steps, inputTokens, outputTokens }) {
353
+ totalInputTokens = inputTokens;
354
+ totalOutputTokens = outputTokens;
355
+ totalSteps = steps;
356
+ },
357
+ },
358
+ });
359
+
360
+ const rl = createInterface({ input: child.stdout });
361
+ for await (const line of rl) {
362
+ parseLine(line);
363
+ }
364
+
365
+ await new Promise(resolve => child.on("close", resolve));
366
+
367
+ const duration = `${((Date.now() - startTime) / 1000).toFixed(1)}s`;
368
+ emit({ type: "session:end", duration, steps: totalSteps, inputTokens: totalInputTokens, outputTokens: totalOutputTokens });
369
+
370
+ return { duration, steps: totalSteps, inputTokens: totalInputTokens, outputTokens: totalOutputTokens };
371
+ }
package/cli/team.mjs CHANGED
@@ -1,23 +1,19 @@
1
1
  // `agentdesk team <TASK-ID> [--description "..."]` — run a team session
2
2
 
3
- import { spawn } from "child_process";
4
3
  import { existsSync, readFileSync } from "fs";
5
- import { createInterface } from "readline";
6
4
  import { join, dirname } from "path";
7
5
  import { randomUUID } from "crypto";
8
6
  import { fileURLToPath } from "url";
9
-
10
- const __dirname = dirname(fileURLToPath(import.meta.url));
11
- const CLI_VERSION = JSON.parse(readFileSync(join(__dirname, "../package.json"), "utf-8")).version;
12
7
  import WebSocket from "ws";
13
8
  import { detectProject } from "./detect.mjs";
14
9
  import { loadConfig } from "./config.mjs";
15
10
  import { getStoredApiKey } from "./login.mjs";
16
11
  import { resolveTeam, generateTeamPrompt } from "./agents.mjs";
17
- import { buildPrompt } from "./prompt.mjs";
18
- import { createStreamParser } from "./stream-parser.mjs";
19
12
  import { runOrchestrator } from "./orchestrator.mjs";
20
13
 
14
+ const __dirname = dirname(fileURLToPath(import.meta.url));
15
+ const CLI_VERSION = JSON.parse(readFileSync(join(__dirname, "../package.json"), "utf-8")).version;
16
+
21
17
  function loadDotEnv(dir) {
22
18
  const envPath = join(dir, ".env");
23
19
  if (!existsSync(envPath)) return {};
@@ -173,112 +169,20 @@ export async function runTeam(taskId, opts = {}) {
173
169
 
174
170
  connectWs();
175
171
 
176
- console.log(`Agents: ${lite ? "lite (shared)" : "full (independent)"}\n`);
177
-
178
- // --- Sub-agent mode (default) ---
179
- if (!lite) {
180
- const result = await runOrchestrator({
181
- taskId, taskLink, description, createTask, tracker, config,
182
- project, team, teamSections, inboxUrl, sessionUrl, cwd,
183
- onEvent: vizSend,
184
- });
185
-
186
- console.log(`\n━━━ DONE ━━━`);
187
- const totalTokens = result.inputTokens + result.outputTokens;
188
- console.log(` ${result.duration} | ${result.steps} steps${totalTokens ? ` | ${totalTokens.toLocaleString()} tokens` : ""}\n`);
189
-
190
- setTimeout(() => { try { vizWs?.close(); } catch {} }, 500);
191
- return 0;
192
- }
193
-
194
- // --- Lite mode (shared conversation) ---
195
- const fullPrompt = buildPrompt({
196
- taskId, taskLink, description, createTask, tracker, config, project,
197
- teamSections, inboxUrl, sessionUrl,
198
- });
199
-
200
- const child = spawn(
201
- "claude",
202
- [
203
- "-p", fullPrompt,
204
- "--allowedTools", "Bash,Read,Edit,Write,Glob,Grep",
205
- "--verbose",
206
- "--output-format", "stream-json",
207
- ],
208
- {
209
- stdio: ["inherit", "pipe", "inherit"],
210
- shell: false,
211
- env: { ...process.env, ...loadDotEnv(cwd) },
212
- cwd,
213
- }
214
- );
215
-
216
- process.on("SIGINT", () => { try { child.kill(); } catch {} process.exit(1); });
217
- process.on("SIGTERM", () => { try { child.kill(); } catch {} process.exit(1); });
218
-
219
- console.log("\n━━━ INTAKE ━━━\n");
220
- vizSend({ type: "phase:change", phase: "INTAKE" });
221
-
222
- function timestamp() {
223
- const d = new Date();
224
- return [d.getHours(), d.getMinutes(), d.getSeconds()]
225
- .map(n => String(n).padStart(2, "0")).join(":");
226
- }
172
+ const agentMode = lite ? "lite" : "full";
173
+ console.log(`Agents: ${agentMode === "lite" ? "lite (shared process)" : "full (independent process)"}\n`);
227
174
 
228
- const { parseLine } = createStreamParser({
229
- teamNames: teamSections.names,
230
- callbacks: {
231
- onPhaseChange({ phase }) {
232
- console.log(`\n━━━ ${phase} ━━━\n`);
233
- vizSend({ type: "phase:change", phase });
234
- },
235
- onAgentMessage({ agent, tag, message }) {
236
- console.log(`${timestamp()} ${agent} [${tag}] ${message}\n`);
237
- vizSend({ type: "agent:message", agent, tag, message });
238
- },
239
- onToolUse({ agent, tool, description }) {
240
- console.log(` ${agent} [ACT] ${description}`);
241
- vizSend({ type: "tool:use", agent, tool, description });
242
- },
243
- onToolResult({ success, summary }) {
244
- console.log(` ${success ? "✓" : "✗"} ${summary}`);
245
- vizSend({ type: "tool:result", success, summary });
246
- },
247
- onSessionUpdate({ taskId: newTaskId, title: newTitle }) {
248
- if (newTitle) {
249
- vizSend({ type: "session:update", title: newTitle });
250
- }
251
- if (newTaskId && createTask) {
252
- taskId = newTaskId;
253
- if (tracker === "linear" && config.linear?.workspace) {
254
- taskLink = `https://linear.app/${config.linear.workspace}/issue/${newTaskId}`;
255
- } else if (tracker === "jira" && config.jira?.baseUrl) {
256
- taskLink = `${config.jira.baseUrl}/browse/${newTaskId}`;
257
- } else if (tracker === "github") {
258
- const repo = config.github?.repo || "";
259
- if (repo) taskLink = `https://github.com/${repo}/issues/${newTaskId}`;
260
- }
261
- console.log(`\n # Task: ${newTaskId}${taskLink ? ` (${taskLink})` : ""}\n`);
262
- vizSend({ type: "session:update", taskId: newTaskId, taskLink, title: newTitle || description || newTaskId });
263
- }
264
- },
265
- onSessionEnd({ duration, steps, inputTokens, outputTokens }) {
266
- const totalTokens = inputTokens + outputTokens;
267
- console.log(`\n━━━ DONE ━━━`);
268
- console.log(` ${duration} | ${steps} steps${totalTokens ? ` | ${totalTokens.toLocaleString()} tokens` : ""}\n`);
269
- vizSend({ type: "session:end", duration, steps, inputTokens, outputTokens });
270
- setTimeout(() => { try { vizWs?.close(); } catch {} }, 500);
271
- },
272
- },
175
+ const result = await runOrchestrator({
176
+ taskId, taskLink, description, createTask, tracker, config,
177
+ project, team, teamSections, inboxUrl, sessionUrl, cwd,
178
+ onEvent: vizSend,
179
+ mode: agentMode,
273
180
  });
274
181
 
275
- const rl = createInterface({ input: child.stdout });
182
+ console.log(`\n━━━ DONE ━━━`);
183
+ const totalTokens = result.inputTokens + result.outputTokens;
184
+ console.log(` ${result.duration} | ${result.steps} steps${totalTokens ? ` | ${totalTokens.toLocaleString()} tokens` : ""}\n`);
276
185
 
277
- for await (const line of rl) {
278
- parseLine(line);
279
- }
280
-
281
- return new Promise(resolve => {
282
- child.on("close", (code) => resolve(code || 0));
283
- });
186
+ setTimeout(() => { try { vizWs?.close(); } catch {} }, 500);
187
+ return 0;
284
188
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kendoo.agentdesk/agentdesk",
3
- "version": "0.8.5",
3
+ "version": "0.9.1",
4
4
  "description": "AI team orchestrator for Claude Code — run collaborative agent sessions from your terminal",
5
5
  "type": "module",
6
6
  "bin": {