@junctionpanel/server 0.1.16

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 (400) hide show
  1. package/.env.example +10 -0
  2. package/LICENSE +671 -0
  3. package/README.md +118 -0
  4. package/agent-prompt.md +339 -0
  5. package/dist/scripts/daemon-runner.js +141 -0
  6. package/dist/scripts/daemon-runner.js.map +1 -0
  7. package/dist/scripts/dev-runner.js +17 -0
  8. package/dist/scripts/dev-runner.js.map +1 -0
  9. package/dist/scripts/mcp-stdio-socket-bridge-cli.mjs +62 -0
  10. package/dist/scripts/supervisor.js +122 -0
  11. package/dist/scripts/supervisor.js.map +1 -0
  12. package/dist/server/client/daemon-client-relay-e2ee-transport.d.ts +8 -0
  13. package/dist/server/client/daemon-client-relay-e2ee-transport.d.ts.map +1 -0
  14. package/dist/server/client/daemon-client-relay-e2ee-transport.js +161 -0
  15. package/dist/server/client/daemon-client-relay-e2ee-transport.js.map +1 -0
  16. package/dist/server/client/daemon-client-terminal-stream-manager.d.ts +43 -0
  17. package/dist/server/client/daemon-client-terminal-stream-manager.d.ts.map +1 -0
  18. package/dist/server/client/daemon-client-terminal-stream-manager.js +134 -0
  19. package/dist/server/client/daemon-client-terminal-stream-manager.js.map +1 -0
  20. package/dist/server/client/daemon-client-transport-types.d.ts +34 -0
  21. package/dist/server/client/daemon-client-transport-types.d.ts.map +1 -0
  22. package/dist/server/client/daemon-client-transport-types.js +2 -0
  23. package/dist/server/client/daemon-client-transport-types.js.map +1 -0
  24. package/dist/server/client/daemon-client-transport-utils.d.ts +9 -0
  25. package/dist/server/client/daemon-client-transport-utils.d.ts.map +1 -0
  26. package/dist/server/client/daemon-client-transport-utils.js +121 -0
  27. package/dist/server/client/daemon-client-transport-utils.js.map +1 -0
  28. package/dist/server/client/daemon-client-transport.d.ts +5 -0
  29. package/dist/server/client/daemon-client-transport.d.ts.map +1 -0
  30. package/dist/server/client/daemon-client-transport.js +4 -0
  31. package/dist/server/client/daemon-client-transport.js.map +1 -0
  32. package/dist/server/client/daemon-client-websocket-transport.d.ts +7 -0
  33. package/dist/server/client/daemon-client-websocket-transport.d.ts.map +1 -0
  34. package/dist/server/client/daemon-client-websocket-transport.js +64 -0
  35. package/dist/server/client/daemon-client-websocket-transport.js.map +1 -0
  36. package/dist/server/client/daemon-client.d.ts +443 -0
  37. package/dist/server/client/daemon-client.d.ts.map +1 -0
  38. package/dist/server/client/daemon-client.js +2223 -0
  39. package/dist/server/client/daemon-client.js.map +1 -0
  40. package/dist/server/server/agent/activity-curator.d.ts +8 -0
  41. package/dist/server/server/agent/activity-curator.d.ts.map +1 -0
  42. package/dist/server/server/agent/activity-curator.js +228 -0
  43. package/dist/server/server/agent/activity-curator.js.map +1 -0
  44. package/dist/server/server/agent/agent-management-mcp.d.ts +36 -0
  45. package/dist/server/server/agent/agent-management-mcp.d.ts.map +1 -0
  46. package/dist/server/server/agent/agent-management-mcp.js +644 -0
  47. package/dist/server/server/agent/agent-management-mcp.js.map +1 -0
  48. package/dist/server/server/agent/agent-manager.d.ts +252 -0
  49. package/dist/server/server/agent/agent-manager.d.ts.map +1 -0
  50. package/dist/server/server/agent/agent-manager.js +1651 -0
  51. package/dist/server/server/agent/agent-manager.js.map +1 -0
  52. package/dist/server/server/agent/agent-metadata-generator.d.ts +29 -0
  53. package/dist/server/server/agent/agent-metadata-generator.d.ts.map +1 -0
  54. package/dist/server/server/agent/agent-metadata-generator.js +163 -0
  55. package/dist/server/server/agent/agent-metadata-generator.js.map +1 -0
  56. package/dist/server/server/agent/agent-projections.d.ts +17 -0
  57. package/dist/server/server/agent/agent-projections.d.ts.map +1 -0
  58. package/dist/server/server/agent/agent-projections.js +270 -0
  59. package/dist/server/server/agent/agent-projections.js.map +1 -0
  60. package/dist/server/server/agent/agent-response-loop.d.ts +60 -0
  61. package/dist/server/server/agent/agent-response-loop.d.ts.map +1 -0
  62. package/dist/server/server/agent/agent-response-loop.js +304 -0
  63. package/dist/server/server/agent/agent-response-loop.js.map +1 -0
  64. package/dist/server/server/agent/agent-sdk-types.d.ts +377 -0
  65. package/dist/server/server/agent/agent-sdk-types.d.ts.map +1 -0
  66. package/dist/server/server/agent/agent-sdk-types.js +12 -0
  67. package/dist/server/server/agent/agent-sdk-types.js.map +1 -0
  68. package/dist/server/server/agent/agent-storage.d.ts +230 -0
  69. package/dist/server/server/agent/agent-storage.d.ts.map +1 -0
  70. package/dist/server/server/agent/agent-storage.js +346 -0
  71. package/dist/server/server/agent/agent-storage.js.map +1 -0
  72. package/dist/server/server/agent/agent-title-limits.d.ts +3 -0
  73. package/dist/server/server/agent/agent-title-limits.d.ts.map +1 -0
  74. package/dist/server/server/agent/agent-title-limits.js +3 -0
  75. package/dist/server/server/agent/agent-title-limits.js.map +1 -0
  76. package/dist/server/server/agent/mcp-server.d.ts +19 -0
  77. package/dist/server/server/agent/mcp-server.d.ts.map +1 -0
  78. package/dist/server/server/agent/mcp-server.js +742 -0
  79. package/dist/server/server/agent/mcp-server.js.map +1 -0
  80. package/dist/server/server/agent/model-resolver.d.ts +11 -0
  81. package/dist/server/server/agent/model-resolver.d.ts.map +1 -0
  82. package/dist/server/server/agent/model-resolver.js +21 -0
  83. package/dist/server/server/agent/model-resolver.js.map +1 -0
  84. package/dist/server/server/agent/orchestrator-instructions.d.ts +7 -0
  85. package/dist/server/server/agent/orchestrator-instructions.d.ts.map +1 -0
  86. package/dist/server/server/agent/orchestrator-instructions.js +51 -0
  87. package/dist/server/server/agent/orchestrator-instructions.js.map +1 -0
  88. package/dist/server/server/agent/orchestrator.d.ts +12 -0
  89. package/dist/server/server/agent/orchestrator.d.ts.map +1 -0
  90. package/dist/server/server/agent/orchestrator.js +12 -0
  91. package/dist/server/server/agent/orchestrator.js.map +1 -0
  92. package/dist/server/server/agent/pcm16-resampler.d.ts +14 -0
  93. package/dist/server/server/agent/pcm16-resampler.d.ts.map +1 -0
  94. package/dist/server/server/agent/pcm16-resampler.js +63 -0
  95. package/dist/server/server/agent/pcm16-resampler.js.map +1 -0
  96. package/dist/server/server/agent/provider-launch-config.d.ts +139 -0
  97. package/dist/server/server/agent/provider-launch-config.d.ts.map +1 -0
  98. package/dist/server/server/agent/provider-launch-config.js +83 -0
  99. package/dist/server/server/agent/provider-launch-config.js.map +1 -0
  100. package/dist/server/server/agent/provider-manifest.d.ts +15 -0
  101. package/dist/server/server/agent/provider-manifest.d.ts.map +1 -0
  102. package/dist/server/server/agent/provider-manifest.js +83 -0
  103. package/dist/server/server/agent/provider-manifest.js.map +1 -0
  104. package/dist/server/server/agent/provider-registry.d.ts +18 -0
  105. package/dist/server/server/agent/provider-registry.d.ts.map +1 -0
  106. package/dist/server/server/agent/provider-registry.js +45 -0
  107. package/dist/server/server/agent/provider-registry.js.map +1 -0
  108. package/dist/server/server/agent/providers/claude/model-catalog.d.ts +29 -0
  109. package/dist/server/server/agent/providers/claude/model-catalog.d.ts.map +1 -0
  110. package/dist/server/server/agent/providers/claude/model-catalog.js +64 -0
  111. package/dist/server/server/agent/providers/claude/model-catalog.js.map +1 -0
  112. package/dist/server/server/agent/providers/claude/task-notification-tool-call.d.ts +44 -0
  113. package/dist/server/server/agent/providers/claude/task-notification-tool-call.d.ts.map +1 -0
  114. package/dist/server/server/agent/providers/claude/task-notification-tool-call.js +250 -0
  115. package/dist/server/server/agent/providers/claude/task-notification-tool-call.js.map +1 -0
  116. package/dist/server/server/agent/providers/claude/tool-call-detail-parser.d.ts +3 -0
  117. package/dist/server/server/agent/providers/claude/tool-call-detail-parser.d.ts.map +1 -0
  118. package/dist/server/server/agent/providers/claude/tool-call-detail-parser.js +109 -0
  119. package/dist/server/server/agent/providers/claude/tool-call-detail-parser.js.map +1 -0
  120. package/dist/server/server/agent/providers/claude/tool-call-mapper.d.ts +16 -0
  121. package/dist/server/server/agent/providers/claude/tool-call-mapper.d.ts.map +1 -0
  122. package/dist/server/server/agent/providers/claude/tool-call-mapper.js +238 -0
  123. package/dist/server/server/agent/providers/claude/tool-call-mapper.js.map +1 -0
  124. package/dist/server/server/agent/providers/claude-agent.d.ts +49 -0
  125. package/dist/server/server/agent/providers/claude-agent.d.ts.map +1 -0
  126. package/dist/server/server/agent/providers/claude-agent.js +3701 -0
  127. package/dist/server/server/agent/providers/claude-agent.js.map +1 -0
  128. package/dist/server/server/agent/providers/codex/tool-call-detail-parser.d.ts +12 -0
  129. package/dist/server/server/agent/providers/codex/tool-call-detail-parser.d.ts.map +1 -0
  130. package/dist/server/server/agent/providers/codex/tool-call-detail-parser.js +104 -0
  131. package/dist/server/server/agent/providers/codex/tool-call-detail-parser.js.map +1 -0
  132. package/dist/server/server/agent/providers/codex/tool-call-mapper.d.ts +15 -0
  133. package/dist/server/server/agent/providers/codex/tool-call-mapper.d.ts.map +1 -0
  134. package/dist/server/server/agent/providers/codex/tool-call-mapper.js +720 -0
  135. package/dist/server/server/agent/providers/codex/tool-call-mapper.js.map +1 -0
  136. package/dist/server/server/agent/providers/codex-app-server-agent.d.ts +34 -0
  137. package/dist/server/server/agent/providers/codex-app-server-agent.d.ts.map +1 -0
  138. package/dist/server/server/agent/providers/codex-app-server-agent.js +2660 -0
  139. package/dist/server/server/agent/providers/codex-app-server-agent.js.map +1 -0
  140. package/dist/server/server/agent/providers/codex-rollout-timeline.d.ts +9 -0
  141. package/dist/server/server/agent/providers/codex-rollout-timeline.d.ts.map +1 -0
  142. package/dist/server/server/agent/providers/codex-rollout-timeline.js +487 -0
  143. package/dist/server/server/agent/providers/codex-rollout-timeline.js.map +1 -0
  144. package/dist/server/server/agent/providers/opencode/tool-call-detail-parser.d.ts +3 -0
  145. package/dist/server/server/agent/providers/opencode/tool-call-detail-parser.d.ts.map +1 -0
  146. package/dist/server/server/agent/providers/opencode/tool-call-detail-parser.js +39 -0
  147. package/dist/server/server/agent/providers/opencode/tool-call-detail-parser.js.map +1 -0
  148. package/dist/server/server/agent/providers/opencode/tool-call-mapper.d.ts +13 -0
  149. package/dist/server/server/agent/providers/opencode/tool-call-mapper.d.ts.map +1 -0
  150. package/dist/server/server/agent/providers/opencode/tool-call-mapper.js +151 -0
  151. package/dist/server/server/agent/providers/opencode/tool-call-mapper.js.map +1 -0
  152. package/dist/server/server/agent/providers/opencode-agent.d.ts +37 -0
  153. package/dist/server/server/agent/providers/opencode-agent.d.ts.map +1 -0
  154. package/dist/server/server/agent/providers/opencode-agent.js +874 -0
  155. package/dist/server/server/agent/providers/opencode-agent.js.map +1 -0
  156. package/dist/server/server/agent/providers/tool-call-detail-primitives.d.ts +1460 -0
  157. package/dist/server/server/agent/providers/tool-call-detail-primitives.d.ts.map +1 -0
  158. package/dist/server/server/agent/providers/tool-call-detail-primitives.js +552 -0
  159. package/dist/server/server/agent/providers/tool-call-detail-primitives.js.map +1 -0
  160. package/dist/server/server/agent/providers/tool-call-mapper-utils.d.ts +17 -0
  161. package/dist/server/server/agent/providers/tool-call-mapper-utils.d.ts.map +1 -0
  162. package/dist/server/server/agent/providers/tool-call-mapper-utils.js +109 -0
  163. package/dist/server/server/agent/providers/tool-call-mapper-utils.js.map +1 -0
  164. package/dist/server/server/agent/system-prompt.d.ts +3 -0
  165. package/dist/server/server/agent/system-prompt.d.ts.map +1 -0
  166. package/dist/server/server/agent/system-prompt.js +19 -0
  167. package/dist/server/server/agent/system-prompt.js.map +1 -0
  168. package/dist/server/server/agent/timeline-append.d.ts +10 -0
  169. package/dist/server/server/agent/timeline-append.d.ts.map +1 -0
  170. package/dist/server/server/agent/timeline-append.js +27 -0
  171. package/dist/server/server/agent/timeline-append.js.map +1 -0
  172. package/dist/server/server/agent/timeline-projection.d.ts +39 -0
  173. package/dist/server/server/agent/timeline-projection.d.ts.map +1 -0
  174. package/dist/server/server/agent/timeline-projection.js +215 -0
  175. package/dist/server/server/agent/timeline-projection.js.map +1 -0
  176. package/dist/server/server/agent/tool-name-normalization.d.ts +7 -0
  177. package/dist/server/server/agent/tool-name-normalization.d.ts.map +1 -0
  178. package/dist/server/server/agent/tool-name-normalization.js +45 -0
  179. package/dist/server/server/agent/tool-name-normalization.js.map +1 -0
  180. package/dist/server/server/agent/wait-for-agent-tracker.d.ts +15 -0
  181. package/dist/server/server/agent/wait-for-agent-tracker.d.ts.map +1 -0
  182. package/dist/server/server/agent/wait-for-agent-tracker.js +53 -0
  183. package/dist/server/server/agent/wait-for-agent-tracker.js.map +1 -0
  184. package/dist/server/server/agent-attention-policy.d.ts +20 -0
  185. package/dist/server/server/agent-attention-policy.d.ts.map +1 -0
  186. package/dist/server/server/agent-attention-policy.js +40 -0
  187. package/dist/server/server/agent-attention-policy.js.map +1 -0
  188. package/dist/server/server/allowed-hosts.d.ts +13 -0
  189. package/dist/server/server/allowed-hosts.d.ts.map +1 -0
  190. package/dist/server/server/allowed-hosts.js +94 -0
  191. package/dist/server/server/allowed-hosts.js.map +1 -0
  192. package/dist/server/server/bootstrap.d.ts +49 -0
  193. package/dist/server/server/bootstrap.d.ts.map +1 -0
  194. package/dist/server/server/bootstrap.js +422 -0
  195. package/dist/server/server/bootstrap.js.map +1 -0
  196. package/dist/server/server/client-message-id.d.ts +3 -0
  197. package/dist/server/server/client-message-id.d.ts.map +1 -0
  198. package/dist/server/server/client-message-id.js +12 -0
  199. package/dist/server/server/client-message-id.js.map +1 -0
  200. package/dist/server/server/config.d.ts +13 -0
  201. package/dist/server/server/config.d.ts.map +1 -0
  202. package/dist/server/server/config.js +58 -0
  203. package/dist/server/server/config.js.map +1 -0
  204. package/dist/server/server/connection-offer.d.ts +19 -0
  205. package/dist/server/server/connection-offer.d.ts.map +1 -0
  206. package/dist/server/server/connection-offer.js +60 -0
  207. package/dist/server/server/connection-offer.js.map +1 -0
  208. package/dist/server/server/daemon-keypair.d.ts +8 -0
  209. package/dist/server/server/daemon-keypair.d.ts.map +1 -0
  210. package/dist/server/server/daemon-keypair.js +40 -0
  211. package/dist/server/server/daemon-keypair.js.map +1 -0
  212. package/dist/server/server/daemon-version.d.ts +5 -0
  213. package/dist/server/server/daemon-version.d.ts.map +1 -0
  214. package/dist/server/server/daemon-version.js +22 -0
  215. package/dist/server/server/daemon-version.js.map +1 -0
  216. package/dist/server/server/exports.d.ts +16 -0
  217. package/dist/server/server/exports.d.ts.map +1 -0
  218. package/dist/server/server/exports.js +16 -0
  219. package/dist/server/server/exports.js.map +1 -0
  220. package/dist/server/server/file-download/token-store.d.ts +25 -0
  221. package/dist/server/server/file-download/token-store.d.ts.map +1 -0
  222. package/dist/server/server/file-download/token-store.js +40 -0
  223. package/dist/server/server/file-download/token-store.js.map +1 -0
  224. package/dist/server/server/file-explorer/service.d.ts +41 -0
  225. package/dist/server/server/file-explorer/service.d.ts.map +1 -0
  226. package/dist/server/server/file-explorer/service.js +226 -0
  227. package/dist/server/server/file-explorer/service.js.map +1 -0
  228. package/dist/server/server/index.d.ts +2 -0
  229. package/dist/server/server/index.d.ts.map +1 -0
  230. package/dist/server/server/index.js +141 -0
  231. package/dist/server/server/index.js.map +1 -0
  232. package/dist/server/server/json-utils.d.ts +11 -0
  233. package/dist/server/server/json-utils.d.ts.map +1 -0
  234. package/dist/server/server/json-utils.js +45 -0
  235. package/dist/server/server/json-utils.js.map +1 -0
  236. package/dist/server/server/junction-home.d.ts +2 -0
  237. package/dist/server/server/junction-home.d.ts.map +1 -0
  238. package/dist/server/server/junction-home.js +19 -0
  239. package/dist/server/server/junction-home.js.map +1 -0
  240. package/dist/server/server/logger.d.ts +12 -0
  241. package/dist/server/server/logger.d.ts.map +1 -0
  242. package/dist/server/server/logger.js +29 -0
  243. package/dist/server/server/logger.js.map +1 -0
  244. package/dist/server/server/messages.d.ts +9 -0
  245. package/dist/server/server/messages.d.ts.map +1 -0
  246. package/dist/server/server/messages.js +29 -0
  247. package/dist/server/server/messages.js.map +1 -0
  248. package/dist/server/server/package-version.d.ts +13 -0
  249. package/dist/server/server/package-version.d.ts.map +1 -0
  250. package/dist/server/server/package-version.js +47 -0
  251. package/dist/server/server/package-version.js.map +1 -0
  252. package/dist/server/server/path-utils.d.ts +3 -0
  253. package/dist/server/server/path-utils.d.ts.map +1 -0
  254. package/dist/server/server/path-utils.js +20 -0
  255. package/dist/server/server/path-utils.js.map +1 -0
  256. package/dist/server/server/persisted-config.d.ts +270 -0
  257. package/dist/server/server/persisted-config.d.ts.map +1 -0
  258. package/dist/server/server/persisted-config.js +152 -0
  259. package/dist/server/server/persisted-config.js.map +1 -0
  260. package/dist/server/server/persistence-hooks.d.ts +30 -0
  261. package/dist/server/server/persistence-hooks.d.ts.map +1 -0
  262. package/dist/server/server/persistence-hooks.js +68 -0
  263. package/dist/server/server/persistence-hooks.js.map +1 -0
  264. package/dist/server/server/pid-lock.d.ts +26 -0
  265. package/dist/server/server/pid-lock.d.ts.map +1 -0
  266. package/dist/server/server/pid-lock.js +280 -0
  267. package/dist/server/server/pid-lock.js.map +1 -0
  268. package/dist/server/server/relay-transport.d.ts +23 -0
  269. package/dist/server/server/relay-transport.d.ts.map +1 -0
  270. package/dist/server/server/relay-transport.js +457 -0
  271. package/dist/server/server/relay-transport.js.map +1 -0
  272. package/dist/server/server/server-id.d.ts +17 -0
  273. package/dist/server/server/server-id.d.ts.map +1 -0
  274. package/dist/server/server/server-id.js +63 -0
  275. package/dist/server/server/server-id.js.map +1 -0
  276. package/dist/server/server/session.d.ts +280 -0
  277. package/dist/server/server/session.d.ts.map +1 -0
  278. package/dist/server/server/session.js +4395 -0
  279. package/dist/server/server/session.js.map +1 -0
  280. package/dist/server/server/terminal-mcp/index.d.ts +4 -0
  281. package/dist/server/server/terminal-mcp/index.d.ts.map +1 -0
  282. package/dist/server/server/terminal-mcp/index.js +3 -0
  283. package/dist/server/server/terminal-mcp/index.js.map +1 -0
  284. package/dist/server/server/terminal-mcp/server.d.ts +10 -0
  285. package/dist/server/server/terminal-mcp/server.d.ts.map +1 -0
  286. package/dist/server/server/terminal-mcp/server.js +217 -0
  287. package/dist/server/server/terminal-mcp/server.js.map +1 -0
  288. package/dist/server/server/terminal-mcp/terminal-manager.d.ts +123 -0
  289. package/dist/server/server/terminal-mcp/terminal-manager.d.ts.map +1 -0
  290. package/dist/server/server/terminal-mcp/terminal-manager.js +351 -0
  291. package/dist/server/server/terminal-mcp/terminal-manager.js.map +1 -0
  292. package/dist/server/server/terminal-mcp/tmux.d.ts +207 -0
  293. package/dist/server/server/terminal-mcp/tmux.d.ts.map +1 -0
  294. package/dist/server/server/terminal-mcp/tmux.js +924 -0
  295. package/dist/server/server/terminal-mcp/tmux.js.map +1 -0
  296. package/dist/server/server/types.d.ts +5 -0
  297. package/dist/server/server/types.d.ts.map +1 -0
  298. package/dist/server/server/types.js +3 -0
  299. package/dist/server/server/types.js.map +1 -0
  300. package/dist/server/server/utils/diff-highlighter.d.ts +52 -0
  301. package/dist/server/server/utils/diff-highlighter.d.ts.map +1 -0
  302. package/dist/server/server/utils/diff-highlighter.js +244 -0
  303. package/dist/server/server/utils/diff-highlighter.js.map +1 -0
  304. package/dist/server/server/utils/syntax-highlighter.d.ts +10 -0
  305. package/dist/server/server/utils/syntax-highlighter.d.ts.map +1 -0
  306. package/dist/server/server/utils/syntax-highlighter.js +145 -0
  307. package/dist/server/server/utils/syntax-highlighter.js.map +1 -0
  308. package/dist/server/server/websocket-server.d.ts +79 -0
  309. package/dist/server/server/websocket-server.d.ts.map +1 -0
  310. package/dist/server/server/websocket-server.js +742 -0
  311. package/dist/server/server/websocket-server.js.map +1 -0
  312. package/dist/server/server/worktree-bootstrap.d.ts +29 -0
  313. package/dist/server/server/worktree-bootstrap.d.ts.map +1 -0
  314. package/dist/server/server/worktree-bootstrap.js +454 -0
  315. package/dist/server/server/worktree-bootstrap.js.map +1 -0
  316. package/dist/server/shared/agent-attention-notification.d.ts +40 -0
  317. package/dist/server/shared/agent-attention-notification.d.ts.map +1 -0
  318. package/dist/server/shared/agent-attention-notification.js +130 -0
  319. package/dist/server/shared/agent-attention-notification.js.map +1 -0
  320. package/dist/server/shared/agent-lifecycle.d.ts +3 -0
  321. package/dist/server/shared/agent-lifecycle.d.ts.map +1 -0
  322. package/dist/server/shared/agent-lifecycle.js +8 -0
  323. package/dist/server/shared/agent-lifecycle.js.map +1 -0
  324. package/dist/server/shared/binary-mux.d.ts +31 -0
  325. package/dist/server/shared/binary-mux.d.ts.map +1 -0
  326. package/dist/server/shared/binary-mux.js +114 -0
  327. package/dist/server/shared/binary-mux.js.map +1 -0
  328. package/dist/server/shared/connection-offer.d.ts +62 -0
  329. package/dist/server/shared/connection-offer.d.ts.map +1 -0
  330. package/dist/server/shared/connection-offer.js +17 -0
  331. package/dist/server/shared/connection-offer.js.map +1 -0
  332. package/dist/server/shared/daemon-endpoints.d.ts +27 -0
  333. package/dist/server/shared/daemon-endpoints.d.ts.map +1 -0
  334. package/dist/server/shared/daemon-endpoints.js +113 -0
  335. package/dist/server/shared/daemon-endpoints.js.map +1 -0
  336. package/dist/server/shared/messages.d.ts +36982 -0
  337. package/dist/server/shared/messages.d.ts.map +1 -0
  338. package/dist/server/shared/messages.js +1793 -0
  339. package/dist/server/shared/messages.js.map +1 -0
  340. package/dist/server/shared/path-utils.d.ts +2 -0
  341. package/dist/server/shared/path-utils.d.ts.map +1 -0
  342. package/dist/server/shared/path-utils.js +16 -0
  343. package/dist/server/shared/path-utils.js.map +1 -0
  344. package/dist/server/shared/terminal-key-input.d.ts +9 -0
  345. package/dist/server/shared/terminal-key-input.d.ts.map +1 -0
  346. package/dist/server/shared/terminal-key-input.js +132 -0
  347. package/dist/server/shared/terminal-key-input.js.map +1 -0
  348. package/dist/server/shared/tool-call-display.d.ts +11 -0
  349. package/dist/server/shared/tool-call-display.d.ts.map +1 -0
  350. package/dist/server/shared/tool-call-display.js +102 -0
  351. package/dist/server/shared/tool-call-display.js.map +1 -0
  352. package/dist/server/shared/tool-call-interpretation.d.ts +14 -0
  353. package/dist/server/shared/tool-call-interpretation.d.ts.map +1 -0
  354. package/dist/server/shared/tool-call-interpretation.js +76 -0
  355. package/dist/server/shared/tool-call-interpretation.js.map +1 -0
  356. package/dist/server/terminal/terminal-manager.d.ts +30 -0
  357. package/dist/server/terminal/terminal-manager.d.ts.map +1 -0
  358. package/dist/server/terminal/terminal-manager.js +148 -0
  359. package/dist/server/terminal/terminal-manager.js.map +1 -0
  360. package/dist/server/terminal/terminal.d.ts +93 -0
  361. package/dist/server/terminal/terminal.d.ts.map +1 -0
  362. package/dist/server/terminal/terminal.js +386 -0
  363. package/dist/server/terminal/terminal.js.map +1 -0
  364. package/dist/server/utils/checkout-git.d.ts +150 -0
  365. package/dist/server/utils/checkout-git.d.ts.map +1 -0
  366. package/dist/server/utils/checkout-git.js +1427 -0
  367. package/dist/server/utils/checkout-git.js.map +1 -0
  368. package/dist/server/utils/city-names.d.ts +17 -0
  369. package/dist/server/utils/city-names.d.ts.map +1 -0
  370. package/dist/server/utils/city-names.js +122 -0
  371. package/dist/server/utils/city-names.js.map +1 -0
  372. package/dist/server/utils/directory-suggestions.d.ts +31 -0
  373. package/dist/server/utils/directory-suggestions.d.ts.map +1 -0
  374. package/dist/server/utils/directory-suggestions.js +678 -0
  375. package/dist/server/utils/directory-suggestions.js.map +1 -0
  376. package/dist/server/utils/git-clone.d.ts +10 -0
  377. package/dist/server/utils/git-clone.d.ts.map +1 -0
  378. package/dist/server/utils/git-clone.js +45 -0
  379. package/dist/server/utils/git-clone.js.map +1 -0
  380. package/dist/server/utils/git-init.d.ts +9 -0
  381. package/dist/server/utils/git-init.d.ts.map +1 -0
  382. package/dist/server/utils/git-init.js +91 -0
  383. package/dist/server/utils/git-init.js.map +1 -0
  384. package/dist/server/utils/path.d.ts +5 -0
  385. package/dist/server/utils/path.d.ts.map +1 -0
  386. package/dist/server/utils/path.js +15 -0
  387. package/dist/server/utils/path.js.map +1 -0
  388. package/dist/server/utils/project-icon.d.ts +39 -0
  389. package/dist/server/utils/project-icon.d.ts.map +1 -0
  390. package/dist/server/utils/project-icon.js +391 -0
  391. package/dist/server/utils/project-icon.js.map +1 -0
  392. package/dist/server/utils/worktree-metadata.d.ts +47 -0
  393. package/dist/server/utils/worktree-metadata.d.ts.map +1 -0
  394. package/dist/server/utils/worktree-metadata.js +116 -0
  395. package/dist/server/utils/worktree-metadata.js.map +1 -0
  396. package/dist/server/utils/worktree.d.ts +175 -0
  397. package/dist/server/utils/worktree.d.ts.map +1 -0
  398. package/dist/server/utils/worktree.js +858 -0
  399. package/dist/server/utils/worktree.js.map +1 -0
  400. package/package.json +107 -0
