quadwork 0.1.2 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/README.md +4 -4
  2. package/bin/quadwork.js +153 -52
  3. package/out/404.html +1 -1
  4. package/out/__next.__PAGE__.txt +1 -1
  5. package/out/__next._full.txt +2 -2
  6. package/out/__next._head.txt +1 -1
  7. package/out/__next._index.txt +2 -2
  8. package/out/__next._tree.txt +2 -2
  9. package/out/_next/static/chunks/02ul7y114vj2f.js +13 -0
  10. package/out/_next/static/chunks/{0jsosmtclw5n5.js → 038g944ax83al.js} +1 -1
  11. package/out/_next/static/chunks/0gy_9ugdx7ueh.js +1 -0
  12. package/out/_next/static/chunks/0idtc5k0469of.js +1 -0
  13. package/out/_next/static/chunks/{03hi.hdp6l230.js → 0wda-2lcle8c4.js} +8 -8
  14. package/out/_next/static/chunks/0yxmvmvm1dx_d.css +2 -0
  15. package/out/_not-found/__next._full.txt +2 -2
  16. package/out/_not-found/__next._head.txt +1 -1
  17. package/out/_not-found/__next._index.txt +2 -2
  18. package/out/_not-found/__next._not-found.__PAGE__.txt +1 -1
  19. package/out/_not-found/__next._not-found.txt +1 -1
  20. package/out/_not-found/__next._tree.txt +2 -2
  21. package/out/_not-found.html +1 -1
  22. package/out/_not-found.txt +2 -2
  23. package/out/index.html +1 -1
  24. package/out/index.txt +2 -2
  25. package/out/project/_/__next._full.txt +3 -3
  26. package/out/project/_/__next._head.txt +1 -1
  27. package/out/project/_/__next._index.txt +2 -2
  28. package/out/project/_/__next._tree.txt +2 -2
  29. package/out/project/_/__next.project.$d$id.__PAGE__.txt +2 -2
  30. package/out/project/_/__next.project.$d$id.txt +1 -1
  31. package/out/project/_/__next.project.txt +1 -1
  32. package/out/project/_/memory/__next._full.txt +3 -3
  33. package/out/project/_/memory/__next._head.txt +1 -1
  34. package/out/project/_/memory/__next._index.txt +2 -2
  35. package/out/project/_/memory/__next._tree.txt +2 -2
  36. package/out/project/_/memory/__next.project.$d$id.memory.__PAGE__.txt +2 -2
  37. package/out/project/_/memory/__next.project.$d$id.memory.txt +1 -1
  38. package/out/project/_/memory/__next.project.$d$id.txt +1 -1
  39. package/out/project/_/memory/__next.project.txt +1 -1
  40. package/out/project/_/memory.html +1 -1
  41. package/out/project/_/memory.txt +3 -3
  42. package/out/project/_/queue/__next._full.txt +3 -3
  43. package/out/project/_/queue/__next._head.txt +1 -1
  44. package/out/project/_/queue/__next._index.txt +2 -2
  45. package/out/project/_/queue/__next._tree.txt +2 -2
  46. package/out/project/_/queue/__next.project.$d$id.queue.__PAGE__.txt +2 -2
  47. package/out/project/_/queue/__next.project.$d$id.queue.txt +1 -1
  48. package/out/project/_/queue/__next.project.$d$id.txt +1 -1
  49. package/out/project/_/queue/__next.project.txt +1 -1
  50. package/out/project/_/queue.html +1 -1
  51. package/out/project/_/queue.txt +3 -3
  52. package/out/project/_.html +1 -1
  53. package/out/project/_.txt +3 -3
  54. package/out/settings/__next._full.txt +3 -3
  55. package/out/settings/__next._head.txt +1 -1
  56. package/out/settings/__next._index.txt +2 -2
  57. package/out/settings/__next._tree.txt +2 -2
  58. package/out/settings/__next.settings.__PAGE__.txt +2 -2
  59. package/out/settings/__next.settings.txt +1 -1
  60. package/out/settings.html +1 -1
  61. package/out/settings.txt +3 -3
  62. package/out/setup/__next._full.txt +3 -3
  63. package/out/setup/__next._head.txt +1 -1
  64. package/out/setup/__next._index.txt +2 -2
  65. package/out/setup/__next._tree.txt +2 -2
  66. package/out/setup/__next.setup.__PAGE__.txt +2 -2
  67. package/out/setup/__next.setup.txt +1 -1
  68. package/out/setup.html +1 -1
  69. package/out/setup.txt +3 -3
  70. package/package.json +1 -1
  71. package/server/config.js +42 -2
  72. package/server/index.js +103 -55
  73. package/server/routes.js +104 -66
  74. package/templates/CLAUDE.md +16 -17
  75. package/templates/config.toml +12 -12
  76. package/templates/seeds/{t3.AGENTS.md → dev.AGENTS.md} +19 -19
  77. package/templates/seeds/{t1.AGENTS.md → head.AGENTS.md} +18 -18
  78. package/templates/seeds/{t2a.AGENTS.md → reviewer1.AGENTS.md} +16 -16
  79. package/templates/seeds/{t2b.AGENTS.md → reviewer2.AGENTS.md} +16 -16
  80. package/out/_next/static/chunks/03yov._jigv17.js +0 -1
  81. package/out/_next/static/chunks/0iqqouh_3i5y5.js +0 -13
  82. package/out/_next/static/chunks/15kwal..m9r49.css +0 -2
  83. package/out/_next/static/chunks/17sk4qv6_d0co.js +0 -1
  84. /package/out/_next/static/{vELqtMegFMn5_6zFOkhtG → 91YUiFoMbLQ9sZW4uk45J}/_buildManifest.js +0 -0
  85. /package/out/_next/static/{vELqtMegFMn5_6zFOkhtG → 91YUiFoMbLQ9sZW4uk45J}/_clientMiddlewareManifest.js +0 -0
  86. /package/out/_next/static/{vELqtMegFMn5_6zFOkhtG → 91YUiFoMbLQ9sZW4uk45J}/_ssgManifest.js +0 -0
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/15kwal..m9r49.css" data-precedence="next"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/13uu.sohs74zg.js"/><script src="/_next/static/chunks/0excsn2a_5qsb.js" async=""></script><script src="/_next/static/chunks/0r7t_sj_sejq9.js" async=""></script><script src="/_next/static/chunks/0.dzh0qf9zq1l.js" async=""></script><script src="/_next/static/chunks/turbopack-06pqx~0d8czn_.js" async=""></script><script src="/_next/static/chunks/08fgie1bcjynm.js" async=""></script><script src="/_next/static/chunks/0ox7p_szjhn69.js" async=""></script><script src="/_next/static/chunks/03yov._jigv17.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"><div hidden=""><!--$--><!--/$--></div><aside class="w-16 shrink-0 h-full border-r border-border bg-bg-surface flex flex-col items-center py-3"><a class="w-10 h-10 flex items-center justify-center rounded-sm transition-colors text-text-muted hover:text-text hover:bg-[#1a1a1a]" title="Home" href="/"><svg width="20" height="20" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M3 10L10 3l7 7"></path><path d="M5 8.5V16h3.5v-4h3v4H15V8.5"></path></svg></a><div class="w-6 h-px bg-border my-2"></div><div class="flex-1 flex flex-col items-center gap-2 overflow-y-auto min-h-0"><a class="w-10 h-10 flex items-center justify-center rounded-full border border-dashed border-border text-text-muted hover:text-text hover:bg-[#1a1a1a] transition-colors" title="Add project" href="/setup"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><path d="M8 3v10M3 8h10"></path></svg></a></div><div class="w-6 h-px bg-border my-2"></div><a class="w-10 h-10 flex items-center justify-center rounded-sm transition-colors text-text-muted hover:text-text hover:bg-[#1a1a1a]" title="Settings" href="/settings"><svg width="18" height="18" viewBox="0 0 18 18" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="9" cy="9" r="2.5"></circle><path d="M7.5 1.5h3l.4 2.1a5.5 5.5 0 011.3.7l2-.8 1.5 2.6-1.6 1.3a5.5 5.5 0 010 1.5l1.6 1.3-1.5 2.6-2-.8a5.5 5.5 0 01-1.3.7l-.4 2.1h-3l-.4-2.1a5.5 5.5 0 01-1.3-.7l-2 .8-1.5-2.6 1.6-1.3a5.5 5.5 0 010-1.5L2.3 6.1l1.5-2.6 2 .8a5.5 5.5 0 011.3-.7z"></path></svg></a></aside><main class="flex-1 min-w-0 overflow-auto"><div class="h-full overflow-y-auto p-6 max-w-4xl"><h1 class="text-lg font-semibold text-text tracking-tight mb-6">New Project Setup</h1><div class="flex gap-8"><div class="w-48 shrink-0"><div class="flex items-center gap-2 py-1.5"><span class="w-5 h-5 flex items-center justify-center text-[10px] border border-accent text-accent bg-accent/10">1</span><span class="text-[11px] text-text font-semibold">Project Name</span></div><div class="flex items-center gap-2 py-1.5"><span class="w-5 h-5 flex items-center justify-center text-[10px] border border-border text-text-muted">2</span><span class="text-[11px] text-text-muted">GitHub Repo</span></div><div class="flex items-center gap-2 py-1.5"><span class="w-5 h-5 flex items-center justify-center text-[10px] border border-border text-text-muted">3</span><span class="text-[11px] text-text-muted">Branch Protection</span></div><div class="flex items-center gap-2 py-1.5"><span class="w-5 h-5 flex items-center justify-center text-[10px] border border-border text-text-muted">4</span><span class="text-[11px] text-text-muted">CLI Backend</span></div><div class="flex items-center gap-2 py-1.5"><span class="w-5 h-5 flex items-center justify-center text-[10px] border border-border text-text-muted">5</span><span class="text-[11px] text-text-muted">Working Directory</span></div><div class="flex items-center gap-2 py-1.5"><span class="w-5 h-5 flex items-center justify-center text-[10px] border border-border text-text-muted">6</span><span class="text-[11px] text-text-muted">Worktree Setup</span></div><div class="flex items-center gap-2 py-1.5"><span class="w-5 h-5 flex items-center justify-center text-[10px] border border-border text-text-muted">7</span><span class="text-[11px] text-text-muted">Seed Files</span></div><div class="flex items-center gap-2 py-1.5"><span class="w-5 h-5 flex items-center justify-center text-[10px] border border-border text-text-muted">8</span><span class="text-[11px] text-text-muted">AgentChattr Config</span></div><div class="flex items-center gap-2 py-1.5"><span class="w-5 h-5 flex items-center justify-center text-[10px] border border-border text-text-muted">9</span><span class="text-[11px] text-text-muted">Save Config</span></div></div><div class="flex-1 border border-border p-4"><div><h2 class="text-sm font-semibold text-text mb-3">Project Name</h2><input placeholder="My Project" class="w-full bg-transparent border border-border px-2 py-1.5 text-[12px] text-text outline-none focus:border-accent mb-3" 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><!--$--><!--/$--></main><script src="/_next/static/chunks/13uu.sohs74zg.js" id="_R_" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0])</script><script>self.__next_f.push([1,"1:\"$Sreact.fragment\"\n2:I[86081,[\"/_next/static/chunks/08fgie1bcjynm.js\",\"/_next/static/chunks/0ox7p_szjhn69.js\"],\"default\"]\n3:I[12527,[\"/_next/static/chunks/08fgie1bcjynm.js\",\"/_next/static/chunks/0ox7p_szjhn69.js\"],\"default\"]\n4:I[59763,[\"/_next/static/chunks/08fgie1bcjynm.js\",\"/_next/static/chunks/0ox7p_szjhn69.js\"],\"default\"]\n5:I[64618,[\"/_next/static/chunks/08fgie1bcjynm.js\",\"/_next/static/chunks/0ox7p_szjhn69.js\",\"/_next/static/chunks/03yov._jigv17.js\"],\"default\"]\n6:I[11717,[\"/_next/static/chunks/08fgie1bcjynm.js\",\"/_next/static/chunks/0ox7p_szjhn69.js\"],\"OutletBoundary\"]\n7:\"$Sreact.suspense\"\na:I[11717,[\"/_next/static/chunks/08fgie1bcjynm.js\",\"/_next/static/chunks/0ox7p_szjhn69.js\"],\"ViewportBoundary\"]\nc:I[11717,[\"/_next/static/chunks/08fgie1bcjynm.js\",\"/_next/static/chunks/0ox7p_szjhn69.js\"],\"MetadataBoundary\"]\ne:I[92243,[\"/_next/static/chunks/08fgie1bcjynm.js\",\"/_next/static/chunks/0ox7p_szjhn69.js\"],\"default\",1]\n:HL[\"/_next/static/chunks/15kwal..m9r49.css\",\"style\"]\n: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/15kwal..m9r49.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-0\",{\"src\":\"/_next/static/chunks/08fgie1bcjynm.js\",\"async\":true,\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-1\",{\"src\":\"/_next/static/chunks/0ox7p_szjhn69.js\",\"async\":true,\"nonce\":\"$undefined\"}]],[\"$\",\"html\",null,{\"lang\":\"en\",\"className\":\"geist_mono_8d43a2aa-module__8Li5zG__variable h-full\",\"children\":[\"$\",\"body\",null,{\"className\":\"h-full flex\",\"children\":[[\"$\",\"$L2\",null,{}],[\"$\",\"main\",null,{\"className\":\"flex-1 min-w-0 overflow-auto\",\"children\":[\"$\",\"$L3\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L4\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":[[[\"$\",\"title\",null,{\"children\":\"404: This page could not be found.\"}],[\"$\",\"div\",null,{\"style\":{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"textAlign\":\"center\",\"display\":\"flex\",\"flexDirection\":\"column\",\"alignItems\":\"center\",\"justifyContent\":\"center\"},\"children\":[\"$\",\"div\",null,{\"children\":[[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}\"}}],[\"$\",\"h1\",null,{\"className\":\"next-error-h1\",\"style\":{\"display\":\"inline-block\",\"margin\":\"0 20px 0 0\",\"padding\":\"0 23px 0 0\",\"fontSize\":24,\"fontWeight\":500,\"verticalAlign\":\"top\",\"lineHeight\":\"49px\"},\"children\":404}],[\"$\",\"div\",null,{\"style\":{\"display\":\"inline-block\"},\"children\":[\"$\",\"h2\",null,{\"style\":{\"fontSize\":14,\"fontWeight\":400,\"lineHeight\":\"49px\",\"margin\":0},\"children\":\"This page could not be found.\"}]}]]}]}]],[]],\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]}]]}]}]]}],{\"children\":[[\"$\",\"$1\",\"c\",{\"children\":[null,[\"$\",\"$L3\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L4\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}],{\"children\":[[\"$\",\"$1\",\"c\",{\"children\":[[\"$\",\"$L5\",null,{}],[[\"$\",\"script\",\"script-0\",{\"src\":\"/_next/static/chunks/03yov._jigv17.js\",\"async\":true,\"nonce\":\"$undefined\"}]],[\"$\",\"$L6\",null,{\"children\":[\"$\",\"$7\",null,{\"name\":\"Next.MetadataOutlet\",\"children\":\"$@8\"}]}]]}],{},null,false,null]},null,false,\"$@9\"]},null,false,null],[\"$\",\"$1\",\"h\",{\"children\":[null,[\"$\",\"$La\",null,{\"children\":\"$Lb\"}],[\"$\",\"div\",null,{\"hidden\":true,\"children\":[\"$\",\"$Lc\",null,{\"children\":[\"$\",\"$7\",null,{\"name\":\"Next.Metadata\",\"children\":\"$Ld\"}]}]}],[\"$\",\"meta\",null,{\"name\":\"next-size-adjust\",\"content\":\"\"}]]}],false]],\"m\":\"$undefined\",\"G\":[\"$e\",[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/chunks/15kwal..m9r49.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}]]],\"S\":true,\"h\":null,\"s\":\"$undefined\",\"l\":\"$undefined\",\"p\":\"$undefined\",\"d\":\"$undefined\",\"b\":\"vELqtMegFMn5_6zFOkhtG\"}\n"])</script><script>self.__next_f.push([1,"f:[]\n9:\"$Wf\"\n"])</script><script>self.__next_f.push([1,"b:[[\"$\",\"meta\",\"0\",{\"charSet\":\"utf-8\"}],[\"$\",\"meta\",\"1\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}]]\n"])</script><script>self.__next_f.push([1,"10:I[80070,[\"/_next/static/chunks/08fgie1bcjynm.js\",\"/_next/static/chunks/0ox7p_szjhn69.js\"],\"IconMark\"]\n8:null\nd:[[\"$\",\"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\"}],[\"$\",\"$L10\",\"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/0yxmvmvm1dx_d.css" data-precedence="next"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/13uu.sohs74zg.js"/><script src="/_next/static/chunks/0excsn2a_5qsb.js" async=""></script><script src="/_next/static/chunks/0r7t_sj_sejq9.js" async=""></script><script src="/_next/static/chunks/0.dzh0qf9zq1l.js" async=""></script><script src="/_next/static/chunks/turbopack-06pqx~0d8czn_.js" async=""></script><script src="/_next/static/chunks/08fgie1bcjynm.js" async=""></script><script src="/_next/static/chunks/0ox7p_szjhn69.js" async=""></script><script src="/_next/static/chunks/0gy_9ugdx7ueh.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"><div hidden=""><!--$--><!--/$--></div><aside class="w-16 shrink-0 h-full border-r border-border bg-bg-surface flex flex-col items-center py-3"><a class="w-10 h-10 flex items-center justify-center rounded-sm transition-colors text-text-muted hover:text-text hover:bg-[#1a1a1a]" title="Home" href="/"><svg width="20" height="20" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M3 10L10 3l7 7"></path><path d="M5 8.5V16h3.5v-4h3v4H15V8.5"></path></svg></a><div class="w-6 h-px bg-border my-2"></div><div class="flex-1 flex flex-col items-center gap-2 overflow-y-auto min-h-0"><a class="w-10 h-10 flex items-center justify-center rounded-full border border-dashed border-border text-text-muted hover:text-text hover:bg-[#1a1a1a] transition-colors" title="Add project" href="/setup"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><path d="M8 3v10M3 8h10"></path></svg></a></div><div class="w-6 h-px bg-border my-2"></div><a class="w-10 h-10 flex items-center justify-center rounded-sm transition-colors text-text-muted hover:text-text hover:bg-[#1a1a1a]" title="Settings" href="/settings"><svg width="18" height="18" viewBox="0 0 18 18" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="9" cy="9" r="2.5"></circle><path d="M7.5 1.5h3l.4 2.1a5.5 5.5 0 011.3.7l2-.8 1.5 2.6-1.6 1.3a5.5 5.5 0 010 1.5l1.6 1.3-1.5 2.6-2-.8a5.5 5.5 0 01-1.3.7l-.4 2.1h-3l-.4-2.1a5.5 5.5 0 01-1.3-.7l-2 .8-1.5-2.6 1.6-1.3a5.5 5.5 0 010-1.5L2.3 6.1l1.5-2.6 2 .8a5.5 5.5 0 011.3-.7z"></path></svg></a></aside><main class="flex-1 min-w-0 overflow-auto"><div class="h-full overflow-y-auto p-6 max-w-4xl"><h1 class="text-lg font-semibold text-text tracking-tight mb-6">New Project Setup</h1><div class="flex gap-8"><div class="w-48 shrink-0"><div class="flex items-center gap-2 py-1.5"><span class="w-5 h-5 flex items-center justify-center text-[10px] border border-accent text-accent bg-accent/10">1</span><span class="text-[11px] text-text font-semibold">Project Name</span></div><div class="flex items-center gap-2 py-1.5"><span class="w-5 h-5 flex items-center justify-center text-[10px] border border-border text-text-muted">2</span><span class="text-[11px] text-text-muted">GitHub Repo</span></div><div class="flex items-center gap-2 py-1.5"><span class="w-5 h-5 flex items-center justify-center text-[10px] border border-border text-text-muted">3</span><span class="text-[11px] text-text-muted">Branch Protection</span></div><div class="flex items-center gap-2 py-1.5"><span class="w-5 h-5 flex items-center justify-center text-[10px] border border-border text-text-muted">4</span><span class="text-[11px] text-text-muted">CLI Backend</span></div><div class="flex items-center gap-2 py-1.5"><span class="w-5 h-5 flex items-center justify-center text-[10px] border border-border text-text-muted">5</span><span class="text-[11px] text-text-muted">Working Directory</span></div><div class="flex items-center gap-2 py-1.5"><span class="w-5 h-5 flex items-center justify-center text-[10px] border border-border text-text-muted">6</span><span class="text-[11px] text-text-muted">Worktree Setup</span></div><div class="flex items-center gap-2 py-1.5"><span class="w-5 h-5 flex items-center justify-center text-[10px] border border-border text-text-muted">7</span><span class="text-[11px] text-text-muted">Seed Files</span></div><div class="flex items-center gap-2 py-1.5"><span class="w-5 h-5 flex items-center justify-center text-[10px] border border-border text-text-muted">8</span><span class="text-[11px] text-text-muted">AgentChattr Config</span></div><div class="flex items-center gap-2 py-1.5"><span class="w-5 h-5 flex items-center justify-center text-[10px] border border-border text-text-muted">9</span><span class="text-[11px] text-text-muted">Save Config</span></div></div><div class="flex-1 border border-border p-4"><div><h2 class="text-sm font-semibold text-text mb-3">Project Name</h2><input placeholder="My Project" class="w-full bg-transparent border border-border px-2 py-1.5 text-[12px] text-text outline-none focus:border-accent mb-3" 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><!--$--><!--/$--></main><script src="/_next/static/chunks/13uu.sohs74zg.js" id="_R_" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0])</script><script>self.__next_f.push([1,"1:\"$Sreact.fragment\"\n2:I[86081,[\"/_next/static/chunks/08fgie1bcjynm.js\",\"/_next/static/chunks/0ox7p_szjhn69.js\"],\"default\"]\n3:I[12527,[\"/_next/static/chunks/08fgie1bcjynm.js\",\"/_next/static/chunks/0ox7p_szjhn69.js\"],\"default\"]\n4:I[59763,[\"/_next/static/chunks/08fgie1bcjynm.js\",\"/_next/static/chunks/0ox7p_szjhn69.js\"],\"default\"]\n5:I[64618,[\"/_next/static/chunks/08fgie1bcjynm.js\",\"/_next/static/chunks/0ox7p_szjhn69.js\",\"/_next/static/chunks/0gy_9ugdx7ueh.js\"],\"default\"]\n6:I[11717,[\"/_next/static/chunks/08fgie1bcjynm.js\",\"/_next/static/chunks/0ox7p_szjhn69.js\"],\"OutletBoundary\"]\n7:\"$Sreact.suspense\"\na:I[11717,[\"/_next/static/chunks/08fgie1bcjynm.js\",\"/_next/static/chunks/0ox7p_szjhn69.js\"],\"ViewportBoundary\"]\nc:I[11717,[\"/_next/static/chunks/08fgie1bcjynm.js\",\"/_next/static/chunks/0ox7p_szjhn69.js\"],\"MetadataBoundary\"]\ne:I[92243,[\"/_next/static/chunks/08fgie1bcjynm.js\",\"/_next/static/chunks/0ox7p_szjhn69.js\"],\"default\",1]\n:HL[\"/_next/static/chunks/0yxmvmvm1dx_d.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/0yxmvmvm1dx_d.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-0\",{\"src\":\"/_next/static/chunks/08fgie1bcjynm.js\",\"async\":true,\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-1\",{\"src\":\"/_next/static/chunks/0ox7p_szjhn69.js\",\"async\":true,\"nonce\":\"$undefined\"}]],[\"$\",\"html\",null,{\"lang\":\"en\",\"className\":\"geist_mono_8d43a2aa-module__8Li5zG__variable h-full\",\"children\":[\"$\",\"body\",null,{\"className\":\"h-full flex\",\"children\":[[\"$\",\"$L2\",null,{}],[\"$\",\"main\",null,{\"className\":\"flex-1 min-w-0 overflow-auto\",\"children\":[\"$\",\"$L3\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L4\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":[[[\"$\",\"title\",null,{\"children\":\"404: This page could not be found.\"}],[\"$\",\"div\",null,{\"style\":{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"textAlign\":\"center\",\"display\":\"flex\",\"flexDirection\":\"column\",\"alignItems\":\"center\",\"justifyContent\":\"center\"},\"children\":[\"$\",\"div\",null,{\"children\":[[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}\"}}],[\"$\",\"h1\",null,{\"className\":\"next-error-h1\",\"style\":{\"display\":\"inline-block\",\"margin\":\"0 20px 0 0\",\"padding\":\"0 23px 0 0\",\"fontSize\":24,\"fontWeight\":500,\"verticalAlign\":\"top\",\"lineHeight\":\"49px\"},\"children\":404}],[\"$\",\"div\",null,{\"style\":{\"display\":\"inline-block\"},\"children\":[\"$\",\"h2\",null,{\"style\":{\"fontSize\":14,\"fontWeight\":400,\"lineHeight\":\"49px\",\"margin\":0},\"children\":\"This page could not be found.\"}]}]]}]}]],[]],\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]}]]}]}]]}],{\"children\":[[\"$\",\"$1\",\"c\",{\"children\":[null,[\"$\",\"$L3\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L4\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}],{\"children\":[[\"$\",\"$1\",\"c\",{\"children\":[[\"$\",\"$L5\",null,{}],[[\"$\",\"script\",\"script-0\",{\"src\":\"/_next/static/chunks/0gy_9ugdx7ueh.js\",\"async\":true,\"nonce\":\"$undefined\"}]],[\"$\",\"$L6\",null,{\"children\":[\"$\",\"$7\",null,{\"name\":\"Next.MetadataOutlet\",\"children\":\"$@8\"}]}]]}],{},null,false,null]},null,false,\"$@9\"]},null,false,null],[\"$\",\"$1\",\"h\",{\"children\":[null,[\"$\",\"$La\",null,{\"children\":\"$Lb\"}],[\"$\",\"div\",null,{\"hidden\":true,\"children\":[\"$\",\"$Lc\",null,{\"children\":[\"$\",\"$7\",null,{\"name\":\"Next.Metadata\",\"children\":\"$Ld\"}]}]}],[\"$\",\"meta\",null,{\"name\":\"next-size-adjust\",\"content\":\"\"}]]}],false]],\"m\":\"$undefined\",\"G\":[\"$e\",[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/chunks/0yxmvmvm1dx_d.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}]]],\"S\":true,\"h\":null,\"s\":\"$undefined\",\"l\":\"$undefined\",\"p\":\"$undefined\",\"d\":\"$undefined\",\"b\":\"91YUiFoMbLQ9sZW4uk45J\"}\n"])</script><script>self.__next_f.push([1,"f:[]\n9:\"$Wf\"\n"])</script><script>self.__next_f.push([1,"b:[[\"$\",\"meta\",\"0\",{\"charSet\":\"utf-8\"}],[\"$\",\"meta\",\"1\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}]]\n"])</script><script>self.__next_f.push([1,"10:I[80070,[\"/_next/static/chunks/08fgie1bcjynm.js\",\"/_next/static/chunks/0ox7p_szjhn69.js\"],\"IconMark\"]\n8:null\nd:[[\"$\",\"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\"}],[\"$\",\"$L10\",\"3\",{}]]\n"])</script></body></html>
package/out/setup.txt CHANGED
@@ -2,15 +2,15 @@
2
2
  2:I[86081,["/_next/static/chunks/08fgie1bcjynm.js","/_next/static/chunks/0ox7p_szjhn69.js"],"default"]
3
3
  3:I[12527,["/_next/static/chunks/08fgie1bcjynm.js","/_next/static/chunks/0ox7p_szjhn69.js"],"default"]
4
4
  4:I[59763,["/_next/static/chunks/08fgie1bcjynm.js","/_next/static/chunks/0ox7p_szjhn69.js"],"default"]
5
- 5:I[64618,["/_next/static/chunks/08fgie1bcjynm.js","/_next/static/chunks/0ox7p_szjhn69.js","/_next/static/chunks/03yov._jigv17.js"],"default"]
5
+ 5:I[64618,["/_next/static/chunks/08fgie1bcjynm.js","/_next/static/chunks/0ox7p_szjhn69.js","/_next/static/chunks/0gy_9ugdx7ueh.js"],"default"]
6
6
  6:I[11717,["/_next/static/chunks/08fgie1bcjynm.js","/_next/static/chunks/0ox7p_szjhn69.js"],"OutletBoundary"]
7
7
  7:"$Sreact.suspense"
8
8
  a:I[11717,["/_next/static/chunks/08fgie1bcjynm.js","/_next/static/chunks/0ox7p_szjhn69.js"],"ViewportBoundary"]
9
9
  c:I[11717,["/_next/static/chunks/08fgie1bcjynm.js","/_next/static/chunks/0ox7p_szjhn69.js"],"MetadataBoundary"]
10
10
  e:I[92243,["/_next/static/chunks/08fgie1bcjynm.js","/_next/static/chunks/0ox7p_szjhn69.js"],"default",1]
11
- :HL["/_next/static/chunks/15kwal..m9r49.css","style"]
11
+ :HL["/_next/static/chunks/0yxmvmvm1dx_d.css","style"]
12
12
  :HL["/_next/static/media/797e433ab948586e-s.p.0.q-h669a_dqa.woff2","font",{"crossOrigin":"","type":"font/woff2"}]
13
- 0:{"P":null,"c":["","setup"],"q":"","i":false,"f":[[["",{"children":["setup",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/chunks/15kwal..m9r49.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/_next/static/chunks/08fgie1bcjynm.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/_next/static/chunks/0ox7p_szjhn69.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","className":"geist_mono_8d43a2aa-module__8Li5zG__variable h-full","children":["$","body",null,{"className":"h-full flex","children":[["$","$L2",null,{}],["$","main",null,{"className":"flex-1 min-w-0 overflow-auto","children":["$","$L3",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L4",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]]}]}]]}],{"children":[["$","$1","c",{"children":[null,["$","$L3",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L4",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}],{"children":[["$","$1","c",{"children":[["$","$L5",null,{}],[["$","script","script-0",{"src":"/_next/static/chunks/03yov._jigv17.js","async":true,"nonce":"$undefined"}]],["$","$L6",null,{"children":["$","$7",null,{"name":"Next.MetadataOutlet","children":"$@8"}]}]]}],{},null,false,null]},null,false,"$@9"]},null,false,null],["$","$1","h",{"children":[null,["$","$La",null,{"children":"$Lb"}],["$","div",null,{"hidden":true,"children":["$","$Lc",null,{"children":["$","$7",null,{"name":"Next.Metadata","children":"$Ld"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],false]],"m":"$undefined","G":["$e",[["$","link","0",{"rel":"stylesheet","href":"/_next/static/chunks/15kwal..m9r49.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"vELqtMegFMn5_6zFOkhtG"}
13
+ 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/0yxmvmvm1dx_d.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/_next/static/chunks/08fgie1bcjynm.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/_next/static/chunks/0ox7p_szjhn69.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","className":"geist_mono_8d43a2aa-module__8Li5zG__variable h-full","children":["$","body",null,{"className":"h-full flex","children":[["$","$L2",null,{}],["$","main",null,{"className":"flex-1 min-w-0 overflow-auto","children":["$","$L3",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L4",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]]}]}]]}],{"children":[["$","$1","c",{"children":[null,["$","$L3",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L4",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}],{"children":[["$","$1","c",{"children":[["$","$L5",null,{}],[["$","script","script-0",{"src":"/_next/static/chunks/0gy_9ugdx7ueh.js","async":true,"nonce":"$undefined"}]],["$","$L6",null,{"children":["$","$7",null,{"name":"Next.MetadataOutlet","children":"$@8"}]}]]}],{},null,false,null]},null,false,"$@9"]},null,false,null],["$","$1","h",{"children":[null,["$","$La",null,{"children":"$Lb"}],["$","div",null,{"hidden":true,"children":["$","$Lc",null,{"children":["$","$7",null,{"name":"Next.Metadata","children":"$Ld"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],false]],"m":"$undefined","G":["$e",[["$","link","0",{"rel":"stylesheet","href":"/_next/static/chunks/0yxmvmvm1dx_d.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"91YUiFoMbLQ9sZW4uk45J"}
14
14
  f:[]
15
15
  9:"$Wf"
16
16
  b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "quadwork",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
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/config.js CHANGED
@@ -10,6 +10,31 @@ const DEFAULT_CONFIG = {
10
10
  projects: [],
11
11
  };
12
12
 
13
+ // Migration: rename old agent keys to new ones
14
+ const AGENT_KEY_MAP = { t1: "head", t2a: "reviewer1", t2b: "reviewer2", t3: "dev" };
15
+
16
+ function migrateAgentKeys(config) {
17
+ let changed = false;
18
+ if (config.projects) {
19
+ for (const project of config.projects) {
20
+ if (!project.agents) continue;
21
+ for (const [oldKey, newKey] of Object.entries(AGENT_KEY_MAP)) {
22
+ if (project.agents[oldKey] && !project.agents[newKey]) {
23
+ project.agents[newKey] = project.agents[oldKey];
24
+ delete project.agents[oldKey];
25
+ changed = true;
26
+ }
27
+ }
28
+ }
29
+ }
30
+ if (changed) {
31
+ try {
32
+ fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2));
33
+ } catch {}
34
+ }
35
+ return config;
36
+ }
37
+
13
38
  function readConfig() {
14
39
  let raw;
15
40
  try {
@@ -28,7 +53,8 @@ function readConfig() {
28
53
  }
29
54
 
30
55
  try {
31
- return JSON.parse(raw);
56
+ const config = JSON.parse(raw);
57
+ return migrateAgentKeys(config);
32
58
  } catch (err) {
33
59
  throw new Error(`Invalid JSON in ${CONFIG_PATH}: ${err.message}`);
34
60
  }
@@ -60,4 +86,18 @@ function resolveAgentCommand(projectId, agentId) {
60
86
  return agent.command;
61
87
  }
62
88
 
63
- module.exports = { readConfig, resolveAgentCwd, resolveAgentCommand, CONFIG_PATH };
89
+ /**
90
+ * Resolve AgentChattr connection for a project (per-project → global fallback).
91
+ */
92
+ function resolveProjectChattr(projectId) {
93
+ const config = readConfig();
94
+ const project = projectId ? config.projects?.find((p) => p.id === projectId) : null;
95
+ return {
96
+ url: project?.agentchattr_url || config.agentchattr_url || "http://127.0.0.1:8300",
97
+ token: project?.agentchattr_token || config.agentchattr_token || null,
98
+ mcp_http_port: project?.mcp_http_port || null,
99
+ mcp_sse_port: project?.mcp_sse_port || null,
100
+ };
101
+ }
102
+
103
+ module.exports = { readConfig, resolveAgentCwd, resolveAgentCommand, resolveProjectChattr, CONFIG_PATH };
package/server/index.js CHANGED
@@ -5,7 +5,7 @@ const fs = require("fs");
5
5
  const { WebSocketServer } = require("ws");
6
6
  const pty = require("node-pty");
7
7
  const { spawn } = require("child_process");
8
- const { readConfig, resolveAgentCwd, resolveAgentCommand } = require("./config");
8
+ const { readConfig, resolveAgentCwd, resolveAgentCommand, resolveProjectChattr } = require("./config");
9
9
  const routes = require("./routes");
10
10
 
11
11
  const config = readConfig();
@@ -30,8 +30,8 @@ app.get("/api/health", (_req, res) => {
30
30
  // PTY (term) is the source of truth for "running". WS is optional (attaches to view terminal).
31
31
  const agentSessions = new Map();
32
32
 
33
- // AgentChattr server process (separate not a PTY agent)
34
- let chattrProcess = { process: null, state: "stopped", error: null };
33
+ // AgentChattr server processesper-project (key = projectId)
34
+ const chattrProcesses = new Map();
35
35
 
36
36
  // Helper: spawn a PTY for a project/agent and register in agentSessions
37
37
  function spawnAgentPty(project, agent) {
@@ -99,79 +99,107 @@ app.get("/api/agents", (_req, res) => {
99
99
  for (const [key, session] of agentSessions) {
100
100
  agents[key] = { state: session.state, error: session.error || null };
101
101
  }
102
- agents["_agentchattr"] = { state: chattrProcess.state, error: chattrProcess.error };
102
+ for (const [pid, proc] of chattrProcesses) {
103
+ agents[`_agentchattr/${pid}`] = { state: proc.state, error: proc.error };
104
+ }
103
105
  res.json(agents);
104
106
  });
105
107
 
106
- app.post("/api/agentchattr/:action", (req, res) => {
107
- const { action } = req.params;
108
- const cfg = readConfig();
109
- const chattrUrl = cfg.agentchattr_url || "http://127.0.0.1:8300";
108
+ // Per-project AgentChattr lifecycle: /api/agentchattr/:project/:action
109
+ // Backward compat: /api/agentchattr/:action uses first project
110
+ function handleAgentChattr(req, res) {
111
+ let projectId, action;
112
+ if (req.params.action) {
113
+ projectId = req.params.projectOrAction;
114
+ action = req.params.action;
115
+ } else {
116
+ // Backward compat: single-param = action, use first project
117
+ action = req.params.projectOrAction;
118
+ const cfg = readConfig();
119
+ projectId = cfg.projects?.[0]?.id || "_default";
120
+ }
121
+
122
+ const { url: chattrUrl } = resolveProjectChattr(projectId);
110
123
  const chattrPort = new URL(chattrUrl).port || "8300";
111
124
 
125
+ // Find per-project config.toml (prefer project working_dir/agentchattr/config.toml)
126
+ const cfg = readConfig();
127
+ const project = cfg.projects?.find((p) => p.id === projectId);
128
+ const projectConfigToml = project?.working_dir
129
+ ? path.join(project.working_dir, "agentchattr", "config.toml")
130
+ : null;
131
+
132
+ function getProc() {
133
+ return chattrProcesses.get(projectId) || { process: null, state: "stopped", error: null };
134
+ }
135
+ function setProc(val) {
136
+ chattrProcesses.set(projectId, val);
137
+ }
138
+
139
+ function spawnChattr() {
140
+ // Use project config.toml if available (isolated data dir + ports), otherwise fall back to --port
141
+ const args = (projectConfigToml && fs.existsSync(projectConfigToml))
142
+ ? ["--config", projectConfigToml]
143
+ : ["--port", chattrPort];
144
+ const child = spawn("agentchattr", args, {
145
+ env: process.env,
146
+ stdio: "ignore",
147
+ detached: true,
148
+ });
149
+ child.unref();
150
+ child.on("error", (err) => {
151
+ setProc({ process: null, state: "error", error: err.message });
152
+ });
153
+ child.on("exit", (code) => {
154
+ const cur = getProc();
155
+ if (cur.process === child) {
156
+ setProc({ process: null, state: "stopped", error: code ? `exit:${code}` : null });
157
+ }
158
+ });
159
+ setProc({ process: child, state: "running", error: null });
160
+ return child;
161
+ }
162
+
112
163
  if (action === "start") {
113
- if (chattrProcess.state === "running" && chattrProcess.process) {
164
+ const proc = getProc();
165
+ if (proc.state === "running" && proc.process) {
114
166
  return res.json({ ok: true, state: "running", message: "Already running" });
115
167
  }
116
168
  try {
117
- const child = spawn("agentchattr", ["--port", chattrPort], {
118
- env: process.env,
119
- stdio: "ignore",
120
- detached: true,
121
- });
122
- child.unref();
123
- child.on("error", (err) => {
124
- chattrProcess = { process: null, state: "error", error: err.message };
125
- });
126
- child.on("exit", (code) => {
127
- if (chattrProcess.process === child) {
128
- chattrProcess = { process: null, state: "stopped", error: code ? `exit:${code}` : null };
129
- }
130
- });
131
- chattrProcess = { process: child, state: "running", error: null };
169
+ const child = spawnChattr();
132
170
  res.json({ ok: true, state: "running", pid: child.pid });
133
171
  } catch (err) {
134
- chattrProcess = { process: null, state: "error", error: err.message };
172
+ setProc({ process: null, state: "error", error: err.message });
135
173
  res.status(500).json({ ok: false, state: "error", error: err.message });
136
174
  }
137
175
  } else if (action === "stop") {
138
- if (chattrProcess.process) {
139
- try { chattrProcess.process.kill("SIGTERM"); } catch {}
176
+ const proc = getProc();
177
+ if (proc.process) {
178
+ try { proc.process.kill("SIGTERM"); } catch {}
140
179
  }
141
- chattrProcess = { process: null, state: "stopped", error: null };
180
+ setProc({ process: null, state: "stopped", error: null });
142
181
  res.json({ ok: true, state: "stopped" });
143
182
  } else if (action === "restart") {
144
- if (chattrProcess.process) {
145
- try { chattrProcess.process.kill("SIGTERM"); } catch {}
183
+ const proc = getProc();
184
+ if (proc.process) {
185
+ try { proc.process.kill("SIGTERM"); } catch {}
146
186
  }
147
- chattrProcess = { process: null, state: "stopped", error: null };
187
+ setProc({ process: null, state: "stopped", error: null });
148
188
  setTimeout(() => {
149
189
  try {
150
- const child = spawn("agentchattr", ["--port", chattrPort], {
151
- env: process.env,
152
- stdio: "ignore",
153
- detached: true,
154
- });
155
- child.unref();
156
- child.on("error", (err) => {
157
- chattrProcess = { process: null, state: "error", error: err.message };
158
- });
159
- child.on("exit", (code) => {
160
- if (chattrProcess.process === child) {
161
- chattrProcess = { process: null, state: "stopped", error: code ? `exit:${code}` : null };
162
- }
163
- });
164
- chattrProcess = { process: child, state: "running", error: null };
190
+ const child = spawnChattr();
165
191
  res.json({ ok: true, state: "running", pid: child.pid });
166
192
  } catch (err) {
167
- chattrProcess = { process: null, state: "error", error: err.message };
193
+ setProc({ process: null, state: "error", error: err.message });
168
194
  res.status(500).json({ ok: false, state: "error", error: err.message });
169
195
  }
170
196
  }, 500);
171
197
  } else {
172
198
  res.status(400).json({ error: "Unknown action" });
173
199
  }
174
- });
200
+ }
201
+ app.post("/api/agentchattr/:projectOrAction/:action", handleAgentChattr);
202
+ app.post("/api/agentchattr/:projectOrAction", handleAgentChattr);
175
203
 
176
204
  // --- Lifecycle: start spawns PTY (visible in terminal panel) ---
177
205
 
@@ -262,17 +290,17 @@ app.post("/api/agents/:project/:agent/write", (req, res) => {
262
290
 
263
291
  const triggers = new Map();
264
292
 
265
- const DEFAULT_MESSAGE = `@t1 @t2a @t2b @t3 — Queue check.
266
- T1: Merge any PR with both approvals, assign next from queue.
267
- T3: Work on assigned ticket or address review feedback.
268
- T2a/T2b: Review open PRs. If T3 pushed fixes, re-review. Post verdict on PR AND notify here.
293
+ const DEFAULT_MESSAGE = `@head @reviewer1 @reviewer2 @dev — Queue check.
294
+ Head: Merge any PR with both approvals, assign next from queue.
295
+ Dev: Work on assigned ticket or address review feedback.
296
+ Reviewer1/Reviewer2: Review open PRs. If Dev pushed fixes, re-review. Post verdict on PR AND notify here.
269
297
  ALL: Communicate via this chat by tagging agents. Your terminal is NOT visible.`;
270
298
 
271
299
  async function sendTriggerMessage(projectId) {
272
300
  const cfg = readConfig();
273
301
  const project = cfg.projects && cfg.projects.find((p) => p.id === projectId);
274
- const chattrUrl = cfg.agentchattr_url || "http://127.0.0.1:8300";
275
- const token = cfg.agentchattr_token || "";
302
+ const { url: chattrUrl, token: chattrToken } = resolveProjectChattr(projectId);
303
+ const token = chattrToken || "";
276
304
  const message = (project && project.trigger_message) || DEFAULT_MESSAGE;
277
305
  const headers = { "Content-Type": "application/json" };
278
306
  if (token) headers["x-session-token"] = token;
@@ -359,11 +387,31 @@ if (fs.existsSync(outDir)) {
359
387
  app.use(express.static(outDir));
360
388
  }
361
389
 
362
- // SPA fallback: serve index.html for all non-API, non-WS routes
390
+ // SPA fallback: serve the correct pre-rendered HTML for dynamic routes.
391
+ // Static export only generates templates for placeholder params (e.g. /project/_),
392
+ // so we map real dynamic segments back to those template files.
363
393
  app.use((req, res, next) => {
364
394
  if (req.method !== "GET" || req.path.startsWith("/api/")) {
365
395
  return next();
366
396
  }
397
+
398
+ // Map dynamic routes to their pre-rendered template HTML
399
+ const dynamicRoutes = [
400
+ { pattern: /^\/project\/[^/]+\/memory\/?$/, template: "project/_/memory.html" },
401
+ { pattern: /^\/project\/[^/]+\/queue\/?$/, template: "project/_/queue.html" },
402
+ { pattern: /^\/project\/[^/]+\/?$/, template: "project/_.html" },
403
+ ];
404
+
405
+ for (const route of dynamicRoutes) {
406
+ if (route.pattern.test(req.path)) {
407
+ const templatePath = path.join(outDir, route.template);
408
+ if (fs.existsSync(templatePath)) {
409
+ return res.sendFile(templatePath);
410
+ }
411
+ }
412
+ }
413
+
414
+ // Default fallback to index.html
367
415
  const indexPath = path.join(outDir, "index.html");
368
416
  if (fs.existsSync(indexPath)) {
369
417
  res.sendFile(indexPath);