quadwork 1.0.17 → 1.1.0

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 (79) hide show
  1. package/README.md +28 -0
  2. package/bin/quadwork.js +436 -52
  3. package/out/404.html +1 -1
  4. package/out/__next.__PAGE__.txt +1 -1
  5. package/out/__next._full.txt +1 -1
  6. package/out/__next._head.txt +1 -1
  7. package/out/__next._index.txt +1 -1
  8. package/out/__next._tree.txt +1 -1
  9. package/out/_not-found/__next._full.txt +1 -1
  10. package/out/_not-found/__next._head.txt +1 -1
  11. package/out/_not-found/__next._index.txt +1 -1
  12. package/out/_not-found/__next._not-found.__PAGE__.txt +1 -1
  13. package/out/_not-found/__next._not-found.txt +1 -1
  14. package/out/_not-found/__next._tree.txt +1 -1
  15. package/out/_not-found.html +1 -1
  16. package/out/_not-found.txt +1 -1
  17. package/out/app-shell/__next._full.txt +1 -1
  18. package/out/app-shell/__next._head.txt +1 -1
  19. package/out/app-shell/__next._index.txt +1 -1
  20. package/out/app-shell/__next._tree.txt +1 -1
  21. package/out/app-shell/__next.app-shell.__PAGE__.txt +1 -1
  22. package/out/app-shell/__next.app-shell.txt +1 -1
  23. package/out/app-shell.html +1 -1
  24. package/out/app-shell.txt +1 -1
  25. package/out/index.html +1 -1
  26. package/out/index.txt +1 -1
  27. package/out/project/_/__next._full.txt +1 -1
  28. package/out/project/_/__next._head.txt +1 -1
  29. package/out/project/_/__next._index.txt +1 -1
  30. package/out/project/_/__next._tree.txt +1 -1
  31. package/out/project/_/__next.project.$d$id.__PAGE__.txt +1 -1
  32. package/out/project/_/__next.project.$d$id.txt +1 -1
  33. package/out/project/_/__next.project.txt +1 -1
  34. package/out/project/_/memory/__next._full.txt +1 -1
  35. package/out/project/_/memory/__next._head.txt +1 -1
  36. package/out/project/_/memory/__next._index.txt +1 -1
  37. package/out/project/_/memory/__next._tree.txt +1 -1
  38. package/out/project/_/memory/__next.project.$d$id.memory.__PAGE__.txt +1 -1
  39. package/out/project/_/memory/__next.project.$d$id.memory.txt +1 -1
  40. package/out/project/_/memory/__next.project.$d$id.txt +1 -1
  41. package/out/project/_/memory/__next.project.txt +1 -1
  42. package/out/project/_/memory.html +1 -1
  43. package/out/project/_/memory.txt +1 -1
  44. package/out/project/_/queue/__next._full.txt +1 -1
  45. package/out/project/_/queue/__next._head.txt +1 -1
  46. package/out/project/_/queue/__next._index.txt +1 -1
  47. package/out/project/_/queue/__next._tree.txt +1 -1
  48. package/out/project/_/queue/__next.project.$d$id.queue.__PAGE__.txt +1 -1
  49. package/out/project/_/queue/__next.project.$d$id.queue.txt +1 -1
  50. package/out/project/_/queue/__next.project.$d$id.txt +1 -1
  51. package/out/project/_/queue/__next.project.txt +1 -1
  52. package/out/project/_/queue.html +1 -1
  53. package/out/project/_/queue.txt +1 -1
  54. package/out/project/_.html +1 -1
  55. package/out/project/_.txt +1 -1
  56. package/out/settings/__next._full.txt +1 -1
  57. package/out/settings/__next._head.txt +1 -1
  58. package/out/settings/__next._index.txt +1 -1
  59. package/out/settings/__next._tree.txt +1 -1
  60. package/out/settings/__next.settings.__PAGE__.txt +1 -1
  61. package/out/settings/__next.settings.txt +1 -1
  62. package/out/settings.html +1 -1
  63. package/out/settings.txt +1 -1
  64. package/out/setup/__next._full.txt +1 -1
  65. package/out/setup/__next._head.txt +1 -1
  66. package/out/setup/__next._index.txt +1 -1
  67. package/out/setup/__next._tree.txt +1 -1
  68. package/out/setup/__next.setup.__PAGE__.txt +1 -1
  69. package/out/setup/__next.setup.txt +1 -1
  70. package/out/setup.html +1 -1
  71. package/out/setup.txt +1 -1
  72. package/package.json +1 -1
  73. package/server/config.js +22 -1
  74. package/server/index.js +13 -4
  75. package/server/install-agentchattr.js +215 -0
  76. package/server/routes.js +38 -3
  77. /package/out/_next/static/{TKQFu1hHpaRuo62RWWrUJ → zx5_zAjM3qhPvkFrygZp8}/_buildManifest.js +0 -0
  78. /package/out/_next/static/{TKQFu1hHpaRuo62RWWrUJ → zx5_zAjM3qhPvkFrygZp8}/_clientMiddlewareManifest.js +0 -0
  79. /package/out/_next/static/{TKQFu1hHpaRuo62RWWrUJ → zx5_zAjM3qhPvkFrygZp8}/_ssgManifest.js +0 -0
