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,928 @@
1
+ // MCP Manager View - Redesigned with Sectioned Layout
2
+ // Comprehensive MCP management for Claude and Codex with clear section separation
3
+
4
+ // ============================================================
5
+ // CONSTANTS & CONFIGURATION
6
+ // ============================================================
7
+
8
+ const CCW_MCP_TOOLS = [
9
+ { name: 'write_file', desc: 'Write/create files', core: true },
10
+ { name: 'edit_file', desc: 'Edit/replace content', core: true },
11
+ { name: 'codex_lens', desc: 'Code index & search', core: true },
12
+ { name: 'smart_search', desc: 'Quick regex/NL search', core: true },
13
+ { name: 'session_manager', desc: 'Workflow sessions', core: false },
14
+ { name: 'generate_module_docs', desc: 'Generate docs', core: false },
15
+ { name: 'update_module_claude', desc: 'Update CLAUDE.md', core: false },
16
+ { name: 'cli_executor', desc: 'Gemini/Qwen/Codex CLI', core: false },
17
+ ];
18
+
19
+ const MCP_CATEGORIES = [
20
+ 'Development Tools',
21
+ 'Data & APIs',
22
+ 'Files & Storage',
23
+ 'AI & ML',
24
+ 'DevOps',
25
+ 'Custom'
26
+ ];
27
+
28
+ // Get currently enabled tools from installed config (Claude)
29
+ function getCcwEnabledTools() {
30
+ const currentPath = projectPath;
31
+ const projectData = mcpAllProjects[currentPath] || {};
32
+ const ccwConfig = projectData.mcpServers?.['ccw-tools'];
33
+ if (ccwConfig?.env?.CCW_ENABLED_TOOLS) {
34
+ const val = ccwConfig.env.CCW_ENABLED_TOOLS;
35
+ if (val.toLowerCase() === 'all') return CCW_MCP_TOOLS.map(t => t.name);
36
+ return val.split(',').map(t => t.trim());
37
+ }
38
+ return CCW_MCP_TOOLS.filter(t => t.core).map(t => t.name);
39
+ }
40
+
41
+ // Get currently enabled tools from Codex config
42
+ function getCcwEnabledToolsCodex() {
43
+ const ccwConfig = codexMcpServers?.['ccw-tools'];
44
+ if (ccwConfig?.env?.CCW_ENABLED_TOOLS) {
45
+ const val = ccwConfig.env.CCW_ENABLED_TOOLS;
46
+ if (val.toLowerCase() === 'all') return CCW_MCP_TOOLS.map(t => t.name);
47
+ return val.split(',').map(t => t.trim());
48
+ }
49
+ return CCW_MCP_TOOLS.filter(t => t.core).map(t => t.name);
50
+ }
51
+
52
+ // ============================================================
53
+ // MODAL DIALOG COMPONENT
54
+ // ============================================================
55
+
56
+ function showMcpEditorModal(options = {}) {
57
+ const {
58
+ mode = 'create',
59
+ serverName = '',
60
+ serverConfig = {},
61
+ template = null,
62
+ cliMode = currentCliMode, // 'claude' or 'codex'
63
+ installTargets = cliMode === 'codex' ? ['codex'] : ['project', 'global']
64
+ } = options;
65
+
66
+ const isView = mode === 'view';
67
+ const isEdit = mode === 'edit';
68
+ const title = isView ? t('mcp.viewServer') : isEdit ? t('mcp.editServer') : t('mcp.createServer');
69
+
70
+ const initialName = serverName || template?.name || '';
71
+ const initialDesc = template?.description || '';
72
+ const initialCategory = template?.category || 'Development Tools';
73
+ const initialConfig = serverConfig || template?.serverConfig || {
74
+ command: '',
75
+ args: [],
76
+ env: {},
77
+ url: '',
78
+ cwd: ''
79
+ };
80
+
81
+ const modalHtml = `
82
+ <div class="fixed inset-0 bg-black/50 flex items-center justify-center z-50" id="mcpEditorModal" style="backdrop-filter: blur(4px);">
83
+ <div class="bg-card border border-border rounded-xl shadow-2xl w-full max-w-3xl max-h-[90vh] overflow-hidden flex flex-col">
84
+ <div class="flex items-center justify-between px-6 py-4 border-b border-border bg-gradient-to-r from-primary/5 to-transparent">
85
+ <div class="flex items-center gap-3">
86
+ <div class="w-10 h-10 rounded-lg bg-primary/10 flex items-center justify-center">
87
+ <i data-lucide="${isView ? 'eye' : isEdit ? 'edit-3' : 'plus-circle'}" class="w-5 h-5 text-primary"></i>
88
+ </div>
89
+ <div>
90
+ <h2 class="text-lg font-bold text-foreground">${title}</h2>
91
+ <p class="text-xs text-muted-foreground">${cliMode === 'codex' ? 'Codex MCP Server' : 'Claude MCP Server'}</p>
92
+ </div>
93
+ </div>
94
+ <button onclick="closeMcpEditorModal()" class="text-muted-foreground hover:text-foreground transition-colors">
95
+ <i data-lucide="x" class="w-5 h-5"></i>
96
+ </button>
97
+ </div>
98
+
99
+ <div class="flex-1 overflow-y-auto px-6 py-4">
100
+ <div class="space-y-4 mb-6">
101
+ <div>
102
+ <label class="block text-sm font-medium text-foreground mb-1.5">${t('mcp.serverName')}</label>
103
+ <input type="text" id="mcpModalName" value="${initialName}" ${isView ? 'disabled' : ''}
104
+ placeholder="my-mcp-server"
105
+ class="w-full px-3 py-2 bg-background border border-border rounded-lg text-foreground placeholder-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary/50 disabled:opacity-50 disabled:cursor-not-allowed" />
106
+ </div>
107
+
108
+ ${!isView && cliMode !== 'codex' ? `
109
+ <div>
110
+ <label class="block text-sm font-medium text-foreground mb-1.5">${t('mcp.description')} (${t('mcp.optional')})</label>
111
+ <input type="text" id="mcpModalDesc" value="${initialDesc}" placeholder="Brief description"
112
+ class="w-full px-3 py-2 bg-background border border-border rounded-lg text-foreground placeholder-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary/50" />
113
+ </div>
114
+ ` : ''}
115
+ </div>
116
+
117
+ <div class="mb-4">
118
+ <div class="flex items-center gap-2 border-b border-border">
119
+ <button class="px-4 py-2 text-sm font-medium border-b-2 border-primary text-primary"
120
+ onclick="switchMcpServerType('stdio')" id="mcpTypeStdio" ${isView ? 'disabled' : ''}>
121
+ <i data-lucide="terminal" class="w-4 h-4 inline mr-1.5"></i>
122
+ STDIO (Command)
123
+ </button>
124
+ <button class="px-4 py-2 text-sm font-medium border-b-2 border-transparent text-muted-foreground hover:text-foreground"
125
+ onclick="switchMcpServerType('http')" id="mcpTypeHttp" ${isView ? 'disabled' : ''}>
126
+ <i data-lucide="globe" class="w-4 h-4 inline mr-1.5"></i>
127
+ HTTP (URL)
128
+ </button>
129
+ </div>
130
+ </div>
131
+
132
+ <div id="mcpStdioConfig" class="space-y-4">
133
+ <div>
134
+ <label class="block text-sm font-medium text-foreground mb-1.5">${t('mcp.command')}</label>
135
+ <input type="text" id="mcpModalCommand" value="${initialConfig.command || ''}" ${isView ? 'disabled' : ''}
136
+ placeholder="node" class="w-full px-3 py-2 bg-background border border-border rounded-lg text-foreground placeholder-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary/50 disabled:opacity-50 disabled:cursor-not-allowed font-mono text-sm" />
137
+ </div>
138
+ <div>
139
+ <label class="block text-sm font-medium text-foreground mb-1.5">${t('mcp.args')} (${t('mcp.optional')})</label>
140
+ <textarea id="mcpModalArgs" ${isView ? 'disabled' : ''} rows="3" placeholder='["/path/to/server.js"]'
141
+ class="w-full px-3 py-2 bg-background border border-border rounded-lg text-foreground placeholder-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary/50 disabled:opacity-50 disabled:cursor-not-allowed font-mono text-sm">${JSON.stringify(initialConfig.args || [], null, 2)}</textarea>
142
+ </div>
143
+ <div>
144
+ <label class="block text-sm font-medium text-foreground mb-1.5">${t('mcp.env')} (${t('mcp.optional')})</label>
145
+ <textarea id="mcpModalEnv" ${isView ? 'disabled' : ''} rows="4" placeholder='{"API_KEY": "your-key"}'
146
+ class="w-full px-3 py-2 bg-background border border-border rounded-lg text-foreground placeholder-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary/50 disabled:opacity-50 disabled:cursor-not-allowed font-mono text-sm">${JSON.stringify(initialConfig.env || {}, null, 2)}</textarea>
147
+ </div>
148
+ <div>
149
+ <label class="block text-sm font-medium text-foreground mb-1.5">${t('mcp.cwd')} (${t('mcp.optional')})</label>
150
+ <input type="text" id="mcpModalCwd" value="${initialConfig.cwd || ''}" ${isView ? 'disabled' : ''}
151
+ placeholder="/path/to/working/directory" class="w-full px-3 py-2 bg-background border border-border rounded-lg text-foreground placeholder-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary/50 disabled:opacity-50 disabled:cursor-not-allowed font-mono text-sm" />
152
+ </div>
153
+ </div>
154
+
155
+ <div id="mcpHttpConfig" class="space-y-4 hidden">
156
+ <div>
157
+ <label class="block text-sm font-medium text-foreground mb-1.5">${t('mcp.url')}</label>
158
+ <input type="text" id="mcpModalUrl" value="${initialConfig.url || ''}" ${isView ? 'disabled' : ''}
159
+ placeholder="https://api.example.com/mcp" class="w-full px-3 py-2 bg-background border border-border rounded-lg text-foreground placeholder-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary/50 disabled:opacity-50 disabled:cursor-not-allowed font-mono text-sm" />
160
+ </div>
161
+ <div>
162
+ <label class="block text-sm font-medium text-foreground mb-1.5">${t('mcp.httpHeaders')} (${t('mcp.optional')})</label>
163
+ <textarea id="mcpModalHttpHeaders" ${isView ? 'disabled' : ''} rows="4" placeholder='{"Authorization": "Bearer token"}'
164
+ class="w-full px-3 py-2 bg-background border border-border rounded-lg text-foreground placeholder-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary/50 disabled:opacity-50 disabled:cursor-not-allowed font-mono text-sm">${JSON.stringify(initialConfig.http_headers || initialConfig.httpHeaders || {}, null, 2)}</textarea>
165
+ </div>
166
+ </div>
167
+
168
+ ${!isView && cliMode !== 'codex' ? `
169
+ <div class="mt-6 pt-6 border-t border-border">
170
+ <label class="flex items-center gap-2 cursor-pointer">
171
+ <input type="checkbox" id="mcpModalSaveTemplate" class="w-4 h-4 rounded border-border text-primary focus:ring-2 focus:ring-primary/50" />
172
+ <span class="text-sm font-medium text-foreground">${t('mcp.saveAsTemplate')}</span>
173
+ </label>
174
+ </div>
175
+ ` : ''}
176
+ </div>
177
+
178
+ <div class="flex items-center justify-between px-6 py-4 border-t border-border bg-muted/30">
179
+ ${!isView ? `
180
+ <div class="flex items-center gap-2">
181
+ <span class="text-sm font-medium text-foreground">${t('mcp.installTo')}:</span>
182
+ <select id="mcpModalTarget" class="px-3 py-1.5 bg-background border border-border rounded-lg text-sm text-foreground focus:outline-none focus:ring-2 focus:ring-primary/50">
183
+ ${installTargets.map(target => {
184
+ const labels = {
185
+ project: 'Project (.mcp.json)',
186
+ global: 'Global (~/.claude.json)',
187
+ codex: 'Codex (~/.codex/config.toml)'
188
+ };
189
+ return `<option value="${target}">${labels[target]}</option>`;
190
+ }).join('')}
191
+ </select>
192
+ </div>
193
+ ` : '<div></div>'}
194
+
195
+ <div class="flex items-center gap-2">
196
+ <button onclick="closeMcpEditorModal()" class="px-4 py-2 text-sm font-medium text-foreground hover:bg-muted rounded-lg transition-colors">
197
+ ${isView ? t('mcp.close') : t('mcp.cancel')}
198
+ </button>
199
+ ${!isView ? `
200
+ <button onclick="saveMcpFromModal('${cliMode}')" class="px-4 py-2 text-sm font-medium bg-primary text-primary-foreground rounded-lg hover:bg-primary/90 transition-colors flex items-center gap-2">
201
+ <i data-lucide="save" class="w-4 h-4"></i>
202
+ ${isEdit ? t('mcp.update') : t('mcp.install')}
203
+ </button>
204
+ ` : ''}
205
+ </div>
206
+ </div>
207
+ </div>
208
+ </div>
209
+ `;
210
+
211
+ const existingModal = document.getElementById('mcpEditorModal');
212
+ if (existingModal) existingModal.remove();
213
+ document.body.insertAdjacentHTML('beforeend', modalHtml);
214
+ if (typeof lucide !== 'undefined') lucide.createIcons();
215
+ if (initialConfig.url) switchMcpServerType('http');
216
+ }
217
+
218
+ function switchMcpServerType(type) {
219
+ const stdioConfig = document.getElementById('mcpStdioConfig');
220
+ const httpConfig = document.getElementById('mcpHttpConfig');
221
+ const stdioBtn = document.getElementById('mcpTypeStdio');
222
+ const httpBtn = document.getElementById('mcpTypeHttp');
223
+
224
+ if (type === 'stdio') {
225
+ stdioConfig.classList.remove('hidden');
226
+ httpConfig.classList.add('hidden');
227
+ stdioBtn.classList.add('border-primary', 'text-primary');
228
+ stdioBtn.classList.remove('border-transparent', 'text-muted-foreground');
229
+ httpBtn.classList.remove('border-primary', 'text-primary');
230
+ httpBtn.classList.add('border-transparent', 'text-muted-foreground');
231
+ } else {
232
+ stdioConfig.classList.add('hidden');
233
+ httpConfig.classList.remove('hidden');
234
+ httpBtn.classList.add('border-primary', 'text-primary');
235
+ httpBtn.classList.remove('border-transparent', 'text-muted-foreground');
236
+ stdioBtn.classList.remove('border-primary', 'text-primary');
237
+ stdioBtn.classList.add('border-transparent', 'text-muted-foreground');
238
+ }
239
+ }
240
+
241
+ function closeMcpEditorModal() {
242
+ const modal = document.getElementById('mcpEditorModal');
243
+ if (modal) modal.remove();
244
+ }
245
+
246
+ async function saveMcpFromModal(cliMode) {
247
+ const name = document.getElementById('mcpModalName').value.trim();
248
+ const desc = document.getElementById('mcpModalDesc')?.value.trim() || '';
249
+ const target = document.getElementById('mcpModalTarget')?.value || (cliMode === 'codex' ? 'codex' : 'project');
250
+ const saveAsTemplate = document.getElementById('mcpModalSaveTemplate')?.checked || false;
251
+
252
+ if (!name) {
253
+ showToast(t('mcp.error'), t('mcp.nameRequired'), 'error');
254
+ return;
255
+ }
256
+
257
+ const isStdio = !document.getElementById('mcpStdioConfig').classList.contains('hidden');
258
+ let serverConfig = {};
259
+
260
+ if (isStdio) {
261
+ const command = document.getElementById('mcpModalCommand').value.trim();
262
+ if (!command) {
263
+ showToast(t('mcp.error'), t('mcp.commandRequired'), 'error');
264
+ return;
265
+ }
266
+ serverConfig.command = command;
267
+
268
+ const argsText = document.getElementById('mcpModalArgs').value.trim();
269
+ if (argsText) {
270
+ try {
271
+ serverConfig.args = JSON.parse(argsText);
272
+ if (!Array.isArray(serverConfig.args)) throw new Error('Args must be an array');
273
+ } catch (e) {
274
+ showToast(t('mcp.error'), t('mcp.invalidArgsJson'), 'error');
275
+ return;
276
+ }
277
+ }
278
+
279
+ const envText = document.getElementById('mcpModalEnv').value.trim();
280
+ if (envText) {
281
+ try {
282
+ serverConfig.env = JSON.parse(envText);
283
+ if (typeof serverConfig.env !== 'object' || Array.isArray(serverConfig.env)) throw new Error('Env must be an object');
284
+ } catch (e) {
285
+ showToast(t('mcp.error'), t('mcp.invalidEnvJson'), 'error');
286
+ return;
287
+ }
288
+ }
289
+
290
+ const cwd = document.getElementById('mcpModalCwd').value.trim();
291
+ if (cwd) serverConfig.cwd = cwd;
292
+ } else {
293
+ const url = document.getElementById('mcpModalUrl').value.trim();
294
+ if (!url) {
295
+ showToast(t('mcp.error'), t('mcp.urlRequired'), 'error');
296
+ return;
297
+ }
298
+ serverConfig.url = url;
299
+
300
+ const headersText = document.getElementById('mcpModalHttpHeaders').value.trim();
301
+ if (headersText) {
302
+ try {
303
+ const headers = JSON.parse(headersText);
304
+ if (typeof headers !== 'object' || Array.isArray(headers)) throw new Error('Headers must be an object');
305
+ serverConfig.http_headers = headers;
306
+ } catch (e) {
307
+ showToast(t('mcp.error'), t('mcp.invalidHeadersJson'), 'error');
308
+ return;
309
+ }
310
+ }
311
+ }
312
+
313
+ if (saveAsTemplate && cliMode !== 'codex') {
314
+ try {
315
+ await fetch('/api/mcp-templates', {
316
+ method: 'POST',
317
+ headers: { 'Content-Type': 'application/json' },
318
+ body: JSON.stringify({ name, description: desc, serverConfig, category: 'Custom' })
319
+ });
320
+ } catch (error) {
321
+ console.error('Error saving template:', error);
322
+ }
323
+ }
324
+
325
+ try {
326
+ let endpoint = '';
327
+ let body = {};
328
+
329
+ if (cliMode === 'codex') {
330
+ endpoint = '/api/codex-mcp-add';
331
+ body = { serverName: name, serverConfig };
332
+ } else {
333
+ if (target === 'global') {
334
+ endpoint = '/api/mcp-add-global-server';
335
+ body = { serverName: name, serverConfig };
336
+ } else {
337
+ endpoint = '/api/mcp-copy-server';
338
+ body = { projectPath, serverName: name, serverConfig, configType: 'mcp' };
339
+ }
340
+ }
341
+
342
+ const res = await fetch(endpoint, {
343
+ method: 'POST',
344
+ headers: { 'Content-Type': 'application/json' },
345
+ body: JSON.stringify(body)
346
+ });
347
+
348
+ const data = await res.json();
349
+
350
+ if (data.success || data.serverName) {
351
+ showToast(t('mcp.success'), t('mcp.serverInstalled'), 'success');
352
+ closeMcpEditorModal();
353
+ await loadMcpConfig();
354
+ renderMcpManager();
355
+ } else {
356
+ showToast(t('mcp.error'), data.error || 'Installation failed', 'error');
357
+ }
358
+ } catch (error) {
359
+ console.error('Error installing MCP server:', error);
360
+ showToast(t('mcp.error'), error.message, 'error');
361
+ }
362
+ }
363
+
364
+ // ============================================================
365
+ // MAIN RENDER FUNCTION
366
+ // ============================================================
367
+
368
+ async function renderMcpManager() {
369
+ const container = document.getElementById('mainContent');
370
+ if (!container) return;
371
+
372
+ const statsGrid = document.getElementById('statsGrid');
373
+ const searchInput = document.getElementById('searchInput');
374
+ if (statsGrid) statsGrid.style.display = 'none';
375
+ if (searchInput) searchInput.parentElement.style.display = 'none';
376
+
377
+ if (!mcpConfig) await loadMcpConfig();
378
+ await loadMcpTemplates();
379
+
380
+ const currentPath = projectPath;
381
+ const projectData = mcpAllProjects[currentPath] || {};
382
+ const projectServers = projectData.mcpServers || {};
383
+ const disabledServers = projectData.disabledMcpServers || [];
384
+ const codexServers = codexMcpServers || {};
385
+ const isClaude = currentCliMode === 'claude';
386
+
387
+ // Section 1: Project Available (Enterprise + Global + Project-specific)
388
+ const projectAvailable = [];
389
+
390
+ if (isClaude) {
391
+ // Enterprise servers
392
+ for (const [name, config] of Object.entries(mcpEnterpriseServers || {})) {
393
+ projectAvailable.push({ name, config, source: 'enterprise', enabled: true, canRemove: false, canToggle: false });
394
+ }
395
+ // Global servers
396
+ for (const [name, config] of Object.entries(mcpUserServers || {})) {
397
+ if (!mcpEnterpriseServers?.[name]) {
398
+ projectAvailable.push({ name, config, source: 'global', enabled: !disabledServers.includes(name), canRemove: false, canToggle: true });
399
+ }
400
+ }
401
+ // Project servers
402
+ for (const [name, config] of Object.entries(projectServers)) {
403
+ if (!mcpEnterpriseServers?.[name] && !mcpUserServers?.[name]) {
404
+ projectAvailable.push({ name, config, source: 'project', enabled: !disabledServers.includes(name), canRemove: true, canToggle: true });
405
+ }
406
+ }
407
+ } else {
408
+ // Codex servers
409
+ for (const [name, config] of Object.entries(codexServers)) {
410
+ projectAvailable.push({ name, config, source: 'codex', enabled: config.enabled !== false, canRemove: true, canToggle: true });
411
+ }
412
+ }
413
+
414
+ // Section 2: Global Management (for Claude only)
415
+ const globalManagement = isClaude ? Object.entries(mcpUserServers || {}) : [];
416
+
417
+ // Section 3: Other Projects (for Claude only)
418
+ const allAvailableServers = isClaude ? getAllAvailableMcpServers() : {};
419
+ const currentProjectServerNames = Object.keys(projectServers);
420
+ const otherProjects = isClaude ? Object.entries(allAvailableServers).filter(([name, info]) => !currentProjectServerNames.includes(name) && !info.isGlobal) : [];
421
+
422
+ // Section 4: Cross-CLI servers (Available from other CLI)
423
+ const crossCliServers = [];
424
+ if (isClaude) {
425
+ // Show Codex servers when in Claude mode
426
+ for (const [name, config] of Object.entries(codexServers)) {
427
+ // Check if already exists in Claude (project or global)
428
+ const existsInClaude = currentProjectServerNames.includes(name) || mcpUserServers?.[name];
429
+ if (!existsInClaude) {
430
+ crossCliServers.push({ name, config, fromCli: 'codex' });
431
+ }
432
+ }
433
+ } else {
434
+ // Show Claude servers when in Codex mode
435
+ // Collect all Claude servers (global + project)
436
+ const allClaudeServers = { ...mcpUserServers, ...projectServers };
437
+ for (const [name, config] of Object.entries(allClaudeServers)) {
438
+ // Check if already exists in Codex
439
+ const existsInCodex = codexServers[name];
440
+ if (!existsInCodex) {
441
+ crossCliServers.push({ name, config, fromCli: 'claude' });
442
+ }
443
+ }
444
+ }
445
+
446
+ container.innerHTML = `
447
+ <div class="mcp-manager">
448
+ <!-- CLI Mode Toggle -->
449
+ <div class="mcp-cli-toggle mb-6">
450
+ <div class="flex items-center justify-between bg-card border border-border rounded-lg p-4">
451
+ <div class="flex items-center gap-3">
452
+ <span class="text-sm font-medium text-foreground">${t('mcp.cliMode')}</span>
453
+ <div class="flex items-center bg-muted rounded-lg p-1">
454
+ <button class="cli-mode-btn px-4 py-2 text-sm font-medium rounded-md transition-all ${isClaude ? 'bg-primary text-primary-foreground shadow-sm' : 'text-muted-foreground hover:text-foreground'}"
455
+ onclick="setCliMode('claude')">
456
+ <i data-lucide="bot" class="w-4 h-4 inline mr-1.5"></i>
457
+ Claude
458
+ </button>
459
+ <button class="cli-mode-btn px-4 py-2 text-sm font-medium rounded-md transition-all ${!isClaude ? 'shadow-sm' : 'text-muted-foreground hover:text-foreground'}"
460
+ onclick="setCliMode('codex')"
461
+ style="${!isClaude ? 'background-color: #f97316; color: white;' : ''}">
462
+ <i data-lucide="code-2" class="w-4 h-4 inline mr-1.5"></i>
463
+ Codex
464
+ </button>
465
+ </div>
466
+ </div>
467
+ <div class="flex items-center gap-3">
468
+ <button onclick="renderMcpTemplates()" class="px-4 py-2 text-sm font-medium bg-muted hover:bg-muted/80 text-foreground rounded-lg transition-colors flex items-center gap-2">
469
+ <i data-lucide="bookmark" class="w-4 h-4"></i>
470
+ ${t('mcp.templates')}
471
+ </button>
472
+ <button onclick="showMcpEditorModal({ mode: 'create', cliMode: '${currentCliMode}' })"
473
+ class="px-4 py-2 text-sm font-medium ${isClaude ? 'bg-primary hover:bg-primary/90 text-primary-foreground' : 'bg-orange-500 hover:bg-orange-600 text-white'} rounded-lg transition-colors flex items-center gap-2">
474
+ <i data-lucide="plus-circle" class="w-4 h-4"></i>
475
+ ${t('mcp.newServer')}
476
+ </button>
477
+ </div>
478
+ </div>
479
+ </div>
480
+
481
+ <!-- Section 1: Current Project Available -->
482
+ <div class="mcp-section mb-6">
483
+ <div class="flex items-center justify-between mb-4">
484
+ <h2 class="text-lg font-semibold text-foreground flex items-center gap-2">
485
+ <i data-lucide="folder-check" class="w-5 h-5"></i>
486
+ ${isClaude ? t('mcp.projectAvailable') : 'Codex Global MCP Servers'}
487
+ <span class="text-sm text-muted-foreground font-normal">(${projectAvailable.length})</span>
488
+ </h2>
489
+ </div>
490
+ <div class="space-y-3">
491
+ ${projectAvailable.length === 0 ? `
492
+ <div class="bg-card border border-dashed border-border rounded-lg p-8 text-center">
493
+ <i data-lucide="inbox" class="w-12 h-12 text-muted-foreground mx-auto mb-3"></i>
494
+ <p class="text-sm text-muted-foreground">${isClaude ? t('mcp.noMcpServers') : 'No Codex MCP servers configured'}</p>
495
+ </div>
496
+ ` : projectAvailable.map(server => renderMcpServerCard(server, isClaude ? 'claude' : 'codex')).join('')}
497
+ </div>
498
+ </div>
499
+
500
+ ${isClaude ? `
501
+ <!-- Section 2: Global Management -->
502
+ ${globalManagement.length > 0 ? `
503
+ <div class="mcp-section mb-6">
504
+ <div class="flex items-center justify-between mb-4">
505
+ <h2 class="text-lg font-semibold text-foreground flex items-center gap-2">
506
+ <i data-lucide="globe" class="w-5 h-5"></i>
507
+ ${t('mcp.user')}
508
+ <span class="text-sm text-muted-foreground font-normal">(${globalManagement.length})</span>
509
+ </h2>
510
+ </div>
511
+ <div class="space-y-3">
512
+ ${globalManagement.map(([name, config]) => renderMcpServerCard({ name, config, source: 'global-manage', enabled: true, canRemove: true, canToggle: false }, 'claude')).join('')}
513
+ </div>
514
+ </div>
515
+ ` : ''}
516
+
517
+ <!-- Section 3: Other Projects -->
518
+ ${otherProjects.length > 0 ? `
519
+ <div class="mcp-section mb-6">
520
+ <div class="flex items-center justify-between mb-4">
521
+ <h2 class="text-lg font-semibold text-foreground flex items-center gap-2">
522
+ <i data-lucide="folder-open" class="w-5 h-5"></i>
523
+ ${t('mcp.availableOther')}
524
+ <span class="text-sm text-muted-foreground font-normal">(${otherProjects.length})</span>
525
+ </h2>
526
+ </div>
527
+ <div class="space-y-3">
528
+ ${otherProjects.map(([name, info]) => renderMcpServerCardAvailable(name, info)).join('')}
529
+ </div>
530
+ </div>
531
+ ` : ''}
532
+
533
+ <!-- Section 4: Cross-CLI Servers -->
534
+ ${crossCliServers.length > 0 ? `
535
+ <div class="mcp-section mb-6">
536
+ <div class="flex items-center justify-between mb-4">
537
+ <h2 class="text-lg font-semibold text-foreground flex items-center gap-2">
538
+ ${isClaude ? `
539
+ <i data-lucide="circle-dashed" class="w-5 h-5 text-orange-500"></i>
540
+ ${t('mcp.claude.copyFromCodex')}
541
+ ` : `
542
+ <i data-lucide="circle" class="w-5 h-5 text-blue-500"></i>
543
+ ${t('mcp.codex.copyFromClaude')}
544
+ `}
545
+ <span class="text-sm text-muted-foreground font-normal">(${crossCliServers.length})</span>
546
+ </h2>
547
+ </div>
548
+ <div class="space-y-3">
549
+ ${crossCliServers.map(server => renderCrossCliServerCard(server, isClaude)).join('')}
550
+ </div>
551
+ </div>
552
+ ` : ''}
553
+ ` : ''}
554
+ </div>
555
+ `;
556
+
557
+ if (typeof lucide !== 'undefined') lucide.createIcons();
558
+ }
559
+
560
+ function renderMcpServerCard(server, cliMode) {
561
+ const { name, config, source, enabled, canRemove, canToggle } = server;
562
+
563
+ const sourceInfo = {
564
+ enterprise: { icon: 'shield', color: 'purple', label: 'Enterprise' },
565
+ global: { icon: 'globe', color: 'green', label: 'Global' },
566
+ 'global-manage': { icon: 'globe', color: 'green', label: 'Global' },
567
+ project: { icon: 'folder', color: 'blue', label: 'Project' },
568
+ codex: { icon: 'code-2', color: 'orange', label: 'Codex' }
569
+ };
570
+
571
+ const info = sourceInfo[source] || sourceInfo.project;
572
+ const isStdio = !!config.command;
573
+ const isHttp = !!config.url;
574
+
575
+ return `
576
+ <div class="bg-card border border-border rounded-lg p-4 hover:shadow-md transition-all ${!enabled ? 'opacity-60' : ''}">
577
+ <div class="flex items-start justify-between gap-4">
578
+ <div class="flex items-start gap-3 flex-1 min-w-0">
579
+ <div class="shrink-0 w-10 h-10 rounded-lg bg-${info.color}-500/10 flex items-center justify-center">
580
+ <i data-lucide="${info.icon}" class="w-5 h-5 text-${info.color}-500"></i>
581
+ </div>
582
+ <div class="flex-1 min-w-0">
583
+ <div class="flex items-center gap-2 mb-1">
584
+ <h3 class="font-semibold text-foreground truncate">${name}</h3>
585
+ <span class="inline-flex items-center gap-1 px-2 py-0.5 text-xs font-medium rounded-full bg-${info.color}-500/10 text-${info.color}-600 dark:text-${info.color}-400">
586
+ ${info.label}
587
+ </span>
588
+ ${enabled ? `
589
+ <span class="inline-flex items-center gap-1 px-2 py-0.5 text-xs font-medium rounded-full bg-success/10 text-success">
590
+ <i data-lucide="check" class="w-3 h-3"></i>
591
+ Enabled
592
+ </span>
593
+ ` : `
594
+ <span class="inline-flex items-center gap-1 px-2 py-0.5 text-xs font-medium rounded-full bg-muted text-muted-foreground">
595
+ <i data-lucide="x" class="w-3 h-3"></i>
596
+ Disabled
597
+ </span>
598
+ `}
599
+ </div>
600
+ <div class="text-sm text-muted-foreground space-y-1">
601
+ ${isStdio ? `
602
+ <div class="flex items-center gap-2">
603
+ <i data-lucide="terminal" class="w-3 h-3"></i>
604
+ <code class="text-xs">${config.command} ${(config.args || []).slice(0, 2).join(' ')}</code>
605
+ </div>
606
+ ` : ''}
607
+ ${isHttp ? `
608
+ <div class="flex items-center gap-2">
609
+ <i data-lucide="globe" class="w-3 h-3"></i>
610
+ <code class="text-xs truncate">${config.url}</code>
611
+ </div>
612
+ ` : ''}
613
+ </div>
614
+ </div>
615
+ </div>
616
+ <div class="flex items-center gap-2">
617
+ ${canToggle ? `
618
+ <button onclick="toggleMcpServer('${name}', '${cliMode}', ${!enabled})" class="p-2 rounded-lg hover:bg-muted transition-colors" title="${enabled ? 'Disable' : 'Enable'}">
619
+ <i data-lucide="${enabled ? 'toggle-right' : 'toggle-left'}" class="w-4 h-4 text-${enabled ? 'success' : 'muted-foreground'}"></i>
620
+ </button>
621
+ ` : ''}
622
+ <button onclick="showMcpEditorModal({ mode: 'view', serverName: '${name}', serverConfig: ${JSON.stringify(config).replace(/"/g, '&quot;')}, cliMode: '${cliMode}' })" class="p-2 rounded-lg hover:bg-muted transition-colors">
623
+ <i data-lucide="eye" class="w-4 h-4 text-foreground"></i>
624
+ </button>
625
+ ${canRemove && source !== 'global-manage' ? `
626
+ <button onclick="showMcpEditorModal({ mode: 'edit', serverName: '${name}', serverConfig: ${JSON.stringify(config).replace(/"/g, '&quot;')}, cliMode: '${cliMode}' })" class="p-2 rounded-lg hover:bg-muted transition-colors">
627
+ <i data-lucide="edit-3" class="w-4 h-4 text-foreground"></i>
628
+ </button>
629
+ ` : ''}
630
+ ${canRemove ? `
631
+ <button onclick="deleteMcpServer('${name}', '${source}', '${cliMode}')" class="p-2 rounded-lg hover:bg-destructive/10 transition-colors">
632
+ <i data-lucide="trash-2" class="w-4 h-4 text-destructive"></i>
633
+ </button>
634
+ ` : ''}
635
+ </div>
636
+ </div>
637
+ </div>
638
+ `;
639
+ }
640
+
641
+ function renderMcpServerCardAvailable(name, info) {
642
+ return `
643
+ <div class="bg-card border border-dashed border-border rounded-lg p-4 hover:shadow-md hover:border-solid transition-all">
644
+ <div class="flex items-start justify-between gap-4">
645
+ <div class="flex items-start gap-3 flex-1">
646
+ <div class="shrink-0 w-10 h-10 rounded-lg bg-muted flex items-center justify-center">
647
+ <i data-lucide="folder" class="w-5 h-5 text-muted-foreground"></i>
648
+ </div>
649
+ <div class="flex-1">
650
+ <div class="flex items-center gap-2 mb-1">
651
+ <h3 class="font-semibold text-foreground">${name}</h3>
652
+ <span class="text-xs px-2 py-0.5 bg-muted rounded-full text-muted-foreground">${t('mcp.available')}</span>
653
+ </div>
654
+ <p class="text-xs text-muted-foreground">${t('mcp.from')} ${info.projectName || info.source}</p>
655
+ </div>
656
+ </div>
657
+ <button onclick="installServerFromOther('${name}', ${JSON.stringify(info.config).replace(/"/g, '&quot;')})" class="px-3 py-1.5 text-sm font-medium bg-primary hover:bg-primary/90 text-primary-foreground rounded-lg transition-colors">
658
+ ${t('mcp.addToProject')}
659
+ </button>
660
+ </div>
661
+ </div>
662
+ `;
663
+ }
664
+
665
+ function renderCrossCliServerCard(server, isClaude) {
666
+ const { name, config, fromCli } = server;
667
+ const isStdio = !!config.command;
668
+ const isHttp = !!config.url;
669
+
670
+ // Use solid circle for Claude, dashed circle for Codex
671
+ const icon = fromCli === 'codex' ? 'circle-dashed' : 'circle';
672
+ const iconColor = fromCli === 'codex' ? 'orange' : 'blue';
673
+ const targetCli = isClaude ? 'project' : 'codex';
674
+ const buttonText = isClaude ? t('mcp.codex.copyToClaude') : t('mcp.claude.copyToCodex');
675
+
676
+ return `
677
+ <div class="bg-card border border-dashed border-${iconColor}-200 dark:border-${iconColor}-800 rounded-lg p-4 hover:shadow-md hover:border-solid transition-all">
678
+ <div class="flex items-start justify-between gap-4">
679
+ <div class="flex items-start gap-3 flex-1 min-w-0">
680
+ <div class="shrink-0 w-10 h-10 rounded-full bg-${iconColor}-50 dark:bg-${iconColor}-950/30 flex items-center justify-center">
681
+ <i data-lucide="${icon}" class="w-5 h-5 text-${iconColor}-500"></i>
682
+ </div>
683
+ <div class="flex-1 min-w-0">
684
+ <div class="flex items-center gap-2 mb-1">
685
+ <h3 class="font-semibold text-foreground truncate">${name}</h3>
686
+ <span class="inline-flex items-center gap-1 px-2 py-0.5 text-xs font-medium rounded-full bg-${iconColor}-50 dark:bg-${iconColor}-950/30 text-${iconColor}-600 dark:text-${iconColor}-400">
687
+ <i data-lucide="${icon}" class="w-3 h-3"></i>
688
+ ${fromCli === 'codex' ? 'Codex' : 'Claude'}
689
+ </span>
690
+ </div>
691
+ <div class="text-sm text-muted-foreground space-y-1">
692
+ ${isStdio ? `
693
+ <div class="flex items-center gap-2">
694
+ <i data-lucide="terminal" class="w-3 h-3"></i>
695
+ <code class="text-xs truncate">${config.command} ${(config.args || []).slice(0, 2).join(' ')}</code>
696
+ </div>
697
+ ` : ''}
698
+ ${isHttp ? `
699
+ <div class="flex items-center gap-2">
700
+ <i data-lucide="globe" class="w-3 h-3"></i>
701
+ <code class="text-xs truncate">${config.url}</code>
702
+ </div>
703
+ ` : ''}
704
+ </div>
705
+ </div>
706
+ </div>
707
+ <button onclick="copyCrossCliServer('${name}', ${JSON.stringify(config).replace(/"/g, '&quot;')}, '${fromCli}', '${targetCli}')" class="px-3 py-1.5 text-sm font-medium bg-${iconColor}-500 hover:bg-${iconColor}-600 text-white rounded-lg transition-colors flex items-center gap-1.5">
708
+ <i data-lucide="copy" class="w-3.5 h-3.5"></i>
709
+ ${buttonText}
710
+ </button>
711
+ </div>
712
+ </div>
713
+ `;
714
+ }
715
+
716
+ async function copyCrossCliServer(name, config, fromCli, targetCli) {
717
+ try {
718
+ let endpoint, body;
719
+
720
+ if (targetCli === 'codex') {
721
+ // Copy from Claude to Codex
722
+ endpoint = '/api/codex-mcp-add';
723
+ body = { serverName: name, serverConfig: config };
724
+ } else if (targetCli === 'project') {
725
+ // Copy from Codex to Claude project
726
+ endpoint = '/api/mcp-copy-server';
727
+ body = { projectPath, serverName: name, serverConfig: config, configType: 'mcp' };
728
+ } else if (targetCli === 'global') {
729
+ // Copy to Claude global
730
+ endpoint = '/api/mcp-add-global-server';
731
+ body = { serverName: name, serverConfig: config };
732
+ }
733
+
734
+ const res = await fetch(endpoint, {
735
+ method: 'POST',
736
+ headers: { 'Content-Type': 'application/json' },
737
+ body: JSON.stringify(body)
738
+ });
739
+
740
+ const data = await res.json();
741
+ if (data.success) {
742
+ const targetName = targetCli === 'codex' ? 'Codex' : 'Claude';
743
+ showToast(t('mcp.success'), `${t('mcp.serverInstalled')} (${targetName})`, 'success');
744
+ await loadMcpConfig();
745
+ renderMcpManager();
746
+ } else {
747
+ showToast(t('mcp.error'), data.error, 'error');
748
+ }
749
+ } catch (error) {
750
+ showToast(t('mcp.error'), error.message, 'error');
751
+ }
752
+ }
753
+
754
+ async function installServerFromOther(name, config) {
755
+ try {
756
+ const res = await fetch('/api/mcp-copy-server', {
757
+ method: 'POST',
758
+ headers: { 'Content-Type': 'application/json' },
759
+ body: JSON.stringify({ projectPath, serverName: name, serverConfig: config, configType: 'mcp' })
760
+ });
761
+ const data = await res.json();
762
+ if (data.success) {
763
+ showToast(t('mcp.success'), t('mcp.serverInstalled'), 'success');
764
+ await loadMcpConfig();
765
+ renderMcpManager();
766
+ } else {
767
+ showToast(t('mcp.error'), data.error, 'error');
768
+ }
769
+ } catch (error) {
770
+ showToast(t('mcp.error'), error.message, 'error');
771
+ }
772
+ }
773
+
774
+ async function toggleMcpServer(serverName, cliMode, enable) {
775
+ try {
776
+ let endpoint = cliMode === 'codex' ? '/api/codex-mcp-toggle' : '/api/mcp-toggle';
777
+ let body = cliMode === 'codex' ? { serverName, enabled: enable } : { projectPath, serverName, enable };
778
+
779
+ const res = await fetch(endpoint, {
780
+ method: 'POST',
781
+ headers: { 'Content-Type': 'application/json' },
782
+ body: JSON.stringify(body)
783
+ });
784
+
785
+ const data = await res.json();
786
+ if (data.success || data.serverName) {
787
+ showToast(t('mcp.success'), enable ? t('mcp.serverEnabled') : t('mcp.serverDisabled'), 'success');
788
+ await loadMcpConfig();
789
+ renderMcpManager();
790
+ } else {
791
+ showToast(t('mcp.error'), data.error, 'error');
792
+ }
793
+ } catch (error) {
794
+ showToast(t('mcp.error'), error.message, 'error');
795
+ }
796
+ }
797
+
798
+ async function deleteMcpServer(serverName, source, cliMode) {
799
+ if (!confirm(`Are you sure you want to delete "${serverName}"?`)) return;
800
+
801
+ try {
802
+ let endpoint = '';
803
+ let body = {};
804
+
805
+ if (cliMode === 'codex') {
806
+ endpoint = '/api/codex-mcp-remove';
807
+ body = { serverName };
808
+ } else if (source === 'global-manage') {
809
+ endpoint = '/api/mcp-remove-global-server';
810
+ body = { serverName };
811
+ } else if (source === 'project') {
812
+ endpoint = '/api/mcp-remove-server';
813
+ body = { projectPath, serverName };
814
+ } else {
815
+ endpoint = '/api/mcp-remove-global-server';
816
+ body = { serverName };
817
+ }
818
+
819
+ const res = await fetch(endpoint, {
820
+ method: 'POST',
821
+ headers: { 'Content-Type': 'application/json' },
822
+ body: JSON.stringify(body)
823
+ });
824
+
825
+ const data = await res.json();
826
+ if (data.success || data.removed) {
827
+ showToast(t('mcp.success'), t('mcp.serverDeleted'), 'success');
828
+ await loadMcpConfig();
829
+ renderMcpManager();
830
+ } else {
831
+ showToast(t('mcp.error'), data.error, 'error');
832
+ }
833
+ } catch (error) {
834
+ showToast(t('mcp.error'), error.message, 'error');
835
+ }
836
+ }
837
+
838
+ async function renderMcpTemplates() {
839
+ const container = document.getElementById('mainContent');
840
+ if (!container) return;
841
+
842
+ if (!mcpTemplates || mcpTemplates.length === 0) await loadMcpTemplates();
843
+ const categories = [...new Set(mcpTemplates.map(t => t.category || 'Custom'))];
844
+
845
+ container.innerHTML = `
846
+ <div class="mcp-templates-view">
847
+ <div class="flex items-center justify-between mb-6">
848
+ <div>
849
+ <button onclick="renderMcpManager()" class="text-sm text-primary hover:underline flex items-center gap-1 mb-2">
850
+ <i data-lucide="arrow-left" class="w-4 h-4"></i>
851
+ ${t('mcp.backToManager')}
852
+ </button>
853
+ <h1 class="text-2xl font-bold text-foreground">${t('mcp.templates')}</h1>
854
+ </div>
855
+ </div>
856
+ <div class="space-y-6">
857
+ ${categories.map(category => {
858
+ const templates = mcpTemplates.filter(t => (t.category || 'Custom') === category);
859
+ return `
860
+ <div>
861
+ <h2 class="text-lg font-semibold text-foreground mb-3">${category} (${templates.length})</h2>
862
+ <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
863
+ ${templates.map(template => `
864
+ <div class="bg-card border border-border rounded-lg p-4 hover:shadow-md transition-all">
865
+ <h3 class="font-semibold text-foreground mb-2">${template.name}</h3>
866
+ <p class="text-sm text-muted-foreground mb-4">${template.description || 'No description'}</p>
867
+ <div class="flex gap-2">
868
+ <button onclick="showMcpEditorModal({ mode: 'create', template: ${JSON.stringify(template).replace(/"/g, '&quot;')} })" class="flex-1 px-3 py-2 text-sm bg-primary hover:bg-primary/90 text-primary-foreground rounded-lg">Install</button>
869
+ <button onclick="deleteTemplate('${template.name}')" class="px-3 py-2 text-sm bg-destructive/10 hover:bg-destructive/20 text-destructive rounded-lg">Delete</button>
870
+ </div>
871
+ </div>
872
+ `).join('')}
873
+ </div>
874
+ </div>
875
+ `;
876
+ }).join('')}
877
+ </div>
878
+ </div>
879
+ `;
880
+
881
+ if (typeof lucide !== 'undefined') lucide.createIcons();
882
+ }
883
+
884
+ async function deleteTemplate(name) {
885
+ if (!confirm(`Delete template "${name}"?`)) return;
886
+ try {
887
+ const res = await fetch(`/api/mcp-templates/${encodeURIComponent(name)}`, { method: 'DELETE' });
888
+ const data = await res.json();
889
+ if (data.success) {
890
+ showToast(t('mcp.success'), t('mcp.templateDeleted'), 'success');
891
+ await loadMcpTemplates();
892
+ renderMcpTemplates();
893
+ }
894
+ } catch (error) {
895
+ showToast(t('mcp.error'), error.message, 'error');
896
+ }
897
+ }
898
+
899
+ function showToast(title, message, type = 'info') {
900
+ console.log(`[${type.toUpperCase()}] ${title}: ${message}`);
901
+ if (typeof window.showNotification === 'function') {
902
+ window.showNotification(title, message, type);
903
+ } else {
904
+ alert(`${title}\n${message}`);
905
+ }
906
+ }
907
+
908
+ async function loadMcpTemplates() {
909
+ try {
910
+ const res = await fetch('/api/mcp-templates');
911
+ const data = await res.json();
912
+ if (data.success) mcpTemplates = data.templates || [];
913
+ } catch (error) {
914
+ console.error('Error loading MCP templates:', error);
915
+ mcpTemplates = [];
916
+ }
917
+ }
918
+
919
+ window.renderMcpManager = renderMcpManager;
920
+ window.renderMcpTemplates = renderMcpTemplates;
921
+ window.showMcpEditorModal = showMcpEditorModal;
922
+ window.closeMcpEditorModal = closeMcpEditorModal;
923
+ window.saveMcpFromModal = saveMcpFromModal;
924
+ window.switchMcpServerType = switchMcpServerType;
925
+ window.toggleMcpServer = toggleMcpServer;
926
+ window.deleteMcpServer = deleteMcpServer;
927
+ window.deleteTemplate = deleteTemplate;
928
+ window.installServerFromOther = installServerFromOther;