infra-kit 0.1.114 → 0.1.117

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 (204) hide show
  1. package/.eslintcache +1 -1
  2. package/.omc/state/agent-replay-1fcfe0ec-7567-41c3-9bfd-501e99e86819.jsonl +19 -0
  3. package/.omc/state/agent-replay-38382db5-3088-4b5a-aff7-2561a60827c7.jsonl +17 -0
  4. package/.omc/state/agent-replay-50c36be9-a3a2-42b9-9c85-5a06d3e22f6a.jsonl +5 -0
  5. package/.omc/state/agent-replay-8198b3e5-fad6-4ee0-9c59-ef773ad89af4.jsonl +1 -0
  6. package/.omc/state/agent-replay-8cd7652a-b8b5-49cf-9b1f-bee8e5972458.jsonl +3 -0
  7. package/.omc/state/agent-replay-9bea518f-a9ee-4d14-bb53-a936ebaf647c.jsonl +7 -0
  8. package/.omc/state/agent-replay-b52d89fb-0e24-4901-bc66-27e3c1697126.jsonl +2 -0
  9. package/.omc/state/agent-replay-f581a1ae-0ee4-48a3-96f3-ce31a0633bb0.jsonl +1 -0
  10. package/.omc/state/agent-replay-fe1057b4-9a25-4e8e-8c3f-3e11652ee697.jsonl +5 -0
  11. package/.omc/state/idle-notif-cooldown.json +1 -1
  12. package/.omc/state/sessions/1fcfe0ec-7567-41c3-9bfd-501e99e86819/last-tool-error-state.json +7 -0
  13. package/.omc/state/sessions/1fcfe0ec-7567-41c3-9bfd-501e99e86819/mission-state.json +109 -0
  14. package/.omc/state/sessions/1fcfe0ec-7567-41c3-9bfd-501e99e86819/pre-tool-advisory-throttle.json +42 -0
  15. package/.omc/state/sessions/1fcfe0ec-7567-41c3-9bfd-501e99e86819/ralph-state.json +13 -0
  16. package/.omc/state/sessions/1fcfe0ec-7567-41c3-9bfd-501e99e86819/skill-active-state.json +15 -0
  17. package/.omc/state/sessions/1fcfe0ec-7567-41c3-9bfd-501e99e86819/subagent-tracking-state.json +62 -0
  18. package/.omc/state/sessions/1fcfe0ec-7567-41c3-9bfd-501e99e86819/ultrawork-state.json +11 -0
  19. package/.omc/state/sessions/23d0ff82-17e5-428b-8743-38f7979bb6f3/last-tool-error-state.json +7 -0
  20. package/.omc/state/sessions/23d0ff82-17e5-428b-8743-38f7979bb6f3/pre-tool-advisory-throttle.json +18 -0
  21. package/.omc/state/sessions/38382db5-3088-4b5a-aff7-2561a60827c7/last-tool-error-state.json +7 -0
  22. package/.omc/state/sessions/38382db5-3088-4b5a-aff7-2561a60827c7/mission-state.json +145 -0
  23. package/.omc/state/sessions/38382db5-3088-4b5a-aff7-2561a60827c7/pre-tool-advisory-throttle.json +38 -0
  24. package/.omc/state/sessions/38382db5-3088-4b5a-aff7-2561a60827c7/ralplan-state.json +11 -0
  25. package/.omc/state/sessions/38382db5-3088-4b5a-aff7-2561a60827c7/subagent-tracking-state.json +98 -0
  26. package/.omc/state/sessions/41336850-028c-476b-be1a-2164e1e46cac/last-tool-error-state.json +7 -0
  27. package/.omc/state/sessions/41336850-028c-476b-be1a-2164e1e46cac/pre-tool-advisory-throttle.json +18 -0
  28. package/.omc/state/sessions/50c36be9-a3a2-42b9-9c85-5a06d3e22f6a/mission-state.json +61 -0
  29. package/.omc/state/sessions/50c36be9-a3a2-42b9-9c85-5a06d3e22f6a/pre-tool-advisory-throttle.json +22 -0
  30. package/.omc/state/sessions/50c36be9-a3a2-42b9-9c85-5a06d3e22f6a/skill-active-state.json +15 -0
  31. package/.omc/state/sessions/50c36be9-a3a2-42b9-9c85-5a06d3e22f6a/subagent-tracking-state.json +26 -0
  32. package/{src/lib/.omc/state/sessions/a60ac2ec-afbd-449f-a540-6df287392fc2 → .omc/state/sessions/8198b3e5-fad6-4ee0-9c59-ef773ad89af4}/pre-tool-advisory-throttle.json +3 -3
  33. package/.omc/state/sessions/8198b3e5-fad6-4ee0-9c59-ef773ad89af4/subagent-tracking-state.json +17 -0
  34. package/.omc/state/sessions/8cd7652a-b8b5-49cf-9b1f-bee8e5972458/pre-tool-advisory-throttle.json +18 -0
  35. package/.omc/state/sessions/8cd7652a-b8b5-49cf-9b1f-bee8e5972458/ralplan-state.json +11 -0
  36. package/.omc/state/sessions/8cd7652a-b8b5-49cf-9b1f-bee8e5972458/subagent-tracking-state.json +35 -0
  37. package/.omc/state/sessions/9bea518f-a9ee-4d14-bb53-a936ebaf647c/last-tool-error-state.json +7 -0
  38. package/.omc/state/sessions/9bea518f-a9ee-4d14-bb53-a936ebaf647c/mission-state.json +79 -0
  39. package/.omc/state/sessions/9bea518f-a9ee-4d14-bb53-a936ebaf647c/pre-tool-advisory-throttle.json +22 -0
  40. package/.omc/state/sessions/9bea518f-a9ee-4d14-bb53-a936ebaf647c/progress.txt +7 -0
  41. package/.omc/state/sessions/9bea518f-a9ee-4d14-bb53-a936ebaf647c/skill-active-state.json +15 -0
  42. package/.omc/state/sessions/9bea518f-a9ee-4d14-bb53-a936ebaf647c/subagent-tracking-state.json +26 -0
  43. package/.omc/state/sessions/b52d89fb-0e24-4901-bc66-27e3c1697126/pre-tool-advisory-throttle.json +14 -0
  44. package/.omc/state/sessions/b52d89fb-0e24-4901-bc66-27e3c1697126/subagent-tracking-state.json +17 -0
  45. package/.omc/state/sessions/c513ef38-cc3e-4a29-9c4c-1e0137245dd5/pre-tool-advisory-throttle.json +18 -0
  46. package/.omc/state/sessions/ed3e2cfb-a666-4bb1-bcc1-569d5d0b837a/pre-tool-advisory-throttle.json +18 -0
  47. package/.omc/state/sessions/f581a1ae-0ee4-48a3-96f3-ce31a0633bb0/pre-tool-advisory-throttle.json +18 -0
  48. package/.omc/state/sessions/f581a1ae-0ee4-48a3-96f3-ce31a0633bb0/subagent-tracking-state.json +17 -0
  49. package/.omc/state/sessions/fe1057b4-9a25-4e8e-8c3f-3e11652ee697/last-tool-error-state.json +7 -0
  50. package/.omc/state/sessions/fe1057b4-9a25-4e8e-8c3f-3e11652ee697/mission-state.json +61 -0
  51. package/.omc/state/sessions/fe1057b4-9a25-4e8e-8c3f-3e11652ee697/prd.json +22 -0
  52. package/.omc/state/sessions/fe1057b4-9a25-4e8e-8c3f-3e11652ee697/pre-tool-advisory-throttle.json +22 -0
  53. package/.omc/state/sessions/fe1057b4-9a25-4e8e-8c3f-3e11652ee697/progress.txt +22 -0
  54. package/.omc/state/sessions/fe1057b4-9a25-4e8e-8c3f-3e11652ee697/skill-active-state.json +15 -0
  55. package/.omc/state/sessions/fe1057b4-9a25-4e8e-8c3f-3e11652ee697/subagent-tracking-state.json +26 -0
  56. package/.turbo/turbo-build.log +3 -3
  57. package/.turbo/turbo-eslint-check.log +2 -0
  58. package/.turbo/turbo-test.log +90 -42
  59. package/dist/boot-K4QUAFSP.js +2 -0
  60. package/dist/boot-K4QUAFSP.js.map +7 -0
  61. package/dist/chunk-FEGOOVZE.js +166 -0
  62. package/dist/chunk-FEGOOVZE.js.map +7 -0
  63. package/dist/chunk-WA4BQRDC.js +2 -0
  64. package/dist/chunk-WA4BQRDC.js.map +7 -0
  65. package/dist/cli.js +11 -107
  66. package/dist/cli.js.map +4 -4
  67. package/dist/entry/index.d.ts +2 -0
  68. package/dist/index.js +1 -1
  69. package/dist/index.js.map +4 -4
  70. package/dist/lib/vendor/config-schema.d.ts +58 -0
  71. package/dist/mcp.js +1 -54
  72. package/dist/mcp.js.map +4 -4
  73. package/eslint.config.js +33 -1
  74. package/package.json +5 -1
  75. package/scripts/build.js +11 -1
  76. package/src/.omc/state/agent-replay-c513ef38-cc3e-4a29-9c4c-1e0137245dd5.jsonl +4 -0
  77. package/src/.omc/state/agent-replay-ed3e2cfb-a666-4bb1-bcc1-569d5d0b837a.jsonl +2 -0
  78. package/src/.omc/state/idle-notif-cooldown.json +1 -1
  79. package/src/.omc/state/sessions/c513ef38-cc3e-4a29-9c4c-1e0137245dd5/mission-state.json +79 -0
  80. package/src/.omc/state/sessions/c513ef38-cc3e-4a29-9c4c-1e0137245dd5/pre-tool-advisory-throttle.json +22 -0
  81. package/src/.omc/state/sessions/c513ef38-cc3e-4a29-9c4c-1e0137245dd5/subagent-tracking-state.json +26 -0
  82. package/src/.omc/state/sessions/ed3e2cfb-a666-4bb1-bcc1-569d5d0b837a/pre-tool-advisory-throttle.json +18 -0
  83. package/src/.omc/state/sessions/ed3e2cfb-a666-4bb1-bcc1-569d5d0b837a/ralph-state.json +13 -0
  84. package/src/.omc/state/sessions/ed3e2cfb-a666-4bb1-bcc1-569d5d0b837a/ultrawork-state.json +11 -0
  85. package/src/commands/config/.omc/state/sessions/41336850-028c-476b-be1a-2164e1e46cac/last-tool-error-state.json +7 -0
  86. package/src/commands/config/.omc/state/sessions/41336850-028c-476b-be1a-2164e1e46cac/pre-tool-advisory-throttle.json +18 -0
  87. package/src/commands/config/config.ts +7 -37
  88. package/src/commands/doctor/__tests__/agent-files.test.ts +8 -30
  89. package/src/commands/doctor/__tests__/check-legacy-user-global-config.test.ts +110 -0
  90. package/src/commands/doctor/doctor.ts +104 -44
  91. package/src/commands/env-autoload/env-autoload.ts +12 -0
  92. package/src/commands/env-autoload/index.ts +1 -0
  93. package/src/commands/env-clear/__tests__/env-clear.test.ts +27 -0
  94. package/src/commands/env-clear/env-clear.ts +22 -8
  95. package/src/commands/env-load/__tests__/env-load.test.ts +36 -1
  96. package/src/commands/env-load/env-load.ts +152 -35
  97. package/src/commands/env-load/index.ts +2 -1
  98. package/src/commands/env-status/__tests__/env-status.test.ts +78 -0
  99. package/src/commands/env-status/env-status.ts +19 -7
  100. package/src/commands/gh-release-deploy-all/gh-release-deploy-all.ts +45 -1
  101. package/src/commands/gh-release-deploy-selected/gh-release-deploy-selected.ts +46 -1
  102. package/src/commands/init/__tests__/agent-files.test.ts +145 -27
  103. package/src/commands/init/__tests__/migrate-config.test.ts +136 -5
  104. package/src/commands/init/__tests__/migrate-factory-config.test.ts +93 -0
  105. package/src/commands/init/__tests__/seed-user-global-config.test.ts +64 -0
  106. package/src/commands/init/__tests__/shell-body.test.ts +30 -0
  107. package/src/commands/init/agent-files.ts +65 -45
  108. package/src/commands/init/init.ts +140 -31
  109. package/src/commands/init/migrate-config.ts +160 -32
  110. package/src/commands/vendor-check/__tests__/runtime-isolation.test.ts +71 -0
  111. package/src/commands/vendor-check/__tests__/vendor-check.test.ts +87 -0
  112. package/src/commands/vendor-check/index.ts +1 -0
  113. package/src/commands/vendor-check/vendor-check.ts +127 -0
  114. package/src/commands/vendor-config/__tests__/vendor-config.test.ts +103 -0
  115. package/src/commands/vendor-config/index.ts +1 -0
  116. package/src/commands/vendor-config/vendor-config.ts +162 -0
  117. package/src/commands/vendor-diff/__tests__/vendor-diff.test.ts +74 -0
  118. package/src/commands/vendor-diff/index.ts +1 -0
  119. package/src/commands/vendor-diff/vendor-diff.ts +77 -0
  120. package/src/commands/vendor-manifest/index.ts +1 -0
  121. package/src/commands/vendor-manifest/vendor-manifest.ts +60 -0
  122. package/src/commands/vendor-sync/__tests__/vendor-write.test.ts +86 -0
  123. package/src/commands/vendor-sync/index.ts +1 -0
  124. package/src/commands/vendor-sync/vendor-sync.ts +90 -0
  125. package/src/commands/worktrees-add/worktrees-add.ts +1 -1
  126. package/src/commands/worktrees-reload/worktrees-reload.ts +6 -5
  127. package/src/commands/worktrees-remove/worktrees-remove.ts +10 -1
  128. package/src/commands/worktrees-sync/worktrees-sync.ts +9 -1
  129. package/src/entry/cli.ts +417 -210
  130. package/src/entry/index.ts +7 -1
  131. package/src/integrations/cmux/__tests__/open-workspace-with-layout.test.ts +127 -0
  132. package/src/integrations/cmux/close-workspace-by-title.ts +2 -2
  133. package/src/integrations/cmux/list-workspace-titles.ts +1 -1
  134. package/src/integrations/cmux/open-workspace-with-layout.ts +15 -4
  135. package/src/integrations/cursor/open-cursor-workspace.ts +6 -11
  136. package/src/integrations/doppler/__tests__/doppler-errors.test.ts +76 -0
  137. package/src/integrations/doppler/doppler-cli-auth.ts +12 -4
  138. package/src/integrations/doppler/doppler-errors.ts +88 -0
  139. package/src/integrations/doppler/doppler-project.ts +80 -0
  140. package/src/integrations/doppler/index.ts +3 -1
  141. package/src/integrations/gh/gh-cli-auth/gh-cli-auth.ts +9 -5
  142. package/src/integrations/gh/gh-release-prs/gh-release-prs.ts +16 -8
  143. package/src/integrations/ide/__tests__/ide-facade.test.ts +49 -10
  144. package/src/integrations/ide/open-ide-workspace.ts +6 -6
  145. package/src/integrations/ide/remove-ide-worktree-folders.ts +95 -30
  146. package/src/integrations/ide/types.ts +10 -1
  147. package/src/integrations/zed/__tests__/open-zed-workspace.test.ts +1 -17
  148. package/src/integrations/zed/__tests__/reuse-zed-workspace.test.ts +86 -0
  149. package/src/integrations/zed/index.ts +1 -0
  150. package/src/integrations/zed/open-zed-workspace.ts +4 -10
  151. package/src/integrations/zed/reuse-zed-workspace.ts +57 -0
  152. package/src/lib/command-catalog/__tests__/__snapshots__/command-catalog.test.ts.snap +212 -0
  153. package/src/lib/command-catalog/__tests__/command-catalog.test.ts +193 -0
  154. package/src/lib/command-catalog/command-catalog.ts +141 -0
  155. package/src/lib/command-catalog/index.ts +2 -0
  156. package/src/lib/command-echo/__tests__/command-echo.test.ts +127 -0
  157. package/src/lib/constants/constants.ts +13 -0
  158. package/src/lib/constants/index.ts +2 -0
  159. package/src/lib/env-autoload/__tests__/env-autoload.test.ts +288 -0
  160. package/src/lib/env-autoload/env-autoload.ts +294 -0
  161. package/src/lib/env-autoload/index.ts +9 -0
  162. package/src/lib/errors/operation-error.ts +1 -1
  163. package/src/lib/infra-kit-config/__tests__/env-autoload-schema.test.ts +71 -0
  164. package/src/lib/infra-kit-config/__tests__/infra-kit-config.test.ts +77 -6
  165. package/src/lib/infra-kit-config/index.ts +9 -1
  166. package/src/lib/infra-kit-config/infra-kit-config.ts +71 -11
  167. package/src/lib/json-output/__tests__/json-output.test.ts +139 -0
  168. package/src/lib/json-output/index.ts +1 -0
  169. package/src/lib/json-output/json-output.ts +47 -0
  170. package/src/lib/managed-block/managed-block.ts +4 -4
  171. package/src/lib/package-config/package-config.ts +1 -3
  172. package/src/lib/path-display/index.ts +1 -0
  173. package/src/lib/path-display/path-display.ts +42 -0
  174. package/src/lib/render/__tests__/render.test.ts +48 -0
  175. package/src/lib/render/index.ts +1 -0
  176. package/src/lib/render/render.ts +24 -0
  177. package/src/lib/vendor/__tests__/config-schema.test.ts +31 -0
  178. package/src/lib/vendor/__tests__/config.test.ts +63 -0
  179. package/src/lib/vendor/__tests__/factory-config.test.ts +92 -0
  180. package/src/lib/vendor/__tests__/vendor-lib.test.ts +158 -0
  181. package/src/lib/vendor/__tests__/zx-mock.ts +32 -0
  182. package/src/lib/vendor/config-schema.ts +78 -0
  183. package/src/lib/vendor/config.ts +58 -0
  184. package/src/lib/vendor/factory-config-schema.ts +35 -0
  185. package/src/lib/vendor/factory-config.ts +91 -0
  186. package/src/lib/vendor/hash.ts +16 -0
  187. package/src/lib/vendor/index.ts +23 -0
  188. package/src/lib/vendor/manifest.ts +151 -0
  189. package/src/lib/vendor/skip-sets.ts +48 -0
  190. package/src/lib/vendor/sync-ops.ts +290 -0
  191. package/src/lib/vendor/walk.ts +56 -0
  192. package/src/mcp/tools/index.ts +5 -42
  193. package/src/sanity.test.tsx +1 -0
  194. package/src/tui/__tests__/no-react-boundary.test.ts +51 -0
  195. package/src/tui/boot.tsx +45 -0
  196. package/src/tui/screens/__tests__/command-palette.test.tsx +136 -0
  197. package/src/tui/screens/command-palette.tsx +159 -0
  198. package/src/tui/types.ts +8 -0
  199. package/tsconfig.json +1 -0
  200. package/tsconfig.tsbuildinfo +1 -1
  201. package/vitest.config.ts +8 -0
  202. package/src/commands/worktrees-open/__tests__/open-cmux.test.ts +0 -96
  203. package/src/commands/worktrees-open/index.ts +0 -1
  204. package/src/commands/worktrees-open/worktrees-open.ts +0 -195
