mixdog 1.1.2 → 1.2.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 (288) hide show
  1. package/README.md +1 -0
  2. package/package.json +2 -1
  3. package/scripts/lib/run-node-tests.mjs +9 -0
  4. package/scripts/prune-desktop-runtime.mjs +3 -0
  5. package/scripts/test-direct.mjs +4 -2
  6. package/src/defaults/shipped-history.json +6 -0
  7. package/src/defaults/skills/setup/references/actions.md +5 -2
  8. package/src/defaults/skills/visualize/SKILL.md +157 -0
  9. package/src/defaults/skills/visualize/references/craft.md +88 -0
  10. package/src/headless-command.mjs +56 -3
  11. package/src/headless-exec.mjs +17 -1
  12. package/src/help.mjs +2 -0
  13. package/src/rules/shared/70-delivery.md +2 -2
  14. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/preset.mjs +1 -0
  15. package/src/runtime/agent/orchestrator/agent-runtime/maintenance-route.mjs +1 -0
  16. package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +2 -0
  17. package/src/runtime/agent/orchestrator/config-presets.mjs +2 -0
  18. package/src/runtime/agent/orchestrator/config-providers-merge.mjs +17 -11
  19. package/src/runtime/agent/orchestrator/config-storage.mjs +1 -0
  20. package/src/runtime/agent/orchestrator/config.mjs +9 -1
  21. package/src/runtime/agent/orchestrator/mcp/client-config.mjs +47 -7
  22. package/src/runtime/agent/orchestrator/mcp/client-handshake.mjs +51 -8
  23. package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +25 -5
  24. package/src/runtime/agent/orchestrator/mcp/client.mjs +348 -122
  25. package/src/runtime/agent/orchestrator/mcp/reconnect-singleflight.mjs +4 -0
  26. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +3 -0
  27. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +15 -1
  28. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +13 -1
  29. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-tokens.mjs +2 -0
  30. package/src/runtime/agent/orchestrator/providers/cursor-auth.mjs +2 -0
  31. package/src/runtime/agent/orchestrator/providers/cursor-wire-normalization.mjs +2 -1
  32. package/src/runtime/agent/orchestrator/providers/effort-configuration.mjs +12 -4
  33. package/src/runtime/agent/orchestrator/providers/effort-rejection.mjs +244 -0
  34. package/src/runtime/agent/orchestrator/providers/grok-client-version.mjs +1 -1
  35. package/src/runtime/agent/orchestrator/providers/grok-oauth-tokens.mjs +3 -0
  36. package/src/runtime/agent/orchestrator/providers/mimo-oauth-profile.mjs +8 -40
  37. package/src/runtime/agent/orchestrator/providers/mimo-oauth-tokens.mjs +2 -0
  38. package/src/runtime/agent/orchestrator/providers/mimo-oauth.mjs +51 -12
  39. package/src/runtime/agent/orchestrator/providers/model-cache.mjs +37 -2
  40. package/src/runtime/agent/orchestrator/providers/model-proxy-channel.d.mts +67 -0
  41. package/src/runtime/agent/orchestrator/providers/model-proxy-channel.mjs +521 -0
  42. package/src/runtime/agent/orchestrator/providers/model-proxy-provider.mjs +251 -0
  43. package/src/runtime/agent/orchestrator/providers/muse-code-oauth-tokens.mjs +2 -0
  44. package/src/runtime/agent/orchestrator/providers/muse-code-oauth.mjs +69 -102
  45. package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +3 -0
  46. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +6 -1
  47. package/src/runtime/agent/orchestrator/providers/openai-oauth-catalog-store.mjs +1 -1
  48. package/src/runtime/agent/orchestrator/providers/openai-oauth-catalog.mjs +5 -2
  49. package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +3 -0
  50. package/src/runtime/agent/orchestrator/providers/registry.mjs +15 -1
  51. package/src/runtime/agent/orchestrator/runtime-core/builtin-features.mjs +10 -0
  52. package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +36 -3
  53. package/src/runtime/agent/orchestrator/runtime-core/effort.mjs +12 -11
  54. package/src/runtime/agent/orchestrator/runtime-core/model-capabilities.mjs +16 -1
  55. package/src/runtime/agent/orchestrator/runtime-core/plugin-mcp.mjs +11 -0
  56. package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +10 -3
  57. package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +10 -0
  58. package/src/runtime/agent/orchestrator/session/cache/prefetch-cache.mjs +9 -5
  59. package/src/runtime/agent/orchestrator/session/cache/read-cache.mjs +18 -8
  60. package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +10 -3
  61. package/src/runtime/agent/orchestrator/session/compact/messages.mjs +18 -11
  62. package/src/runtime/agent/orchestrator/session/compact/runner.mjs +13 -5
  63. package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +362 -0
  64. package/src/runtime/agent/orchestrator/session/compaction-archive.mjs +253 -0
  65. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/continuation.mjs +1 -1
  66. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/diagnostic.mjs +1 -7
  67. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/refusal.mjs +1 -1
  68. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/segments.mjs +3 -2
  69. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +2 -1
  70. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +1 -0
  71. package/src/runtime/agent/orchestrator/session/loop/steering.mjs +17 -7
  72. package/src/runtime/agent/orchestrator/session/loop/usage.mjs +3 -0
  73. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +12 -2
  74. package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +2 -0
  75. package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +5 -3
  76. package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +5 -2
  77. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +37 -16
  78. package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +4 -0
  79. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +90 -0
  80. package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +22 -12
  81. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +32 -3
  82. package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +27 -0
  83. package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +16 -1
  84. package/src/runtime/agent/orchestrator/session/manager/status-telemetry.mjs +1 -0
  85. package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +2 -0
  86. package/src/runtime/agent/orchestrator/session/message-ids.mjs +123 -0
  87. package/src/runtime/agent/orchestrator/session/message-ids.test-support.mjs +29 -0
  88. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +29 -19
  89. package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +3 -0
  90. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-finish.mjs +10 -3
  91. package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +11 -0
  92. package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +29 -0
  93. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +5 -1
  94. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +23 -4
  95. package/src/runtime/agent/orchestrator/session/store-transcript-projection.mjs +20 -3
  96. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +28 -8
  97. package/src/runtime/agent/orchestrator/session/transcript-restore/user-text.mjs +140 -0
  98. package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +52 -2
  99. package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +32 -22
  100. package/src/runtime/agent/orchestrator/tools/builtin/git-repo-rw-lock.mjs +10 -3
  101. package/src/runtime/agent/orchestrator/tools/builtin/read-cache-policy.mjs +8 -0
  102. package/src/runtime/agent/orchestrator/tools/builtin/read-range-index.mjs +31 -12
  103. package/src/runtime/agent/orchestrator/tools/builtin/read-single-fast-paths.mjs +32 -35
  104. package/src/runtime/agent/orchestrator/tools/builtin/read-single-render.mjs +11 -12
  105. package/src/runtime/agent/orchestrator/tools/builtin/read-single-stream.mjs +9 -6
  106. package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +80 -66
  107. package/src/runtime/agent/orchestrator/tools/builtin/read-snapshot-runtime.mjs +8 -3
  108. package/src/runtime/agent/orchestrator/tools/builtin/read-source-windows.mjs +5 -1
  109. package/src/runtime/agent/orchestrator/tools/builtin/read-streaming.mjs +16 -7
  110. package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-dispatch.mjs +2 -1
  111. package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-publication.mjs +137 -0
  112. package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-snapshot.mjs +11 -10
  113. package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +21 -11
  114. package/src/runtime/agent/orchestrator/tools/builtin/runtime-capabilities.mjs +12 -1
  115. package/src/runtime/agent/orchestrator/tools/builtin/task-tool.mjs +56 -13
  116. package/src/runtime/agent/orchestrator/tools/code-graph/disk-cache.mjs +58 -0
  117. package/src/runtime/agent/orchestrator/tools/code-graph/trusted-roots.mjs +25 -1
  118. package/src/runtime/agent/orchestrator/tools/patch/ui-diff.mjs +78 -5
  119. package/src/runtime/agent/orchestrator/tools/progress-message.mjs +2 -0
  120. package/src/runtime/attachments/store.mjs +38 -0
  121. package/src/runtime/shared/atomic-file.mjs +8 -2
  122. package/src/runtime/shared/best-effort-stderr.mjs +8 -0
  123. package/src/runtime/shared/code-graph-state.mjs +12 -0
  124. package/src/runtime/shared/defaults-separation.mjs +2 -0
  125. package/src/runtime/shared/git-runner-worker.mjs +3 -2
  126. package/src/runtime/shared/git-runner.mjs +4 -3
  127. package/src/runtime/shared/llm/default-context-window.mjs +6 -0
  128. package/src/runtime/shared/llm/model-catalog-projection.mjs +5 -4
  129. package/src/runtime/shared/llm/model-catalog.mjs +240 -118
  130. package/src/runtime/shared/llm/provider-catalog-cache.mjs +36 -28
  131. package/src/runtime/shared/llm/provider-catalog-registry.mjs +156 -0
  132. package/src/runtime/shared/llm/provider-seed-rosters.mjs +130 -0
  133. package/src/runtime/shared/model-proxy-mode.d.mts +3 -0
  134. package/src/runtime/shared/model-proxy-mode.mjs +19 -0
  135. package/src/runtime/shared/provider-accounts.mjs +2 -2
  136. package/src/runtime/shared/provider-api-key.mjs +21 -1
  137. package/src/runtime/shared/schedule-model-ref.mjs +11 -2
  138. package/src/runtime/shared/session-usage-totals.mjs +19 -0
  139. package/src/runtime/shared/sharp-electron-linux.mjs +25 -0
  140. package/src/runtime/shared/singleton-owner.mjs +36 -6
  141. package/src/runtime/shared/statusline/statusline-segments.mjs +68 -14
  142. package/src/runtime/shared/tool-surface.mjs +7 -0
  143. package/src/runtime/shared/tool-work-units.mjs +14 -0
  144. package/src/runtime/shared/transcript-metadata.mjs +17 -1
  145. package/src/runtime/shared/turn-changes.mjs +791 -0
  146. package/src/runtime/shared/turn-snapshot-store.mjs +346 -12
  147. package/src/runtime/shared/turn-snapshot.mjs +690 -81
  148. package/src/runtime/shared/turn-worktree-snapshot.mjs +161 -11
  149. package/src/runtime/shared/worktrees/cache-cleanups.mjs +52 -0
  150. package/src/runtime/shared/worktrees/contract.mjs +551 -0
  151. package/src/runtime/shared/worktrees/disk.mjs +57 -0
  152. package/src/runtime/shared/worktrees/git.mjs +247 -0
  153. package/src/runtime/shared/worktrees/include.mjs +480 -0
  154. package/src/runtime/shared/worktrees/lock.mjs +46 -0
  155. package/src/runtime/shared/worktrees/maintenance.mjs +96 -0
  156. package/src/runtime/shared/worktrees/manager.mjs +1215 -0
  157. package/src/runtime/shared/worktrees/merge.mjs +182 -0
  158. package/src/runtime/shared/worktrees/owner.mjs +46 -0
  159. package/src/runtime/shared/worktrees/paths.mjs +62 -0
  160. package/src/runtime/shared/worktrees/registry.mjs +110 -0
  161. package/src/runtime/shared/worktrees/settings.mjs +84 -0
  162. package/src/runtime/shared/worktrees/slug.mjs +62 -0
  163. package/src/runtime/shared/worktrees/test-support.mjs +96 -0
  164. package/src/runtime/visualize-bridge/client.mjs +181 -0
  165. package/src/runtime/visualize-bridge/tool-defs.mjs +97 -0
  166. package/src/session-runtime/boot/apis.mjs +35 -0
  167. package/src/session-runtime/boot/config.mjs +2 -0
  168. package/src/session-runtime/boot/facade.mjs +20 -0
  169. package/src/session-runtime/boot/infrastructure.mjs +1 -0
  170. package/src/session-runtime/boot/session.mjs +1 -0
  171. package/src/session-runtime/boot/tools.mjs +4 -0
  172. package/src/session-runtime/config-lifecycle.mjs +10 -1
  173. package/src/session-runtime/cwd-plugins/cwd-apply.mjs +11 -1
  174. package/src/session-runtime/global-extensions.mjs +2 -2
  175. package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +27 -2
  176. package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +32 -1
  177. package/src/session-runtime/internal-tool-executor.mjs +9 -1
  178. package/src/session-runtime/lifecycle/session-catalog.mjs +8 -1
  179. package/src/session-runtime/lifecycle/session-switch/context-switch.mjs +5 -0
  180. package/src/session-runtime/lifecycle/session-switch/resume.mjs +21 -3
  181. package/src/session-runtime/lifecycle/session-switch/session-delete.mjs +16 -0
  182. package/src/session-runtime/mcp-glue/connect.mjs +11 -6
  183. package/src/session-runtime/mcp-glue/server-catalog.mjs +7 -3
  184. package/src/session-runtime/mcp-glue/server-input.mjs +29 -0
  185. package/src/session-runtime/model-route/route-persist.mjs +3 -0
  186. package/src/session-runtime/model-route/set-route.mjs +15 -2
  187. package/src/session-runtime/notification-bus.mjs +9 -1
  188. package/src/session-runtime/provider-models/row-hydration.mjs +2 -0
  189. package/src/session-runtime/resource-extension-sync.mjs +30 -7
  190. package/src/session-runtime/resource-mcp-api.mjs +20 -7
  191. package/src/session-runtime/route-resolution.mjs +10 -1
  192. package/src/session-runtime/route-state.mjs +16 -2
  193. package/src/session-runtime/runtime-core.mjs +5 -0
  194. package/src/session-runtime/runtime-facade.mjs +8 -0
  195. package/src/session-runtime/runtime-review-api.mjs +9 -0
  196. package/src/session-runtime/services/agent-tool/helpers.mjs +5 -0
  197. package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +3 -0
  198. package/src/session-runtime/services/agent-tool/job-views/job-lookup.mjs +1 -0
  199. package/src/session-runtime/services/agent-tool/job-views/spawn-meta.mjs +1 -0
  200. package/src/session-runtime/services/agent-tool/lead-worker-index.mjs +15 -0
  201. package/src/session-runtime/services/agent-tool/send-flow.mjs +1 -0
  202. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-plan.mjs +10 -2
  203. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-prep.mjs +19 -7
  204. package/src/session-runtime/services/agent-tool/spawn-preset.mjs +3 -0
  205. package/src/session-runtime/services/agent-tool/tag-registry.mjs +2 -1
  206. package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +5 -0
  207. package/src/session-runtime/services/agent-tool/worker-rows.mjs +1 -0
  208. package/src/session-runtime/services/agent-tool.mjs +1 -0
  209. package/src/session-runtime/services/daemon-candidate.mjs +3 -2
  210. package/src/session-runtime/services/daemon-crash-capture/spawn-capture.mjs +55 -0
  211. package/src/session-runtime/session-create/session-options.mjs +10 -1
  212. package/src/session-runtime/session-turn-api.mjs +1 -0
  213. package/src/session-runtime/settings-api.mjs +4 -2
  214. package/src/session-runtime/settings-builtin-tools-api.mjs +9 -2
  215. package/src/session-runtime/settings-worktree-api.mjs +17 -0
  216. package/src/session-runtime/setup-tool/executor.mjs +11 -4
  217. package/src/session-runtime/setup-tool/settings-contract.mjs +3 -1
  218. package/src/session-runtime/setup-tool/tool-defs.mjs +31 -3
  219. package/src/session-runtime/turn/agent-ops.mjs +12 -1
  220. package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +13 -3
  221. package/src/session-runtime/turn/turn-run/snapshot-tracker.mjs +20 -15
  222. package/src/session-runtime/turn/turn-run/turn-open.mjs +4 -5
  223. package/src/session-runtime/turn/turn-run/turn-prepare.mjs +29 -1
  224. package/src/session-runtime/turn/turn-run/turn-settle.mjs +6 -1
  225. package/src/session-runtime/turn/turn-run.mjs +6 -7
  226. package/src/session-runtime/workflow-agents-api/agent-route.mjs +15 -3
  227. package/src/session-runtime/workspace/admission-gate.mjs +197 -0
  228. package/src/session-runtime/workspace/project-branches.mjs +136 -0
  229. package/src/session-runtime/workspace/transition-steps.mjs +364 -0
  230. package/src/session-runtime/workspace/transitions.mjs +651 -0
  231. package/src/session-runtime/workspace/workspace-state.mjs +112 -0
  232. package/src/standalone/channel-transport/http-routes.mjs +17 -0
  233. package/src/standalone/channel-transport.mjs +13 -1
  234. package/src/standalone/daemon-desktop-runtime.mjs +1 -0
  235. package/src/standalone/daemon-owner.mjs +372 -0
  236. package/src/standalone/daemon-stored-session-views.mjs +4 -1
  237. package/src/standalone/daemon.mjs +139 -5
  238. package/src/standalone/desktop-service-registry.mjs +20 -0
  239. package/src/standalone/local-session-runtime.mjs +11 -0
  240. package/src/standalone/session-client/attachment-calls.mjs +3 -1
  241. package/src/standalone/session-client/daemon-unavailable.mjs +62 -0
  242. package/src/standalone/session-client/lease-heartbeat.mjs +47 -0
  243. package/src/standalone/session-client.mjs +104 -13
  244. package/src/standalone/session-protocol.mjs +16 -0
  245. package/src/standalone/session-proxy.mjs +3 -0
  246. package/src/standalone/session-service/agent-tree/agent-child.mjs +4 -0
  247. package/src/standalone/session-service/agent-tree/agent-rehydrate.mjs +1 -0
  248. package/src/standalone/session-service/entries/lifecycle.mjs +6 -0
  249. package/src/standalone/session-service/projection/revisions.mjs +2 -0
  250. package/src/standalone/session-service/projection/transcript-window.mjs +149 -19
  251. package/src/standalone/session-service/projection/workspace.mjs +14 -0
  252. package/src/standalone/session-service/session-calls/session-action.mjs +11 -3
  253. package/src/standalone/session-transport/call-route.mjs +13 -0
  254. package/src/standalone/session-transport/client-registry/client-grace.mjs +45 -3
  255. package/src/standalone/session-transport/client-registry/registration-replays.mjs +1 -0
  256. package/src/standalone/session-transport/client-registry.mjs +20 -3
  257. package/src/standalone/session-transport/routes.mjs +79 -2
  258. package/src/standalone/session-transport/stack.mjs +2 -0
  259. package/src/standalone/session-transport.mjs +24 -1
  260. package/src/tui/app/message-selector.mjs +2 -1
  261. package/src/tui/app/model-options.mjs +35 -5
  262. package/src/tui/app/model-picker/provider-models-list.mjs +2 -2
  263. package/src/tui/app/model-picker.mjs +4 -2
  264. package/src/tui/dist/index.mjs +332 -36
  265. package/src/tui/index.jsx +10 -2
  266. package/src/tui/session/compaction-view.mjs +147 -0
  267. package/src/tui/session/context-state.mjs +6 -0
  268. package/src/tui/session/initial-state.mjs +3 -0
  269. package/src/tui/session/quota-resume.mjs +2 -1
  270. package/src/tui/session/session-api/commands.mjs +10 -2
  271. package/src/tui/session/session-api/controls.mjs +4 -0
  272. package/src/tui/session/session-api/intake/rewind.mjs +2 -1
  273. package/src/tui/session/session-api/integrations.mjs +17 -0
  274. package/src/tui/session/session-api/lifecycle/dispose.mjs +6 -1
  275. package/src/tui/session/session-api/lifecycle/inheritance.mjs +2 -0
  276. package/src/tui/session/session-api/lifecycle/resume.mjs +3 -0
  277. package/src/tui/session/session-api/routes.mjs +10 -1
  278. package/src/tui/session/session-api/settings.mjs +2 -0
  279. package/src/tui/session/session-api/worktree.mjs +60 -0
  280. package/src/tui/session/session-api.mjs +2 -0
  281. package/src/tui/session/session-flow/auto-clear.mjs +31 -12
  282. package/src/tui/session/session-flow/drain.mjs +15 -1
  283. package/src/tui/session/session-flow/session-reset.mjs +4 -3
  284. package/src/tui/session/session-flow.mjs +2 -0
  285. package/src/tui/session/transcript-intake.mjs +14 -5
  286. package/src/tui/session/turn-stream.mjs +32 -4
  287. package/src/tui/session/turn-tool-cards.mjs +10 -2
  288. package/src/tui/session/turn.mjs +39 -10
