@kendoo.agentdesk/agentdesk 0.11.3 → 0.11.5
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/cli/agents.mjs +1 -1
- package/cli/daemon.mjs +46 -8
- package/cli/init.mjs +11 -0
- package/cli/orchestrator.mjs +2 -2
- package/cli/prompt.mjs +41 -4
- package/package.json +1 -1
package/cli/agents.mjs
CHANGED
|
@@ -5,7 +5,7 @@ export const BUILT_IN_AGENTS = {
|
|
|
5
5
|
badge: "●● JANE ●●",
|
|
6
6
|
role: "Product Analyst / Team Lead",
|
|
7
7
|
description: "leads the session, clarifies requirements, coordinates the team, manages tracker status, decomposes large tasks into subtasks",
|
|
8
|
-
groundRules: "Jane focuses on requirements, scope, and coordination — she does
|
|
8
|
+
groundRules: "Jane focuses on requirements, scope, and coordination — she does NOT read code, inspect functions, or reference technical implementation details (no file names, function names, variable names, or code snippets). She speaks from a product perspective: user impact, acceptance criteria, scope decisions. She creates tracker tasks when needed, manages status transitions, posts the session start/end comments, and decomposes large features into subtasks (basic vs deferred).",
|
|
9
9
|
planning: "Requirements: what we're building, acceptance criteria, scope. Flags UI tasks for Luna.",
|
|
10
10
|
execution: {
|
|
11
11
|
step: "Jane wraps up",
|
package/cli/daemon.mjs
CHANGED
|
@@ -381,15 +381,53 @@ export async function runDaemon() {
|
|
|
381
381
|
cwd: project.path,
|
|
382
382
|
apiKey,
|
|
383
383
|
serverUrl: agentdeskServer,
|
|
384
|
-
onEvent(
|
|
385
|
-
//
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
384
|
+
onEvent: (() => {
|
|
385
|
+
// Stagger agent messages for real-time feel, flush on non-message events
|
|
386
|
+
const MSG_STAGGER_MS = 400;
|
|
387
|
+
let msgQueue = [];
|
|
388
|
+
let staggerTimer = null;
|
|
389
|
+
|
|
390
|
+
function flushQueue() {
|
|
391
|
+
clearTimeout(staggerTimer);
|
|
392
|
+
staggerTimer = null;
|
|
393
|
+
for (const queued of msgQueue) sendBuffered(sessionId, queued);
|
|
394
|
+
msgQueue = [];
|
|
391
395
|
}
|
|
392
|
-
|
|
396
|
+
|
|
397
|
+
function drainNext() {
|
|
398
|
+
if (msgQueue.length === 0) { staggerTimer = null; return; }
|
|
399
|
+
sendBuffered(sessionId, msgQueue.shift());
|
|
400
|
+
if (msgQueue.length > 0) {
|
|
401
|
+
staggerTimer = setTimeout(drainNext, MSG_STAGGER_MS);
|
|
402
|
+
} else {
|
|
403
|
+
staggerTimer = null;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
return (event) => {
|
|
408
|
+
if (!activeSession || activeSession.sessionId !== sessionId) return;
|
|
409
|
+
|
|
410
|
+
if (event.type === "agent:message") {
|
|
411
|
+
msgQueue.push(event);
|
|
412
|
+
// First message sends immediately, rest are staggered
|
|
413
|
+
if (!staggerTimer && msgQueue.length === 1) {
|
|
414
|
+
sendBuffered(sessionId, msgQueue.shift());
|
|
415
|
+
if (msgQueue.length > 0) {
|
|
416
|
+
staggerTimer = setTimeout(drainNext, MSG_STAGGER_MS);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
} else {
|
|
420
|
+
// Non-message event: flush all queued messages first, then send this event
|
|
421
|
+
if (msgQueue.length > 0) flushQueue();
|
|
422
|
+
sendBuffered(sessionId, event);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
if (event.type === "tool:use" && event.description) {
|
|
426
|
+
const pathMatch = event.description.match(/(?:Reading|Editing|Writing)\s+(.+)/);
|
|
427
|
+
if (pathMatch) filePathsTouched.add(pathMatch[1]);
|
|
428
|
+
}
|
|
429
|
+
};
|
|
430
|
+
})(),
|
|
393
431
|
});
|
|
394
432
|
|
|
395
433
|
console.log(` ${green}Session complete${reset} ${dim}${sessionId}${reset} (${result.duration}, ${result.steps} steps)`);
|
package/cli/init.mjs
CHANGED
|
@@ -352,6 +352,17 @@ export async function runInit(cwd) {
|
|
|
352
352
|
writeFileSync(configPath, JSON.stringify(merged, null, 2) + "\n");
|
|
353
353
|
console.log(` Saved .agentdesk.json`);
|
|
354
354
|
|
|
355
|
+
// Ensure .agentdesk/ is gitignored (local runtime state — memory, cache, etc.)
|
|
356
|
+
const gitignorePath = join(project.dir, ".gitignore");
|
|
357
|
+
try {
|
|
358
|
+
const existing = existsSync(gitignorePath) ? readFileSync(gitignorePath, "utf-8") : "";
|
|
359
|
+
if (!existing.split("\n").some(line => line.trim() === ".agentdesk/" || line.trim() === ".agentdesk")) {
|
|
360
|
+
const nl = existing.endsWith("\n") || !existing ? "" : "\n";
|
|
361
|
+
writeFileSync(gitignorePath, `${existing}${nl}.agentdesk/\n`);
|
|
362
|
+
console.log(` Added .agentdesk/ to .gitignore`);
|
|
363
|
+
}
|
|
364
|
+
} catch {}
|
|
365
|
+
|
|
355
366
|
// Register in local project index (for daemon discovery)
|
|
356
367
|
registerLocalProject(finalProjectKey, project.name || finalProjectKey, project.dir);
|
|
357
368
|
|
package/cli/orchestrator.mjs
CHANGED
|
@@ -64,8 +64,8 @@ export async function runOrchestrator({
|
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
const fullPrompt = soloAgent
|
|
67
|
-
? buildSoloPrompt({ agentName: soloAgent, taskId, description, tracker, config, project, sessionUrl, childStrategy })
|
|
68
|
-
: buildPrompt({ taskId, taskLink, description, createTask, tracker, config, project, teamSections, inboxUrl, sessionUrl });
|
|
67
|
+
? buildSoloPrompt({ agentName: soloAgent, taskId, description, tracker, config, project, sessionUrl, childStrategy, cwd })
|
|
68
|
+
: buildPrompt({ taskId, taskLink, description, createTask, tracker, config, project, teamSections, inboxUrl, sessionUrl, cwd });
|
|
69
69
|
|
|
70
70
|
emit({
|
|
71
71
|
type: "session:start",
|
package/cli/prompt.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Shared prompt builder — used by both `agentdesk team` and `agentdesk daemon`
|
|
2
2
|
|
|
3
|
-
import { readFileSync } from "fs";
|
|
4
|
-
import { resolve, dirname } from "path";
|
|
3
|
+
import { readFileSync, existsSync } from "fs";
|
|
4
|
+
import { resolve, dirname, join } from "path";
|
|
5
5
|
import { fileURLToPath } from "url";
|
|
6
6
|
import { generateContext } from "./detect.mjs";
|
|
7
7
|
import { BUILT_IN_AGENTS } from "./agents.mjs";
|
|
@@ -9,7 +9,30 @@ import { BUILT_IN_AGENTS } from "./agents.mjs";
|
|
|
9
9
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
10
10
|
const PROMPT_PATH = resolve(__dirname, "../prompts/team.md");
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
function loadProjectMemory(cwd) {
|
|
13
|
+
if (!cwd) return "";
|
|
14
|
+
const memPath = join(cwd, ".agentdesk", "memory.md");
|
|
15
|
+
try {
|
|
16
|
+
if (existsSync(memPath)) return readFileSync(memPath, "utf-8").trim();
|
|
17
|
+
} catch {}
|
|
18
|
+
return "";
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const MEMORY_INSTRUCTIONS = `
|
|
22
|
+
## Project Memory
|
|
23
|
+
|
|
24
|
+
The team has a shared memory file at \`.agentdesk/memory.md\` for storing learnings that should persist across sessions. This is LOCAL and gitignored — it never leaves this machine.
|
|
25
|
+
|
|
26
|
+
**When to save**: After discovering something non-obvious that cost time (test setup steps, required seed data, login credentials for test env, workarounds, environment quirks, deployment steps). If you had to figure it out, save it so you don't have to next time.
|
|
27
|
+
|
|
28
|
+
**When NOT to save**: Code patterns, architecture, or anything derivable from the codebase. Don't duplicate what's already in README or CLAUDE.md.
|
|
29
|
+
|
|
30
|
+
**Format**: Use clear markdown sections. Update existing sections rather than appending duplicates. Never store real secrets — reference env vars instead (e.g. \`$TEST_ADMIN_PASSWORD\`).
|
|
31
|
+
|
|
32
|
+
**How**: Use the Edit or Write tool on \`.agentdesk/memory.md\`. Create the file if it doesn't exist.
|
|
33
|
+
`.trim();
|
|
34
|
+
|
|
35
|
+
export function buildPrompt({ taskId, taskLink, description, createTask, tracker, config, project, teamSections, inboxUrl, sessionUrl, cwd }) {
|
|
13
36
|
let prompt = readFileSync(PROMPT_PATH, "utf-8");
|
|
14
37
|
|
|
15
38
|
// Team substitution
|
|
@@ -99,6 +122,13 @@ export function buildPrompt({ taskId, taskLink, description, createTask, tracker
|
|
|
99
122
|
}));
|
|
100
123
|
}
|
|
101
124
|
|
|
125
|
+
// Project memory
|
|
126
|
+
const memory = loadProjectMemory(cwd);
|
|
127
|
+
prompt += `\n\n${MEMORY_INSTRUCTIONS}`;
|
|
128
|
+
if (memory) {
|
|
129
|
+
prompt += `\n\n### Current memory\n\n${memory}`;
|
|
130
|
+
}
|
|
131
|
+
|
|
102
132
|
// Append project context and current time
|
|
103
133
|
const context = generateContext(project);
|
|
104
134
|
const now = new Date();
|
|
@@ -107,7 +137,7 @@ export function buildPrompt({ taskId, taskLink, description, createTask, tracker
|
|
|
107
137
|
return `${prompt}\n\n---\n\n## PROJECT CONTEXT\n\n${context}\n\n${timeInfo}`;
|
|
108
138
|
}
|
|
109
139
|
|
|
110
|
-
export function buildSoloPrompt({ agentName, taskId, description, tracker, config, project, sessionUrl, childStrategy }) {
|
|
140
|
+
export function buildSoloPrompt({ agentName, taskId, description, tracker, config, project, sessionUrl, childStrategy, cwd }) {
|
|
111
141
|
const agent = BUILT_IN_AGENTS[agentName];
|
|
112
142
|
if (!agent) throw new Error(`Unknown agent: ${agentName}`);
|
|
113
143
|
|
|
@@ -316,6 +346,13 @@ export function buildSoloPrompt({ agentName, taskId, description, tracker, confi
|
|
|
316
346
|
lines.push(`## Additional Instructions`, ``, config.instructions, ``);
|
|
317
347
|
}
|
|
318
348
|
|
|
349
|
+
// Project memory
|
|
350
|
+
const memory = loadProjectMemory(cwd);
|
|
351
|
+
lines.push(MEMORY_INSTRUCTIONS);
|
|
352
|
+
if (memory) {
|
|
353
|
+
lines.push(`### Current memory`, ``, memory);
|
|
354
|
+
}
|
|
355
|
+
|
|
319
356
|
// Add project context
|
|
320
357
|
const context = generateContext(project);
|
|
321
358
|
const now = new Date();
|