@@ -3,4 +3,4 @@
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
  :HL["/_next/static/chunks/10b3c4k.q.yw..css","style"]
6
- 0:{"rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/chunks/10b3c4k.q.yw..css","precedence":"next"}],["$","script","script-0",{"src":"/_next/static/chunks/08fgie1bcjynm.js","async":true}],["$","script","script-1",{"src":"/_next/static/chunks/0ox7p_szjhn69.js","async":true}]],["$","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","template":["$","$L4",null,{}],"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."}]}]]}]}]],[]]}]}]]}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"TKQFu1hHpaRuo62RWWrUJ"}
6
+ 0:{"rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/chunks/10b3c4k.q.yw..css","precedence":"next"}],["$","script","script-0",{"src":"/_next/static/chunks/08fgie1bcjynm.js","async":true}],["$","script","script-1",{"src":"/_next/static/chunks/0ox7p_szjhn69.js","async":true}]],["$","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","template":["$","$L4",null,{}],"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."}]}]]}]}]],[]]}]}]]}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"zx5_zAjM3qhPvkFrygZp8"}
@@ -1,3 +1,3 @@
1
1
  :HL["/_next/static/chunks/10b3c4k.q.yw..css","style"]
2
2
  :HL["/_next/static/media/797e433ab948586e-s.p.0.q-h669a_dqa.woff2","font",{"crossOrigin":"","type":"font/woff2"}]
3
- 0:{"tree":{"name":"","param":null,"prefetchHints":16,"slots":{"children":{"name":"setup","param":null,"prefetchHints":0,"slots":{"children":{"name":"__PAGE__","param":null,"prefetchHints":0,"slots":null}}}}},"staleTime":300,"buildId":"TKQFu1hHpaRuo62RWWrUJ"}
3
+ 0:{"tree":{"name":"","param":null,"prefetchHints":16,"slots":{"children":{"name":"setup","param":null,"prefetchHints":0,"slots":{"children":{"name":"__PAGE__","param":null,"prefetchHints":0,"slots":null}}}}},"staleTime":300,"buildId":"zx5_zAjM3qhPvkFrygZp8"}
@@ -2,5 +2,5 @@
2
2
  2:I[64618,["/_next/static/chunks/08fgie1bcjynm.js","/_next/static/chunks/0ox7p_szjhn69.js","/_next/static/chunks/0caq73v0knw_w.js"],"default"]
3
3
  3:I[11717,["/_next/static/chunks/08fgie1bcjynm.js","/_next/static/chunks/0ox7p_szjhn69.js"],"OutletBoundary"]
