@mxalbert/context-mode 2.0.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 (365) hide show
  1. package/.claude-plugin/marketplace.json +33 -0
  2. package/.claude-plugin/plugin.json +31 -0
  3. package/.codex-plugin/hooks.json +65 -0
  4. package/.codex-plugin/mcp.json +13 -0
  5. package/.codex-plugin/plugin.json +31 -0
  6. package/.openclaw-plugin/index.ts +12 -0
  7. package/.openclaw-plugin/openclaw.plugin.json +38 -0
  8. package/.openclaw-plugin/package.json +28 -0
  9. package/LICENSE +94 -0
  10. package/README.md +1619 -0
  11. package/bin/statusline.mjs +378 -0
  12. package/build/adapters/antigravity/index.d.ts +51 -0
  13. package/build/adapters/antigravity/index.js +169 -0
  14. package/build/adapters/antigravity-cli/index.d.ts +51 -0
  15. package/build/adapters/antigravity-cli/index.js +342 -0
  16. package/build/adapters/base.d.ts +92 -0
  17. package/build/adapters/base.js +135 -0
  18. package/build/adapters/claude-code/config.d.ts +8 -0
  19. package/build/adapters/claude-code/config.js +8 -0
  20. package/build/adapters/claude-code/hooks.d.ts +106 -0
  21. package/build/adapters/claude-code/hooks.js +183 -0
  22. package/build/adapters/claude-code/index.d.ts +80 -0
  23. package/build/adapters/claude-code/index.js +569 -0
  24. package/build/adapters/claude-code-base.d.ts +49 -0
  25. package/build/adapters/claude-code-base.js +113 -0
  26. package/build/adapters/client-map.d.ts +10 -0
  27. package/build/adapters/client-map.js +42 -0
  28. package/build/adapters/codex/config.d.ts +8 -0
  29. package/build/adapters/codex/config.js +8 -0
  30. package/build/adapters/codex/hooks.d.ts +55 -0
  31. package/build/adapters/codex/hooks.js +64 -0
  32. package/build/adapters/codex/index.d.ts +87 -0
  33. package/build/adapters/codex/index.js +995 -0
  34. package/build/adapters/codex/paths.d.ts +1 -0
  35. package/build/adapters/codex/paths.js +12 -0
  36. package/build/adapters/codex/usage.d.ts +107 -0
  37. package/build/adapters/codex/usage.js +227 -0
  38. package/build/adapters/copilot-base.d.ts +94 -0
  39. package/build/adapters/copilot-base.js +297 -0
  40. package/build/adapters/copilot-cli/hooks.d.ts +33 -0
  41. package/build/adapters/copilot-cli/hooks.js +64 -0
  42. package/build/adapters/copilot-cli/index.d.ts +48 -0
  43. package/build/adapters/copilot-cli/index.js +341 -0
  44. package/build/adapters/cursor/config.d.ts +4 -0
  45. package/build/adapters/cursor/config.js +4 -0
  46. package/build/adapters/cursor/hooks.d.ts +55 -0
  47. package/build/adapters/cursor/hooks.js +82 -0
  48. package/build/adapters/cursor/index.d.ts +64 -0
  49. package/build/adapters/cursor/index.js +517 -0
  50. package/build/adapters/detect.d.ts +136 -0
  51. package/build/adapters/detect.js +623 -0
  52. package/build/adapters/gemini-cli/config.d.ts +8 -0
  53. package/build/adapters/gemini-cli/config.js +8 -0
  54. package/build/adapters/gemini-cli/hooks.d.ts +83 -0
  55. package/build/adapters/gemini-cli/hooks.js +115 -0
  56. package/build/adapters/gemini-cli/index.d.ts +80 -0
  57. package/build/adapters/gemini-cli/index.js +483 -0
  58. package/build/adapters/jetbrains-copilot/config.d.ts +8 -0
  59. package/build/adapters/jetbrains-copilot/config.js +8 -0
  60. package/build/adapters/jetbrains-copilot/hooks.d.ts +59 -0
  61. package/build/adapters/jetbrains-copilot/hooks.js +87 -0
  62. package/build/adapters/jetbrains-copilot/index.d.ts +31 -0
  63. package/build/adapters/jetbrains-copilot/index.js +131 -0
  64. package/build/adapters/kimi/config.d.ts +8 -0
  65. package/build/adapters/kimi/config.js +8 -0
  66. package/build/adapters/kimi/hooks.d.ts +28 -0
  67. package/build/adapters/kimi/hooks.js +34 -0
  68. package/build/adapters/kimi/index.d.ts +66 -0
  69. package/build/adapters/kimi/index.js +537 -0
  70. package/build/adapters/kimi/paths.d.ts +21 -0
  71. package/build/adapters/kimi/paths.js +52 -0
  72. package/build/adapters/kimi/usage.d.ts +82 -0
  73. package/build/adapters/kimi/usage.js +217 -0
  74. package/build/adapters/kiro/hooks.d.ts +61 -0
  75. package/build/adapters/kiro/hooks.js +94 -0
  76. package/build/adapters/kiro/index.d.ts +61 -0
  77. package/build/adapters/kiro/index.js +301 -0
  78. package/build/adapters/omp/index.d.ts +65 -0
  79. package/build/adapters/omp/index.js +182 -0
  80. package/build/adapters/omp/plugin.d.ts +81 -0
  81. package/build/adapters/omp/plugin.js +331 -0
  82. package/build/adapters/omp/usage.d.ts +49 -0
  83. package/build/adapters/omp/usage.js +110 -0
  84. package/build/adapters/openclaw/config.d.ts +8 -0
  85. package/build/adapters/openclaw/config.js +8 -0
  86. package/build/adapters/openclaw/hooks.d.ts +50 -0
  87. package/build/adapters/openclaw/hooks.js +61 -0
  88. package/build/adapters/openclaw/index.d.ts +74 -0
  89. package/build/adapters/openclaw/index.js +439 -0
  90. package/build/adapters/openclaw/mcp-tools.d.ts +54 -0
  91. package/build/adapters/openclaw/mcp-tools.js +207 -0
  92. package/build/adapters/openclaw/plugin.d.ts +141 -0
  93. package/build/adapters/openclaw/plugin.js +818 -0
  94. package/build/adapters/openclaw/session-db.d.ts +55 -0
  95. package/build/adapters/openclaw/session-db.js +88 -0
  96. package/build/adapters/openclaw/usage.d.ts +34 -0
  97. package/build/adapters/openclaw/usage.js +52 -0
  98. package/build/adapters/openclaw/workspace-router.d.ts +29 -0
  99. package/build/adapters/openclaw/workspace-router.js +64 -0
  100. package/build/adapters/opencode/config.d.ts +8 -0
  101. package/build/adapters/opencode/config.js +8 -0
  102. package/build/adapters/opencode/hooks.d.ts +38 -0
  103. package/build/adapters/opencode/hooks.js +50 -0
  104. package/build/adapters/opencode/index.d.ts +144 -0
  105. package/build/adapters/opencode/index.js +638 -0
  106. package/build/adapters/opencode/plugin.d.ts +249 -0
  107. package/build/adapters/opencode/plugin.js +1536 -0
  108. package/build/adapters/opencode/v2.d.ts +117 -0
  109. package/build/adapters/opencode/v2.js +224 -0
  110. package/build/adapters/opencode/zod3tov4.d.ts +1 -0
  111. package/build/adapters/opencode/zod3tov4.js +132 -0
  112. package/build/adapters/pi/extension.d.ts +81 -0
  113. package/build/adapters/pi/extension.js +861 -0
  114. package/build/adapters/pi/index.d.ts +57 -0
  115. package/build/adapters/pi/index.js +173 -0
  116. package/build/adapters/pi/mcp-bridge.d.ts +268 -0
  117. package/build/adapters/pi/mcp-bridge.js +894 -0
  118. package/build/adapters/qwen-code/hooks.d.ts +26 -0
  119. package/build/adapters/qwen-code/hooks.js +29 -0
  120. package/build/adapters/qwen-code/index.d.ts +40 -0
  121. package/build/adapters/qwen-code/index.js +361 -0
  122. package/build/adapters/qwen-code/usage.d.ts +90 -0
  123. package/build/adapters/qwen-code/usage.js +222 -0
  124. package/build/adapters/types.d.ts +360 -0
  125. package/build/adapters/types.js +118 -0
  126. package/build/adapters/vscode-copilot/config.d.ts +8 -0
  127. package/build/adapters/vscode-copilot/config.js +8 -0
  128. package/build/adapters/vscode-copilot/hooks.d.ts +71 -0
  129. package/build/adapters/vscode-copilot/hooks.js +98 -0
  130. package/build/adapters/vscode-copilot/index.d.ts +32 -0
  131. package/build/adapters/vscode-copilot/index.js +222 -0
  132. package/build/adapters/zed/index.d.ts +40 -0
  133. package/build/adapters/zed/index.js +163 -0
  134. package/build/cli.d.ts +34 -0
  135. package/build/cli.js +1828 -0
  136. package/build/db-base.d.ts +250 -0
  137. package/build/db-base.js +756 -0
  138. package/build/executor.d.ts +72 -0
  139. package/build/executor.js +678 -0
  140. package/build/exit-classify.d.ts +19 -0
  141. package/build/exit-classify.js +12 -0
  142. package/build/fetch/blocks.d.ts +145 -0
  143. package/build/fetch/blocks.js +244 -0
  144. package/build/fetch/extract.d.ts +68 -0
  145. package/build/fetch/extract.js +123 -0
  146. package/build/fetch/page-store.d.ts +66 -0
  147. package/build/fetch/page-store.js +174 -0
  148. package/build/fetch-cache.d.ts +13 -0
  149. package/build/fetch-cache.js +15 -0
  150. package/build/lifecycle.d.ts +124 -0
  151. package/build/lifecycle.js +278 -0
  152. package/build/package-identity.d.ts +26 -0
  153. package/build/package-identity.js +28 -0
  154. package/build/runPool.d.ts +36 -0
  155. package/build/runPool.js +51 -0
  156. package/build/runtime.d.ts +71 -0
  157. package/build/runtime.js +659 -0
  158. package/build/search/auto-memory.d.ts +47 -0
  159. package/build/search/auto-memory.js +170 -0
  160. package/build/search/ctx-search-schema.d.ts +90 -0
  161. package/build/search/ctx-search-schema.js +135 -0
  162. package/build/search/flood-guard.d.ts +57 -0
  163. package/build/search/flood-guard.js +80 -0
  164. package/build/search/unified.d.ts +56 -0
  165. package/build/search/unified.js +104 -0
  166. package/build/security.d.ts +206 -0
  167. package/build/security.js +780 -0
  168. package/build/server.d.ts +240 -0
  169. package/build/server.js +5034 -0
  170. package/build/session/analytics.d.ts +773 -0
  171. package/build/session/analytics.js +2420 -0
  172. package/build/session/db.d.ts +468 -0
  173. package/build/session/db.js +1330 -0
  174. package/build/session/error-classifier.d.ts +87 -0
  175. package/build/session/error-classifier.js +303 -0
  176. package/build/session/event-emit.d.ts +48 -0
  177. package/build/session/event-emit.js +101 -0
  178. package/build/session/extract.d.ts +333 -0
  179. package/build/session/extract.js +2688 -0
  180. package/build/session/model-prices.json +429 -0
  181. package/build/session/persist-tool-calls.d.ts +54 -0
  182. package/build/session/persist-tool-calls.js +105 -0
  183. package/build/session/pricing.d.ts +64 -0
  184. package/build/session/pricing.js +151 -0
  185. package/build/session/project-attribution.d.ts +73 -0
  186. package/build/session/project-attribution.js +245 -0
  187. package/build/session/purge.d.ts +138 -0
  188. package/build/session/purge.js +240 -0
  189. package/build/session/retrieval-marker.d.ts +39 -0
  190. package/build/session/retrieval-marker.js +65 -0
  191. package/build/session/snapshot.d.ts +46 -0
  192. package/build/session/snapshot.js +532 -0
  193. package/build/store-directory.d.ts +56 -0
  194. package/build/store-directory.js +254 -0
  195. package/build/store.d.ts +147 -0
  196. package/build/store.js +1714 -0
  197. package/build/truncate.d.ts +57 -0
  198. package/build/truncate.js +142 -0
  199. package/build/types.d.ts +117 -0
  200. package/build/types.js +20 -0
  201. package/build/util/claude-config.d.ts +32 -0
  202. package/build/util/claude-config.js +84 -0
  203. package/build/util/hook-config.d.ts +27 -0
  204. package/build/util/hook-config.js +76 -0
  205. package/build/util/jsonc.d.ts +14 -0
  206. package/build/util/jsonc.js +104 -0
  207. package/build/util/plugin-cache-integrity.d.ts +51 -0
  208. package/build/util/plugin-cache-integrity.js +146 -0
  209. package/build/util/project-dir.d.ts +149 -0
  210. package/build/util/project-dir.js +340 -0
  211. package/build/util/sibling-mcp.d.ts +79 -0
  212. package/build/util/sibling-mcp.js +181 -0
  213. package/cli.bundle.mjs +1495 -0
  214. package/configs/antigravity/GEMINI.md +68 -0
  215. package/configs/antigravity/mcp_config.json +7 -0
  216. package/configs/antigravity-cli/hooks/hooks.json +37 -0
  217. package/configs/antigravity-cli/hooks.json +37 -0
  218. package/configs/antigravity-cli/mcp_config.json +10 -0
  219. package/configs/antigravity-cli/plugin.json +14 -0
  220. package/configs/antigravity-cli/rules/context-mode.md +77 -0
  221. package/configs/antigravity-cli/skills/context-mode/SKILL.md +77 -0
  222. package/configs/claude-code/CLAUDE.md +91 -0
  223. package/configs/codex/AGENTS.md +99 -0
  224. package/configs/codex/config.toml +8 -0
  225. package/configs/codex/hooks.json +47 -0
  226. package/configs/copilot-cli/.github/plugin/plugin.json +23 -0
  227. package/configs/copilot-cli/.mcp.json +12 -0
  228. package/configs/copilot-cli/README.md +47 -0
  229. package/configs/copilot-cli/hooks.json +41 -0
  230. package/configs/copilot-cli/skills/context-mode/SKILL.md +38 -0
  231. package/configs/cursor/context-mode.mdc +76 -0
  232. package/configs/cursor/hooks.json +21 -0
  233. package/configs/cursor/mcp.json +7 -0
  234. package/configs/gemini-cli/GEMINI.md +88 -0
  235. package/configs/gemini-cli/mcp.json +7 -0
  236. package/configs/gemini-cli/settings.json +60 -0
  237. package/configs/jetbrains-copilot/copilot-instructions.md +80 -0
  238. package/configs/jetbrains-copilot/hooks.json +16 -0
  239. package/configs/jetbrains-copilot/mcp.json +7 -0
  240. package/configs/kilo/AGENTS.md +84 -0
  241. package/configs/kilo/kilo.json +6 -0
  242. package/configs/kimi/hooks.json +54 -0
  243. package/configs/kiro/KIRO.md +84 -0
  244. package/configs/kiro/agent.json +18 -0
  245. package/configs/kiro/mcp.json +7 -0
  246. package/configs/omp/SYSTEM.md +85 -0
  247. package/configs/omp/mcp.json +7 -0
  248. package/configs/openclaw/AGENTS.md +87 -0
  249. package/configs/openclaw/openclaw.json +13 -0
  250. package/configs/opencode/AGENTS.md +84 -0
  251. package/configs/opencode/opencode.json +6 -0
  252. package/configs/pi/AGENTS.md +3 -0
  253. package/configs/qwen-code/QWEN.md +91 -0
  254. package/configs/vscode-copilot/copilot-instructions.md +80 -0
  255. package/configs/vscode-copilot/hooks.json +16 -0
  256. package/configs/vscode-copilot/mcp.json +7 -0
  257. package/configs/zed/AGENTS.md +68 -0
  258. package/hooks/antigravity-cli/payload.mjs +98 -0
  259. package/hooks/antigravity-cli/posttooluse.mjs +138 -0
  260. package/hooks/antigravity-cli/pretooluse.mjs +78 -0
  261. package/hooks/antigravity-cli/stop.mjs +58 -0
  262. package/hooks/auto-injection.mjs +102 -0
  263. package/hooks/cache-heal-utils.mjs +379 -0
  264. package/hooks/codex/platform.mjs +1 -0
  265. package/hooks/codex/posttooluse.mjs +66 -0
  266. package/hooks/codex/precompact.mjs +70 -0
  267. package/hooks/codex/pretooluse.mjs +42 -0
  268. package/hooks/codex/sessionstart.mjs +121 -0
  269. package/hooks/codex/stop.mjs +138 -0
  270. package/hooks/codex/userpromptsubmit.mjs +75 -0
  271. package/hooks/copilot-cli/posttooluse.mjs +79 -0
  272. package/hooks/copilot-cli/precompact.mjs +66 -0
  273. package/hooks/copilot-cli/pretooluse.mjs +41 -0
  274. package/hooks/copilot-cli/sessionstart.mjs +121 -0
  275. package/hooks/copilot-cli/stop.mjs +59 -0
  276. package/hooks/copilot-cli/userpromptsubmit.mjs +77 -0
  277. package/hooks/core/codex-caps.mjs +112 -0
  278. package/hooks/core/formatters.mjs +356 -0
  279. package/hooks/core/mcp-ready.mjs +109 -0
  280. package/hooks/core/platform-detect.mjs +49 -0
  281. package/hooks/core/routing.mjs +1050 -0
  282. package/hooks/core/stdin.mjs +90 -0
  283. package/hooks/core/tool-naming.mjs +57 -0
  284. package/hooks/cursor/afteragentresponse.mjs +74 -0
  285. package/hooks/cursor/hooks.json +31 -0
  286. package/hooks/cursor/posttooluse.mjs +72 -0
  287. package/hooks/cursor/pretooluse.mjs +26 -0
  288. package/hooks/cursor/sessionstart.mjs +97 -0
  289. package/hooks/cursor/stop.mjs +48 -0
  290. package/hooks/ensure-deps.mjs +254 -0
  291. package/hooks/formatters/claude-code.mjs +113 -0
  292. package/hooks/formatters/cursor.mjs +37 -0
  293. package/hooks/formatters/gemini-cli.mjs +55 -0
  294. package/hooks/formatters/vscode-copilot.mjs +55 -0
  295. package/hooks/gemini-cli/aftermodel.mjs +70 -0
  296. package/hooks/gemini-cli/aftertool.mjs +61 -0
  297. package/hooks/gemini-cli/beforeagent.mjs +99 -0
  298. package/hooks/gemini-cli/beforetool.mjs +27 -0
  299. package/hooks/gemini-cli/precompress.mjs +55 -0
  300. package/hooks/gemini-cli/sessionstart.mjs +137 -0
  301. package/hooks/heal-partial-install.mjs +712 -0
  302. package/hooks/hooks.json +143 -0
  303. package/hooks/jetbrains-copilot/posttooluse.mjs +61 -0
  304. package/hooks/jetbrains-copilot/precompact.mjs +55 -0
  305. package/hooks/jetbrains-copilot/pretooluse.mjs +27 -0
  306. package/hooks/jetbrains-copilot/sessionstart.mjs +124 -0
  307. package/hooks/kimi/platform.mjs +1 -0
  308. package/hooks/kimi/posttooluse.mjs +72 -0
  309. package/hooks/kimi/precompact.mjs +80 -0
  310. package/hooks/kimi/pretooluse.mjs +42 -0
  311. package/hooks/kimi/sessionend.mjs +61 -0
  312. package/hooks/kimi/sessionstart.mjs +113 -0
  313. package/hooks/kimi/stop.mjs +132 -0
  314. package/hooks/kimi/userpromptsubmit.mjs +90 -0
  315. package/hooks/kiro/agentspawn.mjs +97 -0
  316. package/hooks/kiro/posttooluse.mjs +51 -0
  317. package/hooks/kiro/pretooluse.mjs +64 -0
  318. package/hooks/kiro/userpromptsubmit.mjs +88 -0
  319. package/hooks/normalize-hooks.mjs +323 -0
  320. package/hooks/platform-bridge.mjs +340 -0
  321. package/hooks/posttooluse.mjs +225 -0
  322. package/hooks/precompact.mjs +100 -0
  323. package/hooks/pretooluse.mjs +227 -0
  324. package/hooks/qwen-code/platform.mjs +1 -0
  325. package/hooks/qwen-code/stop.mjs +168 -0
  326. package/hooks/routing-block.mjs +105 -0
  327. package/hooks/run-hook.mjs +95 -0
  328. package/hooks/security.bundle.mjs +2 -0
  329. package/hooks/session-attribution.bundle.mjs +1 -0
  330. package/hooks/session-db.bundle.mjs +145 -0
  331. package/hooks/session-directive.mjs +520 -0
  332. package/hooks/session-extract.bundle.mjs +3 -0
  333. package/hooks/session-helpers.mjs +428 -0
  334. package/hooks/session-loaders.mjs +448 -0
  335. package/hooks/session-snapshot.bundle.mjs +32 -0
  336. package/hooks/sessionstart.mjs +464 -0
  337. package/hooks/stop.mjs +82 -0
  338. package/hooks/suppress-stderr.mjs +25 -0
  339. package/hooks/userpromptsubmit.mjs +108 -0
  340. package/hooks/vscode-copilot/posttooluse.mjs +61 -0
  341. package/hooks/vscode-copilot/precompact.mjs +55 -0
  342. package/hooks/vscode-copilot/pretooluse.mjs +27 -0
  343. package/hooks/vscode-copilot/sessionstart.mjs +129 -0
  344. package/openclaw.plugin.json +38 -0
  345. package/package.json +132 -0
  346. package/scripts/heal-better-sqlite3.mjs +430 -0
  347. package/scripts/heal-installed-plugins.mjs +635 -0
  348. package/scripts/plugin-cache-integrity.mjs +248 -0
  349. package/scripts/postinstall.mjs +397 -0
  350. package/server.bundle.mjs +1421 -0
  351. package/server.js +5 -0
  352. package/skills/.ignore +7 -0
  353. package/skills/context-mode/SKILL.md +300 -0
  354. package/skills/context-mode/references/anti-patterns.md +283 -0
  355. package/skills/context-mode/references/patterns-javascript.md +298 -0
  356. package/skills/context-mode/references/patterns-python.md +304 -0
  357. package/skills/context-mode/references/patterns-shell.md +277 -0
  358. package/skills/ctx-doctor/SKILL.md +22 -0
  359. package/skills/ctx-index/SKILL.md +46 -0
  360. package/skills/ctx-insight/SKILL.md +24 -0
  361. package/skills/ctx-purge/SKILL.md +49 -0
  362. package/skills/ctx-search/SKILL.md +35 -0
  363. package/skills/ctx-stats/SKILL.md +26 -0
  364. package/skills/ctx-upgrade/SKILL.md +31 -0
  365. package/start.mjs +616 -0
