quadwork 0.1.0 → 0.1.2

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.
Files changed (66) hide show
  1. package/bin/quadwork.js +215 -73
  2. package/out/404.html +1 -1
  3. package/out/__next.__PAGE__.txt +1 -1
  4. package/out/__next._full.txt +1 -1
  5. package/out/__next._head.txt +1 -1
  6. package/out/__next._index.txt +1 -1
  7. package/out/__next._tree.txt +1 -1
  8. package/out/_not-found/__next._full.txt +1 -1
  9. package/out/_not-found/__next._head.txt +1 -1
  10. package/out/_not-found/__next._index.txt +1 -1
  11. package/out/_not-found/__next._not-found.__PAGE__.txt +1 -1
  12. package/out/_not-found/__next._not-found.txt +1 -1
  13. package/out/_not-found/__next._tree.txt +1 -1
  14. package/out/_not-found.html +1 -1
  15. package/out/_not-found.txt +1 -1
  16. package/out/index.html +1 -1
  17. package/out/index.txt +1 -1
  18. package/out/project/_/__next._full.txt +1 -1
  19. package/out/project/_/__next._head.txt +1 -1
  20. package/out/project/_/__next._index.txt +1 -1
  21. package/out/project/_/__next._tree.txt +1 -1
  22. package/out/project/_/__next.project.$d$id.__PAGE__.txt +1 -1
  23. package/out/project/_/__next.project.$d$id.txt +1 -1
  24. package/out/project/_/__next.project.txt +1 -1
  25. package/out/project/_/memory/__next._full.txt +1 -1
  26. package/out/project/_/memory/__next._head.txt +1 -1
  27. package/out/project/_/memory/__next._index.txt +1 -1
  28. package/out/project/_/memory/__next._tree.txt +1 -1
  29. package/out/project/_/memory/__next.project.$d$id.memory.__PAGE__.txt +1 -1
  30. package/out/project/_/memory/__next.project.$d$id.memory.txt +1 -1
  31. package/out/project/_/memory/__next.project.$d$id.txt +1 -1
  32. package/out/project/_/memory/__next.project.txt +1 -1
  33. package/out/project/_/memory.html +1 -1
  34. package/out/project/_/memory.txt +1 -1
  35. package/out/project/_/queue/__next._full.txt +1 -1
  36. package/out/project/_/queue/__next._head.txt +1 -1
  37. package/out/project/_/queue/__next._index.txt +1 -1
  38. package/out/project/_/queue/__next._tree.txt +1 -1
  39. package/out/project/_/queue/__next.project.$d$id.queue.__PAGE__.txt +1 -1
  40. package/out/project/_/queue/__next.project.$d$id.queue.txt +1 -1
  41. package/out/project/_/queue/__next.project.$d$id.txt +1 -1
  42. package/out/project/_/queue/__next.project.txt +1 -1
  43. package/out/project/_/queue.html +1 -1
  44. package/out/project/_/queue.txt +1 -1
  45. package/out/project/_.html +1 -1
  46. package/out/project/_.txt +1 -1
  47. package/out/settings/__next._full.txt +1 -1
  48. package/out/settings/__next._head.txt +1 -1
  49. package/out/settings/__next._index.txt +1 -1
  50. package/out/settings/__next._tree.txt +1 -1
  51. package/out/settings/__next.settings.__PAGE__.txt +1 -1
  52. package/out/settings/__next.settings.txt +1 -1
  53. package/out/settings.html +1 -1
  54. package/out/settings.txt +1 -1
  55. package/out/setup/__next._full.txt +1 -1
  56. package/out/setup/__next._head.txt +1 -1
  57. package/out/setup/__next._index.txt +1 -1
  58. package/out/setup/__next._tree.txt +1 -1
  59. package/out/setup/__next.setup.__PAGE__.txt +1 -1
  60. package/out/setup/__next.setup.txt +1 -1
  61. package/out/setup.html +1 -1
  62. package/out/setup.txt +1 -1
  63. package/package.json +1 -1
  64. /package/out/_next/static/{eq3ebKZWXVJquNrlYMOZR → vELqtMegFMn5_6zFOkhtG}/_buildManifest.js +0 -0
  65. /package/out/_next/static/{eq3ebKZWXVJquNrlYMOZR → vELqtMegFMn5_6zFOkhtG}/_clientMiddlewareManifest.js +0 -0
  66. /package/out/_next/static/{eq3ebKZWXVJquNrlYMOZR → vELqtMegFMn5_6zFOkhtG}/_ssgManifest.js +0 -0
package/bin/quadwork.js CHANGED
@@ -13,13 +13,43 @@ const CONFIG_PATH = path.join(CONFIG_DIR, "config.json");
13
13
  const TEMPLATES_DIR = path.join(__dirname, "..", "templates");
14
14
  const AGENTS = ["t1", "t2a", "t2b", "t3"];
15
15
 
16
- // ─── Helpers ────────────────────────────────────────────────────────────────
17
-
18
- function log(msg) { console.log(` ${msg}`); }
19
- function ok(msg) { console.log(` ✓ ${msg}`); }
20
- function warn(msg) { console.log(` ${msg}`); }
21
- function fail(msg) { console.error(` ${msg}`); }
22
- function header(msg) { console.log(`\n── ${msg} ${"".repeat(Math.max(0, 58 - msg.length))}\n`); }
16
+ // ─── ANSI Helpers ──────────────────────────────────────────────────────────
17
+
18
+ const isTTY = process.stdout.isTTY;
19
+ const c = isTTY ? {
20
+ reset: "\x1b[0m",
21
+ bold: "\x1b[1m",
22
+ dim: "\x1b[2m",
23
+ green: "\x1b[32m",
24
+ yellow: "\x1b[33m",
25
+ red: "\x1b[31m",
26
+ cyan: "\x1b[36m",
27
+ white: "\x1b[37m",
28
+ } : { reset: "", bold: "", dim: "", green: "", yellow: "", red: "", cyan: "", white: "" };
29
+
30
+ function log(msg) { console.log(` ${c.dim}${msg}${c.reset}`); }
31
+ function ok(msg) { console.log(` ${c.green}✓${c.reset} ${msg}`); }
32
+ function warn(msg) { console.log(` ${c.yellow}⚠ ${msg}${c.reset}`); }
33
+ function fail(msg) { console.error(` ${c.red}✗ ${msg}${c.reset}`); }
34
+ function header(msg) { console.log(`\n ${c.cyan}${c.bold}┌─ ${msg} ${"─".repeat(Math.max(0, 54 - msg.length))}┐${c.reset}\n`); }
35
+
36
+ function spinner(msg) {
37
+ if (!isTTY) {
38
+ console.log(` ${msg}`);
39
+ return { stop(result) { console.log(` ${result ? "✓" : "✗"} ${msg}`); } };
40
+ }
41
+ const frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
42
+ let i = 0;
43
+ const id = setInterval(() => {
44
+ process.stdout.write(`\r ${c.cyan}${frames[i++ % frames.length]}${c.reset} ${msg}`);
45
+ }, 80);
46
+ return {
47
+ stop(result) {
48
+ clearInterval(id);
49
+ process.stdout.write(`\r ${result ? `${c.green}✓${c.reset} ${msg}` : `${c.red}✗${c.reset} ${msg}`}${" ".repeat(10)}\n`);
50
+ },
51
+ };
52
+ }
23
53
 
