claude-code-workflow 6.1.4 → 6.2.2

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 (437) hide show
  1. package/.claude/CLAUDE.md +10 -0
  2. package/.claude/agents/action-planning-agent.md +857 -778
  3. package/.claude/agents/cli-execution-agent.md +266 -269
  4. package/.claude/agents/cli-explore-agent.md +2 -2
  5. package/.claude/agents/cli-lite-planning-agent.md +142 -92
  6. package/.claude/agents/cli-planning-agent.md +4 -4
  7. package/.claude/agents/code-developer.md +7 -6
  8. package/.claude/agents/conceptual-planning-agent.md +2 -2
  9. package/.claude/agents/context-search-agent.md +31 -32
  10. package/.claude/agents/doc-generator.md +4 -4
  11. package/.claude/agents/memory-bridge.md +93 -93
  12. package/.claude/agents/test-context-search-agent.md +8 -7
  13. package/.claude/agents/test-fix-agent.md +7 -6
  14. package/.claude/commands/clean.md +516 -0
  15. package/.claude/commands/memory/compact.md +383 -0
  16. package/.claude/commands/memory/docs-full-cli.md +471 -471
  17. package/.claude/commands/memory/docs-related-cli.md +386 -386
  18. package/.claude/commands/memory/docs.md +615 -615
  19. package/.claude/commands/memory/load.md +5 -5
  20. package/.claude/commands/memory/tech-research-rules.md +310 -0
  21. package/.claude/commands/memory/update-full.md +332 -332
  22. package/.claude/commands/memory/workflow-skill-memory.md +4 -4
  23. package/.claude/commands/task/create.md +151 -151
  24. package/.claude/commands/version.md +254 -254
  25. package/.claude/commands/workflow/brainstorm/api-designer.md +587 -585
  26. package/.claude/commands/workflow/brainstorm/artifacts.md +1 -0
  27. package/.claude/commands/workflow/brainstorm/auto-parallel.md +443 -443
  28. package/.claude/commands/workflow/brainstorm/data-architect.md +220 -220
  29. package/.claude/commands/workflow/brainstorm/product-manager.md +200 -200
  30. package/.claude/commands/workflow/brainstorm/product-owner.md +200 -200
  31. package/.claude/commands/workflow/brainstorm/scrum-master.md +200 -200
  32. package/.claude/commands/workflow/brainstorm/subject-matter-expert.md +200 -200
  33. package/.claude/commands/workflow/brainstorm/system-architect.md +389 -387
  34. package/.claude/commands/workflow/brainstorm/ui-designer.md +221 -221
  35. package/.claude/commands/workflow/brainstorm/ux-expert.md +221 -221
  36. package/.claude/commands/workflow/debug.md +321 -0
  37. package/.claude/commands/workflow/execute.md +13 -0
  38. package/.claude/commands/workflow/init.md +165 -164
  39. package/.claude/commands/workflow/lite-execute.md +119 -13
  40. package/.claude/commands/workflow/lite-fix.md +623 -621
  41. package/.claude/commands/workflow/lite-plan.md +610 -592
  42. package/.claude/commands/workflow/plan.md +5 -5
  43. package/.claude/commands/workflow/review-module-cycle.md +2 -0
  44. package/.claude/commands/workflow/review-session-cycle.md +2 -0
  45. package/.claude/commands/workflow/review.md +297 -291
  46. package/.claude/commands/workflow/session/complete.md +153 -500
  47. package/.claude/commands/workflow/session/list.md +95 -95
  48. package/.claude/commands/workflow/session/resume.md +60 -60
  49. package/.claude/commands/workflow/session/start.md +199 -199
  50. package/.claude/commands/workflow/tdd-plan.md +3 -3
  51. package/.claude/commands/workflow/tdd-verify.md +23 -9
  52. package/.claude/commands/workflow/test-cycle-execute.md +2 -0
  53. package/.claude/commands/workflow/test-fix-gen.md +699 -699
  54. package/.claude/commands/workflow/tools/conflict-resolution.md +104 -18
  55. package/.claude/commands/workflow/tools/context-gather.md +436 -434
  56. package/.claude/commands/workflow/tools/task-generate-agent.md +490 -291
  57. package/.claude/commands/workflow/tools/task-generate-tdd.md +18 -10
  58. package/.claude/commands/workflow/tools/test-concept-enhanced.md +2 -1
  59. package/.claude/commands/workflow/tools/test-context-gather.md +1 -0
  60. package/.claude/commands/workflow/tools/test-task-generate.md +1 -0
  61. package/.claude/commands/workflow/ui-design/import-from-code.md +9 -6
  62. package/.claude/skills/command-guide/SKILL.md +5 -5
  63. package/.claude/skills/command-guide/index/all-commands.json +1 -1
  64. package/.claude/skills/command-guide/index/by-category.json +1 -1
  65. package/.claude/skills/command-guide/index/by-use-case.json +1 -1
  66. package/.claude/skills/command-guide/reference/agents/action-planning-agent.md +857 -778
  67. package/.claude/skills/command-guide/reference/agents/cli-execution-agent.md +266 -269
  68. package/.claude/skills/command-guide/reference/agents/cli-explore-agent.md +2 -2
  69. package/.claude/skills/command-guide/reference/agents/cli-lite-planning-agent.md +142 -92
  70. package/.claude/skills/command-guide/reference/agents/cli-planning-agent.md +4 -4
  71. package/.claude/skills/command-guide/reference/agents/code-developer.md +7 -6
  72. package/.claude/skills/command-guide/reference/agents/conceptual-planning-agent.md +2 -2
  73. package/.claude/skills/command-guide/reference/agents/context-search-agent.md +31 -32
  74. package/.claude/skills/command-guide/reference/agents/doc-generator.md +4 -4
  75. package/.claude/skills/command-guide/reference/agents/memory-bridge.md +93 -93
  76. package/.claude/skills/command-guide/reference/agents/test-context-search-agent.md +8 -7
  77. package/.claude/skills/command-guide/reference/agents/test-fix-agent.md +7 -6
  78. package/.claude/skills/command-guide/reference/commands/memory/docs-full-cli.md +471 -471
  79. package/.claude/skills/command-guide/reference/commands/memory/docs-related-cli.md +386 -386
  80. package/.claude/skills/command-guide/reference/commands/memory/docs.md +17 -16
  81. package/.claude/skills/command-guide/reference/commands/memory/load.md +5 -5
  82. package/.claude/skills/command-guide/reference/commands/memory/tech-research.md +194 -357
  83. package/.claude/skills/command-guide/reference/commands/memory/update-full.md +332 -332
  84. package/.claude/skills/command-guide/reference/commands/memory/workflow-skill-memory.md +4 -4
  85. package/.claude/skills/command-guide/reference/commands/task/create.md +151 -151
  86. package/.claude/skills/command-guide/reference/commands/version.md +254 -254
  87. package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/api-designer.md +585 -585
  88. package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/auto-parallel.md +443 -443
  89. package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/data-architect.md +220 -220
  90. package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/product-manager.md +200 -200
  91. package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/product-owner.md +200 -200
  92. package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/scrum-master.md +200 -200
  93. package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/subject-matter-expert.md +200 -200
  94. package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/system-architect.md +387 -387
  95. package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/ui-designer.md +221 -221
  96. package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/ux-expert.md +221 -221
  97. package/.claude/skills/command-guide/reference/commands/workflow/execute.md +25 -20
  98. package/.claude/skills/command-guide/reference/commands/workflow/init.md +164 -164
  99. package/.claude/skills/command-guide/reference/commands/workflow/lite-execute.md +748 -686
  100. package/.claude/skills/command-guide/reference/commands/workflow/lite-fix.md +664 -621
  101. package/.claude/skills/command-guide/reference/commands/workflow/lite-plan.md +645 -592
  102. package/.claude/skills/command-guide/reference/commands/workflow/plan.md +5 -5
  103. package/.claude/skills/command-guide/reference/commands/workflow/review.md +25 -18
  104. package/.claude/skills/command-guide/reference/commands/workflow/session/complete.md +547 -500
  105. package/.claude/skills/command-guide/reference/commands/workflow/session/list.md +45 -27
  106. package/.claude/skills/command-guide/reference/commands/workflow/session/resume.md +35 -19
  107. package/.claude/skills/command-guide/reference/commands/workflow/session/start.md +90 -33
  108. package/.claude/skills/command-guide/reference/commands/workflow/tdd-plan.md +3 -3
  109. package/.claude/skills/command-guide/reference/commands/workflow/tdd-verify.md +23 -9
  110. package/.claude/skills/command-guide/reference/commands/workflow/test-fix-gen.md +699 -699
  111. package/.claude/skills/command-guide/reference/commands/workflow/tools/conflict-resolution.md +103 -17
  112. package/.claude/skills/command-guide/reference/commands/workflow/tools/context-gather.md +434 -434
  113. package/.claude/skills/command-guide/reference/commands/workflow/tools/task-generate-agent.md +487 -291
  114. package/.claude/skills/command-guide/reference/commands/workflow/tools/task-generate-tdd.md +17 -10
  115. package/.claude/skills/command-guide/reference/commands/workflow/tools/test-concept-enhanced.md +1 -1
  116. package/.claude/skills/command-guide/reference/commands/workflow/ui-design/import-from-code.md +6 -6
  117. package/.claude/workflows/chinese-response.md +38 -0
  118. package/.claude/workflows/cli-templates/prompts/rules/rule-api.txt +122 -0
  119. package/.claude/workflows/cli-templates/prompts/rules/rule-components.txt +122 -0
  120. package/.claude/workflows/cli-templates/prompts/rules/rule-config.txt +89 -0
  121. package/.claude/workflows/cli-templates/prompts/rules/rule-core.txt +60 -0
  122. package/.claude/workflows/cli-templates/prompts/rules/rule-patterns.txt +70 -0
  123. package/.claude/workflows/cli-templates/prompts/rules/rule-testing.txt +81 -0
  124. package/.claude/workflows/cli-templates/prompts/rules/tech-rules-agent-prompt.txt +89 -0
  125. package/.claude/workflows/cli-templates/prompts/workflow/gemini-solution-design.txt +131 -131
  126. package/.claude/workflows/cli-templates/prompts/workflow/skill-conflict-patterns.txt +5 -9
  127. package/.claude/workflows/cli-templates/prompts/workflow/skill-lessons-learned.txt +5 -9
  128. package/.claude/workflows/cli-templates/protocols/analysis-protocol.md +112 -0
  129. package/.claude/workflows/cli-templates/protocols/write-protocol.md +201 -0
  130. package/.claude/workflows/cli-templates/schemas/conflict-resolution-schema.json +137 -0
  131. package/.claude/workflows/cli-templates/schemas/debug-log-json-schema.json +127 -0
  132. package/.claude/workflows/cli-templates/schemas/fix-plan-json-schema.json +25 -0
  133. package/.claude/workflows/cli-templates/schemas/plan-json-schema.json +25 -0
  134. package/.claude/workflows/cli-tools-usage.md +526 -0
  135. package/{CLAUDE.md → .claude/workflows/coding-philosophy.md} +24 -45
  136. package/.claude/workflows/context-tools.md +84 -0
  137. package/.claude/workflows/file-modification.md +64 -0
  138. package/.claude/workflows/tool-strategy.md +216 -79
  139. package/.claude/workflows/windows-platform.md +16 -0
  140. package/.claude/workflows/workflow-architecture.md +942 -942
  141. package/.codex/AGENTS.md +63 -330
  142. package/.codex/prompts/debug.md +318 -0
  143. package/.codex/prompts/execute.md +273 -0
  144. package/.codex/prompts/lite-execute.md +164 -0
  145. package/.codex/prompts/lite-plan.md +469 -0
  146. package/.codex/prompts.zip +0 -0
  147. package/.gemini/GEMINI.md +25 -164
  148. package/.qwen/QWEN.md +0 -139
  149. package/README.md +29 -9
  150. package/ccw/README.md +30 -6
  151. package/ccw/bin/ccw-mcp.js +7 -0
  152. package/ccw/bin/ccw.js +9 -9
  153. package/ccw/package.json +65 -47
  154. package/ccw/src/.workflow/.cli-history/history.db +0 -0
  155. package/ccw/src/.workflow/.cli-history/history.db-shm +0 -0
  156. package/ccw/src/.workflow/.cli-history/history.db-wal +0 -0
  157. package/ccw/src/cli.ts +244 -0
  158. package/ccw/src/commands/cli.ts +740 -0
  159. package/ccw/src/commands/core-memory.ts +770 -0
  160. package/ccw/src/commands/hook.ts +315 -0
  161. package/ccw/src/commands/install.ts +519 -0
  162. package/ccw/src/commands/{list.js → list.ts} +1 -1
  163. package/ccw/src/commands/memory.ts +1090 -0
  164. package/ccw/src/commands/{serve.js → serve.ts} +14 -5
  165. package/ccw/src/commands/session-path-resolver.ts +372 -0
  166. package/ccw/src/commands/session.ts +1141 -0
  167. package/ccw/src/commands/{stop.js → stop.ts} +16 -6
  168. package/ccw/src/commands/tool.ts +201 -0
  169. package/ccw/src/commands/{uninstall.js → uninstall.ts} +89 -40
  170. package/ccw/src/commands/{upgrade.js → upgrade.ts} +68 -23
  171. package/ccw/src/commands/{view.js → view.ts} +22 -8
  172. package/ccw/src/config/storage-paths.ts +670 -0
  173. package/ccw/src/core/cache-manager.ts +294 -0
  174. package/ccw/src/core/claude-freshness.ts +319 -0
  175. package/ccw/src/core/core-memory-store.ts +1528 -0
  176. package/ccw/src/core/{dashboard-generator-patch.js → dashboard-generator-patch.ts} +18 -0
  177. package/ccw/src/core/{dashboard-generator.js → dashboard-generator.ts} +69 -12
  178. package/ccw/src/core/data-aggregator.ts +584 -0
  179. package/ccw/src/core/history-importer.ts +625 -0
  180. package/ccw/src/core/{lite-scanner.js → lite-scanner-complete.ts} +162 -66
  181. package/ccw/src/core/lite-scanner.ts +469 -0
  182. package/ccw/src/core/{manifest.js → manifest.ts} +104 -34
  183. package/ccw/src/core/memory-embedder-bridge.ts +262 -0
  184. package/ccw/src/core/memory-store.ts +978 -0
  185. package/ccw/src/core/routes/ccw-routes.ts +96 -0
  186. package/ccw/src/core/routes/claude-routes.ts +1183 -0
  187. package/ccw/src/core/routes/cli-routes.ts +561 -0
  188. package/ccw/src/core/routes/codexlens-routes.ts +806 -0
  189. package/ccw/src/core/routes/core-memory-routes.ts +605 -0
  190. package/ccw/src/core/routes/files-routes.ts +428 -0
  191. package/ccw/src/core/routes/graph-routes.md +164 -0
  192. package/ccw/src/core/routes/graph-routes.ts +626 -0
  193. package/ccw/src/core/routes/help-routes.ts +308 -0
  194. package/ccw/src/core/routes/hooks-routes.ts +405 -0
  195. package/ccw/src/core/routes/mcp-routes.ts +1271 -0
  196. package/ccw/src/core/routes/mcp-routes.ts.backup +550 -0
  197. package/ccw/src/core/routes/mcp-templates-db.ts +268 -0
  198. package/ccw/src/core/routes/memory-routes.ts +1206 -0
  199. package/ccw/src/core/routes/rules-routes.ts +526 -0
  200. package/ccw/src/core/routes/session-routes.ts +467 -0
  201. package/ccw/src/core/routes/skills-routes.ts +599 -0
  202. package/ccw/src/core/routes/status-routes.ts +57 -0
  203. package/ccw/src/core/routes/system-routes.ts +427 -0
  204. package/ccw/src/core/server.ts +431 -0
  205. package/ccw/src/core/session-clustering-service.ts +1258 -0
  206. package/ccw/src/core/session-scanner.ts +283 -0
  207. package/ccw/src/core/websocket.ts +190 -0
  208. package/ccw/src/{index.js → index.ts} +1 -0
  209. package/ccw/src/mcp-server/index.ts +186 -0
  210. package/ccw/src/templates/assets/css/github-dark.min.css +10 -0
  211. package/ccw/src/templates/assets/css/github.min.css +10 -0
  212. package/ccw/src/templates/assets/js/cytoscape.min.js +32 -0
  213. package/ccw/src/templates/assets/js/d3.min.js +2 -0
  214. package/ccw/src/templates/assets/js/highlight.min.js +1244 -0
  215. package/ccw/src/templates/assets/js/lucide.min.js +12 -0
  216. package/ccw/src/templates/assets/js/marked.min.js +69 -0
  217. package/ccw/src/templates/assets/js/tailwind.js +83 -0
  218. package/ccw/src/templates/dashboard-css/01-base.css +11 -0
  219. package/ccw/src/templates/dashboard-css/02-session.css +22 -0
  220. package/ccw/src/templates/dashboard-css/04-lite-tasks.css +10 -0
  221. package/ccw/src/templates/dashboard-css/06-cards.css +10 -4
  222. package/ccw/src/templates/dashboard-css/07-managers.css +1178 -7
  223. package/ccw/src/templates/dashboard-css/09-explorer.css +23 -12
  224. package/ccw/src/templates/dashboard-css/10-cli-status.css +337 -0
  225. package/ccw/src/templates/dashboard-css/11-cli-history.css +271 -0
  226. package/ccw/src/templates/dashboard-css/12-cli-legacy.css +796 -0
  227. package/ccw/src/templates/dashboard-css/13-cli-ccw.css +199 -0
  228. package/ccw/src/templates/dashboard-css/14-cli-modals.css +258 -0
  229. package/ccw/src/templates/dashboard-css/15-cli-endpoints.css +305 -0
  230. package/ccw/src/templates/dashboard-css/16-cli-session.css +241 -0
  231. package/ccw/src/templates/dashboard-css/17-cli-conversation.css +283 -0
  232. package/ccw/src/templates/dashboard-css/18-cli-settings.css +160 -0
  233. package/ccw/src/templates/dashboard-css/19-cli-native-session.css +496 -0
  234. package/ccw/src/templates/dashboard-css/20-cli-taskqueue.css +188 -0
  235. package/ccw/src/templates/dashboard-css/21-cli-toolmgmt.css +310 -0
  236. package/ccw/src/templates/dashboard-css/22-cli-semantic.css +240 -0
  237. package/ccw/src/templates/dashboard-css/23-memory.css +2390 -0
  238. package/ccw/src/templates/dashboard-css/24-prompt-history.css +1089 -0
  239. package/ccw/src/templates/dashboard-css/25-skills-rules.css +326 -0
  240. package/ccw/src/templates/dashboard-css/26-claude-manager.css +908 -0
  241. package/ccw/src/templates/dashboard-css/27-graph-explorer.css +1678 -0
  242. package/ccw/src/templates/dashboard-css/28-mcp-manager.css +748 -0
  243. package/ccw/src/templates/dashboard-css/29-help.css +264 -0
  244. package/ccw/src/templates/dashboard-css/30-core-memory.css +1700 -0
  245. package/ccw/src/templates/dashboard-js/api.js +162 -142
  246. package/ccw/src/templates/dashboard-js/components/carousel.js +4 -4
  247. package/ccw/src/templates/dashboard-js/components/cli-history.js +876 -0
  248. package/ccw/src/templates/dashboard-js/components/cli-status.js +978 -0
  249. package/ccw/src/templates/dashboard-js/components/global-notifications.js +508 -219
  250. package/ccw/src/templates/dashboard-js/components/hook-manager.js +1277 -282
  251. package/ccw/src/templates/dashboard-js/components/index-manager.js +302 -0
  252. package/ccw/src/templates/dashboard-js/components/mcp-manager.js +718 -27
  253. package/ccw/src/templates/dashboard-js/components/modals.js +66 -0
  254. package/ccw/src/templates/dashboard-js/components/navigation.js +80 -12
  255. package/ccw/src/templates/dashboard-js/components/notifications.js +758 -194
  256. package/ccw/src/templates/dashboard-js/components/storage-manager.js +478 -0
  257. package/ccw/src/templates/dashboard-js/components/tabs-other.js +157 -6
  258. package/ccw/src/templates/dashboard-js/components/task-queue-sidebar.js +716 -0
  259. package/ccw/src/templates/dashboard-js/help-i18n.js +272 -0
  260. package/ccw/src/templates/dashboard-js/i18n.js +2807 -0
  261. package/ccw/src/templates/dashboard-js/main.js +15 -0
  262. package/ccw/src/templates/dashboard-js/state.js +243 -42
  263. package/ccw/src/templates/dashboard-js/utils.js +47 -1
  264. package/ccw/src/templates/dashboard-js/views/claude-manager.js +912 -0
  265. package/ccw/src/templates/dashboard-js/views/cli-manager.js +2272 -0
  266. package/ccw/src/templates/dashboard-js/views/codexlens-manager.js +964 -0
  267. package/ccw/src/templates/dashboard-js/views/core-memory-clusters.js +503 -0
  268. package/ccw/src/templates/dashboard-js/views/core-memory.js +782 -0
  269. package/ccw/src/templates/dashboard-js/views/explorer.js +888 -852
  270. package/ccw/src/templates/dashboard-js/views/graph-explorer.js +1157 -0
  271. package/ccw/src/templates/dashboard-js/views/help.js +856 -0
  272. package/ccw/src/templates/dashboard-js/views/history.js +337 -0
  273. package/ccw/src/templates/dashboard-js/views/home.js +61 -15
  274. package/ccw/src/templates/dashboard-js/views/hook-manager.js +311 -43
  275. package/ccw/src/templates/dashboard-js/views/lite-tasks.js +204 -28
  276. package/ccw/src/templates/dashboard-js/views/mcp-manager.js +2187 -411
  277. package/ccw/src/templates/dashboard-js/views/mcp-manager.js.backup +1729 -0
  278. package/ccw/src/templates/dashboard-js/views/mcp-manager.js.new +928 -0
  279. package/ccw/src/templates/dashboard-js/views/memory.js +1221 -0
  280. package/ccw/src/templates/dashboard-js/views/prompt-history.js +713 -0
  281. package/ccw/src/templates/dashboard-js/views/rules-manager.js +828 -0
  282. package/ccw/src/templates/dashboard-js/views/session-detail.js +54 -53
  283. package/ccw/src/templates/dashboard-js/views/skills-manager.js +819 -0
  284. package/ccw/src/templates/dashboard.html +185 -85
  285. package/ccw/src/templates/hooks-config-example.json +60 -0
  286. package/ccw/src/tools/classify-folders.ts +245 -0
  287. package/ccw/src/tools/cli-config-manager.ts +268 -0
  288. package/ccw/src/tools/cli-executor.ts +2014 -0
  289. package/ccw/src/tools/cli-history-store.ts +1195 -0
  290. package/ccw/src/tools/codex-lens.ts +1141 -0
  291. package/ccw/src/tools/{convert-tokens-to-css.js → convert-tokens-to-css.ts} +73 -23
  292. package/ccw/src/tools/core-memory.ts +444 -0
  293. package/ccw/src/tools/detect-changed-modules.ts +325 -0
  294. package/ccw/src/tools/{discover-design-files.js → discover-design-files.ts} +74 -24
  295. package/ccw/src/tools/edit-file.ts +568 -0
  296. package/ccw/src/tools/{generate-module-docs.js → generate-module-docs.ts} +207 -185
  297. package/ccw/src/tools/{get-modules-by-depth.js → get-modules-by-depth.ts} +120 -79
  298. package/ccw/src/tools/index.ts +370 -0
  299. package/ccw/src/tools/native-session-discovery.ts +795 -0
  300. package/ccw/src/tools/notifier.ts +129 -0
  301. package/ccw/src/tools/read-file.ts +410 -0
  302. package/ccw/src/tools/resume-strategy.ts +345 -0
  303. package/ccw/src/tools/session-content-parser.ts +619 -0
  304. package/ccw/src/tools/session-manager.ts +1026 -0
  305. package/ccw/src/tools/smart-context.ts +228 -0
  306. package/ccw/src/tools/smart-search.ts +2065 -0
  307. package/ccw/src/tools/smart-search.ts.backup +1233 -0
  308. package/ccw/src/tools/storage-manager.ts +455 -0
  309. package/ccw/src/tools/write-file.ts +222 -0
  310. package/ccw/src/types/config.ts +11 -0
  311. package/ccw/src/types/index.ts +3 -0
  312. package/ccw/src/types/session.ts +25 -0
  313. package/ccw/src/types/tool.ts +41 -0
  314. package/ccw/src/utils/{browser-launcher.js → browser-launcher.ts} +10 -8
  315. package/ccw/src/utils/file-utils.ts +48 -0
  316. package/ccw/src/utils/{path-resolver.js → path-resolver.ts} +114 -78
  317. package/ccw/src/utils/path-validator.ts +153 -0
  318. package/ccw/src/utils/{ui.js → ui.ts} +32 -25
  319. package/codex-lens/pyproject.toml +48 -0
  320. package/codex-lens/src/codexlens/.workflow/.cli-history/history.db +0 -0
  321. package/codex-lens/src/codexlens/__init__.py +28 -0
  322. package/codex-lens/src/codexlens/__main__.py +14 -0
  323. package/codex-lens/src/codexlens/__pycache__/__init__.cpython-313.pyc +0 -0
  324. package/codex-lens/src/codexlens/__pycache__/__main__.cpython-313.pyc +0 -0
  325. package/codex-lens/src/codexlens/__pycache__/config.cpython-313.pyc +0 -0
  326. package/codex-lens/src/codexlens/__pycache__/entities.cpython-313.pyc +0 -0
  327. package/codex-lens/src/codexlens/__pycache__/errors.cpython-313.pyc +0 -0
  328. package/codex-lens/src/codexlens/cli/__init__.py +27 -0
  329. package/codex-lens/src/codexlens/cli/__pycache__/__init__.cpython-313.pyc +0 -0
  330. package/codex-lens/src/codexlens/cli/__pycache__/commands.cpython-313.pyc +0 -0
  331. package/codex-lens/src/codexlens/cli/__pycache__/embedding_manager.cpython-313.pyc +0 -0
  332. package/codex-lens/src/codexlens/cli/__pycache__/model_manager.cpython-313.pyc +0 -0
  333. package/codex-lens/src/codexlens/cli/__pycache__/output.cpython-313.pyc +0 -0
  334. package/codex-lens/src/codexlens/cli/commands.py +1931 -0
  335. package/codex-lens/src/codexlens/cli/embedding_manager.py +620 -0
  336. package/codex-lens/src/codexlens/cli/model_manager.py +289 -0
  337. package/codex-lens/src/codexlens/cli/output.py +124 -0
  338. package/codex-lens/src/codexlens/config.py +201 -0
  339. package/codex-lens/src/codexlens/entities.py +121 -0
  340. package/codex-lens/src/codexlens/errors.py +55 -0
  341. package/codex-lens/src/codexlens/indexing/README.md +77 -0
  342. package/codex-lens/src/codexlens/indexing/__init__.py +4 -0
  343. package/codex-lens/src/codexlens/indexing/__pycache__/__init__.cpython-313.pyc +0 -0
  344. package/codex-lens/src/codexlens/indexing/__pycache__/symbol_extractor.cpython-313.pyc +0 -0
  345. package/codex-lens/src/codexlens/indexing/symbol_extractor.py +243 -0
  346. package/codex-lens/src/codexlens/parsers/__init__.py +8 -0
  347. package/codex-lens/src/codexlens/parsers/__pycache__/__init__.cpython-313.pyc +0 -0
  348. package/codex-lens/src/codexlens/parsers/__pycache__/encoding.cpython-313.pyc +0 -0
  349. package/codex-lens/src/codexlens/parsers/__pycache__/factory.cpython-313.pyc +0 -0
  350. package/codex-lens/src/codexlens/parsers/__pycache__/tokenizer.cpython-313.pyc +0 -0
  351. package/codex-lens/src/codexlens/parsers/__pycache__/treesitter_parser.cpython-313.pyc +0 -0
  352. package/codex-lens/src/codexlens/parsers/encoding.py +202 -0
  353. package/codex-lens/src/codexlens/parsers/factory.py +256 -0
  354. package/codex-lens/src/codexlens/parsers/tokenizer.py +98 -0
  355. package/codex-lens/src/codexlens/parsers/treesitter_parser.py +335 -0
  356. package/codex-lens/src/codexlens/search/__init__.py +15 -0
  357. package/codex-lens/src/codexlens/search/__pycache__/__init__.cpython-313.pyc +0 -0
  358. package/codex-lens/src/codexlens/search/__pycache__/chain_search.cpython-313.pyc +0 -0
  359. package/codex-lens/src/codexlens/search/__pycache__/enrichment.cpython-313.pyc +0 -0
  360. package/codex-lens/src/codexlens/search/__pycache__/hybrid_search.cpython-313.pyc +0 -0
  361. package/codex-lens/src/codexlens/search/__pycache__/query_parser.cpython-313.pyc +0 -0
  362. package/codex-lens/src/codexlens/search/__pycache__/ranking.cpython-313.pyc +0 -0
  363. package/codex-lens/src/codexlens/search/chain_search.py +647 -0
  364. package/codex-lens/src/codexlens/search/enrichment.py +150 -0
  365. package/codex-lens/src/codexlens/search/hybrid_search.py +313 -0
  366. package/codex-lens/src/codexlens/search/query_parser.py +242 -0
  367. package/codex-lens/src/codexlens/search/ranking.py +274 -0
  368. package/codex-lens/src/codexlens/semantic/__init__.py +39 -0
  369. package/codex-lens/src/codexlens/semantic/__pycache__/__init__.cpython-313.pyc +0 -0
  370. package/codex-lens/src/codexlens/semantic/__pycache__/ann_index.cpython-313.pyc +0 -0
  371. package/codex-lens/src/codexlens/semantic/__pycache__/chunker.cpython-313.pyc +0 -0
  372. package/codex-lens/src/codexlens/semantic/__pycache__/code_extractor.cpython-313.pyc +0 -0
  373. package/codex-lens/src/codexlens/semantic/__pycache__/embedder.cpython-313.pyc +0 -0
  374. package/codex-lens/src/codexlens/semantic/__pycache__/graph_analyzer.cpython-313.pyc +0 -0
  375. package/codex-lens/src/codexlens/semantic/__pycache__/llm_enhancer.cpython-313.pyc +0 -0
  376. package/codex-lens/src/codexlens/semantic/__pycache__/vector_store.cpython-313.pyc +0 -0
  377. package/codex-lens/src/codexlens/semantic/ann_index.py +414 -0
  378. package/codex-lens/src/codexlens/semantic/chunker.py +448 -0
  379. package/codex-lens/src/codexlens/semantic/code_extractor.py +274 -0
  380. package/codex-lens/src/codexlens/semantic/embedder.py +185 -0
  381. package/codex-lens/src/codexlens/semantic/vector_store.py +955 -0
  382. package/codex-lens/src/codexlens/storage/__init__.py +29 -0
  383. package/codex-lens/src/codexlens/storage/__pycache__/__init__.cpython-313.pyc +0 -0
  384. package/codex-lens/src/codexlens/storage/__pycache__/dir_index.cpython-313.pyc +0 -0
  385. package/codex-lens/src/codexlens/storage/__pycache__/file_cache.cpython-313.pyc +0 -0
  386. package/codex-lens/src/codexlens/storage/__pycache__/index_tree.cpython-313.pyc +0 -0
  387. package/codex-lens/src/codexlens/storage/__pycache__/migration_manager.cpython-313.pyc +0 -0
  388. package/codex-lens/src/codexlens/storage/__pycache__/path_mapper.cpython-313.pyc +0 -0
  389. package/codex-lens/src/codexlens/storage/__pycache__/registry.cpython-313.pyc +0 -0
  390. package/codex-lens/src/codexlens/storage/__pycache__/sqlite_store.cpython-313.pyc +0 -0
  391. package/codex-lens/src/codexlens/storage/__pycache__/sqlite_utils.cpython-313.pyc +0 -0
  392. package/codex-lens/src/codexlens/storage/dir_index.py +1850 -0
  393. package/codex-lens/src/codexlens/storage/file_cache.py +32 -0
  394. package/codex-lens/src/codexlens/storage/index_tree.py +776 -0
  395. package/codex-lens/src/codexlens/storage/migration_manager.py +154 -0
  396. package/codex-lens/src/codexlens/storage/migrations/__init__.py +1 -0
  397. package/codex-lens/src/codexlens/storage/migrations/__pycache__/__init__.cpython-313.pyc +0 -0
  398. package/codex-lens/src/codexlens/storage/migrations/__pycache__/migration_001_normalize_keywords.cpython-313.pyc +0 -0
  399. package/codex-lens/src/codexlens/storage/migrations/__pycache__/migration_002_add_token_metadata.cpython-313.pyc +0 -0
  400. package/codex-lens/src/codexlens/storage/migrations/__pycache__/migration_003_code_relationships.cpython-313.pyc +0 -0
  401. package/codex-lens/src/codexlens/storage/migrations/__pycache__/migration_004_dual_fts.cpython-313.pyc +0 -0
  402. package/codex-lens/src/codexlens/storage/migrations/__pycache__/migration_005_cleanup_unused_fields.cpython-313.pyc +0 -0
  403. package/codex-lens/src/codexlens/storage/migrations/migration_001_normalize_keywords.py +123 -0
  404. package/codex-lens/src/codexlens/storage/migrations/migration_002_add_token_metadata.py +48 -0
  405. package/codex-lens/src/codexlens/storage/migrations/migration_004_dual_fts.py +232 -0
  406. package/codex-lens/src/codexlens/storage/migrations/migration_005_cleanup_unused_fields.py +196 -0
  407. package/codex-lens/src/codexlens/storage/path_mapper.py +274 -0
  408. package/codex-lens/src/codexlens/storage/registry.py +670 -0
  409. package/codex-lens/src/codexlens/storage/sqlite_store.py +576 -0
  410. package/codex-lens/src/codexlens/storage/sqlite_utils.py +64 -0
  411. package/package.json +4 -1
  412. package/.claude/commands/memory/tech-research.md +0 -477
  413. package/.claude/scripts/classify-folders.sh +0 -39
  414. package/.claude/scripts/convert_tokens_to_css.sh +0 -229
  415. package/.claude/scripts/detect_changed_modules.sh +0 -161
  416. package/.claude/scripts/discover-design-files.sh +0 -87
  417. package/.claude/scripts/extract-animations.js +0 -243
  418. package/.claude/scripts/extract-computed-styles.js +0 -118
  419. package/.claude/scripts/extract-layout-structure.js +0 -411
  420. package/.claude/scripts/generate_module_docs.sh +0 -717
  421. package/.claude/scripts/get_modules_by_depth.sh +0 -170
  422. package/.claude/scripts/ui-generate-preview.sh +0 -395
  423. package/.claude/scripts/ui-instantiate-prototypes.sh +0 -815
  424. package/.claude/scripts/update_module_claude.sh +0 -337
  425. package/.claude/workflows/context-search-strategy.md +0 -77
  426. package/.claude/workflows/intelligent-tools-strategy.md +0 -662
  427. package/ccw/src/cli.js +0 -119
  428. package/ccw/src/commands/install.js +0 -324
  429. package/ccw/src/commands/tool.js +0 -138
  430. package/ccw/src/core/data-aggregator.js +0 -409
  431. package/ccw/src/core/server.js +0 -2063
  432. package/ccw/src/core/session-scanner.js +0 -235
  433. package/ccw/src/tools/classify-folders.js +0 -204
  434. package/ccw/src/tools/detect-changed-modules.js +0 -288
  435. package/ccw/src/tools/edit-file.js +0 -266
  436. package/ccw/src/tools/index.js +0 -176
  437. package/ccw/src/utils/file-utils.js +0 -48
