@kendoo.agentdesk/agentdesk 0.9.20 → 0.9.21
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 +13 -0
- package/bin/agentdesk.mjs +31 -1
- package/cli/orchestrator.mjs +7 -8
- package/cli/prompt.mjs +82 -0
- package/cli/team.mjs +5 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -69,6 +69,7 @@ agentdesk logout Sign out and remove credentials
|
|
|
69
69
|
agentdesk init Set up project and configure tracker
|
|
70
70
|
agentdesk team <TASK-ID> Run a team session on an existing task
|
|
71
71
|
agentdesk team -d "..." Describe what you want — task created automatically
|
|
72
|
+
agentdesk <agent> -d "..." Run a single agent (jane, dennis, sam, bart, vera, luna, mark)
|
|
72
73
|
agentdesk daemon Start daemon for remote sessions
|
|
73
74
|
agentdesk update Update to the latest version
|
|
74
75
|
```
|
|
@@ -113,6 +114,18 @@ You can also define them in `.agentdesk.json` as a local override if preferred.
|
|
|
113
114
|
|
|
114
115
|
AgentDesk also auto-discovers agents from `.claude/agents/`, `.claude/commands/`, `.mcp.json`, GitHub Actions workflows, Dependabot, and Renovate configs.
|
|
115
116
|
|
|
117
|
+
### Solo agent mode
|
|
118
|
+
|
|
119
|
+
Run a single agent instead of the full team:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
agentdesk jane -d "Create a customer journey plan for onboarding"
|
|
123
|
+
agentdesk dennis KEN-517
|
|
124
|
+
agentdesk vera -d "Add test coverage for the auth module"
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
The agent runs independently with its own expertise and ground rules. Useful for focused tasks that don't need full team collaboration.
|
|
128
|
+
|
|
116
129
|
### Custom instructions
|
|
117
130
|
|
|
118
131
|
Add project-specific rules that all agents follow. Set via Web UI or in `.agentdesk.json`:
|
package/bin/agentdesk.mjs
CHANGED
|
@@ -64,6 +64,7 @@ if (!command || command === "help" || command === "--help") {
|
|
|
64
64
|
agentdesk init Set up project and configure tracker
|
|
65
65
|
agentdesk team <TASK-ID> Run a team session on an existing task
|
|
66
66
|
agentdesk team -d "..." Create a task and run a session
|
|
67
|
+
agentdesk <agent> -d "..." Run a single agent (jane, dennis, sam, etc.)
|
|
67
68
|
agentdesk daemon Start daemon for remote sessions
|
|
68
69
|
agentdesk update Update to the latest version
|
|
69
70
|
|
|
@@ -94,7 +95,36 @@ if (!command || command === "help" || command === "--help") {
|
|
|
94
95
|
process.exit(0);
|
|
95
96
|
}
|
|
96
97
|
|
|
97
|
-
|
|
98
|
+
// Single-agent mode — agentdesk jane -d "...", agentdesk dennis TASK-123, etc.
|
|
99
|
+
const AGENT_NAMES = ["jane", "dennis", "sam", "bart", "vera", "luna", "mark"];
|
|
100
|
+
if (AGENT_NAMES.includes(command?.toLowerCase())) {
|
|
101
|
+
let description = "";
|
|
102
|
+
let cwd = process.cwd();
|
|
103
|
+
let taskId = null;
|
|
104
|
+
const remaining = args.slice(1);
|
|
105
|
+
|
|
106
|
+
for (let i = 0; i < remaining.length; i++) {
|
|
107
|
+
if ((remaining[i] === "--description" || remaining[i] === "-d") && remaining[i + 1]) {
|
|
108
|
+
description = remaining[++i];
|
|
109
|
+
} else if (remaining[i] === "--cwd" && remaining[i + 1]) {
|
|
110
|
+
cwd = remaining[++i];
|
|
111
|
+
} else if (!taskId && !remaining[i].startsWith("-")) {
|
|
112
|
+
taskId = remaining[i];
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (!taskId && !description) {
|
|
117
|
+
console.error(`Usage: agentdesk ${command} <TASK-ID>`);
|
|
118
|
+
console.error(` or: agentdesk ${command} -d "description"`);
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const { runTeam } = await import("../cli/team.mjs");
|
|
123
|
+
const code = await runTeam(taskId, { description, cwd, soloAgent: command.charAt(0).toUpperCase() + command.slice(1).toLowerCase() });
|
|
124
|
+
process.exit(code);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
else if (command === "login") {
|
|
98
128
|
const { runLogin } = await import("../cli/login.mjs");
|
|
99
129
|
await runLogin();
|
|
100
130
|
}
|
package/cli/orchestrator.mjs
CHANGED
|
@@ -5,7 +5,7 @@ import { existsSync, readFileSync, writeFileSync, unlinkSync } from "fs";
|
|
|
5
5
|
import { createInterface } from "readline";
|
|
6
6
|
import { join, dirname } from "path";
|
|
7
7
|
import { fileURLToPath } from "url";
|
|
8
|
-
import { buildPrompt } from "./prompt.mjs";
|
|
8
|
+
import { buildPrompt, buildSoloPrompt } from "./prompt.mjs";
|
|
9
9
|
import { createStreamParser } from "./stream-parser.mjs";
|
|
10
10
|
|
|
11
11
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
@@ -47,7 +47,7 @@ function timestamp() {
|
|
|
47
47
|
export async function runOrchestrator({
|
|
48
48
|
taskId, taskLink, description, createTask, tracker, config,
|
|
49
49
|
project, team, teamSections, inboxUrl, sessionUrl, cwd,
|
|
50
|
-
onEvent, apiKey, serverUrl,
|
|
50
|
+
onEvent, apiKey, serverUrl, soloAgent,
|
|
51
51
|
}) {
|
|
52
52
|
const trackerCreds = await fetchTrackerCredentials(project?.name, apiKey, serverUrl);
|
|
53
53
|
const env = { ...process.env, ...loadDotEnv(cwd), ...trackerCreds };
|
|
@@ -63,10 +63,9 @@ export async function runOrchestrator({
|
|
|
63
63
|
onEvent?.({ ...event, timestamp: timestamp() });
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
const fullPrompt =
|
|
67
|
-
|
|
68
|
-
project, teamSections, inboxUrl, sessionUrl
|
|
69
|
-
});
|
|
66
|
+
const fullPrompt = soloAgent
|
|
67
|
+
? buildSoloPrompt({ agentName: soloAgent, taskId, description, tracker, config, project, sessionUrl })
|
|
68
|
+
: buildPrompt({ taskId, taskLink, description, createTask, tracker, config, project, teamSections, inboxUrl, sessionUrl });
|
|
70
69
|
|
|
71
70
|
emit({
|
|
72
71
|
type: "session:start",
|
|
@@ -74,11 +73,11 @@ export async function runOrchestrator({
|
|
|
74
73
|
title: description || taskId,
|
|
75
74
|
project: project?.name || null,
|
|
76
75
|
sessionNumber: 1,
|
|
77
|
-
agents: teamSections.names,
|
|
76
|
+
agents: soloAgent ? [soloAgent] : teamSections.names,
|
|
78
77
|
cliVersion: CLI_VERSION,
|
|
79
78
|
});
|
|
80
79
|
|
|
81
|
-
emit({ type: "phase:change", phase: "INTAKE" });
|
|
80
|
+
emit({ type: "phase:change", phase: soloAgent ? "EXECUTION" : "INTAKE" });
|
|
82
81
|
|
|
83
82
|
const child = spawn(
|
|
84
83
|
"claude",
|
package/cli/prompt.mjs
CHANGED
|
@@ -4,6 +4,7 @@ import { readFileSync } from "fs";
|
|
|
4
4
|
import { resolve, dirname } from "path";
|
|
5
5
|
import { fileURLToPath } from "url";
|
|
6
6
|
import { generateContext } from "./detect.mjs";
|
|
7
|
+
import { BUILT_IN_AGENTS } from "./agents.mjs";
|
|
7
8
|
|
|
8
9
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
9
10
|
const PROMPT_PATH = resolve(__dirname, "../prompts/team.md");
|
|
@@ -100,3 +101,84 @@ export function buildPrompt({ taskId, taskLink, description, createTask, tracker
|
|
|
100
101
|
|
|
101
102
|
return `${prompt}\n\n---\n\n## PROJECT CONTEXT\n\n${context}\n\n${timeInfo}`;
|
|
102
103
|
}
|
|
104
|
+
|
|
105
|
+
export function buildSoloPrompt({ agentName, taskId, description, tracker, config, project, sessionUrl }) {
|
|
106
|
+
const agent = BUILT_IN_AGENTS[agentName];
|
|
107
|
+
if (!agent) throw new Error(`Unknown agent: ${agentName}`);
|
|
108
|
+
|
|
109
|
+
const lines = [
|
|
110
|
+
`# ${agentName} — ${agent.role} (Solo Mode)`,
|
|
111
|
+
``,
|
|
112
|
+
`You are ${agentName}, ${agent.description}.`,
|
|
113
|
+
`You are working independently on this task — there is no team. You handle everything yourself.`,
|
|
114
|
+
``,
|
|
115
|
+
agent.groundRules ? `## Ground Rules\n\n${agent.groundRules}` : "",
|
|
116
|
+
agent.codePrinciple ? `## Code Principles\n\n${agent.codePrinciple}` : "",
|
|
117
|
+
``,
|
|
118
|
+
`## Task`,
|
|
119
|
+
``,
|
|
120
|
+
taskId ? `Task ID: ${taskId}` : "",
|
|
121
|
+
description ? `Description: ${description}` : "",
|
|
122
|
+
sessionUrl ? `Session: ${sessionUrl}` : "",
|
|
123
|
+
``,
|
|
124
|
+
`## Instructions`,
|
|
125
|
+
``,
|
|
126
|
+
`Work on this task independently. Follow CLAUDE.md conventions if present.`,
|
|
127
|
+
`Read and understand the codebase before making changes.`,
|
|
128
|
+
``,
|
|
129
|
+
];
|
|
130
|
+
|
|
131
|
+
if (agent.execution?.tasks) {
|
|
132
|
+
lines.push(`## Your responsibilities`, ``);
|
|
133
|
+
for (const t of agent.execution.tasks) {
|
|
134
|
+
lines.push(`- ${t}`);
|
|
135
|
+
}
|
|
136
|
+
lines.push(``);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Extract and include tracker integration from team prompt
|
|
140
|
+
if (tracker && taskId) {
|
|
141
|
+
let teamPrompt = readFileSync(PROMPT_PATH, "utf-8");
|
|
142
|
+
|
|
143
|
+
// Extract the matching tracker block
|
|
144
|
+
const trackerKey = tracker.toUpperCase();
|
|
145
|
+
const trackerRegex = new RegExp(`\\{\\{#${trackerKey}\\}\\}([\\s\\S]*?)\\{\\{\\/${trackerKey}\\}\\}`, "g");
|
|
146
|
+
let trackerSection = "";
|
|
147
|
+
let match;
|
|
148
|
+
while ((match = trackerRegex.exec(teamPrompt)) !== null) {
|
|
149
|
+
trackerSection += match[1] + "\n";
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (trackerSection) {
|
|
153
|
+
// Substitute variables
|
|
154
|
+
trackerSection = trackerSection.replace(/\{\{TASK_ID\}\}/g, taskId);
|
|
155
|
+
trackerSection = trackerSection.replace(/\{\{SESSION_URL\}\}/g, sessionUrl || "");
|
|
156
|
+
if (config.jira?.baseUrl) {
|
|
157
|
+
trackerSection = trackerSection.replace(/\{\{JIRA_BASE_URL\}\}/g, config.jira.baseUrl.replace(/\/+$/, ""));
|
|
158
|
+
}
|
|
159
|
+
lines.push(trackerSection);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
lines.push(
|
|
163
|
+
`## Required tracker actions`,
|
|
164
|
+
``,
|
|
165
|
+
`1. Fetch the task — read summary, description, status, comments, attachments.`,
|
|
166
|
+
`2. Post a comment: "${agentName} working on this task (solo mode). Session: ${sessionUrl || ""}"`,
|
|
167
|
+
`3. Do your work.`,
|
|
168
|
+
`4. Post a final comment summarizing what was done, what was omitted, and any manual steps required.`,
|
|
169
|
+
``,
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Add custom instructions
|
|
174
|
+
if (config?.instructions) {
|
|
175
|
+
lines.push(`## Additional Instructions`, ``, config.instructions, ``);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Add project context
|
|
179
|
+
const context = generateContext(project);
|
|
180
|
+
const now = new Date();
|
|
181
|
+
const timeInfo = `Current date/time: ${now.toLocaleDateString("en-US", { weekday: "long", year: "numeric", month: "long", day: "numeric" })} ${now.toLocaleTimeString("en-US", { hour: "2-digit", minute: "2-digit" })}`;
|
|
182
|
+
|
|
183
|
+
return lines.filter(Boolean).join("\n") + `\n\n---\n\n## PROJECT CONTEXT\n\n${context}\n\n${timeInfo}`;
|
|
184
|
+
}
|
package/cli/team.mjs
CHANGED
|
@@ -87,6 +87,10 @@ export async function runTeam(taskId, opts = {}) {
|
|
|
87
87
|
console.log(`Jira: ${cyan}${config.jira.baseUrl}${reset}`);
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
+
if (opts.soloAgent) {
|
|
91
|
+
console.log(`Agent: ${opts.soloAgent} (solo mode)`);
|
|
92
|
+
}
|
|
93
|
+
|
|
90
94
|
// Resolve team and generate dynamic prompt sections
|
|
91
95
|
const team = resolveTeam(config);
|
|
92
96
|
const teamSections = generateTeamPrompt(team, { tracker, config });
|
|
@@ -206,6 +210,7 @@ export async function runTeam(taskId, opts = {}) {
|
|
|
206
210
|
onEvent: vizSend,
|
|
207
211
|
apiKey,
|
|
208
212
|
serverUrl: agentdeskServer,
|
|
213
|
+
soloAgent: opts.soloAgent || null,
|
|
209
214
|
});
|
|
210
215
|
|
|
211
216
|
clearInterval(heartbeatInterval);
|