patchwork-os 0.2.0-alpha.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 (721) hide show
  1. package/LICENSE +21 -0
  2. package/README.bridge.md +352 -0
  3. package/README.md +72 -0
  4. package/deploy/README.md +172 -0
  5. package/deploy/bootstrap-new-vps.sh +364 -0
  6. package/deploy/claude-ide-bridge.service.template +67 -0
  7. package/deploy/claude-ide-bridge@.service +31 -0
  8. package/deploy/ecosystem.config.js.example +36 -0
  9. package/deploy/install-vps-service.sh +240 -0
  10. package/deploy/nginx-claude-bridge.conf.template +129 -0
  11. package/dist/activityLog.d.ts +112 -0
  12. package/dist/activityLog.js +399 -0
  13. package/dist/activityLog.js.map +1 -0
  14. package/dist/activityTypes.d.ts +28 -0
  15. package/dist/activityTypes.js +9 -0
  16. package/dist/activityTypes.js.map +1 -0
  17. package/dist/adapters/base.d.ts +78 -0
  18. package/dist/adapters/base.js +14 -0
  19. package/dist/adapters/base.js.map +1 -0
  20. package/dist/adapters/claude.d.ts +18 -0
  21. package/dist/adapters/claude.js +276 -0
  22. package/dist/adapters/claude.js.map +1 -0
  23. package/dist/adapters/gemini.d.ts +17 -0
  24. package/dist/adapters/gemini.js +218 -0
  25. package/dist/adapters/gemini.js.map +1 -0
  26. package/dist/adapters/grok.d.ts +7 -0
  27. package/dist/adapters/grok.js +21 -0
  28. package/dist/adapters/grok.js.map +1 -0
  29. package/dist/adapters/index.d.ts +5 -0
  30. package/dist/adapters/index.js +37 -0
  31. package/dist/adapters/index.js.map +1 -0
  32. package/dist/adapters/local.d.ts +7 -0
  33. package/dist/adapters/local.js +22 -0
  34. package/dist/adapters/local.js.map +1 -0
  35. package/dist/adapters/openai.d.ts +22 -0
  36. package/dist/adapters/openai.js +284 -0
  37. package/dist/adapters/openai.js.map +1 -0
  38. package/dist/adapters/sse.d.ts +13 -0
  39. package/dist/adapters/sse.js +58 -0
  40. package/dist/adapters/sse.js.map +1 -0
  41. package/dist/analyticsAggregator.d.ts +28 -0
  42. package/dist/analyticsAggregator.js +133 -0
  43. package/dist/analyticsAggregator.js.map +1 -0
  44. package/dist/analyticsPrefs.d.ts +9 -0
  45. package/dist/analyticsPrefs.js +50 -0
  46. package/dist/analyticsPrefs.js.map +1 -0
  47. package/dist/analyticsSend.d.ts +12 -0
  48. package/dist/analyticsSend.js +34 -0
  49. package/dist/analyticsSend.js.map +1 -0
  50. package/dist/approvalHttp.d.ts +46 -0
  51. package/dist/approvalHttp.js +370 -0
  52. package/dist/approvalHttp.js.map +1 -0
  53. package/dist/approvalQueue.d.ts +49 -0
  54. package/dist/approvalQueue.js +84 -0
  55. package/dist/approvalQueue.js.map +1 -0
  56. package/dist/automation.d.ts +675 -0
  57. package/dist/automation.js +1038 -0
  58. package/dist/automation.js.map +1 -0
  59. package/dist/bridge.d.ts +85 -0
  60. package/dist/bridge.js +1535 -0
  61. package/dist/bridge.js.map +1 -0
  62. package/dist/bridgeLockDiscovery.d.ts +11 -0
  63. package/dist/bridgeLockDiscovery.js +49 -0
  64. package/dist/bridgeLockDiscovery.js.map +1 -0
  65. package/dist/bridgeToken.d.ts +22 -0
  66. package/dist/bridgeToken.js +114 -0
  67. package/dist/bridgeToken.js.map +1 -0
  68. package/dist/bridgeToolsRules.d.ts +20 -0
  69. package/dist/bridgeToolsRules.js +79 -0
  70. package/dist/bridgeToolsRules.js.map +1 -0
  71. package/dist/ccPermissions.d.ts +59 -0
  72. package/dist/ccPermissions.js +163 -0
  73. package/dist/ccPermissions.js.map +1 -0
  74. package/dist/claudeDriver.d.ts +129 -0
  75. package/dist/claudeDriver.js +459 -0
  76. package/dist/claudeDriver.js.map +1 -0
  77. package/dist/claudeMdPatch.d.ts +29 -0
  78. package/dist/claudeMdPatch.js +164 -0
  79. package/dist/claudeMdPatch.js.map +1 -0
  80. package/dist/claudeOrchestrator.d.ts +171 -0
  81. package/dist/claudeOrchestrator.js +591 -0
  82. package/dist/claudeOrchestrator.js.map +1 -0
  83. package/dist/commands/install.d.ts +1 -0
  84. package/dist/commands/install.js +158 -0
  85. package/dist/commands/install.js.map +1 -0
  86. package/dist/commands/marketplace.d.ts +11 -0
  87. package/dist/commands/marketplace.js +120 -0
  88. package/dist/commands/marketplace.js.map +1 -0
  89. package/dist/commands/patchworkInit.d.ts +14 -0
  90. package/dist/commands/patchworkInit.js +155 -0
  91. package/dist/commands/patchworkInit.js.map +1 -0
  92. package/dist/commands/task.d.ts +14 -0
  93. package/dist/commands/task.js +289 -0
  94. package/dist/commands/task.js.map +1 -0
  95. package/dist/commands/tokenEfficiency.d.ts +9 -0
  96. package/dist/commands/tokenEfficiency.js +211 -0
  97. package/dist/commands/tokenEfficiency.js.map +1 -0
  98. package/dist/commands/tools.d.ts +28 -0
  99. package/dist/commands/tools.js +326 -0
  100. package/dist/commands/tools.js.map +1 -0
  101. package/dist/commitIssueLinkLog.d.ts +77 -0
  102. package/dist/commitIssueLinkLog.js +142 -0
  103. package/dist/commitIssueLinkLog.js.map +1 -0
  104. package/dist/companions/registry.d.ts +12 -0
  105. package/dist/companions/registry.js +71 -0
  106. package/dist/companions/registry.js.map +1 -0
  107. package/dist/config.d.ts +105 -0
  108. package/dist/config.js +720 -0
  109. package/dist/config.js.map +1 -0
  110. package/dist/crypto.d.ts +16 -0
  111. package/dist/crypto.js +34 -0
  112. package/dist/crypto.js.map +1 -0
  113. package/dist/dashboard.d.ts +12 -0
  114. package/dist/dashboard.js +149 -0
  115. package/dist/dashboard.js.map +1 -0
  116. package/dist/decisionTraceLog.d.ts +77 -0
  117. package/dist/decisionTraceLog.js +147 -0
  118. package/dist/decisionTraceLog.js.map +1 -0
  119. package/dist/errors.d.ts +32 -0
  120. package/dist/errors.js +34 -0
  121. package/dist/errors.js.map +1 -0
  122. package/dist/extensionClient.d.ts +279 -0
  123. package/dist/extensionClient.js +1253 -0
  124. package/dist/extensionClient.js.map +1 -0
  125. package/dist/fileLock.d.ts +36 -0
  126. package/dist/fileLock.js +121 -0
  127. package/dist/fileLock.js.map +1 -0
  128. package/dist/fp/activityAnalytics.d.ts +39 -0
  129. package/dist/fp/activityAnalytics.js +111 -0
  130. package/dist/fp/activityAnalytics.js.map +1 -0
  131. package/dist/fp/async.d.ts +48 -0
  132. package/dist/fp/async.js +60 -0
  133. package/dist/fp/async.js.map +1 -0
  134. package/dist/fp/automationInterpreter.d.ts +37 -0
  135. package/dist/fp/automationInterpreter.js +523 -0
  136. package/dist/fp/automationInterpreter.js.map +1 -0
  137. package/dist/fp/automationProgram.d.ts +89 -0
  138. package/dist/fp/automationProgram.js +29 -0
  139. package/dist/fp/automationProgram.js.map +1 -0
  140. package/dist/fp/automationState.d.ts +135 -0
  141. package/dist/fp/automationState.js +206 -0
  142. package/dist/fp/automationState.js.map +1 -0
  143. package/dist/fp/automationUtils.d.ts +31 -0
  144. package/dist/fp/automationUtils.js +61 -0
  145. package/dist/fp/automationUtils.js.map +1 -0
  146. package/dist/fp/brandedTypes.d.ts +32 -0
  147. package/dist/fp/brandedTypes.js +41 -0
  148. package/dist/fp/brandedTypes.js.map +1 -0
  149. package/dist/fp/commandDescription.d.ts +18 -0
  150. package/dist/fp/commandDescription.js +125 -0
  151. package/dist/fp/commandDescription.js.map +1 -0
  152. package/dist/fp/extensionSnapshot.d.ts +10 -0
  153. package/dist/fp/extensionSnapshot.js +14 -0
  154. package/dist/fp/extensionSnapshot.js.map +1 -0
  155. package/dist/fp/index.d.ts +8 -0
  156. package/dist/fp/index.js +9 -0
  157. package/dist/fp/index.js.map +1 -0
  158. package/dist/fp/interpreterContext.d.ts +69 -0
  159. package/dist/fp/interpreterContext.js +56 -0
  160. package/dist/fp/interpreterContext.js.map +1 -0
  161. package/dist/fp/policyParser.d.ts +16 -0
  162. package/dist/fp/policyParser.js +334 -0
  163. package/dist/fp/policyParser.js.map +1 -0
  164. package/dist/fp/result.d.ts +38 -0
  165. package/dist/fp/result.js +57 -0
  166. package/dist/fp/result.js.map +1 -0
  167. package/dist/fp/tokenBucket.d.ts +27 -0
  168. package/dist/fp/tokenBucket.js +36 -0
  169. package/dist/fp/tokenBucket.js.map +1 -0
  170. package/dist/index.d.ts +2 -0
  171. package/dist/index.js +1465 -0
  172. package/dist/index.js.map +1 -0
  173. package/dist/instructionsUtils.d.ts +17 -0
  174. package/dist/instructionsUtils.js +38 -0
  175. package/dist/instructionsUtils.js.map +1 -0
  176. package/dist/lockfile.d.ts +16 -0
  177. package/dist/lockfile.js +172 -0
  178. package/dist/lockfile.js.map +1 -0
  179. package/dist/logger.d.ts +16 -0
  180. package/dist/logger.js +68 -0
  181. package/dist/logger.js.map +1 -0
  182. package/dist/oauth.d.ts +105 -0
  183. package/dist/oauth.js +880 -0
  184. package/dist/oauth.js.map +1 -0
  185. package/dist/orchestrator/childBridgeClient.d.ts +33 -0
  186. package/dist/orchestrator/childBridgeClient.js +321 -0
  187. package/dist/orchestrator/childBridgeClient.js.map +1 -0
  188. package/dist/orchestrator/childBridgeRegistry.d.ts +67 -0
  189. package/dist/orchestrator/childBridgeRegistry.js +297 -0
  190. package/dist/orchestrator/childBridgeRegistry.js.map +1 -0
  191. package/dist/orchestrator/index.d.ts +3 -0
  192. package/dist/orchestrator/index.js +3 -0
  193. package/dist/orchestrator/index.js.map +1 -0
  194. package/dist/orchestrator/orchestratorBridge.d.ts +32 -0
  195. package/dist/orchestrator/orchestratorBridge.js +412 -0
  196. package/dist/orchestrator/orchestratorBridge.js.map +1 -0
  197. package/dist/orchestrator/orchestratorConfig.d.ts +11 -0
  198. package/dist/orchestrator/orchestratorConfig.js +85 -0
  199. package/dist/orchestrator/orchestratorConfig.js.map +1 -0
  200. package/dist/orchestrator/orchestratorTools.d.ts +16 -0
  201. package/dist/orchestrator/orchestratorTools.js +272 -0
  202. package/dist/orchestrator/orchestratorTools.js.map +1 -0
  203. package/dist/patchworkCli.d.ts +15 -0
  204. package/dist/patchworkCli.js +41 -0
  205. package/dist/patchworkCli.js.map +1 -0
  206. package/dist/patchworkConfig.d.ts +28 -0
  207. package/dist/patchworkConfig.js +30 -0
  208. package/dist/patchworkConfig.js.map +1 -0
  209. package/dist/plugin.d.ts +106 -0
  210. package/dist/plugin.js +31 -0
  211. package/dist/plugin.js.map +1 -0
  212. package/dist/pluginLoader.d.ts +44 -0
  213. package/dist/pluginLoader.js +357 -0
  214. package/dist/pluginLoader.js.map +1 -0
  215. package/dist/pluginWatcher.d.ts +24 -0
  216. package/dist/pluginWatcher.js +139 -0
  217. package/dist/pluginWatcher.js.map +1 -0
  218. package/dist/preToolUseHook.d.ts +10 -0
  219. package/dist/preToolUseHook.js +57 -0
  220. package/dist/preToolUseHook.js.map +1 -0
  221. package/dist/probe.d.ts +35 -0
  222. package/dist/probe.js +143 -0
  223. package/dist/probe.js.map +1 -0
  224. package/dist/prompts.d.ts +27 -0
  225. package/dist/prompts.js +1680 -0
  226. package/dist/prompts.js.map +1 -0
  227. package/dist/quickTaskPresets.d.ts +64 -0
  228. package/dist/quickTaskPresets.js +156 -0
  229. package/dist/quickTaskPresets.js.map +1 -0
  230. package/dist/recipes/compiler.d.ts +44 -0
  231. package/dist/recipes/compiler.js +140 -0
  232. package/dist/recipes/compiler.js.map +1 -0
  233. package/dist/recipes/installer.d.ts +25 -0
  234. package/dist/recipes/installer.js +62 -0
  235. package/dist/recipes/installer.js.map +1 -0
  236. package/dist/recipes/parser.d.ts +18 -0
  237. package/dist/recipes/parser.js +160 -0
  238. package/dist/recipes/parser.js.map +1 -0
  239. package/dist/recipes/scheduler.d.ts +45 -0
  240. package/dist/recipes/scheduler.js +110 -0
  241. package/dist/recipes/scheduler.js.map +1 -0
  242. package/dist/recipes/schema.d.ts +71 -0
  243. package/dist/recipes/schema.js +11 -0
  244. package/dist/recipes/schema.js.map +1 -0
  245. package/dist/recipesHttp.d.ts +63 -0
  246. package/dist/recipesHttp.js +183 -0
  247. package/dist/recipesHttp.js.map +1 -0
  248. package/dist/resources.d.ts +33 -0
  249. package/dist/resources.js +266 -0
  250. package/dist/resources.js.map +1 -0
  251. package/dist/riskTier.d.ts +40 -0
  252. package/dist/riskTier.js +142 -0
  253. package/dist/riskTier.js.map +1 -0
  254. package/dist/runLog.d.ts +90 -0
  255. package/dist/runLog.js +143 -0
  256. package/dist/runLog.js.map +1 -0
  257. package/dist/server.d.ts +160 -0
  258. package/dist/server.js +1244 -0
  259. package/dist/server.js.map +1 -0
  260. package/dist/sessionCheckpoint.d.ts +37 -0
  261. package/dist/sessionCheckpoint.js +123 -0
  262. package/dist/sessionCheckpoint.js.map +1 -0
  263. package/dist/streamableHttp.d.ts +86 -0
  264. package/dist/streamableHttp.js +702 -0
  265. package/dist/streamableHttp.js.map +1 -0
  266. package/dist/telemetry.d.ts +18 -0
  267. package/dist/telemetry.js +95 -0
  268. package/dist/telemetry.js.map +1 -0
  269. package/dist/tools/activityLog.d.ts +140 -0
  270. package/dist/tools/activityLog.js +204 -0
  271. package/dist/tools/activityLog.js.map +1 -0
  272. package/dist/tools/auditDependencies.d.ts +67 -0
  273. package/dist/tools/auditDependencies.js +298 -0
  274. package/dist/tools/auditDependencies.js.map +1 -0
  275. package/dist/tools/batchLsp.d.ts +262 -0
  276. package/dist/tools/batchLsp.js +328 -0
  277. package/dist/tools/batchLsp.js.map +1 -0
  278. package/dist/tools/blame-utils.d.ts +30 -0
  279. package/dist/tools/blame-utils.js +60 -0
  280. package/dist/tools/blame-utils.js.map +1 -0
  281. package/dist/tools/bridgeDoctor.d.ts +78 -0
  282. package/dist/tools/bridgeDoctor.js +542 -0
  283. package/dist/tools/bridgeDoctor.js.map +1 -0
  284. package/dist/tools/bridgeStatus.d.ts +122 -0
  285. package/dist/tools/bridgeStatus.js +250 -0
  286. package/dist/tools/bridgeStatus.js.map +1 -0
  287. package/dist/tools/cancelClaudeTask.d.ts +48 -0
  288. package/dist/tools/cancelClaudeTask.js +56 -0
  289. package/dist/tools/cancelClaudeTask.js.map +1 -0
  290. package/dist/tools/checkDocumentDirty.d.ts +56 -0
  291. package/dist/tools/checkDocumentDirty.js +74 -0
  292. package/dist/tools/checkDocumentDirty.js.map +1 -0
  293. package/dist/tools/clipboard.d.ts +80 -0
  294. package/dist/tools/clipboard.js +211 -0
  295. package/dist/tools/clipboard.js.map +1 -0
  296. package/dist/tools/closeTabs.d.ts +84 -0
  297. package/dist/tools/closeTabs.js +97 -0
  298. package/dist/tools/closeTabs.js.map +1 -0
  299. package/dist/tools/codeLens.d.ts +50 -0
  300. package/dist/tools/codeLens.js +47 -0
  301. package/dist/tools/codeLens.js.map +1 -0
  302. package/dist/tools/contextBundle.d.ts +75 -0
  303. package/dist/tools/contextBundle.js +218 -0
  304. package/dist/tools/contextBundle.js.map +1 -0
  305. package/dist/tools/createIssueFromAIComment.d.ts +75 -0
  306. package/dist/tools/createIssueFromAIComment.js +119 -0
  307. package/dist/tools/createIssueFromAIComment.js.map +1 -0
  308. package/dist/tools/ctxGetTaskContext.d.ts +103 -0
  309. package/dist/tools/ctxGetTaskContext.js +274 -0
  310. package/dist/tools/ctxGetTaskContext.js.map +1 -0
  311. package/dist/tools/ctxQueryTraces.d.ts +142 -0
  312. package/dist/tools/ctxQueryTraces.js +194 -0
  313. package/dist/tools/ctxQueryTraces.js.map +1 -0
  314. package/dist/tools/ctxSaveTrace.d.ts +87 -0
  315. package/dist/tools/ctxSaveTrace.js +94 -0
  316. package/dist/tools/ctxSaveTrace.js.map +1 -0
  317. package/dist/tools/debug.d.ts +206 -0
  318. package/dist/tools/debug.js +234 -0
  319. package/dist/tools/debug.js.map +1 -0
  320. package/dist/tools/decorations.d.ts +130 -0
  321. package/dist/tools/decorations.js +160 -0
  322. package/dist/tools/decorations.js.map +1 -0
  323. package/dist/tools/detectUnusedCode.d.ts +78 -0
  324. package/dist/tools/detectUnusedCode.js +173 -0
  325. package/dist/tools/detectUnusedCode.js.map +1 -0
  326. package/dist/tools/documentLinks.d.ts +62 -0
  327. package/dist/tools/documentLinks.js +55 -0
  328. package/dist/tools/documentLinks.js.map +1 -0
  329. package/dist/tools/editText.d.ts +108 -0
  330. package/dist/tools/editText.js +318 -0
  331. package/dist/tools/editText.js.map +1 -0
  332. package/dist/tools/enrichCommit.d.ts +89 -0
  333. package/dist/tools/enrichCommit.js +201 -0
  334. package/dist/tools/enrichCommit.js.map +1 -0
  335. package/dist/tools/enrichStackTrace.d.ts +121 -0
  336. package/dist/tools/enrichStackTrace.js +194 -0
  337. package/dist/tools/enrichStackTrace.js.map +1 -0
  338. package/dist/tools/explainDiagnostic.d.ts +137 -0
  339. package/dist/tools/explainDiagnostic.js +230 -0
  340. package/dist/tools/explainDiagnostic.js.map +1 -0
  341. package/dist/tools/explainSymbol.d.ts +119 -0
  342. package/dist/tools/explainSymbol.js +177 -0
  343. package/dist/tools/explainSymbol.js.map +1 -0
  344. package/dist/tools/fileOperations.d.ts +186 -0
  345. package/dist/tools/fileOperations.js +330 -0
  346. package/dist/tools/fileOperations.js.map +1 -0
  347. package/dist/tools/fileWatcher.d.ts +107 -0
  348. package/dist/tools/fileWatcher.js +121 -0
  349. package/dist/tools/fileWatcher.js.map +1 -0
  350. package/dist/tools/findFiles.d.ts +65 -0
  351. package/dist/tools/findFiles.js +142 -0
  352. package/dist/tools/findFiles.js.map +1 -0
  353. package/dist/tools/findRelatedTests.d.ts +83 -0
  354. package/dist/tools/findRelatedTests.js +196 -0
  355. package/dist/tools/findRelatedTests.js.map +1 -0
  356. package/dist/tools/fixAllLintErrors.d.ts +66 -0
  357. package/dist/tools/fixAllLintErrors.js +128 -0
  358. package/dist/tools/fixAllLintErrors.js.map +1 -0
  359. package/dist/tools/foldingRanges.d.ts +50 -0
  360. package/dist/tools/foldingRanges.js +51 -0
  361. package/dist/tools/foldingRanges.js.map +1 -0
  362. package/dist/tools/formatAndSave.d.ts +57 -0
  363. package/dist/tools/formatAndSave.js +87 -0
  364. package/dist/tools/formatAndSave.js.map +1 -0
  365. package/dist/tools/formatDocument.d.ts +61 -0
  366. package/dist/tools/formatDocument.js +144 -0
  367. package/dist/tools/formatDocument.js.map +1 -0
  368. package/dist/tools/generateAPIDocumentation.d.ts +62 -0
  369. package/dist/tools/generateAPIDocumentation.js +249 -0
  370. package/dist/tools/generateAPIDocumentation.js.map +1 -0
  371. package/dist/tools/generateTests.d.ts +75 -0
  372. package/dist/tools/generateTests.js +226 -0
  373. package/dist/tools/generateTests.js.map +1 -0
  374. package/dist/tools/getAIComments.d.ts +79 -0
  375. package/dist/tools/getAIComments.js +93 -0
  376. package/dist/tools/getAIComments.js.map +1 -0
  377. package/dist/tools/getAnalyticsReport.d.ts +102 -0
  378. package/dist/tools/getAnalyticsReport.js +137 -0
  379. package/dist/tools/getAnalyticsReport.js.map +1 -0
  380. package/dist/tools/getArchitectureContext.d.ts +85 -0
  381. package/dist/tools/getArchitectureContext.js +135 -0
  382. package/dist/tools/getArchitectureContext.js.map +1 -0
  383. package/dist/tools/getBufferContent.d.ts +80 -0
  384. package/dist/tools/getBufferContent.js +207 -0
  385. package/dist/tools/getBufferContent.js.map +1 -0
  386. package/dist/tools/getChangeImpact.d.ts +76 -0
  387. package/dist/tools/getChangeImpact.js +184 -0
  388. package/dist/tools/getChangeImpact.js.map +1 -0
  389. package/dist/tools/getClaudeTaskStatus.d.ts +87 -0
  390. package/dist/tools/getClaudeTaskStatus.js +89 -0
  391. package/dist/tools/getClaudeTaskStatus.js.map +1 -0
  392. package/dist/tools/getCodeCoverage.d.ts +86 -0
  393. package/dist/tools/getCodeCoverage.js +237 -0
  394. package/dist/tools/getCodeCoverage.js.map +1 -0
  395. package/dist/tools/getCommitsForIssue.d.ts +98 -0
  396. package/dist/tools/getCommitsForIssue.js +106 -0
  397. package/dist/tools/getCommitsForIssue.js.map +1 -0
  398. package/dist/tools/getCurrentSelection.d.ts +123 -0
  399. package/dist/tools/getCurrentSelection.js +113 -0
  400. package/dist/tools/getCurrentSelection.js.map +1 -0
  401. package/dist/tools/getDebugState.d.ts +140 -0
  402. package/dist/tools/getDebugState.js +109 -0
  403. package/dist/tools/getDebugState.js.map +1 -0
  404. package/dist/tools/getDependencyTree.d.ts +59 -0
  405. package/dist/tools/getDependencyTree.js +207 -0
  406. package/dist/tools/getDependencyTree.js.map +1 -0
  407. package/dist/tools/getDiagnostics.d.ts +108 -0
  408. package/dist/tools/getDiagnostics.js +371 -0
  409. package/dist/tools/getDiagnostics.js.map +1 -0
  410. package/dist/tools/getDiffFromHandoff.d.ts +89 -0
  411. package/dist/tools/getDiffFromHandoff.js +163 -0
  412. package/dist/tools/getDiffFromHandoff.js.map +1 -0
  413. package/dist/tools/getDocumentSymbols.d.ts +74 -0
  414. package/dist/tools/getDocumentSymbols.js +177 -0
  415. package/dist/tools/getDocumentSymbols.js.map +1 -0
  416. package/dist/tools/getFileTree.d.ts +66 -0
  417. package/dist/tools/getFileTree.js +131 -0
  418. package/dist/tools/getFileTree.js.map +1 -0
  419. package/dist/tools/getGitDiff.d.ts +50 -0
  420. package/dist/tools/getGitDiff.js +73 -0
  421. package/dist/tools/getGitDiff.js.map +1 -0
  422. package/dist/tools/getGitHotspots.d.ts +88 -0
  423. package/dist/tools/getGitHotspots.js +145 -0
  424. package/dist/tools/getGitHotspots.js.map +1 -0
  425. package/dist/tools/getGitLog.d.ts +62 -0
  426. package/dist/tools/getGitLog.js +87 -0
  427. package/dist/tools/getGitLog.js.map +1 -0
  428. package/dist/tools/getGitStatus.d.ts +72 -0
  429. package/dist/tools/getGitStatus.js +126 -0
  430. package/dist/tools/getGitStatus.js.map +1 -0
  431. package/dist/tools/getImportTree.d.ts +73 -0
  432. package/dist/tools/getImportTree.js +223 -0
  433. package/dist/tools/getImportTree.js.map +1 -0
  434. package/dist/tools/getImportedSignatures.d.ts +62 -0
  435. package/dist/tools/getImportedSignatures.js +255 -0
  436. package/dist/tools/getImportedSignatures.js.map +1 -0
  437. package/dist/tools/getOpenEditors.d.ts +62 -0
  438. package/dist/tools/getOpenEditors.js +126 -0
  439. package/dist/tools/getOpenEditors.js.map +1 -0
  440. package/dist/tools/getPRTemplate.d.ts +68 -0
  441. package/dist/tools/getPRTemplate.js +187 -0
  442. package/dist/tools/getPRTemplate.js.map +1 -0
  443. package/dist/tools/getProjectContext.d.ts +114 -0
  444. package/dist/tools/getProjectContext.js +344 -0
  445. package/dist/tools/getProjectContext.js.map +1 -0
  446. package/dist/tools/getProjectInfo.d.ts +51 -0
  447. package/dist/tools/getProjectInfo.js +325 -0
  448. package/dist/tools/getProjectInfo.js.map +1 -0
  449. package/dist/tools/getSecurityAdvisories.d.ts +105 -0
  450. package/dist/tools/getSecurityAdvisories.js +472 -0
  451. package/dist/tools/getSecurityAdvisories.js.map +1 -0
  452. package/dist/tools/getSessionUsage.d.ts +58 -0
  453. package/dist/tools/getSessionUsage.js +57 -0
  454. package/dist/tools/getSessionUsage.js.map +1 -0
  455. package/dist/tools/getSymbolHistory.d.ts +157 -0
  456. package/dist/tools/getSymbolHistory.js +256 -0
  457. package/dist/tools/getSymbolHistory.js.map +1 -0
  458. package/dist/tools/getToolCapabilities.d.ts +69 -0
  459. package/dist/tools/getToolCapabilities.js +298 -0
  460. package/dist/tools/getToolCapabilities.js.map +1 -0
  461. package/dist/tools/getTypeSignature.d.ts +70 -0
  462. package/dist/tools/getTypeSignature.js +132 -0
  463. package/dist/tools/getTypeSignature.js.map +1 -0
  464. package/dist/tools/getWorkspaceFolders.d.ts +58 -0
  465. package/dist/tools/getWorkspaceFolders.js +69 -0
  466. package/dist/tools/getWorkspaceFolders.js.map +1 -0
  467. package/dist/tools/getWorkspaceSettings.d.ts +44 -0
  468. package/dist/tools/getWorkspaceSettings.js +70 -0
  469. package/dist/tools/getWorkspaceSettings.js.map +1 -0
  470. package/dist/tools/git-utils.d.ts +16 -0
  471. package/dist/tools/git-utils.js +46 -0
  472. package/dist/tools/git-utils.js.map +1 -0
  473. package/dist/tools/gitHistory.d.ts +110 -0
  474. package/dist/tools/gitHistory.js +167 -0
  475. package/dist/tools/gitHistory.js.map +1 -0
  476. package/dist/tools/gitWrite.d.ts +612 -0
  477. package/dist/tools/gitWrite.js +983 -0
  478. package/dist/tools/gitWrite.js.map +1 -0
  479. package/dist/tools/github/actions.d.ts +152 -0
  480. package/dist/tools/github/actions.js +195 -0
  481. package/dist/tools/github/actions.js.map +1 -0
  482. package/dist/tools/github/index.d.ts +3 -0
  483. package/dist/tools/github/index.js +4 -0
  484. package/dist/tools/github/index.js.map +1 -0
  485. package/dist/tools/github/issues.d.ts +281 -0
  486. package/dist/tools/github/issues.js +340 -0
  487. package/dist/tools/github/issues.js.map +1 -0
  488. package/dist/tools/github/pr.d.ts +433 -0
  489. package/dist/tools/github/pr.js +588 -0
  490. package/dist/tools/github/pr.js.map +1 -0
  491. package/dist/tools/github/shared.d.ts +4 -0
  492. package/dist/tools/github/shared.js +12 -0
  493. package/dist/tools/github/shared.js.map +1 -0
  494. package/dist/tools/handoffNote.d.ts +106 -0
  495. package/dist/tools/handoffNote.js +232 -0
  496. package/dist/tools/handoffNote.js.map +1 -0
  497. package/dist/tools/headless/lspClient.d.ts +26 -0
  498. package/dist/tools/headless/lspClient.js +221 -0
  499. package/dist/tools/headless/lspClient.js.map +1 -0
  500. package/dist/tools/headless/lspFallback.d.ts +28 -0
  501. package/dist/tools/headless/lspFallback.js +122 -0
  502. package/dist/tools/headless/lspFallback.js.map +1 -0
  503. package/dist/tools/hoverAtCursor.d.ts +54 -0
  504. package/dist/tools/hoverAtCursor.js +68 -0
  505. package/dist/tools/hoverAtCursor.js.map +1 -0
  506. package/dist/tools/httpClient.d.ts +141 -0
  507. package/dist/tools/httpClient.js +486 -0
  508. package/dist/tools/httpClient.js.map +1 -0
  509. package/dist/tools/index.d.ts +49 -0
  510. package/dist/tools/index.js +672 -0
  511. package/dist/tools/index.js.map +1 -0
  512. package/dist/tools/inlayHints.d.ts +81 -0
  513. package/dist/tools/inlayHints.js +76 -0
  514. package/dist/tools/inlayHints.js.map +1 -0
  515. package/dist/tools/issueRefs.d.ts +14 -0
  516. package/dist/tools/issueRefs.js +27 -0
  517. package/dist/tools/issueRefs.js.map +1 -0
  518. package/dist/tools/jumpToFirstError.d.ts +63 -0
  519. package/dist/tools/jumpToFirstError.js +124 -0
  520. package/dist/tools/jumpToFirstError.js.map +1 -0
  521. package/dist/tools/launchQuickTask.d.ts +76 -0
  522. package/dist/tools/launchQuickTask.js +170 -0
  523. package/dist/tools/launchQuickTask.js.map +1 -0
  524. package/dist/tools/linters/biome.d.ts +2 -0
  525. package/dist/tools/linters/biome.js +44 -0
  526. package/dist/tools/linters/biome.js.map +1 -0
  527. package/dist/tools/linters/cargo.d.ts +2 -0
  528. package/dist/tools/linters/cargo.js +45 -0
  529. package/dist/tools/linters/cargo.js.map +1 -0
  530. package/dist/tools/linters/eslint.d.ts +2 -0
  531. package/dist/tools/linters/eslint.js +59 -0
  532. package/dist/tools/linters/eslint.js.map +1 -0
  533. package/dist/tools/linters/govet.d.ts +2 -0
  534. package/dist/tools/linters/govet.js +37 -0
  535. package/dist/tools/linters/govet.js.map +1 -0
  536. package/dist/tools/linters/pyright.d.ts +2 -0
  537. package/dist/tools/linters/pyright.js +34 -0
  538. package/dist/tools/linters/pyright.js.map +1 -0
  539. package/dist/tools/linters/ruff.d.ts +2 -0
  540. package/dist/tools/linters/ruff.js +30 -0
  541. package/dist/tools/linters/ruff.js.map +1 -0
  542. package/dist/tools/linters/types.d.ts +16 -0
  543. package/dist/tools/linters/types.js +2 -0
  544. package/dist/tools/linters/types.js.map +1 -0
  545. package/dist/tools/linters/typescript.d.ts +2 -0
  546. package/dist/tools/linters/typescript.js +38 -0
  547. package/dist/tools/linters/typescript.js.map +1 -0
  548. package/dist/tools/listClaudeTasks.d.ts +84 -0
  549. package/dist/tools/listClaudeTasks.js +88 -0
  550. package/dist/tools/listClaudeTasks.js.map +1 -0
  551. package/dist/tools/listTerminals.d.ts +55 -0
  552. package/dist/tools/listTerminals.js +78 -0
  553. package/dist/tools/listTerminals.js.map +1 -0
  554. package/dist/tools/lsp.d.ts +1086 -0
  555. package/dist/tools/lsp.js +1339 -0
  556. package/dist/tools/lsp.js.map +1 -0
  557. package/dist/tools/navigateToSymbolByName.d.ts +56 -0
  558. package/dist/tools/navigateToSymbolByName.js +170 -0
  559. package/dist/tools/navigateToSymbolByName.js.map +1 -0
  560. package/dist/tools/openDiff.d.ts +66 -0
  561. package/dist/tools/openDiff.js +126 -0
  562. package/dist/tools/openDiff.js.map +1 -0
  563. package/dist/tools/openFile.d.ts +69 -0
  564. package/dist/tools/openFile.js +129 -0
  565. package/dist/tools/openFile.js.map +1 -0
  566. package/dist/tools/openInBrowser.d.ts +55 -0
  567. package/dist/tools/openInBrowser.js +129 -0
  568. package/dist/tools/openInBrowser.js.map +1 -0
  569. package/dist/tools/organizeImports.d.ts +56 -0
  570. package/dist/tools/organizeImports.js +115 -0
  571. package/dist/tools/organizeImports.js.map +1 -0
  572. package/dist/tools/performanceReport.d.ts +133 -0
  573. package/dist/tools/performanceReport.js +218 -0
  574. package/dist/tools/performanceReport.js.map +1 -0
  575. package/dist/tools/planPersistence.d.ts +306 -0
  576. package/dist/tools/planPersistence.js +485 -0
  577. package/dist/tools/planPersistence.js.map +1 -0
  578. package/dist/tools/previewEdit.d.ts +107 -0
  579. package/dist/tools/previewEdit.js +270 -0
  580. package/dist/tools/previewEdit.js.map +1 -0
  581. package/dist/tools/recentTracesDigest.d.ts +35 -0
  582. package/dist/tools/recentTracesDigest.js +98 -0
  583. package/dist/tools/recentTracesDigest.js.map +1 -0
  584. package/dist/tools/refactorAnalyze.d.ts +78 -0
  585. package/dist/tools/refactorAnalyze.js +141 -0
  586. package/dist/tools/refactorAnalyze.js.map +1 -0
  587. package/dist/tools/refactorExtractFunction.d.ts +52 -0
  588. package/dist/tools/refactorExtractFunction.js +121 -0
  589. package/dist/tools/refactorExtractFunction.js.map +1 -0
  590. package/dist/tools/refactorPreview.d.ts +75 -0
  591. package/dist/tools/refactorPreview.js +93 -0
  592. package/dist/tools/refactorPreview.js.map +1 -0
  593. package/dist/tools/replaceBlock.d.ts +62 -0
  594. package/dist/tools/replaceBlock.js +125 -0
  595. package/dist/tools/replaceBlock.js.map +1 -0
  596. package/dist/tools/resumeClaudeTask.d.ts +75 -0
  597. package/dist/tools/resumeClaudeTask.js +149 -0
  598. package/dist/tools/resumeClaudeTask.js.map +1 -0
  599. package/dist/tools/runClaudeTask.d.ts +97 -0
  600. package/dist/tools/runClaudeTask.js +224 -0
  601. package/dist/tools/runClaudeTask.js.map +1 -0
  602. package/dist/tools/runCommand.d.ts +82 -0
  603. package/dist/tools/runCommand.js +101 -0
  604. package/dist/tools/runCommand.js.map +1 -0
  605. package/dist/tools/runTests.d.ts +146 -0
  606. package/dist/tools/runTests.js +315 -0
  607. package/dist/tools/runTests.js.map +1 -0
  608. package/dist/tools/saveDocument.d.ts +50 -0
  609. package/dist/tools/saveDocument.js +73 -0
  610. package/dist/tools/saveDocument.js.map +1 -0
  611. package/dist/tools/screenshot.d.ts +23 -0
  612. package/dist/tools/screenshot.js +43 -0
  613. package/dist/tools/screenshot.js.map +1 -0
  614. package/dist/tools/screenshotAndAnnotate.d.ts +103 -0
  615. package/dist/tools/screenshotAndAnnotate.js +192 -0
  616. package/dist/tools/screenshotAndAnnotate.js.map +1 -0
  617. package/dist/tools/searchAndReplace.d.ts +108 -0
  618. package/dist/tools/searchAndReplace.js +281 -0
  619. package/dist/tools/searchAndReplace.js.map +1 -0
  620. package/dist/tools/searchTools.d.ts +61 -0
  621. package/dist/tools/searchTools.js +85 -0
  622. package/dist/tools/searchTools.js.map +1 -0
  623. package/dist/tools/searchWorkspace.d.ts +99 -0
  624. package/dist/tools/searchWorkspace.js +189 -0
  625. package/dist/tools/searchWorkspace.js.map +1 -0
  626. package/dist/tools/selectionRanges.d.ts +58 -0
  627. package/dist/tools/selectionRanges.js +61 -0
  628. package/dist/tools/selectionRanges.js.map +1 -0
  629. package/dist/tools/semanticTokens.d.ts +87 -0
  630. package/dist/tools/semanticTokens.js +86 -0
  631. package/dist/tools/semanticTokens.js.map +1 -0
  632. package/dist/tools/setActiveWorkspaceFolder.d.ts +41 -0
  633. package/dist/tools/setActiveWorkspaceFolder.js +38 -0
  634. package/dist/tools/setActiveWorkspaceFolder.js.map +1 -0
  635. package/dist/tools/signatureHelp.d.ts +86 -0
  636. package/dist/tools/signatureHelp.js +79 -0
  637. package/dist/tools/signatureHelp.js.map +1 -0
  638. package/dist/tools/spawnWorkspace.d.ts +103 -0
  639. package/dist/tools/spawnWorkspace.js +268 -0
  640. package/dist/tools/spawnWorkspace.js.map +1 -0
  641. package/dist/tools/stackTraceParser.d.ts +43 -0
  642. package/dist/tools/stackTraceParser.js +139 -0
  643. package/dist/tools/stackTraceParser.js.map +1 -0
  644. package/dist/tools/terminal.d.ts +352 -0
  645. package/dist/tools/terminal.js +670 -0
  646. package/dist/tools/terminal.js.map +1 -0
  647. package/dist/tools/testRunners/cargoTest.d.ts +2 -0
  648. package/dist/tools/testRunners/cargoTest.js +129 -0
  649. package/dist/tools/testRunners/cargoTest.js.map +1 -0
  650. package/dist/tools/testRunners/goTest.d.ts +2 -0
  651. package/dist/tools/testRunners/goTest.js +108 -0
  652. package/dist/tools/testRunners/goTest.js.map +1 -0
  653. package/dist/tools/testRunners/pytest.d.ts +2 -0
  654. package/dist/tools/testRunners/pytest.js +135 -0
  655. package/dist/tools/testRunners/pytest.js.map +1 -0
  656. package/dist/tools/testRunners/types.d.ts +18 -0
  657. package/dist/tools/testRunners/types.js +2 -0
  658. package/dist/tools/testRunners/types.js.map +1 -0
  659. package/dist/tools/testRunners/vitestJest.d.ts +3 -0
  660. package/dist/tools/testRunners/vitestJest.js +215 -0
  661. package/dist/tools/testRunners/vitestJest.js.map +1 -0
  662. package/dist/tools/testTraceToSource.d.ts +80 -0
  663. package/dist/tools/testTraceToSource.js +206 -0
  664. package/dist/tools/testTraceToSource.js.map +1 -0
  665. package/dist/tools/transaction.d.ts +243 -0
  666. package/dist/tools/transaction.js +309 -0
  667. package/dist/tools/transaction.js.map +1 -0
  668. package/dist/tools/typeHierarchy.d.ts +77 -0
  669. package/dist/tools/typeHierarchy.js +86 -0
  670. package/dist/tools/typeHierarchy.js.map +1 -0
  671. package/dist/tools/utils.d.ts +124 -0
  672. package/dist/tools/utils.js +566 -0
  673. package/dist/tools/utils.js.map +1 -0
  674. package/dist/tools/vscodeCommands.d.ts +90 -0
  675. package/dist/tools/vscodeCommands.js +112 -0
  676. package/dist/tools/vscodeCommands.js.map +1 -0
  677. package/dist/tools/vscodeTasks.d.ts +102 -0
  678. package/dist/tools/vscodeTasks.js +110 -0
  679. package/dist/tools/vscodeTasks.js.map +1 -0
  680. package/dist/tools/watchDiagnostics.d.ts +64 -0
  681. package/dist/tools/watchDiagnostics.js +270 -0
  682. package/dist/tools/watchDiagnostics.js.map +1 -0
  683. package/dist/tools/workspaceSettings.d.ts +57 -0
  684. package/dist/tools/workspaceSettings.js +80 -0
  685. package/dist/tools/workspaceSettings.js.map +1 -0
  686. package/dist/transport.d.ts +207 -0
  687. package/dist/transport.js +1272 -0
  688. package/dist/transport.js.map +1 -0
  689. package/dist/version.d.ts +13 -0
  690. package/dist/version.js +31 -0
  691. package/dist/version.js.map +1 -0
  692. package/dist/wsUtils.d.ts +8 -0
  693. package/dist/wsUtils.js +54 -0
  694. package/dist/wsUtils.js.map +1 -0
  695. package/package.json +118 -0
  696. package/scripts/gen-claude-desktop-config.sh +124 -0
  697. package/scripts/gen-mcp-config.sh +390 -0
  698. package/scripts/install-extension.sh +106 -0
  699. package/scripts/mcp-stdio-shim.cjs +482 -0
  700. package/scripts/postinstall.mjs +68 -0
  701. package/scripts/start-all.sh +502 -0
  702. package/scripts/start-orchestrator.sh +186 -0
  703. package/scripts/start-remote.sh +126 -0
  704. package/scripts/start-vps.sh +116 -0
  705. package/templates/CLAUDE.bridge.md +125 -0
  706. package/templates/automation-policies/security-first.json +46 -0
  707. package/templates/automation-policies/strict-lint.json +41 -0
  708. package/templates/automation-policies/test-driven.json +54 -0
  709. package/templates/automation-policy.example.json +105 -0
  710. package/templates/bridge-tools.md +111 -0
  711. package/templates/dispatch-context.md +33 -0
  712. package/templates/managed-agent/code-review-agent.md +50 -0
  713. package/templates/managed-agent/managed-agent-mcp.json +102 -0
  714. package/templates/recipes/ambient-journal.yaml +11 -0
  715. package/templates/recipes/daily-status.yaml +21 -0
  716. package/templates/recipes/lint-on-save.yaml +13 -0
  717. package/templates/recipes/stale-branches.yaml +18 -0
  718. package/templates/recipes/watch-failing-tests.yaml +15 -0
  719. package/templates/scheduled-tasks/dependency-audit/SKILL.md +77 -0
  720. package/templates/scheduled-tasks/health-check/SKILL.md +73 -0
  721. package/templates/scheduled-tasks/nightly-review/SKILL.md +69 -0
