@kendoo.agentdesk/agentdesk 0.2.0 → 0.3.0

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 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
- // Agent customization
25
- agents: {
26
- Jane: { role: "Product Analyst / Team Lead" },
27
- Dennis: { role: "Senior Developer" },
28
- Bart: { role: "QA Engineer" },
29
- Vera: { role: "Test Engineer" },
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/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");
@@ -153,7 +154,18 @@ export async function runTeam(taskId, opts = {}) {
153
154
  }
154
155
  });
155
156
  vizWs.on("error", () => { vizConnected = false; });
156
- vizWs.on("close", () => { vizConnected = false; });
157
+ vizWs.on("close", (code) => {
158
+ vizConnected = false;
159
+ if (code === 1006 && !vizConnected) {
160
+ console.log("AgentDesk: not connected — run 'agentdesk login' to authenticate");
161
+ }
162
+ });
163
+ vizWs.on("unexpected-response", (_req, res) => {
164
+ if (res.statusCode === 401) {
165
+ console.log("AgentDesk: authentication required — run 'agentdesk login'");
166
+ }
167
+ vizConnected = false;
168
+ });
157
169
  } catch {
158
170
  // AgentDesk not running
159
171
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kendoo.agentdesk/agentdesk",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
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",