@@ -0,0 +1,1271 @@
1
+ // @ts-nocheck
2
+ /**
3
+ * MCP Routes Module
4
+ * Handles all MCP-related API endpoints
5
+ */
6
+ import type { IncomingMessage, ServerResponse } from 'http';
7
+ import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync, statSync } from 'fs';
8
+ import { join, dirname } from 'path';
9
+ import { homedir } from 'os';
10
+ import * as McpTemplatesDb from './mcp-templates-db.js';
11
+
12
+ // Claude config file path
13
+ const CLAUDE_CONFIG_PATH = join(homedir(), '.claude.json');
14
+
15
+ // Codex config file path (TOML format)
16
+ const CODEX_CONFIG_PATH = join(homedir(), '.codex', 'config.toml');
17
+
18
+ // Workspace root path for scanning .mcp.json files
19
+ let WORKSPACE_ROOT = process.cwd();
20
+
21
+ // ========================================
22
+ // TOML Parser for Codex Config
23
+ // ========================================
24
+
25
+ /**
26
+ * Simple TOML parser for Codex config.toml
27
+ * Supports basic types: strings, numbers, booleans, arrays, inline tables
28
+ */
29
+ function parseToml(content: string): Record<string, any> {
30
+ const result: Record<string, any> = {};
31
+ let currentSection: string[] = [];
32
+ const lines = content.split('\n');
33
+
34
+ for (let i = 0; i < lines.length; i++) {
35
+ let line = lines[i].trim();
36
+
37
+ // Skip empty lines and comments
38
+ if (!line || line.startsWith('#')) continue;
39
+
40
+ // Handle section headers [section] or [section.subsection]
41
+ const sectionMatch = line.match(/^\[([^\]]+)\]$/);
42
+ if (sectionMatch) {
43
+ currentSection = sectionMatch[1].split('.');
44
+ // Ensure nested sections exist
45
+ let obj = result;
46
+ for (const part of currentSection) {
47
+ if (!obj[part]) obj[part] = {};
48
+ obj = obj[part];
49
+ }
50
+ continue;
51
+ }
52
+
53
+ // Handle key = value pairs
54
+ const keyValueMatch = line.match(/^([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*(.+)$/);
55
+ if (keyValueMatch) {
56
+ const key = keyValueMatch[1];
57
+ const rawValue = keyValueMatch[2].trim();
58
+ const value = parseTomlValue(rawValue);
59
+
60
+ // Navigate to current section
61
+ let obj = result;
62
+ for (const part of currentSection) {
63
+ if (!obj[part]) obj[part] = {};
64
+ obj = obj[part];
65
+ }
66
+ obj[key] = value;
67
+ }
68
+ }
69
+
70
+ return result;
71
+ }
72
+
73
+ /**
74
+ * Parse a TOML value
75
+ */
76
+ function parseTomlValue(value: string): any {
77
+ // String (double-quoted)
78
+ if (value.startsWith('"') && value.endsWith('"')) {
79
+ return value.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\');
80
+ }
81
+
82
+ // String (single-quoted - literal)
83
+ if (value.startsWith("'") && value.endsWith("'")) {
84
+ return value.slice(1, -1);
85
+ }
86
+
87
+ // Boolean
88
+ if (value === 'true') return true;
89
+ if (value === 'false') return false;
90
+
91
+ // Number
92
+ if (/^-?\d+(\.\d+)?$/.test(value)) {
93
+ return value.includes('.') ? parseFloat(value) : parseInt(value, 10);
94
+ }
95
+
96
+ // Array
97
+ if (value.startsWith('[') && value.endsWith(']')) {
98
+ const inner = value.slice(1, -1).trim();
99
+ if (!inner) return [];
100
+ // Simple array parsing (handles basic cases)
101
+ const items: any[] = [];
102
+ let depth = 0;
103
+ let current = '';
104
+ let inString = false;
105
+ let stringChar = '';
106
+
107
+ for (const char of inner) {
108
+ if (!inString && (char === '"' || char === "'")) {
109
+ inString = true;
110
+ stringChar = char;
111
+ current += char;
112
+ } else if (inString && char === stringChar) {
113
+ inString = false;
114
+ current += char;
115
+ } else if (!inString && (char === '[' || char === '{')) {
116
+ depth++;
117
+ current += char;
118
+ } else if (!inString && (char === ']' || char === '}')) {
119
+ depth--;
120
+ current += char;
121
+ } else if (!inString && char === ',' && depth === 0) {
122
+ items.push(parseTomlValue(current.trim()));
123
+ current = '';
124
+ } else {
125
+ current += char;
126
+ }
127
+ }
128
+ if (current.trim()) {
129
+ items.push(parseTomlValue(current.trim()));
130
+ }
131
+ return items;
132
+ }
133
+
134
+ // Inline table { key = value, ... }
135
+ if (value.startsWith('{') && value.endsWith('}')) {
136
+ const inner = value.slice(1, -1).trim();
137
+ if (!inner) return {};
138
+ const table: Record<string, any> = {};
139
+ // Simple inline table parsing
140
+ const pairs = inner.split(',');
141
+ for (const pair of pairs) {
142
+ const match = pair.trim().match(/^([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*(.+)$/);
143
+ if (match) {
144
+ table[match[1]] = parseTomlValue(match[2].trim());
145
+ }
146
+ }
147
+ return table;
148
+ }
149
+
150
+ // Return as string if nothing else matches
151
+ return value;
152
+ }
153
+
154
+ /**
155
+ * Serialize object to TOML format for Codex config
156
+ *
157
+ * Handles mixed objects containing both simple values and sub-objects.
158
+ * For example: { command: "cmd", args: [...], env: { KEY: "value" } }
159
+ * becomes:
160
+ * [section]
161
+ * command = "cmd"
162
+ * args = [...]
163
+ * [section.env]
164
+ * KEY = "value"
165
+ */
166
+ function serializeToml(obj: Record<string, any>, prefix: string = ''): string {
167
+ let result = '';
168
+
169
+ for (const [key, value] of Object.entries(obj)) {
170
+ if (value === null || value === undefined) continue;
171
+
172
+ if (typeof value === 'object' && !Array.isArray(value)) {
173
+ // Handle nested sections (like mcp_servers.server_name)
174
+ const sectionKey = prefix ? `${prefix}.${key}` : key;
175
+
176
+ // Separate simple values from sub-objects
177
+ const simpleEntries: [string, any][] = [];
178
+ const objectEntries: [string, any][] = [];
179
+
180
+ for (const [subKey, subValue] of Object.entries(value)) {
181
+ if (subValue === null || subValue === undefined) continue;
182
+ if (typeof subValue === 'object' && !Array.isArray(subValue)) {
183
+ objectEntries.push([subKey, subValue]);
184
+ } else {
185
+ simpleEntries.push([subKey, subValue]);
186
+ }
187
+ }
188
+
189
+ // Write section header if there are simple values
190
+ if (simpleEntries.length > 0) {
191
+ result += `\n[${sectionKey}]\n`;
192
+ for (const [subKey, subValue] of simpleEntries) {
193
+ result += `${subKey} = ${serializeTomlValue(subValue)}\n`;
194
+ }
195
+ }
196
+
197
+ // Recursively handle sub-objects
198
+ if (objectEntries.length > 0) {
199
+ for (const [subKey, subValue] of objectEntries) {
200
+ const subSectionKey = `${sectionKey}.${subKey}`;
201
+
202
+ // Check if sub-object has nested objects
203
+ const hasNestedObjects = Object.values(subValue).some(
204
+ v => typeof v === 'object' && v !== null && !Array.isArray(v)
205
+ );
206
+
207
+ if (hasNestedObjects) {
208
+ // Recursively process nested objects
209
+ result += serializeToml({ [subKey]: subValue }, sectionKey);
210
+ } else {
211
+ // Write sub-section with simple values
212
+ result += `\n[${subSectionKey}]\n`;
213
+ for (const [nestedKey, nestedValue] of Object.entries(subValue)) {
214
+ if (nestedValue !== null && nestedValue !== undefined) {
215
+ result += `${nestedKey} = ${serializeTomlValue(nestedValue)}\n`;
216
+ }
217
+ }
218
+ }
219
+ }
220
+ }
221
+
222
+ // If no simple values but has object entries, still need to process
223
+ if (simpleEntries.length === 0 && objectEntries.length === 0) {
224
+ // Empty section - write header only
225
+ result += `\n[${sectionKey}]\n`;
226
+ }
227
+ } else if (!prefix) {
228
+ // Top-level simple values
229
+ result += `${key} = ${serializeTomlValue(value)}\n`;
230
+ }
231
+ }
232
+
233
+ return result;
234
+ }
235
+
236
+ /**
237
+ * Serialize a value to TOML format
238
+ */
239
+ function serializeTomlValue(value: any): string {
240
+ if (typeof value === 'string') {
241
+ return `"${value.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;
242
+ }
243
+ if (typeof value === 'boolean') {
244
+ return value ? 'true' : 'false';
245
+ }
246
+ if (typeof value === 'number') {
247
+ return String(value);
248
+ }
249
+ if (Array.isArray(value)) {
250
+ return `[${value.map(v => serializeTomlValue(v)).join(', ')}]`;
251
+ }
252
+ if (typeof value === 'object' && value !== null) {
253
+ const pairs = Object.entries(value)
254
+ .filter(([_, v]) => v !== null && v !== undefined)
255
+ .map(([k, v]) => `${k} = ${serializeTomlValue(v)}`);
256
+ return `{ ${pairs.join(', ')} }`;
257
+ }
258
+ return String(value);
259
+ }
260
+
261
+ // ========================================
262
+ // Codex MCP Functions
263
+ // ========================================
264
+
265
+ /**
266
+ * Read Codex config.toml and extract MCP servers
267
+ */
268
+ function getCodexMcpConfig(): { servers: Record<string, any>; configPath: string; exists: boolean } {
269
+ try {
270
+ if (!existsSync(CODEX_CONFIG_PATH)) {
271
+ return { servers: {}, configPath: CODEX_CONFIG_PATH, exists: false };
272
+ }
273
+
274
+ const content = readFileSync(CODEX_CONFIG_PATH, 'utf8');
275
+ const config = parseToml(content);
276
+
277
+ // MCP servers are under [mcp_servers] section
278
+ const mcpServers = config.mcp_servers || {};
279
+
280
+ return {
281
+ servers: mcpServers,
282
+ configPath: CODEX_CONFIG_PATH,
283
+ exists: true
284
+ };
285
+ } catch (error: unknown) {
286
+ console.error('Error reading Codex config:', error);
287
+ return { servers: {}, configPath: CODEX_CONFIG_PATH, exists: false };
288
+ }
289
+ }
290
+
291
+ /**
292
+ * Add or update MCP server in Codex config.toml
293
+ */
294
+ function addCodexMcpServer(serverName: string, serverConfig: Record<string, any>): { success?: boolean; error?: string } {
295
+ try {
296
+ const codexDir = join(homedir(), '.codex');
297
+
298
+ // Ensure .codex directory exists
299
+ if (!existsSync(codexDir)) {
300
+ mkdirSync(codexDir, { recursive: true });
301
+ }
302
+
303
+ let config: Record<string, any> = {};
304
+
305
+ // Read existing config if it exists
306
+ if (existsSync(CODEX_CONFIG_PATH)) {
307
+ const content = readFileSync(CODEX_CONFIG_PATH, 'utf8');
308
+ config = parseToml(content);
309
+ }
310
+
311
+ // Ensure mcp_servers section exists
312
+ if (!config.mcp_servers) {
313
+ config.mcp_servers = {};
314
+ }
315
+
316
+ // Convert serverConfig from Claude format to Codex format
317
+ const codexServerConfig: Record<string, any> = {};
318
+
319
+ // Handle STDIO servers (command-based)
320
+ if (serverConfig.command) {
321
+ codexServerConfig.command = serverConfig.command;
322
+ if (serverConfig.args && serverConfig.args.length > 0) {
323
+ codexServerConfig.args = serverConfig.args;
324
+ }
325
+ if (serverConfig.env && Object.keys(serverConfig.env).length > 0) {
326
+ codexServerConfig.env = serverConfig.env;
327
+ }
328
+ if (serverConfig.cwd) {
329
+ codexServerConfig.cwd = serverConfig.cwd;
330
+ }
331
+ }
332
+
333
+ // Handle HTTP servers (url-based)
334
+ if (serverConfig.url) {
335
+ codexServerConfig.url = serverConfig.url;
336
+ if (serverConfig.bearer_token_env_var) {
337
+ codexServerConfig.bearer_token_env_var = serverConfig.bearer_token_env_var;
338
+ }
339
+ if (serverConfig.http_headers) {
340
+ codexServerConfig.http_headers = serverConfig.http_headers;
341
+ }
342
+ }
343
+
344
+ // Copy optional fields
345
+ if (serverConfig.startup_timeout_sec !== undefined) {
346
+ codexServerConfig.startup_timeout_sec = serverConfig.startup_timeout_sec;
347
+ }
348
+ if (serverConfig.tool_timeout_sec !== undefined) {
349
+ codexServerConfig.tool_timeout_sec = serverConfig.tool_timeout_sec;
350
+ }
351
+ if (serverConfig.enabled !== undefined) {
352
+ codexServerConfig.enabled = serverConfig.enabled;
353
+ }
354
+ if (serverConfig.enabled_tools) {
355
+ codexServerConfig.enabled_tools = serverConfig.enabled_tools;
356
+ }
357
+ if (serverConfig.disabled_tools) {
358
+ codexServerConfig.disabled_tools = serverConfig.disabled_tools;
359
+ }
360
+
361
+ // Add the server
362
+ config.mcp_servers[serverName] = codexServerConfig;
363
+
364
+ // Serialize and write back
365
+ const tomlContent = serializeToml(config);
366
+ writeFileSync(CODEX_CONFIG_PATH, tomlContent, 'utf8');
367
+
368
+ return { success: true };
369
+ } catch (error: unknown) {
370
+ console.error('Error adding Codex MCP server:', error);
371
+ return { error: (error as Error).message };
372
+ }
373
+ }
374
+
375
+ /**
376
+ * Remove MCP server from Codex config.toml
377
+ */
378
+ function removeCodexMcpServer(serverName: string): { success?: boolean; error?: string } {
379
+ try {
380
+ if (!existsSync(CODEX_CONFIG_PATH)) {
381
+ return { error: 'Codex config.toml not found' };
382
+ }
383
+
384
+ const content = readFileSync(CODEX_CONFIG_PATH, 'utf8');
385
+ const config = parseToml(content);
386
+
387
+ if (!config.mcp_servers || !config.mcp_servers[serverName]) {
388
+ return { error: `Server not found: ${serverName}` };
389
+ }
390
+
391
+ // Remove the server
392
+ delete config.mcp_servers[serverName];
393
+
394
+ // Serialize and write back
395
+ const tomlContent = serializeToml(config);
396
+ writeFileSync(CODEX_CONFIG_PATH, tomlContent, 'utf8');
397
+
398
+ return { success: true };
399
+ } catch (error: unknown) {
400
+ console.error('Error removing Codex MCP server:', error);
401
+ return { error: (error as Error).message };
402
+ }
403
+ }
404
+
405
+ /**
406
+ * Toggle Codex MCP server enabled state
407
+ */
408
+ function toggleCodexMcpServer(serverName: string, enabled: boolean): { success?: boolean; error?: string } {
409
+ try {
410
+ if (!existsSync(CODEX_CONFIG_PATH)) {
411
+ return { error: 'Codex config.toml not found' };
412
+ }
413
+
414
+ const content = readFileSync(CODEX_CONFIG_PATH, 'utf8');
415
+ const config = parseToml(content);
416
+
417
+ if (!config.mcp_servers || !config.mcp_servers[serverName]) {
418
+ return { error: `Server not found: ${serverName}` };
419
+ }
420
+
421
+ // Set enabled state
422
+ config.mcp_servers[serverName].enabled = enabled;
423
+
424
+ // Serialize and write back
425
+ const tomlContent = serializeToml(config);
426
+ writeFileSync(CODEX_CONFIG_PATH, tomlContent, 'utf8');
427
+
428
+ return { success: true };
429
+ } catch (error: unknown) {
430
+ console.error('Error toggling Codex MCP server:', error);
431
+ return { error: (error as Error).message };
432
+ }
433
+ }
434
+
435
+ export interface RouteContext {
436
+ pathname: string;
437
+ url: URL;
438
+ req: IncomingMessage;
439
+ res: ServerResponse;
440
+ initialPath: string;
441
+ handlePostRequest: (req: IncomingMessage, res: ServerResponse, handler: (body: unknown) => Promise<any>) => void;
442
+ broadcastToClients: (data: unknown) => void;
443
+ }
444
+
445
+ // ========================================
446
+ // Helper Functions
447
+ // ========================================
448
+
449
+ /**
450
+ * Get enterprise managed MCP path (platform-specific)
451
+ */
452
+ function getEnterpriseMcpPath(): string {
453
+ const platform = process.platform;
454
+ if (platform === 'darwin') {
455
+ return '/Library/Application Support/ClaudeCode/managed-mcp.json';
456
+ } else if (platform === 'win32') {
457
+ return 'C:\\Program Files\\ClaudeCode\\managed-mcp.json';
458
+ } else {
459
+ // Linux and WSL
460
+ return '/etc/claude-code/managed-mcp.json';
461
+ }
462
+ }
463
+
464
+ /**
465
+ * Safely read and parse JSON file
466
+ */
467
+ function safeReadJson(filePath) {
468
+ try {
469
+ if (!existsSync(filePath)) return null;
470
+ const content = readFileSync(filePath, 'utf8');
471
+ return JSON.parse(content);
472
+ } catch {
473
+ return null;
474
+ }
475
+ }
476
+
477
+ /**
478
+ * Get MCP servers from a JSON file (expects mcpServers key at top level)
479
+ * @param {string} filePath
480
+ * @returns {Object} mcpServers object or empty object
481
+ */
482
+ function getMcpServersFromFile(filePath) {
483
+ const config = safeReadJson(filePath);
484
+ if (!config) return {};
485
+ return config.mcpServers || {};
486
+ }
487
+
488
+ /**
489
+ * Add or update MCP server in project's .mcp.json file
490
+ * @param {string} projectPath - Project directory path
491
+ * @param {string} serverName - MCP server name
492
+ * @param {Object} serverConfig - MCP server configuration
493
+ * @returns {Object} Result with success/error
494
+ */
495
+ function addMcpServerToMcpJson(projectPath, serverName, serverConfig) {
496
+ try {
497
+ const normalizedPath = normalizePathForFileSystem(projectPath);
498
+ const mcpJsonPath = join(normalizedPath, '.mcp.json');
499
+
500
+ // Read existing .mcp.json or create new structure
501
+ let mcpJson = safeReadJson(mcpJsonPath) || { mcpServers: {} };
502
+
503
+ // Ensure mcpServers exists
504
+ if (!mcpJson.mcpServers) {
505
+ mcpJson.mcpServers = {};
506
+ }
507
+
508
+ // Add or update the server
509
+ mcpJson.mcpServers[serverName] = serverConfig;
510
+
511
+ // Write back to .mcp.json
512
+ writeFileSync(mcpJsonPath, JSON.stringify(mcpJson, null, 2), 'utf8');
513
+
514
+ return {
515
+ success: true,
516
+ serverName,
517
+ serverConfig,
518
+ scope: 'project-mcp-json',
519
+ path: mcpJsonPath
520
+ };
521
+ } catch (error: unknown) {
522
+ console.error('Error adding MCP server to .mcp.json:', error);
523
+ return { error: (error as Error).message };
524
+ }
525
+ }
526
+
527
+ /**
528
+ * Remove MCP server from project's .mcp.json file
529
+ * @param {string} projectPath - Project directory path
530
+ * @param {string} serverName - MCP server name
531
+ * @returns {Object} Result with success/error
532
+ */
533
+ function removeMcpServerFromMcpJson(projectPath, serverName) {
534
+ try {
535
+ const normalizedPath = normalizePathForFileSystem(projectPath);
536
+ const mcpJsonPath = join(normalizedPath, '.mcp.json');
537
+
538
+ if (!existsSync(mcpJsonPath)) {
539
+ return { error: '.mcp.json not found' };
540
+ }
541
+
542
+ const mcpJson = safeReadJson(mcpJsonPath);
543
+ if (!mcpJson || !mcpJson.mcpServers || !mcpJson.mcpServers[serverName]) {
544
+ return { error: `Server not found: ${serverName}` };
545
+ }
546
+
547
+ // Remove the server
548
+ delete mcpJson.mcpServers[serverName];
549
+
550
+ // Write back to .mcp.json
551
+ writeFileSync(mcpJsonPath, JSON.stringify(mcpJson, null, 2), 'utf8');
552
+
553
+ return {
554
+ success: true,
555
+ serverName,
556
+ removed: true,
557
+ scope: 'project-mcp-json'
558
+ };
559
+ } catch (error: unknown) {
560
+ console.error('Error removing MCP server from .mcp.json:', error);
561
+ return { error: (error as Error).message };
562
+ }
563
+ }
564
+
565
+ /**
566
+ * Get MCP configuration from multiple sources (per official Claude Code docs):
567
+ *
568
+ * Priority (highest to lowest):
569
+ * 1. Enterprise managed-mcp.json (cannot be overridden)
570
+ * 2. Local scope (project-specific private in ~/.claude.json)
571
+ * 3. Project scope (.mcp.json in project root)
572
+ * 4. User scope (mcpServers in ~/.claude.json)
573
+ *
574
+ * Note: ~/.claude/settings.json is for MCP PERMISSIONS, NOT definitions!
575
+ *
576
+ * @returns {Object}
577
+ */
578
+ function getMcpConfig() {
579
+ try {
580
+ const result = {
581
+ projects: {},
582
+ userServers: {}, // User-level servers from ~/.claude.json mcpServers
583
+ enterpriseServers: {}, // Enterprise managed servers (highest priority)
584
+ configSources: [] // Track where configs came from for debugging
585
+ };
586
+
587
+ // 1. Read Enterprise managed MCP servers (highest priority)
588
+ const enterprisePath = getEnterpriseMcpPath();
589
+ if (existsSync(enterprisePath)) {
590
+ const enterpriseConfig = safeReadJson(enterprisePath);
591
+ if (enterpriseConfig?.mcpServers) {
592
+ result.enterpriseServers = enterpriseConfig.mcpServers;
593
+ result.configSources.push({ type: 'enterprise', path: enterprisePath, count: Object.keys(enterpriseConfig.mcpServers).length });
594
+ }
595
+ }
596
+
597
+ // 2. Read from ~/.claude.json
598
+ if (existsSync(CLAUDE_CONFIG_PATH)) {
599
+ const claudeConfig = safeReadJson(CLAUDE_CONFIG_PATH);
600
+ if (claudeConfig) {
601
+ // 2a. User-level mcpServers (top-level mcpServers key)
602
+ if (claudeConfig.mcpServers) {
603
+ result.userServers = claudeConfig.mcpServers;
604
+ result.configSources.push({ type: 'user', path: CLAUDE_CONFIG_PATH, count: Object.keys(claudeConfig.mcpServers).length });
605
+ }
606
+
607
+ // 2b. Project-specific configurations (projects[path].mcpServers)
608
+ if (claudeConfig.projects) {
609
+ result.projects = claudeConfig.projects;
610
+ }
611
+ }
612
+ }
613
+
614
+ // 3. For each known project, check for .mcp.json (project-level config)
615
+ // .mcp.json is now the PRIMARY source for project-level MCP servers
616
+ const projectPaths = Object.keys(result.projects);
617
+ for (const projectPath of projectPaths) {
618
+ const mcpJsonPath = join(projectPath, '.mcp.json');
619
+ if (existsSync(mcpJsonPath)) {
620
+ const mcpJsonConfig = safeReadJson(mcpJsonPath);
621
+ if (mcpJsonConfig?.mcpServers) {
622
+ // Merge .mcp.json servers into project config
623
+ // .mcp.json has HIGHER priority than ~/.claude.json projects[path].mcpServers
624
+ const existingServers = result.projects[projectPath]?.mcpServers || {};
625
+ result.projects[projectPath] = {
626
+ ...result.projects[projectPath],
627
+ mcpServers: {
628
+ ...existingServers, // ~/.claude.json projects[path] (lower priority, legacy)
629
+ ...mcpJsonConfig.mcpServers // .mcp.json (higher priority, new default)
630
+ },
631
+ mcpJsonPath: mcpJsonPath, // Track source for debugging
632
+ hasMcpJson: true
633
+ };
634
+ result.configSources.push({
635
+ type: 'project-mcp-json',
636
+ path: mcpJsonPath,
637
+ count: Object.keys(mcpJsonConfig.mcpServers).length
638
+ });
639
+ }
640
+ }
641
+ }
642
+
643
+ // Build globalServers by merging user and enterprise servers
644
+ // Enterprise servers override user servers
645
+ result.globalServers = {
646
+ ...result.userServers,
647
+ ...result.enterpriseServers
648
+ };
649
+
650
+ return result;
651
+ } catch (error: unknown) {
652
+ console.error('Error reading MCP config:', error);
653
+ return { projects: {}, globalServers: {}, userServers: {}, enterpriseServers: {}, configSources: [], error: (error as Error).message };
654
+ }
655
+ }
656
+
657
+ /**
658
+ * Normalize path to filesystem format (for accessing .mcp.json files)
659
+ * Always uses forward slashes for cross-platform compatibility
660
+ * @param {string} path
661
+ * @returns {string}
662
+ */
663
+ function normalizePathForFileSystem(path) {
664
+ let normalized = path.replace(/\\/g, '/');
665
+
666
+ // Handle /d/path format -> D:/path
667
+ if (normalized.match(/^\/[a-zA-Z]\//)) {
668
+ normalized = normalized.charAt(1).toUpperCase() + ':' + normalized.slice(2);
669
+ }
670
+
671
+ return normalized;
672
+ }
673
+
674
+ /**
675
+ * Normalize project path to match existing format in .claude.json
676
+ * Checks both forward slash and backslash formats to find existing entry
677
+ * @param {string} path
678
+ * @param {Object} claudeConfig - Optional existing config to check format
679
+ * @returns {string}
680
+ */
681
+ function normalizeProjectPathForConfig(path, claudeConfig = null) {
682
+ // IMPORTANT: Always normalize to forward slashes to prevent duplicate entries
683
+ // (e.g., prevents both "D:/Claude_dms3" and "D:\\Claude_dms3")
684
+ let normalizedForward = path.replace(/\\/g, '/');
685
+
686
+ // Handle /d/path format -> D:/path
687
+ if (normalizedForward.match(/^\/[a-zA-Z]\//)) {
688
+ normalizedForward = normalizedForward.charAt(1).toUpperCase() + ':' + normalizedForward.slice(2);
689
+ }
690
+
691
+ // ALWAYS return forward slash format to prevent duplicates
692
+ return normalizedForward;
693
+ }
694
+
695
+ /**
696
+ * Toggle MCP server enabled/disabled
697
+ * @param {string} projectPath
698
+ * @param {string} serverName
699
+ * @param {boolean} enable
700
+ * @returns {Object}
701
+ */
702
+ function toggleMcpServerEnabled(projectPath, serverName, enable) {
703
+ try {
704
+ if (!existsSync(CLAUDE_CONFIG_PATH)) {
705
+ return { error: '.claude.json not found' };
706
+ }
707
+
708
+ const content = readFileSync(CLAUDE_CONFIG_PATH, 'utf8');
709
+ const config = JSON.parse(content);
710
+
711
+ const normalizedPath = normalizeProjectPathForConfig(projectPath, config);
712
+
713
+ if (!config.projects || !config.projects[normalizedPath]) {
714
+ return { error: `Project not found: ${normalizedPath}` };
715
+ }
716
+
717
+ const projectConfig = config.projects[normalizedPath];
718
+
719
+ // Ensure disabledMcpServers array exists
720
+ if (!projectConfig.disabledMcpServers) {
721
+ projectConfig.disabledMcpServers = [];
722
+ }
723
+
724
+ if (enable) {
725
+ // Remove from disabled list
726
+ projectConfig.disabledMcpServers = projectConfig.disabledMcpServers.filter(s => s !== serverName);
727
+ } else {
728
+ // Add to disabled list if not already there
729
+ if (!projectConfig.disabledMcpServers.includes(serverName)) {
730
+ projectConfig.disabledMcpServers.push(serverName);
731
+ }
732
+ }
733
+
734
+ // Write back to file
735
+ writeFileSync(CLAUDE_CONFIG_PATH, JSON.stringify(config, null, 2), 'utf8');
736
+
737
+ return {
738
+ success: true,
739
+ serverName,
740
+ enabled: enable,
741
+ disabledMcpServers: projectConfig.disabledMcpServers
742
+ };
743
+ } catch (error: unknown) {
744
+ console.error('Error toggling MCP server:', error);
745
+ return { error: (error as Error).message };
746
+ }
747
+ }
748
+
749
+ /**
750
+ * Add MCP server to project
751
+ * Now defaults to using .mcp.json instead of .claude.json
752
+ * @param {string} projectPath
753
+ * @param {string} serverName
754
+ * @param {Object} serverConfig
755
+ * @param {boolean} useLegacyConfig - If true, use .claude.json instead of .mcp.json
756
+ * @returns {Object}
757
+ */
758
+ function addMcpServerToProject(projectPath, serverName, serverConfig, useLegacyConfig = false) {
759
+ try {
760
+ // Default: Use .mcp.json for project-level MCP servers
761
+ if (!useLegacyConfig) {
762
+ return addMcpServerToMcpJson(projectPath, serverName, serverConfig);
763
+ }
764
+
765
+ // Legacy: Use .claude.json (kept for backward compatibility)
766
+ if (!existsSync(CLAUDE_CONFIG_PATH)) {
767
+ return { error: '.claude.json not found' };
768
+ }
769
+
770
+ const content = readFileSync(CLAUDE_CONFIG_PATH, 'utf8');
771
+ const config = JSON.parse(content);
772
+
773
+ const normalizedPath = normalizeProjectPathForConfig(projectPath, config);
774
+
775
+ // Create project entry if it doesn't exist
776
+ if (!config.projects) {
777
+ config.projects = {};
778
+ }
779
+
780
+ if (!config.projects[normalizedPath]) {
781
+ config.projects[normalizedPath] = {
782
+ allowedTools: [],
783
+ mcpContextUris: [],
784
+ mcpServers: {},
785
+ enabledMcpjsonServers: [],
786
+ disabledMcpjsonServers: [],
787
+ hasTrustDialogAccepted: false,
788
+ projectOnboardingSeenCount: 0,
789
+ hasClaudeMdExternalIncludesApproved: false,
790
+ hasClaudeMdExternalIncludesWarningShown: false
791
+ };
792
+ }
793
+
794
+ const projectConfig = config.projects[normalizedPath];
795
+
796
+ // Ensure mcpServers exists
797
+ if (!projectConfig.mcpServers) {
798
+ projectConfig.mcpServers = {};
799
+ }
800
+
801
+ // Add the server
802
+ projectConfig.mcpServers[serverName] = serverConfig;
803
+
804
+ // Write back to file
805
+ writeFileSync(CLAUDE_CONFIG_PATH, JSON.stringify(config, null, 2), 'utf8');
806
+
807
+ return {
808
+ success: true,
809
+ serverName,
810
+ serverConfig,
811
+ scope: 'project-legacy'
812
+ };
813
+ } catch (error: unknown) {
814
+ console.error('Error adding MCP server:', error);
815
+ return { error: (error as Error).message };
816
+ }
817
+ }
818
+
819
+ /**
820
+ * Remove MCP server from project
821
+ * Checks both .mcp.json and .claude.json
822
+ * @param {string} projectPath
823
+ * @param {string} serverName
824
+ * @returns {Object}
825
+ */
826
+ function removeMcpServerFromProject(projectPath, serverName) {
827
+ try {
828
+ const normalizedPathForFile = normalizePathForFileSystem(projectPath);
829
+ const mcpJsonPath = join(normalizedPathForFile, '.mcp.json');
830
+
831
+ let removedFromMcpJson = false;
832
+ let removedFromClaudeJson = false;
833
+
834
+ // Try to remove from .mcp.json first (new default)
835
+ if (existsSync(mcpJsonPath)) {
836
+ const mcpJson = safeReadJson(mcpJsonPath);
837
+ if (mcpJson?.mcpServers?.[serverName]) {
838
+ const result = removeMcpServerFromMcpJson(projectPath, serverName);
839
+ if (result.success) {
840
+ removedFromMcpJson = true;
841
+ }
842
+ }
843
+ }
844
+
845
+ // Also try to remove from .claude.json (legacy - may coexist)
846
+ if (existsSync(CLAUDE_CONFIG_PATH)) {
847
+ const content = readFileSync(CLAUDE_CONFIG_PATH, 'utf8');
848
+ const config = JSON.parse(content);
849
+
850
+ // Get normalized path that matches existing config format
851
+ const normalizedPath = normalizeProjectPathForConfig(projectPath, config);
852
+
853
+ if (config.projects && config.projects[normalizedPath]) {
854
+ const projectConfig = config.projects[normalizedPath];
855
+
856
+ if (projectConfig.mcpServers && projectConfig.mcpServers[serverName]) {
857
+ // Remove the server
858
+ delete projectConfig.mcpServers[serverName];
859
+
860
+ // Also remove from disabled list if present
861
+ if (projectConfig.disabledMcpServers) {
862
+ projectConfig.disabledMcpServers = projectConfig.disabledMcpServers.filter(s => s !== serverName);
863
+ }
864
+
865
+ // Write back to file
866
+ writeFileSync(CLAUDE_CONFIG_PATH, JSON.stringify(config, null, 2), 'utf8');
867
+ removedFromClaudeJson = true;
868
+ }
869
+ }
870
+ }
871
+
872
+ // Return success if removed from either location
873
+ if (removedFromMcpJson || removedFromClaudeJson) {
874
+ return {
875
+ success: true,
876
+ serverName,
877
+ removed: true,
878
+ scope: removedFromMcpJson ? 'project-mcp-json' : 'project-legacy',
879
+ removedFrom: removedFromMcpJson && removedFromClaudeJson ? 'both' :
880
+ removedFromMcpJson ? '.mcp.json' : '.claude.json'
881
+ };
882
+ }
883
+
884
+ return { error: `Server not found: ${serverName}` };
885
+ } catch (error: unknown) {
886
+ console.error('Error removing MCP server:', error);
887
+ return { error: (error as Error).message };
888
+ }
889
+ }
890
+
891
+ /**
892
+ * Add MCP server to global/user scope (top-level mcpServers in ~/.claude.json)
893
+ * @param {string} serverName
894
+ * @param {Object} serverConfig
895
+ * @returns {Object}
896
+ */
897
+ function addGlobalMcpServer(serverName, serverConfig) {
898
+ try {
899
+ if (!existsSync(CLAUDE_CONFIG_PATH)) {
900
+ return { error: '.claude.json not found' };
901
+ }
902
+
903
+ const content = readFileSync(CLAUDE_CONFIG_PATH, 'utf8');
904
+ const config = JSON.parse(content);
905
+
906
+ // Ensure top-level mcpServers exists
907
+ if (!config.mcpServers) {
908
+ config.mcpServers = {};
909
+ }
910
+
911
+ // Add the server to top-level mcpServers
912
+ config.mcpServers[serverName] = serverConfig;
913
+
914
+ // Write back to file
915
+ writeFileSync(CLAUDE_CONFIG_PATH, JSON.stringify(config, null, 2), 'utf8');
916
+
917
+ return {
918
+ success: true,
919
+ serverName,
920
+ serverConfig,
921
+ scope: 'global'
922
+ };
923
+ } catch (error: unknown) {
924
+ console.error('Error adding global MCP server:', error);
925
+ return { error: (error as Error).message };
926
+ }
927
+ }
928
+
929
+ /**
930
+ * Remove MCP server from global/user scope (top-level mcpServers)
931
+ * @param {string} serverName
932
+ * @returns {Object}
933
+ */
934
+ function removeGlobalMcpServer(serverName) {
935
+ try {
936
+ if (!existsSync(CLAUDE_CONFIG_PATH)) {
937
+ return { error: '.claude.json not found' };
938
+ }
939
+
940
+ const content = readFileSync(CLAUDE_CONFIG_PATH, 'utf8');
941
+ const config = JSON.parse(content);
942
+
943
+ if (!config.mcpServers || !config.mcpServers[serverName]) {
944
+ return { error: `Global server not found: ${serverName}` };
945
+ }
946
+
947
+ // Remove the server from top-level mcpServers
948
+ delete config.mcpServers[serverName];
949
+
950
+ // Write back to file
951
+ writeFileSync(CLAUDE_CONFIG_PATH, JSON.stringify(config, null, 2), 'utf8');
952
+
953
+ return {
954
+ success: true,
955
+ serverName,
956
+ removed: true,
957
+ scope: 'global'
958
+ };
959
+ } catch (error: unknown) {
960
+ console.error('Error removing global MCP server:', error);
961
+ return { error: (error as Error).message };
962
+ }
963
+ }
964
+
965
+ /**
966
+ * Read settings file safely
967
+ * @param {string} filePath
968
+ * @returns {Object}
969
+ */
970
+ function readSettingsFile(filePath) {
971
+ try {
972
+ if (!existsSync(filePath)) {
973
+ return {};
974
+ }
975
+ const content = readFileSync(filePath, 'utf8');
976
+ return JSON.parse(content);
977
+ } catch (error: unknown) {
978
+ console.error(`Error reading settings file ${filePath}:`, error);
979
+ return {};
980
+ }
981
+ }
982
+
983
+ /**
984
+ * Write settings file safely
985
+ * @param {string} filePath
986
+ * @param {Object} settings
987
+ */
988
+ function writeSettingsFile(filePath, settings) {
989
+ const dirPath = dirname(filePath);
990
+ // Ensure directory exists
991
+ if (!existsSync(dirPath)) {
992
+ mkdirSync(dirPath, { recursive: true });
993
+ }
994
+ writeFileSync(filePath, JSON.stringify(settings, null, 2), 'utf8');
995
+ }
996
+
997
+ /**
998
+ * Get project settings path
999
+ * @param {string} projectPath
1000
+ * @returns {string}
1001
+ */
1002
+ function getProjectSettingsPath(projectPath) {
1003
+ const normalizedPath = projectPath.replace(/\//g, '\\').replace(/^\\([a-zA-Z])\\/, '$1:\\');
1004
+ return join(normalizedPath, '.claude', 'settings.json');
1005
+ }
1006
+
1007
+ // ========================================
1008
+ // Route Handlers
1009
+ // ========================================
1010
+
1011
+ /**
1012
+ * Handle MCP routes
1013
+ * @returns true if route was handled, false otherwise
1014
+ */
1015
+ export async function handleMcpRoutes(ctx: RouteContext): Promise<boolean> {
1016
+ const { pathname, url, req, res, initialPath, handlePostRequest, broadcastToClients } = ctx;
1017
+
1018
+ // API: Get MCP configuration (includes both Claude and Codex)
1019
+ if (pathname === '/api/mcp-config') {
1020
+ const mcpData = getMcpConfig();
1021
+ const codexData = getCodexMcpConfig();
1022
+ const combinedData = {
1023
+ ...mcpData,
1024
+ codex: codexData
1025
+ };
1026
+ res.writeHead(200, { 'Content-Type': 'application/json' });
1027
+ res.end(JSON.stringify(combinedData));
1028
+ return true;
1029
+ }
1030
+
1031
+ // ========================================
1032
+ // Codex MCP API Endpoints
1033
+ // ========================================
1034
+
1035
+ // API: Get Codex MCP configuration
1036
+ if (pathname === '/api/codex-mcp-config') {
1037
+ const codexData = getCodexMcpConfig();
1038
+ res.writeHead(200, { 'Content-Type': 'application/json' });
1039
+ res.end(JSON.stringify(codexData));
1040
+ return true;
1041
+ }
1042
+
1043
+ // API: Add Codex MCP server
1044
+ if (pathname === '/api/codex-mcp-add' && req.method === 'POST') {
1045
+ handlePostRequest(req, res, async (body) => {
1046
+ const { serverName, serverConfig } = body;
1047
+ if (!serverName || !serverConfig) {
1048
+ return { error: 'serverName and serverConfig are required', status: 400 };
1049
+ }
1050
+ return addCodexMcpServer(serverName, serverConfig);
1051
+ });
1052
+ return true;
1053
+ }
1054
+
1055
+ // API: Remove Codex MCP server
1056
+ if (pathname === '/api/codex-mcp-remove' && req.method === 'POST') {
1057
+ handlePostRequest(req, res, async (body) => {
1058
+ const { serverName } = body;
1059
+ if (!serverName) {
1060
+ return { error: 'serverName is required', status: 400 };
1061
+ }
1062
+ return removeCodexMcpServer(serverName);
1063
+ });
1064
+ return true;
1065
+ }
1066
+
1067
+ // API: Toggle Codex MCP server enabled state
1068
+ if (pathname === '/api/codex-mcp-toggle' && req.method === 'POST') {
1069
+ handlePostRequest(req, res, async (body) => {
1070
+ const { serverName, enabled } = body;
1071
+ if (!serverName || enabled === undefined) {
1072
+ return { error: 'serverName and enabled are required', status: 400 };
1073
+ }
1074
+ return toggleCodexMcpServer(serverName, enabled);
1075
+ });
1076
+ return true;
1077
+ }
1078
+
1079
+ // API: Toggle MCP server enabled/disabled
1080
+ if (pathname === '/api/mcp-toggle' && req.method === 'POST') {
1081
+ handlePostRequest(req, res, async (body) => {
1082
+ const { projectPath, serverName, enable } = body;
1083
+ if (!projectPath || !serverName) {
1084
+ return { error: 'projectPath and serverName are required', status: 400 };
1085
+ }
1086
+ return toggleMcpServerEnabled(projectPath, serverName, enable);
1087
+ });
1088
+ return true;
1089
+ }
1090
+
1091
+ // API: Copy MCP server to project
1092
+ if (pathname === '/api/mcp-copy-server' && req.method === 'POST') {
1093
+ handlePostRequest(req, res, async (body) => {
1094
+ const { projectPath, serverName, serverConfig, configType } = body;
1095
+ if (!projectPath || !serverName || !serverConfig) {
1096
+ return { error: 'projectPath, serverName, and serverConfig are required', status: 400 };
1097
+ }
1098
+ // configType: 'mcp' = use .mcp.json (default), 'claude' = use .claude.json
1099
+ const useLegacyConfig = configType === 'claude';
1100
+ return addMcpServerToProject(projectPath, serverName, serverConfig, useLegacyConfig);
1101
+ });
1102
+ return true;
1103
+ }
1104
+
1105
+ // API: Install CCW MCP server to project
1106
+ if (pathname === '/api/mcp-install-ccw' && req.method === 'POST') {
1107
+ handlePostRequest(req, res, async (body) => {
1108
+ const { projectPath } = body;
1109
+ if (!projectPath) {
1110
+ return { error: 'projectPath is required', status: 400 };
1111
+ }
1112
+
1113
+ // Generate CCW MCP server config
1114
+ // Use cmd /c to inherit Claude Code's working directory
1115
+ const ccwMcpConfig = {
1116
+ command: "cmd",
1117
+ args: ["/c", "npx", "-y", "ccw-mcp"],
1118
+ env: {
1119
+ CCW_ENABLED_TOOLS: "all"
1120
+ }
1121
+ };
1122
+
1123
+ // Use existing addMcpServerToProject to install CCW MCP
1124
+ return addMcpServerToProject(projectPath, 'ccw-tools', ccwMcpConfig);
1125
+ });
1126
+ return true;
1127
+ }
1128
+
1129
+ // API: Remove MCP server from project
1130
+ if (pathname === '/api/mcp-remove-server' && req.method === 'POST') {
1131
+ handlePostRequest(req, res, async (body) => {
1132
+ const { projectPath, serverName } = body;
1133
+ if (!projectPath || !serverName) {
1134
+ return { error: 'projectPath and serverName are required', status: 400 };
1135
+ }
1136
+ return removeMcpServerFromProject(projectPath, serverName);
1137
+ });
1138
+ return true;
1139
+ }
1140
+
1141
+ // API: Add MCP server to global scope (top-level mcpServers in ~/.claude.json)
1142
+ if (pathname === '/api/mcp-add-global-server' && req.method === 'POST') {
1143
+ handlePostRequest(req, res, async (body) => {
1144
+ const { serverName, serverConfig } = body;
1145
+ if (!serverName || !serverConfig) {
1146
+ return { error: 'serverName and serverConfig are required', status: 400 };
1147
+ }
1148
+ return addGlobalMcpServer(serverName, serverConfig);
1149
+ });
1150
+ return true;
1151
+ }
1152
+
1153
+ // API: Remove MCP server from global scope
1154
+ if (pathname === '/api/mcp-remove-global-server' && req.method === 'POST') {
1155
+ handlePostRequest(req, res, async (body) => {
1156
+ const { serverName } = body;
1157
+ if (!serverName) {
1158
+ return { error: 'serverName is required', status: 400 };
1159
+ }
1160
+ return removeGlobalMcpServer(serverName);
1161
+ });
1162
+ return true;
1163
+ }
1164
+
1165
+ // ========================================
1166
+ // MCP Templates API
1167
+ // ========================================
1168
+
1169
+ // API: Get all MCP templates
1170
+ if (pathname === '/api/mcp-templates' && req.method === 'GET') {
1171
+ const templates = McpTemplatesDb.getAllTemplates();
1172
+ res.writeHead(200, { 'Content-Type': 'application/json' });
1173
+ res.end(JSON.stringify({ success: true, templates }));
1174
+ return true;
1175
+ }
1176
+
1177
+ // API: Save MCP template
1178
+ if (pathname === '/api/mcp-templates' && req.method === 'POST') {
1179
+ handlePostRequest(req, res, async (body) => {
1180
+ const { name, description, serverConfig, tags, category } = body;
1181
+ if (!name || !serverConfig) {
1182
+ return { error: 'name and serverConfig are required', status: 400 };
1183
+ }
1184
+ return McpTemplatesDb.saveTemplate({
1185
+ name,
1186
+ description,
1187
+ serverConfig,
1188
+ tags,
1189
+ category
1190
+ });
1191
+ });
1192
+ return true;
1193
+ }
1194
+
1195
+ // API: Get template by name
1196
+ if (pathname.startsWith('/api/mcp-templates/') && req.method === 'GET') {
1197
+ const templateName = decodeURIComponent(pathname.split('/api/mcp-templates/')[1]);
1198
+ const template = McpTemplatesDb.getTemplateByName(templateName);
1199
+ if (template) {
1200
+ res.writeHead(200, { 'Content-Type': 'application/json' });
1201
+ res.end(JSON.stringify({ success: true, template }));
1202
+ } else {
1203
+ res.writeHead(404, { 'Content-Type': 'application/json' });
1204
+ res.end(JSON.stringify({ success: false, error: 'Template not found' }));
1205
+ }
1206
+ return true;
1207
+ }
1208
+
1209
+ // API: Delete MCP template
1210
+ if (pathname.startsWith('/api/mcp-templates/') && req.method === 'DELETE') {
1211
+ const templateName = decodeURIComponent(pathname.split('/api/mcp-templates/')[1]);
1212
+ const result = McpTemplatesDb.deleteTemplate(templateName);
1213
+ res.writeHead(result.success ? 200 : 404, { 'Content-Type': 'application/json' });
1214
+ res.end(JSON.stringify(result));
1215
+ return true;
1216
+ }
1217
+
1218
+ // API: Search MCP templates
1219
+ if (pathname === '/api/mcp-templates/search' && req.method === 'GET') {
1220
+ const keyword = url.searchParams.get('q') || '';
1221
+ const templates = McpTemplatesDb.searchTemplates(keyword);
1222
+ res.writeHead(200, { 'Content-Type': 'application/json' });
1223
+ res.end(JSON.stringify({ success: true, templates }));
1224
+ return true;
1225
+ }
1226
+
1227
+ // API: Get all categories
1228
+ if (pathname === '/api/mcp-templates/categories' && req.method === 'GET') {
1229
+ const categories = McpTemplatesDb.getAllCategories();
1230
+ res.writeHead(200, { 'Content-Type': 'application/json' });
1231
+ res.end(JSON.stringify({ success: true, categories }));
1232
+ return true;
1233
+ }
1234
+
1235
+ // API: Get templates by category
1236
+ if (pathname.startsWith('/api/mcp-templates/category/') && req.method === 'GET') {
1237
+ const category = decodeURIComponent(pathname.split('/api/mcp-templates/category/')[1]);
1238
+ const templates = McpTemplatesDb.getTemplatesByCategory(category);
1239
+ res.writeHead(200, { 'Content-Type': 'application/json' });
1240
+ res.end(JSON.stringify({ success: true, templates }));
1241
+ return true;
1242
+ }
1243
+
1244
+ // API: Install template to project or global
1245
+ if (pathname === '/api/mcp-templates/install' && req.method === 'POST') {
1246
+ handlePostRequest(req, res, async (body) => {
1247
+ const { templateName, projectPath, scope } = body;
1248
+ if (!templateName) {
1249
+ return { error: 'templateName is required', status: 400 };
1250
+ }
1251
+
1252
+ const template = McpTemplatesDb.getTemplateByName(templateName);
1253
+ if (!template) {
1254
+ return { error: 'Template not found', status: 404 };
1255
+ }
1256
+
1257
+ // Install to global or project
1258
+ if (scope === 'global') {
1259
+ return addGlobalMcpServer(templateName, template.serverConfig);
1260
+ } else {
1261
+ if (!projectPath) {
1262
+ return { error: 'projectPath is required for project scope', status: 400 };
1263
+ }
1264
+ return addMcpServerToProject(projectPath, templateName, template.serverConfig);
1265
+ }
1266
+ });
1267
+ return true;
1268
+ }
1269
+
1270
+ return false;
1271
+ }