package/vitest.config.ts CHANGED
@@ -13,6 +13,14 @@ export default defineConfig(() => {
13
13
  test: {
14
14
  environment: 'node',
15
15
  setupFiles: ['./vitest.setup.ts'],
16
+ server: {
17
+ deps: {
18
+ // ink-testing-library imports `ink` without declaring it as a dep, so
19
+ // pnpm's strict isolation hides it. Inlining lets vite resolve `ink`
20
+ // from this package's node_modules.
21
+ inline: ['ink-testing-library'],
22
+ },
23
+ },
16
24
  },
17
25
  }
18
26
  })
@@ -1,96 +0,0 @@
1
- import { beforeEach, describe, expect, it, vi } from 'vitest'
2
-
3
- import { listCmuxWorkspaceTitles, openCmuxWorkspaceWithLayout } from 'src/integrations/cmux'
4
- import { getRepoName } from 'src/lib/git-utils'
5
-
6
- import { openCmux } from '../worktrees-open'
7
-
8
- vi.mock('src/integrations/cmux', async (importActual) => {
9
- const actual = await importActual<typeof import('src/integrations/cmux')>()
10
-
11
- return {
12
- ...actual,
13
- listCmuxWorkspaceTitles: vi.fn(),
14
- openCmuxWorkspaceWithLayout: vi.fn(),
15
- }
16
- })
17
-
18
- vi.mock('src/lib/git-utils', () => {
19
- return {
20
- getRepoName: vi.fn(),
21
- getProjectRoot: vi.fn(),
22
- getCurrentWorktrees: vi.fn(),
23
- }
24
- })
25
-
26
- const REPO = 'hulyo-monorepo'
27
- const WORKTREE_DIR = '/repos/project-worktrees'
28
- const BRANCHES = ['release/v1.48.0', 'release/checkout-redesign']
29
-
30
- const titleFor = {
31
- versioned: `${REPO} 1.48.0`,
32
- named: `${REPO} checkout-redesign`,
33
- }
34
-
35
- describe('openCmux dedup', () => {
36
- beforeEach(() => {
37
- vi.clearAllMocks()
38
- vi.mocked(getRepoName).mockResolvedValue(REPO)
39
- vi.mocked(openCmuxWorkspaceWithLayout).mockResolvedValue(undefined)
40
- })
41
-
42
- it('opens a workspace for every branch when none are open', async () => {
43
- vi.mocked(listCmuxWorkspaceTitles).mockResolvedValue(new Set())
44
-
45
- const result = await openCmux({ worktreeDir: WORKTREE_DIR, currentBranches: BRANCHES })
46
-
47
- expect(result.opened).toEqual([titleFor.versioned, titleFor.named])
48
- expect(result.skipped).toEqual([])
49
- expect(openCmuxWorkspaceWithLayout).toHaveBeenCalledTimes(2)
50
- })
51
-
52
- it('skips every branch when all are already open (no duplicates)', async () => {
53
- vi.mocked(listCmuxWorkspaceTitles).mockResolvedValue(new Set([titleFor.versioned, titleFor.named]))
54
-
55
- const result = await openCmux({ worktreeDir: WORKTREE_DIR, currentBranches: BRANCHES })
56
-
57
- expect(result.opened).toEqual([])
58
- expect(result.skipped).toEqual([titleFor.versioned, titleFor.named])
59
- expect(openCmuxWorkspaceWithLayout).not.toHaveBeenCalled()
60
- })
61
-
62
- it('opens only the missing branch in a mixed state', async () => {
63
- vi.mocked(listCmuxWorkspaceTitles).mockResolvedValue(new Set([titleFor.versioned]))
64
-
65
- const result = await openCmux({ worktreeDir: WORKTREE_DIR, currentBranches: BRANCHES })
66
-
67
- expect(result.skipped).toEqual([titleFor.versioned])
68
- expect(result.opened).toEqual([titleFor.named])
69
- expect(openCmuxWorkspaceWithLayout).toHaveBeenCalledTimes(1)
70
- expect(openCmuxWorkspaceWithLayout).toHaveBeenCalledWith({
71
- cwd: `${WORKTREE_DIR}/release/checkout-redesign`,
72
- title: titleFor.named,
73
- })
74
- })
75
-
76
- it('skips a versioned branch whose workspace was stored under an old v-prefixed title', async () => {
77
- // listCmuxWorkspaceTitles already returns canonical keys, so an old
78
- // "hulyo-monorepo v1.48.0" workspace is surfaced as the canonical key below.
79
- vi.mocked(listCmuxWorkspaceTitles).mockResolvedValue(new Set([titleFor.versioned]))
80
-
81
- const result = await openCmux({ worktreeDir: WORKTREE_DIR, currentBranches: ['release/v1.48.0'] })
82
-
83
- expect(result.opened).toEqual([])
84
- expect(result.skipped).toEqual([titleFor.versioned])
85
- expect(openCmuxWorkspaceWithLayout).not.toHaveBeenCalled()
86
- })
87
-
88
- it('returns empty outcome when there are no worktrees', async () => {
89
- vi.mocked(listCmuxWorkspaceTitles).mockResolvedValue(new Set())
90
-
91
- const result = await openCmux({ worktreeDir: WORKTREE_DIR, currentBranches: [] })
92
-
93
- expect(result).toEqual({ ran: true, opened: [], skipped: [] })
94
- expect(listCmuxWorkspaceTitles).not.toHaveBeenCalled()
95
- })
96
- })
@@ -1 +0,0 @@
1
- export { worktreesOpen, worktreesOpenMcpTool } from './worktrees-open'
@@ -1,195 +0,0 @@
1
- import { z } from 'zod'
2
-
3
- import {
4
- buildCmuxWorkspaceTitle,
5
- canonicalizeCmuxTitle,
6
- listCmuxWorkspaceTitles,
7
- openCmuxWorkspaceWithLayout,
8
- } from 'src/integrations/cmux'
9
- import { ideProviderLabel, openIdeWorkspace } from 'src/integrations/ide'
10
- import type { IdeProvider } from 'src/integrations/ide'
11
- import { commandEcho } from 'src/lib/command-echo'
12
- import { WORKTREES_DIR_SUFFIX } from 'src/lib/constants'
13
- import { OperationError } from 'src/lib/errors/operation-error'
14
- import { getCurrentWorktrees, getProjectRoot, getRepoName } from 'src/lib/git-utils'
15
- import { logger } from 'src/lib/logger'
16
- import { defineMcpTool, textContent } from 'src/types'
17
-
18
- interface WorktreesOpenResult {
19
- openedCmux: string[]
20
- skippedCmux: string[]
21
- ideProviders: IdeProvider[]
22
- ideFoldersAdded: number
23
- ideFoldersRemoved: number
24
- }
25
-
26
- /**
27
- * Cold-start restore command: reconciles the configured editor workspace against
28
- * the set of release worktrees on disk, opens the editor against it, and ensures
29
- * one cmux workspace exists per worktree. Idempotent and additive — never removes
30
- * worktrees, never recreates running cmux workspaces.
31
- */
32
- export const worktreesOpen = async () => {
33
- commandEcho.start('worktrees-open')
34
-
35
- try {
36
- const projectRoot = await getProjectRoot()
37
- const worktreeDir = `${projectRoot}${WORKTREES_DIR_SUFFIX}`
38
- const currentBranches = await getCurrentWorktrees('release')
39
-
40
- const ideOutcomes = await openIdeWorkspace({
41
- projectRoot,
42
- worktreeDir,
43
- currentBranches,
44
- skipRelaunchWhenEmpty: false,
45
- })
46
- const cmuxOutcome = await openCmux({ worktreeDir, currentBranches })
47
-
48
- const result: WorktreesOpenResult = {
49
- openedCmux: cmuxOutcome.opened,
50
- skippedCmux: cmuxOutcome.skipped,
51
- ideProviders: ideOutcomes
52
- .filter((outcome) => {
53
- return outcome.ran
54
- })
55
- .map((outcome) => {
56
- return outcome.provider
57
- }),
58
- ideFoldersAdded: ideOutcomes.reduce((sum, outcome) => {
59
- return sum + outcome.added
60
- }, 0),
61
- ideFoldersRemoved: ideOutcomes.reduce((sum, outcome) => {
62
- return sum + outcome.removed
63
- }, 0),
64
- }
65
-
66
- logResults(result, {
67
- ideRan: ideOutcomes.some((outcome) => {
68
- return outcome.ran
69
- }),
70
- cmuxRan: cmuxOutcome.ran,
71
- })
72
-
73
- commandEcho.print()
74
-
75
- return {
76
- content: textContent(JSON.stringify(result, null, 2)),
77
- structuredContent: { ...result },
78
- }
79
- } catch (error) {
80
- logger.error({ error }, '❌ Error opening worktrees')
81
- throw new OperationError(error, {
82
- operation: 'open worktrees',
83
- remediation: "run 'worktrees-list' to confirm the branches exist",
84
- })
85
- }
86
- }
87
-
88
- interface OpenCmuxArgs {
89
- worktreeDir: string
90
- currentBranches: string[]
91
- }
92
-
93
- interface OpenCmuxOutcome {
94
- ran: boolean
95
- opened: string[]
96
- skipped: string[]
97
- }
98
-
99
- export const openCmux = async (args: OpenCmuxArgs): Promise<OpenCmuxOutcome> => {
100
- const { worktreeDir, currentBranches } = args
101
-
102
- if (currentBranches.length === 0) {
103
- return { ran: true, opened: [], skipped: [] }
104
- }
105
-
106
- const repoName = await getRepoName()
107
- const existingTitles = await listCmuxWorkspaceTitles()
108
-
109
- const opened: string[] = []
110
- const skipped: string[] = []
111
-
112
- for (const branch of currentBranches) {
113
- const title = buildCmuxWorkspaceTitle({ repoName, branch })
114
-
115
- // existingTitles holds canonical keys; match the built title the same way so
116
- // dedup survives whitespace / cross-CLI-version title drift (no duplicates).
117
- if (existingTitles.has(canonicalizeCmuxTitle(title))) {
118
- skipped.push(title)
119
- continue
120
- }
121
-
122
- try {
123
- await openCmuxWorkspaceWithLayout({ cwd: `${worktreeDir}/${branch}`, title })
124
- opened.push(title)
125
- } catch (error) {
126
- logger.warn({ error, title }, `⚠️ Failed to open cmux workspace for ${branch}`)
127
- }
128
- }
129
-
130
- return { ran: true, opened, skipped }
131
- }
132
-
133
- interface LogResultsContext {
134
- ideRan: boolean
135
- cmuxRan: boolean
136
- }
137
-
138
- const logResults = (result: WorktreesOpenResult, context: LogResultsContext): void => {
139
- if (context.ideRan) {
140
- const ideLabels = result.ideProviders
141
- .map((provider) => {
142
- return ideProviderLabel(provider)
143
- })
144
- .join(', ')
145
-
146
- if (result.ideFoldersAdded > 0) {
147
- logger.info(`✅ Added ${result.ideFoldersAdded} folder(s) to ${ideLabels} workspace(s)`)
148
- }
149
-
150
- if (result.ideFoldersRemoved > 0) {
151
- logger.info(`🧹 Removed ${result.ideFoldersRemoved} dangling folder(s) from ${ideLabels} workspace(s)`)
152
- }
153
- }
154
-
155
- if (result.openedCmux.length > 0) {
156
- logger.info('✅ Opened cmux workspaces:')
157
- for (const title of result.openedCmux) {
158
- logger.info(title)
159
- }
160
- }
161
-
162
- if (result.skippedCmux.length > 0) {
163
- logger.info(`ℹ️ Skipped ${result.skippedCmux.length} cmux workspace(s) already open`)
164
- }
165
-
166
- if (
167
- !context.ideRan &&
168
- result.openedCmux.length === 0 &&
169
- result.skippedCmux.length === 0 &&
170
- result.ideFoldersAdded === 0 &&
171
- result.ideFoldersRemoved === 0
172
- ) {
173
- logger.info('ℹ️ Nothing to open')
174
- }
175
- }
176
-
177
- // MCP Tool Registration
178
- export const worktreesOpenMcpTool = defineMcpTool({
179
- name: 'worktrees-open',
180
- description:
181
- 'Open every configured editor (Cursor and/or Zed) against its workspace and ensure a cmux workspace exists for each existing release worktree. Idempotent and additive — never removes worktrees, never recreates running cmux workspaces. Use after a cold start (editor + cmux closed). For stale-worktree cleanup, use worktrees-sync.',
182
- inputSchema: {},
183
- outputSchema: {
184
- openedCmux: z.array(z.string()).describe('Titles of cmux workspaces opened during this run'),
185
- skippedCmux: z.array(z.string()).describe('Titles of cmux workspaces that were already open'),
186
- ideProviders: z
187
- .array(z.string())
188
- .describe('Configured IDE providers that were opened (cursor | zed); empty if none configured'),
189
- ideFoldersAdded: z.number().describe('Total worktree folders added across all configured editor workspaces'),
190
- ideFoldersRemoved: z
191
- .number()
192
- .describe('Total dangling worktree folders removed across all configured editor workspaces'),
193
- },
194
- handler: worktreesOpen,
195
- })