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,41 @@
1
+ import { z } from 'zod';
2
+
3
+ // Tool parameter schema for Zod validation
4
+ export const ToolParamSchema = z.object({
5
+ name: z.string(),
6
+ type: z.enum(['string', 'number', 'boolean', 'object', 'array']),
7
+ description: z.string(),
8
+ required: z.boolean().default(false),
9
+ default: z.any().optional(),
10
+ enum: z.array(z.string()).optional(),
11
+ });
12
+
13
+ export type ToolParam = z.infer<typeof ToolParamSchema>;
14
+
15
+ // Tool Schema definition (MCP compatible)
16
+ export interface ToolSchema {
17
+ name: string;
18
+ description: string;
19
+ inputSchema: {
20
+ type: 'object';
21
+ properties: Record<string, unknown>;
22
+ required?: string[];
23
+ };
24
+ }
25
+
26
+ // Tool execution result
27
+ export interface ToolResult<T = unknown> {
28
+ success: boolean;
29
+ result?: T;
30
+ error?: string;
31
+ }
32
+
33
+ // Tool handler function type
34
+ export type ToolHandler<TParams = Record<string, unknown>, TResult = unknown> =
35
+ (params: TParams) => Promise<ToolResult<TResult>>;
36
+
37
+ // Tool registration entry
38
+ export interface ToolRegistration<TParams = Record<string, unknown>> {
39
+ schema: ToolSchema;
40
+ handler: ToolHandler<TParams>;
41
+ }
@@ -5,17 +5,18 @@ import { resolve } from 'path';
5
5
  /**
6
6
  * Launch a URL or file in the default browser
7
7
  * Cross-platform compatible (Windows/macOS/Linux)
8
- * @param {string} urlOrPath - HTTP URL or path to HTML file
9
- * @returns {Promise<void>}
8
+ * @param urlOrPath - HTTP URL or path to HTML file
9
+ * @returns Promise that resolves when browser is launched
10
10
  */