@@ -0,0 +1,247 @@
1
+ /**
2
+ * Git process access for managed worktrees. Children run on the shared
3
+ * git-runner worker (process creation never stalls the host loop) with a
4
+ * pinned, non-interactive environment that never inherits a caller's
5
+ * GIT_DIR/GIT_WORK_TREE/GIT_INDEX_FILE.
6
+ */
7
+ import { stat } from 'node:fs/promises';
8
+ import { runGitOffThread } from '../git-runner.mjs';
9
+ import { worktreeError } from './contract.mjs';
10
+ import { canonicalPath } from './paths.mjs';
11
+
12
+ const DEFAULT_TIMEOUT_MS = 120_000;
13
+ const DEFAULT_MAX_BYTES = 64 * 1024 * 1024;
14
+ const SCOPED_GIT_ENV = [
15
+ 'GIT_DIR',
16
+ 'GIT_WORK_TREE',
17
+ 'GIT_INDEX_FILE',
18
+ 'GIT_COMMON_DIR',
19
+ 'GIT_PREFIX',
20
+ 'GIT_OBJECT_DIRECTORY',
21
+ 'GIT_ALTERNATE_OBJECT_DIRECTORIES',
22
+ 'GIT_NAMESPACE',
23
+ ];
24
+
25
+ function gitEnvironment() {
26
+ const env = {
27
+ ...process.env,
28
+ GIT_TERMINAL_PROMPT: '0',
29
+ GIT_EDITOR: 'true',
30
+ GIT_SEQUENCE_EDITOR: 'true',
31
+ GIT_MERGE_AUTOEDIT: 'no',
32
+ };
33
+ for (const name of Object.keys(env)) {
34
+ if (SCOPED_GIT_ENV.includes(name.toUpperCase())) delete env[name];
35
+ }
36
+ return env;
37
+ }
38
+
39
+ export function throwIfAborted(signal) {
40
+ if (!signal?.aborted) return;
41
+ if (signal.reason instanceof Error) throw signal.reason;
42
+ throw Object.assign(new Error('The worktree operation was aborted.'), { name: 'AbortError', code: 'ABORT_ERR' });
43
+ }
44
+
45
+ function gitFailure(args, result) {
46
+ const detail = String(result?.stderr || result?.stdout || '').trim() || 'no diagnostic';
47
+ const error = new Error(`git ${args.join(' ')} failed (exit ${result?.code}): ${detail}`);
48
+ error.code = 'GIT_FAILED';
49
+ error.exitCode = result?.code;
50
+ error.stderr = String(result?.stderr || '');
51
+ return error;
52
+ }
53
+
54
+ /** True for a failure caused by a full disk (ENOSPC, git's or Windows' text). */
55
+ export function isDiskFullError(error) {
56
+ if (!error) return false;
57
+ if (error.code === 'ENOSPC') return true;
58
+ const text = `${error.message || ''}\n${error.stderr || ''}`;
59
+ return /no space left on device|not enough space|ENOSPC|disk (?:is )?full/i.test(text);
60
+ }
61
+
62
+ // Command-scoped settings only (the repository config is never written):
63
+ // paths below a managed worktree root easily pass Windows' MAX_PATH.
64
+ const SCOPED_CONFIG = ['-c', 'core.longpaths=true'];
65
+ // Read-only status probes never take optional locks nor wake an fsmonitor.
66
+ const READ_ONLY_FLAGS = ['--no-optional-locks', '-c', 'core.fsmonitor=false'];
67
+
68
+ /**
69
+ * @param {string} cwd
70
+ * @param {string[]} args
71
+ * @param {{ signal?: AbortSignal, timeoutMs?: number, maxBytes?: number, allowFailure?: boolean, readOnly?: boolean }} [options]
72
+ * @returns {Promise<{ code: number, stdout: string, stderr: string }>}
73
+ */
74
+ export async function runGit(cwd, args, { signal, timeoutMs, maxBytes, allowFailure = false, readOnly = false } = {}) {
75
+ throwIfAborted(signal);
76
+ const result = await runGitOffThread([...(readOnly ? READ_ONLY_FLAGS : []), ...SCOPED_CONFIG, ...args], {
77
+ cwd,
78
+ env: gitEnvironment(),
79
+ timeoutMs: timeoutMs ?? DEFAULT_TIMEOUT_MS,
80
+ maxBytes: maxBytes ?? DEFAULT_MAX_BYTES,
81
+ });
82
+ if (result.code !== 0 && !allowFailure) throw gitFailure(args, result);
83
+ return result;
84
+ }
85
+
86
+ /** The never-throwing runner shape the merge backend takes. */
87
+ export function gitStatusRunner(cwd, args) {
88
+ return runGit(cwd, args, { allowFailure: true });
89
+ }
90
+
91
+ export function firstLine(text) {
92
+ return String(text || '')
93
+ .split(/\r?\n/)[0]
94
+ .trim();
95
+ }
96
+
97
+ function lines(text) {
98
+ return String(text || '')
99
+ .split(/\r?\n/)
100
+ .map((line) => line.trim())
101
+ .filter(Boolean);
102
+ }
103
+
104
+ /** WORKTREE_PROJECT_UNKNOWN (reason 'missing') when the project folder is gone from disk. */
105
+ async function assertProjectFolder(cwd) {
106
+ let isFolder = false;
107
+ try {
108
+ isFolder = (await stat(cwd)).isDirectory();
109
+ } catch (error) {
110
+ if (error?.code !== 'ENOENT' && error?.code !== 'ENOTDIR') throw error;
111
+ }
112
+ if (!isFolder) {
113
+ throw worktreeError('WORKTREE_PROJECT_UNKNOWN', 'The project folder no longer exists.', {
114
+ projectPath: cwd,
115
+ reason: 'missing',
116
+ });
117
+ }
118
+ }
119
+
120
+ /**
121
+ * Repository facts every managed operation starts from. Fails with
122
+ * WORKTREE_PROJECT_UNKNOWN (reason 'missing') when the folder is gone, and
123
+ * WORKTREE_UNSUPPORTED_REPO for non-repositories, bare repositories and
124
+ * repositories without a first commit.
125
+ * @param {string} projectPath
126
+ */
127
+ export async function inspectRepo(projectPath, { signal } = {}) {
128
+ const cwd = canonicalPath(projectPath);
129
+ await assertProjectFolder(cwd);
130
+ const probe = await runGit(cwd, ['rev-parse', '--is-bare-repository', '--path-format=absolute', '--git-common-dir'], {
131
+ signal,
132
+ allowFailure: true,
133
+ });
134
+ if (probe.code !== 0) {
135
+ throw worktreeError('WORKTREE_UNSUPPORTED_REPO', 'The project folder is not a git repository.', {
136
+ projectPath: cwd,
137
+ });
138
+ }
139
+ const [bare, commonDir] = lines(probe.stdout);
140
+ if (bare === 'true' || !commonDir) {
141
+ throw worktreeError('WORKTREE_UNSUPPORTED_REPO', 'Bare repositories cannot host managed worktrees.', {
142
+ projectPath: cwd,
143
+ });
144
+ }
145
+ const top = await runGit(cwd, ['rev-parse', '--show-toplevel'], { signal, allowFailure: true });
146
+ if (top.code !== 0) {
147
+ throw worktreeError('WORKTREE_UNSUPPORTED_REPO', 'The project folder has no git work tree.', { projectPath: cwd });
148
+ }
149
+ const head = await runGit(cwd, ['rev-parse', '--verify', '--quiet', 'HEAD^{commit}'], { signal, allowFailure: true });
150
+ if (head.code !== 0) {
151
+ throw worktreeError('WORKTREE_UNSUPPORTED_REPO', 'The repository has no commits yet; commit once first.', {
152
+ projectPath: cwd,
153
+ });
154
+ }
155
+ return {
156
+ projectPath: cwd,
157
+ topLevel: canonicalPath(firstLine(top.stdout)),
158
+ repoCommonDir: canonicalPath(commonDir),
159
+ headCommit: firstLine(head.stdout),
160
+ currentBranch: await currentBranch(cwd, { signal }),
161
+ };
162
+ }
163
+
164
+ /** Short name of the checked-out branch, or null when detached/unborn-less. */
165
+ export async function currentBranch(cwd, { signal } = {}) {
166
+ const result = await runGit(cwd, ['symbolic-ref', '--quiet', '--short', 'HEAD'], { signal, allowFailure: true });
167
+ return result.code === 0 ? firstLine(result.stdout) || null : null;
168
+ }
169
+
170
+ /** Full commit id of a local branch, or null when it does not exist. */
171
+ export async function branchCommit(cwd, branch, { signal } = {}) {
172
+ const result = await runGit(cwd, ['rev-parse', '--verify', '--quiet', `refs/heads/${branch}^{commit}`], {
173
+ signal,
174
+ allowFailure: true,
175
+ });
176
+ return result.code === 0 ? firstLine(result.stdout) : null;
177
+ }
178
+
179
+ export async function localBranches(cwd, { signal, prefix = '' } = {}) {
180
+ const result = await runGit(cwd, ['for-each-ref', '--format=%(refname:short)', `refs/heads/${prefix}`], { signal });
181
+ return lines(result.stdout);
182
+ }
183
+
184
+ export async function validBranchName(cwd, branch) {
185
+ if (typeof branch !== 'string' || !branch.trim() || branch.startsWith('-')) return false;
186
+ const result = await runGit(cwd, ['check-ref-format', '--branch', branch], { allowFailure: true });
187
+ return result.code === 0;
188
+ }
189
+
190
+ /**
191
+ * `git worktree list --porcelain -z`, with canonical paths.
192
+ * @returns {Promise<Array<{ path: string, head: string|null, branch: string|null, bare: boolean, detached: boolean, locked: string|null, prunable: string|null }>>}
193
+ */
194
+ export async function listWorktrees(cwd, { signal } = {}) {
195
+ const result = await runGit(cwd, ['worktree', 'list', '--porcelain', '-z'], { signal });
196
+ const worktrees = [];
197
+ let current = null;
198
+ for (const field of result.stdout.split('\0')) {
199
+ if (!field) {
200
+ if (current) worktrees.push(current);
201
+ current = null;
202
+ continue;
203
+ }
204
+ const space = field.indexOf(' ');
205
+ const key = space < 0 ? field : field.slice(0, space);
206
+ const value = space < 0 ? '' : field.slice(space + 1);
207
+ if (key === 'worktree') {
208
+ if (current) worktrees.push(current);
209
+ current = {
210
+ path: canonicalPath(value),
211
+ head: null,
212
+ branch: null,
213
+ bare: false,
214
+ detached: false,
215
+ locked: null,
216
+ prunable: null,
217
+ };
218
+ continue;
219
+ }
220
+ if (!current) continue;
221
+ if (key === 'HEAD') current.head = value;
222
+ else if (key === 'branch') current.branch = value.replace(/^refs\/heads\//, '');
223
+ else if (key === 'bare') current.bare = true;
224
+ else if (key === 'detached') current.detached = true;
225
+ else if (key === 'locked') current.locked = value;
226
+ else if (key === 'prunable') current.prunable = value;
227
+ }
228
+ if (current) worktrees.push(current);
229
+ return worktrees;
230
+ }
231
+
232
+ /**
233
+ * Paths reported by `git status --porcelain=v1 -z`.
234
+ * @param {string} raw
235
+ */
236
+ export function parseStatusPaths(raw) {
237
+ const fields = String(raw || '').split('\0');
238
+ const paths = [];
239
+ for (let index = 0; index < fields.length; index++) {
240
+ const field = fields[index];
241
+ if (field.length < 4) continue;
242
+ const code = field.slice(0, 2);
243
+ paths.push(field.slice(3));
244
+ if (code.includes('R') || code.includes('C')) index++;
245
+ }
246
+ return paths;
247
+ }