@@ -0,0 +1,858 @@
1
+ import { exec, spawn } from "child_process";
2
+ import { promisify } from "util";
3
+ import { appendFileSync, existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, rmSync } from "fs";
4
+ import { join, basename, dirname, resolve, sep } from "path";
5
+ import net from "node:net";
6
+ import { createHash } from "node:crypto";
7
+ import { createNameId } from "mnemonic-id";
8
+ import { normalizeBaseRefName, readJunctionWorktreeMetadata, readJunctionWorktreeRuntimePort, writeJunctionWorktreeMetadata, writeJunctionWorktreeRuntimeMetadata, } from "./worktree-metadata.js";
9
+ import { generateCitySlug } from "./city-names.js";
10
+ const execAsync = promisify(exec);
11
+ const READ_ONLY_GIT_ENV = {
12
+ ...process.env,
13
+ GIT_OPTIONAL_LOCKS: "0",
14
+ };
15
+ export class WorktreeSetupError extends Error {
16
+ constructor(message, results) {
17
+ super(message);
18
+ this.name = "WorktreeSetupError";
19
+ this.results = results;
20
+ }
21
+ }
22
+ export class WorktreeDestroyError extends Error {
23
+ constructor(message, results) {
24
+ super(message);
25
+ this.name = "WorktreeDestroyError";
26
+ this.results = results;
27
+ }
28
+ }
29
+ function readJunctionConfig(repoRoot) {
30
+ const junctionConfigPath = join(repoRoot, "junction.json");
31
+ if (!existsSync(junctionConfigPath)) {
32
+ return null;
33
+ }
34
+ try {
35
+ return JSON.parse(readFileSync(junctionConfigPath, "utf8"));
36
+ }
37
+ catch {
38
+ throw new Error(`Failed to parse junction.json`);
39
+ }
40
+ }
41
+ export function getWorktreeSetupCommands(repoRoot) {
42
+ const config = readJunctionConfig(repoRoot);
43
+ const setupCommands = config?.worktree?.setup;
44
+ if (!setupCommands || setupCommands.length === 0) {
45
+ return [];
46
+ }
47
+ return setupCommands.filter((cmd) => typeof cmd === "string" && cmd.trim().length > 0);
48
+ }
49
+ export function getWorktreeDestroyCommands(repoRoot) {
50
+ const config = readJunctionConfig(repoRoot);
51
+ const destroyCommands = config?.worktree?.destroy;
52
+ if (!destroyCommands || destroyCommands.length === 0) {
53
+ return [];
54
+ }
55
+ return destroyCommands.filter((cmd) => typeof cmd === "string" && cmd.trim().length > 0);
56
+ }
57
+ export function getWorktreeTerminalSpecs(repoRoot) {
58
+ const config = readJunctionConfig(repoRoot);
59
+ const terminals = config?.worktree?.terminals;
60
+ if (!Array.isArray(terminals) || terminals.length === 0) {
61
+ return [];
62
+ }
63
+ const specs = [];
64
+ for (const terminal of terminals) {
65
+ if (!terminal || typeof terminal !== "object") {
66
+ continue;
67
+ }
68
+ const rawCommand = terminal.command;
69
+ if (typeof rawCommand !== "string") {
70
+ continue;
71
+ }
72
+ const command = rawCommand.trim();
73
+ if (!command) {
74
+ continue;
75
+ }
76
+ const rawName = terminal.name;
77
+ const name = typeof rawName === "string" && rawName.trim().length > 0
78
+ ? rawName.trim()
79
+ : undefined;
80
+ specs.push({
81
+ ...(name ? { name } : {}),
82
+ command,
83
+ });
84
+ }
85
+ return specs;
86
+ }
87
+ async function execSetupCommand(command, options) {
88
+ const startedAt = Date.now();
89
+ try {
90
+ const { stdout, stderr } = await execAsync(command, {
91
+ cwd: options.cwd,
92
+ env: options.env,
93
+ shell: "/bin/bash",
94
+ });
95
+ return {
96
+ command,
97
+ cwd: options.cwd,
98
+ stdout: stdout ?? "",
99
+ stderr: stderr ?? "",
100
+ exitCode: 0,
101
+ durationMs: Date.now() - startedAt,
102
+ };
103
+ }
104
+ catch (error) {
105
+ return {
106
+ command,
107
+ cwd: options.cwd,
108
+ stdout: error?.stdout ?? "",
109
+ stderr: error?.stderr ??
110
+ (error instanceof Error ? error.message : String(error)),
111
+ exitCode: typeof error?.code === "number" ? error.code : null,
112
+ durationMs: Date.now() - startedAt,
113
+ };
114
+ }
115
+ }
116
+ async function execSetupCommandStreamed(options) {
117
+ return new Promise((resolve) => {
118
+ const startedAt = Date.now();
119
+ const stdoutChunks = [];
120
+ const stderrChunks = [];
121
+ let settled = false;
122
+ const finish = (exitCode) => {
123
+ if (settled) {
124
+ return;
125
+ }
126
+ settled = true;
127
+ const result = {
128
+ command: options.command,
129
+ cwd: options.cwd,
130
+ stdout: stdoutChunks.join(""),
131
+ stderr: stderrChunks.join(""),
132
+ exitCode,
133
+ durationMs: Date.now() - startedAt,
134
+ };
135
+ options.onEvent?.({
136
+ type: "command_completed",
137
+ index: options.index,
138
+ total: options.total,
139
+ command: options.command,
140
+ cwd: options.cwd,
141
+ exitCode: result.exitCode,
142
+ durationMs: result.durationMs,
143
+ stdout: result.stdout,
144
+ stderr: result.stderr,
145
+ });
146
+ resolve(result);
147
+ };
148
+ options.onEvent?.({
149
+ type: "command_started",
150
+ index: options.index,
151
+ total: options.total,
152
+ command: options.command,
153
+ cwd: options.cwd,
154
+ });
155
+ const child = spawn("/bin/bash", ["-lc", options.command], {
156
+ cwd: options.cwd,
157
+ env: options.env,
158
+ stdio: ["ignore", "pipe", "pipe"],
159
+ });
160
+ child.stdout?.on("data", (chunk) => {
161
+ const text = chunk.toString();
162
+ stdoutChunks.push(text);
163
+ options.onEvent?.({
164
+ type: "output",
165
+ index: options.index,
166
+ total: options.total,
167
+ command: options.command,
168
+ cwd: options.cwd,
169
+ stream: "stdout",
170
+ chunk: text,
171
+ });
172
+ });
173
+ child.stderr?.on("data", (chunk) => {
174
+ const text = chunk.toString();
175
+ stderrChunks.push(text);
176
+ options.onEvent?.({
177
+ type: "output",
178
+ index: options.index,
179
+ total: options.total,
180
+ command: options.command,
181
+ cwd: options.cwd,
182
+ stream: "stderr",
183
+ chunk: text,
184
+ });
185
+ });
186
+ child.on("error", (error) => {
187
+ stderrChunks.push(error instanceof Error ? error.message : String(error));
188
+ finish(null);
189
+ });
190
+ child.on("close", (code) => {
191
+ finish(typeof code === "number" ? code : null);
192
+ });
193
+ });
194
+ }
195
+ async function getAvailablePort() {
196
+ return new Promise((resolve, reject) => {
197
+ const server = net.createServer();
198
+ server.once("error", reject);
199
+ server.listen(0, () => {
200
+ const address = server.address();
201
+ if (!address || typeof address === "string") {
202
+ server.close(() => reject(new Error("Failed to acquire available port")));
203
+ return;
204
+ }
205
+ server.close((error) => {
206
+ if (error) {
207
+ reject(error);
208
+ return;
209
+ }
210
+ resolve(address.port);
211
+ });
212
+ });
213
+ });
214
+ }
215
+ async function assertPortAvailable(port) {
216
+ await new Promise((resolve, reject) => {
217
+ const server = net.createServer();
218
+ server.once("error", (error) => {
219
+ const message = error?.code === "EADDRINUSE"
220
+ ? `Persisted worktree port ${port} is already in use`
221
+ : error instanceof Error
222
+ ? error.message
223
+ : String(error);
224
+ reject(new Error(message));
225
+ });
226
+ server.listen(port, () => {
227
+ server.close((error) => {
228
+ if (error) {
229
+ reject(error);
230
+ return;
231
+ }
232
+ resolve();
233
+ });
234
+ });
235
+ });
236
+ }
237
+ async function inferRepoRootPathFromWorktreePath(worktreePath) {
238
+ try {
239
+ const commonDir = await getGitCommonDir(worktreePath);
240
+ const normalizedCommonDir = normalizePathForOwnership(commonDir);
241
+ // Normal repo/worktree: common dir is <repoRoot>/.git
242
+ if (basename(normalizedCommonDir) === ".git") {
243
+ return dirname(normalizedCommonDir);
244
+ }
245
+ // Bare repo: common dir is the repo dir itself
246
+ return normalizedCommonDir;
247
+ }
248
+ catch {
249
+ // Fallback: best-effort resolve toplevel (will be the worktree root in typical cases)
250
+ try {
251
+ const { stdout } = await execAsync("git rev-parse --path-format=absolute --show-toplevel", { cwd: worktreePath, env: READ_ONLY_GIT_ENV });
252
+ const topLevel = stdout.trim();
253
+ if (topLevel) {
254
+ return normalizePathForOwnership(topLevel);
255
+ }
256
+ }
257
+ catch {
258
+ // ignore
259
+ }
260
+ return normalizePathForOwnership(worktreePath);
261
+ }
262
+ }
263
+ export async function runWorktreeSetupCommands(options) {
264
+ // Read junction.json from the worktree (it will have the same content as the source repo)
265
+ const setupCommands = getWorktreeSetupCommands(options.worktreePath);
266
+ if (setupCommands.length === 0) {
267
+ return [];
268
+ }
269
+ const runtimeEnv = options.runtimeEnv ??
270
+ (await resolveWorktreeRuntimeEnv({
271
+ worktreePath: options.worktreePath,
272
+ branchName: options.branchName,
273
+ ...(options.repoRootPath ? { repoRootPath: options.repoRootPath } : {}),
274
+ }));
275
+ const setupEnv = {
276
+ ...process.env,
277
+ ...runtimeEnv,
278
+ };
279
+ const results = [];
280
+ for (const [index, cmd] of setupCommands.entries()) {
281
+ const result = options.onEvent
282
+ ? await execSetupCommandStreamed({
283
+ command: cmd,
284
+ cwd: options.worktreePath,
285
+ env: setupEnv,
286
+ index: index + 1,
287
+ total: setupCommands.length,
288
+ onEvent: options.onEvent,
289
+ })
290
+ : await execSetupCommand(cmd, {
291
+ cwd: options.worktreePath,
292
+ env: setupEnv,
293
+ });
294
+ results.push(result);
295
+ if (result.exitCode !== 0) {
296
+ if (options.cleanupOnFailure) {
297
+ try {
298
+ await execAsync(`git worktree remove "${options.worktreePath}" --force`, {
299
+ cwd: options.worktreePath,
300
+ });
301
+ }
302
+ catch {
303
+ rmSync(options.worktreePath, { recursive: true, force: true });
304
+ }
305
+ }
306
+ throw new WorktreeSetupError(`Worktree setup command failed: ${cmd}\n${result.stderr}`.trim(), results);
307
+ }
308
+ }
309
+ return results;
310
+ }
311
+ async function resolveBranchNameForWorktreePath(worktreePath) {
312
+ try {
313
+ const { stdout } = await execAsync("git branch --show-current", {
314
+ cwd: worktreePath,
315
+ env: READ_ONLY_GIT_ENV,
316
+ });
317
+ const branchName = stdout.trim();
318
+ if (branchName.length > 0) {
319
+ return branchName;
320
+ }
321
+ }
322
+ catch {
323
+ // ignore
324
+ }
325
+ return basename(worktreePath);
326
+ }
327
+ export async function resolveWorktreeRuntimeEnv(options) {
328
+ const repoRootPath = options.repoRootPath ?? (await inferRepoRootPathFromWorktreePath(options.worktreePath));
329
+ const branchName = options.branchName ?? (await resolveBranchNameForWorktreePath(options.worktreePath));
330
+ let worktreePort = readJunctionWorktreeRuntimePort(options.worktreePath);
331
+ if (worktreePort === null) {
332
+ worktreePort = await getAvailablePort();
333
+ const metadata = readJunctionWorktreeMetadata(options.worktreePath);
334
+ if (metadata) {
335
+ writeJunctionWorktreeRuntimeMetadata(options.worktreePath, { worktreePort });
336
+ }
337
+ }
338
+ else {
339
+ await assertPortAvailable(worktreePort);
340
+ }
341
+ return {
342
+ // Source checkout path is the original git repo root (shared across worktrees), not the
343
+ // worktree itself. This allows setup scripts to copy local files (e.g. .env) from the
344
+ // source checkout.
345
+ JUNCTION_SOURCE_CHECKOUT_PATH: repoRootPath,
346
+ // Backward-compatible alias.
347
+ JUNCTION_ROOT_PATH: repoRootPath,
348
+ JUNCTION_WORKTREE_PATH: options.worktreePath,
349
+ JUNCTION_BRANCH_NAME: branchName,
350
+ JUNCTION_WORKTREE_PORT: String(worktreePort),
351
+ };
352
+ }
353
+ export async function runWorktreeDestroyCommands(options) {
354
+ // Read junction.json from the worktree (it will have the same content as the source repo)
355
+ const destroyCommands = getWorktreeDestroyCommands(options.worktreePath);
356
+ if (destroyCommands.length === 0) {
357
+ return [];
358
+ }
359
+ const repoRootPath = options.repoRootPath ?? (await inferRepoRootPathFromWorktreePath(options.worktreePath));
360
+ const branchName = options.branchName ?? (await resolveBranchNameForWorktreePath(options.worktreePath));
361
+ const destroyEnv = {
362
+ ...process.env,
363
+ // Source checkout path is the original git repo root (shared across worktrees), not the
364
+ // worktree itself. This allows destroy scripts to clean resources using paths from the
365
+ // source checkout.
366
+ JUNCTION_SOURCE_CHECKOUT_PATH: repoRootPath,
367
+ // Backward-compatible alias.
368
+ JUNCTION_ROOT_PATH: repoRootPath,
369
+ JUNCTION_WORKTREE_PATH: options.worktreePath,
370
+ JUNCTION_BRANCH_NAME: branchName,
371
+ };
372
+ const results = [];
373
+ for (const cmd of destroyCommands) {
374
+ const result = await execSetupCommand(cmd, {
375
+ cwd: options.worktreePath,
376
+ env: destroyEnv,
377
+ });
378
+ results.push(result);
379
+ if (result.exitCode !== 0) {
380
+ throw new WorktreeDestroyError(`Worktree destroy command failed: ${cmd}\n${result.stderr}`.trim(), results);
381
+ }
382
+ }
383
+ return results;
384
+ }
385
+ /**
386
+ * Get the git common directory (shared across worktrees) for a given cwd.
387
+ * This is where refs, objects, etc. are stored.
388
+ */
389
+ export async function getGitCommonDir(cwd) {
390
+ const { stdout } = await execAsync("git rev-parse --path-format=absolute --git-common-dir", { cwd, env: READ_ONLY_GIT_ENV });
391
+ const commonDir = stdout.trim();
392
+ if (!commonDir) {
393
+ throw new Error("Not in a git repository");
394
+ }
395
+ return commonDir;
396
+ }
397
+ /**
398
+ * Validate that a string is a valid git branch name slug
399
+ * Must be lowercase, alphanumeric, hyphens only
400
+ */
401
+ export function validateBranchSlug(slug) {
402
+ if (!slug || slug.length === 0) {
403
+ return { valid: false, error: "Branch name cannot be empty" };
404
+ }
405
+ if (slug.length > 100) {
406
+ return { valid: false, error: "Branch name too long (max 100 characters)" };
407
+ }
408
+ // Check for valid characters: lowercase letters, numbers, hyphens, forward slashes
409
+ const validPattern = /^[a-z0-9-/]+$/;
410
+ if (!validPattern.test(slug)) {
411
+ return {
412
+ valid: false,
413
+ error: "Branch name must contain only lowercase letters, numbers, hyphens, and forward slashes",
414
+ };
415
+ }
416
+ // Cannot start or end with hyphen
417
+ if (slug.startsWith("-") || slug.endsWith("-")) {
418
+ return {
419
+ valid: false,
420
+ error: "Branch name cannot start or end with a hyphen",
421
+ };
422
+ }
423
+ // Cannot have consecutive hyphens
424
+ if (slug.includes("--")) {
425
+ return { valid: false, error: "Branch name cannot have consecutive hyphens" };
426
+ }
427
+ return { valid: true };
428
+ }
429
+ const MAX_SLUG_LENGTH = 50;
430
+ /**
431
+ * Convert string to kebab-case for branch names
432
+ */
433
+ export function slugify(input) {
434
+ const slug = input
435
+ .toLowerCase()
436
+ .replace(/[^a-z0-9]+/g, "-")
437
+ .replace(/^-+|-+$/g, "");
438
+ if (slug.length <= MAX_SLUG_LENGTH) {
439
+ return slug;
440
+ }
441
+ // Truncate at word boundary (hyphen) if possible
442
+ const truncated = slug.slice(0, MAX_SLUG_LENGTH);
443
+ const lastHyphen = truncated.lastIndexOf("-");
444
+ if (lastHyphen > MAX_SLUG_LENGTH / 2) {
445
+ return truncated.slice(0, lastHyphen);
446
+ }
447
+ return truncated.replace(/-+$/, "");
448
+ }
449
+ function generateWorktreeSlug() {
450
+ return createNameId();
451
+ }
452
+ const WORKTREE_PROJECT_HASH_LENGTH = 8;
453
+ function deriveShortAlphanumericHash(value) {
454
+ const digest = createHash("sha256").update(value).digest();
455
+ let hashValue = 0n;
456
+ for (let index = 0; index < 8; index += 1) {
457
+ hashValue = (hashValue << 8n) | BigInt(digest[index] ?? 0);
458
+ }
459
+ return hashValue
460
+ .toString(36)
461
+ .padStart(13, "0")
462
+ .slice(0, WORKTREE_PROJECT_HASH_LENGTH);
463
+ }
464
+ export async function deriveWorktreeProjectHash(cwd) {
465
+ try {
466
+ const commonDir = await getGitCommonDir(cwd);
467
+ const normalizedCommonDir = normalizePathForOwnership(commonDir);
468
+ const repoRoot = basename(normalizedCommonDir) === ".git"
469
+ ? dirname(normalizedCommonDir)
470
+ : normalizedCommonDir;
471
+ return deriveShortAlphanumericHash(repoRoot);
472
+ }
473
+ catch {
474
+ return deriveShortAlphanumericHash(normalizePathForOwnership(cwd));
475
+ }
476
+ }
477
+ async function getJunctionWorktreesRoot(cwd, _junctionHome) {
478
+ const gitCommonDir = await getGitCommonDir(cwd);
479
+ const repoRoot = resolveRepoRootFromGitCommonDir(gitCommonDir);
480
+ return getInRepoWorktreesRoot(repoRoot);
481
+ }
482
+ function normalizePathForOwnership(input) {
483
+ try {
484
+ return realpathSync(input);
485
+ }
486
+ catch {
487
+ return resolve(input);
488
+ }
489
+ }
490
+ function resolveRepoRootFromGitCommonDir(commonDir) {
491
+ const normalizedCommonDir = normalizePathForOwnership(commonDir);
492
+ return basename(normalizedCommonDir) === ".git"
493
+ ? dirname(normalizedCommonDir)
494
+ : normalizedCommonDir;
495
+ }
496
+ export async function isJunctionOwnedWorktreeCwd(cwd, options) {
497
+ let gitCommonDir;
498
+ try {
499
+ gitCommonDir = await getGitCommonDir(cwd);
500
+ }
501
+ catch {
502
+ return {
503
+ allowed: false,
504
+ worktreePath: normalizePathForOwnership(cwd),
505
+ };
506
+ }
507
+ const repoRoot = resolveRepoRootFromGitCommonDir(gitCommonDir);
508
+ const worktreesRoot = await getJunctionWorktreesRoot(cwd, options?.junctionHome);
509
+ const resolvedRoot = normalizePathForOwnership(worktreesRoot) + sep;
510
+ const resolvedCwd = normalizePathForOwnership(cwd);
511
+ if (!resolvedCwd.startsWith(resolvedRoot)) {
512
+ return {
513
+ allowed: false,
514
+ repoRoot,
515
+ worktreeRoot: worktreesRoot,
516
+ worktreePath: resolvedCwd,
517
+ };
518
+ }
519
+ const worktrees = await listJunctionWorktrees({ cwd, junctionHome: options?.junctionHome });
520
+ const allowed = worktrees.some((entry) => {
521
+ const worktreePath = resolve(entry.path);
522
+ return resolvedCwd === worktreePath || resolvedCwd.startsWith(worktreePath + sep);
523
+ });
524
+ return {
525
+ allowed,
526
+ repoRoot,
527
+ worktreeRoot: worktreesRoot,
528
+ worktreePath: resolvedCwd,
529
+ };
530
+ }
531
+ function parseWorktreeList(output) {
532
+ const entries = [];
533
+ let current = null;
534
+ for (const line of output.split("\n")) {
535
+ if (line.startsWith("worktree ")) {
536
+ if (current?.path) {
537
+ entries.push(current);
538
+ }
539
+ current = { path: line.slice("worktree ".length).trim() };
540
+ continue;
541
+ }
542
+ if (!current) {
543
+ continue;
544
+ }
545
+ if (line.startsWith("branch ")) {
546
+ const ref = line.slice("branch ".length).trim();
547
+ current.branchName = ref.startsWith("refs/heads/")
548
+ ? ref.slice("refs/heads/".length)
549
+ : ref;
550
+ }
551
+ else if (line.startsWith("HEAD ")) {
552
+ current.head = line.slice("HEAD ".length).trim();
553
+ }
554
+ else if (line.trim().length === 0) {
555
+ if (current.path) {
556
+ entries.push(current);
557
+ }
558
+ current = null;
559
+ }
560
+ }
561
+ if (current?.path) {
562
+ entries.push(current);
563
+ }
564
+ return entries;
565
+ }
566
+ export async function listJunctionWorktrees({ cwd, junctionHome, }) {
567
+ const worktreesRoot = await getJunctionWorktreesRoot(cwd, junctionHome);
568
+ const { stdout } = await execAsync("git worktree list --porcelain", {
569
+ cwd,
570
+ env: READ_ONLY_GIT_ENV,
571
+ });
572
+ const rootPrefix = normalizePathForOwnership(worktreesRoot) + sep;
573
+ return parseWorktreeList(stdout)
574
+ .map((entry) => ({ ...entry, path: normalizePathForOwnership(entry.path) }))
575
+ .filter((entry) => entry.path.startsWith(rootPrefix));
576
+ }
577
+ export async function resolveJunctionWorktreeRootForCwd(cwd, options) {
578
+ let gitCommonDir;
579
+ try {
580
+ gitCommonDir = await getGitCommonDir(cwd);
581
+ }
582
+ catch {
583
+ return null;
584
+ }
585
+ const worktreesRoot = await getJunctionWorktreesRoot(cwd, options?.junctionHome);
586
+ const resolvedRoot = normalizePathForOwnership(worktreesRoot) + sep;
587
+ let worktreeRoot = null;
588
+ try {
589
+ const { stdout } = await execAsync("git rev-parse --path-format=absolute --show-toplevel", { cwd, env: READ_ONLY_GIT_ENV });
590
+ const trimmed = stdout.trim();
591
+ worktreeRoot = trimmed.length > 0 ? trimmed : null;
592
+ }
593
+ catch {
594
+ worktreeRoot = null;
595
+ }
596
+ if (!worktreeRoot) {
597
+ return null;
598
+ }
599
+ const resolvedWorktreeRoot = normalizePathForOwnership(worktreeRoot);
600
+ if (!resolvedWorktreeRoot.startsWith(resolvedRoot)) {
601
+ return null;
602
+ }
603
+ const knownWorktrees = await listJunctionWorktrees({
604
+ cwd,
605
+ junctionHome: options?.junctionHome,
606
+ });
607
+ const match = knownWorktrees.find((entry) => entry.path === resolvedWorktreeRoot);
608
+ if (!match) {
609
+ return null;
610
+ }
611
+ return {
612
+ repoRoot: resolveRepoRootFromGitCommonDir(gitCommonDir),
613
+ worktreeRoot: worktreesRoot,
614
+ worktreePath: match.path,
615
+ };
616
+ }
617
+ export async function deleteJunctionWorktree({ cwd, worktreePath, worktreeSlug, junctionHome, }) {
618
+ if (!worktreePath && !worktreeSlug) {
619
+ throw new Error("worktreePath or worktreeSlug is required");
620
+ }
621
+ const worktreesRoot = await getJunctionWorktreesRoot(cwd, junctionHome);
622
+ const resolvedRoot = normalizePathForOwnership(worktreesRoot) + sep;
623
+ const requestedPath = worktreePath ?? join(worktreesRoot, worktreeSlug);
624
+ const resolvedRequested = normalizePathForOwnership(requestedPath);
625
+ const resolvedWorktree = (await resolveJunctionWorktreeRootForCwd(requestedPath, { junctionHome }))?.worktreePath ??
626
+ resolvedRequested;
627
+ if (!resolvedWorktree.startsWith(resolvedRoot)) {
628
+ throw new Error("Refusing to delete non-Junction worktree");
629
+ }
630
+ await runWorktreeDestroyCommands({
631
+ worktreePath: resolvedWorktree,
632
+ });
633
+ await execAsync(`git worktree remove "${resolvedWorktree}" --force`, {
634
+ cwd,
635
+ });
636
+ if (existsSync(resolvedWorktree)) {
637
+ rmSync(resolvedWorktree, { recursive: true, force: true });
638
+ }
639
+ }
640
+ /**
641
+ * Create a git worktree with proper naming conventions
642
+ */
643
+ export async function createWorktree({ branchName, cwd, baseBranch, worktreeSlug, runSetup = true, junctionHome, }) {
644
+ // Validate branch name
645
+ const validation = validateBranchSlug(branchName);
646
+ if (!validation.valid) {
647
+ throw new Error(`Invalid branch name: ${validation.error}`);
648
+ }
649
+ const normalizedBaseBranch = baseBranch ? normalizeBaseRefName(baseBranch) : "";
650
+ if (!normalizedBaseBranch) {
651
+ throw new Error("Base branch is required when creating a Junction worktree");
652
+ }
653
+ if (normalizedBaseBranch === "HEAD") {
654
+ throw new Error("Base branch cannot be HEAD when creating a Junction worktree");
655
+ }
656
+ // Resolve the base branch - try local first, then remote
657
+ let resolvedBaseBranch = normalizedBaseBranch;
658
+ try {
659
+ await execAsync(`git rev-parse --verify ${normalizedBaseBranch}`, { cwd });
660
+ }
661
+ catch {
662
+ // Local branch doesn't exist, try remote (origin/{branch})
663
+ try {
664
+ await execAsync(`git rev-parse --verify origin/${normalizedBaseBranch}`, { cwd });
665
+ resolvedBaseBranch = `origin/${normalizedBaseBranch}`;
666
+ }
667
+ catch {
668
+ throw new Error(`Base branch not found: ${normalizedBaseBranch}`);
669
+ }
670
+ }
671
+ const repoRoot = resolveRepoRootFromGitCommonDir(await getGitCommonDir(cwd));
672
+ ensureJunctionGitignore(repoRoot);
673
+ let worktreePath;
674
+ const desiredSlug = worktreeSlug || generateWorktreeSlug();
675
+ worktreePath = join(await getJunctionWorktreesRoot(cwd, junctionHome), desiredSlug);
676
+ mkdirSync(dirname(worktreePath), { recursive: true });
677
+ // Check if branch already exists
678
+ let branchExists = false;
679
+ try {
680
+ await execAsync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd });
681
+ branchExists = true;
682
+ }
683
+ catch {
684
+ branchExists = false;
685
+ }
686
+ // Always create a new branch for the worktree
687
+ // If branchName already exists, use it as base and create worktree-slug as branch name
688
+ // If branchName doesn't exist, create it from baseBranch (resolved to remote if needed)
689
+ const base = branchExists ? branchName : resolvedBaseBranch;
690
+ const candidateBranch = branchExists ? desiredSlug : branchName;
691
+ // Find unique branch name if collision
692
+ let newBranchName = candidateBranch;
693
+ let suffix = 1;
694
+ while (true) {
695
+ try {
696
+ await execAsync(`git show-ref --verify --quiet refs/heads/${newBranchName}`, { cwd });
697
+ // Branch exists, try with suffix
698
+ newBranchName = `${candidateBranch}-${suffix}`;
699
+ suffix++;
700
+ }
701
+ catch {
702
+ break;
703
+ }
704
+ }
705
+ // Also handle worktree path collision
706
+ let finalWorktreePath = worktreePath;
707
+ let pathSuffix = 1;
708
+ while (existsSync(finalWorktreePath)) {
709
+ finalWorktreePath = `${worktreePath}-${pathSuffix}`;
710
+ pathSuffix++;
711
+ }
712
+ const command = `git worktree add "${finalWorktreePath}" -b "${newBranchName}" "${base}"`;
713
+ await execAsync(command, { cwd });
714
+ worktreePath = normalizePathForOwnership(finalWorktreePath);
715
+ writeJunctionWorktreeMetadata(worktreePath, { baseRefName: normalizedBaseBranch });
716
+ if (runSetup) {
717
+ await runWorktreeSetupCommands({
718
+ worktreePath,
719
+ branchName: newBranchName,
720
+ cleanupOnFailure: true,
721
+ });
722
+ }
723
+ return {
724
+ branchName: newBranchName,
725
+ worktreePath,
726
+ };
727
+ }
728
+ // ============================================================================
729
+ // In-repo worktree support (.junction/ directory)
730
+ // ============================================================================
731
+ const IN_REPO_WORKTREE_DIR = ".junction";
732
+ /**
733
+ * Get the root directory for in-repo worktrees: `{repoRoot}/.junction`
734
+ */
735
+ export function getInRepoWorktreesRoot(repoRoot) {
736
+ return join(repoRoot, IN_REPO_WORKTREE_DIR);
737
+ }
738
+ /**
739
+ * List existing worktree directory names inside `{repoRoot}/.junction/`.
740
+ */
741
+ export function listInRepoWorktreeNames(repoRoot) {
742
+ const root = getInRepoWorktreesRoot(repoRoot);
743
+ if (!existsSync(root)) {
744
+ return [];
745
+ }
746
+ try {
747
+ return readdirSync(root, { withFileTypes: true })
748
+ .filter((entry) => entry.isDirectory())
749
+ .map((entry) => entry.name);
750
+ }
751
+ catch {
752
+ return [];
753
+ }
754
+ }
755
+ /**
756
+ * Ensure `.junction/` is ignored without mutating tracked files.
757
+ *
758
+ * Preference order:
759
+ * 1) If repo `.gitignore` already covers `.junction/`, do nothing.
760
+ * 2) Otherwise, append to `.git/info/exclude` (local-only ignore rules).
761
+ */
762
+ export function ensureJunctionGitignore(repoRoot) {
763
+ const gitignorePath = join(repoRoot, ".gitignore");
764
+ const gitInfoExcludePath = join(repoRoot, ".git", "info", "exclude");
765
+ const entry = `/${IN_REPO_WORKTREE_DIR}/`;
766
+ const normalizedEntries = new Set([
767
+ entry,
768
+ IN_REPO_WORKTREE_DIR,
769
+ `${IN_REPO_WORKTREE_DIR}/`,
770
+ `/${IN_REPO_WORKTREE_DIR}`,
771
+ ]);
772
+ if (existsSync(gitignorePath)) {
773
+ const content = readFileSync(gitignorePath, "utf8");
774
+ const lines = content.split("\n").map((l) => l.trim());
775
+ if (lines.some((line) => normalizedEntries.has(line))) {
776
+ return;
777
+ }
778
+ }
779
+ const excludeDir = dirname(gitInfoExcludePath);
780
+ mkdirSync(excludeDir, { recursive: true });
781
+ const existingExclude = existsSync(gitInfoExcludePath)
782
+ ? readFileSync(gitInfoExcludePath, "utf8")
783
+ : "";
784
+ const excludeLines = existingExclude.split("\n").map((line) => line.trim());
785
+ if (excludeLines.some((line) => normalizedEntries.has(line))) {
786
+ return;
787
+ }
788
+ const prefix = existingExclude.length > 0 && !existingExclude.endsWith("\n") ? "\n" : "";
789
+ appendFileSync(gitInfoExcludePath, `${prefix}${entry}\n`, "utf8");
790
+ }
791
+ /**
792
+ * Check whether a path is already inside an in-repo `.junction/` worktree.
793
+ */
794
+ export function isInsideInRepoWorktree(cwd) {
795
+ const parts = cwd.split(sep);
796
+ return parts.includes(IN_REPO_WORKTREE_DIR);
797
+ }
798
+ /**
799
+ * Create an in-repo worktree inside `{repoRoot}/.junction/{citySlug}`.
800
+ *
801
+ * The branch name is set to the city slug (e.g. "tokyo", "tokyo-v2").
802
+ */
803
+ export async function createInRepoWorktree({ repoRoot, baseBranch, runSetup = false, }) {
804
+ ensureJunctionGitignore(repoRoot);
805
+ const existingNames = listInRepoWorktreeNames(repoRoot);
806
+ const slug = generateCitySlug(existingNames);
807
+ const worktreesRoot = getInRepoWorktreesRoot(repoRoot);
808
+ mkdirSync(worktreesRoot, { recursive: true });
809
+ const worktreePath = join(worktreesRoot, slug);
810
+ const normalizedBaseBranch = normalizeBaseRefName(baseBranch);
811
+ if (!normalizedBaseBranch) {
812
+ throw new Error("Base branch is required when creating an in-repo worktree");
813
+ }
814
+ // Resolve the base branch - try local first, then remote
815
+ let resolvedBaseBranch = normalizedBaseBranch;
816
+ try {
817
+ await execAsync(`git rev-parse --verify ${normalizedBaseBranch}`, { cwd: repoRoot });
818
+ }
819
+ catch {
820
+ try {
821
+ await execAsync(`git rev-parse --verify origin/${normalizedBaseBranch}`, { cwd: repoRoot });
822
+ resolvedBaseBranch = `origin/${normalizedBaseBranch}`;
823
+ }
824
+ catch {
825
+ throw new Error(`Base branch not found: ${normalizedBaseBranch}`);
826
+ }
827
+ }
828
+ // Find a unique branch name (city slug may collide with existing branches)
829
+ let branchName = slug;
830
+ let branchSuffix = 2;
831
+ while (true) {
832
+ try {
833
+ await execAsync(`git show-ref --verify --quiet refs/heads/${branchName}`, { cwd: repoRoot });
834
+ branchName = `${slug}-b${branchSuffix}`;
835
+ branchSuffix++;
836
+ }
837
+ catch {
838
+ break;
839
+ }
840
+ }
841
+ const command = `git worktree add "${worktreePath}" -b "${branchName}" "${resolvedBaseBranch}"`;
842
+ await execAsync(command, { cwd: repoRoot });
843
+ const resolvedWorktreePath = normalizePathForOwnership(worktreePath);
844
+ writeJunctionWorktreeMetadata(resolvedWorktreePath, { baseRefName: normalizedBaseBranch });
845
+ if (runSetup) {
846
+ await runWorktreeSetupCommands({
847
+ worktreePath: resolvedWorktreePath,
848
+ branchName,
849
+ cleanupOnFailure: true,
850
+ });
851
+ }
852
+ return {
853
+ branchName,
854
+ worktreePath: resolvedWorktreePath,
855
+ slug,
856
+ };
857
+ }
858
+ //# sourceMappingURL=worktree.js.map