package/dist/index.js ADDED
@@ -0,0 +1,1465 @@
1
+ #!/usr/bin/env node
2
+ // Enable V8 compile cache for faster cold-start on repeated restarts (Node 22.8+).
3
+ import nodeModule from "node:module";
4
+ if (typeof nodeModule.enableCompileCache === "function") {
5
+ nodeModule.enableCompileCache();
6
+ }
7
+ import { execFileSync, spawn, spawnSync } from "node:child_process";
8
+ import crypto from "node:crypto";
9
+ import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, statSync, unlinkSync, writeFileSync, } from "node:fs";
10
+ import os from "node:os";
11
+ import path from "node:path";
12
+ import readline from "node:readline";
13
+ import { fileURLToPath } from "node:url";
14
+ import { getAnalyticsPref, setAnalyticsPref } from "./analyticsPrefs.js";
15
+ import { Bridge } from "./bridge.js";
16
+ import { isBridgeToolsFileValid, repairBridgeToolsRulesIfStale, } from "./bridgeToolsRules.js";
17
+ import { findEditor, parseConfig } from "./config.js";
18
+ import { PACKAGE_VERSION, semverGt } from "./version.js";
19
+ const __dirnameTop = path.dirname(fileURLToPath(import.meta.url));
20
+ const OPEN_VSX_PUBLISHER = "oolab-labs";
21
+ const OPEN_VSX_NAME = "claude-ide-bridge-extension";
22
+ // CLAUDE.md versioned-block patching moved to ./claudeMdPatch.ts so tests
23
+ // can import the helpers without triggering the top-level CLI side effects
24
+ // at the bottom of this file. Re-exported here for back-compat.
25
+ export { BRIDGE_BLOCK_END, BRIDGE_BLOCK_RE, bridgeBlockStartMarker, extractClaudeMdBlockVersion, patchClaudeMdImport, } from "./claudeMdPatch.js";
26
+ import { extractClaudeMdBlockVersion, patchClaudeMdImport, } from "./claudeMdPatch.js";
27
+ /**
28
+ * Downloads the latest VSIX from Open VSX Registry to a temp file.
29
+ * Returns the temp file path (caller is responsible for deleting it).
30
+ * Throws on network or API errors.
31
+ */
32
+ async function downloadVsixFromOpenVsx() {
33
+ const metaUrl = `https://open-vsx.org/api/${OPEN_VSX_PUBLISHER}/${OPEN_VSX_NAME}`;
34
+ const metaRes = await fetch(metaUrl);
35
+ if (!metaRes.ok) {
36
+ throw new Error(`Open VSX metadata request failed: ${metaRes.status} ${metaRes.statusText}`);
37
+ }
38
+ const meta = (await metaRes.json());
39
+ const downloadUrl = meta?.files?.download;
40
+ if (typeof downloadUrl !== "string" || !downloadUrl.startsWith("https://")) {
41
+ throw new Error("Open VSX response missing files.download URL");
42
+ }
43
+ const version = meta?.version ?? "unknown";
44
+ process.stderr.write(` Downloading extension v${version} from Open VSX...\n`);
45
+ const vsixRes = await fetch(downloadUrl);
46
+ if (!vsixRes.ok) {
47
+ throw new Error(`Open VSX download failed: ${vsixRes.status} ${vsixRes.statusText}`);
48
+ }
49
+ const tmpPath = path.join(os.tmpdir(), `${OPEN_VSX_NAME}-${version}-${Date.now()}.vsix`);
50
+ const buf = await vsixRes.arrayBuffer();
51
+ writeFileSync(tmpPath, Buffer.from(buf));
52
+ return tmpPath;
53
+ }
54
+ // Handle --version flag — print package version and exit.
55
+ if (process.argv[2] === "--version" || process.argv[2] === "-v") {
56
+ console.log(`claude-ide-bridge ${PACKAGE_VERSION}`);
57
+ process.exit(0);
58
+ }
59
+ // Handle patchwork-init subcommand — T2 from docs/install-ux-plan.md.
60
+ // Separate from the bridge-only `init` to preserve back-compat. See ADR-0008.
61
+ if (process.argv[2] === "patchwork-init") {
62
+ const { runPatchworkInit } = await import("./commands/patchworkInit.js");
63
+ await runPatchworkInit(process.argv.slice(3));
64
+ process.exit(0);
65
+ }
66
+ // Handle start-all subcommand — launches the full 3-pane tmux orchestrator.
67
+ // Also triggered when invoked as `claude-ide-bridge-start` directly.
68
+ const isStartAll = process.argv[2] === "start-all" ||
69
+ path.basename(process.argv[1] ?? "").startsWith("claude-ide-bridge-start");
70
+ if (isStartAll) {
71
+ const startAllArgs = process.argv[2] === "start-all"
72
+ ? process.argv.slice(3)
73
+ : process.argv.slice(2);
74
+ const scriptPath = path.resolve(__dirnameTop, "..", "scripts", "start-all.sh");
75
+ const result = spawnSync("bash", [scriptPath, ...startAllArgs], {
76
+ stdio: "inherit",
77
+ });
78
+ process.exit(result.status ?? 1);
79
+ }
80
+ function writeRulesFileAtomic(rulesFilePath, content) {
81
+ const tmpPath = `${rulesFilePath}.tmp`;
82
+ writeFileSync(tmpPath, content, { encoding: "utf-8", flag: "wx" });
83
+ try {
84
+ renameSync(tmpPath, rulesFilePath);
85
+ }
86
+ catch (err) {
87
+ try {
88
+ unlinkSync(tmpPath);
89
+ }
90
+ catch {
91
+ /* best-effort cleanup */
92
+ }
93
+ throw err;
94
+ }
95
+ }
96
+ /**
97
+ * Handles errors from rules file write operations. EACCES → warning + instructions.
98
+ * ELOOP → hard error (symlink cycle — indicates possible symlink attack).
99
+ * EEXIST → hard error (wx exclusive-create failed — indicates a symlink was placed
100
+ * at the .tmp path, since we pre-clean stale .tmp files before every wx write).
101
+ * Others → warning. Returns the exit code to use (0 for warnings, 1 for hard errors).
102
+ */
103
+ function handleRulesWriteError(err, rulesFilePath, indent) {
104
+ const code = err.code;
105
+ if (code === "EACCES") {
106
+ process.stderr.write(`${indent}[warn] Bridge rules — permission denied writing to ${rulesFilePath}.\n` +
107
+ `${indent} Run with elevated permissions or create the file manually.\n\n`);
108
+ return 0;
109
+ }
110
+ if (code === "ELOOP" || code === "EEXIST") {
111
+ process.stderr.write(`${indent}[error] Bridge rules — suspicious path condition (${code}): ${rulesFilePath}\n\n`);
112
+ return 1;
113
+ }
114
+ process.stderr.write(`${indent}[warn] Bridge rules — write failed (${code ?? String(err)})\n\n`);
115
+ return 0;
116
+ }
117
+ // Handle gen-claude-md subcommand — generates a CLAUDE.md bridge workflow section
118
+ if (process.argv[2] === "gen-claude-md") {
119
+ const argv = process.argv.slice(3);
120
+ if (argv.includes("--help")) {
121
+ console.log(`claude-ide-bridge gen-claude-md — Generate CLAUDE.md bridge section
122
+
123
+ Usage: claude-ide-bridge gen-claude-md [options]
124
+
125
+ Options:
126
+ --write Write to CLAUDE.md in the workspace (default: print to stdout)
127
+ --workspace <path> Target workspace folder (default: cwd)
128
+ --help Show this help`);
129
+ process.exit(0);
130
+ }
131
+ const writeToDisk = argv.includes("--write");
132
+ const workspaceIdx = argv.indexOf("--workspace");
133
+ const workspace = workspaceIdx !== -1 && argv[workspaceIdx + 1]
134
+ ? argv[workspaceIdx + 1]
135
+ : process.cwd();
136
+ const templatePath = path.resolve(__dirnameTop, "..", "templates", "CLAUDE.bridge.md");
137
+ let content;
138
+ try {
139
+ content = readFileSync(templatePath, "utf-8");
140
+ }
141
+ catch {
142
+ process.stderr.write(`Error: template not found at ${templatePath}\n`);
143
+ process.exit(1);
144
+ }
145
+ if (!writeToDisk) {
146
+ process.stdout.write(`${content}\n`);
147
+ process.stderr.write("Note: run with --write to append this section to CLAUDE.md and also write .claude/rules/bridge-tools.md\n");
148
+ process.exit(0);
149
+ }
150
+ const targetPath = path.join(workspace, "CLAUDE.md");
151
+ const marker = "## Claude IDE Bridge";
152
+ const IMPORT_LINE = "@import .claude/rules/bridge-tools.md";
153
+ // Idempotent: skip if the section already exists (with @import line)
154
+ const patchResult = patchClaudeMdImport(targetPath, marker, IMPORT_LINE);
155
+ if (patchResult === "already-current") {
156
+ process.stderr.write(`CLAUDE.md bridge block already up to date (v${PACKAGE_VERSION}) — no changes made.\n`);
157
+ repairBridgeToolsRulesIfStale(workspace, undefined, {
158
+ writeIfMissing: true,
159
+ });
160
+ process.exit(0);
161
+ }
162
+ if (patchResult === "updated") {
163
+ process.stderr.write(`Updated CLAUDE.md bridge block to v${PACKAGE_VERSION}.\n`);
164
+ repairBridgeToolsRulesIfStale(workspace, undefined, {
165
+ writeIfMissing: true,
166
+ });
167
+ process.exit(0);
168
+ }
169
+ if (patchResult === "patched") {
170
+ process.stderr.write(`Patched existing CLAUDE.md — added missing @import line (v${PACKAGE_VERSION}).\n`);
171
+ repairBridgeToolsRulesIfStale(workspace, undefined, {
172
+ writeIfMissing: true,
173
+ });
174
+ process.exit(0);
175
+ }
176
+ if (patchResult === "already-present") {
177
+ process.stderr.write(`CLAUDE.md already contains a '${marker}' section — no changes made.\n`);
178
+ repairBridgeToolsRulesIfStale(workspace, undefined, {
179
+ writeIfMissing: true,
180
+ });
181
+ process.exit(0);
182
+ }
183
+ // Wrap the template content in a versioned block so future re-runs can detect the version stamp.
184
+ const versionedGenContent = content
185
+ .trimEnd()
186
+ .replace(marker, `<!-- claude-ide-bridge:start:${PACKAGE_VERSION} -->\n${marker}`)
187
+ .concat(`\n<!-- claude-ide-bridge:end -->`);
188
+ if (existsSync(targetPath)) {
189
+ const existing = readFileSync(targetPath, "utf-8");
190
+ // Write tmp first with exclusive-create — if the write fails, the original is intact
191
+ const updated = `${existing.trimEnd()}\n\n${versionedGenContent}\n`;
192
+ const tmpPath = `${targetPath}.tmp`;
193
+ writeFileSync(tmpPath, updated, { encoding: "utf-8", flag: "wx" });
194
+ // Backup existing file before replacing
195
+ const ts = new Date().toISOString().replace(/[:.]/g, "-");
196
+ const backupPath = `${targetPath}.${ts}.bak`;
197
+ try {
198
+ renameSync(targetPath, backupPath);
199
+ renameSync(tmpPath, targetPath);
200
+ }
201
+ catch (err) {
202
+ try {
203
+ unlinkSync(tmpPath);
204
+ }
205
+ catch {
206
+ /* best-effort cleanup */
207
+ }
208
+ throw err;
209
+ }
210
+ process.stderr.write(`Backed up existing CLAUDE.md to ${backupPath}\n`);
211
+ }
212
+ else {
213
+ mkdirSync(workspace, { recursive: true });
214
+ writeFileSync(`${targetPath}.tmp`, `${versionedGenContent}\n`, {
215
+ encoding: "utf-8",
216
+ flag: "wx",
217
+ });
218
+ renameSync(`${targetPath}.tmp`, targetPath);
219
+ }
220
+ process.stderr.write(`✓ Bridge workflow section written to ${targetPath} (v${PACKAGE_VERSION})\n`);
221
+ // Also write bridge-tools rules file alongside CLAUDE.md
222
+ repairBridgeToolsRulesIfStale(workspace, undefined, { writeIfMissing: true });
223
+ process.exit(0);
224
+ }
225
+ // Handle install subcommand — install a companion MCP server
226
+ if (process.argv[2] === "install") {
227
+ const { runInstall } = await import("./commands/install.js");
228
+ await runInstall(process.argv.slice(3));
229
+ process.exit(0);
230
+ }
231
+ // Handle marketplace subcommand — browse and install community skills
232
+ if (process.argv[2] === "marketplace") {
233
+ const { runMarketplace } = await import("./commands/marketplace.js");
234
+ await runMarketplace(process.argv.slice(3));
235
+ process.exit(0);
236
+ }
237
+ // Handle tools subcommand — search/list tools without a bridge connection
238
+ if (process.argv[2] === "tools") {
239
+ const { runToolsCommand } = await import("./commands/tools.js");
240
+ await runToolsCommand(process.argv.slice(3));
241
+ process.exit(0);
242
+ }
243
+ // Headless parity subcommands — launch Claude tasks from CLI (no sidebar/VS Code required).
244
+ // Reuse the bridge's running process; no new dependencies.
245
+ if (process.argv[2] === "quick-task") {
246
+ const { runQuickTask } = await import("./commands/task.js");
247
+ await runQuickTask(process.argv.slice(3));
248
+ // runQuickTask calls process.exit() itself
249
+ }
250
+ if (process.argv[2] === "start-task") {
251
+ const { runStartTask } = await import("./commands/task.js");
252
+ await runStartTask(process.argv.slice(3));
253
+ }
254
+ if (process.argv[2] === "continue-handoff") {
255
+ const { runContinueHandoff } = await import("./commands/task.js");
256
+ await runContinueHandoff(process.argv.slice(3));
257
+ }
258
+ // Handle print-token subcommand — print the bridge auth token from a lock file
259
+ if (process.argv[2] === "print-token") {
260
+ const argv = process.argv.slice(3);
261
+ if (argv.includes("--help")) {
262
+ console.log(`claude-ide-bridge print-token — Print bridge auth token
263
+
264
+ Usage: claude-ide-bridge print-token [options]
265
+
266
+ Options:
267
+ --port <number> Read token from a specific port's lock file (default: most recent)
268
+ --help Show this help`);
269
+ process.exit(0);
270
+ }
271
+ const portIdx = argv.indexOf("--port");
272
+ const portArg = portIdx !== -1 ? argv[portIdx + 1] : undefined;
273
+ const lockDir = path.join(process.env.CLAUDE_CONFIG_DIR ?? path.join(os.homedir(), ".claude"), "ide");
274
+ let lockFile;
275
+ if (portArg) {
276
+ lockFile = path.join(lockDir, `${portArg}.lock`);
277
+ if (!existsSync(lockFile)) {
278
+ process.stderr.write(`Error: No lock file found for port ${portArg} at ${lockFile}\n`);
279
+ process.exit(1);
280
+ }
281
+ }
282
+ else {
283
+ // Find the most recently modified lock file
284
+ let bestMtime = 0;
285
+ try {
286
+ for (const f of readdirSync(lockDir)) {
287
+ if (!f.endsWith(".lock"))
288
+ continue;
289
+ const full = path.join(lockDir, f);
290
+ const mtime = statSync(full).mtimeMs;
291
+ if (mtime > bestMtime) {
292
+ bestMtime = mtime;
293
+ lockFile = full;
294
+ }
295
+ }
296
+ }
297
+ catch {
298
+ // lock dir doesn't exist — handled below
299
+ }
300
+ }
301
+ if (!lockFile) {
302
+ process.stderr.write(`Error: No bridge lock file found in ${lockDir}\n`);
303
+ process.stderr.write("Make sure the bridge is running first, or pass --port <port>.\n");
304
+ process.exit(1);
305
+ }
306
+ try {
307
+ const data = JSON.parse(readFileSync(lockFile, "utf-8"));
308
+ if (!data.authToken) {
309
+ process.stderr.write(`Error: Lock file ${lockFile} has no authToken field\n`);
310
+ process.exit(1);
311
+ }
312
+ process.stdout.write(`${data.authToken}\n`);
313
+ }
314
+ catch {
315
+ process.stderr.write(`Error: Could not read lock file ${lockFile}\n`);
316
+ process.exit(1);
317
+ }
318
+ process.exit(0);
319
+ }
320
+ // Handle notify subcommand — called from CC hooks to fire bridge automation
321
+ // Usage: claude-ide-bridge notify <CcEventName> [--cwd <p>] [--taskId <id>] [--prompt <t>] [--tool <n>] [--reason <r>] [--port <n>]
322
+ if (process.argv[2] === "notify") {
323
+ const VALID_NOTIFY_EVENTS = new Set([
324
+ "PreCompact",
325
+ "PostCompact",
326
+ "InstructionsLoaded",
327
+ "TaskCreated",
328
+ "PermissionDenied",
329
+ "CwdChanged",
330
+ ]);
331
+ const notifyArgv = process.argv.slice(3);
332
+ const ccEvent = notifyArgv[0];
333
+ if (!ccEvent || !VALID_NOTIFY_EVENTS.has(ccEvent)) {
334
+ process.stderr.write(`Usage: claude-ide-bridge notify <CcEventName> [options]\n\nValid events: ${[...VALID_NOTIFY_EVENTS].join(", ")}\n`);
335
+ process.exit(1);
336
+ }
337
+ const notifyRest = notifyArgv.slice(1);
338
+ const namedArgs = {};
339
+ for (let i = 0; i < notifyRest.length - 1; i++) {
340
+ const arg = notifyRest[i];
341
+ if (arg?.startsWith("--")) {
342
+ const key = arg.slice(2);
343
+ const val = notifyRest[i + 1] ?? "";
344
+ namedArgs[key] = val;
345
+ i++;
346
+ }
347
+ }
348
+ const notifyLockDir = path.join(process.env.CLAUDE_CONFIG_DIR ?? path.join(os.homedir(), ".claude"), "ide");
349
+ let notifyLockFile;
350
+ let notifyPort;
351
+ if (namedArgs.port) {
352
+ notifyPort = Number(namedArgs.port);
353
+ notifyLockFile = path.join(notifyLockDir, `${notifyPort}.lock`);
354
+ if (!existsSync(notifyLockFile)) {
355
+ process.stderr.write(`Error: No lock file found for port ${notifyPort}\n`);
356
+ process.exit(1);
357
+ }
358
+ }
359
+ else {
360
+ let bestMtime = 0;
361
+ try {
362
+ for (const f of readdirSync(notifyLockDir)) {
363
+ if (!f.endsWith(".lock"))
364
+ continue;
365
+ const full = path.join(notifyLockDir, f);
366
+ const mtime = statSync(full).mtimeMs;
367
+ if (mtime > bestMtime) {
368
+ bestMtime = mtime;
369
+ notifyLockFile = full;
370
+ notifyPort = Number(path.basename(f, ".lock"));
371
+ }
372
+ }
373
+ }
374
+ catch {
375
+ // lock dir doesn't exist
376
+ }
377
+ }
378
+ if (!notifyLockFile || !notifyPort) {
379
+ process.stderr.write(`Error: No bridge lock file found in ${notifyLockDir}\n`);
380
+ process.stderr.write("Make sure the bridge is running first (claude-ide-bridge --watch ...).\n");
381
+ process.exit(1);
382
+ }
383
+ let notifyToken;
384
+ try {
385
+ const data = JSON.parse(readFileSync(notifyLockFile, "utf-8"));
386
+ if (!data.authToken) {
387
+ process.stderr.write(`Error: Lock file has no authToken field\n`);
388
+ process.exit(1);
389
+ }
390
+ notifyToken = data.authToken;
391
+ }
392
+ catch {
393
+ process.stderr.write(`Error: Could not read lock file ${notifyLockFile}\n`);
394
+ process.exit(1);
395
+ }
396
+ try {
397
+ const resp = await fetch(`http://127.0.0.1:${notifyPort}/notify`, {
398
+ method: "POST",
399
+ headers: {
400
+ Authorization: `Bearer ${notifyToken}`,
401
+ "Content-Type": "application/json",
402
+ },
403
+ body: JSON.stringify({ event: ccEvent, args: namedArgs }),
404
+ signal: AbortSignal.timeout(5_000),
405
+ });
406
+ if (!resp.ok) {
407
+ const text = await resp.text().catch(() => "");
408
+ process.stderr.write(`Error: Bridge returned ${resp.status}: ${text}\n`);
409
+ process.exit(1);
410
+ }
411
+ }
412
+ catch (err) {
413
+ process.stderr.write(`Error: Could not connect to bridge at port ${notifyPort}: ${err instanceof Error ? err.message : String(err)}\n`);
414
+ process.exit(1);
415
+ }
416
+ process.exit(0);
417
+ }
418
+ // Handle token-efficiency subcommand — show config/session usage or run benchmark
419
+ if (process.argv[2] === "token-efficiency") {
420
+ const teArgv = process.argv.slice(3);
421
+ const teSubCommand = teArgv[0] ?? "status";
422
+ if (teSubCommand === "--help" || teArgv.includes("--help")) {
423
+ console.log(`claude-ide-bridge token-efficiency — Token usage tools
424
+
425
+ Usage: claude-ide-bridge token-efficiency [status|benchmark] [options]
426
+
427
+ Subcommands:
428
+ status Show current config + live session usage (default)
429
+ benchmark [args...] Run benchmark against a running bridge
430
+ --iterations N Number of iterations (default: 50)
431
+ --json Emit JSON output
432
+ --threshold <ms> Fail if p99 RTT exceeds threshold
433
+
434
+ Options:
435
+ --help Show this help`);
436
+ process.exit(0);
437
+ }
438
+ const { tokenEfficiencyStatus, tokenEfficiencyBenchmark } = await import("./commands/tokenEfficiency.js");
439
+ if (teSubCommand === "benchmark") {
440
+ await tokenEfficiencyBenchmark(teArgv.slice(1));
441
+ }
442
+ else {
443
+ // status (default)
444
+ await tokenEfficiencyStatus();
445
+ }
446
+ process.exit(0);
447
+ }
448
+ // Handle gen-plugin-stub subcommand — scaffolds a new plugin directory
449
+ if (process.argv[2] === "gen-plugin-stub") {
450
+ const argv = process.argv.slice(3);
451
+ // Parse args: gen-plugin-stub <dir> [--name <name>] [--prefix <prefix>]
452
+ const dirArg = argv.find((a) => !a.startsWith("--"));
453
+ if (!dirArg) {
454
+ process.stderr.write("Usage: claude-ide-bridge gen-plugin-stub <output-dir> [--name <org/plugin-name>] [--prefix <toolPrefix>]\n");
455
+ process.exit(1);
456
+ }
457
+ const nameIdx = argv.indexOf("--name");
458
+ const prefixIdx = argv.indexOf("--prefix");
459
+ const pluginName = nameIdx !== -1 && argv[nameIdx + 1]
460
+ ? argv[nameIdx + 1]
461
+ : "my-org/my-plugin";
462
+ const toolPrefix = prefixIdx !== -1 && argv[prefixIdx + 1]
463
+ ? argv[prefixIdx + 1]
464
+ : "myPlugin";
465
+ // Validate name format
466
+ if (!/^[a-zA-Z0-9@._/-]{1,100}$/.test(pluginName)) {
467
+ process.stderr.write(`Error: --name "${pluginName}" contains invalid characters. Use only letters, numbers, @, ., _, /, -.\n`);
468
+ process.exit(1);
469
+ }
470
+ // Validate prefix format
471
+ if (!/^[a-zA-Z][a-zA-Z0-9_]{1,19}$/.test(toolPrefix)) {
472
+ process.stderr.write(`Error: --prefix "${toolPrefix}" is invalid. Must match /^[a-zA-Z][a-zA-Z0-9_]{1,19}$/ (2–20 chars, start with a letter).\n`);
473
+ process.exit(1);
474
+ }
475
+ const outDir = path.resolve(dirArg);
476
+ if (existsSync(outDir)) {
477
+ process.stderr.write(`Error: "${outDir}" already exists. Choose a new directory.\n`);
478
+ process.exit(1);
479
+ }
480
+ mkdirSync(outDir, { recursive: true });
481
+ // claude-ide-bridge-plugin.json
482
+ const manifest = {
483
+ schemaVersion: 1,
484
+ name: pluginName,
485
+ version: "0.1.0",
486
+ description: "A Claude IDE Bridge plugin",
487
+ entrypoint: "./index.mjs",
488
+ toolNamePrefix: toolPrefix,
489
+ minBridgeVersion: "2.1.24",
490
+ };
491
+ writeFileSync(path.join(outDir, "claude-ide-bridge-plugin.json"), `${JSON.stringify(manifest, null, 2)}\n`, "utf-8");
492
+ // index.mjs entrypoint
493
+ const entrypoint = `/**
494
+ * ${pluginName} — Claude IDE Bridge plugin
495
+ *
496
+ * Each tool must have a name starting with "${toolPrefix}".
497
+ * The \`ctx\` object provides: ctx.workspace, ctx.workspaceFolders,
498
+ * ctx.config (commandTimeout, maxResultSize), and ctx.logger.
499
+ */
500
+
501
+ /** @param {import('claude-ide-bridge/plugin').PluginContext} ctx */
502
+ export function register(ctx) {
503
+ ctx.logger.info(${JSON.stringify(`${pluginName} loaded`)}, { workspace: ctx.workspace });
504
+
505
+ return {
506
+ tools: [
507
+ {
508
+ schema: {
509
+ name: ${JSON.stringify(`${toolPrefix}Hello`)},
510
+ description: "Example tool — returns a greeting",
511
+ inputSchema: {
512
+ type: "object",
513
+ required: ["name"],
514
+ additionalProperties: false,
515
+ properties: {
516
+ name: { type: "string", description: "Name to greet" },
517
+ },
518
+ },
519
+ annotations: { readOnlyHint: true },
520
+ },
521
+ handler: async (args, _signal) => ({
522
+ content: [{ type: "text", text: "Hello from " + ${JSON.stringify(pluginName)} + ", " + args.name + "!" }],
523
+ }),
524
+ },
525
+ ],
526
+ };
527
+ }
528
+ `;
529
+ writeFileSync(path.join(outDir, "index.mjs"), entrypoint, "utf-8");
530
+ // package.json (optional, for npm publishing)
531
+ const pkg = {
532
+ name: pluginName.replace(/^@[^/]+\//, "").replace(/\//g, "-"),
533
+ version: "0.1.0",
534
+ description: "A Claude IDE Bridge plugin",
535
+ type: "module",
536
+ main: "index.mjs",
537
+ keywords: ["claude-ide-bridge", "claude-ide-bridge-plugin"],
538
+ peerDependencies: { "claude-ide-bridge": ">=2.1.24" },
539
+ };
540
+ writeFileSync(path.join(outDir, "package.json"), `${JSON.stringify(pkg, null, 2)}\n`, "utf-8");
541
+ // .gitignore
542
+ writeFileSync(path.join(outDir, ".gitignore"), "node_modules\n", "utf-8");
543
+ process.stderr.write(`✓ Plugin stub created at ${outDir}\n`);
544
+ process.stderr.write("\nNext steps:\n");
545
+ process.stderr.write(` 1. Edit ${path.join(outDir, "index.mjs")} to implement your tools\n`);
546
+ process.stderr.write(` 2. Run the bridge with: claude-ide-bridge --plugin ${outDir}\n`);
547
+ process.stderr.write(` 3. Or add to your config: { "plugins": ["${outDir}"] }\n`);
548
+ process.exit(0);
549
+ }
550
+ // Patchwork: `patchwork recipe run <name>` — POSTs to a running bridge's
551
+ // /recipes/run endpoint to enqueue the recipe via the Claude orchestrator.
552
+ if (process.argv[2] === "recipe" && process.argv[3] === "run") {
553
+ const name = process.argv[4];
554
+ if (!name) {
555
+ process.stderr.write("Usage: patchwork recipe run <name>\n");
556
+ process.exit(1);
557
+ }
558
+ (async () => {
559
+ try {
560
+ const { findBridgeLock } = await import("./bridgeLockDiscovery.js");
561
+ const lock = findBridgeLock();
562
+ if (!lock) {
563
+ process.stderr.write("Error: no running bridge found under ~/.claude/ide/. Start the bridge with --claude-driver subprocess first.\n");
564
+ process.exit(1);
565
+ return;
566
+ }
567
+ const res = await fetch(`http://127.0.0.1:${lock.port}/recipes/run`, {
568
+ method: "POST",
569
+ headers: {
570
+ Authorization: `Bearer ${lock.authToken}`,
571
+ "Content-Type": "application/json",
572
+ },
573
+ body: JSON.stringify({ name }),
574
+ });
575
+ const body = (await res.json());
576
+ if (!body.ok) {
577
+ process.stderr.write(`Error: ${body.error ?? "unknown"}\n`);
578
+ process.exit(1);
579
+ return;
580
+ }
581
+ process.stdout.write(` ✓ enqueued recipe "${name}" as task ${(body.taskId ?? "").slice(0, 8)}\n` +
582
+ " Watch progress on the dashboard Tasks page or via listClaudeTasks.\n");
583
+ process.exit(0);
584
+ }
585
+ catch (err) {
586
+ process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}\n`);
587
+ process.exit(1);
588
+ }
589
+ })();
590
+ }
591
+ // Handle init subcommand — one-command setup: install extension + write CLAUDE.md + print next steps
592
+ // Patchwork: `patchwork recipe install <file.json>` subcommand.
593
+ if (process.argv[2] === "recipe" && process.argv[3] === "install") {
594
+ const file = process.argv[4];
595
+ if (!file) {
596
+ process.stderr.write("Usage: patchwork recipe install <file.json>\n");
597
+ process.exit(1);
598
+ }
599
+ (async () => {
600
+ try {
601
+ const { installRecipeFromFile } = await import("./recipes/installer.js");
602
+ const recipesDir = path.join(os.homedir(), ".patchwork", "recipes");
603
+ const result = installRecipeFromFile(path.resolve(file), {
604
+ recipesDir,
605
+ });
606
+ process.stdout.write(` ✓ ${result.action} ${result.installedPath}\n` +
607
+ ` ℹ permissions snippet written to ${result.installedPath}.permissions.json\n` +
608
+ ` Review + merge into ~/.claude/settings.json to pre-approve recipe steps.\n`);
609
+ process.exit(0);
610
+ }
611
+ catch (err) {
612
+ process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}\n`);
613
+ process.exit(1);
614
+ }
615
+ })();
616
+ }
617
+ if (process.argv[2] === "init") {
618
+ const argv = process.argv.slice(3);
619
+ // Handle init --help
620
+ if (argv.includes("--help")) {
621
+ console.log(`claude-ide-bridge init — One-command setup
622
+
623
+ Usage: claude-ide-bridge init [options]
624
+
625
+ Options:
626
+ --workspace <path> Target workspace folder (default: cwd)
627
+ --help Show this help
628
+
629
+ Steps performed:
630
+ 1. Install the companion VS Code extension
631
+ 2. Write bridge section to CLAUDE.md
632
+ 3. Write .claude/rules/bridge-tools.md
633
+ 4. Register MCP shim in ~/.claude.json
634
+ 5. Wire CC automation hooks in ~/.claude/settings.json
635
+ 6. Verify claude-ide-bridge is on PATH
636
+ 7. Print next steps`);
637
+ process.exit(0);
638
+ }
639
+ const workspaceIdx = argv.indexOf("--workspace");
640
+ const workspace = workspaceIdx !== -1 && argv[workspaceIdx + 1]
641
+ ? path.resolve(argv[workspaceIdx + 1])
642
+ : process.cwd();
643
+ process.stderr.write("Claude IDE Bridge — setup\n\n");
644
+ // WSL detection: warn early if running in WSL without a detected editor
645
+ const isWsl = process.platform === "linux" &&
646
+ (process.env.WSL_DISTRO_NAME !== undefined ||
647
+ process.env.WSLENV !== undefined ||
648
+ (() => {
649
+ try {
650
+ return readFileSync("/proc/version", "utf-8")
651
+ .toLowerCase()
652
+ .includes("microsoft");
653
+ }
654
+ catch {
655
+ return false;
656
+ }
657
+ })());
658
+ // Step 1: Install extension
659
+ const editor = findEditor();
660
+ if (!editor) {
661
+ const wslHint = isWsl
662
+ ? "\n WSL detected: ensure VS Code is installed on the Windows host and\n" +
663
+ " the Remote - WSL extension is active. Then re-run init.\n" +
664
+ " Alternatively, pass the editor explicitly: claude-ide-bridge install-extension code\n"
665
+ : "";
666
+ process.stderr.write(` [skip] Extension install — no supported editor found on PATH.\n Install manually: https://open-vsx.org/extension/${OPEN_VSX_PUBLISHER}/${OPEN_VSX_NAME}\n${wslHint}\n`);
667
+ }
668
+ else {
669
+ process.stderr.write(` Installing extension into ${editor}...\n`);
670
+ const __dirname2 = path.dirname(fileURLToPath(import.meta.url));
671
+ const vsixDir = path.resolve(__dirname2, "..", "vscode-extension");
672
+ let localVsix2;
673
+ if (existsSync(vsixDir)) {
674
+ const vsixFiles = readdirSync(vsixDir)
675
+ .filter((f) => f.endsWith(".vsix"))
676
+ .sort()
677
+ .reverse();
678
+ if (vsixFiles.length > 0)
679
+ localVsix2 = path.join(vsixDir, vsixFiles[0]);
680
+ }
681
+ let tmpVsix2;
682
+ let extensionArg2;
683
+ if (localVsix2) {
684
+ extensionArg2 = localVsix2;
685
+ }
686
+ else {
687
+ try {
688
+ tmpVsix2 = await downloadVsixFromOpenVsx();
689
+ extensionArg2 = tmpVsix2;
690
+ }
691
+ catch {
692
+ // Download failed — warn but don't abort init
693
+ }
694
+ }
695
+ if (extensionArg2) {
696
+ try {
697
+ execFileSync(editor, ["--install-extension", extensionArg2], {
698
+ stdio: "pipe",
699
+ timeout: 30000,
700
+ });
701
+ process.stderr.write(` ✓ Extension installed via ${editor}\n\n`);
702
+ }
703
+ catch {
704
+ process.stderr.write(` [warn] Extension install failed — download manually from:\n https://open-vsx.org/extension/${OPEN_VSX_PUBLISHER}/${OPEN_VSX_NAME}\n\n`);
705
+ }
706
+ finally {
707
+ if (tmpVsix2) {
708
+ try {
709
+ unlinkSync(tmpVsix2);
710
+ }
711
+ catch {
712
+ /* best effort */
713
+ }
714
+ }
715
+ }
716
+ }
717
+ else {
718
+ process.stderr.write(` [warn] Could not download extension — install manually from:\n https://open-vsx.org/extension/${OPEN_VSX_PUBLISHER}/${OPEN_VSX_NAME}\n\n`);
719
+ }
720
+ }
721
+ // Step 2: Write CLAUDE.md
722
+ const templatePath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "templates", "CLAUDE.bridge.md");
723
+ if (!existsSync(templatePath)) {
724
+ process.stderr.write(" [skip] CLAUDE.md — template not found. Run gen-claude-md manually.\n\n");
725
+ }
726
+ else {
727
+ const content = readFileSync(templatePath, "utf-8");
728
+ const targetPath = path.join(workspace, "CLAUDE.md");
729
+ const marker = "## Claude IDE Bridge";
730
+ const importLine = "@import .claude/rules/bridge-tools.md";
731
+ // Capture old version before patching so we can show "v1.2 → v1.3" in the message.
732
+ const prevBlockVersion = existsSync(targetPath)
733
+ ? extractClaudeMdBlockVersion(readFileSync(targetPath, "utf-8"))
734
+ : null;
735
+ const initPatchResult = patchClaudeMdImport(targetPath, marker, importLine);
736
+ if (initPatchResult === "already-current") {
737
+ process.stderr.write(` ✓ CLAUDE.md — bridge block already up to date (v${PACKAGE_VERSION})\n\n`);
738
+ }
739
+ else if (initPatchResult === "updated") {
740
+ process.stderr.write(` ✓ CLAUDE.md — bridge block updated${prevBlockVersion ? ` v${prevBlockVersion} →` : ""} v${PACKAGE_VERSION}\n\n`);
741
+ }
742
+ else if (initPatchResult === "patched") {
743
+ process.stderr.write(` ✓ CLAUDE.md — bridge section patched and stamped v${PACKAGE_VERSION}\n\n`);
744
+ }
745
+ else if (initPatchResult === "already-present") {
746
+ process.stderr.write(" ✓ CLAUDE.md — bridge section already present\n\n");
747
+ }
748
+ else {
749
+ // no-section: append or create with versioned block
750
+ mkdirSync(workspace, { recursive: true });
751
+ const existing = existsSync(targetPath)
752
+ ? readFileSync(targetPath, "utf-8")
753
+ : null;
754
+ // Wrap the template content in a versioned block before inserting
755
+ const versionedContent = content
756
+ .trimEnd()
757
+ .replace(marker, `<!-- claude-ide-bridge:start:${PACKAGE_VERSION} -->\n${marker}`)
758
+ .concat(`\n<!-- claude-ide-bridge:end -->`);
759
+ const updated = existing !== null
760
+ ? `${existing.trimEnd()}\n\n${versionedContent}\n`
761
+ : `${versionedContent}\n`;
762
+ const tmpPath = `${targetPath}.tmp`;
763
+ writeFileSync(tmpPath, updated, { encoding: "utf-8", flag: "wx" });
764
+ if (existing !== null) {
765
+ const ts = new Date().toISOString().replace(/[:.]/g, "-");
766
+ try {
767
+ renameSync(targetPath, `${targetPath}.${ts}.bak`);
768
+ renameSync(tmpPath, targetPath);
769
+ }
770
+ catch (err) {
771
+ try {
772
+ unlinkSync(tmpPath);
773
+ }
774
+ catch {
775
+ /* best-effort cleanup */
776
+ }
777
+ throw err;
778
+ }
779
+ }
780
+ else {
781
+ renameSync(tmpPath, targetPath);
782
+ }
783
+ process.stderr.write(` ✓ CLAUDE.md — bridge section written to ${targetPath} (v${PACKAGE_VERSION})\n\n`);
784
+ }
785
+ }
786
+ // Step 2b: Write bridge-tools rules file to .claude/rules/bridge-tools.md
787
+ const rulesDir = path.join(workspace, ".claude", "rules");
788
+ const rulesFilePath = path.join(rulesDir, "bridge-tools.md");
789
+ const bridgeToolsTemplatePath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "templates", "bridge-tools.md");
790
+ if (isBridgeToolsFileValid(rulesFilePath)) {
791
+ process.stderr.write(` ✓ Bridge rules — already up to date (v${PACKAGE_VERSION}) at ${rulesFilePath}\n\n`);
792
+ }
793
+ else if (existsSync(bridgeToolsTemplatePath)) {
794
+ const repairing = existsSync(rulesFilePath);
795
+ try {
796
+ mkdirSync(rulesDir, { recursive: true });
797
+ writeRulesFileAtomic(rulesFilePath, readFileSync(bridgeToolsTemplatePath, "utf-8").replace("{{VERSION}}", PACKAGE_VERSION));
798
+ process.stderr.write(repairing
799
+ ? ` ✓ Bridge rules — updated to v${PACKAGE_VERSION} at ${rulesFilePath}\n\n`
800
+ : ` ✓ Bridge rules — written (v${PACKAGE_VERSION}) to ${rulesFilePath}\n\n`);
801
+ }
802
+ catch (err) {
803
+ const exitCode = handleRulesWriteError(err, rulesFilePath, " ");
804
+ if (exitCode !== 0)
805
+ process.exit(exitCode);
806
+ }
807
+ }
808
+ else {
809
+ process.stderr.write(` [skip] Bridge rules — template not found\n\n`);
810
+ }
811
+ // Step 3: Register shim in ~/.claude.json so bridge tools appear in every claude session.
812
+ // NOTE: ~/.claude.json is a FILE sitting next to the ~/.claude/ directory, with the same
813
+ // dotted prefix. Earlier versions computed `path.join(CONFIG_DIR, "..", "claude.json")`
814
+ // which dropped the leading dot and wrote to ~/claude.json — a ghost file that Claude
815
+ // Code never reads. Append ".json" to the config dir path instead.
816
+ const claudeDirForJson = process.env.CLAUDE_CONFIG_DIR ?? path.join(os.homedir(), ".claude");
817
+ const claudeJsonAbs = path.resolve(`${claudeDirForJson}.json`);
818
+ try {
819
+ let claudeJson = {};
820
+ if (existsSync(claudeJsonAbs)) {
821
+ claudeJson = JSON.parse(readFileSync(claudeJsonAbs, "utf-8"));
822
+ }
823
+ const mcpServers = (claudeJson.mcpServers ?? {});
824
+ if (mcpServers["claude-ide-bridge"]) {
825
+ process.stderr.write(` ✓ MCP shim — already registered in ${claudeJsonAbs}\n\n`);
826
+ }
827
+ else {
828
+ mcpServers["claude-ide-bridge"] = {
829
+ command: "claude-ide-bridge",
830
+ args: ["shim"],
831
+ type: "stdio",
832
+ };
833
+ claudeJson.mcpServers = mcpServers;
834
+ writeFileSync(claudeJsonAbs, `${JSON.stringify(claudeJson, null, 2)}\n`);
835
+ process.stderr.write(` ✓ MCP shim — registered in ${claudeJsonAbs}\n Note: bridge tools are wired via ~/.claude.json (global), not .mcp.json.\n This is intentional — when VS Code/Windsurf/Cursor launches Claude Code it\n injects --mcp-config which overrides any project .mcp.json. Only ~/.claude.json\n is always loaded. You do not need to add anything to .mcp.json.\n\n`);
836
+ }
837
+ }
838
+ catch {
839
+ process.stderr.write(` [warn] Could not update ${claudeJsonAbs} — add manually:\n { "mcpServers": { "claude-ide-bridge": { "command": "claude-ide-bridge", "args": ["shim"] } } }\n\n`);
840
+ }
841
+ // Step 3b: Wire CC hooks in ~/.claude/settings.json
842
+ const ccSettingsPath = path.join(process.env.CLAUDE_CONFIG_DIR ?? path.join(os.homedir(), ".claude"), "settings.json");
843
+ const CC_HOOK_NOTIFY_CMDS = {
844
+ PreCompact: "claude-ide-bridge notify PreCompact",
845
+ PostCompact: "claude-ide-bridge notify PostCompact",
846
+ InstructionsLoaded: "claude-ide-bridge notify InstructionsLoaded",
847
+ TaskCreated: "claude-ide-bridge notify TaskCreated --taskId $TASK_ID --prompt $PROMPT",
848
+ PermissionDenied: "claude-ide-bridge notify PermissionDenied --tool $TOOL --reason $REASON",
849
+ CwdChanged: "claude-ide-bridge notify CwdChanged --cwd $CWD",
850
+ };
851
+ try {
852
+ let ccSettings = {};
853
+ if (existsSync(ccSettingsPath)) {
854
+ ccSettings = JSON.parse(readFileSync(ccSettingsPath, "utf-8"));
855
+ }
856
+ const ccHooks = (ccSettings.hooks ?? {});
857
+ const isNested = (e) => !!e && Array.isArray(e.hooks);
858
+ const normalize = (e) => isNested(e)
859
+ ? { matcher: e.matcher ?? "", hooks: e.hooks ?? [] }
860
+ : { matcher: "", hooks: [e] };
861
+ const added = [];
862
+ const migrated = [];
863
+ for (const [ccEvent, cmd] of Object.entries(CC_HOOK_NOTIFY_CMDS)) {
864
+ const rawEntries = ccHooks[ccEvent] ?? [];
865
+ const hadLegacy = rawEntries.some((e) => !isNested(e));
866
+ const normalized = rawEntries.map(normalize);
867
+ const alreadyWired = normalized.some((entry) => (entry.hooks ?? []).some((h) => typeof h.command === "string" &&
868
+ (h.command.includes(cmd) ||
869
+ h.command.includes(`notify ${ccEvent}`))));
870
+ if (!alreadyWired) {
871
+ normalized.push({
872
+ matcher: "",
873
+ hooks: [{ type: "command", command: cmd }],
874
+ });
875
+ added.push(ccEvent);
876
+ }
877
+ if (!alreadyWired || hadLegacy) {
878
+ ccHooks[ccEvent] = normalized;
879
+ if (hadLegacy)
880
+ migrated.push(ccEvent);
881
+ }
882
+ }
883
+ if (added.length > 0 || migrated.length > 0) {
884
+ ccSettings.hooks = ccHooks;
885
+ writeFileSync(ccSettingsPath, `${JSON.stringify(ccSettings, null, 2)}\n`);
886
+ const addMsg = added.length > 0
887
+ ? ` ✓ CC hooks — wired ${added.length} automation hook(s) in ${ccSettingsPath}\n Added: ${added.join(", ")}\n`
888
+ : "";
889
+ const migMsg = migrated.length > 0
890
+ ? ` ✓ CC hooks — migrated ${migrated.length} legacy entrie(s) to matcher+hooks format\n Migrated: ${migrated.join(", ")}\n`
891
+ : "";
892
+ process.stderr.write(`${addMsg}${migMsg}\n`);
893
+ }
894
+ else {
895
+ process.stderr.write(` ✓ CC hooks — already wired in ${ccSettingsPath}\n\n`);
896
+ }
897
+ }
898
+ catch {
899
+ process.stderr.write(` [warn] Could not update ${ccSettingsPath} — add CC hook entries manually.\n` +
900
+ ` See CLAUDE.md Automation Policy section for the settings.json snippet.\n\n`);
901
+ }
902
+ // Patchwork: register PreToolUse approval hook so the dashboard can
903
+ // approve/reject CC tool calls in real time.
904
+ try {
905
+ const { registerPreToolUseHook } = await import("./preToolUseHook.js");
906
+ const result = registerPreToolUseHook(ccSettingsPath);
907
+ if (result.action === "added") {
908
+ process.stderr.write(` ✓ Patchwork PreToolUse hook — registered\n ${result.hookCommand}\n\n`);
909
+ }
910
+ else if (result.action === "already-wired") {
911
+ process.stderr.write(` ✓ Patchwork PreToolUse hook — already registered\n\n`);
912
+ }
913
+ else {
914
+ process.stderr.write(` [warn] Patchwork PreToolUse hook — could not register: ${result.error}\n\n`);
915
+ }
916
+ }
917
+ catch (err) {
918
+ process.stderr.write(` [warn] Patchwork PreToolUse hook — ${err instanceof Error ? err.message : String(err)}\n\n`);
919
+ }
920
+ // Step 4: Verify shim can be found on PATH
921
+ let shimOnPath = false;
922
+ try {
923
+ execFileSync("claude-ide-bridge", ["--version"], {
924
+ stdio: "pipe",
925
+ timeout: 5000,
926
+ });
927
+ shimOnPath = true;
928
+ }
929
+ catch {
930
+ // not on PATH or version flag not supported — non-fatal
931
+ shimOnPath = existsSync(path.resolve(__dirnameTop, "..", "scripts", "mcp-stdio-shim.cjs"));
932
+ }
933
+ if (!shimOnPath) {
934
+ process.stderr.write(" [warn] claude-ide-bridge not found on PATH.\n" +
935
+ " If you installed locally, ensure your npm global bin is in PATH:\n" +
936
+ " npm config get prefix # add <prefix>/bin to PATH\n\n");
937
+ }
938
+ else {
939
+ process.stderr.write(" ✓ Shim — claude-ide-bridge found on PATH\n\n");
940
+ }
941
+ // Step 5: Success message + next steps
942
+ // Check if the workspace is a linked git worktree — surface Cowork gotcha if so.
943
+ // (In main worktree .git is a dir; in a linked worktree it is a file.)
944
+ let inWorktree = false;
945
+ try {
946
+ inWorktree = statSync(path.join(workspace, ".git")).isFile();
947
+ }
948
+ catch {
949
+ /* no .git at workspace — not a git repo, fine */
950
+ }
951
+ process.stdout.write("\n✅ Setup complete.\n\n" +
952
+ "Next steps:\n" +
953
+ ` 1. Start the bridge: claude-ide-bridge --watch (runs in this workspace)\n` +
954
+ " 2. Restart your IDE once so it picks up the new extension + MCP config.\n" +
955
+ " 3. Open Claude Code and type /mcp — the claude-ide-bridge server should show as connected.\n" +
956
+ " 4. Type /ide to see live workspace state (open editors, diagnostics, git status).\n\n");
957
+ if (inWorktree) {
958
+ process.stdout.write("⚠ This workspace is a linked git worktree. If this is a Cowork session,\n" +
959
+ " bridge MCP tools are unreachable from inside Cowork itself — run\n" +
960
+ " /mcp__bridge__cowork in regular Claude Code/Desktop chat FIRST to\n" +
961
+ " gather context, then switch to Cowork. See docs/cowork.md.\n\n");
962
+ }
963
+ // Step 6: Verify setup
964
+ process.stdout.write("📋 Setup verification:\n");
965
+ process.stdout.write(shimOnPath
966
+ ? " ✓ bridge on PATH\n"
967
+ : ' ✗ bridge not on PATH — add npm global bin to your PATH (e.g. export PATH="$(npm bin -g):$PATH")\n');
968
+ let mcpWired = false;
969
+ try {
970
+ const claudeJsonPath = path.join(os.homedir(), ".claude.json");
971
+ const cj = JSON.parse(readFileSync(claudeJsonPath, "utf-8"));
972
+ mcpWired = !!(cj?.mcpServers &&
973
+ typeof cj.mcpServers === "object" &&
974
+ cj.mcpServers["claude-ide-bridge"]);
975
+ }
976
+ catch {
977
+ /* file may not exist yet — non-fatal */
978
+ }
979
+ process.stdout.write(mcpWired
980
+ ? " ✓ MCP shim registered in ~/.claude.json\n"
981
+ : " ✗ MCP shim not found in ~/.claude.json — re-run init or check Step 3 output above\n");
982
+ let hooksWired = false;
983
+ try {
984
+ const settingsPath = path.join(os.homedir(), ".claude", "settings.json");
985
+ const sj = JSON.parse(readFileSync(settingsPath, "utf-8"));
986
+ const hooksObj = sj?.hooks;
987
+ if (hooksObj && typeof hooksObj === "object") {
988
+ hooksWired = Object.values(hooksObj).flat().some((e) => typeof e?.command ===
989
+ "string" &&
990
+ (e.command ?? "").includes("claude-ide-bridge"));
991
+ }
992
+ }
993
+ catch {
994
+ /* file may not exist yet — non-fatal */
995
+ }
996
+ process.stdout.write(hooksWired
997
+ ? " ✓ CC hooks wired in ~/.claude/settings.json\n"
998
+ : " ✗ CC hooks not wired — re-run init to add them\n");
999
+ // Auto-open automation docs when --workspace was provided
1000
+ if (workspaceIdx !== -1 && workspace) {
1001
+ const docsPath = path.join(workspace, "docs", "automation.md");
1002
+ const fallbackDocs = path.resolve(__dirnameTop, "..", "docs", "automation.md");
1003
+ const target = existsSync(docsPath)
1004
+ ? docsPath
1005
+ : existsSync(fallbackDocs)
1006
+ ? fallbackDocs
1007
+ : null;
1008
+ if (target) {
1009
+ const { exec } = await import("node:child_process");
1010
+ exec(`code "${target}"`, { timeout: 3000 }, () => { });
1011
+ }
1012
+ }
1013
+ // Analytics opt-in prompt — only ask once; skip if preference already set
1014
+ const existingPref = getAnalyticsPref();
1015
+ if (existingPref === null) {
1016
+ process.stdout.write("\n");
1017
+ process.stdout.write("Optional: send anonymous usage statistics to help prioritize features?\n" +
1018
+ " Tool names, success/error counts, and durations only.\n" +
1019
+ " No file paths, code, error messages, or personal data. Ever.\n" +
1020
+ " Change anytime: claude-ide-bridge --analytics on|off\n\n");
1021
+ const answer = await new Promise((resolve) => {
1022
+ const rl = readline.createInterface({
1023
+ input: process.stdin,
1024
+ output: process.stdout,
1025
+ });
1026
+ rl.question("Send anonymous usage stats? [y/N]: ", (ans) => {
1027
+ rl.close();
1028
+ resolve(ans.trim().toLowerCase());
1029
+ });
1030
+ // If stdin is not a TTY (e.g. piped), default to no
1031
+ if (!process.stdin.isTTY) {
1032
+ rl.close();
1033
+ resolve("n");
1034
+ }
1035
+ });
1036
+ const opted = answer === "y" || answer === "yes";
1037
+ setAnalyticsPref(opted);
1038
+ process.stdout.write(opted
1039
+ ? " ✓ Analytics enabled — thank you.\n"
1040
+ : " ✓ Analytics disabled — no data will be sent.\n");
1041
+ }
1042
+ process.exit(0);
1043
+ }
1044
+ // Handle shim subcommand — stdio relay that auto-discovers the running bridge/orchestrator.
1045
+ // Intended use: add to ~/.claude.json mcpServers so bridge tools are available everywhere.
1046
+ // { "command": "claude-ide-bridge", "args": ["shim"] }
1047
+ if (process.argv[2] === "shim") {
1048
+ const shimPath = path.resolve(__dirnameTop, "..", "scripts", "mcp-stdio-shim.cjs");
1049
+ // Pass remaining args (e.g. explicit port + token for testing)
1050
+ const shimArgs = [shimPath, ...process.argv.slice(3)];
1051
+ const child = spawn(process.execPath, shimArgs, { stdio: "inherit" });
1052
+ child.on("exit", (code, signal) => {
1053
+ process.exit(code ?? (signal ? 1 : 0));
1054
+ });
1055
+ // Forward signals so the shim can clean up
1056
+ for (const sig of ["SIGTERM", "SIGINT"]) {
1057
+ process.once(sig, () => child.kill(sig));
1058
+ }
1059
+ // Prevent fall-through — keep alive until child exits
1060
+ await new Promise(() => { });
1061
+ }
1062
+ // Handle orchestrator subcommand — starts the meta-bridge that coordinates multiple IDEs
1063
+ if (process.argv[2] === "orchestrator") {
1064
+ const { parseOrchestratorArgs, OrchestratorBridge } = await import("./orchestrator/index.js");
1065
+ const orchConfig = parseOrchestratorArgs(process.argv);
1066
+ const orch = new OrchestratorBridge(orchConfig);
1067
+ await orch.start().catch((err) => {
1068
+ const message = err instanceof Error ? err.message : String(err);
1069
+ process.stderr.write(`Orchestrator error: ${message}\n`);
1070
+ process.exit(1);
1071
+ });
1072
+ // Stay alive serving connections — do not fall through to parseConfig
1073
+ await new Promise(() => { });
1074
+ }
1075
+ else if (process.argv[2] === "install-extension") {
1076
+ const KNOWN_EDITORS = new Set([
1077
+ "code",
1078
+ "windsurf",
1079
+ "cursor",
1080
+ "antigravity",
1081
+ "ag",
1082
+ ]);
1083
+ const editorArg = process.argv[3];
1084
+ if (editorArg !== undefined &&
1085
+ !KNOWN_EDITORS.has(editorArg) &&
1086
+ !path.isAbsolute(editorArg)) {
1087
+ process.stderr.write(`Error: Unknown editor "${editorArg}". Use one of: code, windsurf, cursor, antigravity, ag\n`);
1088
+ process.exit(1);
1089
+ }
1090
+ const editor = process.argv[3] || findEditor();
1091
+ if (!editor) {
1092
+ process.stderr.write("Error: No editor found. Specify the editor command: claude-ide-bridge install-extension <code|cursor|windsurf>\n");
1093
+ process.exit(1);
1094
+ }
1095
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
1096
+ const vsixDir = path.resolve(__dirname, "..", "vscode-extension");
1097
+ // Prefer a local .vsix (source checkout / dev build). When installed via
1098
+ // `npm install -g` there is no vscode-extension/ dir, so download from Open VSX.
1099
+ let localVsix;
1100
+ if (existsSync(vsixDir)) {
1101
+ const vsixFiles = readdirSync(vsixDir)
1102
+ .filter((f) => f.endsWith(".vsix"))
1103
+ .sort()
1104
+ .reverse();
1105
+ if (vsixFiles.length > 0)
1106
+ localVsix = path.join(vsixDir, vsixFiles[0]);
1107
+ }
1108
+ let tmpVsix;
1109
+ let extensionArg;
1110
+ if (localVsix) {
1111
+ extensionArg = localVsix;
1112
+ }
1113
+ else {
1114
+ try {
1115
+ tmpVsix = await downloadVsixFromOpenVsx();
1116
+ extensionArg = tmpVsix;
1117
+ }
1118
+ catch (err) {
1119
+ const msg = err instanceof Error ? err.message : String(err);
1120
+ process.stderr.write(`Error downloading extension from Open VSX: ${msg}\n`);
1121
+ process.stderr.write(`Install manually: download from https://open-vsx.org/extension/${OPEN_VSX_PUBLISHER}/${OPEN_VSX_NAME}\n`);
1122
+ process.exit(1);
1123
+ }
1124
+ }
1125
+ try {
1126
+ process.stderr.write(`Installing extension via ${editor}...\n`);
1127
+ execFileSync(editor, ["--install-extension", extensionArg], {
1128
+ stdio: "inherit",
1129
+ timeout: 30000,
1130
+ });
1131
+ process.stderr.write("Extension installed successfully.\n");
1132
+ }
1133
+ catch (err) {
1134
+ const message = err instanceof Error ? err.message : String(err);
1135
+ process.stderr.write(`Error installing extension: ${message}\n`);
1136
+ process.exit(1);
1137
+ }
1138
+ finally {
1139
+ if (tmpVsix) {
1140
+ try {
1141
+ unlinkSync(tmpVsix);
1142
+ }
1143
+ catch {
1144
+ /* best effort */
1145
+ }
1146
+ }
1147
+ }
1148
+ process.exit(0);
1149
+ }
1150
+ // Handle --analytics on|off subcommand — update stored preference
1151
+ if (process.argv[2] === "--analytics") {
1152
+ const val = process.argv[3];
1153
+ if (val !== "on" && val !== "off") {
1154
+ process.stderr.write("Usage: claude-ide-bridge --analytics on|off\n");
1155
+ process.exit(1);
1156
+ }
1157
+ setAnalyticsPref(val === "on");
1158
+ process.stdout.write(`Analytics ${val === "on" ? "enabled" : "disabled"}. Preference saved to ~/.claude/ide/analytics.json\n`);
1159
+ process.exit(0);
1160
+ }
1161
+ // Handle status subcommand — check bridge health and extension connectivity
1162
+ if (process.argv[2] === "status") {
1163
+ const argv = process.argv.slice(3);
1164
+ if (argv.includes("--help")) {
1165
+ console.log(`claude-ide-bridge status — Check bridge health
1166
+
1167
+ Usage: claude-ide-bridge status [options]
1168
+
1169
+ Options:
1170
+ --port <number> Check a specific port (default: most recent lock file)
1171
+ --json Output as JSON
1172
+ --help Show this help`);
1173
+ process.exit(0);
1174
+ }
1175
+ const portIdx = argv.indexOf("--port");
1176
+ const portArg = portIdx !== -1 ? argv[portIdx + 1] : undefined;
1177
+ const jsonFlag = argv.includes("--json");
1178
+ const lockDir = path.join(process.env.CLAUDE_CONFIG_DIR ?? path.join(os.homedir(), ".claude"), "ide");
1179
+ let lockFile;
1180
+ let lockPort;
1181
+ if (portArg) {
1182
+ lockFile = path.join(lockDir, `${portArg}.lock`);
1183
+ lockPort = portArg;
1184
+ if (!existsSync(lockFile)) {
1185
+ process.stderr.write(`Error: No lock file found for port ${portArg} at ${lockFile}\n`);
1186
+ process.exit(1);
1187
+ }
1188
+ }
1189
+ else {
1190
+ let bestMtime = 0;
1191
+ try {
1192
+ for (const f of readdirSync(lockDir)) {
1193
+ if (!f.endsWith(".lock"))
1194
+ continue;
1195
+ const full = path.join(lockDir, f);
1196
+ const mtime = statSync(full).mtimeMs;
1197
+ if (mtime > bestMtime) {
1198
+ bestMtime = mtime;
1199
+ lockFile = full;
1200
+ lockPort = f.replace(".lock", "");
1201
+ }
1202
+ }
1203
+ }
1204
+ catch {
1205
+ // lock dir doesn't exist
1206
+ }
1207
+ }
1208
+ if (!lockFile) {
1209
+ if (jsonFlag) {
1210
+ process.stdout.write(`${JSON.stringify({ status: "not_running" })}\n`);
1211
+ }
1212
+ else {
1213
+ process.stderr.write(`No bridge lock file found in ${lockDir}\n`);
1214
+ process.stderr.write("Bridge is not running. Start it with: claude-ide-bridge --watch\n");
1215
+ }
1216
+ process.exit(1);
1217
+ }
1218
+ let lockData;
1219
+ try {
1220
+ lockData = JSON.parse(readFileSync(lockFile, "utf-8"));
1221
+ }
1222
+ catch {
1223
+ process.stderr.write(`Error: Could not read lock file ${lockFile}\n`);
1224
+ process.exit(1);
1225
+ }
1226
+ // Check if PID is alive
1227
+ let pidAlive = false;
1228
+ if (lockData.pid) {
1229
+ try {
1230
+ process.kill(lockData.pid, 0);
1231
+ pidAlive = true;
1232
+ }
1233
+ catch {
1234
+ // process not running
1235
+ }
1236
+ }
1237
+ if (!pidAlive) {
1238
+ if (jsonFlag) {
1239
+ process.stdout.write(`${JSON.stringify({
1240
+ status: "stale_lock",
1241
+ port: lockPort,
1242
+ pid: lockData.pid,
1243
+ })}\n`);
1244
+ }
1245
+ else {
1246
+ process.stderr.write(`Bridge lock file exists (port ${lockPort}) but process ${lockData.pid} is not running.\n`);
1247
+ process.stderr.write("The lock file is stale. Restart with: claude-ide-bridge --watch\n");
1248
+ }
1249
+ process.exit(1);
1250
+ }
1251
+ // Fetch /health from the running bridge
1252
+ const healthUrl = `http://127.0.0.1:${lockPort}/health`;
1253
+ try {
1254
+ const resp = await fetch(healthUrl, {
1255
+ headers: {
1256
+ Authorization: `Bearer ${lockData.authToken}`,
1257
+ },
1258
+ signal: AbortSignal.timeout(5_000),
1259
+ });
1260
+ const health = (await resp.json());
1261
+ if (jsonFlag) {
1262
+ process.stdout.write(`${JSON.stringify({
1263
+ status: "running",
1264
+ port: lockPort,
1265
+ pid: lockData.pid,
1266
+ workspace: lockData.workspace,
1267
+ ide: lockData.ideName,
1268
+ ...health,
1269
+ })}\n`);
1270
+ }
1271
+ else {
1272
+ const uptimeMs = health.uptimeMs ?? 0;
1273
+ const mins = Math.floor(uptimeMs / 60_000);
1274
+ const secs = Math.floor((uptimeMs % 60_000) / 1_000);
1275
+ const uptime = mins > 0 ? `${mins}m ${secs}s` : `${secs}s`;
1276
+ process.stdout.write("Bridge status: running\n");
1277
+ process.stdout.write(` Port: ${lockPort}\n`);
1278
+ process.stdout.write(` PID: ${lockData.pid}\n`);
1279
+ process.stdout.write(` Workspace: ${lockData.workspace ?? "unknown"}\n`);
1280
+ process.stdout.write(` IDE: ${lockData.ideName ?? "unknown"}\n`);
1281
+ process.stdout.write(` Uptime: ${uptime}\n`);
1282
+ process.stdout.write(` Extension: ${health.extensionConnected === true ? "connected" : health.extensionConnected === false ? "disconnected" : "unknown"}\n`);
1283
+ process.stdout.write(` Sessions: ${health.connections ?? 0}\n`);
1284
+ if (health.toolCount !== undefined) {
1285
+ process.stdout.write(` Tools: ${health.toolCount}\n`);
1286
+ }
1287
+ }
1288
+ }
1289
+ catch (err) {
1290
+ if (jsonFlag) {
1291
+ process.stdout.write(`${JSON.stringify({
1292
+ status: "unreachable",
1293
+ port: lockPort,
1294
+ pid: lockData.pid,
1295
+ error: err instanceof Error ? err.message : String(err),
1296
+ })}\n`);
1297
+ }
1298
+ else {
1299
+ process.stderr.write(`Bridge process is running (PID ${lockData.pid}) but /health endpoint is unreachable.\n`);
1300
+ process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}\n`);
1301
+ }
1302
+ process.exit(1);
1303
+ }
1304
+ process.exit(0);
1305
+ }
1306
+ // F6: "Did you mean?" for unknown CLI subcommands
1307
+ {
1308
+ const KNOWN_COMMANDS = [
1309
+ "init",
1310
+ "patchwork-init",
1311
+ "start-all",
1312
+ "install-extension",
1313
+ "gen-claude-md",
1314
+ "print-token",
1315
+ "gen-plugin-stub",
1316
+ "notify",
1317
+ "install",
1318
+ "marketplace",
1319
+ "status",
1320
+ "shim",
1321
+ "recipe",
1322
+ ];
1323
+ const unknownSub = process.argv[2];
1324
+ if (unknownSub &&
1325
+ !unknownSub.startsWith("-") &&
1326
+ !KNOWN_COMMANDS.includes(unknownSub)) {
1327
+ const lev = (a, b) => {
1328
+ const dp = Array.from({ length: a.length + 1 }, (_, i) => Array.from({ length: b.length + 1 }, (_, j) => i === 0 ? j : j === 0 ? i : 0));
1329
+ for (let i = 1; i <= a.length; i++)
1330
+ for (let j = 1; j <= b.length; j++)
1331
+ // biome-ignore lint/style/noNonNullAssertion: dp is fully pre-allocated
1332
+ dp[i][j] =
1333
+ a[i - 1] === b[j - 1]
1334
+ ? // biome-ignore lint/style/noNonNullAssertion: dp is fully pre-allocated
1335
+ dp[i - 1][j - 1]
1336
+ : 1 +
1337
+ // biome-ignore lint/style/noNonNullAssertion: dp is fully pre-allocated
1338
+ Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]);
1339
+ // biome-ignore lint/style/noNonNullAssertion: dp is fully pre-allocated
1340
+ return dp[a.length][b.length];
1341
+ };
1342
+ const closest = [...KNOWN_COMMANDS].sort((a, b) => lev(unknownSub, a) - lev(unknownSub, b))[0];
1343
+ console.error(`Unknown command: '${unknownSub}'. Did you mean: ${closest}?`);
1344
+ process.exit(1);
1345
+ }
1346
+ }
1347
+ const config = parseConfig(process.argv);
1348
+ // Patchwork: resolve --model flag (optional, non-invasive) — stashes the
1349
+ // configured adapter on globalThis for consumers that opt into the adapter
1350
+ // layer. Bridge subprocess driver still works when --model is absent.
1351
+ try {
1352
+ const { resolveModel } = await import("./patchworkCli.js");
1353
+ const resolved = resolveModel(process.argv);
1354
+ if (resolved) {
1355
+ globalThis.__patchworkAdapter =
1356
+ resolved.adapter;
1357
+ process.stderr.write(`[patchwork] model adapter initialized: ${resolved.adapter.name}\n`);
1358
+ }
1359
+ }
1360
+ catch (err) {
1361
+ process.stderr.write(`[patchwork] adapter init failed: ${err instanceof Error ? err.message : String(err)}\n`);
1362
+ }
1363
+ // If --analytics flag was passed, persist the preference immediately
1364
+ if (config.analyticsEnabled !== null) {
1365
+ setAnalyticsPref(config.analyticsEnabled);
1366
+ }
1367
+ // Auto-tmux: if requested and not already inside tmux or screen, re-exec inside a tmux session
1368
+ if (config.autoTmux &&
1369
+ !process.env.TMUX &&
1370
+ !process.env.STY &&
1371
+ !process.env.ZELLIJ &&
1372
+ !process.env.ZELLIJ_SESSION_NAME) {
1373
+ const ws = config.workspace.replace(/[^a-zA-Z0-9]/g, "").slice(-8);
1374
+ const hash = crypto
1375
+ .createHash("sha256")
1376
+ .update(config.workspace)
1377
+ .digest("hex")
1378
+ .slice(0, 6);
1379
+ const sessionName = `claude-bridge-${ws}${hash}`;
1380
+ // Check if tmux is available
1381
+ const tmuxCheck = spawnSync("which", ["tmux"], { stdio: "ignore" });
1382
+ if (tmuxCheck.status !== 0) {
1383
+ process.stderr.write("WARNING: --auto-tmux requested but tmux is not installed. Running without tmux.\n");
1384
+ }
1385
+ else {
1386
+ // Strip --auto-tmux from argv to avoid infinite re-exec loop
1387
+ const newArgv = process.argv.filter((a) => a !== "--auto-tmux");
1388
+ // Pass each argv token as a separate tmux argument so paths with spaces work correctly
1389
+ const result = spawnSync("tmux", ["new-session", "-d", "-s", sessionName, ...newArgv], { stdio: "inherit", timeout: 5000 });
1390
+ if (result.status === 0) {
1391
+ process.stderr.write(`Bridge launched in tmux session '${sessionName}'.\n`);
1392
+ process.stderr.write(` Attach with: tmux attach -t ${sessionName}\n`);
1393
+ process.exit(0);
1394
+ }
1395
+ else {
1396
+ // tmux session likely already exists — attach to it or fall through
1397
+ process.stderr.write(`WARNING: Could not create tmux session '${sessionName}' (already exists?). Running without auto-tmux.\n`);
1398
+ }
1399
+ }
1400
+ }
1401
+ // --watch: supervisor mode — spawn this binary as a child (without --watch) and restart on crash
1402
+ if (config.watch) {
1403
+ const childArgv = process.argv.filter((a) => a !== "--watch");
1404
+ const STABLE_THRESHOLD_MS = 60_000;
1405
+ const BASE_DELAY_MS = 2_000;
1406
+ const MAX_DELAY_MS = 30_000;
1407
+ let delay = BASE_DELAY_MS;
1408
+ let stopping = false;
1409
+ function runChild() {
1410
+ if (stopping)
1411
+ return;
1412
+ const startAt = Date.now();
1413
+ process.stderr.write("[supervisor] starting bridge\n");
1414
+ const [cmd, ...args] = childArgv;
1415
+ if (!cmd)
1416
+ return;
1417
+ const child = spawn(cmd, args, {
1418
+ stdio: "inherit",
1419
+ });
1420
+ for (const sig of ["SIGTERM", "SIGINT"]) {
1421
+ process.once(sig, () => {
1422
+ stopping = true;
1423
+ child.kill(sig);
1424
+ });
1425
+ }
1426
+ child.on("exit", (code, signal) => {
1427
+ if (stopping) {
1428
+ process.stderr.write("[supervisor] bridge stopped\n");
1429
+ process.exit(0);
1430
+ }
1431
+ const uptime = Date.now() - startAt;
1432
+ if (uptime >= STABLE_THRESHOLD_MS) {
1433
+ delay = BASE_DELAY_MS; // reset backoff after a stable run
1434
+ }
1435
+ process.stderr.write(`[supervisor] bridge exited (code=${code ?? signal}), restarting in ${delay / 1000}s\n`);
1436
+ setTimeout(() => {
1437
+ delay = Math.min(delay * 2, MAX_DELAY_MS);
1438
+ runChild();
1439
+ }, delay);
1440
+ });
1441
+ }
1442
+ runChild();
1443
+ }
1444
+ else {
1445
+ const bridge = new Bridge(config);
1446
+ bridge.start().catch((err) => {
1447
+ const message = err instanceof Error ? err.message : String(err);
1448
+ process.stderr.write(`Error: ${message}\n`);
1449
+ process.exit(1);
1450
+ });
1451
+ // F5: Silent self-update nudge (fire-and-forget)
1452
+ import("node:child_process")
1453
+ .then(({ exec }) => {
1454
+ exec("npm view claude-ide-bridge version", { timeout: 5000 }, (err, stdout) => {
1455
+ if (err || !stdout)
1456
+ return;
1457
+ const latest = stdout.trim();
1458
+ if (latest && semverGt(latest, PACKAGE_VERSION)) {
1459
+ console.log(`\n Bridge v${latest} available — run: npm update -g claude-ide-bridge\n`);
1460
+ }
1461
+ });
1462
+ })
1463
+ .catch(() => { });
1464
+ }
1465
+ //# sourceMappingURL=index.js.map