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
@@ -0,0 +1,212 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`command catalog — MCP exposure policy > matches the golden MCP tools/list surface (names + schema shape) 1`] = `
4
+ [
5
+ {
6
+ "input": [
7
+ "all",
8
+ "root",
9
+ ],
10
+ "name": "audit",
11
+ "output": [
12
+ "allPassed",
13
+ "packages",
14
+ ],
15
+ },
16
+ {
17
+ "input": [],
18
+ "name": "env-clear",
19
+ "output": [
20
+ "filePath",
21
+ "unsetStatements",
22
+ "variableCount",
23
+ ],
24
+ },
25
+ {
26
+ "input": [],
27
+ "name": "env-list",
28
+ "output": [
29
+ "configs",
30
+ "project",
31
+ ],
32
+ },
33
+ {
34
+ "input": [
35
+ "config",
36
+ ],
37
+ "name": "env-load",
38
+ "output": [
39
+ "config",
40
+ "filePath",
41
+ "project",
42
+ "variableCount",
43
+ ],
44
+ },
45
+ {
46
+ "input": [],
47
+ "name": "env-status",
48
+ "output": [
49
+ "autoLoaded",
50
+ "cleared",
51
+ "sessionConfig",
52
+ "sessionId",
53
+ "sessionLoadedAt",
54
+ "sessionLoadedCount",
55
+ "sessionProject",
56
+ "sessionTotalCount",
57
+ ],
58
+ },
59
+ {
60
+ "input": [
61
+ "all",
62
+ ],
63
+ "name": "gh-merge-dev",
64
+ "output": [
65
+ "failedBranches",
66
+ "failedMerges",
67
+ "successfulMerges",
68
+ "totalBranches",
69
+ ],
70
+ },
71
+ {
72
+ "input": [
73
+ "env",
74
+ "skipTerraform",
75
+ "version",
76
+ ],
77
+ "name": "gh-release-deploy-all",
78
+ "output": [
79
+ "environment",
80
+ "releaseBranch",
81
+ "skipTerraformDeploy",
82
+ "success",
83
+ "version",
84
+ ],
85
+ },
86
+ {
87
+ "input": [
88
+ "env",
89
+ "services",
90
+ "skipTerraform",
91
+ "version",
92
+ ],
93
+ "name": "gh-release-deploy-selected",
94
+ "output": [
95
+ "environment",
96
+ "releaseBranch",
97
+ "services",
98
+ "skipTerraformDeploy",
99
+ "success",
100
+ "version",
101
+ ],
102
+ },
103
+ {
104
+ "input": [],
105
+ "name": "gh-release-list",
106
+ "output": [
107
+ "count",
108
+ "releases",
109
+ ],
110
+ },
111
+ {
112
+ "input": [
113
+ "releases",
114
+ ],
115
+ "name": "release-create",
116
+ "output": [
117
+ "createdBranches",
118
+ "failedReleases",
119
+ "failureCount",
120
+ "releases",
121
+ "successCount",
122
+ ],
123
+ },
124
+ {
125
+ "input": [
126
+ "description",
127
+ "version",
128
+ ],
129
+ "name": "release-desc-edit",
130
+ "output": [
131
+ "branch",
132
+ "changed",
133
+ "jiraVersionUrl",
134
+ "newDescription",
135
+ "previousDescription",
136
+ "version",
137
+ ],
138
+ },
139
+ {
140
+ "input": [],
141
+ "name": "vendor-check",
142
+ "output": [
143
+ "added",
144
+ "fileCount",
145
+ "modified",
146
+ "ok",
147
+ "removed",
148
+ "status",
149
+ ],
150
+ },
151
+ {
152
+ "input": [
153
+ "repos",
154
+ ],
155
+ "name": "vendor-diff",
156
+ "output": [
157
+ "drifted",
158
+ "entries",
159
+ "ok",
160
+ ],
161
+ },
162
+ {
163
+ "input": [],
164
+ "name": "version",
165
+ "output": [
166
+ "version",
167
+ ],
168
+ },
169
+ {
170
+ "input": [
171
+ "all",
172
+ "cmux",
173
+ "cursor",
174
+ "githubDesktop",
175
+ "ide",
176
+ "versions",
177
+ ],
178
+ "name": "worktrees-add",
179
+ "output": [
180
+ "count",
181
+ "createdWorktrees",
182
+ ],
183
+ },
184
+ {
185
+ "input": [],
186
+ "name": "worktrees-list",
187
+ "output": [
188
+ "count",
189
+ "worktrees",
190
+ ],
191
+ },
192
+ {
193
+ "input": [],
194
+ "name": "worktrees-reload",
195
+ "output": [
196
+ "closedCmux",
197
+ "ideFoldersAdded",
198
+ "ideFoldersRemoved",
199
+ "ideProviders",
200
+ "openedCmux",
201
+ ],
202
+ },
203
+ {
204
+ "input": [],
205
+ "name": "worktrees-sync",
206
+ "output": [
207
+ "count",
208
+ "removedWorktrees",
209
+ ],
210
+ },
211
+ ]
212
+ `;
@@ -0,0 +1,193 @@
1
+ import { describe, expect, it } from 'vitest'
2
+
3
+ import { commandCatalog, getExposedMcpTools, getMenuGroupCommands } from '../command-catalog'
4
+
5
+ // The exact MCP tool surface that was hand-listed in src/mcp/tools/index.ts
6
+ // before the catalog refactor. The catalog must keep this byte-for-byte.
7
+ const EXPECTED_EXPOSED_TOOLS = [
8
+ 'env-status',
9
+ 'env-list',
10
+ 'env-load',
11
+ 'env-clear',
12
+ 'gh-merge-dev',
13
+ 'release-create',
14
+ 'release-desc-edit',
15
+ 'gh-release-deploy-all',
16
+ 'gh-release-deploy-selected',
17
+ 'gh-release-list',
18
+ 'audit',
19
+ 'vendor-check',
20
+ 'vendor-diff',
21
+ 'version',
22
+ 'worktrees-add',
23
+ 'worktrees-list',
24
+ 'worktrees-reload',
25
+ 'worktrees-sync',
26
+ ]
27
+
28
+ // Deliberately NOT exposed as MCP tools (mutating / host-inspecting / irreversible).
29
+ // release-deliver (prod delivery + admin-merge) and worktrees-remove (rm -rf) are
30
+ // CLI-only by design.
31
+ const EXPECTED_UNEXPOSED_WITH_TOOL = [
32
+ 'doctor',
33
+ 'vendor-sync',
34
+ 'vendor-manifest',
35
+ 'gh-release-deliver',
36
+ 'worktrees-remove',
37
+ ]
38
+
39
+ describe('command catalog — MCP exposure policy', () => {
40
+ it('exposes exactly the expected 18 MCP tools (set-equal, order-independent)', () => {
41
+ const exposedNames = getExposedMcpTools()
42
+ .map((tool) => {
43
+ return tool.name
44
+ })
45
+ .sort()
46
+
47
+ expect(exposedNames).toEqual([...EXPECTED_EXPOSED_TOOLS].sort())
48
+ expect(exposedNames).toHaveLength(18)
49
+ })
50
+
51
+ it('never exposes the irreversible release-deliver / worktrees-remove tools', () => {
52
+ const exposedNames = new Set(
53
+ getExposedMcpTools().map((tool) => {
54
+ return tool.name
55
+ }),
56
+ )
57
+
58
+ expect(exposedNames.has('gh-release-deliver')).toBe(false)
59
+ expect(exposedNames.has('worktrees-remove')).toBe(false)
60
+ })
61
+
62
+ it('keeps doctor, vendor-sync and vendor-manifest UNEXPOSED even though they have tools', () => {
63
+ const exposedNames = new Set(
64
+ getExposedMcpTools().map((tool) => {
65
+ return tool.name
66
+ }),
67
+ )
68
+
69
+ for (const unexposed of EXPECTED_UNEXPOSED_WITH_TOOL) {
70
+ const entry = commandCatalog.find((candidate) => {
71
+ return candidate.mcpTool?.name === unexposed
72
+ })
73
+
74
+ expect(entry, `catalog should carry a tool for ${unexposed}`).toBeDefined()
75
+ expect(entry?.mcpExposed, `${unexposed} must stay unexposed`).toBe(false)
76
+ expect(exposedNames.has(unexposed), `${unexposed} must not be registered`).toBe(false)
77
+ }
78
+ })
79
+
80
+ it('every exposed entry carries a tool, and every entry with mcpExposed=true has one', () => {
81
+ for (const entry of commandCatalog) {
82
+ if (entry.mcpExposed) {
83
+ expect(entry.mcpTool, `${entry.cliName} is exposed but has no tool`).not.toBeNull()
84
+ }
85
+ }
86
+ })
87
+
88
+ it('mcpTool name matches a stable identifier (no duplicate registrations)', () => {
89
+ const names = getExposedMcpTools().map((tool) => {
90
+ return tool.name
91
+ })
92
+
93
+ expect(new Set(names).size).toBe(names.length)
94
+ })
95
+
96
+ // Golden snapshot of the registered MCP surface: tool name + input/output
97
+ // schema field names. Locks names AND schema shape so any accidental change to
98
+ // the exposed tools/list fails CI (the "tools/list identical" guardrail).
99
+ it('matches the golden MCP tools/list surface (names + schema shape)', () => {
100
+ const surface = getExposedMcpTools()
101
+ .map((tool) => {
102
+ return {
103
+ name: tool.name,
104
+ input: Object.keys(tool.inputSchema).sort(),
105
+ output: Object.keys(tool.outputSchema).sort(),
106
+ }
107
+ })
108
+ .sort((a, b) => {
109
+ return a.name.localeCompare(b.name)
110
+ })
111
+
112
+ expect(surface).toMatchSnapshot()
113
+ })
114
+ })
115
+
116
+ describe('command catalog — CLI/MCP name parity', () => {
117
+ // The authoritative (cliName -> mcpName) map. Divergences are INTENTIONAL and
118
+ // grandfathered here (the `gh-` prefix on release tools). Any new accidental
119
+ // divergence — or a typo that renames an MCP tool — fails this test.
120
+ const EXPECTED_PARITY: Record<string, string> = {
121
+ 'merge-dev': 'gh-merge-dev',
122
+ 'release-list': 'gh-release-list',
123
+ 'release-create': 'release-create',
124
+ 'release-desc-edit': 'release-desc-edit',
125
+ 'release-deploy-all': 'gh-release-deploy-all',
126
+ 'release-deploy-selected': 'gh-release-deploy-selected',
127
+ 'release-deliver': 'gh-release-deliver',
128
+ 'worktrees-add': 'worktrees-add',
129
+ 'worktrees-list': 'worktrees-list',
130
+ 'worktrees-reload': 'worktrees-reload',
131
+ 'worktrees-remove': 'worktrees-remove',
132
+ 'worktrees-sync': 'worktrees-sync',
133
+ audit: 'audit',
134
+ doctor: 'doctor',
135
+ version: 'version',
136
+ 'env-status': 'env-status',
137
+ 'env-list': 'env-list',
138
+ 'env-load': 'env-load',
139
+ 'env-clear': 'env-clear',
140
+ 'vendor-check': 'vendor-check',
141
+ 'vendor-diff': 'vendor-diff',
142
+ 'vendor-manifest': 'vendor-manifest',
143
+ 'vendor-sync': 'vendor-sync',
144
+ }
145
+
146
+ it('every catalog entry with a tool matches its expected (cliName, mcpName) pair', () => {
147
+ const actualParity: Record<string, string> = {}
148
+
149
+ for (const entry of commandCatalog) {
150
+ if (entry.mcpTool) {
151
+ actualParity[entry.cliName] = entry.mcpTool.name
152
+ }
153
+ }
154
+
155
+ expect(actualParity).toEqual(EXPECTED_PARITY)
156
+ })
157
+ })
158
+
159
+ describe('command catalog — menu grouping', () => {
160
+ it('preserves the three menu groups in display order', () => {
161
+ expect(getMenuGroupCommands('release')).toEqual([
162
+ 'merge-dev',
163
+ 'release-list',
164
+ 'release-create',
165
+ 'release-desc-edit',
166
+ 'release-deploy-all',
167
+ 'release-deploy-selected',
168
+ 'release-deliver',
169
+ ])
170
+
171
+ expect(getMenuGroupCommands('worktrees')).toEqual([
172
+ 'worktrees-add',
173
+ 'worktrees-list',
174
+ 'worktrees-reload',
175
+ 'worktrees-remove',
176
+ 'worktrees-sync',
177
+ ])
178
+
179
+ expect(getMenuGroupCommands('environment')).toEqual([
180
+ 'audit',
181
+ 'vendor',
182
+ 'vendor-config',
183
+ 'doctor',
184
+ 'init',
185
+ 'version',
186
+ 'config',
187
+ 'env-status',
188
+ 'env-list',
189
+ 'env-load',
190
+ 'env-clear',
191
+ ])
192
+ })
193
+ })
@@ -0,0 +1,141 @@
1
+ import type { z } from 'zod'
2
+
3
+ import { auditMcpTool } from 'src/commands/audit'
4
+ import { doctorMcpTool } from 'src/commands/doctor'
5
+ import { envClearMcpTool } from 'src/commands/env-clear'
6
+ import { envListMcpTool } from 'src/commands/env-list'
7
+ import { envLoadMcpTool } from 'src/commands/env-load'
8
+ import { envStatusMcpTool } from 'src/commands/env-status'
9
+ import { ghMergeDevMcpTool } from 'src/commands/gh-merge-dev'
10
+ import { ghReleaseDeliverMcpTool } from 'src/commands/gh-release-deliver'
11
+ import { ghReleaseDeployAllMcpTool } from 'src/commands/gh-release-deploy-all'
12
+ import { ghReleaseDeploySelectedMcpTool } from 'src/commands/gh-release-deploy-selected'
13
+ import { ghReleaseListMcpTool } from 'src/commands/gh-release-list'
14
+ import { releaseCreateMcpTool } from 'src/commands/release-create'
15
+ import { releaseDescEditMcpTool } from 'src/commands/release-desc-edit'
16
+ import { vendorCheckMcpTool } from 'src/commands/vendor-check'
17
+ import { vendorDiffMcpTool } from 'src/commands/vendor-diff'
18
+ import { vendorManifestMcpTool } from 'src/commands/vendor-manifest'
19
+ import { vendorSyncMcpTool } from 'src/commands/vendor-sync'
20
+ import { versionMcpTool } from 'src/commands/version'
21
+ import { worktreesAddMcpTool } from 'src/commands/worktrees-add'
22
+ import { worktreesListMcpTool } from 'src/commands/worktrees-list'
23
+ import { worktreesReloadMcpTool } from 'src/commands/worktrees-reload'
24
+ import { worktreesRemoveMcpTool } from 'src/commands/worktrees-remove'
25
+ import { worktreesSyncMcpTool } from 'src/commands/worktrees-sync'
26
+ import type { ToolsExecutionResult } from 'src/types'
27
+
28
+ /**
29
+ * Registration-facing shape of an MCP tool. The concrete `*McpTool` definitions
30
+ * are generic over their Zod input/output shapes (and invariant), so they cannot
31
+ * share one precise `McpTool<...>` element type in an array. This widened, non-
32
+ * generic view exposes exactly what registration needs and every concrete tool
33
+ * assigns to it. Matches the loose handler typing already used in tool-handler.
34
+ */
35
+ export interface CatalogMcpTool {
36
+ name: string
37
+ description: string
38
+ inputSchema: z.ZodRawShape
39
+ outputSchema: z.ZodRawShape
40
+ // Heterogeneous tool params; loose `any` mirrors the existing tool-handler typing.
41
+ handler: (params: any) => Promise<ToolsExecutionResult>
42
+ }
43
+
44
+ /**
45
+ * Single source of truth for the CLI command surface. It consolidates what used
46
+ * to live in three hand-maintained places (the MCP `tools[]` array and the
47
+ * three no-arg-menu name arrays) into one list, so they can no longer drift.
48
+ *
49
+ * It does NOT replace Commander's `.command().option()` wiring in entry/cli.ts —
50
+ * that stays the source of truth for argument parsing. This catalog only carries
51
+ * cross-surface metadata: the canonical names, which menu group a command shows
52
+ * in, and whether the command is exposed as an MCP tool.
53
+ */
54
+
55
+ /** Top-level menu group for the no-arg interactive picker (null = not shown). */
56
+ export type MenuGroup = 'release' | 'worktrees' | 'environment'
57
+
58
+ export interface CommandCatalogEntry {
59
+ /** CLI command name as registered in Commander (flat form, e.g. `merge-dev`). */
60
+ cliName: string
61
+ /** Menu group, or null for subcommands not shown at the top level. */
62
+ menuGroup: MenuGroup | null
63
+ /** The co-located MCP tool, or null for CLI-only commands (init/config/vendor group). */
64
+ mcpTool: CatalogMcpTool | null
65
+ /**
66
+ * Whether the command is registered as an MCP tool. Explicit allowlist:
67
+ * `doctor`, `vendor-sync`, and `vendor-manifest` are deliberately UNEXPOSED
68
+ * (vendor-sync/manifest mutate consumer repos; doctor is host-inspecting), so
69
+ * they must never become agent-callable by accident.
70
+ */
71
+ mcpExposed: boolean
72
+ }
73
+
74
+ /**
75
+ * Authored in no-arg-menu display order so the interactive picker derives
76
+ * directly from this list (see entry/cli.ts). MCP registration filters this
77
+ * list by `mcpExposed`; MCP tool order is not contractual (clients address
78
+ * tools by name), so the registration order need not match the array order.
79
+ */
80
+ export const commandCatalog: CommandCatalogEntry[] = [
81
+ // --- Release Management (menu group) ---
82
+ { cliName: 'merge-dev', menuGroup: 'release', mcpTool: ghMergeDevMcpTool, mcpExposed: true },
83
+ { cliName: 'release-list', menuGroup: 'release', mcpTool: ghReleaseListMcpTool, mcpExposed: true },
84
+ { cliName: 'release-create', menuGroup: 'release', mcpTool: releaseCreateMcpTool, mcpExposed: true },
85
+ { cliName: 'release-desc-edit', menuGroup: 'release', mcpTool: releaseDescEditMcpTool, mcpExposed: true },
86
+ { cliName: 'release-deploy-all', menuGroup: 'release', mcpTool: ghReleaseDeployAllMcpTool, mcpExposed: true },
87
+ {
88
+ cliName: 'release-deploy-selected',
89
+ menuGroup: 'release',
90
+ mcpTool: ghReleaseDeploySelectedMcpTool,
91
+ mcpExposed: true,
92
+ },
93
+ // release-deliver does prod delivery + admin-merge — genuinely irreversible,
94
+ // so it is CLI-only by design (mirrors the vendor-sync/manifest rationale).
95
+ { cliName: 'release-deliver', menuGroup: 'release', mcpTool: ghReleaseDeliverMcpTool, mcpExposed: false },
96
+
97
+ // --- Worktrees (menu group) ---
98
+ { cliName: 'worktrees-add', menuGroup: 'worktrees', mcpTool: worktreesAddMcpTool, mcpExposed: true },
99
+ { cliName: 'worktrees-list', menuGroup: 'worktrees', mcpTool: worktreesListMcpTool, mcpExposed: true },
100
+ { cliName: 'worktrees-reload', menuGroup: 'worktrees', mcpTool: worktreesReloadMcpTool, mcpExposed: true },
101
+ // worktrees-remove runs rm -rf on worktree dirs — genuinely irreversible, so
102
+ // it is CLI-only by design (mirrors the vendor-sync/manifest rationale).
103
+ { cliName: 'worktrees-remove', menuGroup: 'worktrees', mcpTool: worktreesRemoveMcpTool, mcpExposed: false },
104
+ { cliName: 'worktrees-sync', menuGroup: 'worktrees', mcpTool: worktreesSyncMcpTool, mcpExposed: true },
105
+
106
+ // --- Environment (menu group) ---
107
+ { cliName: 'audit', menuGroup: 'environment', mcpTool: auditMcpTool, mcpExposed: true },
108
+ { cliName: 'vendor', menuGroup: 'environment', mcpTool: null, mcpExposed: false },
109
+ { cliName: 'vendor-config', menuGroup: 'environment', mcpTool: null, mcpExposed: false },
110
+ { cliName: 'doctor', menuGroup: 'environment', mcpTool: doctorMcpTool, mcpExposed: false },
111
+ { cliName: 'init', menuGroup: 'environment', mcpTool: null, mcpExposed: false },
112
+ { cliName: 'version', menuGroup: 'environment', mcpTool: versionMcpTool, mcpExposed: true },
113
+ { cliName: 'config', menuGroup: 'environment', mcpTool: null, mcpExposed: false },
114
+ { cliName: 'env-status', menuGroup: 'environment', mcpTool: envStatusMcpTool, mcpExposed: true },
115
+ { cliName: 'env-list', menuGroup: 'environment', mcpTool: envListMcpTool, mcpExposed: true },
116
+ { cliName: 'env-load', menuGroup: 'environment', mcpTool: envLoadMcpTool, mcpExposed: true },
117
+ { cliName: 'env-clear', menuGroup: 'environment', mcpTool: envClearMcpTool, mcpExposed: true },
118
+ // Internal shell-startup trigger; hidden from the menu and never an MCP tool
119
+ // (it can't apply env to a shell — only the zsh integration sources the file).
120
+ { cliName: 'env-autoload', menuGroup: null, mcpTool: null, mcpExposed: false },
121
+
122
+ // --- vendor subcommands (not top-level menu items; MCP tools where applicable) ---
123
+ { cliName: 'vendor-check', menuGroup: null, mcpTool: vendorCheckMcpTool, mcpExposed: true },
124
+ { cliName: 'vendor-diff', menuGroup: null, mcpTool: vendorDiffMcpTool, mcpExposed: true },
125
+ { cliName: 'vendor-manifest', menuGroup: null, mcpTool: vendorManifestMcpTool, mcpExposed: false },
126
+ { cliName: 'vendor-sync', menuGroup: null, mcpTool: vendorSyncMcpTool, mcpExposed: false },
127
+ ]
128
+
129
+ /** The MCP tools to register: catalog entries that are exposed and carry a tool. */
130
+ export const getExposedMcpTools = (): CatalogMcpTool[] => {
131
+ return commandCatalog.flatMap((entry) => {
132
+ return entry.mcpExposed && entry.mcpTool ? [entry.mcpTool] : []
133
+ })
134
+ }
135
+
136
+ /** CLI command names for a menu group, in catalog (display) order. */
137
+ export const getMenuGroupCommands = (group: MenuGroup): string[] => {
138
+ return commandCatalog.flatMap((entry) => {
139
+ return entry.menuGroup === group ? [entry.cliName] : []
140
+ })
141
+ }
@@ -0,0 +1,2 @@
1
+ export { commandCatalog, getExposedMcpTools, getMenuGroupCommands } from './command-catalog'
2
+ export type { CommandCatalogEntry, MenuGroup } from './command-catalog'
@@ -0,0 +1,127 @@
1
+ import { beforeEach, describe, expect, it, vi } from 'vitest'
2
+
3
+ import { logger } from 'src/lib/logger'
4
+
5
+ import { commandEcho } from '../command-echo'
6
+
7
+ vi.mock('src/lib/logger', () => {
8
+ return { logger: { info: vi.fn(), warn: vi.fn(), error: vi.fn() } }
9
+ })
10
+
11
+ const infoMock = vi.mocked(logger.info)
12
+
13
+ // The single printed argument, or undefined when print() emitted nothing.
14
+ const printedLine = (): string | undefined => {
15
+ return infoMock.mock.calls[0]?.[0] as string | undefined
16
+ }
17
+
18
+ beforeEach(() => {
19
+ commandEcho.reset()
20
+ infoMock.mockClear()
21
+ })
22
+
23
+ describe('commandEcho.print', () => {
24
+ it('emits nothing when the command was not interactive', () => {
25
+ commandEcho.start('release-create')
26
+ commandEcho.addOption('--yes', true)
27
+
28
+ commandEcho.print()
29
+
30
+ expect(infoMock).not.toHaveBeenCalled()
31
+ })
32
+
33
+ it('emits nothing when interactive but no options were recorded', () => {
34
+ commandEcho.start('release-create')
35
+ commandEcho.setInteractive()
36
+
37
+ commandEcho.print()
38
+
39
+ expect(infoMock).not.toHaveBeenCalled()
40
+ })
41
+
42
+ it('renders a boolean true option as a bare flag and omits boolean false', () => {
43
+ commandEcho.start('release-create')
44
+ commandEcho.setInteractive()
45
+ commandEcho.addOption('--yes', true)
46
+ commandEcho.addOption('--all', false)
47
+
48
+ commandEcho.print()
49
+
50
+ const line = printedLine()
51
+
52
+ expect(line).toContain('--yes')
53
+ expect(line).not.toContain('--all')
54
+ })
55
+
56
+ it('quotes a string option value', () => {
57
+ commandEcho.start('release-create')
58
+ commandEcho.setInteractive()
59
+ commandEcho.addOption('--release', 'checkout-redesign:regular:Q3 work')
60
+
61
+ commandEcho.print()
62
+
63
+ expect(printedLine()).toContain('--release "checkout-redesign:regular:Q3 work"')
64
+ })
65
+
66
+ it('joins and quotes an array option value', () => {
67
+ commandEcho.start('gh-merge-dev')
68
+ commandEcho.setInteractive()
69
+ commandEcho.addOption('--versions', ['1.2.5', '1.3.0'])
70
+
71
+ commandEcho.print()
72
+
73
+ expect(printedLine()).toContain('--versions "1.2.5, 1.3.0"')
74
+ })
75
+
76
+ it('formats the full equivalent command and round-trips the release description', () => {
77
+ commandEcho.start('release-create')
78
+ commandEcho.setInteractive()
79
+ commandEcho.addOption('--release', 'sponsored-banners-scroll-fix:regular:Fix scroll bug in results pages')
80
+ commandEcho.addOption('--yes', true)
81
+
82
+ commandEcho.print()
83
+
84
+ expect(printedLine()).toBe(
85
+ '📟 Equivalent command: \npnpm exec infra-kit release-create --release "sponsored-banners-scroll-fix:regular:Fix scroll bug in results pages" --yes\n',
86
+ )
87
+ })
88
+
89
+ it('preserves option order and drops false-valued flags from the middle', () => {
90
+ commandEcho.start('release-create')
91
+ commandEcho.setInteractive()
92
+ commandEcho.addOption('--release', 'one')
93
+ commandEcho.addOption('--all', false)
94
+ commandEcho.addOption('--yes', true)
95
+
96
+ commandEcho.print()
97
+
98
+ expect(printedLine()).toBe('📟 Equivalent command: \npnpm exec infra-kit release-create --release "one" --yes\n')
99
+ })
100
+ })
101
+
102
+ describe('commandEcho lifecycle', () => {
103
+ it('start() clears options and interactive state from a prior command', () => {
104
+ commandEcho.start('release-create')
105
+ commandEcho.setInteractive()
106
+ commandEcho.addOption('--release', 'stale')
107
+
108
+ commandEcho.start('worktrees-add')
109
+ commandEcho.addOption('--versions', ['1.0.0'])
110
+
111
+ commandEcho.print()
112
+
113
+ // start() reset isInteractive to false, so print() stays silent.
114
+ expect(infoMock).not.toHaveBeenCalled()
115
+ })
116
+
117
+ it('reset() clears all state so a later print() emits nothing', () => {
118
+ commandEcho.start('release-create')
119
+ commandEcho.setInteractive()
120
+ commandEcho.addOption('--yes', true)
121
+
122
+ commandEcho.reset()
123
+ commandEcho.print()
124
+
125
+ expect(infoMock).not.toHaveBeenCalled()
126
+ })
127
+ })
@@ -10,6 +10,19 @@ export const INFRA_KIT_SESSION_VAR = 'INFRA_KIT_SESSION'
10
10
  export const INFRA_KIT_ENV_CONFIG_VAR = 'INFRA_KIT_ENV_CONFIG'
11
11
  export const INFRA_KIT_ENV_PROJECT_VAR = 'INFRA_KIT_ENV_PROJECT'
12
12
  export const INFRA_KIT_ENV_LOADED_AT_VAR = 'INFRA_KIT_ENV_LOADED_AT'
13
+ /**
14
+ * Marker exported into env-load.sh ONLY when the load was triggered automatically
15
+ * (see lib/env-autoload). Its presence is the sole signal distinguishing an
16
+ * auto-loaded env from a deliberate manual `env-load`, so auto-load never
17
+ * clobbers a manual choice. A manual load unsets it.
18
+ */
19
+ export const INFRA_KIT_ENV_AUTOLOADED_VAR = 'INFRA_KIT_ENV_AUTOLOADED'
20
+ /**
21
+ * Suppression sentinel exported by env-clear. While set in a shell, cli-invocation
22
+ * auto-load stays silent (a deliberate clear must not be immediately re-loaded).
23
+ * Lifted by a manual `env-load` or a new shell.
24
+ */
25
+ export const INFRA_KIT_ENV_CLEARED_VAR = 'INFRA_KIT_ENV_CLEARED'
13
26
 
14
27
  /**
15
28
  * Matches a line of the form `KEY=...` where KEY is an env-var identifier