@kendoo.agentdesk/agentdesk 0.4.0 → 0.4.3

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/bin/agentdesk.mjs CHANGED
@@ -2,6 +2,27 @@
2
2
 
3
3
  // AgentDesk CLI — AI team orchestrator
4
4
 
5
+ import { readFileSync } from "fs";
6
+ import { fileURLToPath } from "url";
7
+ import { dirname, join } from "path";
8
+
9
+ const __filename = fileURLToPath(import.meta.url);
10
+ const __dirname = dirname(__filename);
11
+ const pkg = JSON.parse(readFileSync(join(__dirname, "../package.json"), "utf-8"));
12
+
13
+ // Non-blocking update check — runs in background, never delays startup
14
+ (async () => {
15
+ try {
16
+ const res = await fetch(`https://registry.npmjs.org/${pkg.name}/latest`, { signal: AbortSignal.timeout(3000) });
17
+ if (!res.ok) return;
18
+ const { version } = await res.json();
19
+ if (version !== pkg.version) {
20
+ console.log(`\n Update available: ${pkg.version} → ${version}`);
21
+ console.log(` Run: npm i -g ${pkg.name}\n`);
22
+ }
23
+ } catch {}
24
+ })();
25
+
5
26
  const args = process.argv.slice(2);
6
27
  const command = args[0];
7
28
 
