@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 +21 -0
- package/cli/agents.mjs +5 -2
- package/cli/screenshot.mjs +91 -0
- package/cli/team.mjs +28 -22
- package/package.json +1 -1
- package/prompts/team.md +49 -0
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
|
|
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
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
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 ===
|
|
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
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
|
|