quadwork 1.5.3 → 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 (116) hide show
  1. package/bin/quadwork.js +4 -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/{0wreuebrwlg.2.js → 0.fo6qk3ltnbn.js} +2 -2
  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 +25 -3
  94. package/server/routes.telegramBridge.test.js +4 -1
  95. package/templates/CLAUDE.md +2 -1
  96. package/templates/seeds/dev.AGENTS.md +2 -0
  97. package/templates/seeds/head.AGENTS.md +2 -0
  98. package/templates/seeds/reviewer1.AGENTS.md +3 -1
  99. package/templates/seeds/reviewer2.AGENTS.md +3 -1
  100. package/out/_next/static/chunks/04_t39bv8y9pe.js +0 -1
  101. package/out/_next/static/chunks/084lff9v4p_vh.js +0 -1
  102. package/out/_next/static/chunks/09sq17vme9g6p.js +0 -1
  103. package/out/_next/static/chunks/0e.ktwt1nyj...js +0 -1
  104. package/out/_next/static/chunks/0excsn2a_5qsb.js +0 -4
  105. package/out/_next/static/chunks/0ezniz80psxr6.js +0 -1
  106. package/out/_next/static/chunks/0g-nq4.uckan-.js +0 -1
  107. package/out/_next/static/chunks/0ox7p_szjhn69.js +0 -1
  108. package/out/_next/static/chunks/0r7t_sj_sejq9.js +0 -1
  109. package/out/_next/static/chunks/0r_tb4lmfa_yb.js +0 -1
  110. package/out/_next/static/chunks/0whtwwbpg72ar.js +0 -1
  111. package/out/_next/static/chunks/0z~0.4hivi.f2.js +0 -31
  112. package/out/_next/static/chunks/15i5_ay.0ap.6.js +0 -2
  113. package/out/_next/static/chunks/17y2walb2um9w.js +0 -1
  114. /package/out/_next/static/{X4zdS6Y6HkLOaElNeHwnq → XCVQTR6dRg8tK75W8jDzw}/_buildManifest.js +0 -0
  115. /package/out/_next/static/{X4zdS6Y6HkLOaElNeHwnq → XCVQTR6dRg8tK75W8jDzw}/_clientMiddlewareManifest.js +0 -0
  116. /package/out/_next/static/{X4zdS6Y6HkLOaElNeHwnq → XCVQTR6dRg8tK75W8jDzw}/_ssgManifest.js +0 -0
@@ -1,6 +1,6 @@
1
1
  1:"$Sreact.fragment"
2
- 2:I[64618,["/_next/static/chunks/04_t39bv8y9pe.js","/_next/static/chunks/0ox7p_szjhn69.js","/_next/static/chunks/084lff9v4p_vh.js"],"default"]
3
- 3:I[11717,["/_next/static/chunks/04_t39bv8y9pe.js","/_next/static/chunks/0ox7p_szjhn69.js"],"OutletBoundary"]
2
+ 2:I[94810,["/_next/static/chunks/0fkhi51bdw9~~.js","/_next/static/chunks/0d3shmwh5_nmn.js","/_next/static/chunks/0mtmv-f5qymoi.js"],"default"]
3
+ 3:I[97367,["/_next/static/chunks/0fkhi51bdw9~~.js","/_next/static/chunks/0d3shmwh5_nmn.js"],"OutletBoundary"]
4
4
  4:"$Sreact.suspense"
