@kendoo.agentdesk/agentdesk 0.9.11 → 0.9.12
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 +155 -6
- package/cli/daemon.mjs +1 -1
- package/cli/team.mjs +1 -1
- package/package.json +2 -2
- package/prompts/team.md +82 -0
package/cli/agents.mjs
CHANGED
|
@@ -4,8 +4,8 @@ export const BUILT_IN_AGENTS = {
|
|
|
4
4
|
Jane: {
|
|
5
5
|
badge: "●● JANE ●●",
|
|
6
6
|
role: "Product Analyst / Team Lead",
|
|
7
|
-
description: "leads the session, clarifies requirements, coordinates the team, manages tracker status",
|
|
8
|
-
groundRules: "Jane focuses on requirements, scope, and coordination — she does not read code. She creates tracker tasks when needed, manages status transitions,
|
|
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 not read code. 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",
|
|
@@ -202,10 +202,119 @@ export function resolveTeam(config) {
|
|
|
202
202
|
return team;
|
|
203
203
|
}
|
|
204
204
|
|
|
205
|
+
/**
|
|
206
|
+
* Return tracker-specific command snippets for execution steps.
|
|
207
|
+
*/
|
|
208
|
+
function trackerCommands(tracker, config) {
|
|
209
|
+
if (!tracker) return null;
|
|
210
|
+
|
|
211
|
+
if (tracker === "linear") {
|
|
212
|
+
return {
|
|
213
|
+
postPrLink: [
|
|
214
|
+
'Post PR link on Linear using attachmentCreate:',
|
|
215
|
+
'```',
|
|
216
|
+
'curl -s -X POST https://api.linear.app/graphql \\',
|
|
217
|
+
' -H "Authorization: $LINEAR_API_KEY" -H "Content-Type: application/json" \\',
|
|
218
|
+
' -d \'{"query":"mutation { attachmentCreate(input: { issueId: \\"$ISSUE_ID\\", title: \\"Pull Request\\", url: \\"$PR_URL\\" }) { success } }"}\'',
|
|
219
|
+
'```',
|
|
220
|
+
].join('\n'),
|
|
221
|
+
agentComment: [
|
|
222
|
+
'Post a comment on Linear with key findings:',
|
|
223
|
+
'```',
|
|
224
|
+
'curl -s -X POST https://api.linear.app/graphql \\',
|
|
225
|
+
' -H "Authorization: $LINEAR_API_KEY" -H "Content-Type: application/json" \\',
|
|
226
|
+
' -d \'{"query":"mutation { commentCreate(input: { issueId: \\"$ISSUE_ID\\", body: \\"<COMMENT>\\" }) { success } }"}\'',
|
|
227
|
+
'```',
|
|
228
|
+
].join('\n'),
|
|
229
|
+
transitionToReview: [
|
|
230
|
+
'Transition to "In Review" on Linear:',
|
|
231
|
+
'```',
|
|
232
|
+
'curl -s -X POST https://api.linear.app/graphql \\',
|
|
233
|
+
' -H "Authorization: $LINEAR_API_KEY" -H "Content-Type: application/json" \\',
|
|
234
|
+
' -d \'{"query":"mutation { issueUpdate(id: \\"$ISSUE_ID\\", input: { stateId: \\"$IN_REVIEW_STATE_ID\\" }) { success } }"}\'',
|
|
235
|
+
'```',
|
|
236
|
+
'Find the "In Review" state ID from workflowStates first.',
|
|
237
|
+
].join('\n'),
|
|
238
|
+
postSummary: [
|
|
239
|
+
'Post final summary comment on Linear (use commentCreate mutation above).',
|
|
240
|
+
].join('\n'),
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const baseUrl = config?.jira?.baseUrl || '$JIRA_BASE_URL';
|
|
245
|
+
if (tracker === "jira") {
|
|
246
|
+
return {
|
|
247
|
+
postPrLink: [
|
|
248
|
+
'Post PR link on Jira as a remote link:',
|
|
249
|
+
'```bash',
|
|
250
|
+
`curl -s -X POST -u "$JIRA_EMAIL:$JIRA_API_TOKEN" -H "Content-Type: application/json" \\`,
|
|
251
|
+
` "${baseUrl}/rest/api/3/issue/$TASK_ID/remotelink" \\`,
|
|
252
|
+
` -d '{"object":{"url":"$PR_URL","title":"Pull Request"}}'`,
|
|
253
|
+
'```',
|
|
254
|
+
].join('\n'),
|
|
255
|
+
agentComment: [
|
|
256
|
+
'Post a comment on Jira with key findings:',
|
|
257
|
+
'```bash',
|
|
258
|
+
`curl -s -X POST -u "$JIRA_EMAIL:$JIRA_API_TOKEN" -H "Content-Type: application/json" \\`,
|
|
259
|
+
` "${baseUrl}/rest/api/3/issue/$TASK_ID/comment" \\`,
|
|
260
|
+
` -d '{"body":{"type":"doc","version":1,"content":[{"type":"paragraph","content":[{"type":"text","text":"<COMMENT>"}]}]}}'`,
|
|
261
|
+
'```',
|
|
262
|
+
].join('\n'),
|
|
263
|
+
transitionToReview: [
|
|
264
|
+
'Transition task to "In Review" on Jira:',
|
|
265
|
+
'```bash',
|
|
266
|
+
`# First get available transitions:`,
|
|
267
|
+
`curl -s -u "$JIRA_EMAIL:$JIRA_API_TOKEN" "${baseUrl}/rest/api/3/issue/$TASK_ID/transitions"`,
|
|
268
|
+
`# Then transition (find the "In Review" transition ID):`,
|
|
269
|
+
`curl -s -X POST -u "$JIRA_EMAIL:$JIRA_API_TOKEN" -H "Content-Type: application/json" \\`,
|
|
270
|
+
` "${baseUrl}/rest/api/3/issue/$TASK_ID/transitions" \\`,
|
|
271
|
+
` -d '{"transition":{"id":"<TRANSITION_ID>"}}'`,
|
|
272
|
+
'```',
|
|
273
|
+
].join('\n'),
|
|
274
|
+
postSummary: [
|
|
275
|
+
`Post final summary comment on Jira (use comment endpoint above).`,
|
|
276
|
+
].join('\n'),
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if (tracker === "github") {
|
|
281
|
+
return {
|
|
282
|
+
postPrLink: [
|
|
283
|
+
'Reference the issue in the PR body ("Closes #$TASK_ID") and post a comment:',
|
|
284
|
+
'```bash',
|
|
285
|
+
'gh issue comment $TASK_ID --body "PR created: $PR_URL"',
|
|
286
|
+
'```',
|
|
287
|
+
].join('\n'),
|
|
288
|
+
agentComment: [
|
|
289
|
+
'Post a comment on GitHub with key findings:',
|
|
290
|
+
'```bash',
|
|
291
|
+
'gh issue comment $TASK_ID --body "<COMMENT>"',
|
|
292
|
+
'```',
|
|
293
|
+
].join('\n'),
|
|
294
|
+
transitionToReview: [
|
|
295
|
+
'Add "in review" label on GitHub:',
|
|
296
|
+
'```bash',
|
|
297
|
+
'gh issue edit $TASK_ID --remove-label "in progress" --add-label "in review" 2>/dev/null || true',
|
|
298
|
+
'```',
|
|
299
|
+
].join('\n'),
|
|
300
|
+
postSummary: [
|
|
301
|
+
'Post final summary comment on GitHub:',
|
|
302
|
+
'```bash',
|
|
303
|
+
'gh issue comment $TASK_ID --body "<SUMMARY>"',
|
|
304
|
+
'```',
|
|
305
|
+
].join('\n'),
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
return null;
|
|
310
|
+
}
|
|
311
|
+
|
|
205
312
|
/**
|
|
206
313
|
* Generate the dynamic parts of the prompt from the team.
|
|
314
|
+
* @param {Array} team - resolved team array
|
|
315
|
+
* @param {Object} opts - optional { tracker, config } for tracker-specific execution steps
|
|
207
316
|
*/
|
|
208
|
-
export function generateTeamPrompt(team) {
|
|
317
|
+
export function generateTeamPrompt(team, opts = {}) {
|
|
209
318
|
const count = team.length;
|
|
210
319
|
const agentWord = count === 1 ? "one AI agent" : `${count} AI agents`;
|
|
211
320
|
|
|
@@ -246,15 +355,55 @@ export function generateTeamPrompt(team) {
|
|
|
246
355
|
`${a.badge} ${a.planning}`
|
|
247
356
|
).join("\n");
|
|
248
357
|
|
|
249
|
-
// Execution steps
|
|
358
|
+
// Execution steps — inject tracker-specific commands
|
|
359
|
+
const cmds = trackerCommands(opts.tracker, opts.config);
|
|
250
360
|
const execAgents = team
|
|
251
361
|
.filter(a => a.execution)
|
|
252
362
|
.sort((a, b) => a.execution.order - b.execution.order);
|
|
253
363
|
let stepNum = 1;
|
|
254
364
|
const execSteps = [];
|
|
255
365
|
for (const a of execAgents) {
|
|
256
|
-
|
|
257
|
-
|
|
366
|
+
let tasks = [...a.execution.tasks];
|
|
367
|
+
|
|
368
|
+
if (cmds) {
|
|
369
|
+
// Bart: expand PR link + tracker comment tasks with actual commands
|
|
370
|
+
if (a.name === "Bart") {
|
|
371
|
+
tasks = tasks.map(t => {
|
|
372
|
+
if (/PR link/i.test(t) || /Post PR.*tracker/i.test(t)) {
|
|
373
|
+
return `Push and create PR.\n ${cmds.postPrLink}`;
|
|
374
|
+
}
|
|
375
|
+
if (/screenshot.*tracker/i.test(t)) {
|
|
376
|
+
return `Post screenshots as separate tracker comment.\n ${cmds.agentComment.replace('<COMMENT>', 'Screenshots attached above.')}`;
|
|
377
|
+
}
|
|
378
|
+
return t;
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// Jane: expand transition + summary tasks
|
|
383
|
+
if (a.name === "Jane") {
|
|
384
|
+
tasks = tasks.map(t => {
|
|
385
|
+
if (/transition.*in review/i.test(t) || /move.*in review/i.test(t)) {
|
|
386
|
+
return `Transition task to "In Review".\n ${cmds.transitionToReview}`;
|
|
387
|
+
}
|
|
388
|
+
if (/summary.*tracker/i.test(t) || /final summary/i.test(t)) {
|
|
389
|
+
return `Post final summary comment on tracker.\n ${cmds.postSummary}`;
|
|
390
|
+
}
|
|
391
|
+
return t;
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// Dennis & Sam: expand tracker comment
|
|
396
|
+
if (a.name === "Dennis" || a.name === "Sam") {
|
|
397
|
+
const hasTrackerTask = tasks.some(t => /tracker/i.test(t) || /comment/i.test(t));
|
|
398
|
+
if (!hasTrackerTask) {
|
|
399
|
+
const findingsLabel = a.name === "Dennis" ? "Files changed, technical decisions" : "Architecture concerns or clean audit";
|
|
400
|
+
tasks.push(`Post comment on tracker with key findings (${findingsLabel}).\n ${cmds.agentComment}`);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
const taskList = tasks.map((t, i) => `${i + 1}. ${t}`).join("\n");
|
|
406
|
+
execSteps.push(`### Step ${stepNum} — ${a.name} ${a.execution.step.replace(/^[A-Za-z]+ /, "")}:\n${taskList}`);
|
|
258
407
|
stepNum++;
|
|
259
408
|
}
|
|
260
409
|
sections.executionSteps = execSteps.join("\n\n");
|
package/cli/daemon.mjs
CHANGED
|
@@ -365,7 +365,7 @@ export async function runDaemon() {
|
|
|
365
365
|
|
|
366
366
|
// Resolve team
|
|
367
367
|
const team = resolveTeam(config);
|
|
368
|
-
const teamSections = generateTeamPrompt(team);
|
|
368
|
+
const teamSections = generateTeamPrompt(team, { tracker, config });
|
|
369
369
|
|
|
370
370
|
const inboxUrl = `${agentdeskServer}/api/sessions/${sessionId}/inbox`;
|
|
371
371
|
const sessionUrl = `${agentdeskServer}/sessions/${sessionId}`;
|
package/cli/team.mjs
CHANGED
|
@@ -82,7 +82,7 @@ export async function runTeam(taskId, opts = {}) {
|
|
|
82
82
|
|
|
83
83
|
// Resolve team and generate dynamic prompt sections
|
|
84
84
|
const team = resolveTeam(config);
|
|
85
|
-
const teamSections = generateTeamPrompt(team);
|
|
85
|
+
const teamSections = generateTeamPrompt(team, { tracker, config });
|
|
86
86
|
|
|
87
87
|
// --- Verify tracker permissions before starting session ---
|
|
88
88
|
if (tracker) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kendoo.agentdesk/agentdesk",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.12",
|
|
4
4
|
"description": "AI team orchestrator for Claude Code — run collaborative agent sessions from your terminal",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"server": "node server/index.mjs",
|
|
22
22
|
"build": "vite build",
|
|
23
23
|
"preview": "vite preview",
|
|
24
|
-
"test": "node --test tests/server.test.mjs"
|
|
24
|
+
"test": "node --test tests/server.test.mjs tests/agents.test.mjs"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"bcryptjs": "^3.0.3",
|
package/prompts/team.md
CHANGED
|
@@ -231,6 +231,88 @@ Jane MUST post the session start comment and set "In Progress" before moving on.
|
|
|
231
231
|
|
|
232
232
|
Output on its own line: `SESSION_TITLE: <4-8 word title>`
|
|
233
233
|
|
|
234
|
+
### Decompose (if needed)
|
|
235
|
+
|
|
236
|
+
After assessing the task, Jane evaluates whether it is **too large for a single session**. Signs of a large task:
|
|
237
|
+
- Multiple independent workstreams or features
|
|
238
|
+
- Cross-cutting concerns spanning 3+ areas of the codebase
|
|
239
|
+
- Estimated effort exceeding what a team session can deliver in one run
|
|
240
|
+
- The description explicitly describes an epic, initiative, or multi-part feature
|
|
241
|
+
|
|
242
|
+
**If the task is small/focused** — skip decomposition, proceed to PLAN.
|
|
243
|
+
|
|
244
|
+
**If the task is large**, Jane decomposes it:
|
|
245
|
+
|
|
246
|
+
1. **Check current issue type.** If the task is already a subtask of another issue, do NOT decompose further — just work on it.
|
|
247
|
+
|
|
248
|
+
2. **Break the feature into subtasks.** Draft 3-8 subtasks that together would deliver the full feature. Each subtask should be independently deliverable.
|
|
249
|
+
|
|
250
|
+
3. **Classify each subtask:**
|
|
251
|
+
- `[basic]` — straightforward, low-risk, can be completed by the team in this session
|
|
252
|
+
- `[deferred]` — complex, needs user input, or depends on external factors
|
|
253
|
+
|
|
254
|
+
4. **Create subtasks in the tracker:**
|
|
255
|
+
|
|
256
|
+
{{#JIRA}}
|
|
257
|
+
For each subtask, create a Jira issue with the original task as parent:
|
|
258
|
+
```bash
|
|
259
|
+
curl -s -X POST -u "$JIRA_EMAIL:$JIRA_API_TOKEN" -H "Content-Type: application/json" \
|
|
260
|
+
"{{JIRA_BASE_URL}}/rest/api/3/issue" \
|
|
261
|
+
-d '{
|
|
262
|
+
"fields": {
|
|
263
|
+
"project": {"key": "<PROJECT_KEY>"},
|
|
264
|
+
"parent": {"key": "{{TASK_ID}}"},
|
|
265
|
+
"summary": "<subtask summary>",
|
|
266
|
+
"description": {"type":"doc","version":1,"content":[{"type":"paragraph","content":[{"type":"text","text":"<subtask description>"}]}]},
|
|
267
|
+
"issuetype": {"name": "Subtask"},
|
|
268
|
+
"labels": ["basic"] or ["deferred"]
|
|
269
|
+
}
|
|
270
|
+
}'
|
|
271
|
+
```
|
|
272
|
+
Note: Extract the project key from {{TASK_ID}} (the part before the hyphen).
|
|
273
|
+
{{/JIRA}}
|
|
274
|
+
|
|
275
|
+
{{#LINEAR}}
|
|
276
|
+
For each subtask, create a Linear sub-issue:
|
|
277
|
+
```
|
|
278
|
+
mutation {
|
|
279
|
+
issueCreate(input: {
|
|
280
|
+
teamId: "$TEAM_ID"
|
|
281
|
+
parentId: "$PARENT_ISSUE_ID"
|
|
282
|
+
title: "<subtask title>"
|
|
283
|
+
description: "<subtask description>"
|
|
284
|
+
labelIds: ["$BASIC_OR_DEFERRED_LABEL_ID"]
|
|
285
|
+
}) { success issue { id identifier title } }
|
|
286
|
+
}
|
|
287
|
+
```
|
|
288
|
+
First fetch the parent issue's teamId and id. Create "basic" and "deferred" labels if they don't exist.
|
|
289
|
+
{{/LINEAR}}
|
|
290
|
+
|
|
291
|
+
{{#GITHUB}}
|
|
292
|
+
GitHub doesn't support native subtasks. Instead:
|
|
293
|
+
- Edit the original issue body to add a task list:
|
|
294
|
+
```
|
|
295
|
+
## Subtasks
|
|
296
|
+
- [ ] [basic] Subtask 1 description
|
|
297
|
+
- [ ] [basic] Subtask 2 description
|
|
298
|
+
- [ ] [deferred] Subtask 3 description
|
|
299
|
+
```
|
|
300
|
+
- Create separate linked issues for each subtask with a `subtask` label:
|
|
301
|
+
```bash
|
|
302
|
+
gh issue create --title "[basic] Subtask title" --body "Parent: #{{TASK_ID}}\n\nDescription..." --label subtask
|
|
303
|
+
```
|
|
304
|
+
{{/GITHUB}}
|
|
305
|
+
|
|
306
|
+
{{#NO_TRACKER}}
|
|
307
|
+
Without a tracker, list the subtasks in the session output with their classification. The user can create tracker tasks manually.
|
|
308
|
+
{{/NO_TRACKER}}
|
|
309
|
+
|
|
310
|
+
5. **Post decomposition summary** on the tracker as a comment. List all subtasks with their classification.
|
|
311
|
+
|
|
312
|
+
6. **Announce to the team** which `[basic]` subtasks will be tackled in this session. The `[deferred]` subtasks are left for the user to schedule.
|
|
313
|
+
|
|
314
|
+
7. **Continue to PLAN** with only the `[basic]` subtasks in scope.
|
|
315
|
+
|
|
234
316
|
---
|
|
235
317
|
|
|
236
318
|
## PLAN (1-2 rounds max)
|