@@ -0,0 +1,569 @@
1
+ /**
2
+ * adapters/claude-code — Claude Code platform adapter.
3
+ *
4
+ * Extends ClaudeCodeBaseAdapter (shared wire-protocol parse/format methods)
5
+ * with Claude Code-specific configuration, diagnostics, and upgrade logic.
6
+ *
7
+ * Claude Code hook specifics:
8
+ * - Session ID: transcript_path UUID > session_id > CLAUDE_SESSION_ID > ppid
9
+ * - Config root: $CLAUDE_CONFIG_DIR (when set) or ~/.claude
10
+ * - Settings: <configDir>/settings.json
11
+ * - Session dir: <configDir>/context-mode/sessions/
12
+ * - Plugin registry: <configDir>/plugins/installed_plugins.json
13
+ */
14
+ import { readFileSync, writeFileSync, existsSync, readdirSync, chmodSync, accessSync, mkdirSync, constants, } from "node:fs";
15
+ import { resolve, join } from "node:path";
16
+ import { homedir } from "node:os";
17
+ import { ClaudeCodeBaseAdapter } from "../claude-code-base.js";
18
+ import { resolveContextModeDataRoot } from "../base.js";
19
+ import { resolveClaudeConfigDir } from "../../util/claude-config.js";
20
+ import { checkPluginCacheIntegritySync } from "../../util/plugin-cache-integrity.js";
21
+ import { buildHookRuntimeCommand, } from "../types.js";
22
+ import { HOOK_TYPES, HOOK_SCRIPTS, REQUIRED_HOOKS, PRE_TOOL_USE_MATCHERS, PRE_TOOL_USE_MATCHER_PATTERN, isContextModeHook, isAnyContextModeHook, extractHookScriptPath, buildHookCommand, } from "./hooks.js";
23
+ // ─────────────────────────────────────────────────────────
24
+ // Adapter implementation
25
+ // ─────────────────────────────────────────────────────────
26
+ export class ClaudeCodeAdapter extends ClaudeCodeBaseAdapter {
27
+ constructor() {
28
+ super([".claude"]);
29
+ }
30
+ name = "Claude Code";
31
+ paradigm = "json-stdio";
32
+ projectDirEnvVar = "CLAUDE_PROJECT_DIR";
33
+ capabilities = {
34
+ preToolUse: true,
35
+ postToolUse: true,
36
+ preCompact: true,
37
+ sessionStart: true,
38
+ canModifyArgs: true,
39
+ canModifyOutput: true,
40
+ canInjectSessionContext: true,
41
+ };
42
+ // ── Configuration ──────────────────────────────────────
43
+ /**
44
+ * Honor `CLAUDE_CONFIG_DIR` (the canonical Claude Code config root) before
45
+ * falling back to `~/.claude`. Mirrors the contract that
46
+ * `hooks/session-helpers.mjs::resolveConfigDir` already follows — including
47
+ * tilde expansion for shells that pass `~/foo` through unchanged — so server
48
+ * and hooks agree on where session-scoped state lives. See issue #453.
49
+ *
50
+ * Tilde regex `/^~[/\\]?/` only handles the current-user form (`~`, `~/`,
51
+ * `~\`); `~user/` is NOT expanded to a per-user homedir (matches
52
+ * `resolveConfigDir`). Non-tilde values are run through `resolve()` to
53
+ * normalize relative paths to absolute against cwd; the hook helper
54
+ * intentionally leaves them raw, but the adapter contract guarantees an
55
+ * absolute path (BaseAdapter.getConfigDir docstring).
56
+ *
57
+ * Issue #460 round-3: routed through the canonical
58
+ * `resolveClaudeConfigDir` util so server, CLI, security, and adapter
59
+ * agree byte-for-byte (incl. empty/whitespace-only env fallback).
60
+ */
61
+ getConfigDir(_projectDir) {
62
+ return resolveClaudeConfigDir();
63
+ }
64
+ getSessionDir() {
65
+ // Issue #649: honor CONTEXT_MODE_DATA_DIR universal storage override
66
+ // before falling back to the Claude-rooted default. The override moves
67
+ // ONLY context-mode-owned state; settings.json + CLAUDE_CONFIG_DIR stay
68
+ // intact below.
69
+ const override = resolveContextModeDataRoot();
70
+ const dir = override
71
+ ? join(override, "context-mode", "sessions")
72
+ : join(this.getConfigDir(), "context-mode", "sessions");
73
+ mkdirSync(dir, { recursive: true });
74
+ return dir;
75
+ }
76
+ getSettingsPath() {
77
+ return join(this.getConfigDir(), "settings.json");
78
+ }
79
+ generateHookConfig(pluginRoot) {
80
+ // Algo-D3: every command flows through `buildHookRuntimeCommand`
81
+ // (defined in src/adapters/types.ts), which:
82
+ // - quotes both runtime path and scriptPath (#548 — Windows
83
+ // pluginRoots with spaces no longer fall through
84
+ // extractHookScriptPath's ambiguous-tail fallback),
85
+ // - swaps backslashes for forward slashes (#372 MSYS path mangling),
86
+ // - resolves the JS runtime via `resolveHookRuntime`: Bun ≥1.0 when
87
+ // available, else `process.execPath` (#369 PATH resolution on Git
88
+ // Bash, #738 bun cold-start win).
89
+ // Pre-D3 we hand-rolled `node "${pluginRoot}/hooks/X.mjs"` for all
90
+ // six events; bare `node` made claude-code the lone outlier and
91
+ // dropping the execPath swap re-opened the Windows class. Algo-D3.5
92
+ // (CI invariant in tests/adapters/claude-code.test.ts) locks this in
93
+ // for adapter #16.
94
+ const preToolUseCommand = buildHookRuntimeCommand(`${pluginRoot}/hooks/pretooluse.mjs`);
95
+ const preToolUseMatchers = [...PRE_TOOL_USE_MATCHERS];
96
+ return {
97
+ PreToolUse: preToolUseMatchers.map((matcher) => ({
98
+ matcher,
99
+ hooks: [{ type: "command", command: preToolUseCommand }],
100
+ })),
101
+ PostToolUse: [
102
+ {
103
+ matcher: "",
104
+ hooks: [
105
+ {
106
+ type: "command",
107
+ command: buildHookRuntimeCommand(`${pluginRoot}/hooks/posttooluse.mjs`),
108
+ },
109
+ ],
110
+ },
111
+ ],
112
+ PreCompact: [
113
+ {
114
+ matcher: "",
115
+ hooks: [
116
+ {
117
+ type: "command",
118
+ command: buildHookRuntimeCommand(`${pluginRoot}/hooks/precompact.mjs`),
119
+ },
120
+ ],
121
+ },
122
+ ],
123
+ UserPromptSubmit: [
124
+ {
125
+ matcher: "",
126
+ hooks: [
127
+ {
128
+ type: "command",
129
+ command: buildHookRuntimeCommand(`${pluginRoot}/hooks/userpromptsubmit.mjs`),
130
+ },
131
+ ],
132
+ },
133
+ ],
134
+ SessionStart: [
135
+ {
136
+ matcher: "",
137
+ hooks: [
138
+ {
139
+ type: "command",
140
+ command: buildHookRuntimeCommand(`${pluginRoot}/hooks/sessionstart.mjs`),
141
+ },
142
+ ],
143
+ },
144
+ ],
145
+ Stop: [
146
+ {
147
+ matcher: "",
148
+ hooks: [
149
+ {
150
+ type: "command",
151
+ command: buildHookRuntimeCommand(`${pluginRoot}/hooks/stop.mjs`),
152
+ },
153
+ ],
154
+ },
155
+ ],
156
+ };
157
+ }
158
+ readSettings() {
159
+ try {
160
+ const raw = readFileSync(this.getSettingsPath(), "utf-8");
161
+ return JSON.parse(raw);
162
+ }
163
+ catch {
164
+ return null;
165
+ }
166
+ }
167
+ writeSettings(settings) {
168
+ writeFileSync(this.getSettingsPath(), JSON.stringify(settings, null, 2) + "\n", "utf-8");
169
+ }
170
+ // ── Diagnostics (doctor) ─────────────────────────────────
171
+ validateHooks(pluginRoot) {
172
+ const results = [];
173
+ const settings = this.readSettings();
174
+ if (!settings) {
175
+ results.push({
176
+ check: "PreToolUse hook",
177
+ status: "fail",
178
+ message: `Could not read ${this.getSettingsPath()}`,
179
+ fix: "context-mode upgrade",
180
+ });
181
+ return results;
182
+ }
183
+ const hooks = settings.hooks;
184
+ // Read plugin hooks.json as fallback (Issue #94: plugin installs
185
+ // register hooks in hooks/hooks.json, not in settings.json)
186
+ const pluginHooks = this.readPluginHooks(pluginRoot);
187
+ // Check PreToolUse (settings.json first, then plugin hooks.json fallback)
188
+ const hasPreToolUse = this.checkHookType(hooks, pluginHooks, HOOK_TYPES.PRE_TOOL_USE);
189
+ results.push({
190
+ check: "PreToolUse hook",
191
+ status: hasPreToolUse ? "pass" : "fail",
192
+ message: hasPreToolUse
193
+ ? "PreToolUse hook configured"
194
+ : "No PreToolUse hooks found",
195
+ fix: hasPreToolUse ? undefined : "context-mode upgrade",
196
+ });
197
+ // Check SessionStart (settings.json first, then plugin hooks.json fallback)
198
+ const hasSessionStart = this.checkHookType(hooks, pluginHooks, HOOK_TYPES.SESSION_START);
199
+ results.push({
200
+ check: "SessionStart hook",
201
+ status: hasSessionStart ? "pass" : "fail",
202
+ message: hasSessionStart
203
+ ? "SessionStart hook configured"
204
+ : "No SessionStart hooks found",
205
+ fix: hasSessionStart ? undefined : "context-mode upgrade",
206
+ });
207
+ return results;
208
+ }
209
+ /**
210
+ * Adapter-defined health checks (Algo-D1 + Algo-D5).
211
+ *
212
+ * For each entry in HOOK_SCRIPTS (the canonical hookType → scriptName
213
+ * map), emit a HealthCheck that joins `pluginRoot + "hooks" +
214
+ * scriptName` and probes via `existsSync`. Crucially, this NEVER
215
+ * parses a hook command — pluginRoot and scriptName are both in our
216
+ * hand, so the regex round-trip that produced the #548 doubled-path
217
+ * FAIL is bypassed entirely.
218
+ *
219
+ * The hook check derives from HOOK_SCRIPTS (single source of truth in
220
+ * src/adapters/claude-code/hooks.ts), so adding a new hook event in
221
+ * that map auto-extends doctor coverage — no parallel hardcoded list
222
+ * to maintain.
223
+ *
224
+ * Algo-D5: appends a single "Plugin cache integrity" check that
225
+ * delegates to the same helper start.mjs uses at boot
226
+ * (scripts/plugin-cache-integrity.mjs::assertPluginCacheIntegrity).
227
+ * Same code, two callsites — boot fail-fast and doctor diagnostic
228
+ * agree byte-for-byte. Users hitting #550 get the actionable signal
229
+ * without restarting the MCP server.
230
+ */
231
+ getHealthChecks(pluginRoot) {
232
+ const hookChecks = Object.entries(HOOK_SCRIPTS).map(([hookType, scriptName]) => {
233
+ const absolutePath = join(pluginRoot, "hooks", scriptName);
234
+ return {
235
+ name: `Hook script: ${hookType} (${scriptName})`,
236
+ check: () => {
237
+ // Direct existsSync — no hook-command parsing, no regex.
238
+ // pluginRoot is the value the doctor was invoked with;
239
+ // scriptName comes from the canonical HOOK_SCRIPTS map.
240
+ if (existsSync(absolutePath)) {
241
+ return { status: "OK", detail: absolutePath };
242
+ }
243
+ return {
244
+ status: "FAIL",
245
+ detail: `not found at ${absolutePath}`,
246
+ };
247
+ },
248
+ };
249
+ });
250
+ const integrityCheck = {
251
+ name: "Plugin cache integrity",
252
+ check: () => checkPluginCacheIntegritySync(pluginRoot),
253
+ };
254
+ return [...hookChecks, integrityCheck];
255
+ }
256
+ /** Read plugin hooks from hooks/hooks.json or .claude-plugin/hooks/hooks.json */
257
+ readPluginHooks(pluginRoot) {
258
+ const candidates = [
259
+ join(pluginRoot, "hooks", "hooks.json"),
260
+ join(pluginRoot, ".claude-plugin", "hooks", "hooks.json"),
261
+ ];
262
+ for (const candidate of candidates) {
263
+ try {
264
+ const raw = readFileSync(candidate, "utf-8");
265
+ const parsed = JSON.parse(raw);
266
+ if (parsed.hooks)
267
+ return parsed.hooks;
268
+ }
269
+ catch { /* not available */ }
270
+ }
271
+ return undefined;
272
+ }
273
+ /** Check if a hook type is configured in either settings.json or plugin hooks */
274
+ checkHookType(settingsHooks, pluginHooks, hookType) {
275
+ // Check settings.json
276
+ const fromSettings = settingsHooks?.[hookType];
277
+ if (fromSettings && fromSettings.length > 0) {
278
+ if (fromSettings.some((entry) => isContextModeHook(entry, hookType))) {
279
+ return true;
280
+ }
281
+ }
282
+ // Fallback: check plugin hooks.json
283
+ const fromPlugin = pluginHooks?.[hookType];
284
+ if (fromPlugin && fromPlugin.length > 0) {
285
+ if (fromPlugin.some((entry) => isContextModeHook(entry, hookType))) {
286
+ return true;
287
+ }
288
+ }
289
+ return false;
290
+ }
291
+ checkPluginRegistration() {
292
+ const settings = this.readSettings();
293
+ if (!settings) {
294
+ return {
295
+ check: "Plugin registration",
296
+ status: "warn",
297
+ message: "Could not read settings.json",
298
+ };
299
+ }
300
+ const enabledPlugins = settings.enabledPlugins;
301
+ if (!enabledPlugins) {
302
+ return {
303
+ check: "Plugin registration",
304
+ status: "warn",
305
+ message: "No enabledPlugins section found (might be using standalone MCP mode)",
306
+ };
307
+ }
308
+ const pluginKey = Object.keys(enabledPlugins).find((k) => k.startsWith("context-mode"));
309
+ if (pluginKey && enabledPlugins[pluginKey]) {
310
+ return {
311
+ check: "Plugin registration",
312
+ status: "pass",
313
+ message: `Plugin enabled: ${pluginKey}`,
314
+ };
315
+ }
316
+ return {
317
+ check: "Plugin registration",
318
+ status: "warn",
319
+ message: "context-mode not in enabledPlugins (might be using standalone MCP mode)",
320
+ };
321
+ }
322
+ getInstalledVersion() {
323
+ // Primary: read from installed_plugins.json
324
+ try {
325
+ const ipPath = join(this.getConfigDir(), "plugins", "installed_plugins.json");
326
+ const ipRaw = JSON.parse(readFileSync(ipPath, "utf-8"));
327
+ const plugins = ipRaw.plugins ?? {};
328
+ for (const [key, entries] of Object.entries(plugins)) {
329
+ if (!key.toLowerCase().includes("context-mode"))
330
+ continue;
331
+ const arr = entries;
332
+ if (arr.length > 0 && typeof arr[0].version === "string") {
333
+ return arr[0].version;
334
+ }
335
+ }
336
+ }
337
+ catch {
338
+ /* fallback below */
339
+ }
340
+ // Fallback: scan common plugin cache locations.
341
+ // `resolveClaudeConfigDir` honors $CLAUDE_CONFIG_DIR; the literal
342
+ // `~/.claude` is also retained as a hard floor so environments that
343
+ // misconfigure the env still find the canonical dir if it exists.
344
+ const bases = Array.from(new Set([
345
+ this.getConfigDir(),
346
+ resolveClaudeConfigDir(),
347
+ resolve(homedir(), ".claude"),
348
+ resolve(homedir(), ".config", "claude"),
349
+ ]));
350
+ for (const base of bases) {
351
+ const cacheDir = resolve(base, "plugins", "cache", "context-mode", "context-mode");
352
+ try {
353
+ const entries = readdirSync(cacheDir);
354
+ const versions = entries
355
+ .filter((e) => /^\d+\.\d+\.\d+/.test(e))
356
+ .sort((a, b) => {
357
+ const pa = a.split(".").map(Number);
358
+ const pb = b.split(".").map(Number);
359
+ for (let i = 0; i < 3; i++) {
360
+ if ((pa[i] ?? 0) !== (pb[i] ?? 0))
361
+ return (pa[i] ?? 0) - (pb[i] ?? 0);
362
+ }
363
+ return 0;
364
+ });
365
+ if (versions.length > 0)
366
+ return versions[versions.length - 1];
367
+ }
368
+ catch {
369
+ /* continue */
370
+ }
371
+ }
372
+ return "not installed";
373
+ }
374
+ // ── Upgrade ────────────────────────────────────────────
375
+ configureAllHooks(pluginRoot) {
376
+ const settings = this.readSettings() ?? {};
377
+ const hooks = (settings.hooks ?? {});
378
+ const changes = [];
379
+ // Remove stale context-mode hook entries across ALL hook types (fixes #187).
380
+ // After a marketplace auto-update or version change, settings.json may contain
381
+ // hardcoded paths pointing to deleted version directories (e.g., .../0.9.17/hooks/...).
382
+ // Clean these before registering fresh entries to prevent SessionStart errors.
383
+ for (const hookType of Object.keys(hooks)) {
384
+ const entries = hooks[hookType];
385
+ if (!Array.isArray(entries))
386
+ continue;
387
+ const filtered = entries.filter((entry) => {
388
+ const typedEntry = entry;
389
+ if (!isAnyContextModeHook(typedEntry))
390
+ return true; // preserve non-context-mode hooks
391
+ // Keep CLI dispatcher entries (path-independent, never stale)
392
+ const commands = typedEntry.hooks ?? [];
393
+ const hasOnlyDispatcherCommands = commands.every((h) => !h.command || !extractHookScriptPath(h.command));
394
+ if (hasOnlyDispatcherCommands)
395
+ return true;
396
+ // For node path commands, check if the referenced script file exists
397
+ return commands.every((h) => {
398
+ const scriptPath = h.command ? extractHookScriptPath(h.command) : null;
399
+ if (!scriptPath)
400
+ return true; // not a path-based command
401
+ return existsSync(scriptPath);
402
+ });
403
+ });
404
+ const removed = entries.length - filtered.length;
405
+ if (removed > 0) {
406
+ hooks[hookType] = filtered;
407
+ changes.push(`Removed ${removed} stale ${hookType} hook(s)`);
408
+ }
409
+ }
410
+ // If plugin hooks.json already covers all required hooks AND context-mode is
411
+ // actually installed as a Claude Code plugin (present in enabledPlugins), skip
412
+ // settings.json registration — hooks.json with ${CLAUDE_PLUGIN_ROOT} is the
413
+ // source of truth for plugin installs (Issue #198).
414
+ //
415
+ // Standalone / MacPorts installs are NOT in enabledPlugins. For those, the
416
+ // hooks/hooks.json shipped in the npm package is never consulted by Claude Code
417
+ // (it uses ${CLAUDE_PLUGIN_ROOT} which is only set in plugin mode). We must
418
+ // always write absolute-path hook commands to settings.json in that case.
419
+ const pluginRegistration = this.checkPluginRegistration();
420
+ const isPluginInstall = pluginRegistration.status === "pass";
421
+ const pluginHooks = isPluginInstall ? this.readPluginHooks(pluginRoot) : undefined;
422
+ if (pluginHooks) {
423
+ const allCovered = REQUIRED_HOOKS.every((ht) => this.checkHookType(undefined, pluginHooks, ht));
424
+ if (allCovered) {
425
+ // Strip ONLY the inner context-mode hook commands from each matcher entry —
426
+ // hooks.json is the source of truth for ctx-mode. User hooks co-located in
427
+ // the same matcher entry MUST be preserved (#415: entry-level filter wiped
428
+ // every co-located user hook). After stripping, prune entries whose `hooks`
429
+ // array becomes empty.
430
+ const ctxScriptNames = Object.values(HOOK_SCRIPTS);
431
+ const isCtxModeCommand = (cmd) => cmd != null &&
432
+ (ctxScriptNames.some((s) => cmd.includes(s)) ||
433
+ cmd.includes("context-mode hook"));
434
+ for (const hookType of Object.keys(hooks)) {
435
+ const entries = hooks[hookType];
436
+ if (!Array.isArray(entries))
437
+ continue;
438
+ let totalRemoved = 0;
439
+ for (const entry of entries) {
440
+ const typedEntry = entry;
441
+ const innerHooks = typedEntry.hooks ?? [];
442
+ const before = innerHooks.length;
443
+ typedEntry.hooks = innerHooks.filter((h) => !isCtxModeCommand(h.command));
444
+ totalRemoved += before - typedEntry.hooks.length;
445
+ }
446
+ const pruned = entries.filter((e) => {
447
+ const ih = e.hooks;
448
+ return Array.isArray(ih) && ih.length > 0;
449
+ });
450
+ if (totalRemoved > 0 || pruned.length !== entries.length) {
451
+ hooks[hookType] = pruned;
452
+ if (totalRemoved > 0) {
453
+ changes.push(`Removed ${totalRemoved} duplicate ${hookType} hook(s) — covered by plugin hooks.json`);
454
+ }
455
+ }
456
+ }
457
+ settings.hooks = hooks;
458
+ this.writeSettings(settings);
459
+ changes.push("Skipped settings.json registration — plugin hooks.json is sufficient");
460
+ return changes;
461
+ }
462
+ }
463
+ // Register fresh hooks for required hook types
464
+ const hookTypes = [
465
+ HOOK_TYPES.PRE_TOOL_USE,
466
+ HOOK_TYPES.SESSION_START,
467
+ ];
468
+ for (const hookType of hookTypes) {
469
+ const command = buildHookCommand(hookType, pluginRoot);
470
+ if (hookType === HOOK_TYPES.PRE_TOOL_USE) {
471
+ const entry = {
472
+ matcher: PRE_TOOL_USE_MATCHER_PATTERN,
473
+ hooks: [{ type: "command", command }],
474
+ };
475
+ const existing = hooks.PreToolUse;
476
+ if (existing && Array.isArray(existing)) {
477
+ const idx = existing.findIndex((e) => isContextModeHook(e, hookType));
478
+ if (idx >= 0) {
479
+ existing[idx] = entry;
480
+ changes.push(`Updated existing ${hookType} hook entry`);
481
+ }
482
+ else {
483
+ existing.push(entry);
484
+ changes.push(`Added ${hookType} hook entry`);
485
+ }
486
+ hooks.PreToolUse = existing;
487
+ }
488
+ else {
489
+ hooks.PreToolUse = [entry];
490
+ changes.push(`Created ${hookType} hooks section`);
491
+ }
492
+ }
493
+ else {
494
+ const entry = {
495
+ matcher: "",
496
+ hooks: [{ type: "command", command }],
497
+ };
498
+ const existing = hooks[hookType];
499
+ if (existing && Array.isArray(existing)) {
500
+ const idx = existing.findIndex((e) => isContextModeHook(e, hookType));
501
+ if (idx >= 0) {
502
+ existing[idx] = entry;
503
+ changes.push(`Updated existing ${hookType} hook entry`);
504
+ }
505
+ else {
506
+ existing.push(entry);
507
+ changes.push(`Added ${hookType} hook entry`);
508
+ }
509
+ hooks[hookType] = existing;
510
+ }
511
+ else {
512
+ hooks[hookType] = [entry];
513
+ changes.push(`Created ${hookType} hooks section`);
514
+ }
515
+ }
516
+ }
517
+ settings.hooks = hooks;
518
+ this.writeSettings(settings);
519
+ return changes;
520
+ }
521
+ setHookPermissions(pluginRoot) {
522
+ const set = [];
523
+ for (const [, scriptName] of Object.entries(HOOK_SCRIPTS)) {
524
+ const scriptPath = resolve(pluginRoot, "hooks", scriptName);
525
+ try {
526
+ accessSync(scriptPath, constants.R_OK);
527
+ chmodSync(scriptPath, 0o755);
528
+ set.push(scriptPath);
529
+ }
530
+ catch {
531
+ /* skip missing scripts */
532
+ }
533
+ }
534
+ return set;
535
+ }
536
+ updatePluginRegistry(pluginRoot, version) {
537
+ try {
538
+ const ipPath = join(this.getConfigDir(), "plugins", "installed_plugins.json");
539
+ const ipRaw = JSON.parse(readFileSync(ipPath, "utf-8"));
540
+ for (const [key, entries] of Object.entries(ipRaw.plugins || {})) {
541
+ if (!key.toLowerCase().includes("context-mode"))
542
+ continue;
543
+ for (const entry of entries) {
544
+ entry.installPath = pluginRoot;
545
+ entry.version = version;
546
+ entry.lastUpdated = new Date().toISOString();
547
+ }
548
+ }
549
+ writeFileSync(ipPath, JSON.stringify(ipRaw, null, 2) + "\n", "utf-8");
550
+ }
551
+ catch {
552
+ /* best effort */
553
+ }
554
+ }
555
+ // ── Session ID extraction ───────────────────────────────
556
+ // Claude Code priority: transcript_path UUID > session_id > CLAUDE_SESSION_ID > ppid
557
+ extractSessionId(input) {
558
+ if (input.transcript_path) {
559
+ const match = input.transcript_path.match(/([a-f0-9-]{36})\.jsonl$/);
560
+ if (match)
561
+ return match[1];
562
+ }
563
+ if (input.session_id)
564
+ return input.session_id;
565
+ if (process.env.CLAUDE_SESSION_ID)
566
+ return process.env.CLAUDE_SESSION_ID;
567
+ return `pid-${process.ppid}`;
568
+ }
569
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * adapters/claude-code-base — Shared base for Claude Code wire-protocol adapters.
3
+ *
4
+ * Claude Code and Qwen Code use the identical JSON stdin/stdout hook protocol:
5
+ * - Input fields: tool_name, tool_input, tool_output, is_error, session_id,
6
+ * transcript_path, source
7
+ * - Blocking: `permissionDecision: "deny"` in response
8
+ * - Arg modification: `updatedInput` field in response
9
+ * - Output modification: `updatedMCPToolOutput` field in response
10
+ * - Context injection: `additionalContext` at response root (not wrapped)
11
+ * - PreCompact/SessionStart: stdout on exit 0
12
+ *
13
+ * This base class implements the 8 shared parse/format methods.
14
+ * Subclasses provide platform-specific config (env vars, settings path,
15
+ * session ID priority, hook config, diagnostics, upgrade).
16
+ */
17
+ import { BaseAdapter } from "./base.js";
18
+ import type { PreToolUseEvent, PostToolUseEvent, PreCompactEvent, SessionStartEvent, PreToolUseResponse, PostToolUseResponse, PreCompactResponse, SessionStartResponse } from "./types.js";
19
+ export interface ClaudeCodeWireInput {
20
+ tool_name?: string;
21
+ tool_input?: Record<string, unknown>;
22
+ tool_output?: string;
23
+ is_error?: boolean;
24
+ session_id?: string;
25
+ transcript_path?: string;
26
+ source?: string;
27
+ }
28
+ export declare abstract class ClaudeCodeBaseAdapter extends BaseAdapter {
29
+ /**
30
+ * Environment variable name for the project directory.
31
+ * Claude Code: "CLAUDE_PROJECT_DIR", Qwen Code: "QWEN_PROJECT_DIR"
32
+ */
33
+ protected abstract readonly projectDirEnvVar: string;
34
+ parsePreToolUseInput(raw: unknown): PreToolUseEvent;
35
+ parsePostToolUseInput(raw: unknown): PostToolUseEvent;
36
+ parsePreCompactInput(raw: unknown): PreCompactEvent;
37
+ parseSessionStartInput(raw: unknown): SessionStartEvent;
38
+ formatPreToolUseResponse(response: PreToolUseResponse): unknown;
39
+ formatPostToolUseResponse(response: PostToolUseResponse): unknown;
40
+ formatPreCompactResponse(response: PreCompactResponse): unknown;
41
+ formatSessionStartResponse(response: SessionStartResponse): unknown;
42
+ /**
43
+ * Extract session ID from wire input. Default priority (Claude Code):
44
+ * transcript_path UUID > session_id > env var > ppid fallback
45
+ *
46
+ * Override in subclasses for different priority (e.g., Qwen: session_id first).
47
+ */
48
+ protected abstract extractSessionId(input: ClaudeCodeWireInput): string;
49
+ }