5
- 0:{"rsc":["$","$1","c",{"children":[["$","$L2",null,{}],[["$","script","script-0",{"src":"/_next/static/chunks/084lff9v4p_vh.js","async":true}]],["$","$L3",null,{"children":["$","$4",null,{"name":"Next.MetadataOutlet","children":"$@5"}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"X4zdS6Y6HkLOaElNeHwnq"}
5
+ 0:{"rsc":["$","$1","c",{"children":[["$","$L2",null,{}],[["$","script","script-0",{"src":"/_next/static/chunks/0mtmv-f5qymoi.js","async":true}]],["$","$L3",null,{"children":["$","$4",null,{"name":"Next.MetadataOutlet","children":"$@5"}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"XCVQTR6dRg8tK75W8jDzw"}
6
6
  5:null
@@ -1,5 +1,5 @@
1
1
  1:"$Sreact.fragment"
2
- 2:I[12527,["/_next/static/chunks/04_t39bv8y9pe.js","/_next/static/chunks/0ox7p_szjhn69.js"],"default"]
3
- 3:I[59763,["/_next/static/chunks/04_t39bv8y9pe.js","/_next/static/chunks/0ox7p_szjhn69.js"],"default"]
2
+ 2:I[39756,["/_next/static/chunks/0fkhi51bdw9~~.js","/_next/static/chunks/0d3shmwh5_nmn.js"],"default"]
3
+ 3:I[37457,["/_next/static/chunks/0fkhi51bdw9~~.js","/_next/static/chunks/0d3shmwh5_nmn.js"],"default"]
4
4
  4:[]
5
- 0:{"rsc":["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","template":["$","$L3",null,{}]}]]}],"isPartial":false,"staleTime":300,"varyParams":"$W4","buildId":"X4zdS6Y6HkLOaElNeHwnq"}
5
+ 0:{"rsc":["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","template":["$","$L3",null,{}]}]]}],"isPartial":false,"staleTime":300,"varyParams":"$W4","buildId":"XCVQTR6dRg8tK75W8jDzw"}
package/out/setup.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="preload" href="/_next/static/media/797e433ab948586e-s.p.0.q-h669a_dqa.woff2" as="font" crossorigin="" type="font/woff2"/><link rel="stylesheet" href="/_next/static/chunks/0ccoe1hsu70ql.css" data-precedence="next"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/0z~0.4hivi.f2.js"/><script src="/_next/static/chunks/0ezniz80psxr6.js" async=""></script><script src="/_next/static/chunks/0r7t_sj_sejq9.js" async=""></script><script src="/_next/static/chunks/15i5_ay.0ap.6.js" async=""></script><script src="/_next/static/chunks/0excsn2a_5qsb.js" async=""></script><script src="/_next/static/chunks/turbopack-0wh29ykoy-rb5.js" async=""></script><script src="/_next/static/chunks/04_t39bv8y9pe.js" async=""></script><script src="/_next/static/chunks/0ox7p_szjhn69.js" async=""></script><script src="/_next/static/chunks/084lff9v4p_vh.js" async=""></script><meta name="next-size-adjust" content=""/><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 flex-col"><div hidden=""><!--$--><!--/$--></div><header class="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"></header><div class="flex flex-1 min-h-0"><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 class="h-full overflow-y-auto"><div class="px-6 pt-6 pb-4 border-b border-border"><h1 class="text-lg font-semibold text-text tracking-tight">Set Up Your AI Dev Team</h1><p class="text-[11px] text-text-muted mt-1">Configure agents, connect your repo, and launch a multi-agent development workflow in minutes.</p></div><div class="flex h-[calc(100%-80px)]"><div class="flex-1 flex gap-6 p-6 overflow-y-auto"><div class="w-44 shrink-0"><div class="flex items-start gap-2 py-2"><span class="w-5 h-5 flex items-center justify-center text-[10px] border shrink-0 mt-0.5 border-accent text-accent bg-accent/10">1</span><div><span class="text-[11px] block leading-tight text-text font-semibold">Project Name</span><span class="text-[10px] text-text-muted block">Name your project</span></div></div><div class="flex items-start gap-2 py-2"><span class="w-5 h-5 flex items-center justify-center text-[10px] border shrink-0 mt-0.5 border-border text-text-muted">2</span><div><span class="text-[11px] block leading-tight text-text-muted">GitHub Repo</span><span class="text-[10px] text-text-muted block">Connect a repository</span></div></div><div class="flex items-start gap-2 py-2"><span class="w-5 h-5 flex items-center justify-center text-[10px] border shrink-0 mt-0.5 border-border text-text-muted">3</span><div><span class="text-[11px] block leading-tight text-text-muted">Agent Models</span><span class="text-[10px] text-text-muted block">Configure CLI backends</span></div></div><div class="flex items-start gap-2 py-2"><span class="w-5 h-5 flex items-center justify-center text-[10px] border shrink-0 mt-0.5 border-border text-text-muted">4</span><div><span class="text-[11px] block leading-tight text-text-muted">Working Directory</span><span class="text-[10px] text-text-muted block">Set the local path</span></div></div><div class="flex items-start gap-2 py-2"><span class="w-5 h-5 flex items-center justify-center text-[10px] border shrink-0 mt-0.5 border-border text-text-muted">5</span><div><span class="text-[11px] block leading-tight text-text-muted">Create Workspaces</span><span class="text-[10px] text-text-muted block">Worktrees + seed files</span></div></div><div class="flex items-start gap-2 py-2"><span class="w-5 h-5 flex items-center justify-center text-[10px] border shrink-0 mt-0.5 border-border text-text-muted">6</span><div><span class="text-[11px] block leading-tight text-text-muted">Ready to Launch</span><span class="text-[10px] text-text-muted block">Review &amp; start</span></div></div></div><div class="flex-1 border border-border p-5 min-h-0"><div><h2 class="text-sm font-semibold text-text mb-1">Name your project</h2><p class="text-[11px] text-text-muted mb-4">This name identifies your project in the dashboard and agent configs.</p><input placeholder="e.g. My DeFi App" class="w-full bg-transparent border border-border px-2 py-1.5 text-[12px] text-text outline-none focus:border-accent mb-4" autofocus="" value=""/><button disabled="" class="px-4 py-1.5 bg-accent text-bg text-[12px] font-semibold hover:bg-accent-dim transition-colors disabled:opacity-50">Next</button></div></div></div><div class="w-64 shrink-0 border-l border-border p-4 overflow-y-auto bg-bg-surface/50"><h3 class="text-[11px] font-semibold text-text-muted uppercase tracking-wider mb-3">Configuration Preview</h3><div class="space-y-3 text-[11px]"><div><span class="text-text-muted block mb-0.5">Project</span><span class="text-text">—</span></div><div><span class="text-text-muted block mb-0.5">Repository</span><span class="text-text">—</span></div><div><span class="text-text-muted block mb-0.5">Backends</span><div class="flex justify-between"><span class="text-text capitalize">head</span><span class="text-accent">claude</span></div><div class="flex justify-between"><span class="text-text capitalize">reviewer1</span><span class="text-accent">claude</span></div><div class="flex justify-between"><span class="text-text capitalize">reviewer2</span><span class="text-accent">claude</span></div><div class="flex justify-between"><span class="text-text capitalize">dev</span><span class="text-accent">claude</span></div></div><div><span class="text-text-muted block mb-0.5">Directory</span><span class="text-text font-mono text-[10px]">—</span></div><div><span class="text-text-muted block mb-0.5">Status</span><div class="space-y-0.5"><div class="flex items-center gap-1.5"><span class="text-[10px] text-text">●</span><span class="text-[10px] text-text">Project Name</span></div><div class="flex items-center gap-1.5"><span class="text-[10px] text-text-muted">○</span><span class="text-[10px] text-text-muted">GitHub Repo</span></div><div class="flex items-center gap-1.5"><span class="text-[10px] text-text-muted">○</span><span class="text-[10px] text-text-muted">Agent Models</span></div><div class="flex items-center gap-1.5"><span class="text-[10px] text-text-muted">○</span><span class="text-[10px] text-text-muted">Working Directory</span></div><div class="flex items-center gap-1.5"><span class="text-[10px] text-text-muted">○</span><span class="text-[10px] text-text-muted">Create Workspaces</span></div><div class="flex items-center gap-1.5"><span class="text-[10px] text-text-muted">○</span><span class="text-[10px] text-text-muted">Ready to Launch</span></div></div></div></div></div></div></div><!--$--><!--/$--></main></div><script src="/_next/static/chunks/0z~0.4hivi.f2.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[34852,[\"/_next/static/chunks/04_t39bv8y9pe.js\",\"/_next/static/chunks/0ox7p_szjhn69.js\"],\"default\"]\n3:I[86081,[\"/_next/static/chunks/04_t39bv8y9pe.js\",\"/_next/static/chunks/0ox7p_szjhn69.js\"],\"default\"]\n4:I[12527,[\"/_next/static/chunks/04_t39bv8y9pe.js\",\"/_next/static/chunks/0ox7p_szjhn69.js\"],\"default\"]\n5:I[59763,[\"/_next/static/chunks/04_t39bv8y9pe.js\",\"/_next/static/chunks/0ox7p_szjhn69.js\"],\"default\"]\n6:I[64618,[\"/_next/static/chunks/04_t39bv8y9pe.js\",\"/_next/static/chunks/0ox7p_szjhn69.js\",\"/_next/static/chunks/084lff9v4p_vh.js\"],\"default\"]\n7:I[11717,[\"/_next/static/chunks/04_t39bv8y9pe.js\",\"/_next/static/chunks/0ox7p_szjhn69.js\"],\"OutletBoundary\"]\n8:\"$Sreact.suspense\"\nb:I[11717,[\"/_next/static/chunks/04_t39bv8y9pe.js\",\"/_next/static/chunks/0ox7p_szjhn69.js\"],\"ViewportBoundary\"]\nd:I[11717,[\"/_next/static/chunks/04_t39bv8y9pe.js\",\"/_next/static/chunks/0ox7p_szjhn69.js\"],\"MetadataBoundary\"]\nf:I[92243,[\"/_next/static/chunks/04_t39bv8y9pe.js\",\"/_next/static/chunks/0ox7p_szjhn69.js\"],\"default\",1]\n:HL[\"/_next/static/chunks/0ccoe1hsu70ql.css\",\"style\"]\n:HL[\"/_next/static/media/797e433ab948586e-s.p.0.q-h669a_dqa.woff2\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\n"])</script><script>self.__next_f.push([1,"0:{\"P\":null,\"c\":[\"\",\"setup\"],\"q\":\"\",\"i\":false,\"f\":[[[\"\",{\"children\":[\"setup\",{\"children\":[\"__PAGE__\",{}]}]},\"$undefined\",\"$undefined\",16],[[\"$\",\"$1\",\"c\",{\"children\":[[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/chunks/0ccoe1hsu70ql.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-0\",{\"src\":\"/_next/static/chunks/04_t39bv8y9pe.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 flex-col\",\"children\":[[\"$\",\"$L2\",null,{}],[\"$\",\"div\",null,{\"className\":\"flex flex-1 min-h-0\",\"children\":[[\"$\",\"$L3\",null,{}],[\"$\",\"main\",null,{\"className\":\"flex-1 min-w-0 overflow-auto\",\"children\":[\"$\",\"$L4\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L5\",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,[\"$\",\"$L4\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L5\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}],{\"children\":[[\"$\",\"$1\",\"c\",{\"children\":[[\"$\",\"$L6\",null,{}],[[\"$\",\"script\",\"script-0\",{\"src\":\"/_next/static/chunks/084lff9v4p_vh.js\",\"async\":true,\"nonce\":\"$undefined\"}]],[\"$\",\"$L7\",null,{\"children\":[\"$\",\"$8\",null,{\"name\":\"Next.MetadataOutlet\",\"children\":\"$@9\"}]}]]}],{},null,false,null]},null,false,\"$@a\"]},null,false,null],[\"$\",\"$1\",\"h\",{\"children\":[null,[\"$\",\"$Lb\",null,{\"children\":\"$Lc\"}],[\"$\",\"div\",null,{\"hidden\":true,\"children\":[\"$\",\"$Ld\",null,{\"children\":[\"$\",\"$8\",null,{\"name\":\"Next.Metadata\",\"children\":\"$Le\"}]}]}],[\"$\",\"meta\",null,{\"name\":\"next-size-adjust\",\"content\":\"\"}]]}],false]],\"m\":\"$undefined\",\"G\":[\"$f\",[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/chunks/0ccoe1hsu70ql.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}]]],\"S\":true,\"h\":null,\"s\":\"$undefined\",\"l\":\"$undefined\",\"p\":\"$undefined\",\"d\":\"$undefined\",\"b\":\"X4zdS6Y6HkLOaElNeHwnq\"}\n"])</script><script>self.__next_f.push([1,"10:[]\na:\"$W10\"\n"])</script><script>self.__next_f.push([1,"c:[[\"$\",\"meta\",\"0\",{\"charSet\":\"utf-8\"}],[\"$\",\"meta\",\"1\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}]]\n"])</script><script>self.__next_f.push([1,"11:I[80070,[\"/_next/static/chunks/04_t39bv8y9pe.js\",\"/_next/static/chunks/0ox7p_szjhn69.js\"],\"IconMark\"]\n9:null\ne:[[\"$\",\"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\"}],[\"$\",\"$L11\",\"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="preload" href="/_next/static/media/797e433ab948586e-s.p.0.q-h669a_dqa.woff2" as="font" crossorigin="" type="font/woff2"/><link rel="stylesheet" href="/_next/static/chunks/0ccoe1hsu70ql.css" data-precedence="next"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/0ze4gu236oq96.js"/><script src="/_next/static/chunks/0.bbxho1vnxin.js" async=""></script><script src="/_next/static/chunks/16g.ca89g7fib.js" async=""></script><script src="/_next/static/chunks/0zfotsowwll1x.js" async=""></script><script src="/_next/static/chunks/0pqt~8bl3ukh4.js" async=""></script><script src="/_next/static/chunks/turbopack-0lcwh84lrj9gi.js" async=""></script><script src="/_next/static/chunks/0fkhi51bdw9~~.js" async=""></script><script src="/_next/static/chunks/0d3shmwh5_nmn.js" async=""></script><script src="/_next/static/chunks/0mtmv-f5qymoi.js" async=""></script><meta name="next-size-adjust" content=""/><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 flex-col"><div hidden=""><!--$--><!--/$--></div><header class="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"></header><div class="flex flex-1 min-h-0"><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 class="h-full overflow-y-auto"><div class="px-6 pt-6 pb-4 border-b border-border"><h1 class="text-lg font-semibold text-text tracking-tight">Set Up Your AI Dev Team</h1><p class="text-[11px] text-text-muted mt-1">Configure agents, connect your repo, and launch a multi-agent development workflow in minutes.</p></div><div class="flex h-[calc(100%-80px)]"><div class="flex-1 flex gap-6 p-6 overflow-y-auto"><div class="w-44 shrink-0"><div class="flex items-start gap-2 py-2"><span class="w-5 h-5 flex items-center justify-center text-[10px] border shrink-0 mt-0.5 border-accent text-accent bg-accent/10">1</span><div><span class="text-[11px] block leading-tight text-text font-semibold">Project Name</span><span class="text-[10px] text-text-muted block">Name your project</span></div></div><div class="flex items-start gap-2 py-2"><span class="w-5 h-5 flex items-center justify-center text-[10px] border shrink-0 mt-0.5 border-border text-text-muted">2</span><div><span class="text-[11px] block leading-tight text-text-muted">GitHub Repo</span><span class="text-[10px] text-text-muted block">Connect a repository</span></div></div><div class="flex items-start gap-2 py-2"><span class="w-5 h-5 flex items-center justify-center text-[10px] border shrink-0 mt-0.5 border-border text-text-muted">3</span><div><span class="text-[11px] block leading-tight text-text-muted">Agent Models</span><span class="text-[10px] text-text-muted block">Configure CLI backends</span></div></div><div class="flex items-start gap-2 py-2"><span class="w-5 h-5 flex items-center justify-center text-[10px] border shrink-0 mt-0.5 border-border text-text-muted">4</span><div><span class="text-[11px] block leading-tight text-text-muted">Working Directory</span><span class="text-[10px] text-text-muted block">Set the local path</span></div></div><div class="flex items-start gap-2 py-2"><span class="w-5 h-5 flex items-center justify-center text-[10px] border shrink-0 mt-0.5 border-border text-text-muted">5</span><div><span class="text-[11px] block leading-tight text-text-muted">Create Workspaces</span><span class="text-[10px] text-text-muted block">Worktrees + seed files</span></div></div><div class="flex items-start gap-2 py-2"><span class="w-5 h-5 flex items-center justify-center text-[10px] border shrink-0 mt-0.5 border-border text-text-muted">6</span><div><span class="text-[11px] block leading-tight text-text-muted">Ready to Launch</span><span class="text-[10px] text-text-muted block">Review &amp; start</span></div></div></div><div class="flex-1 border border-border p-5 min-h-0"><div><h2 class="text-sm font-semibold text-text mb-1">Name your project</h2><p class="text-[11px] text-text-muted mb-4">This name identifies your project in the dashboard and agent configs.</p><input placeholder="e.g. My DeFi App" class="w-full bg-transparent border border-border px-2 py-1.5 text-[12px] text-text outline-none focus:border-accent mb-4" autofocus="" value=""/><button disabled="" class="px-4 py-1.5 bg-accent text-bg text-[12px] font-semibold hover:bg-accent-dim transition-colors disabled:opacity-50">Next</button></div></div></div><div class="w-64 shrink-0 border-l border-border p-4 overflow-y-auto bg-bg-surface/50"><h3 class="text-[11px] font-semibold text-text-muted uppercase tracking-wider mb-3">Configuration Preview</h3><div class="space-y-3 text-[11px]"><div><span class="text-text-muted block mb-0.5">Project</span><span class="text-text">—</span></div><div><span class="text-text-muted block mb-0.5">Repository</span><span class="text-text">—</span></div><div><span class="text-text-muted block mb-0.5">Backends</span><div class="flex justify-between"><span class="text-text capitalize">head</span><span class="text-accent">claude</span></div><div class="flex justify-between"><span class="text-text capitalize">reviewer1</span><span class="text-accent">claude</span></div><div class="flex justify-between"><span class="text-text capitalize">reviewer2</span><span class="text-accent">claude</span></div><div class="flex justify-between"><span class="text-text capitalize">dev</span><span class="text-accent">claude</span></div></div><div><span class="text-text-muted block mb-0.5">Directory</span><span class="text-text font-mono text-[10px]">—</span></div><div><span class="text-text-muted block mb-0.5">Status</span><div class="space-y-0.5"><div class="flex items-center gap-1.5"><span class="text-[10px] text-text">●</span><span class="text-[10px] text-text">Project Name</span></div><div class="flex items-center gap-1.5"><span class="text-[10px] text-text-muted">○</span><span class="text-[10px] text-text-muted">GitHub Repo</span></div><div class="flex items-center gap-1.5"><span class="text-[10px] text-text-muted">○</span><span class="text-[10px] text-text-muted">Agent Models</span></div><div class="flex items-center gap-1.5"><span class="text-[10px] text-text-muted">○</span><span class="text-[10px] text-text-muted">Working Directory</span></div><div class="flex items-center gap-1.5"><span class="text-[10px] text-text-muted">○</span><span class="text-[10px] text-text-muted">Create Workspaces</span></div><div class="flex items-center gap-1.5"><span class="text-[10px] text-text-muted">○</span><span class="text-[10px] text-text-muted">Ready to Launch</span></div></div></div></div></div></div></div><!--$--><!--/$--></main></div><script src="/_next/static/chunks/0ze4gu236oq96.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[26704,[\"/_next/static/chunks/0fkhi51bdw9~~.js\",\"/_next/static/chunks/0d3shmwh5_nmn.js\"],\"default\"]\n3:I[22140,[\"/_next/static/chunks/0fkhi51bdw9~~.js\",\"/_next/static/chunks/0d3shmwh5_nmn.js\"],\"default\"]\n4:I[39756,[\"/_next/static/chunks/0fkhi51bdw9~~.js\",\"/_next/static/chunks/0d3shmwh5_nmn.js\"],\"default\"]\n5:I[37457,[\"/_next/static/chunks/0fkhi51bdw9~~.js\",\"/_next/static/chunks/0d3shmwh5_nmn.js\"],\"default\"]\n6:I[94810,[\"/_next/static/chunks/0fkhi51bdw9~~.js\",\"/_next/static/chunks/0d3shmwh5_nmn.js\",\"/_next/static/chunks/0mtmv-f5qymoi.js\"],\"default\"]\n7:I[97367,[\"/_next/static/chunks/0fkhi51bdw9~~.js\",\"/_next/static/chunks/0d3shmwh5_nmn.js\"],\"OutletBoundary\"]\n8:\"$Sreact.suspense\"\nb:I[97367,[\"/_next/static/chunks/0fkhi51bdw9~~.js\",\"/_next/static/chunks/0d3shmwh5_nmn.js\"],\"ViewportBoundary\"]\nd:I[97367,[\"/_next/static/chunks/0fkhi51bdw9~~.js\",\"/_next/static/chunks/0d3shmwh5_nmn.js\"],\"MetadataBoundary\"]\nf:I[68027,[\"/_next/static/chunks/0fkhi51bdw9~~.js\",\"/_next/static/chunks/0d3shmwh5_nmn.js\"],\"default\",1]\n:HL[\"/_next/static/chunks/0ccoe1hsu70ql.css\",\"style\"]\n:HL[\"/_next/static/media/797e433ab948586e-s.p.0.q-h669a_dqa.woff2\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\n"])</script><script>self.__next_f.push([1,"0:{\"P\":null,\"c\":[\"\",\"setup\"],\"q\":\"\",\"i\":false,\"f\":[[[\"\",{\"children\":[\"setup\",{\"children\":[\"__PAGE__\",{}]}]},\"$undefined\",\"$undefined\",16],[[\"$\",\"$1\",\"c\",{\"children\":[[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/chunks/0ccoe1hsu70ql.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-0\",{\"src\":\"/_next/static/chunks/0fkhi51bdw9~~.js\",\"async\":true,\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-1\",{\"src\":\"/_next/static/chunks/0d3shmwh5_nmn.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 flex-col\",\"children\":[[\"$\",\"$L2\",null,{}],[\"$\",\"div\",null,{\"className\":\"flex flex-1 min-h-0\",\"children\":[[\"$\",\"$L3\",null,{}],[\"$\",\"main\",null,{\"className\":\"flex-1 min-w-0 overflow-auto\",\"children\":[\"$\",\"$L4\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L5\",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,[\"$\",\"$L4\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L5\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}],{\"children\":[[\"$\",\"$1\",\"c\",{\"children\":[[\"$\",\"$L6\",null,{}],[[\"$\",\"script\",\"script-0\",{\"src\":\"/_next/static/chunks/0mtmv-f5qymoi.js\",\"async\":true,\"nonce\":\"$undefined\"}]],[\"$\",\"$L7\",null,{\"children\":[\"$\",\"$8\",null,{\"name\":\"Next.MetadataOutlet\",\"children\":\"$@9\"}]}]]}],{},null,false,null]},null,false,\"$@a\"]},null,false,null],[\"$\",\"$1\",\"h\",{\"children\":[null,[\"$\",\"$Lb\",null,{\"children\":\"$Lc\"}],[\"$\",\"div\",null,{\"hidden\":true,\"children\":[\"$\",\"$Ld\",null,{\"children\":[\"$\",\"$8\",null,{\"name\":\"Next.Metadata\",\"children\":\"$Le\"}]}]}],[\"$\",\"meta\",null,{\"name\":\"next-size-adjust\",\"content\":\"\"}]]}],false]],\"m\":\"$undefined\",\"G\":[\"$f\",[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/chunks/0ccoe1hsu70ql.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}]]],\"S\":true,\"h\":null,\"s\":\"$undefined\",\"l\":\"$undefined\",\"p\":\"$undefined\",\"d\":\"$undefined\",\"b\":\"XCVQTR6dRg8tK75W8jDzw\"}\n"])</script><script>self.__next_f.push([1,"10:[]\na:\"$W10\"\n"])</script><script>self.__next_f.push([1,"c:[[\"$\",\"meta\",\"0\",{\"charSet\":\"utf-8\"}],[\"$\",\"meta\",\"1\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}]]\n"])</script><script>self.__next_f.push([1,"11:I[27201,[\"/_next/static/chunks/0fkhi51bdw9~~.js\",\"/_next/static/chunks/0d3shmwh5_nmn.js\"],\"IconMark\"]\n9:null\ne:[[\"$\",\"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\"}],[\"$\",\"$L11\",\"3\",{}]]\n"])</script></body></html>
package/out/setup.txt CHANGED
@@ -1,20 +1,20 @@
1
1
  1:"$Sreact.fragment"
2
- 2:I[34852,["/_next/static/chunks/04_t39bv8y9pe.js","/_next/static/chunks/0ox7p_szjhn69.js"],"default"]
3
- 3:I[86081,["/_next/static/chunks/04_t39bv8y9pe.js","/_next/static/chunks/0ox7p_szjhn69.js"],"default"]
4
- 4:I[12527,["/_next/static/chunks/04_t39bv8y9pe.js","/_next/static/chunks/0ox7p_szjhn69.js"],"default"]
5
- 5:I[59763,["/_next/static/chunks/04_t39bv8y9pe.js","/_next/static/chunks/0ox7p_szjhn69.js"],"default"]
6
- 6:I[64618,["/_next/static/chunks/04_t39bv8y9pe.js","/_next/static/chunks/0ox7p_szjhn69.js","/_next/static/chunks/084lff9v4p_vh.js"],"default"]
7
- 7:I[11717,["/_next/static/chunks/04_t39bv8y9pe.js","/_next/static/chunks/0ox7p_szjhn69.js"],"OutletBoundary"]
2
+ 2:I[26704,["/_next/static/chunks/0fkhi51bdw9~~.js","/_next/static/chunks/0d3shmwh5_nmn.js"],"default"]
3
+ 3:I[22140,["/_next/static/chunks/0fkhi51bdw9~~.js","/_next/static/chunks/0d3shmwh5_nmn.js"],"default"]
4
+ 4:I[39756,["/_next/static/chunks/0fkhi51bdw9~~.js","/_next/static/chunks/0d3shmwh5_nmn.js"],"default"]
5
+ 5:I[37457,["/_next/static/chunks/0fkhi51bdw9~~.js","/_next/static/chunks/0d3shmwh5_nmn.js"],"default"]
6
+ 6:I[94810,["/_next/static/chunks/0fkhi51bdw9~~.js","/_next/static/chunks/0d3shmwh5_nmn.js","/_next/static/chunks/0mtmv-f5qymoi.js"],"default"]
7
+ 7:I[97367,["/_next/static/chunks/0fkhi51bdw9~~.js","/_next/static/chunks/0d3shmwh5_nmn.js"],"OutletBoundary"]
8
8
  8:"$Sreact.suspense"
9
- b:I[11717,["/_next/static/chunks/04_t39bv8y9pe.js","/_next/static/chunks/0ox7p_szjhn69.js"],"ViewportBoundary"]
10
- d:I[11717,["/_next/static/chunks/04_t39bv8y9pe.js","/_next/static/chunks/0ox7p_szjhn69.js"],"MetadataBoundary"]
11
- f:I[92243,["/_next/static/chunks/04_t39bv8y9pe.js","/_next/static/chunks/0ox7p_szjhn69.js"],"default",1]
9
+ b:I[97367,["/_next/static/chunks/0fkhi51bdw9~~.js","/_next/static/chunks/0d3shmwh5_nmn.js"],"ViewportBoundary"]
10
+ d:I[97367,["/_next/static/chunks/0fkhi51bdw9~~.js","/_next/static/chunks/0d3shmwh5_nmn.js"],"MetadataBoundary"]
11
+ f:I[68027,["/_next/static/chunks/0fkhi51bdw9~~.js","/_next/static/chunks/0d3shmwh5_nmn.js"],"default",1]
12
12
  :HL["/_next/static/chunks/0ccoe1hsu70ql.css","style"]
13
13
  :HL["/_next/static/media/797e433ab948586e-s.p.0.q-h669a_dqa.woff2","font",{"crossOrigin":"","type":"font/woff2"}]
14
- 0:{"P":null,"c":["","setup"],"q":"","i":false,"f":[[["",{"children":["setup",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/chunks/0ccoe1hsu70ql.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/_next/static/chunks/04_t39bv8y9pe.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 flex-col","children":[["$","$L2",null,{}],["$","div",null,{"className":"flex flex-1 min-h-0","children":[["$","$L3",null,{}],["$","main",null,{"className":"flex-1 min-w-0 overflow-auto","children":["$","$L4",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",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,["$","$L4",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}],{"children":[["$","$1","c",{"children":[["$","$L6",null,{}],[["$","script","script-0",{"src":"/_next/static/chunks/084lff9v4p_vh.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"children":["$","$8",null,{"name":"Next.MetadataOutlet","children":"$@9"}]}]]}],{},null,false,null]},null,false,"$@a"]},null,false,null],["$","$1","h",{"children":[null,["$","$Lb",null,{"children":"$Lc"}],["$","div",null,{"hidden":true,"children":["$","$Ld",null,{"children":["$","$8",null,{"name":"Next.Metadata","children":"$Le"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],false]],"m":"$undefined","G":["$f",[["$","link","0",{"rel":"stylesheet","href":"/_next/static/chunks/0ccoe1hsu70ql.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"X4zdS6Y6HkLOaElNeHwnq"}
14
+ 0:{"P":null,"c":["","setup"],"q":"","i":false,"f":[[["",{"children":["setup",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/chunks/0ccoe1hsu70ql.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/_next/static/chunks/0fkhi51bdw9~~.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/_next/static/chunks/0d3shmwh5_nmn.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 flex-col","children":[["$","$L2",null,{}],["$","div",null,{"className":"flex flex-1 min-h-0","children":[["$","$L3",null,{}],["$","main",null,{"className":"flex-1 min-w-0 overflow-auto","children":["$","$L4",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",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,["$","$L4",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}],{"children":[["$","$1","c",{"children":[["$","$L6",null,{}],[["$","script","script-0",{"src":"/_next/static/chunks/0mtmv-f5qymoi.js","async":true,"nonce":"$undefined"}]],["$","$L7",null,{"children":["$","$8",null,{"name":"Next.MetadataOutlet","children":"$@9"}]}]]}],{},null,false,null]},null,false,"$@a"]},null,false,null],["$","$1","h",{"children":[null,["$","$Lb",null,{"children":"$Lc"}],["$","div",null,{"hidden":true,"children":["$","$Ld",null,{"children":["$","$8",null,{"name":"Next.Metadata","children":"$Le"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],false]],"m":"$undefined","G":["$f",[["$","link","0",{"rel":"stylesheet","href":"/_next/static/chunks/0ccoe1hsu70ql.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"XCVQTR6dRg8tK75W8jDzw"}
15
15
  10:[]
16
16
  a:"$W10"
17
17
  c:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]
18
- 11:I[80070,["/_next/static/chunks/04_t39bv8y9pe.js","/_next/static/chunks/0ox7p_szjhn69.js"],"IconMark"]
18
+ 11:I[27201,["/_next/static/chunks/0fkhi51bdw9~~.js","/_next/static/chunks/0d3shmwh5_nmn.js"],"IconMark"]
19
19
  9:null
20
20
  e:[["$","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"}],["$","$L11","3",{}]]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "quadwork",
3
- "version": "1.5.3",
3
+ "version": "1.5.4",
4
4
  "description": "Unified dashboard for multi-agent coding teams — 4 AI agents, one terminal",
5
5
  "bin": {
6
6
  "quadwork": "./bin/quadwork.js"
package/server/index.js CHANGED
@@ -9,6 +9,7 @@ const { spawn } = require("child_process");
9
9
  const { readConfig, resolveAgentCwd, resolveAgentCommand, resolveProjectChattr, resolveChattrSpawn, syncChattrToken, CONFIG_PATH } = require("./config");
10
10
  const routes = require("./routes");
11
11
  const { waitForAgentChattrReady, registerAgent, deregisterAgent, startHeartbeat, stopHeartbeat } = require("./agentchattr-registry");
12
+ const { patchAgentchattrCss } = require("./install-agentchattr");
12
13
  const { startQueueWatcher, stopQueueWatcher } = require("./queue-watcher");
13
14
 
14
15
  const net = require("net");
@@ -800,6 +801,9 @@ async function handleAgentChattr(req, res) {
800
801
 
801
802
  // Resolve AgentChattr from its cloned directory
802
803
  const { dir: acDir } = resolveProjectChattr(projectId);
804
+ // #394: backfill sender-overflow CSS/JS patch on every spawn so
805
+ // existing installs receive the fix without manual update.
806
+ patchAgentchattrCss(acDir);
803
807
  const acSpawn = resolveChattrSpawn(acDir);
804
808
  if (!acSpawn) {
805
809
  setProc({ process: null, state: "error", error: `AgentChattr not installed. Clone it: git clone https://github.com/bcurts/agentchattr.git ${acDir}` });
@@ -834,11 +838,77 @@ async function handleAgentChattr(req, res) {
834
838
  return child;
835
839
  }
836
840
 
841
+ // #386: Kill any process listening on the AC port. Handles orphaned
842
+ // processes that survive QuadWork restarts (detached + unref'd spawns
843
+ // lose their tracked reference when the Node process recycles).
844
+ function killProcessOnPort(port) {
845
+ try {
846
+ const pids = execSync(`lsof -ti TCP:${port} -sTCP:LISTEN`, {
847
+ encoding: "utf-8",
848
+ timeout: 5000,
849
+ stdio: ["pipe", "pipe", "pipe"],
850
+ }).trim();
851
+ if (!pids) return;
852
+ for (const line of pids.split("\n")) {
853
+ const pid = parseInt(line, 10);
854
+ if (pid > 0) {
855
+ try { process.kill(pid, "SIGTERM"); } catch {}
856
+ }
857
+ }
858
+ } catch {
859
+ // lsof exits non-zero when no matching process — expected
860
+ }
861
+ }
862
+
863
+ // #386: Poll until the port is free or timeout expires.
864
+ function waitForPortFree(port, timeoutMs = 3000) {
865
+ const start = Date.now();
866
+ return new Promise((resolve) => {
867
+ function check() {
868
+ try {
869
+ execSync(`lsof -ti TCP:${port} -sTCP:LISTEN`, {
870
+ encoding: "utf-8",
871
+ timeout: 2000,
872
+ stdio: ["pipe", "pipe", "pipe"],
873
+ });
874
+ // Still occupied — retry if within budget
875
+ if (Date.now() - start < timeoutMs) {
876
+ setTimeout(check, 200);
877
+ } else {
878
+ resolve(false);
879
+ }
880
+ } catch {
881
+ // lsof found nothing — port is free
882
+ resolve(true);
883
+ }
884
+ }
885
+ check();
886
+ });
887
+ }
888
+
837
889
  if (action === "start") {
838
890
  const proc = getProc();
839
891
  if (proc.state === "running" && proc.process) {
840
892
  return res.json({ ok: true, state: "running", message: "Already running" });
841
893
  }
894
+ // #401: validate AgentChattr is installed BEFORE killing anything on
895
+ // the port. Without this guard, clicking Start when AC is missing
896
+ // kills an unrelated process then fails with "not installed".
897
+ const { dir: acDir } = resolveProjectChattr(projectId);
898
+ const acSpawn = resolveChattrSpawn(acDir);
899
+ if (!acSpawn) {
900
+ const errMsg = `AgentChattr not installed. Clone it: git clone https://github.com/bcurts/agentchattr.git ${acDir}`;
901
+ setProc({ process: null, state: "error", error: errMsg });
902
+ return res.status(500).json({ ok: false, state: "error", error: errMsg });
903
+ }
904
+
905
+ // #393: kill any orphaned process on the port before spawning
906
+ // (same pattern as restart/stop from #386).
907
+ killProcessOnPort(chattrPort);
908
+ const portFree = await waitForPortFree(chattrPort, 3000);
909
+ if (!portFree) {
910
+ console.warn(`[agentchattr] ${projectId} port ${chattrPort} still occupied after 3s — spawning anyway`);
911
+ }
842
912
  try {
843
913
  const child = spawnChattr();
844
914
  if (!child) {
@@ -857,6 +927,8 @@ async function handleAgentChattr(req, res) {
857
927
  if (proc.process) {
858
928
  try { proc.process.kill("SIGTERM"); } catch {}
859
929
  }
930
+ // #386: also kill any orphaned process holding the port
931
+ killProcessOnPort(chattrPort);
860
932
  setProc({ process: null, state: "stopped", error: null });
861
933
  res.json({ ok: true, state: "stopped" });
862
934
  } else if (action === "restart") {
@@ -876,46 +948,53 @@ async function handleAgentChattr(req, res) {
876
948
  if (proc.process) {
877
949
  try { proc.process.kill("SIGTERM"); } catch {}
878
950
  }
951
+ // #386: also kill any orphaned process holding the port (handles
952
+ // detached processes that survived a QuadWork restart).
953
+ killProcessOnPort(chattrPort);
879
954
  setProc({ process: null, state: "stopped", error: null });
880
- setTimeout(() => {
881
- try {
882
- const child = spawnChattr();
883
- if (!child) {
884
- const errProc = getProc();
885
- return res.status(500).json({ ok: false, state: "error", error: errProc.error || "Failed to start AgentChattr" });
886
- }
887
- // Sync token after AgentChattr restarts
888
- setTimeout(() => syncChattrToken(projectId), 2000);
889
- // #424 / quadwork#304 Phase 3: optional auto-restore.
890
- // Fire the restore 3s after spawn so AC's ws is ready.
891
- // Best-effort: never blocks the restart response or
892
- // rolls back on error.
893
- if (shouldAutoRestore) {
894
- setTimeout(async () => {
895
- try {
896
- const snapDir = path.join(require("os").homedir(), ".quadwork", projectId, "history-snapshots");
897
- if (!fs.existsSync(snapDir)) return;
898
- const newest = fs.readdirSync(snapDir)
899
- .filter((f) => f.endsWith(".json"))
900
- .map((f) => ({ f, t: fs.statSync(path.join(snapDir, f)).mtimeMs }))
901
- .sort((a, b) => b.t - a.t)[0];
902
- if (!newest) return;
903
- const r = await fetch(`http://127.0.0.1:${PORT}/api/project-history/restore?project=${encodeURIComponent(projectId)}&name=${encodeURIComponent(newest.f)}`, {
904
- method: "POST",
905
- });
906
- if (r.ok) console.log(`[snapshot] ${projectId} auto-restored ${newest.f}`);
907
- else console.warn(`[snapshot] ${projectId} auto-restore returned ${r.status}`);
908
- } catch (err) {
909
- console.warn(`[snapshot] ${projectId} auto-restore failed: ${err.message || err}`);
910
- }
911
- }, 3000);
912
- }
913
- res.json({ ok: true, state: "running", pid: child.pid });
914
- } catch (err) {
915
- setProc({ process: null, state: "error", error: err.message });
916
- res.status(500).json({ ok: false, state: "error", error: err.message });
955
+ // #386: wait for the port to actually be free before spawning,
956
+ // instead of a fixed 500ms that may race the old process.
957
+ const portFree = await waitForPortFree(chattrPort, 3000);
958
+ if (!portFree) {
959
+ console.warn(`[agentchattr] ${projectId} port ${chattrPort} still occupied after 3s — spawning anyway`);
960
+ }
961
+ try {
962
+ const child = spawnChattr();
963
+ if (!child) {
964
+ const errProc = getProc();
965
+ return res.status(500).json({ ok: false, state: "error", error: errProc.error || "Failed to start AgentChattr" });
917
966
  }
918
- }, 500);
967
+ // Sync token after AgentChattr restarts
968
+ setTimeout(() => syncChattrToken(projectId), 2000);
969
+ // #424 / quadwork#304 Phase 3: optional auto-restore.
970
+ // Fire the restore 3s after spawn so AC's ws is ready.
971
+ // Best-effort: never blocks the restart response or
972
+ // rolls back on error.
973
+ if (shouldAutoRestore) {
974
+ setTimeout(async () => {
975
+ try {
976
+ const snapDir = path.join(require("os").homedir(), ".quadwork", projectId, "history-snapshots");
977
+ if (!fs.existsSync(snapDir)) return;
978
+ const newest = fs.readdirSync(snapDir)
979
+ .filter((f) => f.endsWith(".json"))
980
+ .map((f) => ({ f, t: fs.statSync(path.join(snapDir, f)).mtimeMs }))
981
+ .sort((a, b) => b.t - a.t)[0];
982
+ if (!newest) return;
983
+ const r = await fetch(`http://127.0.0.1:${PORT}/api/project-history/restore?project=${encodeURIComponent(projectId)}&name=${encodeURIComponent(newest.f)}`, {
984
+ method: "POST",
985
+ });
986
+ if (r.ok) console.log(`[snapshot] ${projectId} auto-restored ${newest.f}`);
987
+ else console.warn(`[snapshot] ${projectId} auto-restore returned ${r.status}`);
988
+ } catch (err) {
989
+ console.warn(`[snapshot] ${projectId} auto-restore failed: ${err.message || err}`);
990
+ }
991
+ }, 3000);
992
+ }
993
+ res.json({ ok: true, state: "running", pid: child.pid });
994
+ } catch (err) {
995
+ setProc({ process: null, state: "error", error: err.message });
996
+ res.status(500).json({ ok: false, state: "error", error: err.message });
997
+ }
919
998
  } else if (action === "update") {
920
999
  // Update AgentChattr: stop → git pull → pip install → restart
921
1000
  const { dir: acDir } = resolveProjectChattr(projectId);
@@ -939,12 +1018,18 @@ async function handleAgentChattr(req, res) {
939
1018
  const wasRunning = proc.process && proc.state === "running";
940
1019
  if (wasRunning) {
941
1020
  try { proc.process.kill("SIGTERM"); } catch {}
1021
+ }
1022
+ // #386: kill orphaned processes on the port too
1023
+ killProcessOnPort(chattrPort);
1024
+ if (wasRunning) {
942
1025
  setProc({ process: null, state: "stopped", error: null });
943
- // Brief wait for process to release files
944
- await new Promise(r => setTimeout(r, 1000));
1026
+ // Wait for the port to be released before pulling/restarting
1027
+ await waitForPortFree(chattrPort, 3000);
945
1028
  }
946
1029
 
947
1030
  const pullResult = execSync("git pull 2>&1", { cwd: acDir, encoding: "utf-8", timeout: 30000 }).trim();
1031
+ // #388: re-apply sender-overflow CSS patch after git pull
1032
+ patchAgentchattrCss(acDir);
948
1033
  const venvPython = path.join(acDir, ".venv", "bin", "python");
949
1034
  let pipResult = "";
950
1035
  const reqFile = path.join(acDir, "requirements.txt");
@@ -1658,10 +1743,14 @@ setInterval(runLoopGuardPollingTick, LOOP_GUARD_POLL_INTERVAL_MS);
1658
1743
  server.listen(PORT, "127.0.0.1", () => {
1659
1744
  console.log(`QuadWork server listening on http://127.0.0.1:${PORT}`);
1660
1745
  syncTriggersFromConfig();
1661
- // Sync AgentChattr tokens for all projects on startup
1746
+ // Sync AgentChattr tokens for all projects on startup and backfill
1747
+ // the sender-overflow CSS/JS patch (#402) so already-running AC
1748
+ // instances receive the fix without requiring a restart.
1662
1749
  const startupCfg = readConfig();
1663
1750
  for (const p of (startupCfg.projects || [])) {
1664
1751
  syncChattrToken(p.id);
1752
+ const { dir: acDir } = resolveProjectChattr(p.id);
1753
+ if (acDir) patchAgentchattrCss(acDir);
1665
1754
  }
1666
1755
  });
1667
1756
 
@@ -192,6 +192,8 @@ function _installAgentChattrLocked(dir, setError) {
192
192
  if (pipResult === null) return setError(`pip install -r ${reqFile} failed`);
193
193
  }
194
194
  }
195
+ // #388: patch sender-column overflow CSS after clone/install
196
+ patchAgentchattrCss(dir);
195
197
  return dir;
196
198
  }
197
199
 
@@ -207,9 +209,60 @@ function chattrSpawnArgs(dir, extraArgs) {
207
209
  return { command: venvPython, spawnArgs: ["run.py", ...(extraArgs || [])], cwd: dir };
208
210
  }
209
211
 
212
+ /**
213
+ * #388: Patch AgentChattr's static files for sender-column overflow.
214
+ * Idempotent — skips if the marker is already present.
215
+ * Called after install and after update (git pull overwrites static/).
216
+ *
217
+ * CSS: cap .msg-sender width with ellipsis truncation.
218
+ * JS: add title attribute to .msg-sender spans for hover tooltip.
219
+ */
220
+ function patchAgentchattrCss(dir) {
221
+ if (!dir) return;
222
+ // --- CSS patch ---
223
+ const cssPath = path.join(dir, "static", "style.css");
224
+ if (fs.existsSync(cssPath)) {
225
+ try {
226
+ const content = fs.readFileSync(cssPath, "utf-8");
227
+ if (!content.includes("/* quadwork#388 sender-overflow fix */")) {
228
+ const patch = `
229
+ /* quadwork#388 sender-overflow fix */
230
+ .msg-sender {
231
+ max-width: 80px;
232
+ overflow: hidden;
233
+ text-overflow: ellipsis;
234
+ white-space: nowrap;
235
+ display: inline-block;
236
+ vertical-align: middle;
237
+ }
238
+ `;
239
+ fs.writeFileSync(cssPath, content + patch);
240
+ }
241
+ } catch {}
242
+ }
243
+ // --- JS patch: add title attribute to .msg-sender for hover tooltip ---
244
+ const jsPath = path.join(dir, "static", "chat.js");
245
+ if (fs.existsSync(jsPath)) {
246
+ try {
247
+ const content = fs.readFileSync(jsPath, "utf-8");
248
+ if (!content.includes("quadwork#388")) {
249
+ // Add title= to the msg-sender span so truncated names show full on hover
250
+ const patched = content.replace(
251
+ /(<span class="msg-sender" style="color: \$\{senderColor\}">)/g,
252
+ `<span class="msg-sender" title="\${escapeHtml(msg.sender)}" style="color: \${senderColor}">`,
253
+ );
254
+ if (patched !== content) {
255
+ fs.writeFileSync(jsPath, patched + "\n// quadwork#388\n");
256
+ }
257
+ }
258
+ } catch {}
259
+ }
260
+ }
261
+
210
262
  module.exports = {
211
263
  AGENTCHATTR_REPO,
212
264
  findAgentChattr,
213
265
  installAgentChattr,
214
266
  chattrSpawnArgs,
267
+ patchAgentchattrCss,
215
268
  };
package/server/routes.js CHANGED
@@ -2407,12 +2407,19 @@ function resolveProjectAgentchattrUrl(cfg, project) {
2407
2407
  // #383 Bug 2: the upstream bridge only reads `agentchattr_url` from
2408
2408
  // inside `[telegram]`. A separate `[agentchattr]` section is silently
2409
2409
  // ignored and the bridge falls back to its hardcoded :8300 default.
2410
- function buildTelegramBridgeToml(tg) {
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`);
2411
2417
  return (
2412
2418
  `[telegram]\n` +
2413
2419
  `bot_token = "${tg.bot_token}"\n` +
2414
2420
  `chat_id = "${tg.chat_id}"\n` +
2415
- `agentchattr_url = "${tg.agentchattr_url}"\n`
2421
+ `agentchattr_url = "${tg.agentchattr_url}"\n` +
2422
+ `cursor_file = "${cursorFile}"\n`
2416
2423
  );
2417
2424
  }
2418
2425
 
@@ -2745,7 +2752,7 @@ router.post("/api/telegram", async (req, res) => {
2745
2752
  const tomlPath = telegramConfigToml(projectId);
2746
2753
  // #383 Bug 2: write agentchattr_url inside [telegram]; the
2747
2754
  // bridge's load_config only reads from that section.
2748
- const tomlContent = buildTelegramBridgeToml(tg);
2755
+ const tomlContent = buildTelegramBridgeToml(tg, projectId);
2749
2756
  fs.writeFileSync(tomlPath, tomlContent, { mode: 0o600 });
2750
2757
  fs.chmodSync(tomlPath, 0o600);
2751
2758
  // #353: pre-flight import check so a fresh install with no
@@ -2837,6 +2844,21 @@ router.post("/api/telegram", async (req, res) => {
2837
2844
  case "stop": {
2838
2845
  const projectId = body.project_id;
2839
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 {}
2840
2862
  try {
2841
2863
  const pf = telegramPidFile(projectId);
2842
2864
  if (fs.existsSync(pf)) {
@@ -176,11 +176,14 @@ try {
176
176
  bot_token: "123:abc",
177
177
  chat_id: "-42",
178
178
  agentchattr_url: "http://127.0.0.1:8301",
179
- });
179
+ }, "testproject");
180
180
  assert.match(toml13, /^\[telegram\]/);
181
181
  assert.match(toml13, /bot_token = "123:abc"/);
182
182
  assert.match(toml13, /chat_id = "-42"/);
183
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"/);
184
187
  // Must NOT emit a separate [agentchattr] section — the bridge
185
188
  // would silently ignore it.
186
189
  assert.equal(toml13.includes("\n[agentchattr]\n"), false);
@@ -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.
@@ -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.