quadwork 1.5.2 → 1.5.4

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 (117) hide show
  1. package/bin/quadwork.js +16 -1
  2. package/out/404.html +1 -1
  3. package/out/__next.__PAGE__.txt +3 -3
  4. package/out/__next._full.txt +11 -11
  5. package/out/__next._head.txt +4 -4
  6. package/out/__next._index.txt +5 -5
  7. package/out/__next._tree.txt +1 -1
  8. package/out/_next/static/chunks/0.bbxho1vnxin.js +1 -0
  9. package/out/_next/static/chunks/{0m83k84.midd1.js → 0.fo6qk3ltnbn.js} +3 -3
  10. package/out/_next/static/chunks/00n1ppi~-l_sv.js +1 -0
  11. package/out/_next/static/chunks/{0.57eg262w~qg.js → 01xlw8hd842-c.js} +1 -1
  12. package/out/_next/static/chunks/{135rms05ismy4.js → 0656i.-.r7.a9.js} +2 -2
  13. package/out/_next/static/chunks/09elx026_5z7..js +1 -0
  14. package/out/_next/static/chunks/0d3shmwh5_nmn.js +1 -0
  15. package/out/_next/static/chunks/0fkhi51bdw9~~.js +1 -0
  16. package/out/_next/static/chunks/0fpg8z.yd2xb7.js +1 -0
  17. package/out/_next/static/chunks/0iwycgwby2dd_.js +1 -0
  18. package/out/_next/static/chunks/0mtmv-f5qymoi.js +1 -0
  19. package/out/_next/static/chunks/0pqt~8bl3ukh4.js +4 -0
  20. package/out/_next/static/chunks/0ze4gu236oq96.js +31 -0
  21. package/out/_next/static/chunks/0zfotsowwll1x.js +2 -0
  22. package/out/_next/static/chunks/0~xrqi87fqraz.js +1 -0
  23. package/out/_next/static/chunks/11v7tu7pto6_x.js +1 -0
  24. package/out/_next/static/chunks/{0dmi9pk2bd712.js → 12i404gkhv7q..js} +2 -2
  25. package/out/_next/static/chunks/16g.ca89g7fib.js +1 -0
  26. package/out/_next/static/chunks/{turbopack-0wh29ykoy-rb5.js → turbopack-0lcwh84lrj9gi.js} +1 -1
  27. package/out/_not-found/__next._full.txt +10 -10
  28. package/out/_not-found/__next._head.txt +4 -4
  29. package/out/_not-found/__next._index.txt +5 -5
  30. package/out/_not-found/__next._not-found.__PAGE__.txt +2 -2
  31. package/out/_not-found/__next._not-found.txt +3 -3
  32. package/out/_not-found/__next._tree.txt +1 -1
  33. package/out/_not-found.html +1 -1
  34. package/out/_not-found.txt +10 -10
  35. package/out/app-shell/__next._full.txt +10 -10
  36. package/out/app-shell/__next._head.txt +4 -4
  37. package/out/app-shell/__next._index.txt +5 -5
  38. package/out/app-shell/__next._tree.txt +1 -1
  39. package/out/app-shell/__next.app-shell.__PAGE__.txt +2 -2
  40. package/out/app-shell/__next.app-shell.txt +3 -3
  41. package/out/app-shell.html +1 -1
  42. package/out/app-shell.txt +10 -10
  43. package/out/index.html +1 -1
  44. package/out/index.txt +11 -11
  45. package/out/project/_/__next._full.txt +11 -11
  46. package/out/project/_/__next._head.txt +4 -4
  47. package/out/project/_/__next._index.txt +5 -5
  48. package/out/project/_/__next._tree.txt +1 -1
  49. package/out/project/_/__next.project.$d$id.__PAGE__.txt +3 -3
  50. package/out/project/_/__next.project.$d$id.txt +3 -3
  51. package/out/project/_/__next.project.txt +3 -3
  52. package/out/project/_/memory/__next._full.txt +11 -11
  53. package/out/project/_/memory/__next._head.txt +4 -4
  54. package/out/project/_/memory/__next._index.txt +5 -5
  55. package/out/project/_/memory/__next._tree.txt +1 -1
  56. package/out/project/_/memory/__next.project.$d$id.memory.__PAGE__.txt +3 -3
  57. package/out/project/_/memory/__next.project.$d$id.memory.txt +3 -3
  58. package/out/project/_/memory/__next.project.$d$id.txt +3 -3
  59. package/out/project/_/memory/__next.project.txt +3 -3
  60. package/out/project/_/memory.html +1 -1
  61. package/out/project/_/memory.txt +11 -11
  62. package/out/project/_/queue/__next._full.txt +11 -11
  63. package/out/project/_/queue/__next._head.txt +4 -4
  64. package/out/project/_/queue/__next._index.txt +5 -5
  65. package/out/project/_/queue/__next._tree.txt +1 -1
  66. package/out/project/_/queue/__next.project.$d$id.queue.__PAGE__.txt +3 -3
  67. package/out/project/_/queue/__next.project.$d$id.queue.txt +3 -3
  68. package/out/project/_/queue/__next.project.$d$id.txt +3 -3
  69. package/out/project/_/queue/__next.project.txt +3 -3
  70. package/out/project/_/queue.html +1 -1
  71. package/out/project/_/queue.txt +11 -11
  72. package/out/project/_.html +1 -1
  73. package/out/project/_.txt +11 -11
  74. package/out/settings/__next._full.txt +11 -11
  75. package/out/settings/__next._head.txt +4 -4
  76. package/out/settings/__next._index.txt +5 -5
  77. package/out/settings/__next._tree.txt +1 -1
  78. package/out/settings/__next.settings.__PAGE__.txt +3 -3
  79. package/out/settings/__next.settings.txt +3 -3
  80. package/out/settings.html +1 -1
  81. package/out/settings.txt +11 -11
  82. package/out/setup/__next._full.txt +11 -11
  83. package/out/setup/__next._head.txt +4 -4
  84. package/out/setup/__next._index.txt +5 -5
  85. package/out/setup/__next._tree.txt +1 -1
  86. package/out/setup/__next.setup.__PAGE__.txt +3 -3
  87. package/out/setup/__next.setup.txt +3 -3
  88. package/out/setup.html +1 -1
  89. package/out/setup.txt +11 -11
  90. package/package.json +1 -1
  91. package/server/index.js +130 -41
  92. package/server/install-agentchattr.js +53 -0
  93. package/server/routes.js +122 -3
  94. package/server/routes.telegramBridge.test.js +100 -2
  95. package/templates/CLAUDE.md +2 -1
  96. package/templates/config.toml +8 -0
  97. package/templates/seeds/dev.AGENTS.md +2 -0
  98. package/templates/seeds/head.AGENTS.md +2 -0
  99. package/templates/seeds/reviewer1.AGENTS.md +3 -1
  100. package/templates/seeds/reviewer2.AGENTS.md +3 -1
  101. package/out/_next/static/chunks/0-7v31f-nsgw-.js +0 -1
  102. package/out/_next/static/chunks/04_t39bv8y9pe.js +0 -1
  103. package/out/_next/static/chunks/084lff9v4p_vh.js +0 -1
  104. package/out/_next/static/chunks/0e.ktwt1nyj...js +0 -1
  105. package/out/_next/static/chunks/0excsn2a_5qsb.js +0 -4
  106. package/out/_next/static/chunks/0ezniz80psxr6.js +0 -1
  107. package/out/_next/static/chunks/0g-nq4.uckan-.js +0 -1
  108. package/out/_next/static/chunks/0ox7p_szjhn69.js +0 -1
  109. package/out/_next/static/chunks/0r7t_sj_sejq9.js +0 -1
  110. package/out/_next/static/chunks/0r_tb4lmfa_yb.js +0 -1
  111. package/out/_next/static/chunks/0whtwwbpg72ar.js +0 -1
  112. package/out/_next/static/chunks/0z~0.4hivi.f2.js +0 -31
  113. package/out/_next/static/chunks/15i5_ay.0ap.6.js +0 -2
  114. package/out/_next/static/chunks/17y2walb2um9w.js +0 -1
  115. /package/out/_next/static/{yMYfZ4LAn8Fy22suFUnOy → XCVQTR6dRg8tK75W8jDzw}/_buildManifest.js +0 -0
  116. /package/out/_next/static/{yMYfZ4LAn8Fy22suFUnOy → XCVQTR6dRg8tK75W8jDzw}/_clientMiddlewareManifest.js +0 -0
  117. /package/out/_next/static/{yMYfZ4LAn8Fy22suFUnOy → XCVQTR6dRg8tK75W8jDzw}/_ssgManifest.js +0 -0
package/server/routes.js CHANGED
@@ -2385,6 +2385,70 @@ function telegramConfigToml(projectId) {
2385
2385
  return path.join(CONFIG_DIR, `telegram-${projectId}.toml`);
2386
2386
  }
2387
2387
 
2388
+ // #383: path to a project's AgentChattr config.toml. The install
2389
+ // handler patches this file to declare the `telegram-bridge` agent
2390
+ // so AC's registry accepts the bridge's register call.
2391
+ function projectAgentchattrConfigPath(projectId) {
2392
+ return path.join(CONFIG_DIR, projectId, "agentchattr", "config.toml");
2393
+ }
2394
+
2395
+ // #383 Bug 1: prefer the per-project agentchattr_url. Every project
2396
+ // after the first uses a distinct port (8301, 8302, ...), so reading
2397
+ // the global default silently routed bridge traffic to the wrong AC
2398
+ // instance.
2399
+ function resolveProjectAgentchattrUrl(cfg, project) {
2400
+ return (
2401
+ (project && project.agentchattr_url) ||
2402
+ (cfg && cfg.agentchattr_url) ||
2403
+ "http://127.0.0.1:8300"
2404
+ );
2405
+ }
2406
+
2407
+ // #383 Bug 2: the upstream bridge only reads `agentchattr_url` from
2408
+ // inside `[telegram]`. A separate `[agentchattr]` section is silently
2409
+ // ignored and the bridge falls back to its hardcoded :8300 default.
2410
+ // #404: accept projectId so we can write a per-project cursor_file
2411
+ // path. Without this, multiple project bridges share the same default
2412
+ // cursor and clobber each other's position — the project with higher
2413
+ // AC message IDs advances the cursor past the other project's range,
2414
+ // silently killing AC→TG forwarding for that project.
2415
+ function buildTelegramBridgeToml(tg, projectId) {
2416
+ const cursorFile = path.join(CONFIG_DIR, `telegram-bridge-cursor-${projectId}.json`);
2417
+ return (
2418
+ `[telegram]\n` +
2419
+ `bot_token = "${tg.bot_token}"\n` +
2420
+ `chat_id = "${tg.chat_id}"\n` +
2421
+ `agentchattr_url = "${tg.agentchattr_url}"\n` +
2422
+ `cursor_file = "${cursorFile}"\n`
2423
+ );
2424
+ }
2425
+
2426
+ // #383 Bug 3: AC's registry rejects any base name not pre-declared
2427
+ // in config.toml with `400 unknown base`. The bridge registers as
2428
+ // `telegram-bridge`, so every per-project AC config must declare it.
2429
+ // Idempotent: only appends if the section is not already present.
2430
+ function patchAgentchattrConfigForTelegramBridge(tomlText) {
2431
+ if (/^\[agents\.telegram-bridge\]\s*$/m.test(tomlText)) {
2432
+ return { text: tomlText, changed: false };
2433
+ }
2434
+ const sep = tomlText.length === 0 || tomlText.endsWith("\n") ? "" : "\n";
2435
+ const block = `\n[agents.telegram-bridge]\nlabel = "Telegram Bridge"\n`;
2436
+ return { text: tomlText + sep + block, changed: true };
2437
+ }
2438
+
2439
+ // #383 Bug 4: the upstream bridge treats env vars as higher
2440
+ // precedence than TOML values. If the parent shell exported
2441
+ // TELEGRAM_BOT_TOKEN for a different bot, the bridge silently ran
2442
+ // as the wrong identity. Scrub those keys from the child's env so
2443
+ // the TOML is the single source of truth.
2444
+ function buildTelegramBridgeSpawnEnv(parentEnv) {
2445
+ const env = { ...parentEnv };
2446
+ delete env.TELEGRAM_BOT_TOKEN;
2447
+ delete env.TELEGRAM_CHAT_ID;
2448
+ delete env.AGENTCHATTR_URL;
2449
+ return env;
2450
+ }
2451
+
2388
2452
  // #353: per-project log file for the bridge subprocess. The start
2389
2453
  // handler redirects stdout + stderr here so crashes (ImportError,
2390
2454
  // config parse, auth failure) are recoverable instead of
@@ -2502,7 +2566,8 @@ function getProjectTelegram(projectId) {
2502
2566
  return {
2503
2567
  bot_token: resolveToken(project.telegram.bot_token || ""),
2504
2568
  chat_id: project.telegram.chat_id || "",
2505
- agentchattr_url: cfg.agentchattr_url || "http://127.0.0.1:8300",
2569
+ // #383 Bug 1: prefer per-project URL over the global default.
2570
+ agentchattr_url: resolveProjectAgentchattrUrl(cfg, project),
2506
2571
  };
2507
2572
  } catch {
2508
2573
  return null;
@@ -2638,7 +2703,31 @@ router.post("/api/telegram", async (req, res) => {
2638
2703
  `pip output tail:\n${pipOutput.split("\n").slice(-10).join("\n")}`,
2639
2704
  });
2640
2705
  }