package/cli/agents.mjs CHANGED
@@ -7,7 +7,7 @@ export const BUILT_IN_AGENTS = {
7
7
  description: "facilitates discussion, clarifies requirements, keeps the team focused, evaluates team performance at the end",
8
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
9
  brainstorm: { tag: "SAY", focus: "facilitation, question, or summary" },
10
- planning: "Requirements Summary (what we're building, acceptance criteria, scope boundaries)",
10
+ planning: "Requirements Summary (what we're building, acceptance criteria, scope boundaries). If this task involves UI changes, Jane declares it a UI task and tells Luna to define screenshot requirements.",
11
11
  execution: {
12
12
  step: "Jane wraps up",
13
13
  tasks: [
@@ -68,7 +68,9 @@ export const BUILT_IN_AGENTS = {
68
68
  "Read EVERY file the developer changed.",
69
69
  "Check calculations, edge cases, error handling.",
70
70
  "Run linter and build.",
71
+ "If this is a UI task, capture screenshots following Luna's screenshot plan (see SCREENSHOTS section in prompt).",
71
72
  "If ALL criteria pass, push and create PR: `gh pr create --title \"...\" --body \"...\"`",
73
+ "Post screenshots to the task tracker as a separate comment (not inside badge blocks).",
72
74
  "Approve the PR if clean.",
73
75
  ],
74
76
  order: 4,
@@ -100,7 +102,7 @@ export const BUILT_IN_AGENTS = {
100
102
  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
103
  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
104
  brainstorm: { tag: "THINK", focus: "UX/UI impact, visual consistency, interaction patterns, accessibility" },
103
- planning: "UX Review (visual impact, layout concerns, accessibility checklist, interaction improvements)",
105
+ planning: "UX Review (visual impact, layout concerns, accessibility checklist, interaction improvements). For UI tasks: define which pages/views need screenshots and whether desktop, mobile, or both are relevant.",
104
106
  execution: {
105
107
  step: "Luna reviews UI changes (if applicable)",
106
108
  tasks: [
@@ -109,6 +111,7 @@ export const BUILT_IN_AGENTS = {
109
111
  "Verify accessibility: contrast ratios, focus states, keyboard navigation, screen reader labels.",
110
112
  "Check responsive behavior and interaction flow.",
111
113
  "If issues found, propose specific fixes (exact CSS values, spacing, colors). The developer implements them before proceeding.",
114
+ "Define the screenshot plan: which pages/routes to capture and which viewports (desktop, mobile, or both). Hand this plan to Bart.",
112
115
  "Skip this step if the task has no UI impact.",
113
116
  ],
114
117
  order: 2.1,
@@ -0,0 +1,91 @@
1
+ #!/usr/bin/env node
2
+
3
+ // Lightweight screenshot utility — used by agents to capture UI changes
4
+ // Usage: node cli/screenshot.mjs <url> [--out <dir>] [--viewports desktop,mobile]
5
+ //
6
+ // Requires puppeteer — installed on demand via dynamic import if not present.
7
+ // Outputs file paths of captured screenshots.
8
+
9
+ import { mkdirSync, existsSync } from "fs";
10
+ import { join } from "path";
11
+
12
+ const VIEWPORTS = {
13
+ desktop: { width: 1280, height: 800, label: "desktop" },
14
+ mobile: { width: 375, height: 812, label: "mobile", isMobile: true },
15
+ };
16
+
17
+ function parseArgs(argv) {
18
+ const args = argv.slice(2);
19
+ const result = { url: null, out: "screenshots", viewports: ["desktop", "mobile"] };
20
+
21
+ for (let i = 0; i < args.length; i++) {
22
+ if (args[i] === "--out" && args[i + 1]) result.out = args[++i];
23
+ else if (args[i] === "--viewports" && args[i + 1]) result.viewports = args[++i].split(",");
24
+ else if (!args[i].startsWith("-")) result.url = args[i];
25
+ }
26
+
27
+ return result;
28
+ }
29
+
30
+ async function loadPuppeteer() {
31
+ try {
32
+ return await import("puppeteer");
33
+ } catch {
34
+ console.log("Installing puppeteer...");
35
+ const { execSync } = await import("child_process");
36
+ execSync("npm install --no-save puppeteer", { stdio: "inherit" });
37
+ return await import("puppeteer");
38
+ }
39
+ }
40
+
41
+ async function main() {
42
+ const { url, out, viewports } = parseArgs(process.argv);
43
+
44
+ if (!url) {
45
+ console.error("Usage: node cli/screenshot.mjs <url> [--out <dir>] [--viewports desktop,mobile]");
46
+ process.exit(1);
47
+ }
48
+
49
+ const puppeteer = await loadPuppeteer();
50
+ const browser = await puppeteer.default.launch({ headless: "new" });
51
+ const outDir = join(process.cwd(), out);
52
+ if (!existsSync(outDir)) mkdirSync(outDir, { recursive: true });
53
+
54
+ const files = [];
55
+
56
+ for (const vp of viewports) {
57
+ const config = VIEWPORTS[vp];
58
+ if (!config) {
59
+ console.warn(`Unknown viewport "${vp}" — skipping`);
60
+ continue;
61
+ }
62
+
63
+ const page = await browser.newPage();
64
+ await page.setViewport({ width: config.width, height: config.height });
65
+ if (config.isMobile) {
66
+ await page.emulate(puppeteer.default.devices["iPhone 13"]);
67
+ }
68
+
69
+ await page.goto(url, { waitUntil: "networkidle0", timeout: 30000 });
70
+ // Wait a bit for animations to settle
71
+ await new Promise(r => setTimeout(r, 1000));
72
+
73
+ const filename = `${config.label}-${Date.now()}.png`;
74
+ const filepath = join(outDir, filename);
75
+ await page.screenshot({ path: filepath, fullPage: true });
76
+ files.push(filepath);
77
+ console.log(`Captured ${config.label}: ${filepath}`);
78
+ await page.close();
79
+ }
80
+
81
+ await browser.close();
82
+
83
+ // Print paths for agents to use
84
+ console.log("\nScreenshot files:");
85
+ for (const f of files) console.log(f);
86
+ }
87
+
88
+ main().catch(err => {
89
+ console.error("Screenshot failed:", err.message);
90
+ process.exit(1);
91
+ });
package/cli/team.mjs CHANGED
@@ -139,8 +139,7 @@ export async function runTeam(taskId, opts = {}) {
139
139
  const projectEnv = loadDotEnv(cwd);
140
140
  const apiKey = projectEnv.AGENTDESK_API_KEY || process.env.AGENTDESK_API_KEY || getStoredApiKey() || null;
141
141
  const agentdeskServer = process.env.AGENTDESK_SERVER || "https://agentdesk.live";
142
- const baseUrl = process.env.AGENTDESK_URL || "wss://agentdesk.live/ws/agent";
143
- const AGENTDESK_URL = apiKey ? `${baseUrl}?api_key=${apiKey}` : baseUrl;
142
+ const AGENTDESK_URL = process.env.AGENTDESK_URL || "wss://agentdesk.live/ws/agent";
144
143
  const sessionId = `${taskId}-${randomUUID().slice(0, 8)}`;
145
144
  const inboxUrl = `${agentdeskServer}/api/sessions/${sessionId}/inbox`;
146
145
 
@@ -177,33 +176,40 @@ export async function runTeam(taskId, opts = {}) {
177
176
  try {
178
177
  vizWs = new WebSocket(AGENTDESK_URL);
179
178
  vizWs.on("open", () => {
180
- vizConnected = true;
181
- console.log("AgentDesk: connected");
182
- vizSend({
183
- type: "session:start",
184
- taskId,
185
- taskLink,
186
- title: description || taskId,
187
- project: project.name || null,
188
- sessionNumber: 1,
189
- agents: ["Jane", "Dennis", "Bart", "Vera", "Sam", "Luna", "Mark"],
190
- });
191
- while (vizQueue.length > 0 && vizWs.readyState === WebSocket.OPEN) {
192
- vizWs.send(vizQueue.shift());
179
+ // Authenticate via first message (proxy-safe — query params can be stripped)
180
+ if (apiKey) {
181
+ vizWs.send(JSON.stringify({ type: "auth", apiKey }));
182
+ }
183
+ });
184
+ vizWs.on("message", (raw) => {
185
+ let msg;
186
+ try { msg = JSON.parse(raw.toString()); } catch { return; }
187
+ if (msg.type === "auth:ok") {
188
+ vizConnected = true;
189
+ console.log("AgentDesk: connected");
190
+ vizSend({
191
+ type: "session:start",
192
+ taskId,
193
+ taskLink,
194
+ title: description || taskId,
195
+ project: project.name || null,
196
+ sessionNumber: 1,
197
+ agents: ["Jane", "Dennis", "Bart", "Vera", "Sam", "Luna", "Mark"],
198
+ });
199
+ while (vizQueue.length > 0 && vizWs.readyState === WebSocket.OPEN) {
200
+ vizWs.send(vizQueue.shift());
201
+ }
202
+ } else if (msg.type === "auth:error") {
203
+ console.log("AgentDesk: authentication failed — run 'agentdesk login'");
204
+ vizConnected = false;
193
205
  }
194
206
  });
195
207
  vizWs.on("error", () => { vizConnected = false; });
196
208
  vizWs.on("close", (code) => {
197
209
  vizConnected = false;
198
- if (code === 1006 && !vizConnected) {
199
- console.log("AgentDesk: not connected — run 'agentdesk login' to authenticate");
200
- }
201
- });
202
- vizWs.on("unexpected-response", (_req, res) => {
203
- if (res.statusCode === 401) {
210
+ if (code === 4001) {
204
211
  console.log("AgentDesk: authentication required — run 'agentdesk login'");
205
212
  }
206
- vizConnected = false;
207
213
  });
208
214
  } catch {
209
215
  // AgentDesk not running
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kendoo.agentdesk/agentdesk",
3
- "version": "0.4.0",
3
+ "version": "0.4.3",
4
4
  "description": "AI team orchestrator for Claude Code — run collaborative agent sessions from your terminal",
5
5
  "type": "module",
6
6
  "bin": {
package/prompts/team.md CHANGED
@@ -110,6 +110,55 @@ Use log level tags in agent statements to classify the type of communication:
110
110
  ---
111
111
  ```
112
112
 
113
+ ## SCREENSHOTS
114
+
115
+ When a task involves UI changes, the team MUST capture screenshots of the affected views. This is not optional for UI tasks.
116
+
117
+ **Roles:**
118
+ - **Jane** identifies during PLANNING whether this is a UI task (any visual changes to components, pages, layouts, or styles).
119
+ - **Luna** defines the screenshot plan during her review step: which pages/routes to capture and which viewports (desktop, mobile, or both — based on what's relevant to the change).
120
+ - **Bart** executes the screenshot plan during his review step, after all code changes are finalized.
121
+
122
+ **How to capture screenshots:**
123
+
124
+ 1. Start the dev server in the background:
125
+ ```bash
126
+ echo "Starting dev server for screenshots..."
127
+ npm run dev &
128
+ DEV_PID=$!
129
+ sleep 5 # Wait for server to start
130
+ ```
131
+
132
+ 2. Take screenshots using the agentdesk screenshot utility:
133
+ ```bash
134
+ # Desktop + mobile (default)
135
+ node node_modules/@kendoo.agentdesk/agentdesk/cli/screenshot.mjs http://localhost:3000/affected-route --out screenshots
136
+
137
+ # Desktop only
138
+ node node_modules/@kendoo.agentdesk/agentdesk/cli/screenshot.mjs http://localhost:3000/route --viewports desktop
139
+
140
+ # Mobile only
141
+ node node_modules/@kendoo.agentdesk/agentdesk/cli/screenshot.mjs http://localhost:3000/route --viewports mobile
142
+ ```
143
+
144
+ If the utility is not available, use a manual approach:
145
+ ```bash
146
+ npx --yes puppeteer-cli screenshot http://localhost:3000/route --viewport 1280x800 --output desktop.png
147
+ npx --yes puppeteer-cli screenshot http://localhost:3000/route --viewport 375x812 --output mobile.png
148
+ ```
149
+
150
+ 3. Stop the dev server when done:
151
+ ```bash
152
+ kill $DEV_PID 2>/dev/null
153
+ ```
154
+
155
+ 4. Upload screenshots to the task tracker and post them as a separate comment (see STRICT RULES above for format).
156
+
157
+ **When to capture:**
158
+ - After ALL code changes, reviews, and fixes are complete (during Bart's review step)
159
+ - Before creating the PR
160
+ - Only capture the pages/views that were actually affected by the changes
161
+
113
162
  {{#LINEAR}}
114
163
  ## LINEAR INTEGRATION
115
164