4
4
  4:"$Sreact.suspense"
5
- 0:{"rsc":["$","$1","c",{"children":[["$","$L2",null,{}],[["$","script","script-0",{"src":"/_next/static/chunks/0caq73v0knw_w.js","async":true}]],["$","$L3",null,{"children":["$","$4",null,{"name":"Next.MetadataOutlet","children":"$@5"}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"TKQFu1hHpaRuo62RWWrUJ"}
5
+ 0:{"rsc":["$","$1","c",{"children":[["$","$L2",null,{}],[["$","script","script-0",{"src":"/_next/static/chunks/0caq73v0knw_w.js","async":true}]],["$","$L3",null,{"children":["$","$4",null,{"name":"Next.MetadataOutlet","children":"$@5"}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"zx5_zAjM3qhPvkFrygZp8"}
6
6
  5:null
@@ -2,4 +2,4 @@
2
2
  2:I[12527,["/_next/static/chunks/08fgie1bcjynm.js","/_next/static/chunks/0ox7p_szjhn69.js"],"default"]
3
3
  3:I[59763,["/_next/static/chunks/08fgie1bcjynm.js","/_next/static/chunks/0ox7p_szjhn69.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":"TKQFu1hHpaRuo62RWWrUJ"}
5
+ 0:{"rsc":["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","template":["$","$L3",null,{}]}]]}],"isPartial":false,"staleTime":300,"varyParams":"$W4","buildId":"zx5_zAjM3qhPvkFrygZp8"}
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/10b3c4k.q.yw..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/0io_y3d0p5v~b.js" async=""></script><script src="/_next/static/chunks/0excsn2a_5qsb.js" async=""></script><script src="/_next/static/chunks/turbopack-0sammtvunroor.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/0caq73v0knw_w.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"><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><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[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/0caq73v0knw_w.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/10b3c4k.q.yw..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/10b3c4k.q.yw..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/0caq73v0knw_w.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/10b3c4k.q.yw..css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}]]],\"S\":true,\"h\":null,\"s\":\"$undefined\",\"l\":\"$undefined\",\"p\":\"$undefined\",\"d\":\"$undefined\",\"b\":\"TKQFu1hHpaRuo62RWWrUJ\"}\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/10b3c4k.q.yw..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/0io_y3d0p5v~b.js" async=""></script><script src="/_next/static/chunks/0excsn2a_5qsb.js" async=""></script><script src="/_next/static/chunks/turbopack-0sammtvunroor.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/0caq73v0knw_w.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"><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><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[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/0caq73v0knw_w.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/10b3c4k.q.yw..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/10b3c4k.q.yw..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/0caq73v0knw_w.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/10b3c4k.q.yw..css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}]]],\"S\":true,\"h\":null,\"s\":\"$undefined\",\"l\":\"$undefined\",\"p\":\"$undefined\",\"d\":\"$undefined\",\"b\":\"zx5_zAjM3qhPvkFrygZp8\"}\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
@@ -10,7 +10,7 @@ c:I[11717,["/_next/static/chunks/08fgie1bcjynm.js","/_next/static/chunks/0ox7p_s
10
10
  e:I[92243,["/_next/static/chunks/08fgie1bcjynm.js","/_next/static/chunks/0ox7p_szjhn69.js"],"default",1]
11
11
  :HL["/_next/static/chunks/10b3c4k.q.yw..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/10b3c4k.q.yw..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/0caq73v0knw_w.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/10b3c4k.q.yw..css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"TKQFu1hHpaRuo62RWWrUJ"}
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/10b3c4k.q.yw..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/0caq73v0knw_w.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/10b3c4k.q.yw..css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"zx5_zAjM3qhPvkFrygZp8"}
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": "1.0.17",
3
+ "version": "1.1.0",
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
@@ -93,12 +93,33 @@ function resolveAgentCommand(projectId, agentId) {
93
93
  function resolveProjectChattr(projectId) {
94
94
  const config = readConfig();
95
95
  const project = projectId ? config.projects?.find((p) => p.id === projectId) : null;
96
+
97
+ // Resolution order for AgentChattr install dir:
98
+ // 1. project.agentchattr_dir — per-project clone (Option B, #181)
99
+ // 2. config.agentchattr_dir — legacy global clone (v1 backward compat)
100
+ // 3. ~/.quadwork/{projectId}/agentchattr — per-project default
101
+ //
102
+ // Phase 1A (#182) is schema-only: project.agentchattr_dir is now written
103
+ // on every new project, but the actual clone-on-create logic does not
104
+ // land until #183/#184/#185. Until then, if the project field points at
105
+ // a directory that does not yet contain a working install, fall back to
106
+ // the legacy global so existing setups (and brand-new projects on a v1
107
+ // host) keep starting AgentChattr from the working clone.
108
+ const perProjectDefault = projectId
109
+ ? path.join(os.homedir(), ".quadwork", projectId, "agentchattr")
110
+ : path.join(os.homedir(), ".quadwork", "agentchattr");
111
+ const legacyGlobal = config.agentchattr_dir || path.join(os.homedir(), ".quadwork", "agentchattr");
112
+ let dir = project?.agentchattr_dir || legacyGlobal || perProjectDefault;
113
+ if (!fs.existsSync(path.join(dir, "run.py")) && fs.existsSync(path.join(legacyGlobal, "run.py"))) {
114
+ dir = legacyGlobal;
115
+ }
116
+
96
117
  return {
97
118
  url: project?.agentchattr_url || config.agentchattr_url || "http://127.0.0.1:8300",
98
119
  token: project?.agentchattr_token || config.agentchattr_token || null,
99
120
  mcp_http_port: project?.mcp_http_port || null,
100
121
  mcp_sse_port: project?.mcp_sse_port || null,
101
- dir: project?.agentchattr_dir || config.agentchattr_dir || path.join(os.homedir(), ".quadwork", "agentchattr"),
122
+ dir,
102
123
  };
103
124
  }
104
125
 
package/server/index.js CHANGED
@@ -398,12 +398,21 @@ async function handleAgentChattr(req, res) {
398
398
  const { url: chattrUrl } = resolveProjectChattr(projectId);
399
399
  const chattrPort = new URL(chattrUrl).port || "8300";
400
400
 
401
- // Find per-project config.toml (prefer project working_dir/agentchattr/config.toml)
401
+ // Find per-project config.toml. Phase 2E / #181: prefer the
402
+ // per-project AgentChattr clone ROOT (where the web/CLI wizards now
403
+ // write it as of #184/#185 — and where run.py actually reads it from).
404
+ // Fall back to the legacy <working_dir>/agentchattr/config.toml for
405
+ // v1 setups that haven't been migrated yet (#188).
402
406
  const cfg = readConfig();
403
407
  const project = cfg.projects?.find((p) => p.id === projectId);
404
- const projectConfigToml = project?.working_dir
405
- ? path.join(project.working_dir, "agentchattr", "config.toml")
406
- : null;
408
+ const { dir: resolvedAcDir } = resolveProjectChattr(projectId);
409
+ let projectConfigToml = null;
410
+ if (resolvedAcDir && fs.existsSync(path.join(resolvedAcDir, "config.toml"))) {
411
+ projectConfigToml = path.join(resolvedAcDir, "config.toml");
412
+ } else if (project?.working_dir) {
413
+ const legacyToml = path.join(project.working_dir, "agentchattr", "config.toml");
414
+ if (fs.existsSync(legacyToml)) projectConfigToml = legacyToml;
415
+ }
407
416
 
408
417
  function getProc() {
409
418
  return chattrProcesses.get(projectId) || { process: null, state: "stopped", error: null };
@@ -0,0 +1,215 @@
1
+ // Shared AgentChattr install helper used by both the CLI wizard
2
+ // (bin/quadwork.js) and the web setup route (server/routes.js).
3
+ //
4
+ // Extracted as part of #185 (Phase 2D of master #181) so the web UI
5
+ // can clone AgentChattr per-project without duplicating the locking,
6
+ // idempotency, and cleanup-safety logic that #183 + #187 added.
7
+ //
8
+ // Public API:
9
+ // findAgentChattr(dir) → string|null
10
+ // installAgentChattr(dir) → string|null (.lastError on failure)
11
+ // chattrSpawnArgs(dir, extraArgs) → { command, spawnArgs, cwd } | null
12
+ // AGENTCHATTR_REPO → upstream URL constant
13
+ //
14
+ // Self-contained — depends only on Node built-ins so it's safe to require
15
+ // from anywhere in the project (CLI bin, server routes, future tests).
16
+
17
+ const { execSync } = require("child_process");
18
+ const fs = require("fs");
19
+ const path = require("path");
20
+
21
+ const AGENTCHATTR_REPO = "https://github.com/bcurts/agentchattr.git";
22
+
23
+ // Stale-lock thresholds for installAgentChattr().
24
+ // Lock files older than this OR whose owning pid is no longer alive are
25
+ // treated as crashed and reclaimed. Tuned to comfortably exceed the longest
26
+ // step (pip install of agentchattr requirements, ~120s timeout).
27
+ const INSTALL_LOCK_STALE_MS = 10 * 60 * 1000; // 10 min
28
+ const INSTALL_LOCK_WAIT_TOTAL_MS = 30 * 1000; // wait up to 30s for a peer
29
+ const INSTALL_LOCK_POLL_MS = 500;
30
+
31
+ function _run(cmd, opts = {}) {
32
+ try { return execSync(cmd, { encoding: "utf-8", stdio: "pipe", ...opts }).trim(); }
33
+ catch { return null; }
34
+ }
35
+
36
+ function _isPidAlive(pid) {
37
+ if (!pid || !Number.isFinite(pid)) return false;
38
+ try { process.kill(pid, 0); return true; }
39
+ catch (e) { return e.code === "EPERM"; }
40
+ }
41
+
42
+ function _readLock(lockFile) {
43
+ try {
44
+ const raw = fs.readFileSync(lockFile, "utf-8").trim();
45
+ const [pidStr, tsStr] = raw.split(":");
46
+ return { pid: parseInt(pidStr, 10), ts: parseInt(tsStr, 10) || 0 };
47
+ } catch { return null; }
48
+ }
49
+
50
+ function _isLockStale(lockFile) {
51
+ const info = _readLock(lockFile);
52
+ if (!info) return true;
53
+ if (Date.now() - info.ts > INSTALL_LOCK_STALE_MS) return true;
54
+ if (!_isPidAlive(info.pid)) return true;
55
+ return false;
56
+ }
57
+
58
+ /**
59
+ * Check if AgentChattr is fully installed (cloned + venv ready) at `dir`.
60
+ * Returns the directory path if both run.py and .venv/bin/python exist, or null.
61
+ * Caller must pass an explicit `dir` — there is no default.
62
+ */
63
+ function findAgentChattr(dir) {
64
+ if (!dir) return null;
65
+ if (fs.existsSync(path.join(dir, "run.py")) && fs.existsSync(path.join(dir, ".venv", "bin", "python"))) return dir;
66
+ return null;
67
+ }
68
+
69
+ /**
70
+ * Clone AgentChattr and set up its venv at `dir`. Idempotent — safe to
71
+ * re-run on the same path, and safe to call repeatedly with different
72
+ * paths in the same process. Designed to support per-project clones (#181).
73
+ *
74
+ * Behavior on re-run:
75
+ * - Fully-installed path → no-op (skips clone, skips venv create, skips pip)
76
+ * - Missing run.py → clones (only after refusing to overwrite
77
+ * unrelated content; see safety rules below)
78
+ * - Missing venv → creates venv and reinstalls requirements
79
+ *
80
+ * Safety rules — never accidentally clean up unrelated directories:
81
+ * - Empty dir → safe to remove
82
+ * - Git repo whose origin contains "agentchattr" → safe to remove
83
+ * - Anything else → refuse, return null
84
+ *
85
+ * Concurrency: a per-target lock at `${dir}.install.lock` serializes
86
+ * concurrent installs to the same path. Stale locks (dead pid OR older
87
+ * than 10 min) are reclaimed atomically via rename → unlink. Live
88
+ * peers are polled for up to 30s; after that, returns null with a
89
+ * clear lastError.
90
+ *
91
+ * On failure, returns null and stores a human-readable reason on
92
+ * `installAgentChattr.lastError` so callers can surface it without
93
+ * changing the return shape.
94
+ */
95
+ function installAgentChattr(dir) {
96
+ if (!dir) {
97
+ installAgentChattr.lastError = "installAgentChattr: dir is required";
98
+ return null;
99
+ }
100
+ installAgentChattr.lastError = null;
101
+ const setError = (msg) => { installAgentChattr.lastError = msg; return null; };
102
+
103
+ // --- Per-target lock ---
104
+ const lockFile = `${dir}.install.lock`;
105
+ try { fs.mkdirSync(path.dirname(lockFile), { recursive: true }); }
106
+ catch (e) { return setError(`Cannot create parent of ${dir}: ${e.message}`); }
107
+
108
+ let acquired = false;
109
+ const deadline = Date.now() + INSTALL_LOCK_WAIT_TOTAL_MS;
110
+ while (!acquired) {
111
+ try {
112
+ fs.writeFileSync(lockFile, `${process.pid}:${Date.now()}`, { flag: "wx" });
113
+ acquired = true;
114
+ } catch (e) {
115
+ if (e.code !== "EEXIST") return setError(`Cannot create install lock ${lockFile}: ${e.message}`);
116
+ if (_isLockStale(lockFile)) {
117
+ const sideline = `${lockFile}.stale.${process.pid}.${Date.now()}`;
118
+ try {
119
+ fs.renameSync(lockFile, sideline);
120
+ try { fs.unlinkSync(sideline); } catch {}
121
+ } catch (renameErr) {
122
+ if (renameErr.code !== "ENOENT") {
123
+ return setError(`Cannot reclaim stale lock ${lockFile}: ${renameErr.message}`);
124
+ }
125
+ }
126
+ continue;
127
+ }
128
+ if (Date.now() >= deadline) {
129
+ const info = _readLock(lockFile) || { pid: "?", ts: 0 };
130
+ return setError(`Another install is in progress at ${dir} (pid ${info.pid}); timed out after ${INSTALL_LOCK_WAIT_TOTAL_MS}ms. Re-run after it finishes, or remove ${lockFile} if stale.`);
131
+ }
132
+ try { execSync(`sleep ${INSTALL_LOCK_POLL_MS / 1000}`); }
133
+ catch { /* sleep interrupted; loop will recheck */ }
134
+ }
135
+ }
136
+
137
+ try {
138
+ return _installAgentChattrLocked(dir, setError);
139
+ } finally {
140
+ try { fs.unlinkSync(lockFile); } catch {}
141
+ }
142
+ }
143
+ installAgentChattr.lastError = null;
144
+
145
+ function _installAgentChattrLocked(dir, setError) {
146
+ const runPy = path.join(dir, "run.py");
147
+ const venvPython = path.join(dir, ".venv", "bin", "python");
148
+ let venvJustCreated = false;
149
+
150
+ // 1. Clone if run.py is missing.
151
+ if (!fs.existsSync(runPy)) {
152
+ if (fs.existsSync(dir)) {
153
+ let entries;
154
+ try { entries = fs.readdirSync(dir); }
155
+ catch (e) { return setError(`Cannot read ${dir}: ${e.message}`); }
156
+ const isEmpty = entries.length === 0;
157
+ if (isEmpty) {
158
+ try { fs.rmSync(dir, { recursive: true, force: true }); }
159
+ catch (e) { return setError(`Cannot remove empty dir ${dir}: ${e.message}`); }
160
+ } else if (fs.existsSync(path.join(dir, ".git"))) {
161
+ const remote = _run(`git -C "${dir}" remote get-url origin 2>/dev/null`);
162
+ if (remote && remote.includes("agentchattr")) {
163
+ try { fs.rmSync(dir, { recursive: true, force: true }); }
164
+ catch (e) { return setError(`Cannot remove failed clone at ${dir}: ${e.message}`); }
165
+ } else {
166
+ return setError(`Refusing to overwrite ${dir}: contains a non-AgentChattr git repo`);
167
+ }
168
+ } else {
169
+ return setError(`Refusing to overwrite ${dir}: directory exists with unrelated content`);
170
+ }
171
+ }
172
+ try { fs.mkdirSync(path.dirname(dir), { recursive: true }); }
173
+ catch (e) { return setError(`Cannot create parent of ${dir}: ${e.message}`); }
174
+ const cloneResult = _run(`git clone "${AGENTCHATTR_REPO}" "${dir}" 2>&1`, { timeout: 60000 });
175
+ if (cloneResult === null) return setError(`git clone of ${AGENTCHATTR_REPO} into ${dir} failed`);
176
+ if (!fs.existsSync(runPy)) return setError(`Clone completed but run.py missing at ${dir}`);
177
+ }
178
+
179
+ // 2. Create venv if missing.
180
+ if (!fs.existsSync(venvPython)) {
181
+ const venvResult = _run(`python3 -m venv "${path.join(dir, ".venv")}" 2>&1`, { timeout: 60000 });
182
+ if (venvResult === null) return setError(`python3 -m venv failed at ${dir}/.venv (is python3 installed?)`);
183
+ if (!fs.existsSync(venvPython)) return setError(`venv created but ${venvPython} missing`);
184
+ venvJustCreated = true;
185
+ }
186
+
187
+ // 3. Install requirements only when the venv was just (re)created.
188
+ if (venvJustCreated) {
189
+ const reqFile = path.join(dir, "requirements.txt");
190
+ if (fs.existsSync(reqFile)) {
191
+ const pipResult = _run(`"${venvPython}" -m pip install -r "${reqFile}" 2>&1`, { timeout: 120000 });
192
+ if (pipResult === null) return setError(`pip install -r ${reqFile} failed`);
193
+ }
194
+ }
195
+ return dir;
196
+ }
197
+
198
+ /**
199
+ * Get spawn args for launching AgentChattr from its cloned directory.
200
+ * Returns { command, spawnArgs, cwd } or null if not fully installed.
201
+ * Requires .venv/bin/python — never falls back to bare python3.
202
+ */
203
+ function chattrSpawnArgs(dir, extraArgs) {
204
+ if (!dir) return null;
205
+ const venvPython = path.join(dir, ".venv", "bin", "python");
206
+ if (!fs.existsSync(path.join(dir, "run.py")) || !fs.existsSync(venvPython)) return null;
207
+ return { command: venvPython, spawnArgs: ["run.py", ...(extraArgs || [])], cwd: dir };
208
+ }
209
+
210
+ module.exports = {
211
+ AGENTCHATTR_REPO,
212
+ findAgentChattr,
213
+ installAgentChattr,
214
+ chattrSpawnArgs,
215
+ };
package/server/routes.js CHANGED
@@ -69,6 +69,7 @@ router.put("/api/config", (req, res) => {
69
69
  // ─── Chat (AgentChattr proxy) ──────────────────────────────────────────────
70
70
 
71
71
  const { resolveProjectChattr } = require("./config");
72
+ const { installAgentChattr, findAgentChattr } = require("./install-agentchattr");
72
73
 
73
74
  function getChattrConfig(projectId) {
74
75
  const resolved = resolveProjectChattr(projectId);
@@ -650,9 +651,28 @@ router.post("/api/setup", (req, res) => {
650
651
  const parentDir = path.dirname(workingDir);
651
652
  const backends = body.backends;
652
653
 
653
- // Per-project: isolated config dir + data dir
654
- const projectConfigDir = path.join(workingDir, "agentchattr");
655
- fs.mkdirSync(projectConfigDir, { recursive: true });
654
+ // Phase 2D / #181: config.toml lives at the per-project AgentChattr
655
+ // clone ROOT (~/.quadwork/{id}/agentchattr/), not inside the user's
656
+ // project working_dir. AgentChattr's run.py loads ROOT/config.toml
657
+ // and ignores --config, so the toml has to be at the same path the
658
+ // clone lives at. Same path matches what writeQuadWorkConfig()
659
+ // persists in agentchattr_dir (#182) and what the CLI wizard
660
+ // writes (#184).
661
+ //
662
+ // We install the clone *here*, before writing config.toml. The
663
+ // install must run first because installAgentChattr() refuses to
664
+ // overwrite a non-empty directory it doesn't recognize — if we
665
+ // mkdir + write config.toml first, the subsequent install in
666
+ // add-config would see "unrelated content" and reject the dir,
667
+ // breaking first-run web project creation (t2a's review of #195).
668
+ const projectConfigDir = path.join(CONFIG_DIR, dirName, "agentchattr");
669
+ if (!findAgentChattr(projectConfigDir)) {
670
+ const installResult = installAgentChattr(projectConfigDir);
671
+ if (!installResult) {
672
+ const reason = installAgentChattr.lastError || "unknown error";
673
+ return res.json({ ok: false, error: `AgentChattr install failed at ${projectConfigDir}: ${reason}` });
674
+ }
675
+ }
656
676
  const dataDir = path.join(projectConfigDir, "data");
657
677
  fs.mkdirSync(dataDir, { recursive: true });
658
678
  const tomlPath = path.join(projectConfigDir, "config.toml");
@@ -743,12 +763,27 @@ router.post("/api/setup", (req, res) => {
743
763
  while (usedMcpPorts.has(mcp_sse_port)) mcp_sse_port++;
744
764
  }
745
765
  if (!agentchattr_token) agentchattr_token = crypto.randomBytes(16).toString("hex");
766
+
767
+ // Phase 2D / #181: clone AgentChattr per-project before saving config.
768
+ // The path here must match the one written into agentchattr_dir below
769
+ // and the one agentchattr-config writes config.toml into.
770
+ const perProjectDir = path.join(CONFIG_DIR, id, "agentchattr");
771
+ if (!findAgentChattr(perProjectDir)) {
772
+ const installResult = installAgentChattr(perProjectDir);
773
+ if (!installResult) {
774
+ const reason = installAgentChattr.lastError || "unknown error";
775
+ return res.json({ ok: false, error: `AgentChattr install failed at ${perProjectDir}: ${reason}` });
776
+ }
777
+ }
778
+
746
779
  cfg.projects.push({
747
780
  id, name, repo, working_dir: workingDir, agents,
748
781
  agentchattr_url: `http://127.0.0.1:${chattrPort}`,
749
782
  agentchattr_token,
750
783
  mcp_http_port,
751
784
  mcp_sse_port,
785
+ // Per-project AgentChattr clone path (Option B / #181).
786
+ agentchattr_dir: perProjectDir,
752
787
  });
753
788
  const dir = path.dirname(CONFIG_PATH);
754
789
  if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });