@kendoo.agentdesk/agentdesk 0.2.1 → 0.3.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 +24 -17
- package/bin/agentdesk.mjs +16 -19
- package/cli/agents.mjs +267 -0
- package/cli/config.mjs +6 -8
- package/cli/init.mjs +104 -17
- package/cli/team.mjs +1 -0
- package/package.json +6 -9
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# AgentDesk
|
|
2
2
|
|
|
3
|
-
AI team orchestrator
|
|
3
|
+
AI team orchestrator. Run collaborative agent sessions from your terminal and watch them live on [agentdesk.live](https://agentdesk.live).
|
|
4
4
|
|
|
5
5
|
AgentDesk spawns a team of 7 specialized AI agents that collaborate on your tasks:
|
|
6
6
|
|
|
@@ -22,36 +22,44 @@ AgentDesk spawns a team of 7 specialized AI agents that collaborate on your task
|
|
|
22
22
|
npm i -g @kendoo.agentdesk/agentdesk
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
Requires [Node.js](https://nodejs.org/) 18
|
|
25
|
+
Requires [Node.js](https://nodejs.org/) 18+.
|
|
26
26
|
|
|
27
|
-
### 2.
|
|
27
|
+
### 2. Sign in
|
|
28
28
|
|
|
29
29
|
```bash
|
|
30
30
|
agentdesk login
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
Opens your browser to [agentdesk.live](https://agentdesk.live) where you sign up or log in (email/password or Google). Your API key is saved automatically.
|
|
34
34
|
|
|
35
35
|
### 3. Set up your project
|
|
36
36
|
|
|
37
|
-
Navigate to your project and run init:
|
|
38
|
-
|
|
39
37
|
```bash
|
|
40
38
|
agentdesk init
|
|
41
39
|
```
|
|
42
40
|
|
|
41
|
+
This detects your project, asks which task tracker you use (Linear, Jira, GitHub Issues, or none), and saves the config to `.agentdesk.json`.
|
|
42
|
+
|
|
43
43
|
### 4. Run a team session
|
|
44
44
|
|
|
45
|
+
With a task tracker:
|
|
46
|
+
|
|
45
47
|
```bash
|
|
46
48
|
agentdesk team KEN-517
|
|
47
49
|
agentdesk team BUG-42 -d "Fix the checkout total calculation"
|
|
48
50
|
```
|
|
49
51
|
|
|
52
|
+
Without a tracker — pass requirements directly:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
agentdesk team add-auth -d "Add Google OAuth to the login page"
|
|
56
|
+
```
|
|
57
|
+
|
|
50
58
|
Open [agentdesk.live](https://agentdesk.live) to watch the session live.
|
|
51
59
|
|
|
52
60
|
## Configuration
|
|
53
61
|
|
|
54
|
-
|
|
62
|
+
`agentdesk init` creates `.agentdesk.json` in your project root. You can also edit it manually:
|
|
55
63
|
|
|
56
64
|
```json
|
|
57
65
|
{
|
|
@@ -65,8 +73,9 @@ Create `.agentdesk.json` in your project root to customize behavior:
|
|
|
65
73
|
| Tracker | Config |
|
|
66
74
|
|---------|--------|
|
|
67
75
|
| Linear | `{ "tracker": "linear", "linear": { "workspace": "..." } }` |
|
|
68
|
-
| Jira | `{ "tracker": "jira", "jira": { "baseUrl": "https://
|
|
76
|
+
| Jira | `{ "tracker": "jira", "jira": { "baseUrl": "https://company.atlassian.net" } }` |
|
|
69
77
|
| GitHub Issues | `{ "tracker": "github", "github": { "repo": "owner/repo" } }` |
|
|
78
|
+
| None | No config needed — pass `-d "description"` when running a session |
|
|
70
79
|
|
|
71
80
|
### Declaring project agents
|
|
72
81
|
|
|
@@ -87,12 +96,12 @@ If your project has existing AI agents, bots, or automation, declare them so the
|
|
|
87
96
|
|
|
88
97
|
AgentDesk also auto-discovers agents from `.claude/agents/`, `.claude/commands/`, `.mcp.json`, GitHub Actions workflows, Dependabot, and Renovate configs.
|
|
89
98
|
|
|
90
|
-
##
|
|
99
|
+
## Commands
|
|
91
100
|
|
|
92
101
|
```
|
|
93
|
-
agentdesk login
|
|
94
|
-
agentdesk logout
|
|
95
|
-
agentdesk init
|
|
102
|
+
agentdesk login Sign in to AgentDesk
|
|
103
|
+
agentdesk logout Sign out and remove credentials
|
|
104
|
+
agentdesk init Set up project and configure tracker
|
|
96
105
|
agentdesk team <TASK-ID> Run a team session on a task
|
|
97
106
|
agentdesk team <TASK-ID> -d "..." Run with a task description
|
|
98
107
|
```
|
|
@@ -101,19 +110,17 @@ agentdesk team <TASK-ID> -d "..." Run with a task description
|
|
|
101
110
|
|
|
102
111
|
| Flag | Description |
|
|
103
112
|
|------|-------------|
|
|
104
|
-
| `--description`, `-d` | Task description
|
|
113
|
+
| `--description`, `-d` | Task description or requirements |
|
|
105
114
|
| `--cwd` | Working directory (defaults to current) |
|
|
106
115
|
|
|
107
116
|
## How It Works
|
|
108
117
|
|
|
109
118
|
1. You run `agentdesk team TASK-ID` in your project directory
|
|
110
119
|
2. AgentDesk detects your project type, reads `CLAUDE.md`, and discovers existing agents
|
|
111
|
-
3.
|
|
112
|
-
4. The
|
|
113
|
-
5. The session streams live to [agentdesk.live](https://agentdesk.live) where you can watch and send messages to the team
|
|
120
|
+
3. 7 AI agents collaborate on your task in structured phases: **Brainstorm** > **Planning** > **Execution** > **Review**
|
|
121
|
+
4. The session streams live to [agentdesk.live](https://agentdesk.live) where you can watch and send messages to the team
|
|
114
122
|
|
|
115
123
|
## Requirements
|
|
116
124
|
|
|
117
125
|
- [Node.js](https://nodejs.org/) 18+
|
|
118
|
-
- [Claude Code](https://claude.ai/claude-code) CLI installed and authenticated
|
|
119
126
|
- An AgentDesk account at [agentdesk.live](https://agentdesk.live)
|
package/bin/agentdesk.mjs
CHANGED
|
@@ -1,42 +1,39 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
// AgentDesk CLI — AI team orchestrator
|
|
3
|
+
// AgentDesk CLI — AI team orchestrator
|
|
4
4
|
|
|
5
5
|
const args = process.argv.slice(2);
|
|
6
6
|
const command = args[0];
|
|
7
7
|
|
|
8
8
|
if (!command || command === "help" || command === "--help") {
|
|
9
9
|
console.log(`
|
|
10
|
-
AgentDesk — AI team orchestrator
|
|
10
|
+
AgentDesk — AI team orchestrator
|
|
11
11
|
|
|
12
12
|
Getting started:
|
|
13
|
-
1.
|
|
14
|
-
2.
|
|
15
|
-
3.
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
agentdesk login
|
|
19
|
-
agentdesk logout
|
|
20
|
-
agentdesk init
|
|
13
|
+
1. agentdesk login Sign in to your account
|
|
14
|
+
2. agentdesk init Set up your project (choose tracker, save config)
|
|
15
|
+
3. agentdesk team TASK-123 Run a team session
|
|
16
|
+
|
|
17
|
+
Commands:
|
|
18
|
+
agentdesk login Sign in to AgentDesk
|
|
19
|
+
agentdesk logout Sign out and remove credentials
|
|
20
|
+
agentdesk init Set up project and configure tracker
|
|
21
21
|
agentdesk team <TASK-ID> Run a team session on a task
|
|
22
22
|
agentdesk team <TASK-ID> -d "..." Run with a task description
|
|
23
23
|
|
|
24
24
|
Options:
|
|
25
|
-
--description, -d Task description
|
|
25
|
+
--description, -d Task description or requirements
|
|
26
26
|
--cwd Working directory (defaults to current)
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
"linear": { "workspace": "..." } // for task links in the dashboard
|
|
33
|
-
}
|
|
28
|
+
Task trackers:
|
|
29
|
+
agentdesk init configures your tracker (Linear, Jira, GitHub Issues).
|
|
30
|
+
Without a tracker, pass requirements directly:
|
|
31
|
+
agentdesk team my-feature -d "Add dark mode support"
|
|
34
32
|
|
|
35
33
|
Examples:
|
|
36
|
-
agentdesk login
|
|
37
|
-
agentdesk init
|
|
38
34
|
agentdesk team KEN-517
|
|
39
35
|
agentdesk team BUG-42 -d "Fix the checkout total calculation"
|
|
36
|
+
agentdesk team add-auth -d "Add Google OAuth to the login page"
|
|
40
37
|
|
|
41
38
|
Dashboard: https://agentdesk.live
|
|
42
39
|
`);
|
package/cli/agents.mjs
ADDED
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
// Agent registry — defines all built-in agents and generates prompt sections
|
|
2
|
+
|
|
3
|
+
export const BUILT_IN_AGENTS = {
|
|
4
|
+
Jane: {
|
|
5
|
+
badge: "●● JANE ●●",
|
|
6
|
+
role: "Product Analyst / Team Lead",
|
|
7
|
+
description: "facilitates discussion, clarifies requirements, keeps the team focused, evaluates team performance at the end",
|
|
8
|
+
groundRules: "Jane resolves disagreements and keeps the discussion productive. She NEVER reads code, runs tools, or inspects files — that's the developer and auditor's job. She focuses on requirements, user impact, scope, and coordination.",
|
|
9
|
+
brainstorm: { tag: "SAY", focus: "facilitation, question, or summary" },
|
|
10
|
+
planning: "Requirements Summary (what we're building, acceptance criteria, scope boundaries)",
|
|
11
|
+
execution: {
|
|
12
|
+
step: "Jane wraps up",
|
|
13
|
+
tasks: [
|
|
14
|
+
"Review the PR description. Ensure it explains what changed and why.",
|
|
15
|
+
"Summarize the session.",
|
|
16
|
+
],
|
|
17
|
+
order: 99, // last
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
Dennis: {
|
|
21
|
+
badge: "■■ DENNIS ■■",
|
|
22
|
+
role: "Senior Developer",
|
|
23
|
+
description: "assesses technical feasibility, proposes architecture, implements the solution",
|
|
24
|
+
groundRules: "Dennis must verify at least 1 assumption about the codebase before agreeing to any approach.",
|
|
25
|
+
codePrinciple: "Never write logic inline inside components or UI templates. Extract all conditionals, transformations, calculations, and API calls into dedicated functions, hooks, or services.",
|
|
26
|
+
brainstorm: { tag: "THINK", focus: "technical feasibility, existing patterns" },
|
|
27
|
+
planning: "Implementation Plan (files to modify, approach, complexity S/M/L)",
|
|
28
|
+
execution: {
|
|
29
|
+
step: "Dennis implements",
|
|
30
|
+
tasks: [
|
|
31
|
+
"Create a branch following project conventions.",
|
|
32
|
+
"Implement the changes according to the agreed plan.",
|
|
33
|
+
"Run linter and build to verify.",
|
|
34
|
+
"Commit the implementation.",
|
|
35
|
+
],
|
|
36
|
+
order: 1,
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
Sam: {
|
|
40
|
+
badge: "◆◆ SAM ◆◆",
|
|
41
|
+
role: "Architecture Auditor",
|
|
42
|
+
description: "scans for separation-of-concerns violations, guards code architecture",
|
|
43
|
+
groundRules: "Sam must verify that the proposed approach does not introduce architecture violations. He always backs claims with a file reference or line number.",
|
|
44
|
+
codePrinciple: "Actively guards separation of concerns throughout the session. Always cites file and line number.",
|
|
45
|
+
brainstorm: { tag: "THINK", focus: "codebase patterns, architecture risks" },
|
|
46
|
+
planning: "Architecture Review (existing violations, whether approach is clean)",
|
|
47
|
+
execution: {
|
|
48
|
+
step: "Sam scans for violations",
|
|
49
|
+
tasks: [
|
|
50
|
+
"Read all files the developer changed.",
|
|
51
|
+
"Check for architecture violations.",
|
|
52
|
+
"If violations found, the developer fixes them before proceeding.",
|
|
53
|
+
],
|
|
54
|
+
order: 2,
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
Bart: {
|
|
58
|
+
badge: "▲▲ BART ▲▲",
|
|
59
|
+
role: "QA Engineer",
|
|
60
|
+
description: "identifies edge cases, test plans, acceptance criteria, quality risks",
|
|
61
|
+
groundRules: "Bart must identify at least 2 risks or edge cases before agreeing to any plan.",
|
|
62
|
+
codePrinciple: "Flag any logic written directly inside a component or UI template.",
|
|
63
|
+
brainstorm: { tag: "SAY", focus: "risks, edge cases, \"what happens when...\"" },
|
|
64
|
+
planning: "Test Plan (acceptance criteria, key test cases, edge cases)",
|
|
65
|
+
execution: {
|
|
66
|
+
step: "Bart reviews",
|
|
67
|
+
tasks: [
|
|
68
|
+
"Read EVERY file the developer changed.",
|
|
69
|
+
"Check calculations, edge cases, error handling.",
|
|
70
|
+
"Run linter and build.",
|
|
71
|
+
"If ALL criteria pass, push and create PR: `gh pr create --title \"...\" --body \"...\"`",
|
|
72
|
+
"Approve the PR if clean.",
|
|
73
|
+
],
|
|
74
|
+
order: 4,
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
Vera: {
|
|
78
|
+
badge: "◈◈ VERA ◈◈",
|
|
79
|
+
role: "Test Engineer",
|
|
80
|
+
description: "writes unit and regression tests for changed code, ensures test coverage",
|
|
81
|
+
groundRules: "Vera must identify which functions need unit test coverage before agreeing to any plan.",
|
|
82
|
+
codePrinciple: "Never mount or render UI to test a logic outcome. Test files must mirror the service/utility structure.",
|
|
83
|
+
brainstorm: { tag: "SAY", focus: "test coverage gaps, which functions need tests" },
|
|
84
|
+
planning: "Test Plan (which functions need tests, regression tests)",
|
|
85
|
+
execution: {
|
|
86
|
+
step: "Vera writes tests",
|
|
87
|
+
tasks: [
|
|
88
|
+
"Read every file the developer changed. Identify testable functions.",
|
|
89
|
+
"Write unit tests following existing test patterns.",
|
|
90
|
+
"Run tests to verify they pass.",
|
|
91
|
+
"Commit test files.",
|
|
92
|
+
],
|
|
93
|
+
order: 3,
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
Luna: {
|
|
97
|
+
badge: "☾☾ LUNA ☾☾",
|
|
98
|
+
role: "UX/UI Designer",
|
|
99
|
+
description: "designs pixel-perfect interfaces, champions user experience, applies psychology of user behavior, ensures visual consistency, accessibility, and intuitive interaction patterns",
|
|
100
|
+
groundRules: "Luna must review any UI changes for visual consistency, spacing, color harmony, accessibility (contrast, focus states), and intuitive interaction flow. She references specific components, screenshots, or design patterns.",
|
|
101
|
+
codePrinciple: "Reviews all UI changes for visual hierarchy, whitespace balance, color consistency, typography, responsive behavior, and accessibility (WCAG). Pushes back on cluttered layouts, inconsistent spacing, poor contrast, or confusing interaction flows. Proposes specific CSS/styling improvements with exact values.",
|
|
102
|
+
brainstorm: { tag: "THINK", focus: "UX/UI impact, visual consistency, interaction patterns, accessibility" },
|
|
103
|
+
planning: "UX Review (visual impact, layout concerns, accessibility checklist, interaction improvements)",
|
|
104
|
+
execution: {
|
|
105
|
+
step: "Luna reviews UI changes (if applicable)",
|
|
106
|
+
tasks: [
|
|
107
|
+
"Read any changed component, page, or style files.",
|
|
108
|
+
"Check visual hierarchy, spacing consistency, color harmony, typography.",
|
|
109
|
+
"Verify accessibility: contrast ratios, focus states, keyboard navigation, screen reader labels.",
|
|
110
|
+
"Check responsive behavior and interaction flow.",
|
|
111
|
+
"If issues found, propose specific fixes (exact CSS values, spacing, colors). The developer implements them before proceeding.",
|
|
112
|
+
"Skip this step if the task has no UI impact.",
|
|
113
|
+
],
|
|
114
|
+
order: 2.1,
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
Mark: {
|
|
118
|
+
badge: "✦✦ MARK ✦✦",
|
|
119
|
+
role: "Content Writer",
|
|
120
|
+
description: "crafts precise, engaging copy for UI text, error messages, tooltips, onboarding flows, and documentation. Simplifies technical language into friendly, clear wording. Ensures consistent tone and voice across the product",
|
|
121
|
+
groundRules: "Mark must review all user-facing text — labels, buttons, headings, error messages, tooltips, empty states, confirmation dialogs. He ensures the tone is friendly and consistent, wording is concise, and technical jargon is avoided unless the audience is technical.",
|
|
122
|
+
codePrinciple: "Reviews all user-facing strings in changed files. Rewrites vague, wordy, or technical copy into clear, concise, human-friendly language. Ensures consistent voice — no mixing formal and casual tone. Checks empty states, error messages, and confirmation dialogs for helpfulness.",
|
|
123
|
+
brainstorm: { tag: "THINK", focus: "copy clarity, tone, user-facing text quality" },
|
|
124
|
+
planning: "Content Review (user-facing text audit, tone, clarity, empty states, error messages)",
|
|
125
|
+
execution: {
|
|
126
|
+
step: "Mark reviews content (if applicable)",
|
|
127
|
+
tasks: [
|
|
128
|
+
"Read all changed files that contain user-facing text (components, templates, error handlers).",
|
|
129
|
+
"Check every label, button, heading, tooltip, error message, empty state, and confirmation dialog.",
|
|
130
|
+
"Rewrite anything vague, wordy, jargon-heavy, or inconsistent in tone.",
|
|
131
|
+
"Propose exact replacement strings. The developer implements them.",
|
|
132
|
+
"Skip this step if the task has no user-facing text changes.",
|
|
133
|
+
],
|
|
134
|
+
order: 2.2,
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Resolve the team from config.
|
|
141
|
+
* - If config.team is an array of names, use those built-in agents in that order.
|
|
142
|
+
* - If config.team contains objects, treat as custom agents.
|
|
143
|
+
* - Jane is always included (she's the lead).
|
|
144
|
+
* - Dennis is always included (someone has to implement).
|
|
145
|
+
*/
|
|
146
|
+
export function resolveTeam(config) {
|
|
147
|
+
const teamConfig = config.team || null;
|
|
148
|
+
|
|
149
|
+
// Default: all 7 agents
|
|
150
|
+
if (!teamConfig) {
|
|
151
|
+
return Object.entries(BUILT_IN_AGENTS).map(([name, agent]) => ({ name, ...agent }));
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const team = [];
|
|
155
|
+
for (const entry of teamConfig) {
|
|
156
|
+
if (typeof entry === "string") {
|
|
157
|
+
// Built-in agent by name
|
|
158
|
+
const agent = BUILT_IN_AGENTS[entry];
|
|
159
|
+
if (agent) {
|
|
160
|
+
team.push({ name: entry, ...agent });
|
|
161
|
+
} else {
|
|
162
|
+
console.warn(`Warning: Unknown agent "${entry}" — skipping`);
|
|
163
|
+
}
|
|
164
|
+
} else if (typeof entry === "object" && entry.name) {
|
|
165
|
+
// Custom agent
|
|
166
|
+
const builtin = BUILT_IN_AGENTS[entry.name];
|
|
167
|
+
if (builtin) {
|
|
168
|
+
// Override a built-in agent's role
|
|
169
|
+
team.push({ ...builtin, name: entry.name, ...entry, badge: builtin.badge });
|
|
170
|
+
} else {
|
|
171
|
+
// Fully custom agent
|
|
172
|
+
team.push({
|
|
173
|
+
name: entry.name,
|
|
174
|
+
badge: entry.badge || `** ${entry.name.toUpperCase()} **`,
|
|
175
|
+
role: entry.role || "Team Member",
|
|
176
|
+
description: entry.description || entry.role || "Custom team member",
|
|
177
|
+
groundRules: entry.groundRules || "",
|
|
178
|
+
brainstorm: entry.brainstorm || { tag: "SAY", focus: entry.role || "general input" },
|
|
179
|
+
planning: entry.planning || `${entry.role || entry.name} Review`,
|
|
180
|
+
execution: entry.execution || null,
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Ensure Jane and Dennis are always present
|
|
187
|
+
if (!team.find(a => a.name === "Jane")) {
|
|
188
|
+
team.unshift({ name: "Jane", ...BUILT_IN_AGENTS.Jane });
|
|
189
|
+
}
|
|
190
|
+
if (!team.find(a => a.name === "Dennis")) {
|
|
191
|
+
const jane = team.findIndex(a => a.name === "Jane");
|
|
192
|
+
team.splice(jane + 1, 0, { name: "Dennis", ...BUILT_IN_AGENTS.Dennis });
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return team;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Generate the dynamic parts of the prompt from the team.
|
|
200
|
+
*/
|
|
201
|
+
export function generateTeamPrompt(team) {
|
|
202
|
+
const count = team.length;
|
|
203
|
+
const agentWord = count === 1 ? "one AI agent" : `${count} AI agents`;
|
|
204
|
+
|
|
205
|
+
const sections = {};
|
|
206
|
+
|
|
207
|
+
// Agent list
|
|
208
|
+
sections.agentList = team.map(a =>
|
|
209
|
+
`- ${a.name} (${a.role}) — ${a.description}`
|
|
210
|
+
).join("\n");
|
|
211
|
+
|
|
212
|
+
// Speaking order
|
|
213
|
+
sections.speakingOrder = team.map((a, i) =>
|
|
214
|
+
`${i + 1}. ${a.badge}`
|
|
215
|
+
).join("\n");
|
|
216
|
+
|
|
217
|
+
// Ground rules
|
|
218
|
+
const rules = [];
|
|
219
|
+
rules.push(`1. Each agent speaks in turn, prefixed with their badge (e.g., "${team[0].badge} Starting.").`);
|
|
220
|
+
rules.push(`2. ALL text output MUST be prefixed with the acting agent's badge. Never output unprefixed text.`);
|
|
221
|
+
rules.push(`3. Agents should DISAGREE when they see problems — don't rubber-stamp each other.`);
|
|
222
|
+
let ruleNum = 4;
|
|
223
|
+
for (const a of team) {
|
|
224
|
+
if (a.groundRules) {
|
|
225
|
+
rules.push(`${ruleNum}. ${a.groundRules}`);
|
|
226
|
+
ruleNum++;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
rules.push(`${ruleNum}. Every statement should add value — no filler, no repeating what someone else already said.`);
|
|
230
|
+
sections.groundRules = rules.join("\n");
|
|
231
|
+
|
|
232
|
+
// Code principles
|
|
233
|
+
const principles = team
|
|
234
|
+
.filter(a => a.codePrinciple)
|
|
235
|
+
.map(a => `- ${a.name}: ${a.codePrinciple}`);
|
|
236
|
+
sections.codePrinciples = principles.length > 0 ? principles.join("\n") : "";
|
|
237
|
+
|
|
238
|
+
// Brainstorm order
|
|
239
|
+
sections.brainstormOrder = team.map((a, i) =>
|
|
240
|
+
`${i + 1}. ${a.badge} [${a.brainstorm.tag}] ${a.brainstorm.focus}`
|
|
241
|
+
).join("\n");
|
|
242
|
+
|
|
243
|
+
// Planning presentations
|
|
244
|
+
sections.planningOrder = team.map(a =>
|
|
245
|
+
`${a.badge} ${a.planning}`
|
|
246
|
+
).join("\n");
|
|
247
|
+
|
|
248
|
+
// Execution steps
|
|
249
|
+
const execAgents = team
|
|
250
|
+
.filter(a => a.execution)
|
|
251
|
+
.sort((a, b) => a.execution.order - b.execution.order);
|
|
252
|
+
let stepNum = 1;
|
|
253
|
+
const execSteps = [];
|
|
254
|
+
for (const a of execAgents) {
|
|
255
|
+
const tasks = a.execution.tasks.map((t, i) => `${i + 1}. ${t}`).join("\n");
|
|
256
|
+
execSteps.push(`### Step ${stepNum} — ${a.name} ${a.execution.step.replace(/^[A-Za-z]+ /, "")}:\n${tasks}`);
|
|
257
|
+
stepNum++;
|
|
258
|
+
}
|
|
259
|
+
sections.executionSteps = execSteps.join("\n\n");
|
|
260
|
+
|
|
261
|
+
// Count and phrasing
|
|
262
|
+
sections.count = count;
|
|
263
|
+
sections.agentWord = agentWord;
|
|
264
|
+
sections.names = team.map(a => a.name);
|
|
265
|
+
|
|
266
|
+
return sections;
|
|
267
|
+
}
|
package/cli/config.mjs
CHANGED
|
@@ -21,14 +21,12 @@ const DEFAULTS = {
|
|
|
21
21
|
repo: null, // e.g., "owner/repo" — auto-detected from git if null
|
|
22
22
|
},
|
|
23
23
|
|
|
24
|
-
//
|
|
25
|
-
agents:
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
Sam: { role: "Architecture Auditor" },
|
|
31
|
-
},
|
|
24
|
+
// Team composition — array of agent names or custom agent objects
|
|
25
|
+
// Default: all 7 agents. To customize:
|
|
26
|
+
// "team": ["Jane", "Dennis", "Sam", "Bart"] — 4-agent team (no Luna, Mark, Vera)
|
|
27
|
+
// "team": ["Jane", "Dennis", "Sam", {"name": "Alex", "role": "DevOps Engineer", "description": "..."}]
|
|
28
|
+
// Jane and Dennis are always included even if omitted.
|
|
29
|
+
team: null,
|
|
32
30
|
|
|
33
31
|
// Commands — auto-detected if null
|
|
34
32
|
commands: {
|
package/cli/init.mjs
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
// `agentdesk init` —
|
|
1
|
+
// `agentdesk init` — interactive project setup with tracker configuration
|
|
2
2
|
|
|
3
|
-
import { existsSync, readFileSync } from "fs";
|
|
3
|
+
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
4
4
|
import { join } from "path";
|
|
5
|
+
import { createInterface } from "readline";
|
|
5
6
|
import { detectProject } from "./detect.mjs";
|
|
6
7
|
import { loadConfig } from "./config.mjs";
|
|
7
8
|
import { getStoredApiKey } from "./login.mjs";
|
|
@@ -15,11 +16,34 @@ function loadApiKey(dir) {
|
|
|
15
16
|
return match?.[1]?.trim() || getStoredApiKey();
|
|
16
17
|
}
|
|
17
18
|
|
|
19
|
+
function ask(rl, question) {
|
|
20
|
+
return new Promise(resolve => rl.question(question, resolve));
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async function selectOption(rl, prompt, options) {
|
|
24
|
+
console.log(` ${prompt}`);
|
|
25
|
+
console.log("");
|
|
26
|
+
options.forEach((opt, i) => {
|
|
27
|
+
console.log(` ${i + 1}) ${opt.label}`);
|
|
28
|
+
});
|
|
29
|
+
console.log("");
|
|
30
|
+
|
|
31
|
+
while (true) {
|
|
32
|
+
const answer = await ask(rl, ` Choose (1-${options.length}): `);
|
|
33
|
+
const num = parseInt(answer.trim(), 10);
|
|
34
|
+
if (num >= 1 && num <= options.length) return options[num - 1];
|
|
35
|
+
console.log(` Please enter a number between 1 and ${options.length}`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
18
39
|
export async function runInit(cwd) {
|
|
19
40
|
const project = detectProject(cwd);
|
|
20
|
-
const
|
|
21
|
-
const tracker = config.tracker || (project.hasLinear ? "linear" : null);
|
|
41
|
+
const existingConfig = loadConfig(cwd);
|
|
22
42
|
const projectId = project.name || cwd.split("/").pop();
|
|
43
|
+
const configPath = join(cwd, ".agentdesk.json");
|
|
44
|
+
const hasConfig = existsSync(configPath);
|
|
45
|
+
|
|
46
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
23
47
|
|
|
24
48
|
console.log("");
|
|
25
49
|
console.log(" AgentDesk — Project Setup");
|
|
@@ -28,9 +52,8 @@ export async function runInit(cwd) {
|
|
|
28
52
|
console.log(` Project: ${project.name || "unknown"}`);
|
|
29
53
|
console.log(` Type: ${project.label}`);
|
|
30
54
|
console.log(` Directory: ${project.dir}`);
|
|
31
|
-
console.log(` Git: ${project.hasGit ? "
|
|
32
|
-
console.log(` CLAUDE.md: ${project.hasClaudeMd ? "
|
|
33
|
-
console.log(` Tracker: ${tracker || "— (none)"}`);
|
|
55
|
+
console.log(` Git: ${project.hasGit ? "yes" : "no"}`);
|
|
56
|
+
console.log(` CLAUDE.md: ${project.hasClaudeMd ? "yes" : "no (recommended)"}`);
|
|
34
57
|
console.log("");
|
|
35
58
|
|
|
36
59
|
if (project.testCommand) console.log(` Test: ${project.testCommand}`);
|
|
@@ -38,7 +61,68 @@ export async function runInit(cwd) {
|
|
|
38
61
|
if (project.lintCommand) console.log(` Lint: ${project.lintCommand}`);
|
|
39
62
|
if (project.testCommand || project.buildCommand || project.lintCommand) console.log("");
|
|
40
63
|
|
|
41
|
-
//
|
|
64
|
+
// --- Tracker selection ---
|
|
65
|
+
const trackerOptions = [
|
|
66
|
+
{ label: "Linear", value: "linear" },
|
|
67
|
+
{ label: "Jira", value: "jira" },
|
|
68
|
+
{ label: "GitHub Issues", value: "github" },
|
|
69
|
+
{ label: "None (use descriptions only)", value: null },
|
|
70
|
+
];
|
|
71
|
+
|
|
72
|
+
const selected = await selectOption(rl, "Task tracker:", trackerOptions);
|
|
73
|
+
const tracker = selected.value;
|
|
74
|
+
console.log("");
|
|
75
|
+
|
|
76
|
+
// Build config
|
|
77
|
+
const config = {};
|
|
78
|
+
if (tracker) config.tracker = tracker;
|
|
79
|
+
|
|
80
|
+
if (tracker === "linear") {
|
|
81
|
+
const current = existingConfig.linear?.workspace || "";
|
|
82
|
+
const answer = await ask(rl, ` Linear workspace slug${current ? ` (${current})` : ""}: `);
|
|
83
|
+
const ws = answer.trim() || current;
|
|
84
|
+
if (ws) config.linear = { workspace: ws };
|
|
85
|
+
console.log("");
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (tracker === "jira") {
|
|
89
|
+
const current = existingConfig.jira?.baseUrl || "";
|
|
90
|
+
const answer = await ask(rl, ` Jira base URL${current ? ` (${current})` : ""}: `);
|
|
91
|
+
const url = answer.trim() || current;
|
|
92
|
+
if (url) config.jira = { baseUrl: url };
|
|
93
|
+
console.log("");
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (tracker === "github") {
|
|
97
|
+
const current = existingConfig.github?.repo || "";
|
|
98
|
+
const answer = await ask(rl, ` GitHub repo${current ? ` (${current})` : ""} (owner/repo): `);
|
|
99
|
+
const r = answer.trim() || current;
|
|
100
|
+
if (r) config.github = { repo: r };
|
|
101
|
+
console.log("");
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// --- Save .agentdesk.json ---
|
|
105
|
+
let merged = {};
|
|
106
|
+
if (hasConfig) {
|
|
107
|
+
try { merged = JSON.parse(readFileSync(configPath, "utf-8")); } catch {}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (tracker) {
|
|
111
|
+
merged.tracker = tracker;
|
|
112
|
+
if (config.linear) merged.linear = config.linear;
|
|
113
|
+
if (config.jira) merged.jira = config.jira;
|
|
114
|
+
if (config.github) merged.github = config.github;
|
|
115
|
+
} else {
|
|
116
|
+
delete merged.tracker;
|
|
117
|
+
delete merged.linear;
|
|
118
|
+
delete merged.jira;
|
|
119
|
+
delete merged.github;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
writeFileSync(configPath, JSON.stringify(merged, null, 2) + "\n");
|
|
123
|
+
console.log(` Saved .agentdesk.json`);
|
|
124
|
+
|
|
125
|
+
// --- Register with server ---
|
|
42
126
|
try {
|
|
43
127
|
const res = await fetch(`${SERVER}/api/projects`, {
|
|
44
128
|
method: "POST",
|
|
@@ -55,24 +139,27 @@ export async function runInit(cwd) {
|
|
|
55
139
|
}),
|
|
56
140
|
});
|
|
57
141
|
if (res.ok) {
|
|
58
|
-
console.log("
|
|
59
|
-
} else {
|
|
60
|
-
console.log(" ⚠ AgentDesk server not available — project will register on first session");
|
|
142
|
+
console.log(" Registered with agentdesk.live");
|
|
61
143
|
}
|
|
62
144
|
} catch {
|
|
63
|
-
|
|
145
|
+
// Server not available
|
|
64
146
|
}
|
|
65
147
|
|
|
66
148
|
console.log("");
|
|
67
|
-
console.log(" Ready
|
|
149
|
+
console.log(" Ready! Run a team session:");
|
|
68
150
|
console.log("");
|
|
69
|
-
|
|
70
|
-
|
|
151
|
+
if (tracker) {
|
|
152
|
+
console.log(` agentdesk team TASK-123`);
|
|
153
|
+
console.log(` agentdesk team TASK-123 -d "Optional extra context"`);
|
|
154
|
+
} else {
|
|
155
|
+
console.log(` agentdesk team my-feature -d "Add dark mode support"`);
|
|
156
|
+
}
|
|
71
157
|
console.log("");
|
|
72
158
|
|
|
73
159
|
if (!project.hasClaudeMd) {
|
|
74
|
-
console.log(" Tip: Create a CLAUDE.md
|
|
75
|
-
console.log(" understand your codebase conventions, architecture, and commands.");
|
|
160
|
+
console.log(" Tip: Create a CLAUDE.md to help the agents understand your codebase.");
|
|
76
161
|
console.log("");
|
|
77
162
|
}
|
|
163
|
+
|
|
164
|
+
rl.close();
|
|
78
165
|
}
|
package/cli/team.mjs
CHANGED
|
@@ -10,6 +10,7 @@ import WebSocket from "ws";
|
|
|
10
10
|
import { detectProject, generateContext } from "./detect.mjs";
|
|
11
11
|
import { loadConfig } from "./config.mjs";
|
|
12
12
|
import { getStoredApiKey } from "./login.mjs";
|
|
13
|
+
import { resolveTeam, generateTeamPrompt } from "./agents.mjs";
|
|
13
14
|
|
|
14
15
|
function loadDotEnv(dir) {
|
|
15
16
|
const envPath = join(dir, ".env");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kendoo.agentdesk/agentdesk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "AI team orchestrator for Claude Code — run collaborative agent sessions from your terminal",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -24,25 +24,22 @@
|
|
|
24
24
|
"test": "node --test tests/server.test.mjs"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
+
"bcryptjs": "^3.0.3",
|
|
28
|
+
"express": "^5.1.0",
|
|
29
|
+
"jsonwebtoken": "^9.0.3",
|
|
30
|
+
"sql.js": "^1.14.1",
|
|
27
31
|
"ws": "^8.18.0"
|
|
28
32
|
},
|
|
29
33
|
"devDependencies": {
|
|
30
|
-
"@logto/react": "^4.0.13",
|
|
31
34
|
"@vitejs/plugin-react": "^4.5.2",
|
|
32
35
|
"autoprefixer": "^10.4.21",
|
|
33
|
-
"bcryptjs": "^3.0.3",
|
|
34
36
|
"concurrently": "^9.2.0",
|
|
35
|
-
"express": "^5.1.0",
|
|
36
|
-
"jose": "^6.2.2",
|
|
37
|
-
"jsonwebtoken": "^9.0.3",
|
|
38
37
|
"lucide-react": "^0.577.0",
|
|
39
38
|
"postcss": "^8.5.4",
|
|
40
39
|
"react": "^19.1.0",
|
|
41
40
|
"react-dom": "^19.1.0",
|
|
42
|
-
"sql.js": "^1.14.1",
|
|
43
41
|
"tailwindcss": "^3.4.17",
|
|
44
|
-
"vite": "^6.3.5"
|
|
45
|
-
"ws": "^8.18.0"
|
|
42
|
+
"vite": "^6.3.5"
|
|
46
43
|
},
|
|
47
44
|
"keywords": [
|
|
48
45
|
"ai",
|