24
54
  function run(cmd, opts = {}) {
25
55
  try {
@@ -35,17 +65,58 @@ function which(cmd) {
35
65
 
36
66
  function ask(rl, question, defaultVal) {
37
67
  return new Promise((resolve) => {
38
- const suffix = defaultVal ? ` (${defaultVal})` : "";
39
- rl.question(` ${question}${suffix}: `, (answer) => {
68
+ const suffix = defaultVal ? ` ${c.dim}[${defaultVal}]${c.reset}` : "";
69
+ rl.question(` ${c.bold}${question}${c.reset}${suffix}${c.cyan} > ${c.reset}`, (answer) => {
40
70
  resolve(answer.trim() || defaultVal || "");
41
71
  });
42
72
  });
43
73
  }
44
74
 
75
+ function askSecret(rl, question) {
76
+ return new Promise((resolve) => {
77
+ const stdin = process.stdin;
78
+ const stdout = process.stdout;
79
+ stdout.write(` ${c.bold}${question}${c.reset}${c.cyan} > ${c.reset}`);
80
+ let secret = "";
81
+ const wasRaw = stdin.isRaw;
82
+ stdin.setRawMode(true);
83
+ stdin.resume();
84
+ const onData = (ch) => {
85
+ // Iterate per character to handle pasted multi-char input
86
+ const str = ch.toString("utf-8");
87
+ for (const c of str) {
88
+ if (c === "\n" || c === "\r") {
89
+ stdin.setRawMode(wasRaw || false);
90
+ stdin.removeListener("data", onData);
91
+ stdout.write("\n");
92
+ resolve(secret);
93
+ return;
94
+ } else if (c === "\u007F" || c === "\b") {
95
+ if (secret.length > 0) {
96
+ secret = secret.slice(0, -1);
97
+ stdout.write("\b \b");
98
+ }
99
+ } else if (c === "\u0003") {
100
+ process.exit(1);
101
+ } else if (c >= " ") {
102
+ secret += c;
103
+ stdout.write("*");
104
+ }
105
+ }
106
+ };
107
+ stdin.on("data", onData);
108
+ });
109
+ }
110
+
111
+ function maskValue(val) {
112
+ if (!val || val.length < 8) return "****";
113
+ return val.slice(0, 4) + "***" + val.slice(-3);
114
+ }
115
+
45
116
  function askYN(rl, question, defaultYes = false) {
46
117
  return new Promise((resolve) => {
47
118
  const hint = defaultYes ? "Y/n" : "y/N";
48
- rl.question(` ${question} [${hint}]: `, (answer) => {
119
+ rl.question(` ${c.bold}${question}${c.reset} ${c.dim}[${hint}]${c.reset}${c.cyan} > ${c.reset}`, (answer) => {
49
120
  const a = answer.trim().toLowerCase();
50
121
  resolve(a === "" ? defaultYes : a === "y" || a === "yes");
51
122
  });
@@ -67,6 +138,8 @@ function writeConfig(config) {
67
138
 
68
139
  // ─── Prerequisites ──────────────────────────────────────────────────────────
69
140
 
141
+ let agentChattrFound = false;
142
+
70
143
  function checkPrereqs() {
71
144
  header("Step 1: Prerequisites");
72
145
  let allOk = true;
@@ -90,7 +163,7 @@ function checkPrereqs() {
90
163
 
91
164
  // AgentChattr
92
165
  const acVer = run("agentchattr --version") || run("python3 -m agentchattr --version");
93
- if (acVer) ok(`AgentChattr ${acVer}`);
166
+ if (acVer) { ok(`AgentChattr ${acVer}`); agentChattrFound = true; }
94
167
  else { warn("AgentChattr not found — install: pip install agentchattr"); allOk = false; }
95
168
 
96
169
  // gh CLI
@@ -124,6 +197,7 @@ async function setupGitHub(rl) {
124
197
  return null;
125
198
  }
126
199
 
200
+ log("Enter the GitHub repo for your first project. You can add more later with `quadwork add-project`.");
127
201
  const repo = await ask(rl, "GitHub repo (owner/repo)", "");
128
202
  if (!repo || !repo.includes("/")) {
129
203
  fail("Invalid repo format — use owner/repo");
@@ -131,10 +205,12 @@ async function setupGitHub(rl) {
131
205
  }
132
206
 
133
207
  // Verify repo exists
208
+ const sp = spinner(`Verifying ${repo}...`);
134
209
  const repoCheck = run(`gh repo view ${repo} --json name 2>&1`);
135
210
  if (repoCheck && repoCheck.includes('"name"')) {
136
- ok(`Repo ${repo} verified`);
211
+ sp.stop(true);
137
212
  } else {
213
+ sp.stop(false);
138
214
  fail(`Cannot access ${repo} — check permissions`);
139
215
  return null;
140
216
  }
@@ -151,12 +227,27 @@ async function setupAgents(rl, repo) {
151
227
  const hasClaude = which("claude");
152
228
  const hasCodex = which("codex");
153
229
  let defaultBackend = hasClaude ? "claude" : "codex";
154
- const backend = await ask(rl, "CLI backend for agents (claude/codex)", defaultBackend);
230
+ log("Choose which AI CLI to run in agent terminals. Claude Code (`claude`) or OpenAI Codex (`codex`).");
231
+ const backend = await ask(rl, "Default CLI backend (claude/codex)", defaultBackend);
155
232
  if (backend !== "claude" && backend !== "codex") {
156
233
  fail("Backend must be 'claude' or 'codex'");
157
234
  return null;
158
235
  }
159
236
 
237
+ // Per-agent backend selection
238
+ const backends = {};
239
+ const customPerAgent = await askYN(rl, "Use same backend for all agents?", true);
240
+ if (customPerAgent) {
241
+ for (const agent of AGENTS) backends[agent] = backend;
242
+ } else {
243
+ for (const agent of AGENTS) {
244
+ const agentBackend = await ask(rl, `${agent.toUpperCase()} backend (claude/codex)`, backend);
245
+ backends[agent] = (agentBackend === "claude" || agentBackend === "codex") ? agentBackend : backend;
246
+ }
247
+ }
248
+
249
+ log("Path to your local clone of the repo. Four worktrees will be created next to it");
250
+ log("(e.g., project-t1/, project-t2a/, project-t2b/, project-t3/).");
160
251
  const projectDir = await ask(rl, "Project directory", process.cwd());
161
252
  const absDir = path.resolve(projectDir);
162
253
 
@@ -171,31 +262,33 @@ async function setupAgents(rl, repo) {
171
262
  return null;
172
263
  }
173
264
 
174
- // Prompt for reviewer credentials (used in T2a/T2b seed templates)
175
- const reviewerUser = await ask(rl, "Reviewer GitHub username (for T2a/T2b)", "");
176
- const reviewerTokenPath = await ask(rl, "Reviewer token file path (for T2a/T2b)", path.join(os.homedir(), ".quadwork", "reviewer-token"));
265
+ // Prompt for reviewer credentials (optional)
266
+ log("A separate reviewer account lets T2a/T2b approve PRs independently. You can set this up later in Settings.");
267
+ const wantReviewer = await askYN(rl, "Use a separate GitHub account for reviewers (T2a/T2b)?", false);
268
+ let reviewerUser = "";
269
+ let reviewerTokenPath = "";
270
+ if (wantReviewer) {
271
+ log("GitHub username for the reviewer account (used in T2a/T2b seed files for PR reviews).");
272
+ reviewerUser = await ask(rl, "Reviewer GitHub username", "");
273
+ log("Path to a file containing a GitHub PAT for the reviewer account.");
274
+ reviewerTokenPath = await ask(rl, "Reviewer token file path", path.join(os.homedir(), ".quadwork", "reviewer-token"));
275
+ }
177
276
 
178
277
  const projectName = path.basename(absDir);
179
278
  log(`Project: ${projectName}`);
180
- log("Creating worktrees for 4 agents...\n");
279
+ const wtSpinner = spinner("Creating worktrees and seeding files...");
181
280
 
182
281
  const worktrees = {};
282
+ let wtFailed = null;
183
283
  for (const agent of AGENTS) {
184
284
  const wtDir = path.join(path.dirname(absDir), `${projectName}-${agent}`);
185
- if (fs.existsSync(wtDir)) {
186
- ok(`Worktree exists: ${agent} → ${wtDir}`);
187
- } else {
285
+ if (!fs.existsSync(wtDir)) {
188
286
  const branchName = `worktree-${agent}`;
189
- // Create branch if needed
190
287
  run(`git -C "${absDir}" branch ${branchName} HEAD 2>&1`);
191
288
  const result = run(`git -C "${absDir}" worktree add "${wtDir}" ${branchName} 2>&1`);
192
- if (result !== null) {
193
- ok(`Created worktree: ${agent} → ${wtDir}`);
194
- } else {
195
- // Try without branch (detached)
289
+ if (!result) {
196
290
  const result2 = run(`git -C "${absDir}" worktree add --detach "${wtDir}" HEAD 2>&1`);
197
- if (result2 !== null) ok(`Created worktree (detached): ${agent} ${wtDir}`);
198
- else { fail(`Failed to create worktree for ${agent}`); return null; }
291
+ if (!result2) { wtFailed = agent; break; }
199
292
  }
200
293
  }
201
294
  worktrees[agent] = wtDir;
@@ -205,13 +298,25 @@ async function setupAgents(rl, repo) {
205
298
  const seedDst = path.join(wtDir, "AGENTS.md");
206
299
  if (fs.existsSync(seedSrc)) {
207
300
  let seedContent = fs.readFileSync(seedSrc, "utf-8");
208
- seedContent = seedContent.replace(/\{\{reviewer_github_user\}\}/g, reviewerUser);
209
- seedContent = seedContent.replace(/\{\{reviewer_token_path\}\}/g, reviewerTokenPath);
301
+ if (reviewerUser) {
302
+ seedContent = seedContent.replace(/\{\{reviewer_github_user\}\}/g, reviewerUser);
303
+ seedContent = seedContent.replace(/\{\{reviewer_token_path\}\}/g, reviewerTokenPath);
304
+ } else {
305
+ // No reviewer configured — remove the GitHub Authentication section
306
+ seedContent = seedContent.replace(/## GitHub Authentication[\s\S]*?## Forbidden Actions/, "## Forbidden Actions");
307
+ seedContent = seedContent.replace(/\{\{reviewer_github_user\}\}/g, "");
308
+ seedContent = seedContent.replace(/\{\{reviewer_token_path\}\}/g, "");
309
+ }
210
310
  fs.writeFileSync(seedDst, seedContent);
211
- log(` Copied ${agent}.AGENTS.md`);
212
311
  }
213
312
  }
214
313
 
314
+ if (wtFailed) {
315
+ wtSpinner.stop(false);
316
+ fail(`Failed to create worktree for ${wtFailed}`);
317
+ return null;
318
+ }
319
+
215
320
  // Copy CLAUDE.md to each worktree
216
321
  const claudeSrc = path.join(TEMPLATES_DIR, "CLAUDE.md");
217
322
  if (fs.existsSync(claudeSrc)) {
@@ -219,20 +324,20 @@ async function setupAgents(rl, repo) {
219
324
  claudeContent = claudeContent.replace(/\{\{project_name\}\}/g, projectName);
220
325
  for (const agent of AGENTS) {
221
326
  const dst = path.join(worktrees[agent], "CLAUDE.md");
222
- // Don't overwrite if CLAUDE.md already exists
223
327
  if (!fs.existsSync(dst)) {
224
328
  fs.writeFileSync(dst, claudeContent);
225
329
  }
226
330
  }
227
- ok("Copied CLAUDE.md to all worktrees");
228
331
  }
229
332
 
230
- return { projectName, absDir, worktrees, repo, backend };
333
+ wtSpinner.stop(true);
334
+
335
+ return { projectName, absDir, worktrees, repo, backend, backends };
231
336
  }
232
337
 
233
338
  // ─── AgentChattr Config ─────────────────────────────────────────────────────
234
339
 
235
- function writeAgentChattrConfig(setup, configTomlPath) {
340
+ function writeAgentChattrConfig(setup, configTomlPath, { skipInstall = false } = {}) {
236
341
  header("Step 4: AgentChattr Setup");
237
342
 
238
343
  let tomlContent = fs.readFileSync(path.join(TEMPLATES_DIR, "config.toml"), "utf-8");
@@ -242,8 +347,14 @@ function writeAgentChattrConfig(setup, configTomlPath) {
242
347
  // Replace placeholders
243
348
  tomlContent = tomlContent.replace(/\{\{project_name\}\}/g, setup.projectName);
244
349
  tomlContent = tomlContent.replace(/\{\{repo\}\}/g, setup.repo);
245
- // Replace all agent commands with the chosen backend
246
- tomlContent = tomlContent.replace(/command = "(?:claude|codex)"/g, `command = "${setup.backend}"`);
350
+ // Replace per-agent commands with chosen backends
351
+ for (const agent of AGENTS) {
352
+ const cmd = (setup.backends && setup.backends[agent]) || setup.backend;
353
+ tomlContent = tomlContent.replace(
354
+ new RegExp(`(\\[agents\\.${agent}\\][\\s\\S]*?command = )"(?:claude|codex)"`),
355
+ `$1"${cmd}"`
356
+ );
357
+ }
247
358
 
248
359
  // Write config.toml
249
360
  const configDir = path.dirname(configTomlPath);
@@ -251,30 +362,42 @@ function writeAgentChattrConfig(setup, configTomlPath) {
251
362
  fs.writeFileSync(configTomlPath, tomlContent);
252
363
  ok(`Wrote ${configTomlPath}`);
253
364
 
254
- // Install AgentChattr if missing, then start it
255
- const acInstalled = run("agentchattr --version") || run("python3 -m agentchattr --version");
256
- if (!acInstalled) {
257
- log("Installing AgentChattr...");
365
+ // Start AgentChattr if available; optionally skip install attempt
366
+ let acAvailable = which("agentchattr");
367
+ if (!acAvailable && !skipInstall) {
368
+ const acSpinner = spinner("Installing AgentChattr...");
258
369
  const installResult = run("pip install agentchattr 2>&1");
259
- if (installResult !== null) ok("Installed AgentChattr");
260
- else warn("Failed to install AgentChattr — install manually: pip install agentchattr");
370
+ if (installResult !== null) {
371
+ acSpinner.stop(true);
372
+ acAvailable = which("agentchattr");
373
+ if (!acAvailable) warn("agentchattr binary not found in PATH after install");
374
+ } else {
375
+ acSpinner.stop(false);
376
+ warn("Install manually: pip install agentchattr");
377
+ }
261
378
  }
262
379
 
263
- // Start AgentChattr server
264
- log("Starting AgentChattr server...");
265
- const acProc = spawn("agentchattr", ["--config", configTomlPath], {
266
- stdio: "ignore",
267
- detached: true,
268
- });
269
- acProc.unref();
270
- if (acProc.pid) {
271
- ok(`AgentChattr started (PID: ${acProc.pid})`);
272
- // Save PID for stop
273
- const pidFile = path.join(CONFIG_DIR, "agentchattr.pid");
274
- if (!fs.existsSync(CONFIG_DIR)) fs.mkdirSync(CONFIG_DIR, { recursive: true });
275
- fs.writeFileSync(pidFile, String(acProc.pid));
380
+ // Start AgentChattr server (only if installed)
381
+ if (acAvailable) {
382
+ log("Starting AgentChattr server...");
383
+ const acProc = spawn("agentchattr", ["--config", configTomlPath], {
384
+ stdio: "ignore",
385
+ detached: true,
386
+ });
387
+ acProc.on("error", (err) => {
388
+ warn(`AgentChattr failed to start: ${err.message}`);
389
+ });
390
+ acProc.unref();
391
+ if (acProc.pid) {
392
+ ok(`AgentChattr started (PID: ${acProc.pid})`);
393
+ const pidFile = path.join(CONFIG_DIR, "agentchattr.pid");
394
+ if (!fs.existsSync(CONFIG_DIR)) fs.mkdirSync(CONFIG_DIR, { recursive: true });
395
+ fs.writeFileSync(pidFile, String(acProc.pid));
396
+ } else {
397
+ warn("Could not start AgentChattr — start manually: agentchattr --config " + configTomlPath);
398
+ }
276
399
  } else {
277
- warn("Could not start AgentChattr — start manually: agentchattr --config " + configTomlPath);
400
+ warn("AgentChattr not installedskipping auto-start. Start manually later: agentchattr --config " + configTomlPath);
278
401
  }
279
402
 
280
403
  return configTomlPath;
@@ -286,14 +409,15 @@ async function setupAddons(rl, setup, configTomlPath) {
286
409
  header("Step 5: Optional Add-ons");
287
410
 
288
411
  // Telegram Bridge
412
+ log("Optional: connect a Telegram bot for remote notifications.");
289
413
  const wantTelegram = await askYN(rl, "Set up Telegram Bridge?", false);
290
414
  if (wantTelegram) {
291
415
  const telegramDir = path.join(path.dirname(setup.absDir), "agentchattr-telegram");
292
416
  if (!fs.existsSync(telegramDir)) {
293
- log("Cloning agentchattr-telegram...");
417
+ const cloneSpinner = spinner("Cloning agentchattr-telegram...");
294
418
  const cloneResult = run(`git clone https://github.com/realproject7/agentchattr-telegram.git "${telegramDir}" 2>&1`);
295
- if (cloneResult !== null) ok("Cloned agentchattr-telegram");
296
- else warn("Failed to clone — you can set it up manually later");
419
+ cloneSpinner.stop(cloneResult !== null);
420
+ if (!cloneResult) warn("You can set it up manually later");
297
421
  } else {
298
422
  ok("agentchattr-telegram already present");
299
423
  }
@@ -301,12 +425,20 @@ async function setupAddons(rl, setup, configTomlPath) {
301
425
  if (fs.existsSync(telegramDir)) {
302
426
  const reqFile = path.join(telegramDir, "requirements.txt");
303
427
  if (fs.existsSync(reqFile)) {
304
- run(`pip install -r "${reqFile}" 2>&1`);
305
- ok("Installed Telegram Bridge dependencies");
428
+ const tgSpinner = spinner("Installing Telegram Bridge dependencies...");
429
+ const tgResult = run(`pip install -r "${reqFile}" 2>&1`);
430
+ tgSpinner.stop(tgResult !== null);
306
431
  }
307
432
 
308
- const botToken = await ask(rl, "Telegram bot token", "");
433
+ log("Create a bot via @BotFather on Telegram (https://t.me/BotFather), then copy the token.");
434
+ const botToken = await askSecret(rl, "Telegram bot token");
435
+ log("To find your chat ID:");
436
+ log(" 1. Open your bot on Telegram and send it any message (e.g., 'hi')");
437
+ log(" 2. Run: curl https://api.telegram.org/bot<TOKEN>/getUpdates");
438
+ log(" 3. Look for \"chat\":{\"id\":123456789,...} — the number is your chat ID");
439
+ log(" Note: Returns empty if no messages have been sent to the bot yet.");
309
440
  const chatId = await ask(rl, "Telegram chat ID", "");
441
+ log("Need help? See https://github.com/realproject7/agentchattr-telegram#readme");
310
442
 
311
443
  if (botToken && chatId) {
312
444
  // Write bot token to ~/.quadwork/.env (never stored in config files)
@@ -323,7 +455,7 @@ async function setupAddons(rl, setup, configTomlPath) {
323
455
  }
324
456
  fs.writeFileSync(envPath, envContent, { mode: 0o600 });
325
457
  fs.chmodSync(envPath, 0o600);
326
- ok(`Saved bot token to ${envPath}`);
458
+ ok(`Saved bot token (${maskValue(botToken)}) to ${envPath}`);
327
459
 
328
460
  // Persist telegram settings for writeQuadWorkConfig (env reference, not plaintext)
329
461
  setup.telegram = {
@@ -370,14 +502,15 @@ bridge_sender = "telegram-bridge"
370
502
  }
371
503
 
372
504
  // Shared Memory
505
+ log("Optional: set up shared memory cards for cross-agent knowledge.");
373
506
  const wantMemory = await askYN(rl, "Set up Shared Memory?", false);
374
507
  if (wantMemory) {
375
508
  const memoryDir = path.join(path.dirname(setup.absDir), "agent-memory");
376
509
  if (!fs.existsSync(memoryDir)) {
377
- log("Cloning agent-memory...");
510
+ const memSpinner = spinner("Cloning agent-memory...");
378
511
  const cloneResult = run(`git clone https://github.com/realproject7/agent-memory.git "${memoryDir}" 2>&1`);
379
- if (cloneResult !== null) ok("Cloned agent-memory");
380
- else warn("Failed to clone — you can set it up manually later");
512
+ memSpinner.stop(cloneResult !== null);
513
+ if (!cloneResult) warn("You can set it up manually later");
381
514
  } else {
382
515
  ok("agent-memory already present");
383
516
  }
@@ -436,7 +569,7 @@ function writeQuadWorkConfig(setup) {
436
569
  };
437
570
 
438
571
  for (const agent of AGENTS) {
439
- project.agents[agent] = { cwd: setup.worktrees[agent], command: setup.backend };
572
+ project.agents[agent] = { cwd: setup.worktrees[agent], command: (setup.backends && setup.backends[agent]) || setup.backend };
440
573
  }
441
574
 
442
575
  if (setup.memoryDir) {
@@ -465,7 +598,12 @@ function writeQuadWorkConfig(setup) {
465
598
  // ─── Init Command ───────────────────────────────────────────────────────────
466
599
 
467
600
  async function cmdInit() {
468
- console.log("\n QuadWork Init — 4-agent coding team setup\n");
601
+ console.log("");
602
+ console.log(` ${c.cyan}${c.bold}╔══════════════════════════════════════════╗${c.reset}`);
603
+ console.log(` ${c.cyan}${c.bold}║${c.reset} ${c.white}${c.bold}QuadWork Init${c.reset} ${c.cyan}${c.bold}║${c.reset}`);
604
+ console.log(` ${c.cyan}${c.bold}║${c.reset} ${c.dim}4-agent coding team setup${c.reset} ${c.cyan}${c.bold}║${c.reset}`);
605
+ console.log(` ${c.cyan}${c.bold}╚══════════════════════════════════════════╝${c.reset}`);
606
+ console.log(`\n ${c.dim}Tip: Press Enter to accept defaults shown in [brackets].${c.reset}\n`);
469
607
 
470
608
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
471
609
 
@@ -485,9 +623,9 @@ async function cmdInit() {
485
623
  const setup = await setupAgents(rl, repo);
486
624
  if (!setup) { rl.close(); process.exit(1); }
487
625
 
488
- // Step 4: AgentChattr config
626
+ // Step 4: AgentChattr config (skip install if prereqs already flagged it missing)
489
627
  const configTomlPath = path.join(setup.absDir, "config.toml");
490
- writeAgentChattrConfig(setup, configTomlPath);
628
+ writeAgentChattrConfig(setup, configTomlPath, { skipInstall: !agentChattrFound });
491
629
 
492
630
  // Step 5: Optional add-ons
493
631
  await setupAddons(rl, setup, configTomlPath);
@@ -499,9 +637,12 @@ async function cmdInit() {
499
637
  header("Setup Complete");
500
638
  log(`Project: ${setup.projectName}`);
501
639
  log(`Repo: ${setup.repo}`);
502
- log(`Worktrees: ${AGENTS.map((a) => `${a}/`).join(", ")}`);
640
+ log(`Worktrees: ${AGENTS.map((a) => `${setup.projectName}-${a}/`).join(", ")}`);
641
+ log(`Backends: ${AGENTS.map((a) => `${a.toUpperCase()}=${(setup.backends && setup.backends[a]) || setup.backend}`).join(", ")}`);
503
642
  log(`Config: ${CONFIG_PATH}`);
504
643
  log(`AgentChattr: ${configTomlPath}`);
644
+ if (setup.telegram) log(`Telegram: configured`);
645
+ if (setup.memoryDir) log(`Shared Memory: ${setup.memoryDir}`);
505
646
  log("");
506
647
  log("Next steps:");
507
648
  log(" npx quadwork start — launch dashboard + agents");
@@ -557,15 +698,16 @@ function cmdStart() {
557
698
  const pidFile = path.join(CONFIG_DIR, "server.pid");
558
699
  fs.writeFileSync(pidFile, String(server.pid));
559
700
 
560
- // Start AgentChattr if config.toml exists for first project
701
+ // Start AgentChattr if installed and config.toml exists for first project
561
702
  const firstProject = config.projects[0];
562
- if (firstProject) {
703
+ if (firstProject && which("agentchattr")) {
563
704
  const configToml = path.join(firstProject.working_dir, "config.toml");
564
705
  if (fs.existsSync(configToml)) {
565
706
  const acProc = spawn("agentchattr", ["--config", configToml], {
566
707
  stdio: "ignore",
567
708
  detached: true,
568
709
  });
710
+ acProc.on("error", () => {});
569
711
  acProc.unref();
570
712
  if (acProc.pid) {
571
713
  ok(`AgentChattr started (PID: ${acProc.pid})`);
package/out/404.html CHANGED
@@ -1 +1 @@
1
- <!DOCTYPE html><html lang="en" class="geist_mono_8d43a2aa-module__8Li5zG__variable h-full"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="/_next/static/chunks/15kwal..m9r49.css" data-precedence="next"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/13uu.sohs74zg.js"/><script src="/_next/static/chunks/0excsn2a_5qsb.js" async=""></script><script src="/_next/static/chunks/0r7t_sj_sejq9.js" async=""></script><script src="/_next/static/chunks/0.dzh0qf9zq1l.js" async=""></script><script src="/_next/static/chunks/turbopack-06pqx~0d8czn_.js" async=""></script><script src="/_next/static/chunks/08fgie1bcjynm.js" async=""></script><script src="/_next/static/chunks/0ox7p_szjhn69.js" async=""></script><meta name="robots" content="noindex"/><meta name="next-size-adjust" content=""/><title>404: This page could not be found.</title><title>QuadWork</title><meta name="description" content="Unified dashboard for multi-agent coding teams"/><link rel="icon" href="/favicon.ico?favicon.0x3dzn~oxb6tn.ico" sizes="256x256" type="image/x-icon"/><script src="/_next/static/chunks/03~yq9q893hmn.js" noModule=""></script></head><body class="h-full flex"><div hidden=""><!--$--><!--/$--></div><aside class="w-16 shrink-0 h-full border-r border-border bg-bg-surface flex flex-col items-center py-3"><a class="w-10 h-10 flex items-center justify-center rounded-sm transition-colors text-text-muted hover:text-text hover:bg-[#1a1a1a]" title="Home" href="/"><svg width="20" height="20" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M3 10L10 3l7 7"></path><path d="M5 8.5V16h3.5v-4h3v4H15V8.5"></path></svg></a><div class="w-6 h-px bg-border my-2"></div><div class="flex-1 flex flex-col items-center gap-2 overflow-y-auto min-h-0"><a class="w-10 h-10 flex items-center justify-center rounded-full border border-dashed border-border text-text-muted hover:text-text hover:bg-[#1a1a1a] transition-colors" title="Add project" href="/setup"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><path d="M8 3v10M3 8h10"></path></svg></a></div><div class="w-6 h-px bg-border my-2"></div><a class="w-10 h-10 flex items-center justify-center rounded-sm transition-colors text-text-muted hover:text-text hover:bg-[#1a1a1a]" title="Settings" href="/settings"><svg width="18" height="18" viewBox="0 0 18 18" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="9" cy="9" r="2.5"></circle><path d="M7.5 1.5h3l.4 2.1a5.5 5.5 0 011.3.7l2-.8 1.5 2.6-1.6 1.3a5.5 5.5 0 010 1.5l1.6 1.3-1.5 2.6-2-.8a5.5 5.5 0 01-1.3.7l-.4 2.1h-3l-.4-2.1a5.5 5.5 0 01-1.3-.7l-2 .8-1.5-2.6 1.6-1.3a5.5 5.5 0 010-1.5L2.3 6.1l1.5-2.6 2 .8a5.5 5.5 0 011.3-.7z"></path></svg></a></aside><main class="flex-1 min-w-0 overflow-auto"><div style="font-family:system-ui,&quot;Segoe UI&quot;,Roboto,Helvetica,Arial,sans-serif,&quot;Apple Color Emoji&quot;,&quot;Segoe UI Emoji&quot;;height:100vh;text-align:center;display:flex;flex-direction:column;align-items:center;justify-content:center"><div><style>body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}</style><h1 class="next-error-h1" style="display:inline-block;margin:0 20px 0 0;padding:0 23px 0 0;font-size:24px;font-weight:500;vertical-align:top;line-height:49px">404</h1><div style="display:inline-block"><h2 style="font-size:14px;font-weight:400;line-height:49px;margin:0">This page could not be found.</h2></div></div></div><!--$--><!--/$--></main><script src="/_next/static/chunks/13uu.sohs74zg.js" id="_R_" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0])</script><script>self.__next_f.push([1,"1:\"$Sreact.fragment\"\n2:I[86081,[\"/_next/static/chunks/08fgie1bcjynm.js\",\"/_next/static/chunks/0ox7p_szjhn69.js\"],\"default\"]\n3:I[12527,[\"/_next/static/chunks/08fgie1bcjynm.js\",\"/_next/static/chunks/0ox7p_szjhn69.js\"],\"default\"]\n4:I[59763,[\"/_next/static/chunks/08fgie1bcjynm.js\",\"/_next/static/chunks/0ox7p_szjhn69.js\"],\"default\"]\n5:I[11717,[\"/_next/static/chunks/08fgie1bcjynm.js\",\"/_next/static/chunks/0ox7p_szjhn69.js\"],\"OutletBoundary\"]\n6:\"$Sreact.suspense\"\n9:I[11717,[\"/_next/static/chunks/08fgie1bcjynm.js\",\"/_next/static/chunks/0ox7p_szjhn69.js\"],\"ViewportBoundary\"]\nb:I[11717,[\"/_next/static/chunks/08fgie1bcjynm.js\",\"/_next/static/chunks/0ox7p_szjhn69.js\"],\"MetadataBoundary\"]\nd:I[92243,[\"/_next/static/chunks/08fgie1bcjynm.js\",\"/_next/static/chunks/0ox7p_szjhn69.js\"],\"default\",1]\n:HL[\"/_next/static/chunks/15kwal..m9r49.css\",\"style\"]\n"])</script><script>self.__next_f.push([1,"0:{\"P\":null,\"c\":[\"\",\"_not-found\"],\"q\":\"\",\"i\":false,\"f\":[[[\"\",{\"children\":[\"/_not-found\",{\"children\":[\"__PAGE__\",{}]}]},\"$undefined\",\"$undefined\",16],[[\"$\",\"$1\",\"c\",{\"children\":[[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/chunks/15kwal..m9r49.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-0\",{\"src\":\"/_next/static/chunks/08fgie1bcjynm.js\",\"async\":true,\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-1\",{\"src\":\"/_next/static/chunks/0ox7p_szjhn69.js\",\"async\":true,\"nonce\":\"$undefined\"}]],[\"$\",\"html\",null,{\"lang\":\"en\",\"className\":\"geist_mono_8d43a2aa-module__8Li5zG__variable h-full\",\"children\":[\"$\",\"body\",null,{\"className\":\"h-full flex\",\"children\":[[\"$\",\"$L2\",null,{}],[\"$\",\"main\",null,{\"className\":\"flex-1 min-w-0 overflow-auto\",\"children\":[\"$\",\"$L3\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L4\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":[[[\"$\",\"title\",null,{\"children\":\"404: This page could not be found.\"}],[\"$\",\"div\",null,{\"style\":{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"textAlign\":\"center\",\"display\":\"flex\",\"flexDirection\":\"column\",\"alignItems\":\"center\",\"justifyContent\":\"center\"},\"children\":[\"$\",\"div\",null,{\"children\":[[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}\"}}],[\"$\",\"h1\",null,{\"className\":\"next-error-h1\",\"style\":{\"display\":\"inline-block\",\"margin\":\"0 20px 0 0\",\"padding\":\"0 23px 0 0\",\"fontSize\":24,\"fontWeight\":500,\"verticalAlign\":\"top\",\"lineHeight\":\"49px\"},\"children\":404}],[\"$\",\"div\",null,{\"style\":{\"display\":\"inline-block\"},\"children\":[\"$\",\"h2\",null,{\"style\":{\"fontSize\":14,\"fontWeight\":400,\"lineHeight\":\"49px\",\"margin\":0},\"children\":\"This page could not be found.\"}]}]]}]}]],[]],\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]}]]}]}]]}],{\"children\":[[\"$\",\"$1\",\"c\",{\"children\":[null,[\"$\",\"$L3\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L4\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}],{\"children\":[[\"$\",\"$1\",\"c\",{\"children\":[[[\"$\",\"title\",null,{\"children\":\"404: This page could not be found.\"}],[\"$\",\"div\",null,{\"style\":\"$0:f:0:1:0:props:children:1:props:children:props:children:1:props:children:props:notFound:0:1:props:style\",\"children\":[\"$\",\"div\",null,{\"children\":[[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}\"}}],[\"$\",\"h1\",null,{\"className\":\"next-error-h1\",\"style\":\"$0:f:0:1:0:props:children:1:props:children:props:children:1:props:children:props:notFound:0:1:props:children:props:children:1:props:style\",\"children\":404}],[\"$\",\"div\",null,{\"style\":\"$0:f:0:1:0:props:children:1:props:children:props:children:1:props:children:props:notFound:0:1:props:children:props:children:2:props:style\",\"children\":[\"$\",\"h2\",null,{\"style\":\"$0:f:0:1:0:props:children:1:props:children:props:children:1:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style\",\"children\":\"This page could not be found.\"}]}]]}]}]],null,[\"$\",\"$L5\",null,{\"children\":[\"$\",\"$6\",null,{\"name\":\"Next.MetadataOutlet\",\"children\":\"$@7\"}]}]]}],{},null,false,null]},null,false,\"$@8\"]},null,false,null],[\"$\",\"$1\",\"h\",{\"children\":[[\"$\",\"meta\",null,{\"name\":\"robots\",\"content\":\"noindex\"}],[\"$\",\"$L9\",null,{\"children\":\"$La\"}],[\"$\",\"div\",null,{\"hidden\":true,\"children\":[\"$\",\"$Lb\",null,{\"children\":[\"$\",\"$6\",null,{\"name\":\"Next.Metadata\",\"children\":\"$Lc\"}]}]}],[\"$\",\"meta\",null,{\"name\":\"next-size-adjust\",\"content\":\"\"}]]}],false]],\"m\":\"$undefined\",\"G\":[\"$d\",[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/chunks/15kwal..m9r49.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}]]],\"S\":true,\"h\":null,\"s\":\"$undefined\",\"l\":\"$undefined\",\"p\":\"$undefined\",\"d\":\"$undefined\",\"b\":\"eq3ebKZWXVJquNrlYMOZR\"}\n"])</script><script>self.__next_f.push([1,"e:[]\n8:\"$We\"\n"])</script><script>self.__next_f.push([1,"a:[[\"$\",\"meta\",\"0\",{\"charSet\":\"utf-8\"}],[\"$\",\"meta\",\"1\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}]]\n"])</script><script>self.__next_f.push([1,"f:I[80070,[\"/_next/static/chunks/08fgie1bcjynm.js\",\"/_next/static/chunks/0ox7p_szjhn69.js\"],\"IconMark\"]\n7:null\nc:[[\"$\",\"title\",\"0\",{\"children\":\"QuadWork\"}],[\"$\",\"meta\",\"1\",{\"name\":\"description\",\"content\":\"Unified dashboard for multi-agent coding teams\"}],[\"$\",\"link\",\"2\",{\"rel\":\"icon\",\"href\":\"/favicon.ico?favicon.0x3dzn~oxb6tn.ico\",\"sizes\":\"256x256\",\"type\":\"image/x-icon\"}],[\"$\",\"$Lf\",\"3\",{}]]\n"])</script></body></html>
1
+ <!DOCTYPE html><html lang="en" class="geist_mono_8d43a2aa-module__8Li5zG__variable h-full"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="/_next/static/chunks/15kwal..m9r49.css" data-precedence="next"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/13uu.sohs74zg.js"/><script src="/_next/static/chunks/0excsn2a_5qsb.js" async=""></script><script src="/_next/static/chunks/0r7t_sj_sejq9.js" async=""></script><script src="/_next/static/chunks/0.dzh0qf9zq1l.js" async=""></script><script src="/_next/static/chunks/turbopack-06pqx~0d8czn_.js" async=""></script><script src="/_next/static/chunks/08fgie1bcjynm.js" async=""></script><script src="/_next/static/chunks/0ox7p_szjhn69.js" async=""></script><meta name="robots" content="noindex"/><meta name="next-size-adjust" content=""/><title>404: This page could not be found.</title><title>QuadWork</title><meta name="description" content="Unified dashboard for multi-agent coding teams"/><link rel="icon" href="/favicon.ico?favicon.0x3dzn~oxb6tn.ico" sizes="256x256" type="image/x-icon"/><script src="/_next/static/chunks/03~yq9q893hmn.js" noModule=""></script></head><body class="h-full flex"><div hidden=""><!--$--><!--/$--></div><aside class="w-16 shrink-0 h-full border-r border-border bg-bg-surface flex flex-col items-center py-3"><a class="w-10 h-10 flex items-center justify-center rounded-sm transition-colors text-text-muted hover:text-text hover:bg-[#1a1a1a]" title="Home" href="/"><svg width="20" height="20" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M3 10L10 3l7 7"></path><path d="M5 8.5V16h3.5v-4h3v4H15V8.5"></path></svg></a><div class="w-6 h-px bg-border my-2"></div><div class="flex-1 flex flex-col items-center gap-2 overflow-y-auto min-h-0"><a class="w-10 h-10 flex items-center justify-center rounded-full border border-dashed border-border text-text-muted hover:text-text hover:bg-[#1a1a1a] transition-colors" title="Add project" href="/setup"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><path d="M8 3v10M3 8h10"></path></svg></a></div><div class="w-6 h-px bg-border my-2"></div><a class="w-10 h-10 flex items-center justify-center rounded-sm transition-colors text-text-muted hover:text-text hover:bg-[#1a1a1a]" title="Settings" href="/settings"><svg width="18" height="18" viewBox="0 0 18 18" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="9" cy="9" r="2.5"></circle><path d="M7.5 1.5h3l.4 2.1a5.5 5.5 0 011.3.7l2-.8 1.5 2.6-1.6 1.3a5.5 5.5 0 010 1.5l1.6 1.3-1.5 2.6-2-.8a5.5 5.5 0 01-1.3.7l-.4 2.1h-3l-.4-2.1a5.5 5.5 0 01-1.3-.7l-2 .8-1.5-2.6 1.6-1.3a5.5 5.5 0 010-1.5L2.3 6.1l1.5-2.6 2 .8a5.5 5.5 0 011.3-.7z"></path></svg></a></aside><main class="flex-1 min-w-0 overflow-auto"><div style="font-family:system-ui,&quot;Segoe UI&quot;,Roboto,Helvetica,Arial,sans-serif,&quot;Apple Color Emoji&quot;,&quot;Segoe UI Emoji&quot;;height:100vh;text-align:center;display:flex;flex-direction:column;align-items:center;justify-content:center"><div><style>body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}</style><h1 class="next-error-h1" style="display:inline-block;margin:0 20px 0 0;padding:0 23px 0 0;font-size:24px;font-weight:500;vertical-align:top;line-height:49px">404</h1><div style="display:inline-block"><h2 style="font-size:14px;font-weight:400;line-height:49px;margin:0">This page could not be found.</h2></div></div></div><!--$--><!--/$--></main><script src="/_next/static/chunks/13uu.sohs74zg.js" id="_R_" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0])</script><script>self.__next_f.push([1,"1:\"$Sreact.fragment\"\n2:I[86081,[\"/_next/static/chunks/08fgie1bcjynm.js\",\"/_next/static/chunks/0ox7p_szjhn69.js\"],\"default\"]\n3:I[12527,[\"/_next/static/chunks/08fgie1bcjynm.js\",\"/_next/static/chunks/0ox7p_szjhn69.js\"],\"default\"]\n4:I[59763,[\"/_next/static/chunks/08fgie1bcjynm.js\",\"/_next/static/chunks/0ox7p_szjhn69.js\"],\"default\"]\n5:I[11717,[\"/_next/static/chunks/08fgie1bcjynm.js\",\"/_next/static/chunks/0ox7p_szjhn69.js\"],\"OutletBoundary\"]\n6:\"$Sreact.suspense\"\n9:I[11717,[\"/_next/static/chunks/08fgie1bcjynm.js\",\"/_next/static/chunks/0ox7p_szjhn69.js\"],\"ViewportBoundary\"]\nb:I[11717,[\"/_next/static/chunks/08fgie1bcjynm.js\",\"/_next/static/chunks/0ox7p_szjhn69.js\"],\"MetadataBoundary\"]\nd:I[92243,[\"/_next/static/chunks/08fgie1bcjynm.js\",\"/_next/static/chunks/0ox7p_szjhn69.js\"],\"default\",1]\n:HL[\"/_next/static/chunks/15kwal..m9r49.css\",\"style\"]\n"])</script><script>self.__next_f.push([1,"0:{\"P\":null,\"c\":[\"\",\"_not-found\"],\"q\":\"\",\"i\":false,\"f\":[[[\"\",{\"children\":[\"/_not-found\",{\"children\":[\"__PAGE__\",{}]}]},\"$undefined\",\"$undefined\",16],[[\"$\",\"$1\",\"c\",{\"children\":[[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/chunks/15kwal..m9r49.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-0\",{\"src\":\"/_next/static/chunks/08fgie1bcjynm.js\",\"async\":true,\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-1\",{\"src\":\"/_next/static/chunks/0ox7p_szjhn69.js\",\"async\":true,\"nonce\":\"$undefined\"}]],[\"$\",\"html\",null,{\"lang\":\"en\",\"className\":\"geist_mono_8d43a2aa-module__8Li5zG__variable h-full\",\"children\":[\"$\",\"body\",null,{\"className\":\"h-full flex\",\"children\":[[\"$\",\"$L2\",null,{}],[\"$\",\"main\",null,{\"className\":\"flex-1 min-w-0 overflow-auto\",\"children\":[\"$\",\"$L3\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L4\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":[[[\"$\",\"title\",null,{\"children\":\"404: This page could not be found.\"}],[\"$\",\"div\",null,{\"style\":{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"textAlign\":\"center\",\"display\":\"flex\",\"flexDirection\":\"column\",\"alignItems\":\"center\",\"justifyContent\":\"center\"},\"children\":[\"$\",\"div\",null,{\"children\":[[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}\"}}],[\"$\",\"h1\",null,{\"className\":\"next-error-h1\",\"style\":{\"display\":\"inline-block\",\"margin\":\"0 20px 0 0\",\"padding\":\"0 23px 0 0\",\"fontSize\":24,\"fontWeight\":500,\"verticalAlign\":\"top\",\"lineHeight\":\"49px\"},\"children\":404}],[\"$\",\"div\",null,{\"style\":{\"display\":\"inline-block\"},\"children\":[\"$\",\"h2\",null,{\"style\":{\"fontSize\":14,\"fontWeight\":400,\"lineHeight\":\"49px\",\"margin\":0},\"children\":\"This page could not be found.\"}]}]]}]}]],[]],\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]}]]}]}]]}],{\"children\":[[\"$\",\"$1\",\"c\",{\"children\":[null,[\"$\",\"$L3\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L4\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}],{\"children\":[[\"$\",\"$1\",\"c\",{\"children\":[[[\"$\",\"title\",null,{\"children\":\"404: This page could not be found.\"}],[\"$\",\"div\",null,{\"style\":\"$0:f:0:1:0:props:children:1:props:children:props:children:1:props:children:props:notFound:0:1:props:style\",\"children\":[\"$\",\"div\",null,{\"children\":[[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}\"}}],[\"$\",\"h1\",null,{\"className\":\"next-error-h1\",\"style\":\"$0:f:0:1:0:props:children:1:props:children:props:children:1:props:children:props:notFound:0:1:props:children:props:children:1:props:style\",\"children\":404}],[\"$\",\"div\",null,{\"style\":\"$0:f:0:1:0:props:children:1:props:children:props:children:1:props:children:props:notFound:0:1:props:children:props:children:2:props:style\",\"children\":[\"$\",\"h2\",null,{\"style\":\"$0:f:0:1:0:props:children:1:props:children:props:children:1:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style\",\"children\":\"This page could not be found.\"}]}]]}]}]],null,[\"$\",\"$L5\",null,{\"children\":[\"$\",\"$6\",null,{\"name\":\"Next.MetadataOutlet\",\"children\":\"$@7\"}]}]]}],{},null,false,null]},null,false,\"$@8\"]},null,false,null],[\"$\",\"$1\",\"h\",{\"children\":[[\"$\",\"meta\",null,{\"name\":\"robots\",\"content\":\"noindex\"}],[\"$\",\"$L9\",null,{\"children\":\"$La\"}],[\"$\",\"div\",null,{\"hidden\":true,\"children\":[\"$\",\"$Lb\",null,{\"children\":[\"$\",\"$6\",null,{\"name\":\"Next.Metadata\",\"children\":\"$Lc\"}]}]}],[\"$\",\"meta\",null,{\"name\":\"next-size-adjust\",\"content\":\"\"}]]}],false]],\"m\":\"$undefined\",\"G\":[\"$d\",[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/chunks/15kwal..m9r49.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}]]],\"S\":true,\"h\":null,\"s\":\"$undefined\",\"l\":\"$undefined\",\"p\":\"$undefined\",\"d\":\"$undefined\",\"b\":\"vELqtMegFMn5_6zFOkhtG\"}\n"])</script><script>self.__next_f.push([1,"e:[]\n8:\"$We\"\n"])</script><script>self.__next_f.push([1,"a:[[\"$\",\"meta\",\"0\",{\"charSet\":\"utf-8\"}],[\"$\",\"meta\",\"1\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}]]\n"])</script><script>self.__next_f.push([1,"f:I[80070,[\"/_next/static/chunks/08fgie1bcjynm.js\",\"/_next/static/chunks/0ox7p_szjhn69.js\"],\"IconMark\"]\n7:null\nc:[[\"$\",\"title\",\"0\",{\"children\":\"QuadWork\"}],[\"$\",\"meta\",\"1\",{\"name\":\"description\",\"content\":\"Unified dashboard for multi-agent coding teams\"}],[\"$\",\"link\",\"2\",{\"rel\":\"icon\",\"href\":\"/favicon.ico?favicon.0x3dzn~oxb6tn.ico\",\"sizes\":\"256x256\",\"type\":\"image/x-icon\"}],[\"$\",\"$Lf\",\"3\",{}]]\n"])</script></body></html>
@@ -2,5 +2,5 @@
2
2
  2:I[16348,["/_next/static/chunks/08fgie1bcjynm.js","/_next/static/chunks/0ox7p_szjhn69.js","/_next/static/chunks/03v5eoc-wic6o.js"],"default"]
3
3
  3:I[11717,["/_next/static/chunks/08fgie1bcjynm.js","/_next/static/chunks/0ox7p_szjhn69.js"],"OutletBoundary"]
4
4
  4:"$Sreact.suspense"
5
- 0:{"rsc":["$","$1","c",{"children":[["$","$L2",null,{}],[["$","script","script-0",{"src":"/_next/static/chunks/03v5eoc-wic6o.js","async":true}]],["$","$L3",null,{"children":["$","$4",null,{"name":"Next.MetadataOutlet","children":"$@5"}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"eq3ebKZWXVJquNrlYMOZR"}
5
+ 0:{"rsc":["$","$1","c",{"children":[["$","$L2",null,{}],[["$","script","script-0",{"src":"/_next/static/chunks/03v5eoc-wic6o.js","async":true}]],["$","$L3",null,{"children":["$","$4",null,{"name":"Next.MetadataOutlet","children":"$@5"}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"vELqtMegFMn5_6zFOkhtG"}
6
6
  5:null
@@ -10,7 +10,7 @@ b:I[11717,["/_next/static/chunks/08fgie1bcjynm.js","/_next/static/chunks/0ox7p_s
10
10
  d:I[92243,["/_next/static/chunks/08fgie1bcjynm.js","/_next/static/chunks/0ox7p_szjhn69.js"],"default",1]
11
11
  :HL["/_next/static/chunks/15kwal..m9r49.css","style"]
12
12
  :HL["/_next/static/media/797e433ab948586e-s.p.0.q-h669a_dqa.woff2","font",{"crossOrigin":"","type":"font/woff2"}]
13
- 0:{"P":null,"c":["",""],"q":"","i":false,"f":[[["",{"children":["__PAGE__",{}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/chunks/15kwal..m9r49.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/_next/static/chunks/08fgie1bcjynm.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/_next/static/chunks/0ox7p_szjhn69.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","className":"geist_mono_8d43a2aa-module__8Li5zG__variable h-full","children":["$","body",null,{"className":"h-full flex","children":[["$","$L2",null,{}],["$","main",null,{"className":"flex-1 min-w-0 overflow-auto","children":["$","$L3",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L4",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]]}]}]]}],{"children":[["$","$1","c",{"children":[["$","$L5",null,{}],[["$","script","script-0",{"src":"/_next/static/chunks/03v5eoc-wic6o.js","async":true,"nonce":"$undefined"}]],["$","$L6",null,{"children":["$","$7",null,{"name":"Next.MetadataOutlet","children":"$@8"}]}]]}],{},null,false,null]},null,false,null],["$","$1","h",{"children":[null,["$","$L9",null,{"children":"$La"}],["$","div",null,{"hidden":true,"children":["$","$Lb",null,{"children":["$","$7",null,{"name":"Next.Metadata","children":"$Lc"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],false]],"m":"$undefined","G":["$d",[["$","link","0",{"rel":"stylesheet","href":"/_next/static/chunks/15kwal..m9r49.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"eq3ebKZWXVJquNrlYMOZR"}
13
+ 0:{"P":null,"c":["",""],"q":"","i":false,"f":[[["",{"children":["__PAGE__",{}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/chunks/15kwal..m9r49.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/_next/static/chunks/08fgie1bcjynm.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/_next/static/chunks/0ox7p_szjhn69.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","className":"geist_mono_8d43a2aa-module__8Li5zG__variable h-full","children":["$","body",null,{"className":"h-full flex","children":[["$","$L2",null,{}],["$","main",null,{"className":"flex-1 min-w-0 overflow-auto","children":["$","$L3",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L4",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]]}]}]]}],{"children":[["$","$1","c",{"children":[["$","$L5",null,{}],[["$","script","script-0",{"src":"/_next/static/chunks/03v5eoc-wic6o.js","async":true,"nonce":"$undefined"}]],["$","$L6",null,{"children":["$","$7",null,{"name":"Next.MetadataOutlet","children":"$@8"}]}]]}],{},null,false,null]},null,false,null],["$","$1","h",{"children":[null,["$","$L9",null,{"children":"$La"}],["$","div",null,{"hidden":true,"children":["$","$Lb",null,{"children":["$","$7",null,{"name":"Next.Metadata","children":"$Lc"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],false]],"m":"$undefined","G":["$d",[["$","link","0",{"rel":"stylesheet","href":"/_next/static/chunks/15kwal..m9r49.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"vELqtMegFMn5_6zFOkhtG"}
14
14
  a:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]
15
15
  e:I[80070,["/_next/static/chunks/08fgie1bcjynm.js","/_next/static/chunks/0ox7p_szjhn69.js"],"IconMark"]
16
16
  8:null
@@ -3,4 +3,4 @@
3
3
  3:I[11717,["/_next/static/chunks/08fgie1bcjynm.js","/_next/static/chunks/0ox7p_szjhn69.js"],"MetadataBoundary"]
4
4
  4:"$Sreact.suspense"
5
5
  5:I[80070,["/_next/static/chunks/08fgie1bcjynm.js","/_next/static/chunks/0ox7p_szjhn69.js"],"IconMark"]
6
- 0:{"rsc":["$","$1","h",{"children":[null,["$","$L2",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]}],["$","div",null,{"hidden":true,"children":["$","$L3",null,{"children":["$","$4",null,{"name":"Next.Metadata","children":[["$","title","0",{"children":"QuadWork"}],["$","meta","1",{"name":"description","content":"Unified dashboard for multi-agent coding teams"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0x3dzn~oxb6tn.ico","sizes":"256x256","type":"image/x-icon"}],["$","$L5","3",{}]]}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"eq3ebKZWXVJquNrlYMOZR"}
6
+ 0:{"rsc":["$","$1","h",{"children":[null,["$","$L2",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]}],["$","div",null,{"hidden":true,"children":["$","$L3",null,{"children":["$","$4",null,{"name":"Next.Metadata","children":[["$","title","0",{"children":"QuadWork"}],["$","meta","1",{"name":"description","content":"Unified dashboard for multi-agent coding teams"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.0x3dzn~oxb6tn.ico","sizes":"256x256","type":"image/x-icon"}],["$","$L5","3",{}]]}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"vELqtMegFMn5_6zFOkhtG"}