11
- export async function launchBrowser(urlOrPath) {
11
+ export async function launchBrowser(urlOrPath: string): Promise<void> {
12
12
  // Check if it's already a URL (http:// or https://)
13
13
  if (urlOrPath.startsWith('http://') || urlOrPath.startsWith('https://')) {
14
14
  try {
15
15
  await open(urlOrPath);
16
16
  return;
17
17
  } catch (error) {
18
- throw new Error(`Failed to open browser: ${error.message}`);
18
+ const message = error instanceof Error ? error.message : String(error);
19
+ throw new Error(`Failed to open browser: ${message}`);
19
20
  }
20
21
  }
21
22
 
@@ -23,7 +24,7 @@ export async function launchBrowser(urlOrPath) {
23
24
  const absolutePath = resolve(urlOrPath);
24
25
 
25
26
  // Construct file:// URL based on platform
26
- let url;
27
+ let url: string;
27
28
  if (platform() === 'win32') {
28
29
  // Windows: file:///C:/path/to/file.html
29
30
  url = `file:///${absolutePath.replace(/\\/g, '/')}`;
@@ -40,16 +41,17 @@ export async function launchBrowser(urlOrPath) {
40
41
  try {
41
42
  await open(absolutePath);
42
43
  } catch (fallbackError) {
43
- throw new Error(`Failed to open browser: ${error.message}`);
44
+ const message = error instanceof Error ? error.message : String(error);
45
+ throw new Error(`Failed to open browser: ${message}`);
44
46
  }
45
47
  }
46
48
  }
47
49
 
48
50
  /**
49
51
  * Check if we're running in a headless/CI environment
50
- * @returns {boolean}
52
+ * @returns True if running in headless environment
51
53
  */
52
- export function isHeadlessEnvironment() {
54
+ export function isHeadlessEnvironment(): boolean {
53
55
  return !!(
54
56
  process.env.CI ||
55
57
  process.env.CONTINUOUS_INTEGRATION ||
@@ -0,0 +1,48 @@
1
+ import { readFileSync, existsSync, writeFileSync } from 'fs';
2
+ import { join } from 'path';
3
+
4
+ /**
5
+ * Safely read a JSON file
6
+ * @param filePath - Path to JSON file
7
+ * @returns Parsed JSON or null on error
8
+ */
9
+ export function readJsonFile(filePath: string): unknown | null {
10
+ if (!existsSync(filePath)) return null;
11
+ try {
12
+ return JSON.parse(readFileSync(filePath, 'utf8'));
13
+ } catch {
14
+ return null;
15
+ }
16
+ }
17
+
18
+ /**
19
+ * Safely read a text file
20
+ * @param filePath - Path to text file
21
+ * @returns File contents or null on error
22
+ */
23
+ export function readTextFile(filePath: string): string | null {
24
+ if (!existsSync(filePath)) return null;
25
+ try {
26
+ return readFileSync(filePath, 'utf8');
27
+ } catch {
28
+ return null;
29
+ }
30
+ }
31
+
32
+ /**
33
+ * Write content to a file
34
+ * @param filePath - Path to file
35
+ * @param content - Content to write
36
+ */
37
+ export function writeTextFile(filePath: string, content: string): void {
38
+ writeFileSync(filePath, content, 'utf8');
39
+ }
40
+
41
+ /**
42
+ * Check if a path exists
43
+ * @param filePath - Path to check
44
+ * @returns True if path exists
45
+ */
46
+ export function pathExists(filePath: string): boolean {
47
+ return existsSync(filePath);
48
+ }
@@ -1,13 +1,32 @@
1
1
  import { resolve, join, relative, isAbsolute } from 'path';
2
2
  import { existsSync, mkdirSync, realpathSync, statSync, readFileSync, writeFileSync } from 'fs';
3
3
  import { homedir } from 'os';
4
+ import { StoragePaths, ensureStorageDir, LegacyPaths } from '../config/storage-paths.js';
5
+
6
+ /**
7
+ * Validation result for path operations
8
+ */
9
+ export interface PathValidationResult {
10
+ valid: boolean;
11
+ path: string | null;
12
+ error: string | null;
13
+ }
14
+
15
+ /**
16
+ * Options for path validation
17
+ */
18
+ export interface ValidatePathOptions {
19
+ baseDir?: string | null;
20
+ mustExist?: boolean;
21
+ allowHome?: boolean;
22
+ }
4
23
 
5
24
  /**
6
25
  * Resolve a path, handling ~ for home directory
7
- * @param {string} inputPath - Path to resolve
8
- * @returns {string} - Absolute path
26
+ * @param inputPath - Path to resolve
27
+ * @returns Absolute path
9
28
  */
10
- export function resolvePath(inputPath) {
29
+ export function resolvePath(inputPath: string): string {
11
30
  if (!inputPath) return process.cwd();
12
31
 
13
32
  // Handle ~ for home directory
@@ -21,14 +40,11 @@ export function resolvePath(inputPath) {
21
40
  /**
22
41
  * Validate and sanitize a user-provided path
23
42
  * Prevents path traversal attacks and validates path is within allowed boundaries
24
- * @param {string} inputPath - User-provided path
25
- * @param {Object} options - Validation options
26
- * @param {string} options.baseDir - Base directory to restrict paths within (optional)
27
- * @param {boolean} options.mustExist - Whether path must exist (default: false)
28
- * @param {boolean} options.allowHome - Whether to allow home directory paths (default: true)
29
- * @returns {Object} - { valid: boolean, path: string|null, error: string|null }
43
+ * @param inputPath - User-provided path
44
+ * @param options - Validation options
45
+ * @returns Validation result with path or error
30
46
  */
31
- export function validatePath(inputPath, options = {}) {
47
+ export function validatePath(inputPath: string, options: ValidatePathOptions = {}): PathValidationResult {
32
48
  const { baseDir = null, mustExist = false, allowHome = true } = options;
33
49
 
34
50
  // Check for empty/null input
@@ -45,11 +61,12 @@ export function validatePath(inputPath, options = {}) {
45
61
  }
46
62
 
47
63
  // Resolve the path
48
- let resolvedPath;
64
+ let resolvedPath: string;
49
65
  try {
50
66
  resolvedPath = resolvePath(trimmedPath);
51
67
  } catch (err) {
52
- return { valid: false, path: null, error: `Invalid path: ${err.message}` };
68
+ const message = err instanceof Error ? err.message : String(err);
69
+ return { valid: false, path: null, error: `Invalid path: ${message}` };
53
70
  }
54
71
 
55
72
  // Check if path exists when required
@@ -63,7 +80,8 @@ export function validatePath(inputPath, options = {}) {
63
80
  try {
64
81
  realPath = realpathSync(resolvedPath);
65
82
  } catch (err) {
66
- return { valid: false, path: null, error: `Cannot resolve path: ${err.message}` };
83
+ const message = err instanceof Error ? err.message : String(err);
84
+ return { valid: false, path: null, error: `Cannot resolve path: ${message}` };
67
85
  }
68
86
  }
69
87
 
@@ -95,11 +113,11 @@ export function validatePath(inputPath, options = {}) {
95
113
 
96
114
  /**
97
115
  * Validate output file path for writing
98
- * @param {string} outputPath - Output file path
99
- * @param {string} defaultDir - Default directory if path is relative
100
- * @returns {Object} - { valid: boolean, path: string|null, error: string|null }
116
+ * @param outputPath - Output file path
117
+ * @param defaultDir - Default directory if path is relative
118
+ * @returns Validation result with path or error
101
119
  */
102
- export function validateOutputPath(outputPath, defaultDir = process.cwd()) {
120
+ export function validateOutputPath(outputPath: string, defaultDir: string = process.cwd()): PathValidationResult {
103
121
  if (!outputPath || typeof outputPath !== 'string') {
104
122
  return { valid: false, path: null, error: 'Output path is required' };
105
123
  }
@@ -112,12 +130,13 @@ export function validateOutputPath(outputPath, defaultDir = process.cwd()) {
112
130
  }
113
131
 
114
132
  // Resolve the path
115
- let resolvedPath;
133
+ let resolvedPath: string;
116
134
  try {
117
135
  resolvedPath = isAbsolute(trimmedPath) ? trimmedPath : join(defaultDir, trimmedPath);
118
136
  resolvedPath = resolve(resolvedPath);
119
137
  } catch (err) {
120
- return { valid: false, path: null, error: `Invalid output path: ${err.message}` };
138
+ const message = err instanceof Error ? err.message : String(err);
139
+ return { valid: false, path: null, error: `Invalid output path: ${message}` };
121
140
  }
122
141
 
123
142
  // Ensure it's not a directory
@@ -137,9 +156,9 @@ export function validateOutputPath(outputPath, defaultDir = process.cwd()) {
137
156
 
138
157
  /**
139
158
  * Get potential template locations
140
- * @returns {string[]} - Array of existing template directories
159
+ * @returns Array of existing template directories
141
160
  */
142
- export function getTemplateLocations() {
161
+ export function getTemplateLocations(): string[] {
143
162
  const locations = [
144
163
  join(homedir(), '.claude', 'templates'),
145
164
  join(process.cwd(), '.claude', 'templates')
@@ -150,10 +169,10 @@ export function getTemplateLocations() {
150
169
 
151
170
  /**
152
171
  * Find a template file in known locations
153
- * @param {string} templateName - Name of template file (e.g., 'workflow-dashboard.html')
154
- * @returns {string|null} - Path to template or null if not found
172
+ * @param templateName - Name of template file (e.g., 'workflow-dashboard.html')
173
+ * @returns Path to template or null if not found
155
174
  */
156
- export function findTemplate(templateName) {
175
+ export function findTemplate(templateName: string): string | null {
157
176
  const locations = getTemplateLocations();
158
177
 
159
178
  for (const loc of locations) {
@@ -168,45 +187,58 @@ export function findTemplate(templateName) {
168
187
 
169
188
  /**
170
189
  * Ensure directory exists, creating if necessary
171
- * @param {string} dirPath - Directory path to ensure
190
+ * @param dirPath - Directory path to ensure
172
191
  */
173
- export function ensureDir(dirPath) {
192
+ export function ensureDir(dirPath: string): void {
174
193
  if (!existsSync(dirPath)) {
175
194
  mkdirSync(dirPath, { recursive: true });
176
195
  }
177
196
  }
178
197
 
179
198
  /**
180
- * Get the .workflow directory path from project path
181
- * @param {string} projectPath - Path to project
182
- * @returns {string} - Path to .workflow directory
199
+ * Normalize path for display (handle Windows backslashes)
200
+ * @param filePath - Path to normalize
201
+ * @returns Normalized path with forward slashes
183
202
  */
184
- export function getWorkflowDir(projectPath) {
185
- return join(resolvePath(projectPath), '.workflow');
203
+ export function normalizePathForDisplay(filePath: string): string {
204
+ return filePath.replace(/\\/g, '/');
186
205
  }
187
206
 
207
+ // Recent paths storage - uses centralized storage with backward compatibility
208
+ const MAX_RECENT_PATHS = 10;
209
+
188
210
  /**
189
- * Normalize path for display (handle Windows backslashes)
190
- * @param {string} filePath - Path to normalize
191
- * @returns {string}
211
+ * Get the recent paths file location
212
+ * Uses new location but falls back to legacy location for backward compatibility
192
213
  */
193
- export function normalizePathForDisplay(filePath) {
194
- return filePath.replace(/\\/g, '/');
214
+ function getRecentPathsFile(): string {
215
+ const newPath = StoragePaths.global.recentPaths();
216
+ const legacyPath = LegacyPaths.recentPaths();
217
+
218
+ // Backward compatibility: use legacy if it exists and new doesn't
219
+ if (!existsSync(newPath) && existsSync(legacyPath)) {
220
+ return legacyPath;
221
+ }
222
+ return newPath;
195
223
  }
196
224
 
197
- // Recent paths storage file
198
- const RECENT_PATHS_FILE = join(homedir(), '.ccw-recent-paths.json');
199
- const MAX_RECENT_PATHS = 10;
225
+ /**
226
+ * Recent paths data structure
227
+ */
228
+ interface RecentPathsData {
229
+ paths: string[];
230
+ }
200
231
 
201
232
  /**
202
233
  * Get recent project paths
203
- * @returns {string[]} - Array of recent paths
234
+ * @returns Array of recent paths
204
235
  */
205
- export function getRecentPaths() {
236
+ export function getRecentPaths(): string[] {
206
237
  try {
207
- if (existsSync(RECENT_PATHS_FILE)) {
208
- const content = readFileSync(RECENT_PATHS_FILE, 'utf8');
209
- const data = JSON.parse(content);
238
+ const recentPathsFile = getRecentPathsFile();
239
+ if (existsSync(recentPathsFile)) {
240
+ const content = readFileSync(recentPathsFile, 'utf8');
241
+ const data = JSON.parse(content) as RecentPathsData;
210
242
  return Array.isArray(data.paths) ? data.paths : [];
211
243
  }
212
244
  } catch {
@@ -217,9 +249,9 @@ export function getRecentPaths() {
217
249
 
218
250
  /**
219
251
  * Track a project path (add to recent paths)
220
- * @param {string} projectPath - Path to track
252
+ * @param projectPath - Path to track
221
253
  */
222
- export function trackRecentPath(projectPath) {
254
+ export function trackRecentPath(projectPath: string): void {
223
255
  try {
224
256
  const normalized = normalizePathForDisplay(resolvePath(projectPath));
225
257
  let paths = getRecentPaths();
@@ -233,8 +265,10 @@ export function trackRecentPath(projectPath) {
233
265
  // Limit to max
234
266
  paths = paths.slice(0, MAX_RECENT_PATHS);
235
267
 
236
- // Save
237
- writeFileSync(RECENT_PATHS_FILE, JSON.stringify({ paths }, null, 2), 'utf8');
268
+ // Save to new centralized location
269
+ const recentPathsFile = StoragePaths.global.recentPaths();
270
+ ensureStorageDir(StoragePaths.global.config());
271
+ writeFileSync(recentPathsFile, JSON.stringify({ paths }, null, 2), 'utf8');
238
272
  } catch {
239
273
  // Ignore errors
240
274
  }
@@ -243,37 +277,39 @@ export function trackRecentPath(projectPath) {
243
277
  /**
244
278
  * Clear recent paths
245
279
  */
246
- export function clearRecentPaths() {
280
+ export function clearRecentPaths(): void {
247
281
  try {
248
- if (existsSync(RECENT_PATHS_FILE)) {
249
- writeFileSync(RECENT_PATHS_FILE, JSON.stringify({ paths: [] }, null, 2), 'utf8');
250
- }
282
+ const recentPathsFile = StoragePaths.global.recentPaths();
283
+ ensureStorageDir(StoragePaths.global.config());
284
+ writeFileSync(recentPathsFile, JSON.stringify({ paths: [] }, null, 2), 'utf8');
251
285
  } catch {
252
286
  // Ignore errors
253
287
  }
254
- }
255
-
256
- /**
257
- * Remove a specific path from recent paths
258
- * @param {string} pathToRemove - Path to remove
259
- * @returns {boolean} - True if removed, false if not found
260
- */
261
- export function removeRecentPath(pathToRemove) {
262
- try {
263
- const normalized = normalizePathForDisplay(resolvePath(pathToRemove));
264
- let paths = getRecentPaths();
265
- const originalLength = paths.length;
266
-
267
- // Filter out the path to remove
268
- paths = paths.filter(p => normalizePathForDisplay(p) !== normalized);
269
-
270
- if (paths.length < originalLength) {
271
- // Save updated list
272
- writeFileSync(RECENT_PATHS_FILE, JSON.stringify({ paths }, null, 2), 'utf8');
273
- return true;
274
- }
275
- return false;
276
- } catch {
277
- return false;
278
- }
279
- }
288
+ }
289
+
290
+ /**
291
+ * Remove a specific path from recent paths
292
+ * @param pathToRemove - Path to remove
293
+ * @returns True if removed, false if not found
294
+ */
295
+ export function removeRecentPath(pathToRemove: string): boolean {
296
+ try {
297
+ const normalized = normalizePathForDisplay(resolvePath(pathToRemove));
298
+ let paths = getRecentPaths();
299
+ const originalLength = paths.length;
300
+
301
+ // Filter out the path to remove
302
+ paths = paths.filter(p => normalizePathForDisplay(p) !== normalized);
303
+
304
+ if (paths.length < originalLength) {
305
+ // Save updated list to new centralized location
306
+ const recentPathsFile = StoragePaths.global.recentPaths();
307
+ ensureStorageDir(StoragePaths.global.config());
308
+ writeFileSync(recentPathsFile, JSON.stringify({ paths }, null, 2), 'utf8');
309
+ return true;
310
+ }
311
+ return false;
312
+ } catch {
313
+ return false;
314
+ }
315
+ }
@@ -0,0 +1,153 @@
1
+ /**
2
+ * Centralized Path Validation Utility
3
+ *
4
+ * Provides secure path validation and resolution for MCP tools.
5
+ * Prevents path traversal attacks and ensures operations stay within allowed directories.
6
+ *
7
+ * Inspired by MCP filesystem server's security model.
8
+ */
9
+
10
+ import { resolve, isAbsolute, normalize, relative } from 'path';
11
+ import { realpath, access } from 'fs/promises';
12
+ import { constants } from 'fs';
13
+
14
+ // Environment variable configuration
15
+ const ENV_PROJECT_ROOT = 'CCW_PROJECT_ROOT';
16
+ const ENV_ALLOWED_DIRS = 'CCW_ALLOWED_DIRS';
17
+
18
+ /**
19
+ * Get project root directory
20
+ * Priority: CCW_PROJECT_ROOT > process.cwd()
21
+ */
22
+ export function getProjectRoot(): string {
23
+ return process.env[ENV_PROJECT_ROOT] || process.cwd();
24
+ }
25
+
26
+ /**
27
+ * Get allowed directories list
28
+ * Priority: CCW_ALLOWED_DIRS > [getProjectRoot()]
29
+ */
30
+ export function getAllowedDirectories(): string[] {
31
+ const envDirs = process.env[ENV_ALLOWED_DIRS];
32
+ if (envDirs) {
33
+ return envDirs.split(',').map(d => d.trim()).filter(Boolean);
34
+ }
35
+ return [getProjectRoot()];
36
+ }
37
+
38
+ /**
39
+ * Normalize path (unify separators to forward slash)
40
+ */
41
+ export function normalizePath(p: string): string {
42
+ return normalize(p).replace(/\\/g, '/');
43
+ }
44
+
45
+ /**
46
+ * Check if path is within allowed directories
47
+ */
48
+ export function isPathWithinAllowedDirectories(
49
+ targetPath: string,
50
+ allowedDirectories: string[]
51
+ ): boolean {
52
+ const normalizedTarget = normalizePath(targetPath);
53
+ return allowedDirectories.some(dir => {
54
+ const normalizedDir = normalizePath(dir);
55
+ // Check if path equals or starts with allowed directory
56
+ return normalizedTarget === normalizedDir ||
57
+ normalizedTarget.startsWith(normalizedDir + '/');
58
+ });
59
+ }
60
+
61
+ /**
62
+ * Validate and resolve path (core function)
63
+ *
64
+ * Security model:
65
+ * 1. Resolve to absolute path
66
+ * 2. Check against allowed directories
67
+ * 3. Resolve symlinks and re-verify
68
+ *
69
+ * @param filePath - Path to validate
70
+ * @param options - Validation options
71
+ * @returns Validated absolute path
72
+ * @throws Error if path is outside allowed directories or validation fails
73
+ */
74
+ export async function validatePath(
75
+ filePath: string,
76
+ options: {
77
+ allowedDirectories?: string[];
78
+ mustExist?: boolean;
79
+ } = {}
80
+ ): Promise<string> {
81
+ const allowedDirs = options.allowedDirectories || getAllowedDirectories();
82
+
83
+ // 1. Resolve to absolute path
84
+ const absolutePath = isAbsolute(filePath)
85
+ ? filePath
86
+ : resolve(getProjectRoot(), filePath);
87
+ const normalizedPath = normalizePath(absolutePath);
88
+
89
+ // 2. Initial sandbox check
90
+ if (!isPathWithinAllowedDirectories(normalizedPath, allowedDirs)) {
91
+ throw new Error(
92
+ `Access denied: path "${normalizedPath}" is outside allowed directories. ` +
93
+ `Allowed: [${allowedDirs.join(', ')}]`
94
+ );
95
+ }
96
+
97
+ // 3. Try to resolve symlinks and re-verify
98
+ try {
99
+ const realPath = await realpath(absolutePath);
100
+ const normalizedReal = normalizePath(realPath);
101
+
102
+ if (!isPathWithinAllowedDirectories(normalizedReal, allowedDirs)) {
103
+ throw new Error(
104
+ `Access denied: symlink target "${normalizedReal}" is outside allowed directories`
105
+ );
106
+ }
107
+
108
+ return normalizedReal;
109
+ } catch (error: any) {
110
+ // File doesn't exist - validate parent directory
111
+ if (error.code === 'ENOENT') {
112
+ if (options.mustExist) {
113
+ throw new Error(`File not found: ${absolutePath}`);
114
+ }
115
+
116
+ // Validate parent directory's real path
117
+ const parentDir = resolve(absolutePath, '..');
118
+ try {
119
+ const realParent = await realpath(parentDir);
120
+ const normalizedParent = normalizePath(realParent);
121
+
122
+ if (!isPathWithinAllowedDirectories(normalizedParent, allowedDirs)) {
123
+ throw new Error(
124
+ `Access denied: parent directory "${normalizedParent}" is outside allowed directories`
125
+ );
126
+ }
127
+ } catch (parentError: any) {
128
+ if (parentError.code === 'ENOENT') {
129
+ // Parent directory doesn't exist either - return original absolute path
130
+ // Let the caller create it if needed
131
+ return absolutePath;
132
+ }
133
+ throw parentError;
134
+ }
135
+
136
+ return absolutePath;
137
+ }
138
+
139
+ // Re-throw access denied errors
140
+ if (error.message?.includes('Access denied')) {
141
+ throw error;
142
+ }
143
+ throw error;
144
+ }
145
+ }
146
+
147
+ /**
148
+ * Resolve project-relative path (simplified, no strict validation)
149
+ * Use for cases where strict security validation is not needed
150
+ */
151
+ export function resolveProjectPath(...pathSegments: string[]): string {
152
+ return resolve(getProjectRoot(), ...pathSegments);
153
+ }