2641
- return res.json({ ok: true });
2706
+ // #383 Bug 3: ensure every known project's AC config declares
2707
+ // the `telegram-bridge` agent. Without this, AC's registry
2708
+ // rejects the bridge's register call with `400 unknown base`
2709
+ // and the bridge enters an infinite re-register loop.
2710
+ // Idempotent — append-only, skips configs that already have
2711
+ // the section. Does NOT restart AC servers; the operator
2712
+ // must click SERVER → Restart to load the new agent slug.
2713
+ const patched = [];
2714
+ try {
2715
+ const cfgAll = JSON.parse(fs.readFileSync(CONFIG_PATH, "utf-8"));
2716
+ for (const proj of cfgAll.projects || []) {
2717
+ if (!proj || !proj.id) continue;
2718
+ const acPath = projectAgentchattrConfigPath(proj.id);
2719
+ if (!fs.existsSync(acPath)) continue;
2720
+ try {
2721
+ const before = fs.readFileSync(acPath, "utf-8");
2722
+ const { text, changed } = patchAgentchattrConfigForTelegramBridge(before);
2723
+ if (changed) {
2724
+ fs.writeFileSync(acPath, text);
2725
+ patched.push(proj.id);
2726
+ }
2727
+ } catch {}
2728
+ }
2729
+ } catch {}
2730
+ return res.json({ ok: true, patched_projects: patched });
2642
2731
  }
2643
2732
  case "start": {
2644
2733
  const projectId = body.project_id;
@@ -2661,7 +2750,9 @@ router.post("/api/telegram", async (req, res) => {
2661
2750
  const tg = getProjectTelegram(projectId);
2662
2751
  if (!tg || !tg.bot_token || !tg.chat_id) return res.json({ ok: false, error: "Save bot_token and chat_id in project settings first." });
2663
2752
  const tomlPath = telegramConfigToml(projectId);
2664
- const tomlContent = `[telegram]\nbot_token = "${tg.bot_token}"\nchat_id = "${tg.chat_id}"\n\n[agentchattr]\nurl = "${tg.agentchattr_url}"\n`;
2753
+ // #383 Bug 2: write agentchattr_url inside [telegram]; the
2754
+ // bridge's load_config only reads from that section.
2755
+ const tomlContent = buildTelegramBridgeToml(tg, projectId);
2665
2756
  fs.writeFileSync(tomlPath, tomlContent, { mode: 0o600 });
2666
2757
  fs.chmodSync(tomlPath, 0o600);
2667
2758
  // #353: pre-flight import check so a fresh install with no
@@ -2710,9 +2801,15 @@ router.post("/api/telegram", async (req, res) => {
2710
2801
  }
2711
2802
  let child;
2712
2803
  try {
2804
+ // #383 Bug 4: scrub TELEGRAM_*/AGENTCHATTR_URL from the child
2805
+ // env so an operator shell that exports a different bot's
2806
+ // token (common on machines running AC2) can't silently
2807
+ // override the TOML. Makes the TOML the single source of
2808
+ // truth for the bridge's identity.
2713
2809
  child = spawn(venvPython, [bridgeScript, "--config", tomlPath], {
2714
2810
  detached: true,
2715
2811
  stdio: ["ignore", outFd, errFd],
2812
+ env: buildTelegramBridgeSpawnEnv(process.env),
2716
2813
  });
2717
2814
  child.unref();
2718
2815
  if (child.pid) fs.writeFileSync(telegramPidFile(projectId), String(child.pid));
@@ -2747,6 +2844,21 @@ router.post("/api/telegram", async (req, res) => {
2747
2844
  case "stop": {
2748
2845
  const projectId = body.project_id;
2749
2846
  if (!projectId) return res.json({ ok: false, error: "Missing project_id" });
2847
+ // #388: deregister the bridge from AC before killing so the slot
2848
+ // clears immediately instead of lingering for 60s as a stale -2/-3.
2849
+ // Awaited so a fast stop→start cycle doesn't race the deregister.
2850
+ try {
2851
+ const cfg = JSON.parse(fs.readFileSync(CONFIG_PATH, "utf-8"));
2852
+ const project = cfg.projects?.find((p) => p.id === projectId);
2853
+ const acUrl = resolveProjectAgentchattrUrl(cfg, project);
2854
+ if (acUrl) {
2855
+ const acPort = new URL(acUrl).port || "8300";
2856
+ await fetch(`http://127.0.0.1:${acPort}/api/deregister/telegram-bridge`, {
2857
+ method: "POST",
2858
+ signal: AbortSignal.timeout(3000),
2859
+ }).catch(() => {});
2860
+ }
2861
+ } catch {}
2750
2862
  try {
2751
2863
  const pf = telegramPidFile(projectId);
2752
2864
  if (fs.existsSync(pf)) {
@@ -2898,6 +3010,13 @@ module.exports.readLastLines = readLastLines;
2898
3010
  // #380: expose checkTelegramBridgePythonDeps so the bridge test can
2899
3011
  // exercise the venv-path interpreter argument round trip.
2900
3012
  module.exports.checkTelegramBridgePythonDeps = checkTelegramBridgePythonDeps;
3013
+ // #383: pure helpers exposed for unit tests in
3014
+ // routes.telegramBridge.test.js. No production callers outside
3015
+ // this file.
3016
+ module.exports.resolveProjectAgentchattrUrl = resolveProjectAgentchattrUrl;
3017
+ module.exports.buildTelegramBridgeToml = buildTelegramBridgeToml;
3018
+ module.exports.patchAgentchattrConfigForTelegramBridge = patchAgentchattrConfigForTelegramBridge;
3019
+ module.exports.buildTelegramBridgeSpawnEnv = buildTelegramBridgeSpawnEnv;
2901
3020
  // #236: expose sendViaWebSocket so the chat-ws-send regression test
2902
3021
  // can verify the ack/body/error paths against a fake AC ws server.
2903
3022
  module.exports.sendViaWebSocket = sendViaWebSocket;
@@ -11,7 +11,14 @@ const fs = require("node:fs");
11
11
  const os = require("node:os");
12
12
  const path = require("node:path");
13
13
  const { execFileSync } = require("node:child_process");
14
- const { readLastLines, checkTelegramBridgePythonDeps } = require("./routes");
14
+ const {
15
+ readLastLines,
16
+ checkTelegramBridgePythonDeps,
17
+ resolveProjectAgentchattrUrl,
18
+ buildTelegramBridgeToml,
19
+ patchAgentchattrConfigForTelegramBridge,
20
+ buildTelegramBridgeSpawnEnv,
21
+ } = require("./routes");
15
22
 
16
23
  const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "qw-bridge-log-"));
17
24
  function write(name, content) {
@@ -133,8 +140,99 @@ try {
133
140
  venvSkipped = true;
134
141
  }
135
142
 
143
+ // 12) #383 Bug 1: resolveProjectAgentchattrUrl prefers the
144
+ // per-project URL over the global default. Every project
145
+ // after the first uses a distinct port, so silently reading
146
+ // the global default routed bridge traffic to the wrong AC
147
+ // instance.
148
+ assert.equal(
149
+ resolveProjectAgentchattrUrl(
150
+ { agentchattr_url: "http://127.0.0.1:8300" },
151
+ { id: "quadwork", agentchattr_url: "http://127.0.0.1:8301" },
152
+ ),
153
+ "http://127.0.0.1:8301",
154
+ );
155
+ // Falls back to global default when the project has no URL of
156
+ // its own (legacy single-project installs).
157
+ assert.equal(
158
+ resolveProjectAgentchattrUrl(
159
+ { agentchattr_url: "http://127.0.0.1:8300" },
160
+ { id: "legacy" },
161
+ ),
162
+ "http://127.0.0.1:8300",
163
+ );
164
+ // Hard-coded fallback when neither is set.
165
+ assert.equal(
166
+ resolveProjectAgentchattrUrl({}, {}),
167
+ "http://127.0.0.1:8300",
168
+ );
169
+
170
+ // 13) #383 Bug 2: buildTelegramBridgeToml writes agentchattr_url
171
+ // inside [telegram]. The upstream bridge's load_config only
172
+ // reads from that section — a separate [agentchattr] section
173
+ // is silently ignored and the bridge falls back to its
174
+ // hardcoded :8300 default.
175
+ const toml13 = buildTelegramBridgeToml({
176
+ bot_token: "123:abc",
177
+ chat_id: "-42",
178
+ agentchattr_url: "http://127.0.0.1:8301",
179
+ }, "testproject");
180
+ assert.match(toml13, /^\[telegram\]/);
181
+ assert.match(toml13, /bot_token = "123:abc"/);
182
+ assert.match(toml13, /chat_id = "-42"/);
183
+ assert.match(toml13, /agentchattr_url = "http:\/\/127\.0\.0\.1:8301"/);
184
+ // #404: cursor_file must be per-project so multiple bridges
185
+ // don't clobber each other's position.
186
+ assert.match(toml13, /cursor_file = ".*telegram-bridge-cursor-testproject\.json"/);
187
+ // Must NOT emit a separate [agentchattr] section — the bridge
188
+ // would silently ignore it.
189
+ assert.equal(toml13.includes("\n[agentchattr]\n"), false);
190
+
191
+ // 14) #383 Bug 3: patchAgentchattrConfigForTelegramBridge is
192
+ // idempotent. The Install Bridge migration may run multiple
193
+ // times; it must not duplicate the section or corrupt the
194
+ // file.
195
+ const baseConfig =
196
+ "[agents.head]\nlabel = \"Head\"\n\n[agents.dev]\nlabel = \"Dev\"\n";
197
+ const first = patchAgentchattrConfigForTelegramBridge(baseConfig);
198
+ assert.equal(first.changed, true);
199
+ assert.match(first.text, /^\[agents\.telegram-bridge\]$/m);
200
+ assert.match(first.text, /label = "Telegram Bridge"/);
201
+ // Running a second time is a no-op.
202
+ const second = patchAgentchattrConfigForTelegramBridge(first.text);
203
+ assert.equal(second.changed, false);
204
+ assert.equal(second.text, first.text);
205
+ // A config that was hand-patched during diagnosis is recognized
206
+ // as already-correct — do not clobber the operator's edit.
207
+ const handPatched =
208
+ baseConfig + "\n[agents.telegram-bridge]\nlabel = \"Telegram Bridge\"\n";
209
+ const third = patchAgentchattrConfigForTelegramBridge(handPatched);
210
+ assert.equal(third.changed, false);
211
+ assert.equal(third.text, handPatched);
212
+
213
+ // 15) #383 Bug 4: buildTelegramBridgeSpawnEnv strips the three
214
+ // env vars the upstream bridge treats as higher-precedence
215
+ // than TOML. Without this, an operator shell that exported a
216
+ // different bot's token (common on machines running AC2)
217
+ // silently overrode the QuadWork-written TOML and the bridge
218
+ // ran as the wrong identity.
219
+ const scrubbed = buildTelegramBridgeSpawnEnv({
220
+ PATH: "/usr/bin",
221
+ HOME: "/home/op",
222
+ TELEGRAM_BOT_TOKEN: "wrong-token",
223
+ TELEGRAM_CHAT_ID: "-999",
224
+ AGENTCHATTR_URL: "http://127.0.0.1:9999",
225
+ });
226
+ assert.equal(scrubbed.TELEGRAM_BOT_TOKEN, undefined);
227
+ assert.equal(scrubbed.TELEGRAM_CHAT_ID, undefined);
228
+ assert.equal(scrubbed.AGENTCHATTR_URL, undefined);
229
+ // Non-telegram keys must pass through untouched — the bridge
230
+ // still needs PATH/HOME/etc. to find python and open files.
231
+ assert.equal(scrubbed.PATH, "/usr/bin");
232
+ assert.equal(scrubbed.HOME, "/home/op");
233
+
136
234
  console.log(
137
- "routes.telegramBridge.test.js: all assertions passed (11 cases" +
235
+ "routes.telegramBridge.test.js: all assertions passed (15 cases" +
138
236
  (venvSkipped ? ", case 11 pip step skipped" : "") +
139
237
  ")",
140
238
  );
@@ -33,7 +33,8 @@ Branch naming (strict): `task/<issue-number>-<short-slug>`
33
33
 
34
34
  ## Communication Rules
35
35
 
36
- - **No acknowledgment messages** — don't send "on it", "noted", "standing by"
36
+ - **Always reply to the operator** — when the operator (sender: "user") addresses you in chat, you MUST reply via `chat_send`. The operator's terminal is invisible; if you don't `chat_send`, your response does not exist.
37
+ - **No acknowledgment messages between agents** — don't send "on it", "noted", "standing by" to other agents. This rule does NOT apply to operator messages — always reply to the operator.
37
38
  - **No status updates to Head** — Dev works silently until PR is ready
38
39
  - **Strict routing**: Dev→Reviewer1/Reviewer2 (review) → Dev→Head (merge request) → Head→Dev (merged)
39
40
  - **Post-merge silence**: Head sends ONE "merged" message. No further replies from anyone.
@@ -37,6 +37,14 @@ cwd = "{{dev_cwd}}"
37
37
  color = "#da7756"
38
38
  label = "Dev Builder"
39
39
 
40
+ # #383: AC's registry rejects bases not declared in config.toml.
41
+ # The Telegram bridge registers as `telegram-bridge`, so every
42
+ # per-project AC config must declare it. The bridge has no
43
+ # command/cwd of its own — it is a long-running external client
44
+ # that posts to AC's HTTP API.
45
+ [agents.telegram-bridge]
46
+ label = "Telegram Bridge"
47
+
40
48
  [routing]
41
49
  default = "none"
42
50
  max_agent_hops = 30
@@ -85,5 +85,7 @@ Head owns this file — do not edit it. Read it when you need context on the bat
85
85
  - After BOTH Reviewer1 AND Reviewer2 approve → ONLY THEN message **@head** to request merge.
86
86
  - Always include issue/PR numbers in messages
87
87
  - Report blockers to @head immediately
88
+ - **Always reply to the operator**: when the operator (sender: "user") sends a message that mentions you or is addressed to you, you MUST reply via `chat_send`. If it's a question, answer it. If it's an instruction, confirm what you will do, then do it. If it's not actionable for your role, reply explaining that and suggest which agent should handle it. The operator's terminal is invisible — if you don't `chat_send`, your response does not exist.
89
+ - **No acknowledgment messages between agents** — don't send "on it", "noted", "standing by" to other agents. This rule does NOT apply to operator messages — always reply to the operator.
88
90
  - **Do NOT send ANY message to @head between assignment and merge request** — no acks, no status updates.
89
91
  - **After merge confirmation from Head**: do NOT reply. The loop is COMPLETE — silence is required.
@@ -97,5 +97,7 @@ When the operator asks you in chat to start a task or batch:
97
97
  - **ALWAYS @mention the next agent** — never @user or @human
98
98
  - Route: you → @dev for task assignments. You do NOT message @reviewer1 or @reviewer2 directly.
99
99
  - Include issue/PR numbers in all messages
100
+ - **Always reply to the operator**: when the operator (sender: "user") sends a message that mentions you or is addressed to you, you MUST reply via `chat_send`. If it's a question, answer it. If it's an instruction, confirm what you will do, then do it. If it's not actionable for your role, reply explaining that and suggest which agent should handle it. The operator's terminal is invisible — if you don't `chat_send`, your response does not exist.
101
+ - **No acknowledgment messages between agents** — don't send "on it", "noted", "standing by" to other agents. This rule does NOT apply to operator messages — always reply to the operator.
100
102
  - **Do NOT reply to acknowledgments** — if Dev says "on it" or similar, do NOT respond. Wait silently for the PR.
101
103
  - **After merge**: send ONE message: "@dev PR #<number> merged. Issue #<number> closed." — no further replies needed.
@@ -101,5 +101,7 @@ Run this once at the start of each session.
101
101
  - **After BLOCK**: send message to @head AND @dev — Head decides whether to reassign or close
102
102
  - Always include PR number in messages
103
103
  - Tag specific findings with file:line references
104
- - **Do NOT send "standing by" or acknowledgment messages** only message when you have a completed review to deliver.
104
+ - **Always reply to the operator**: when the operator (sender: "user") sends a message that mentions you or is addressed to you, you MUST reply via `chat_send`. If it's a question, answer it. If it's an instruction, confirm what you will do, then do it. If it's not actionable for your role, reply explaining that and suggest which agent should handle it. The operator's terminal is invisible — if you don't `chat_send`, your response does not exist.
105
+ - **No acknowledgment messages between agents** — don't send "on it", "noted", "standing by" to other agents. This rule does NOT apply to operator messages — always reply to the operator.
106
+ - Only send unsolicited messages when delivering a completed review verdict. But ALWAYS reply when the operator addresses you directly — even if the message is not a review request. The operator may be asking about your status, giving instructions, or testing connectivity.
105
107
  - **After merge confirmation from Head**: do NOT reply. The loop is complete — no acknowledgment needed.
@@ -101,5 +101,7 @@ Run this once at the start of each session.
101
101
  - **After BLOCK**: send message to @head AND @dev — Head decides whether to reassign or close
102
102
  - Always include PR number in messages
103
103
  - Tag specific findings with file:line references
104
- - **Do NOT send "standing by" or acknowledgment messages** only message when you have a completed review to deliver.
104
+ - **Always reply to the operator**: when the operator (sender: "user") sends a message that mentions you or is addressed to you, you MUST reply via `chat_send`. If it's a question, answer it. If it's an instruction, confirm what you will do, then do it. If it's not actionable for your role, reply explaining that and suggest which agent should handle it. The operator's terminal is invisible — if you don't `chat_send`, your response does not exist.
105
+ - **No acknowledgment messages between agents** — don't send "on it", "noted", "standing by" to other agents. This rule does NOT apply to operator messages — always reply to the operator.
106
+ - Only send unsolicited messages when delivering a completed review verdict. But ALWAYS reply when the operator addresses you directly — even if the message is not a review request. The operator may be asking about your status, giving instructions, or testing connectivity.
105
107
  - **After merge confirmation from Head**: do NOT reply. The loop is complete — no acknowledgment needed.
@@ -1 +0,0 @@
1
- (globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,15352,(e,t,n)=>{"use strict";Object.defineProperty(n,"__esModule",{value:!0}),Object.defineProperty(n,"BailoutToCSR",{enumerable:!0,get:function(){return l}});let r=e.r(17071);function l({reason:e,children:t}){if("u"<typeof window)throw Object.defineProperty(new r.BailoutToCSRError(e),"__NEXT_ERROR_CODE",{value:"E394",enumerable:!1,configurable:!0});return t}},87083,(e,t,n)=>{"use strict";function r(e){return e.split("/").map(e=>encodeURIComponent(e)).join("/")}Object.defineProperty(n,"__esModule",{value:!0}),Object.defineProperty(n,"encodeURIPath",{enumerable:!0,get:function(){return r}})},48021,(e,t,n)=>{"use strict";Object.defineProperty(n,"__esModule",{value:!0}),Object.defineProperty(n,"PreloadChunks",{enumerable:!0,get:function(){return a}});let r=e.r(85899),l=e.r(90184),u=e.r(27807),o=e.r(87083),s=e.r(7866);function a({moduleIds:e}){if("u">typeof window)return null;let t=u.workAsyncStorage.getStore();if(void 0===t)return null;let n=[];if(t.reactLoadableManifest&&e){let r=t.reactLoadableManifest;for(let t of e){if(!r[t])continue;let e=r[t].files;n.push(...e)}}if(0===n.length)return null;let i=(0,s.getAssetTokenQuery)();return(0,r.jsx)(r.Fragment,{children:n.map(e=>{let n=`${t.assetPrefix}/_next/${(0,o.encodeURIPath)(e)}${i}`;return e.endsWith(".css")?(0,r.jsx)("link",{precedence:"dynamic",href:n,rel:"stylesheet",as:"style",nonce:t.nonce},e):((0,l.preload)(n,{as:"script",fetchPriority:"low",nonce:t.nonce}),null)})})}},82525,(e,t,n)=>{"use strict";Object.defineProperty(n,"__esModule",{value:!0}),Object.defineProperty(n,"default",{enumerable:!0,get:function(){return i}});let r=e.r(85899),l=e.r(4232),u=e.r(15352),o=e.r(48021);function s(e){return{default:e&&"default"in e?e.default:e}}let a={loader:()=>Promise.resolve(s(()=>null)),loading:null,ssr:!0},i=function(e){let t={...a,...e},n=(0,l.lazy)(()=>t.loader().then(s)),i=t.loading;function d(e){let s=i?(0,r.jsx)(i,{isLoading:!0,pastDelay:!0,error:null}):null,a=!t.ssr||!!t.loading,d=a?l.Suspense:l.Fragment,c=t.ssr?(0,r.jsxs)(r.Fragment,{children:["u"<typeof window?(0,r.jsx)(o.PreloadChunks,{moduleIds:t.modules}):null,(0,r.jsx)(n,{...e})]}):(0,r.jsx)(u.BailoutToCSR,{reason:"next/dynamic",children:(0,r.jsx)(n,{...e})});return(0,r.jsx)(d,{...a?{fallback:s}:{},children:c})}return d.displayName="LoadableComponent",d}},52279,(e,t,n)=>{"use strict";Object.defineProperty(n,"__esModule",{value:!0}),Object.defineProperty(n,"default",{enumerable:!0,get:function(){return l}});let r=e.r(5581)._(e.r(82525));function l(e,t){let n={};"function"==typeof e&&(n.loader=e);let l={...n,...t};return(0,r.default)({...l,modules:l.loadableGenerated?.modules})}("function"==typeof n.default||"object"==typeof n.default&&null!==n.default)&&void 0===n.default.__esModule&&(Object.defineProperty(n.default,"__esModule",{value:!0}),Object.assign(n.default,n),t.exports=n.default)},66610,e=>{"use strict";var t=e.i(85899),n=e.i(52279),r=e.i(16353);let l=(0,n.default)(()=>e.A(34183),{loadableGenerated:{modules:[71714]},ssr:!1});e.s(["default",0,function(){let e=(0,r.usePathname)().split("/")[2]||"";return e&&"_"!==e?(0,t.jsx)("div",{className:"w-full h-full",children:(0,t.jsx)(l,{projectId:e})}):null}])},34183,e=>{e.v(t=>Promise.all(["static/chunks/0whtwwbpg72ar.js","static/chunks/0m83k84.midd1.js","static/chunks/17oc2l.ekcs8b.css"].map(t=>e.l(t))).then(()=>t(71714)))}]);
@@ -1 +0,0 @@
1
- (globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,62206,(e,t,r)=>{"use strict";Object.defineProperty(r,"__esModule",{value:!0});var n={assign:function(){return l},searchParamsToUrlQuery:function(){return s},urlQueryToSearchParams:function(){return i}};for(var o in n)Object.defineProperty(r,o,{enumerable:!0,get:n[o]});function s(e){let t={};for(let[r,n]of e.entries()){let e=t[r];void 0===e?t[r]=n:Array.isArray(e)?e.push(n):t[r]=[e,n]}return t}function a(e){return"string"==typeof e?e:("number"!=typeof e||isNaN(e))&&"boolean"!=typeof e?"":String(e)}function i(e){let t=new URLSearchParams;for(let[r,n]of Object.entries(e))if(Array.isArray(n))for(let e of n)t.append(r,a(e));else t.set(r,a(n));return t}function l(e,...t){for(let r of t){for(let t of r.keys())e.delete(t);for(let[t,n]of r.entries())e.append(t,n)}return e}},71281,(e,t,r)=>{"use strict";Object.defineProperty(r,"__esModule",{value:!0});var n={DecodeError:function(){return g},MiddlewareNotFoundError:function(){return v},MissingStaticPage:function(){return y},NormalizeError:function(){return b},PageNotFoundError:function(){return j},SP:function(){return x},ST:function(){return m},WEB_VITALS:function(){return s},execOnce:function(){return a},getDisplayName:function(){return d},getLocationOrigin:function(){return u},getURL:function(){return c},isAbsoluteUrl:function(){return l},isResSent:function(){return f},loadGetInitialProps:function(){return p},normalizeRepeatedSlashes:function(){return h},stringifyError:function(){return w}};for(var o in n)Object.defineProperty(r,o,{enumerable:!0,get:n[o]});let s=["CLS","FCP","FID","INP","LCP","TTFB"];function a(e){let t,r=!1;return(...n)=>(r||(r=!0,t=e(...n)),t)}let i=/^[a-zA-Z][a-zA-Z\d+\-.]*?:/,l=e=>i.test(e);function u(){let{protocol:e,hostname:t,port:r}=window.location;return`${e}//${t}${r?":"+r:""}`}function c(){let{href:e}=window.location,t=u();return e.substring(t.length)}function d(e){return"string"==typeof e?e:e.displayName||e.name||"Unknown"}function f(e){return e.finished||e.headersSent}function h(e){let t=e.split("?");return t[0].replace(/\\/g,"/").replace(/\/\/+/g,"/")+(t[1]?`?${t.slice(1).join("?")}`:"")}async function p(e,t){let r=t.res||t.ctx&&t.ctx.res;if(!e.getInitialProps)return t.ctx&&t.Component?{pageProps:await p(t.Component,t.ctx)}:{};let n=await e.getInitialProps(t);if(r&&f(r))return n;if(!n)throw Object.defineProperty(Error(`"${d(e)}.getInitialProps()" should resolve to an object. But found "${n}" instead.`),"__NEXT_ERROR_CODE",{value:"E1025",enumerable:!1,configurable:!0});return n}let x="u">typeof performance,m=x&&["mark","measure","getEntriesByName"].every(e=>"function"==typeof performance[e]);class g extends Error{}class b extends Error{}class j extends Error{constructor(e){super(),this.code="ENOENT",this.name="PageNotFoundError",this.message=`Cannot find module for page: ${e}`}}class y extends Error{constructor(e,t){super(),this.message=`Failed to load static file for page: ${e} ${t}`}}class v extends Error{constructor(){super(),this.code="ENOENT",this.message="Cannot find the middleware module"}}function w(e){return JSON.stringify({message:e.message,stack:e.stack})}},11938,(e,t,r)=>{"use strict";Object.defineProperty(r,"__esModule",{value:!0}),Object.defineProperty(r,"warnOnce",{enumerable:!0,get:function(){return n}});let n=e=>{}},16353,(e,t,r)=>{t.exports=e.r(89093)},56749,(e,t,r)=>{"use strict";Object.defineProperty(r,"__esModule",{value:!0});var n={formatUrl:function(){return i},formatWithValidation:function(){return u},urlObjectKeys:function(){return l}};for(var o in n)Object.defineProperty(r,o,{enumerable:!0,get:n[o]});let s=e.r(60224)._(e.r(62206)),a=/https?|ftp|gopher|file/;function i(e){let{auth:t,hostname:r}=e,n=e.protocol||"",o=e.pathname||"",i=e.hash||"",l=e.query||"",u=!1;t=t?encodeURIComponent(t).replace(/%3A/i,":")+"@":"",e.host?u=t+e.host:r&&(u=t+(~r.indexOf(":")?`[${r}]`:r),e.port&&(u+=":"+e.port)),l&&"object"==typeof l&&(l=String(s.urlQueryToSearchParams(l)));let c=e.search||l&&`?${l}`||"";return n&&!n.endsWith(":")&&(n+=":"),e.slashes||(!n||a.test(n))&&!1!==u?(u="//"+(u||""),o&&"/"!==o[0]&&(o="/"+o)):u||(u=""),i&&"#"!==i[0]&&(i="#"+i),c&&"?"!==c[0]&&(c="?"+c),o=o.replace(/[?#]/g,encodeURIComponent),c=c.replace("#","%23"),`${n}${u}${o}${c}${i}`}let l=["auth","hash","host","hostname","href","path","pathname","port","protocol","query","search","slashes"];function u(e){return i(e)}},88173,(e,t,r)=>{"use strict";Object.defineProperty(r,"__esModule",{value:!0}),Object.defineProperty(r,"useMergedRef",{enumerable:!0,get:function(){return o}});let n=e.r(4232);function o(e,t){let r=(0,n.useRef)(null),o=(0,n.useRef)(null);return(0,n.useCallback)(n=>{if(null===n){let e=r.current;e&&(r.current=null,e());let t=o.current;t&&(o.current=null,t())}else e&&(r.current=s(e,n)),t&&(o.current=s(t,n))},[e,t])}function s(e,t){if("function"!=typeof e)return e.current=t,()=>{e.current=null};{let r=e(t);return"function"==typeof r?r:()=>e(null)}}("function"==typeof r.default||"object"==typeof r.default&&null!==r.default)&&void 0===r.default.__esModule&&(Object.defineProperty(r.default,"__esModule",{value:!0}),Object.assign(r.default,r),t.exports=r.default)},47244,(e,t,r)=>{"use strict";Object.defineProperty(r,"__esModule",{value:!0}),Object.defineProperty(r,"isLocalURL",{enumerable:!0,get:function(){return s}});let n=e.r(71281),o=e.r(17608);function s(e){if(!(0,n.isAbsoluteUrl)(e))return!0;try{let t=(0,n.getLocationOrigin)(),r=new URL(e,t);return r.origin===t&&(0,o.hasBasePath)(r.pathname)}catch(e){return!1}}},33010,(e,t,r)=>{"use strict";Object.defineProperty(r,"__esModule",{value:!0}),Object.defineProperty(r,"errorOnce",{enumerable:!0,get:function(){return n}});let n=e=>{}},2270,(e,t,r)=>{"use strict";Object.defineProperty(r,"__esModule",{value:!0});var n={default:function(){return g},useLinkStatus:function(){return j}};for(var o in n)Object.defineProperty(r,o,{enumerable:!0,get:n[o]});let s=e.r(60224),a=e.r(85899),i=s._(e.r(4232)),l=e.r(56749),u=e.r(4340),c=e.r(88173),d=e.r(71281),f=e.r(93614);e.r(11938);let h=e.r(77710),p=e.r(97991),x=e.r(47244),m=e.r(44367);function g(t){var r,n;let o,s,g,[j,y]=(0,i.useOptimistic)(p.IDLE_LINK_STATUS),v=(0,i.useRef)(null),{href:w,as:k,children:N,prefetch:E=null,passHref:P,replace:S,shallow:C,scroll:_,onClick:O,onMouseEnter:T,onTouchStart:L,legacyBehavior:R=!1,onNavigate:M,transitionTypes:A,ref:I,unstable_dynamicOnHover:$,...B}=t;o=N,R&&("string"==typeof o||"number"==typeof o)&&(o=(0,a.jsx)("a",{children:o}));let U=i.default.useContext(u.AppRouterContext),F=!1!==E,W=!1!==E?null===(n=E)||"auto"===n?m.FetchStrategy.PPR:m.FetchStrategy.Full:m.FetchStrategy.PPR,z="string"==typeof(r=k||w)?r:(0,l.formatUrl)(r);if(R){if(o?.$$typeof===Symbol.for("react.lazy"))throw Object.defineProperty(Error("`<Link legacyBehavior>` received a direct child that is either a Server Component, or JSX that was loaded with React.lazy(). This is not supported. Either remove legacyBehavior, or make the direct child a Client Component that renders the Link's `<a>` tag."),"__NEXT_ERROR_CODE",{value:"E863",enumerable:!1,configurable:!0});s=i.default.Children.only(o)}let D=R?s&&"object"==typeof s&&s.ref:I,q=i.default.useCallback(e=>(null!==U&&(v.current=(0,p.mountLinkInstance)(e,z,U,W,F,y)),()=>{v.current&&((0,p.unmountLinkForCurrentNavigation)(v.current),v.current=null),(0,p.unmountPrefetchableInstance)(e)}),[F,z,U,W,y]),K={ref:(0,c.useMergedRef)(q,D),onClick(t){R||"function"!=typeof O||O(t),R&&s.props&&"function"==typeof s.props.onClick&&s.props.onClick(t),!U||t.defaultPrevented||function(t,r,n,o,s,a,l){if("u">typeof window){let u,{nodeName:c}=t.currentTarget;if("A"===c.toUpperCase()&&((u=t.currentTarget.getAttribute("target"))&&"_self"!==u||t.metaKey||t.ctrlKey||t.shiftKey||t.altKey||t.nativeEvent&&2===t.nativeEvent.which)||t.currentTarget.hasAttribute("download"))return;if(!(0,x.isLocalURL)(r)){o&&(t.preventDefault(),location.replace(r));return}if(t.preventDefault(),a){let e=!1;if(a({preventDefault:()=>{e=!0}}),e)return}let{dispatchNavigateAction:d}=e.r(93845);i.default.startTransition(()=>{d(r,o?"replace":"push",!1===s?h.ScrollBehavior.NoScroll:h.ScrollBehavior.Default,n.current,l)})}}(t,z,v,S,_,M,A)},onMouseEnter(e){R||"function"!=typeof T||T(e),R&&s.props&&"function"==typeof s.props.onMouseEnter&&s.props.onMouseEnter(e),U&&F&&(0,p.onNavigationIntent)(e.currentTarget,!0===$)},onTouchStart:function(e){R||"function"!=typeof L||L(e),R&&s.props&&"function"==typeof s.props.onTouchStart&&s.props.onTouchStart(e),U&&F&&(0,p.onNavigationIntent)(e.currentTarget,!0===$)}};return(0,d.isAbsoluteUrl)(z)?K.href=z:R&&!P&&("a"!==s.type||"href"in s.props)||(K.href=(0,f.addBasePath)(z)),g=R?i.default.cloneElement(s,K):(0,a.jsx)("a",{...B,...K,children:o}),(0,a.jsx)(b.Provider,{value:j,children:g})}e.r(33010);let b=(0,i.createContext)(p.IDLE_LINK_STATUS),j=()=>(0,i.useContext)(b);("function"==typeof r.default||"object"==typeof r.default&&null!==r.default)&&void 0===r.default.__esModule&&(Object.defineProperty(r.default,"__esModule",{value:!0}),Object.assign(r.default,r),t.exports=r.default)},86081,e=>{"use strict";var t=e.i(85899),r=e.i(2270),n=e.i(16353),o=e.i(4232);function s(){return(0,t.jsxs)("svg",{width:"20",height:"20",viewBox:"0 0 20 20",fill:"none",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round",strokeLinejoin:"round",children:[(0,t.jsx)("path",{d:"M3 10L10 3l7 7"}),(0,t.jsx)("path",{d:"M5 8.5V16h3.5v-4h3v4H15V8.5"})]})}function a(){return(0,t.jsxs)("svg",{width:"18",height:"18",viewBox:"0 0 18 18",fill:"none",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round",strokeLinejoin:"round",children:[(0,t.jsx)("circle",{cx:"9",cy:"9",r:"2.5"}),(0,t.jsx)("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"})]})}function i(){return(0,t.jsx)("svg",{width:"16",height:"16",viewBox:"0 0 16 16",fill:"none",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round",children:(0,t.jsx)("path",{d:"M8 3v10M3 8h10"})})}function l({project:e,isActive:n}){let[s,a]=(0,o.useState)(null),i=(0,o.useRef)(null);return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(r.default,{ref:i,href:`/project/${e.id}`,onMouseEnter:()=>{let e=i.current?.getBoundingClientRect();e&&a({top:e.top+e.height/2})},onMouseLeave:()=>a(null),children:(0,t.jsx)("div",{className:`w-10 h-10 flex items-center justify-center rounded-full text-[11px] font-semibold uppercase tracking-tight transition-colors ${n?"border-2 border-accent text-accent":"border border-border text-text-muted hover:text-text hover:bg-[#1a1a1a]"}`,children:e.name.slice(0,2)||"?"})}),s&&(0,t.jsx)("div",{className:"fixed px-2 py-1 bg-bg-surface border border-border text-text text-xs whitespace-nowrap pointer-events-none z-50",style:{left:72,top:s.top,transform:"translateY(-50%)"},children:e.name})]})}e.s(["default",0,function(){let e=(0,n.usePathname)(),[u,c]=(0,o.useState)([]),[d,f]=(0,o.useState)("online");(0,o.useEffect)(()=>{fetch("/api/config").then(e=>{if(!e.ok)throw Error(`Config fetch failed: ${e.status}`);return e.json()}).then(e=>c((e.projects||[]).filter(e=>!e.archived))).catch(()=>{})},[]),(0,o.useEffect)(()=>{let e,t=!1,r=async()=>{try{let e=await fetch("/api/health",{signal:AbortSignal.timeout(3e3)});if(t)return;e.ok?f(e=>"offline"===e?"recovering":"online"):f("offline")}catch{if(t)return;f("offline")}t||(e=setTimeout(r,5e3))};return r(),()=>{t=!0,clearTimeout(e)}},[]),(0,o.useEffect)(()=>{if("recovering"===d){let e=setTimeout(()=>f("online"),1500);return()=>clearTimeout(e)}},[d]);let h="/"===e,p="/settings"===e,x=e.startsWith("/project/")?e.split("/")[2]:null;return(0,t.jsxs)("aside",{className:"w-16 shrink-0 h-full border-r border-border bg-bg-surface flex flex-col items-center py-3",children:[(0,t.jsx)(r.default,{href:"/",className:`w-10 h-10 flex items-center justify-center rounded-sm transition-colors ${h?"text-accent":"text-text-muted hover:text-text hover:bg-[#1a1a1a]"}`,title:"Home",children:(0,t.jsx)(s,{})}),(0,t.jsx)("div",{className:"w-6 h-px bg-border my-2"}),(0,t.jsxs)("div",{className:"flex-1 flex flex-col items-center gap-2 overflow-y-auto min-h-0",children:[u.map(e=>{let r=x===e.id;return(0,t.jsx)(l,{project:e,isActive:r},e.id)}),(0,t.jsx)(r.default,{href:"/setup",className:"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",children:(0,t.jsx)(i,{})})]}),(0,t.jsx)("div",{className:"w-6 h-px bg-border my-2"}),"online"!==d&&(0,t.jsxs)("div",{className:"mb-2 relative group",children:[(0,t.jsx)("div",{className:`w-3 h-3 rounded-full ${"offline"===d?"bg-red-500 animate-pulse":"bg-green-500"}`}),(0,t.jsx)("div",{className:"fixed left-16 ml-2 px-2 py-1 bg-bg-surface border border-border text-xs whitespace-nowrap z-50 hidden group-hover:block",style:{transform:"translateY(-50%)",top:"auto"},children:"offline"===d?"Backend offline — run quadwork start":"Backend reconnected"})]}),(0,t.jsx)(r.default,{href:"/settings",className:`w-10 h-10 flex items-center justify-center rounded-sm transition-colors ${p?"text-accent":"text-text-muted hover:text-text hover:bg-[#1a1a1a]"}`,title:"Settings",children:(0,t.jsx)(a,{})})]})}])},34852,e=>{"use strict";var t=e.i(85899),r=e.i(2270),n=e.i(4232);function o({open:e,onClose:r}){return((0,n.useEffect)(()=>{if(!e)return;let t=e=>{"Escape"===e.key&&r()};return window.addEventListener("keydown",t),()=>window.removeEventListener("keydown",t)},[e,r]),e)?(0,t.jsx)("div",{className:"fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm",onClick:r,role:"dialog","aria-modal":"true","aria-labelledby":"about-title",children:(0,t.jsxs)("div",{className:"relative mx-4 max-w-lg w-full rounded-lg border border-white/10 bg-neutral-950 p-6 shadow-2xl",onClick:e=>e.stopPropagation(),children:[(0,t.jsx)("button",{type:"button",onClick:r,"aria-label":"Close",className:"absolute right-3 top-3 rounded p-1 text-neutral-400 hover:bg-white/5 hover:text-white",children:(0,t.jsx)("svg",{width:"18",height:"18",viewBox:"0 0 20 20",fill:"none",stroke:"currentColor",strokeWidth:"1.8",children:(0,t.jsx)("path",{d:"M4 4l12 12M16 4L4 16",strokeLinecap:"round"})})}),(0,t.jsx)("h2",{id:"about-title",className:"text-lg font-semibold text-white",children:"What is QuadWork?"}),(0,t.jsx)("p",{className:"mt-3 text-sm leading-relaxed text-neutral-300",children:"QuadWork is a local dashboard that runs a team of 4 AI agents — Head, Dev, and two Reviewers — that code, review, and ship while you sleep."}),(0,t.jsx)("p",{className:"mt-3 text-sm leading-relaxed text-neutral-300",children:"Every task follows a strict GitHub workflow: Issue → Branch → Pull Request → 2 Reviews → Merge. Branch protection ensures no agent can skip the process."}),(0,t.jsx)("h3",{className:"mt-5 text-sm font-semibold text-white",children:"Why QuadWork?"}),(0,t.jsxs)("ul",{className:"mt-2 space-y-1.5 text-sm text-neutral-300",children:[(0,t.jsxs)("li",{children:["🤖 ",(0,t.jsx)("b",{children:"Run 24/7"})," — agents work overnight while you rest"]}),(0,t.jsxs)("li",{children:["🛡️ ",(0,t.jsx)("b",{children:"Always reviewed"})," — every PR needs 2 independent approvals"]}),(0,t.jsxs)("li",{children:["🔒 ",(0,t.jsx)("b",{children:"Local-first"})," — runs entirely on your machine, no data leaves"]}),(0,t.jsxs)("li",{children:["🧰 ",(0,t.jsx)("b",{children:"Bring your own CLI"})," — works with Claude Code, Codex, or both"]}),(0,t.jsxs)("li",{children:["📦 ",(0,t.jsx)("b",{children:"One install"})," — ",(0,t.jsx)("code",{className:"rounded bg-white/5 px-1 py-0.5 text-[12px]",children:"npx quadwork init"})," and you're set"]})]}),(0,t.jsx)("div",{className:"mt-5",children:(0,t.jsx)("a",{href:"https://github.com/realproject7/quadwork",target:"_blank",rel:"noopener noreferrer",className:"inline-flex items-center gap-1 text-sm text-blue-400 hover:text-blue-300",children:"Read the full docs on GitHub →"})})]})}):null}let s=["sleep.","eat.","enjoy life.","touch grass.","spend time with people.","watch a movie.","take a vacation.","go for a run."],a="quadwork_tagline_animation";function i(e){return!Number.isFinite(e)||e<=0?"0h":e<1?`${(60*e).toFixed(0)}m`:`${e.toFixed(1)}h`}e.s(["default",0,function(){let[e,l]=(0,n.useState)(!1);(0,n.useEffect)(()=>{l(!0)},[]);let[u,c]=(0,n.useState)(!1),[d,f]=(0,n.useState)(null),[h,p]=(0,n.useState)(!1);(0,n.useEffect)(()=>{let e=!1,t=()=>{fetch("/api/activity/stats").then(e=>e.ok?e.json():null).then(t=>{!e&&t&&f(t)}).catch(()=>{})};t();let r=setInterval(t,6e4);return()=>{e=!0,clearInterval(r)}},[]);let[x,m]=(0,n.useState)(!0);(0,n.useEffect)(()=>{try{let e=window.localStorage.getItem(a);"off"===e&&m(!1)}catch{}},[]);let g=function(e,t){let[r,o]=(0,n.useState)(0),[s,a]=(0,n.useState)(""),[i,l]=(0,n.useState)("typing");return(0,n.useEffect)(()=>{let n;if(!t)return;let u=e[r];return"typing"===i?n=s.length<u.length?setTimeout(()=>a(u.slice(0,s.length+1)),70):setTimeout(()=>l("holding"),0):"holding"===i?n=setTimeout(()=>l("deleting"),2e3):s.length>0?n=setTimeout(()=>a(u.slice(0,s.length-1)),35):(o(t=>(t+1)%e.length),l("typing")),()=>clearTimeout(n)},[s,i,r,e,t]),s}(s,x),b=x?g:s[0]||"",[j,y]=(0,n.useState)(!1);(0,n.useEffect)(()=>{if(!x)return void y(!0);let e=setTimeout(()=>y(!0),5e3);return()=>clearTimeout(e)},[x]);let v=(0,n.useRef)(!1);return((0,n.useEffect)(()=>{x&&(g.length>0?v.current=!0:v.current&&y(!0))},[x,g]),e)?(0,t.jsxs)(t.Fragment,{children:[(0,t.jsxs)("header",{className:"sticky top-0 z-40 flex h-12 items-center justify-between border-b border-white/10 bg-neutral-950/90 px-4 backdrop-blur",children:[(0,t.jsxs)("div",{className:"flex items-center gap-3 min-w-0",children:[(0,t.jsx)(r.default,{href:"/",className:"text-sm font-bold text-white hover:text-blue-400 shrink-0",children:"QuadWork"}),(0,t.jsx)("span",{className:"hidden sm:inline text-neutral-600",children:"|"}),(0,t.jsxs)("span",{className:"hidden sm:inline text-[13px] text-neutral-400 truncate",children:["Your AI dev team while you"," ",(0,t.jsx)("span",{className:"text-neutral-200",children:b}),x&&(0,t.jsx)("span",{className:"ml-0.5 inline-block w-[1px] h-[12px] align-middle bg-neutral-400 animate-qw-blink"})]}),j&&(0,t.jsx)("button",{type:"button",onClick:()=>{m(e=>{let t=!e;try{window.localStorage.setItem(a,t?"on":"off")}catch{}return t})},"aria-label":x?"Pause tagline animation":"Resume tagline animation","aria-pressed":x,title:x?"Pause tagline animation":"Resume tagline animation",className:"hidden sm:inline-flex items-center justify-center w-3.5 h-3.5 ml-1 rounded-full border border-white/15 text-neutral-500 hover:text-white hover:border-white/40 transition-colors text-[8px]",children:x?"❚❚":"▶"})]}),(0,t.jsxs)("div",{className:"flex items-center gap-3 shrink-0",children:[d&&(0,t.jsxs)("div",{className:"relative hidden md:flex items-center gap-2 text-[10px] text-neutral-500",onMouseEnter:()=>p(!0),onMouseLeave:()=>p(!1),onFocus:()=>p(!0),onBlur:()=>p(!1),tabIndex:0,children:[(0,t.jsx)("span",{className:"text-neutral-200",children:"Your AI team worked:"}),(0,t.jsxs)("span",{children:["Today ",(0,t.jsx)("span",{className:"text-neutral-200",children:i(d.today)})]}),(0,t.jsx)("span",{className:"text-neutral-700",children:"·"}),(0,t.jsxs)("span",{children:["Week ",(0,t.jsx)("span",{className:"text-neutral-200",children:i(d.week)})]}),(0,t.jsx)("span",{className:"text-neutral-700",children:"·"}),(0,t.jsxs)("span",{children:["Month ",(0,t.jsx)("span",{className:"text-neutral-200",children:i(d.month)})]}),h&&(0,t.jsxs)("div",{className:"absolute top-6 right-0 z-50 min-w-[220px] p-2 text-[10px] leading-snug text-neutral-200 bg-neutral-900 border border-white/15 rounded shadow-lg",children:[(0,t.jsx)("div",{className:"mb-1 text-neutral-400 uppercase tracking-wider text-[9px]",children:"Per project"}),0===Object.entries(d.by_project).length&&(0,t.jsx)("div",{className:"text-neutral-500",children:"No activity logged yet"}),Object.entries(d.by_project).map(([e,r])=>(0,t.jsxs)("div",{className:"flex items-baseline gap-2",children:[(0,t.jsx)("span",{className:"text-neutral-400 truncate flex-1",children:e}),(0,t.jsx)("span",{className:"tabular-nums text-neutral-200",children:i(r.month)}),(0,t.jsx)("span",{className:"text-neutral-600 text-[9px]",children:"/ mo"})]},e)),(0,t.jsxs)("div",{className:"mt-1 pt-1 border-t border-white/10 text-neutral-500",children:["Lifetime: ",(0,t.jsx)("span",{className:"text-neutral-200",children:i(d.total)})]}),(0,t.jsx)("div",{className:"mt-1 pt-1 border-t border-white/10 text-neutral-500 leading-snug",children:"ⓘ Stats are best-effort. Server restarts may undercount in-flight sessions."})]})]}),(0,t.jsx)("button",{type:"button",onClick:()=>c(!0),"aria-label":"About QuadWork",className:"rounded p-1 text-neutral-400 hover:bg-white/5 hover:text-white",children:(0,t.jsxs)("svg",{width:"18",height:"18",viewBox:"0 0 20 20",fill:"none",stroke:"currentColor",strokeWidth:"1.5",children:[(0,t.jsx)("circle",{cx:"10",cy:"10",r:"8"}),(0,t.jsx)("path",{d:"M10 9v5",strokeLinecap:"round"}),(0,t.jsx)("circle",{cx:"10",cy:"6.5",r:"0.8",fill:"currentColor"})]})}),(0,t.jsx)("a",{href:"https://github.com/realproject7/quadwork",target:"_blank",rel:"noopener noreferrer",className:"text-[12px] text-neutral-400 hover:text-white",children:"QuadWork github"})]})]}),(0,t.jsx)(o,{open:u,onClose:()=>c(!1)})]}):(0,t.jsx)("header",{className:"sticky top-0 z-40 flex h-12 items-center justify-between border-b border-white/10 bg-neutral-950/90 px-4 backdrop-blur","aria-hidden":"true"})}],34852)}]);
@@ -1 +0,0 @@
1
- (globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,64618,e=>{"use strict";var t=e.i(24046),s=e.i(85899),a=e.i(4232),r=e.i(16353);let c=[{id:"name",label:"Project Name",subtitle:"Name your project",status:"active"},{id:"repo",label:"GitHub Repo",subtitle:"Connect a repository",status:"pending"},{id:"models",label:"Agent Models",subtitle:"Configure CLI backends",status:"pending"},{id:"workdir",label:"Working Directory",subtitle:"Set the local path",status:"pending"},{id:"workspaces",label:"Create Workspaces",subtitle:"Worktrees + seed files",status:"pending"},{id:"launch",label:"Ready to Launch",subtitle:"Review & start",status:"pending"}],n=[{value:"claude",label:"Claude Code"},{value:"codex",label:"Codex"}],o=[{key:"head",label:"T1 — Head",role:"Owner / Final Guard",desc:"Merges PRs, makes final calls"},{key:"reviewer1",label:"T2a — Reviewer 1",role:"Design Reviewer",desc:"Reviews architecture & design"},{key:"reviewer2",label:"T2b — Reviewer 2",role:"Code Reviewer",desc:"Reviews implementation quality"},{key:"dev",label:"T3 — Developer",role:"Full-Stack Builder",desc:"Implements features & fixes"}];function l({repo:e,workingDir:t,setWorkingDir:r,error:c,onNext:n}){let[o,i]=(0,a.useState)(!0),[x,d]=(0,a.useState)(null),[p,m]=(0,a.useState)(!1),u=e?e.split("/")[1]:"project";return(0,a.useEffect)(()=>{e?fetch(`/api/setup/detect-clone?repo=${encodeURIComponent(e)}`).then(e=>e.ok?e.json():null).then(e=>{d(e),e?.found&&e.path?r(e.path):e?.suggested&&r(e.suggested),i(!1)}).catch(()=>i(!1)):i(!1)},[e,r]),(0,s.jsxs)("div",{children:[(0,s.jsx)("h2",{className:"text-sm font-semibold text-text mb-1",children:"Where is your project?"}),(0,s.jsx)("p",{className:"text-[11px] text-text-muted mb-3",children:"Your project's git repository on your local machine. QuadWork will create 4 agent workspaces next to this directory."}),o&&(0,s.jsx)("p",{className:"text-[11px] text-text-muted mb-3",children:"Scanning for existing clone..."}),!o&&x?.found&&(0,s.jsxs)("div",{className:"border border-accent/30 bg-accent/5 p-3 mb-4 text-[11px]",children:[(0,s.jsx)("p",{className:"text-accent font-semibold mb-1",children:"Found existing clone"}),(0,s.jsx)("p",{className:"text-text font-mono",children:x.path}),(0,s.jsxs)("div",{className:"flex gap-2 mt-2",children:[(0,s.jsx)("button",{onClick:n,className:"px-3 py-1 bg-accent text-bg text-[11px] font-semibold hover:bg-accent-dim transition-colors",children:"Use this"}),(0,s.jsx)("button",{onClick:()=>{m(!0),r("")},className:"px-3 py-1 text-[11px] text-text-muted border border-border hover:text-text transition-colors",children:"Choose different path"})]})]}),!o&&!x?.found&&!p&&(0,s.jsxs)("div",{className:"border border-border bg-bg-surface p-3 mb-4 text-[11px]",children:[(0,s.jsxs)("p",{className:"text-text-muted mb-1",children:["No local clone found for ",(0,s.jsx)("span",{className:"text-accent",children:e})]}),(0,s.jsx)("p",{className:"text-text-muted mb-2",children:"Setup will clone it to:"}),(0,s.jsx)("p",{className:"text-text font-mono mb-2",children:x?.suggested||`~/Projects/${u}`}),(0,s.jsxs)("div",{className:"flex gap-2",children:[(0,s.jsx)("button",{onClick:n,disabled:!t.trim(),className:"px-3 py-1 bg-accent text-bg text-[11px] font-semibold hover:bg-accent-dim transition-colors disabled:opacity-50",children:"Clone here & continue"}),(0,s.jsx)("button",{onClick:()=>m(!0),className:"px-3 py-1 text-[11px] text-text-muted border border-border hover:text-text transition-colors",children:"Choose different path"})]})]}),(p||!o&&!x)&&(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)("input",{value:t,onChange:e=>r(e.target.value),placeholder:`~/Projects/${u}`,className:"w-full bg-transparent border border-border px-2 py-1.5 text-[12px] text-text outline-none focus:border-accent mb-2"}),(0,s.jsx)("button",{onClick:n,disabled:!t.trim(),className:"px-4 py-1.5 bg-accent text-bg text-[12px] font-semibold hover:bg-accent-dim transition-colors disabled:opacity-50",children:"Next"})]}),c&&(0,s.jsx)("p",{className:"text-[11px] text-error mt-2",children:c}),(0,s.jsxs)("div",{className:"border border-border bg-bg-surface p-3 mt-4 text-[11px] text-text-muted font-mono space-y-0.5",children:[(0,s.jsx)("p",{className:"text-[10px] uppercase tracking-wider text-text-muted mb-1 font-sans",children:"Workspace layout"}),(0,s.jsxs)("p",{className:"text-accent",children:[u,"/ ← your repo"]}),(0,s.jsxs)("p",{children:[u,"-head/ ← Head agent"]}),(0,s.jsxs)("p",{children:[u,"-dev/ ← Dev agent"]}),(0,s.jsxs)("p",{children:[u,"-reviewer1/ ← Reviewer1"]}),(0,s.jsxs)("p",{children:[u,"-reviewer2/ ← Reviewer2"]})]})]})}e.s(["default",0,function(){let e=(0,r.useRouter)(),[i,x]=(0,a.useState)(c),[d,p]=(0,a.useState)(0),[m,u]=(0,a.useState)(""),[h,b]=(0,a.useState)(""),[g,j]=(0,a.useState)(""),[f,v]=(0,a.useState)([]),[N,k]=(0,a.useState)(!1),[y,w]=(0,a.useState)(!1),[C,S]=(0,a.useState)(""),[_,R]=(0,a.useState)(!1),[T,H]=(0,a.useState)({head:"claude",reviewer1:"claude",reviewer2:"claude",dev:"claude"}),[P,$]=(0,a.useState)(!0),[q,A]=(0,a.useState)(!1),[I,L]=(0,a.useState)(""),[E,F]=(0,a.useState)("paste"),[U,W]=(0,a.useState)(""),[D,G]=(0,a.useState)("~/.quadwork/reviewer-token"),[O,B]=(0,a.useState)(""),[M,Y]=(0,a.useState)({}),[z,J]=(0,a.useState)(!1),[K,V]=(0,a.useState)([]),[X,Q]=(0,a.useState)("idle"),[Z,ee]=(0,a.useState)(!1),[et,es]=(0,a.useState)({chattr:0,mcpHttp:0,mcpSse:0}),[ea,er]=(0,a.useState)({chattr:"",mcpHttp:"",mcpSse:""}),ec=e=>{let t=parseInt(ea[e],10),s=Number.isFinite(t)&&t>0&&t<=65535?t:0;es(t=>({...t,[e]:s})),er(t=>({...t,[e]:s?String(s):""}))},[en,eo]=(0,a.useState)({chattr:0,mcpHttp:0,mcpSse:0}),[el,ei]=(0,a.useState)(null);(0,a.useEffect)(()=>{fetch("/api/cli-status").then(e=>e.json()).then(e=>{ei(e);let t=e.claude&&!e.codex?"claude":!e.claude&&e.codex?"codex":null;t?H({head:t,reviewer1:t,reviewer2:t,dev:t}):e.claude&&e.codex&&H({head:"codex",dev:"claude",reviewer1:"codex",reviewer2:"claude"})}).catch(()=>{})},[]),(0,a.useEffect)(()=>{fetch("/api/github/user").then(e=>e.json()).then(e=>{e.login&&S(e.login)}).catch(()=>{})},[]),(0,a.useEffect)(()=>{C&&(k(!0),fetch(`/api/github/repos?owner=${encodeURIComponent(C)}`).then(e=>e.json()).then(e=>{Array.isArray(e)&&v(e)}).catch(()=>{}).finally(()=>k(!1)))},[C]);let ex=(0,a.useCallback)((e,t)=>{x(s=>s.map((s,a)=>a===e?{...s,...t}:s))},[]),ed=(0,a.useCallback)(()=>{x(e=>e.map((e,t)=>t===d?{...e,status:"done"}:t===d+1?{...e,status:"active"}:e)),p(e=>e+1)},[d]),ep=async(e,t)=>{J(!0);try{let s=await fetch(`/api/setup?step=${e}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)}),a=await s.json();return J(!1),a}catch{return J(!1),{ok:!1,error:"Request failed"}}},em=async()=>{let e=await ep("verify-repo",{repo:h});e.ok?ed():ex(d,{status:"error",error:e.error})},eu=async()=>{if(J(!0),V([]),q&&"paste"===E&&U){V(e=>[...e,"Saving reviewer token..."]);try{let e=await fetch("/api/setup/save-token",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({token:U})}),t=await e.json();t.ok&&V(e=>[...e,`Token saved to ${t.path}`])}catch{}}V(e=>[...e,"Creating worktrees..."]);let e=await ep("create-worktrees",{workingDir:O,repo:h});if(!e.ok){V(t=>[...t,`Error: ${e.errors?.join(", ")||e.error}`]),ex(d,{status:"error",error:e.errors?.join(", ")||e.error}),J(!1);return}V(e=>[...e,"Worktrees created."]),V(e=>[...e,"Writing seed files..."]);let t=q?"file"===E?D:"~/.quadwork/reviewer-token":"",s=await ep("seed-files",{workingDir:O,projectName:m,repo:h,reviewerUser:q?I:"",reviewerTokenPath:t});if(!s.ok){V(e=>[...e,`Error: ${s.error}`]),ex(d,{status:"error",error:s.error}),J(!1);return}V(e=>[...e,"Seed files written."]),V(e=>[...e,"Done."]),J(!1),ed()},eh=async()=>{let t,s,a;ec("chattr"),ec("mcpHttp"),ec("mcpSse");let r=e=>{let t=ea[e];if(""!==t){let e=parseInt(t,10);return Number.isFinite(e)&&e>0&&e<=65535?e:0}return et[e]},c={chattr:r("chattr"),mcpHttp:r("mcpHttp"),mcpSse:r("mcpSse")};if(Q("running"),Z&&c.chattr>0){t=c.chattr,s=c.mcpHttp||c.chattr-100,a=c.mcpSse||s+1;let e=[t,s,a];try{let t=(await Promise.all(e.map(e=>fetch(`/api/port-check?port=${e}`).then(e=>e.json())))).filter(e=>!e.free).map(e=>e.port);if(t.length>0){Q("error"),ex(d,{status:"error",error:`Port${t.length>1?"s":""} ${t.join(", ")} already in use`});return}}catch{}}else if(en.chattr)t=en.chattr,s=en.mcpHttp,a=en.mcpSse;else try{let e=await fetch("/api/port-check/auto?start=8300&count=1"),r=await e.json(),c=await fetch("/api/port-check/auto?start=8200&count=2"),n=await c.json();t=r.ports?.[0]||8300,s=n.ports?.[0]||8200,a=n.ports?.[1]||8201}catch{t=8300,s=8200,a=8201}let n=await ep("agentchattr-config",{workingDir:O,projectName:m,repo:h,backends:T,agentchattr_port:t,mcp_http_port:s,mcp_sse_port:a});n.ok&&Y({agentchattr_token:n.agentchattr_token,agentchattr_port:n.agentchattr_port,mcp_http_port:n.mcp_http_port,mcp_sse_port:n.mcp_sse_port});let o=O.split("/").pop()||m.toLowerCase().replace(/\s+/g,"-").replace(/[^a-z0-9-]/g,""),l=await ep("add-config",{id:o,name:m,repo:h,workingDir:O,backends:T,auto_approve:P,...n.ok?{agentchattr_token:n.agentchattr_token,agentchattr_port:n.agentchattr_port,mcp_http_port:n.mcp_http_port,mcp_sse_port:n.mcp_sse_port}:M});l.ok?(Q("done"),ex(d,{status:"done"}),setTimeout(()=>e.push(`/project/${o}`),1200)):(Q("error"),ex(d,{status:"error",error:l.error}))};(0,a.useEffect)(()=>{i[d]?.id==="launch"&&(async()=>{try{let e=await fetch("/api/port-check/auto?start=8300&count=1"),t=await e.json(),s=await fetch("/api/port-check/auto?start=8200&count=2"),a=await s.json(),r={chattr:t.ports?.[0]||8300,mcpHttp:a.ports?.[0]||8200,mcpSse:a.ports?.[1]||8201};eo(r),et.chattr||(es(r),er({chattr:String(r.chattr),mcpHttp:String(r.mcpHttp),mcpSse:String(r.mcpSse)}))}catch{}})()},[d,i]);let eb=f.filter(e=>e.name.toLowerCase().includes(g.toLowerCase())),eg=i[d];return(0,s.jsxs)("div",{className:"h-full overflow-y-auto",children:[(0,s.jsxs)("div",{className:"px-6 pt-6 pb-4 border-b border-border",children:[(0,s.jsx)("h1",{className:"text-lg font-semibold text-text tracking-tight",children:"Set Up Your AI Dev Team"}),(0,s.jsx)("p",{className:"text-[11px] text-text-muted mt-1",children:"Configure agents, connect your repo, and launch a multi-agent development workflow in minutes."})]}),(0,s.jsxs)("div",{className:"flex h-[calc(100%-80px)]",children:[(0,s.jsxs)("div",{className:"flex-1 flex gap-6 p-6 overflow-y-auto",children:[(0,s.jsx)("div",{className:"w-44 shrink-0",children:i.map((e,t)=>(0,s.jsxs)("div",{className:"flex items-start gap-2 py-2",children:[(0,s.jsx)("span",{className:`w-5 h-5 flex items-center justify-center text-[10px] border shrink-0 mt-0.5 ${"done"===e.status?"border-accent text-accent":"error"===e.status?"border-error text-error":"active"===e.status?"border-accent text-accent bg-accent/10":"skipped"===e.status?"border-border text-text-muted line-through":"border-border text-text-muted"}`,children:"done"===e.status?"✓":"error"===e.status?"!":t+1}),(0,s.jsxs)("div",{children:[(0,s.jsx)("span",{className:`text-[11px] block leading-tight ${"active"===e.status?"text-text font-semibold":"done"===e.status?"text-accent":"text-text-muted"}`,children:e.label}),(0,s.jsx)("span",{className:"text-[10px] text-text-muted block",children:e.subtitle})]})]},e.id))}),(0,s.jsxs)("div",{className:"flex-1 border border-border p-5 min-h-0",children:[eg?.id==="name"&&(0,s.jsxs)("div",{children:[(0,s.jsx)("h2",{className:"text-sm font-semibold text-text mb-1",children:"Name your project"}),(0,s.jsx)("p",{className:"text-[11px] text-text-muted mb-4",children:"This name identifies your project in the dashboard and agent configs."}),(0,s.jsx)("input",{value:m,onChange:e=>u(e.target.value),placeholder:"e.g. My DeFi App",className:"w-full bg-transparent border border-border px-2 py-1.5 text-[12px] text-text outline-none focus:border-accent mb-4",autoFocus:!0}),(0,s.jsx)("button",{onClick:ed,disabled:!m.trim(),className:"px-4 py-1.5 bg-accent text-bg text-[12px] font-semibold hover:bg-accent-dim transition-colors disabled:opacity-50",children:"Next"})]}),eg?.id==="repo"&&(0,s.jsxs)("div",{children:[(0,s.jsx)("h2",{className:"text-sm font-semibold text-text mb-1",children:"Connect a GitHub repository"}),(0,s.jsx)("p",{className:"text-[11px] text-text-muted mb-4",children:"Select an existing repo or enter one manually. Agents will work within this repo."}),!y&&(0,s.jsxs)(s.Fragment,{children:[C&&(0,s.jsxs)("p",{className:"text-[11px] text-text-muted mb-2",children:["Showing repos for ",(0,s.jsx)("span",{className:"text-accent",children:C})]}),(0,s.jsx)("input",{value:g,onChange:e=>j(e.target.value),placeholder:"Search repos...",className:"w-full bg-transparent border border-border px-2 py-1.5 text-[12px] text-text outline-none focus:border-accent mb-2"}),N&&(0,s.jsx)("p",{className:"text-[11px] text-text-muted mb-2",children:"Loading..."}),(0,s.jsxs)("div",{className:"max-h-40 overflow-y-auto border border-border mb-3",children:[eb.map(e=>(0,s.jsxs)("button",{onClick:()=>b(`${C}/${e.name}`),className:`w-full text-left px-3 py-1.5 text-[11px] border-b border-border/50 last:border-b-0 hover:bg-accent/5 transition-colors ${h===`${C}/${e.name}`?"bg-accent/10 text-accent":"text-text"}`,children:[(0,s.jsx)("span",{className:"font-semibold",children:e.name}),e.isPrivate&&(0,s.jsx)("span",{className:"text-[10px] text-text-muted ml-2",children:"private"}),e.description&&(0,s.jsx)("span",{className:"text-[10px] text-text-muted ml-2",children:e.description})]},e.name)),!N&&0===eb.length&&(0,s.jsx)("p",{className:"px-3 py-2 text-[11px] text-text-muted",children:"No repos found."})]}),(0,s.jsx)("button",{onClick:()=>w(!0),className:"text-[11px] text-text-muted hover:text-accent transition-colors mb-3 block",children:"Enter manually instead"})]}),y&&(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)("input",{value:h,onChange:e=>b(e.target.value),placeholder:"owner/repo",className:"w-full bg-transparent border border-border px-2 py-1.5 text-[12px] text-text outline-none focus:border-accent mb-2"}),(0,s.jsx)("button",{onClick:()=>w(!1),className:"text-[11px] text-text-muted hover:text-accent transition-colors mb-3 block",children:"Back to repo list"})]}),(0,s.jsxs)("label",{className:"flex items-center gap-2 mb-4 cursor-pointer",children:[(0,s.jsx)("input",{type:"checkbox",checked:_,onChange:e=>R(e.target.checked),className:"accent-accent"}),(0,s.jsxs)("span",{className:"text-[11px] text-text-muted",children:["Enable branch protection on ",(0,s.jsx)("code",{className:"text-accent",children:"main"})]})]}),_&&(0,s.jsxs)("div",{className:"border border-border bg-bg-surface p-3 mb-4 text-[11px] space-y-2",children:[(0,s.jsx)("p",{className:"text-text-muted",children:"Run this after setup, or configure in GitHub UI:"}),(0,s.jsxs)("div",{className:"flex items-center gap-2",children:[(0,s.jsx)("code",{className:"text-accent flex-1 select-all text-[10px] break-all",children:`gh api repos/${h||"owner/repo"}/branches/main/protection -X PUT -f "required_pull_request_reviews[required_approving_review_count]=1" -f "enforce_admins=false" -f "required_status_checks=null" -f "restrictions=null"`}),(0,s.jsx)("button",{onClick:()=>navigator.clipboard.writeText(`gh api repos/${h}/branches/main/protection -X PUT -f "required_pull_request_reviews[required_approving_review_count]=1" -f "enforce_admins=false" -f "required_status_checks=null" -f "restrictions=null"`),className:"text-[10px] text-text-muted hover:text-accent shrink-0",children:"copy"})]})]}),eg.error&&(0,s.jsx)("p",{className:"text-[11px] text-error mb-2",children:eg.error}),(0,s.jsx)("button",{onClick:em,disabled:!h||z,className:"px-4 py-1.5 bg-accent text-bg text-[12px] font-semibold hover:bg-accent-dim transition-colors disabled:opacity-50",children:z?"Verifying...":"Verify & Continue"})]}),eg?.id==="models"&&(0,s.jsxs)("div",{children:[(0,s.jsx)("h2",{className:"text-sm font-semibold text-text mb-1",children:"Configure agent CLI backends"}),(0,s.jsx)("p",{className:"text-[11px] text-text-muted mb-4",children:"Each agent runs its own CLI instance. Pick the backend for each role."}),el&&!el.claude&&el.codex&&(0,s.jsxs)("div",{className:"border border-accent/20 bg-accent/5 p-3 mb-4 text-[11px]",children:[(0,s.jsx)("p",{className:"text-text",children:"You have Codex CLI installed — great! All 4 agents will use Codex."}),(0,s.jsx)("p",{className:"text-text-muted mt-1.5",children:"Tip: Installing Claude Code too gives your team different AI perspectives, which can improve code review quality. You can add it anytime:"}),(0,s.jsx)("p",{className:"text-accent mt-1 font-mono text-[10px]",children:"npm install -g @anthropic-ai/claude-code"}),(0,s.jsx)("p",{className:"text-text-muted mt-1.5",children:"For now, Codex CLI handles everything perfectly. Let's continue!"})]}),el&&el.claude&&!el.codex&&(0,s.jsxs)("div",{className:"border border-accent/20 bg-accent/5 p-3 mb-4 text-[11px]",children:[(0,s.jsx)("p",{className:"text-text",children:"You have Claude Code installed — great! All 4 agents will use Claude."}),(0,s.jsx)("p",{className:"text-text-muted mt-1.5",children:"Tip: Installing Codex CLI too gives your team different AI perspectives, which can improve code review quality. You can add it anytime:"}),(0,s.jsx)("p",{className:"text-accent mt-1 font-mono text-[10px]",children:"npm install -g codex"}),(0,s.jsx)("p",{className:"text-text-muted mt-1.5",children:"For now, Claude Code handles everything perfectly. Let's continue!"})]}),(0,s.jsx)("div",{className:"border border-border mb-4",children:o.map(e=>(0,s.jsxs)("div",{className:"flex items-center justify-between px-3 py-2 border-b border-border/50 last:border-b-0",children:[(0,s.jsxs)("div",{className:"flex-1 min-w-0",children:[(0,s.jsx)("span",{className:"text-[11px] text-text font-semibold block",children:e.label}),(0,s.jsx)("span",{className:"text-[10px] text-text-muted",children:e.desc})]}),(0,s.jsx)("select",{value:T[e.key],onChange:t=>H({...T,[e.key]:t.target.value}),className:"bg-transparent border border-border px-2 py-0.5 text-[11px] text-text outline-none focus:border-accent cursor-pointer ml-3",children:n.map(e=>(0,s.jsxs)("option",{value:e.value,className:"bg-bg-surface",disabled:!!el&&!el[e.value],children:[e.label,el&&!el[e.value]?" (not installed)":""]},e.value))})]},e.key))}),(0,s.jsxs)("label",{className:"flex items-center gap-2 mb-3 cursor-pointer",title:"Enable permission bypass flags so agents can work autonomously without prompting for approval on every action",children:[(0,s.jsx)("input",{type:"checkbox",checked:P,onChange:e=>$(e.target.checked),className:"accent-accent"}),(0,s.jsx)("span",{className:"text-[11px] text-text",children:"Auto-approve agent actions"}),(0,s.jsx)("span",{className:"text-[10px] text-text-muted",children:"(required for autonomous work)"})]}),(0,s.jsxs)("label",{className:"flex items-center gap-2 mb-3 cursor-pointer",children:[(0,s.jsx)("input",{type:"checkbox",checked:q,onChange:e=>A(e.target.checked),className:"accent-accent"}),(0,s.jsx)("span",{className:"text-[11px] text-text-muted",children:"Configure reviewer credentials (for GitHub PR reviews)"})]}),q&&(0,s.jsxs)("div",{className:"border border-border p-3 mb-4 space-y-3",children:[(0,s.jsxs)("div",{children:[(0,s.jsx)("label",{className:"text-[11px] text-text-muted block mb-1",children:"Reviewer GitHub username"}),(0,s.jsx)("input",{value:I,onChange:e=>L(e.target.value),placeholder:"github-username",className:"w-full bg-transparent border border-border px-2 py-1.5 text-[12px] text-text outline-none focus:border-accent"})]}),(0,s.jsxs)("div",{children:[(0,s.jsx)("label",{className:"text-[11px] text-text-muted block mb-2",children:"Token source"}),(0,s.jsxs)("div",{className:"flex gap-4 mb-2",children:[(0,s.jsxs)("label",{className:"flex items-center gap-1.5 cursor-pointer",children:[(0,s.jsx)("input",{type:"radio",name:"tokenMode",checked:"paste"===E,onChange:()=>F("paste"),className:"accent-accent"}),(0,s.jsx)("span",{className:"text-[11px] text-text",children:"Paste token"})]}),(0,s.jsxs)("label",{className:"flex items-center gap-1.5 cursor-pointer",children:[(0,s.jsx)("input",{type:"radio",name:"tokenMode",checked:"file"===E,onChange:()=>F("file"),className:"accent-accent"}),(0,s.jsx)("span",{className:"text-[11px] text-text",children:"Use existing file"})]})]}),"paste"===E?(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)("input",{value:U,onChange:e=>W(e.target.value),placeholder:"ghp_xxxxxxxxxxxxxxxxxxxx",type:"password",className:"w-full bg-transparent border border-border px-2 py-1.5 text-[12px] text-text outline-none focus:border-accent"}),(0,s.jsxs)("div",{className:"mt-2 text-[10px] text-text-muted leading-relaxed",children:[(0,s.jsxs)("p",{children:["Paste a GitHub ",(0,s.jsx)("span",{className:"text-text",children:"Personal Access Token (classic)"}),"."]}),(0,s.jsxs)("p",{className:"mt-1",children:["Create one at"," ",(0,s.jsx)("a",{href:"https://github.com/settings/tokens",target:"_blank",rel:"noopener noreferrer",className:"text-accent hover:underline",children:"github.com/settings/tokens"})," ","→ Generate new token (classic)"]}),(0,s.jsxs)("p",{className:"mt-1",children:["Required permission: ",(0,s.jsx)("span",{className:"text-accent",children:"repo"})," (Full control of private repositories)",(0,s.jsx)("br",{}),(0,s.jsx)("span",{className:"text-text-muted",children:"Needed for reading PRs, posting reviews, and approving/requesting changes"})]})]})]}):(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)("input",{value:D,onChange:e=>G(e.target.value),placeholder:"~/.quadwork/reviewer-token",className:"w-full bg-transparent border border-border px-2 py-1.5 text-[12px] text-text outline-none focus:border-accent"}),D&&!D.startsWith("~/.quadwork")&&!D.startsWith(String.raw`${t.default.env.HOME}/.quadwork`)&&(0,s.jsx)("p",{className:"text-[10px] text-[#ffcc00] mt-1",children:"This path may be inside a git repository. Consider using the default ~/.quadwork/ location to avoid accidentally committing tokens."})]})]})]}),(0,s.jsx)("button",{onClick:ed,className:"px-4 py-1.5 bg-accent text-bg text-[12px] font-semibold hover:bg-accent-dim transition-colors",children:"Next"})]}),eg?.id==="workdir"&&(0,s.jsx)(l,{repo:h,workingDir:O,setWorkingDir:B,error:eg.error,onNext:ed}),eg?.id==="workspaces"&&(0,s.jsxs)("div",{children:[(0,s.jsx)("h2",{className:"text-sm font-semibold text-text mb-1",children:"Create workspaces"}),(0,s.jsx)("p",{className:"text-[11px] text-text-muted mb-4",children:"This creates git worktrees for each agent and writes seed configuration files (AGENTS.md, CLAUDE.md) into each workspace."}),eg.error&&(0,s.jsx)("p",{className:"text-[11px] text-error mb-2",children:eg.error}),K.length>0&&(0,s.jsx)("div",{className:"border border-border bg-bg-surface p-3 mb-4 text-[11px] text-text-muted space-y-0.5 font-mono",children:K.map((e,t)=>(0,s.jsx)("p",{children:e},t))}),(0,s.jsx)("button",{onClick:eu,disabled:z,className:"px-4 py-1.5 bg-accent text-bg text-[12px] font-semibold hover:bg-accent-dim transition-colors disabled:opacity-50",children:z?"Creating...":"Create Worktrees & Seed Files"})]}),eg?.id==="launch"&&(0,s.jsxs)("div",{children:[(0,s.jsx)("h2",{className:"text-sm font-semibold text-text mb-1",children:"Ready to launch"}),(0,s.jsx)("p",{className:"text-[11px] text-text-muted mb-4",children:"Everything is configured. Review the summary and launch your AI dev team."}),(0,s.jsxs)("div",{className:"border border-border mb-4",children:[(0,s.jsx)("div",{className:"px-3 py-1.5 border-b border-border bg-bg-surface",children:(0,s.jsx)("span",{className:"text-[11px] text-text font-semibold",children:"Team Roster"})}),o.map(e=>(0,s.jsxs)("div",{className:"flex items-center justify-between px-3 py-1.5 border-b border-border/50 last:border-b-0",children:[(0,s.jsx)("span",{className:"text-[11px] text-text font-semibold",children:e.label}),(0,s.jsx)("span",{className:"text-[10px] text-text-muted",children:e.role}),(0,s.jsx)("span",{className:"text-[11px] text-accent",children:"claude"===T[e.key]?"Claude Code":"Codex"})]},e.key))]}),(0,s.jsxs)("div",{className:"mb-4",children:[(0,s.jsxs)("label",{className:"flex items-center gap-2 cursor-pointer mb-2",children:[(0,s.jsx)("input",{type:"checkbox",checked:Z,onChange:e=>ee(e.target.checked),className:"accent-accent"}),(0,s.jsx)("span",{className:"text-[11px] text-text-muted",children:"Custom ports"})]}),Z&&(0,s.jsx)("div",{className:"border border-border p-3 space-y-2",children:(0,s.jsxs)("div",{className:"grid grid-cols-3 gap-3",children:[(0,s.jsxs)("div",{className:"flex flex-col gap-1",children:[(0,s.jsx)("label",{className:"text-[10px] text-text-muted uppercase tracking-wider",children:"AgentChattr port"}),(0,s.jsx)("input",{type:"number",value:ea.chattr,onChange:e=>er({...ea,chattr:e.target.value}),onBlur:()=>ec("chattr"),placeholder:String(en.chattr||8300),className:"bg-transparent border border-border px-2 py-1 text-[11px] text-text outline-none focus:border-accent"}),en.chattr>0&&(0,s.jsxs)("span",{className:"text-[10px] text-text-muted",children:["auto-detected: ",en.chattr]})]}),(0,s.jsxs)("div",{className:"flex flex-col gap-1",children:[(0,s.jsx)("label",{className:"text-[10px] text-text-muted uppercase tracking-wider",children:"MCP HTTP port"}),(0,s.jsx)("input",{type:"number",value:ea.mcpHttp,onChange:e=>er({...ea,mcpHttp:e.target.value}),onBlur:()=>ec("mcpHttp"),placeholder:String(en.mcpHttp||8200),className:"bg-transparent border border-border px-2 py-1 text-[11px] text-text outline-none focus:border-accent"}),en.mcpHttp>0&&(0,s.jsxs)("span",{className:"text-[10px] text-text-muted",children:["auto-detected: ",en.mcpHttp]})]}),(0,s.jsxs)("div",{className:"flex flex-col gap-1",children:[(0,s.jsx)("label",{className:"text-[10px] text-text-muted uppercase tracking-wider",children:"MCP SSE port"}),(0,s.jsx)("input",{type:"number",value:ea.mcpSse,onChange:e=>er({...ea,mcpSse:e.target.value}),onBlur:()=>ec("mcpSse"),placeholder:String(en.mcpSse||8201),className:"bg-transparent border border-border px-2 py-1 text-[11px] text-text outline-none focus:border-accent"}),en.mcpSse>0&&(0,s.jsxs)("span",{className:"text-[10px] text-text-muted",children:["auto-detected: ",en.mcpSse]})]})]})})]}),eg.error&&(0,s.jsx)("p",{className:"text-[11px] text-error mb-2",children:eg.error}),"done"===X&&(0,s.jsx)("p",{className:"text-[11px] text-accent mb-2",children:"Project saved. Redirecting to dashboard..."}),(0,s.jsx)("button",{onClick:eh,disabled:"running"===X||"done"===X,className:"px-5 py-2 bg-accent text-bg text-[12px] font-semibold hover:bg-accent-dim transition-colors disabled:opacity-50",children:"running"===X?"Launching...":"done"===X?"Launched!":"Launch Project"})]}),d>=i.length&&(0,s.jsxs)("div",{className:"text-center py-8",children:[(0,s.jsx)("p",{className:"text-accent text-sm font-semibold",children:"Setup complete!"}),(0,s.jsx)("p",{className:"text-[11px] text-text-muted mt-2",children:"Redirecting to project dashboard..."})]})]})]}),(0,s.jsxs)("div",{className:"w-64 shrink-0 border-l border-border p-4 overflow-y-auto bg-bg-surface/50",children:[(0,s.jsx)("h3",{className:"text-[11px] font-semibold text-text-muted uppercase tracking-wider mb-3",children:"Configuration Preview"}),(0,s.jsxs)("div",{className:"space-y-3 text-[11px]",children:[(0,s.jsxs)("div",{children:[(0,s.jsx)("span",{className:"text-text-muted block mb-0.5",children:"Project"}),(0,s.jsx)("span",{className:"text-text",children:m||"—"})]}),(0,s.jsxs)("div",{children:[(0,s.jsx)("span",{className:"text-text-muted block mb-0.5",children:"Repository"}),(0,s.jsx)("span",{className:"text-text",children:h||"—"}),_&&(0,s.jsx)("span",{className:"text-[10px] text-accent block",children:"+ branch protection"})]}),(0,s.jsxs)("div",{children:[(0,s.jsx)("span",{className:"text-text-muted block mb-0.5",children:"Backends"}),Object.entries(T).map(([e,t])=>(0,s.jsxs)("div",{className:"flex justify-between",children:[(0,s.jsx)("span",{className:"text-text capitalize",children:e}),(0,s.jsx)("span",{className:"text-accent",children:t})]},e))]}),q&&I&&(0,s.jsxs)("div",{children:[(0,s.jsx)("span",{className:"text-text-muted block mb-0.5",children:"Reviewer"}),(0,s.jsxs)("span",{className:"text-text",children:["@",I]})]}),(0,s.jsxs)("div",{children:[(0,s.jsx)("span",{className:"text-text-muted block mb-0.5",children:"Directory"}),(0,s.jsx)("span",{className:"text-text font-mono text-[10px]",children:O||"—"})]}),(0,s.jsxs)("div",{children:[(0,s.jsx)("span",{className:"text-text-muted block mb-0.5",children:"Status"}),(0,s.jsx)("div",{className:"space-y-0.5",children:i.map(e=>(0,s.jsxs)("div",{className:"flex items-center gap-1.5",children:[(0,s.jsx)("span",{className:`text-[10px] ${"done"===e.status?"text-accent":"error"===e.status?"text-error":"active"===e.status?"text-text":"text-text-muted"}`,children:"done"===e.status?"✓":"error"===e.status?"✗":"active"===e.status?"●":"○"}),(0,s.jsx)("span",{className:`text-[10px] ${"active"===e.status?"text-text":"text-text-muted"}`,children:e.label})]},e.id))})]})]})]})]})]})}])}]);
@@ -1 +0,0 @@
1
- (globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,18540,e=>{"use strict";var t=e.i(85899),r=e.i(4232);let s=[{title:"You assign a task in the chat",body:"Tell @head what to build. Be as specific or as vague as you like."},{title:"Head creates a GitHub issue",body:"Head opens an issue, adds it to the queue, and waits for your trigger."},{title:"Dev writes the code",body:"Dev clones a branch, implements the change, and opens a pull request."},{title:"Reviewers check the work",body:"Reviewer1 and Reviewer2 each review the PR independently. Both must approve before the PR is mergeable."},{title:"Head merges and continues",body:"Head merges the approved PR and assigns the next ticket from the queue. The cycle continues all night while you sleep."}];e.s(["default",0,function({open:e,onClose:a}){return((0,r.useEffect)(()=>{if(!e)return;let t=e=>{"Escape"===e.key&&a()};return window.addEventListener("keydown",t),()=>window.removeEventListener("keydown",t)},[e,a]),e)?(0,t.jsx)("div",{className:"fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm",onClick:a,role:"dialog","aria-modal":"true","aria-labelledby":"how-to-work-title",children:(0,t.jsxs)("div",{className:"relative mx-4 max-w-xl w-full max-h-[90vh] overflow-auto rounded-lg border border-white/10 bg-neutral-950 p-6 shadow-2xl",onClick:e=>e.stopPropagation(),children:[(0,t.jsx)("button",{type:"button",onClick:a,"aria-label":"Close",className:"absolute right-3 top-3 rounded p-1 text-neutral-400 hover:bg-white/5 hover:text-white",children:(0,t.jsx)("svg",{width:"18",height:"18",viewBox:"0 0 20 20",fill:"none",stroke:"currentColor",strokeWidth:"1.8",children:(0,t.jsx)("path",{d:"M4 4l12 12M16 4L4 16",strokeLinecap:"round"})})}),(0,t.jsx)("h2",{id:"how-to-work-title",className:"text-base font-semibold text-white",children:"How QuadWork builds your code"}),(0,t.jsx)("p",{className:"mt-2 text-[12px] text-neutral-400",children:"Five steps from your one-line request to a merged pull request."}),(0,t.jsxs)("ol",{className:"mt-5 relative",children:[(0,t.jsx)("span",{"aria-hidden":!0,className:"absolute left-[14px] top-3 bottom-3 w-px bg-accent/30"}),s.map((e,r)=>(0,t.jsxs)("li",{className:"relative pl-10 pb-5 last:pb-0",children:[(0,t.jsx)("span",{className:"absolute left-0 top-0 inline-flex items-center justify-center w-7 h-7 rounded-full border border-accent bg-neutral-950 text-accent text-[12px] font-semibold tabular-nums","aria-hidden":!0,children:r+1}),(0,t.jsx)("div",{className:"text-[13px] font-semibold text-white",children:e.title}),(0,t.jsx)("div",{className:"mt-1 text-[12px] leading-relaxed text-neutral-400",children:e.body})]},r))]})]})}):null}])},16348,e=>{"use strict";var t=e.i(85899),r=e.i(4232),s=e.i(2270),a=e.i(18540);function i({hasProjects:e}){let[l,o]=(0,r.useState)(!1);return(0,t.jsxs)("div",{className:"flex flex-col items-center justify-center text-center px-6 py-12 border border-border bg-bg-surface",children:[(0,t.jsxs)("svg",{width:"64",height:"64",viewBox:"0 0 64 64",fill:"none","aria-hidden":!0,className:"text-accent",children:[(0,t.jsx)("rect",{x:"6",y:"14",width:"18",height:"22",rx:"2",stroke:"currentColor",strokeWidth:"2"}),(0,t.jsx)("rect",{x:"40",y:"14",width:"18",height:"22",rx:"2",stroke:"currentColor",strokeWidth:"2"}),(0,t.jsx)("rect",{x:"6",y:"42",width:"18",height:"14",rx:"2",stroke:"currentColor",strokeWidth:"2"}),(0,t.jsx)("rect",{x:"40",y:"42",width:"18",height:"14",rx:"2",stroke:"currentColor",strokeWidth:"2"}),(0,t.jsx)("circle",{cx:"15",cy:"25",r:"2",fill:"currentColor"}),(0,t.jsx)("circle",{cx:"49",cy:"25",r:"2",fill:"currentColor"}),(0,t.jsx)("circle",{cx:"15",cy:"49",r:"2",fill:"currentColor"}),(0,t.jsx)("circle",{cx:"49",cy:"49",r:"2",fill:"currentColor"}),(0,t.jsx)("path",{d:"M24 25 L40 25 M24 49 L40 49 M15 36 L15 42 M49 36 L49 42",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round"})]}),(0,t.jsx)("h1",{className:"mt-5 text-lg font-semibold text-text max-w-md",children:e?"Pick a project from the sidebar to start working":"Welcome to QuadWork — let's set up your first AI dev team"}),(0,t.jsx)("p",{className:"mt-2 text-[12px] text-text-muted leading-relaxed max-w-md",children:e?"Each project has its own 4-agent team and chat. Click any chip in the left sidebar to open one.":"QuadWork runs Head, Dev, and two Reviewers as a team. They open issues, write code, review PRs, and merge — while you sleep."}),(0,t.jsxs)("div",{className:"mt-5 flex items-center gap-3",children:[e?(0,t.jsx)("span",{className:"text-[11px] text-text-muted italic",children:"← look at the left sidebar"}):(0,t.jsx)(s.default,{href:"/setup",className:"px-4 py-2 text-[12px] font-semibold text-bg bg-accent hover:bg-accent-dim transition-colors",children:"Add Your First Project →"}),(0,t.jsx)("button",{type:"button",onClick:()=>o(!0),className:"px-4 py-2 text-[12px] text-text-muted border border-border hover:text-text hover:border-text-muted transition-colors",children:"How to Work"})]}),(0,t.jsx)(a.default,{open:l,onClose:()=>o(!1)})]})}e.s(["default",0,function(){let[e,a]=(0,r.useState)([]),[l,o]=(0,r.useState)([]),[c,n]=(0,r.useState)("loading");return(0,r.useEffect)(()=>{fetch("/api/projects").then(e=>{if(!e.ok)throw Error(`${e.status}`);return e.json()}).then(e=>{e.projects&&Array.isArray(e.projects)&&a(e.projects.filter(e=>!e.archived)),e.recentEvents&&Array.isArray(e.recentEvents)&&o(e.recentEvents),n("loaded")}).catch(()=>{n("error")})},[]),(0,t.jsxs)("div",{className:"h-full overflow-y-auto p-6",children:["loaded"===c&&(0,t.jsx)("div",{className:"mb-6",children:(0,t.jsx)(i,{hasProjects:e.length>0})}),"error"===c&&(0,t.jsx)("div",{className:"mb-6 border border-error/30 bg-error/5 text-error text-[11px] px-3 py-2",children:"Could not load projects from /api/projects. The dashboard may be out of date — check the server logs and reload."}),(0,t.jsxs)("div",{className:"mb-6",children:[(0,t.jsx)("h1",{className:"text-lg font-semibold text-text tracking-tight",children:"Projects"}),(0,t.jsxs)("p",{className:"text-xs text-text-muted mt-1",children:[e.length," configured project",1!==e.length?"s":""]})]}),(0,t.jsxs)("div",{className:"grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-3 mb-8",children:[e.map(e=>(0,t.jsxs)(s.default,{href:`/project/${e.id}`,className:"block border border-border bg-bg-surface p-4 hover:bg-[#1a1a1a] transition-colors group",children:[(0,t.jsxs)("div",{className:"flex items-center justify-between mb-3",children:[(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)("span",{className:`w-1.5 h-1.5 rounded-full ${"active"===e.state?"bg-accent":"bg-text-muted"}`}),(0,t.jsx)("span",{className:"text-sm font-semibold text-text",children:e.name}),(0,t.jsx)("span",{className:"text-[10px] text-text-muted",children:e.state})]}),(0,t.jsx)("span",{className:"text-[10px] text-text-muted opacity-0 group-hover:opacity-100 transition-opacity",children:"open →"})]}),(0,t.jsxs)("div",{className:"flex gap-4 text-[11px] mb-2",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)("span",{className:"text-text-muted",children:"agents"}),(0,t.jsx)("span",{className:"ml-1.5 text-text",children:e.agentCount})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)("span",{className:"text-text-muted",children:"PRs"}),(0,t.jsx)("span",{className:"ml-1.5 text-text",children:e.openPrs})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)("span",{className:"text-text-muted",children:"repo"}),(0,t.jsx)("span",{className:"ml-1.5 text-text",children:e.repo})]})]}),e.lastActivity&&(0,t.jsxs)("div",{className:"text-[10px] text-text-muted",children:["last activity: ",function(e){let t=Math.floor((Date.now()-new Date(e).getTime())/6e4);if(t<1)return"just now";if(t<60)return`${t}m ago`;let r=Math.floor(t/60);if(r<24)return`${r}h ago`;let s=Math.floor(r/24);return`${s}d ago`}(e.lastActivity)]})]},e.id)),(0,t.jsx)(s.default,{href:"/setup",className:"border border-dashed border-border p-4 flex items-center justify-center text-text-muted hover:text-text hover:border-text-muted transition-colors min-h-[88px]",children:(0,t.jsx)("span",{className:"text-sm",children:"+ New Project"})})]}),(0,t.jsxs)("div",{className:"mb-6",children:[(0,t.jsx)("h2",{className:"text-xs text-text-muted uppercase tracking-wider mb-3",children:"Recent Activity"}),(0,t.jsxs)("div",{className:"border border-border bg-bg-surface",children:[0===l.length&&(0,t.jsx)("div",{className:"px-3 py-3 text-[11px] text-text-muted",children:"No recent activity"}),l.map((e,r)=>(0,t.jsxs)("div",{className:"flex gap-3 px-3 py-1.5 border-b border-border/50 last:border-b-0 text-[11px]",children:[(0,t.jsx)("span",{className:"text-text-muted shrink-0 w-10 text-right tabular-nums",children:e.time?.slice(0,5)||""}),(0,t.jsx)("span",{className:"text-accent shrink-0 font-semibold w-12",children:e.projectName}),(0,t.jsx)("span",{className:"text-[#ffcc00] shrink-0 font-semibold w-12",children:"reviewer1"===e.actor?"RE1":"reviewer2"===e.actor?"RE2":e.actor}),(0,t.jsx)("span",{className:"text-text truncate min-w-0",children:e.text})]},`${e.time}-${r}`))]})]})]})}],16348)}]);