@cdoing/core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (378) hide show
  1. package/dist/agents/coordinator.d.ts +114 -0
  2. package/dist/agents/coordinator.d.ts.map +1 -0
  3. package/dist/agents/coordinator.js +158 -0
  4. package/dist/agents/coordinator.js.map +1 -0
  5. package/dist/context-providers/clipboard.d.ts +13 -0
  6. package/dist/context-providers/clipboard.d.ts.map +1 -0
  7. package/dist/context-providers/clipboard.js +53 -0
  8. package/dist/context-providers/clipboard.js.map +1 -0
  9. package/dist/context-providers/codebase.d.ts +46 -0
  10. package/dist/context-providers/codebase.d.ts.map +1 -0
  11. package/dist/context-providers/codebase.js +273 -0
  12. package/dist/context-providers/codebase.js.map +1 -0
  13. package/dist/context-providers/diff.d.ts +18 -0
  14. package/dist/context-providers/diff.d.ts.map +1 -0
  15. package/dist/context-providers/diff.js +63 -0
  16. package/dist/context-providers/diff.js.map +1 -0
  17. package/dist/context-providers/docs.d.ts +21 -0
  18. package/dist/context-providers/docs.d.ts.map +1 -0
  19. package/dist/context-providers/docs.js +180 -0
  20. package/dist/context-providers/docs.js.map +1 -0
  21. package/dist/context-providers/file-include.d.ts +13 -0
  22. package/dist/context-providers/file-include.d.ts.map +1 -0
  23. package/dist/context-providers/file-include.js +82 -0
  24. package/dist/context-providers/file-include.js.map +1 -0
  25. package/dist/context-providers/folder.d.ts +19 -0
  26. package/dist/context-providers/folder.d.ts.map +1 -0
  27. package/dist/context-providers/folder.js +130 -0
  28. package/dist/context-providers/folder.js.map +1 -0
  29. package/dist/context-providers/git.d.ts +19 -0
  30. package/dist/context-providers/git.d.ts.map +1 -0
  31. package/dist/context-providers/git.js +74 -0
  32. package/dist/context-providers/git.js.map +1 -0
  33. package/dist/context-providers/index.d.ts +26 -0
  34. package/dist/context-providers/index.d.ts.map +1 -0
  35. package/dist/context-providers/index.js +37 -0
  36. package/dist/context-providers/index.js.map +1 -0
  37. package/dist/context-providers/open-files.d.ts +25 -0
  38. package/dist/context-providers/open-files.d.ts.map +1 -0
  39. package/dist/context-providers/open-files.js +134 -0
  40. package/dist/context-providers/open-files.js.map +1 -0
  41. package/dist/context-providers/problems.d.ts +24 -0
  42. package/dist/context-providers/problems.d.ts.map +1 -0
  43. package/dist/context-providers/problems.js +97 -0
  44. package/dist/context-providers/problems.js.map +1 -0
  45. package/dist/context-providers/registry.d.ts +61 -0
  46. package/dist/context-providers/registry.d.ts.map +1 -0
  47. package/dist/context-providers/registry.js +92 -0
  48. package/dist/context-providers/registry.js.map +1 -0
  49. package/dist/context-providers/terminal.d.ts +25 -0
  50. package/dist/context-providers/terminal.d.ts.map +1 -0
  51. package/dist/context-providers/terminal.js +55 -0
  52. package/dist/context-providers/terminal.js.map +1 -0
  53. package/dist/context-providers/tree.d.ts +29 -0
  54. package/dist/context-providers/tree.d.ts.map +1 -0
  55. package/dist/context-providers/tree.js +172 -0
  56. package/dist/context-providers/tree.js.map +1 -0
  57. package/dist/context-providers/types.d.ts +72 -0
  58. package/dist/context-providers/types.d.ts.map +1 -0
  59. package/dist/context-providers/types.js +10 -0
  60. package/dist/context-providers/types.js.map +1 -0
  61. package/dist/context-providers/url.d.ts +27 -0
  62. package/dist/context-providers/url.d.ts.map +1 -0
  63. package/dist/context-providers/url.js +131 -0
  64. package/dist/context-providers/url.js.map +1 -0
  65. package/dist/effort/index.d.ts +78 -0
  66. package/dist/effort/index.d.ts.map +1 -0
  67. package/dist/effort/index.js +146 -0
  68. package/dist/effort/index.js.map +1 -0
  69. package/dist/hooks/index.d.ts +47 -0
  70. package/dist/hooks/index.d.ts.map +1 -0
  71. package/dist/hooks/index.js +151 -0
  72. package/dist/hooks/index.js.map +1 -0
  73. package/dist/index.d.ts +75 -0
  74. package/dist/index.d.ts.map +1 -0
  75. package/dist/index.js +152 -0
  76. package/dist/index.js.map +1 -0
  77. package/dist/indexing/chunker.d.ts +25 -0
  78. package/dist/indexing/chunker.d.ts.map +1 -0
  79. package/dist/indexing/chunker.js +217 -0
  80. package/dist/indexing/chunker.js.map +1 -0
  81. package/dist/indexing/database.d.ts +49 -0
  82. package/dist/indexing/database.d.ts.map +1 -0
  83. package/dist/indexing/database.js +287 -0
  84. package/dist/indexing/database.js.map +1 -0
  85. package/dist/indexing/index.d.ts +9 -0
  86. package/dist/indexing/index.d.ts.map +1 -0
  87. package/dist/indexing/index.js +13 -0
  88. package/dist/indexing/index.js.map +1 -0
  89. package/dist/indexing/indexer.d.ts +63 -0
  90. package/dist/indexing/indexer.d.ts.map +1 -0
  91. package/dist/indexing/indexer.js +352 -0
  92. package/dist/indexing/indexer.js.map +1 -0
  93. package/dist/indexing/recent-edits-cache.d.ts +77 -0
  94. package/dist/indexing/recent-edits-cache.d.ts.map +1 -0
  95. package/dist/indexing/recent-edits-cache.js +123 -0
  96. package/dist/indexing/recent-edits-cache.js.map +1 -0
  97. package/dist/indexing/types.d.ts +39 -0
  98. package/dist/indexing/types.d.ts.map +1 -0
  99. package/dist/indexing/types.js +6 -0
  100. package/dist/indexing/types.js.map +1 -0
  101. package/dist/mcp/index.d.ts +33 -0
  102. package/dist/mcp/index.d.ts.map +1 -0
  103. package/dist/mcp/index.js +37 -0
  104. package/dist/mcp/index.js.map +1 -0
  105. package/dist/mcp/manager.d.ts +123 -0
  106. package/dist/mcp/manager.d.ts.map +1 -0
  107. package/dist/mcp/manager.js +331 -0
  108. package/dist/mcp/manager.js.map +1 -0
  109. package/dist/oauth.d.ts +33 -0
  110. package/dist/oauth.d.ts.map +1 -0
  111. package/dist/oauth.js +312 -0
  112. package/dist/oauth.js.map +1 -0
  113. package/dist/permissions/index.d.ts +216 -0
  114. package/dist/permissions/index.d.ts.map +1 -0
  115. package/dist/permissions/index.js +938 -0
  116. package/dist/permissions/index.js.map +1 -0
  117. package/dist/plan/index.d.ts +20 -0
  118. package/dist/plan/index.d.ts.map +1 -0
  119. package/dist/plan/index.js +24 -0
  120. package/dist/plan/index.js.map +1 -0
  121. package/dist/plan/manager.d.ts +101 -0
  122. package/dist/plan/manager.d.ts.map +1 -0
  123. package/dist/plan/manager.js +170 -0
  124. package/dist/plan/manager.js.map +1 -0
  125. package/dist/rules/index.d.ts +28 -0
  126. package/dist/rules/index.d.ts.map +1 -0
  127. package/dist/rules/index.js +31 -0
  128. package/dist/rules/index.js.map +1 -0
  129. package/dist/rules/manager.d.ts +77 -0
  130. package/dist/rules/manager.d.ts.map +1 -0
  131. package/dist/rules/manager.js +279 -0
  132. package/dist/rules/manager.js.map +1 -0
  133. package/dist/rules/types.d.ts +34 -0
  134. package/dist/rules/types.d.ts.map +1 -0
  135. package/dist/rules/types.js +9 -0
  136. package/dist/rules/types.js.map +1 -0
  137. package/dist/sandbox/filesystem.d.ts +20 -0
  138. package/dist/sandbox/filesystem.d.ts.map +1 -0
  139. package/dist/sandbox/filesystem.js +141 -0
  140. package/dist/sandbox/filesystem.js.map +1 -0
  141. package/dist/sandbox/index.d.ts +4 -0
  142. package/dist/sandbox/index.d.ts.map +1 -0
  143. package/dist/sandbox/index.js +8 -0
  144. package/dist/sandbox/index.js.map +1 -0
  145. package/dist/sandbox/manager.d.ts +47 -0
  146. package/dist/sandbox/manager.d.ts.map +1 -0
  147. package/dist/sandbox/manager.js +220 -0
  148. package/dist/sandbox/manager.js.map +1 -0
  149. package/dist/sandbox/network.d.ts +14 -0
  150. package/dist/sandbox/network.d.ts.map +1 -0
  151. package/dist/sandbox/network.js +87 -0
  152. package/dist/sandbox/network.js.map +1 -0
  153. package/dist/sandbox/types.d.ts +42 -0
  154. package/dist/sandbox/types.d.ts.map +1 -0
  155. package/dist/sandbox/types.js +25 -0
  156. package/dist/sandbox/types.js.map +1 -0
  157. package/dist/tools/ast-edit.d.ts +57 -0
  158. package/dist/tools/ast-edit.d.ts.map +1 -0
  159. package/dist/tools/ast-edit.js +443 -0
  160. package/dist/tools/ast-edit.js.map +1 -0
  161. package/dist/tools/code-verify.d.ts +8 -0
  162. package/dist/tools/code-verify.d.ts.map +1 -0
  163. package/dist/tools/code-verify.js +159 -0
  164. package/dist/tools/code-verify.js.map +1 -0
  165. package/dist/tools/codebase-search.d.ts +17 -0
  166. package/dist/tools/codebase-search.d.ts.map +1 -0
  167. package/dist/tools/codebase-search.js +104 -0
  168. package/dist/tools/codebase-search.js.map +1 -0
  169. package/dist/tools/file-delete.d.ts +26 -0
  170. package/dist/tools/file-delete.d.ts.map +1 -0
  171. package/dist/tools/file-delete.js +179 -0
  172. package/dist/tools/file-delete.js.map +1 -0
  173. package/dist/tools/file-edit.d.ts +10 -0
  174. package/dist/tools/file-edit.d.ts.map +1 -0
  175. package/dist/tools/file-edit.js +138 -0
  176. package/dist/tools/file-edit.js.map +1 -0
  177. package/dist/tools/file-read.d.ts +12 -0
  178. package/dist/tools/file-read.d.ts.map +1 -0
  179. package/dist/tools/file-read.js +211 -0
  180. package/dist/tools/file-read.js.map +1 -0
  181. package/dist/tools/file-run.d.ts +10 -0
  182. package/dist/tools/file-run.d.ts.map +1 -0
  183. package/dist/tools/file-run.js +179 -0
  184. package/dist/tools/file-run.js.map +1 -0
  185. package/dist/tools/file-write.d.ts +10 -0
  186. package/dist/tools/file-write.d.ts.map +1 -0
  187. package/dist/tools/file-write.js +134 -0
  188. package/dist/tools/file-write.js.map +1 -0
  189. package/dist/tools/glob-search.d.ts +8 -0
  190. package/dist/tools/glob-search.d.ts.map +1 -0
  191. package/dist/tools/glob-search.js +108 -0
  192. package/dist/tools/glob-search.js.map +1 -0
  193. package/dist/tools/grep-search.d.ts +8 -0
  194. package/dist/tools/grep-search.d.ts.map +1 -0
  195. package/dist/tools/grep-search.js +139 -0
  196. package/dist/tools/grep-search.js.map +1 -0
  197. package/dist/tools/list-dir.d.ts +16 -0
  198. package/dist/tools/list-dir.d.ts.map +1 -0
  199. package/dist/tools/list-dir.js +183 -0
  200. package/dist/tools/list-dir.js.map +1 -0
  201. package/dist/tools/multi-edit.d.ts +16 -0
  202. package/dist/tools/multi-edit.d.ts.map +1 -0
  203. package/dist/tools/multi-edit.js +163 -0
  204. package/dist/tools/multi-edit.js.map +1 -0
  205. package/dist/tools/notebook-edit.d.ts +31 -0
  206. package/dist/tools/notebook-edit.d.ts.map +1 -0
  207. package/dist/tools/notebook-edit.js +321 -0
  208. package/dist/tools/notebook-edit.js.map +1 -0
  209. package/dist/tools/registry.d.ts +16 -0
  210. package/dist/tools/registry.d.ts.map +1 -0
  211. package/dist/tools/registry.js +41 -0
  212. package/dist/tools/registry.js.map +1 -0
  213. package/dist/tools/shell-exec.d.ts +12 -0
  214. package/dist/tools/shell-exec.d.ts.map +1 -0
  215. package/dist/tools/shell-exec.js +261 -0
  216. package/dist/tools/shell-exec.js.map +1 -0
  217. package/dist/tools/sub-agent-manager.d.ts +57 -0
  218. package/dist/tools/sub-agent-manager.d.ts.map +1 -0
  219. package/dist/tools/sub-agent-manager.js +153 -0
  220. package/dist/tools/sub-agent-manager.js.map +1 -0
  221. package/dist/tools/sub-agent-status.d.ts +12 -0
  222. package/dist/tools/sub-agent-status.d.ts.map +1 -0
  223. package/dist/tools/sub-agent-status.js +59 -0
  224. package/dist/tools/sub-agent-status.js.map +1 -0
  225. package/dist/tools/sub-agent-terminate.d.ts +12 -0
  226. package/dist/tools/sub-agent-terminate.d.ts.map +1 -0
  227. package/dist/tools/sub-agent-terminate.js +55 -0
  228. package/dist/tools/sub-agent-terminate.js.map +1 -0
  229. package/dist/tools/sub-agent.d.ts +34 -0
  230. package/dist/tools/sub-agent.d.ts.map +1 -0
  231. package/dist/tools/sub-agent.js +140 -0
  232. package/dist/tools/sub-agent.js.map +1 -0
  233. package/dist/tools/system-info.d.ts +24 -0
  234. package/dist/tools/system-info.d.ts.map +1 -0
  235. package/dist/tools/system-info.js +220 -0
  236. package/dist/tools/system-info.js.map +1 -0
  237. package/dist/tools/todo.d.ts +16 -0
  238. package/dist/tools/todo.d.ts.map +1 -0
  239. package/dist/tools/todo.js +144 -0
  240. package/dist/tools/todo.js.map +1 -0
  241. package/dist/tools/types.d.ts +20 -0
  242. package/dist/tools/types.d.ts.map +1 -0
  243. package/dist/tools/types.js +3 -0
  244. package/dist/tools/types.js.map +1 -0
  245. package/dist/tools/view-diff.d.ts +11 -0
  246. package/dist/tools/view-diff.d.ts.map +1 -0
  247. package/dist/tools/view-diff.js +88 -0
  248. package/dist/tools/view-diff.js.map +1 -0
  249. package/dist/tools/view-repo-map.d.ts +18 -0
  250. package/dist/tools/view-repo-map.d.ts.map +1 -0
  251. package/dist/tools/view-repo-map.js +245 -0
  252. package/dist/tools/view-repo-map.js.map +1 -0
  253. package/dist/tools/web-fetch.d.ts +13 -0
  254. package/dist/tools/web-fetch.d.ts.map +1 -0
  255. package/dist/tools/web-fetch.js +106 -0
  256. package/dist/tools/web-fetch.js.map +1 -0
  257. package/dist/tools/web-search.d.ts +10 -0
  258. package/dist/tools/web-search.d.ts.map +1 -0
  259. package/dist/tools/web-search.js +106 -0
  260. package/dist/tools/web-search.js.map +1 -0
  261. package/dist/utils/gitignore.d.ts +10 -0
  262. package/dist/utils/gitignore.d.ts.map +1 -0
  263. package/dist/utils/gitignore.js +104 -0
  264. package/dist/utils/gitignore.js.map +1 -0
  265. package/dist/utils/lazy-apply.d.ts +45 -0
  266. package/dist/utils/lazy-apply.d.ts.map +1 -0
  267. package/dist/utils/lazy-apply.js +164 -0
  268. package/dist/utils/lazy-apply.js.map +1 -0
  269. package/dist/utils/memory.d.ts +36 -0
  270. package/dist/utils/memory.d.ts.map +1 -0
  271. package/dist/utils/memory.js +136 -0
  272. package/dist/utils/memory.js.map +1 -0
  273. package/dist/utils/path-matching.d.ts +24 -0
  274. package/dist/utils/path-matching.d.ts.map +1 -0
  275. package/dist/utils/path-matching.js +116 -0
  276. package/dist/utils/path-matching.js.map +1 -0
  277. package/dist/utils/path-safety.d.ts +13 -0
  278. package/dist/utils/path-safety.d.ts.map +1 -0
  279. package/dist/utils/path-safety.js +54 -0
  280. package/dist/utils/path-safety.js.map +1 -0
  281. package/dist/utils/project-config.d.ts +18 -0
  282. package/dist/utils/project-config.d.ts.map +1 -0
  283. package/dist/utils/project-config.js +76 -0
  284. package/dist/utils/project-config.js.map +1 -0
  285. package/dist/utils/search-match.d.ts +63 -0
  286. package/dist/utils/search-match.d.ts.map +1 -0
  287. package/dist/utils/search-match.js +426 -0
  288. package/dist/utils/search-match.js.map +1 -0
  289. package/dist/utils/shell-paths.d.ts +17 -0
  290. package/dist/utils/shell-paths.d.ts.map +1 -0
  291. package/dist/utils/shell-paths.js +107 -0
  292. package/dist/utils/shell-paths.js.map +1 -0
  293. package/dist/utils/streaming-diff.d.ts +45 -0
  294. package/dist/utils/streaming-diff.d.ts.map +1 -0
  295. package/dist/utils/streaming-diff.js +230 -0
  296. package/dist/utils/streaming-diff.js.map +1 -0
  297. package/dist/utils/todo.d.ts +47 -0
  298. package/dist/utils/todo.d.ts.map +1 -0
  299. package/dist/utils/todo.js +102 -0
  300. package/dist/utils/todo.js.map +1 -0
  301. package/package.json +23 -0
  302. package/src/agents/coordinator.ts +240 -0
  303. package/src/context-providers/clipboard.ts +48 -0
  304. package/src/context-providers/codebase.ts +274 -0
  305. package/src/context-providers/diff.ts +66 -0
  306. package/src/context-providers/docs.ts +160 -0
  307. package/src/context-providers/file-include.ts +54 -0
  308. package/src/context-providers/folder.ts +106 -0
  309. package/src/context-providers/git.ts +72 -0
  310. package/src/context-providers/index.ts +26 -0
  311. package/src/context-providers/open-files.ts +113 -0
  312. package/src/context-providers/problems.ts +100 -0
  313. package/src/context-providers/registry.ts +99 -0
  314. package/src/context-providers/terminal.ts +58 -0
  315. package/src/context-providers/tree.ts +161 -0
  316. package/src/context-providers/types.ts +84 -0
  317. package/src/context-providers/url.ts +138 -0
  318. package/src/effort/index.ts +177 -0
  319. package/src/hooks/index.ts +148 -0
  320. package/src/index.ts +114 -0
  321. package/src/indexing/README.md +267 -0
  322. package/src/indexing/chunker.ts +206 -0
  323. package/src/indexing/database.ts +299 -0
  324. package/src/indexing/index.ts +15 -0
  325. package/src/indexing/indexer.ts +383 -0
  326. package/src/indexing/recent-edits-cache.ts +150 -0
  327. package/src/indexing/types.ts +44 -0
  328. package/src/mcp/index.ts +33 -0
  329. package/src/mcp/manager.ts +385 -0
  330. package/src/oauth.ts +330 -0
  331. package/src/permissions/index.ts +1011 -0
  332. package/src/plan/index.ts +20 -0
  333. package/src/plan/manager.ts +233 -0
  334. package/src/rules/index.ts +28 -0
  335. package/src/rules/manager.ts +276 -0
  336. package/src/rules/types.ts +40 -0
  337. package/src/sandbox/filesystem.ts +135 -0
  338. package/src/sandbox/index.ts +9 -0
  339. package/src/sandbox/manager.ts +213 -0
  340. package/src/sandbox/network.ts +101 -0
  341. package/src/sandbox/types.ts +63 -0
  342. package/src/tools/ast-edit.ts +493 -0
  343. package/src/tools/code-verify.ts +143 -0
  344. package/src/tools/codebase-search.ts +117 -0
  345. package/src/tools/file-delete.ts +155 -0
  346. package/src/tools/file-edit.ts +115 -0
  347. package/src/tools/file-read.ts +195 -0
  348. package/src/tools/file-run.ts +158 -0
  349. package/src/tools/file-write.ts +104 -0
  350. package/src/tools/glob-search.ts +80 -0
  351. package/src/tools/grep-search.ts +120 -0
  352. package/src/tools/list-dir.ts +172 -0
  353. package/src/tools/multi-edit.ts +138 -0
  354. package/src/tools/notebook-edit.ts +342 -0
  355. package/src/tools/registry.ts +43 -0
  356. package/src/tools/shell-exec.ts +251 -0
  357. package/src/tools/sub-agent-manager.ts +183 -0
  358. package/src/tools/sub-agent-status.ts +67 -0
  359. package/src/tools/sub-agent-terminate.ts +62 -0
  360. package/src/tools/sub-agent.ts +162 -0
  361. package/src/tools/system-info.ts +248 -0
  362. package/src/tools/todo.ts +149 -0
  363. package/src/tools/types.ts +21 -0
  364. package/src/tools/view-diff.ts +99 -0
  365. package/src/tools/view-repo-map.ts +249 -0
  366. package/src/tools/web-fetch.ts +118 -0
  367. package/src/tools/web-search.ts +129 -0
  368. package/src/utils/gitignore.ts +73 -0
  369. package/src/utils/lazy-apply.ts +189 -0
  370. package/src/utils/memory.ts +124 -0
  371. package/src/utils/path-matching.ts +84 -0
  372. package/src/utils/path-safety.ts +19 -0
  373. package/src/utils/project-config.ts +41 -0
  374. package/src/utils/search-match.ts +495 -0
  375. package/src/utils/shell-paths.ts +79 -0
  376. package/src/utils/streaming-diff.ts +260 -0
  377. package/src/utils/todo.ts +115 -0
  378. package/tsconfig.json +18 -0
@@ -0,0 +1,150 @@
1
+ /**
2
+ * Recently-Edited LRU Cache — Keeps recently edited files hot for fast retrieval.
3
+ *
4
+ * When the agent edits a file, it's added to this cache. Subsequent codebase
5
+ * searches prioritize cached files, and context providers can include recent
6
+ * edits without re-reading from disk.
7
+ *
8
+ * This matches what Cursor and Windsurf do with their "recently edited" caches
9
+ * to improve relevance of search results and context suggestions.
10
+ */
11
+
12
+ export interface CachedEdit {
13
+ /** Absolute file path */
14
+ filePath: string;
15
+ /** Content at time of last edit */
16
+ content: string;
17
+ /** Timestamp of last edit */
18
+ editedAt: number;
19
+ /** Number of times this file has been edited in the session */
20
+ editCount: number;
21
+ /** Brief description of last edit (e.g., "replaced 2 occurrences") */
22
+ lastEditSummary: string;
23
+ }
24
+
25
+ export class RecentEditsCache {
26
+ private cache = new Map<string, CachedEdit>();
27
+ private readonly maxSize: number;
28
+
29
+ constructor(maxSize: number = 50) {
30
+ this.maxSize = maxSize;
31
+ }
32
+
33
+ /**
34
+ * Record a file edit. Moves the file to the front of the LRU cache.
35
+ */
36
+ put(filePath: string, content: string, editSummary: string = ""): void {
37
+ const existing = this.cache.get(filePath);
38
+
39
+ // Delete first to re-insert at end (Map preserves insertion order)
40
+ if (existing) {
41
+ this.cache.delete(filePath);
42
+ }
43
+
44
+ this.cache.set(filePath, {
45
+ filePath,
46
+ content,
47
+ editedAt: Date.now(),
48
+ editCount: (existing?.editCount || 0) + 1,
49
+ lastEditSummary: editSummary,
50
+ });
51
+
52
+ // Evict oldest if over capacity
53
+ if (this.cache.size > this.maxSize) {
54
+ const oldest = this.cache.keys().next().value;
55
+ if (oldest !== undefined) {
56
+ this.cache.delete(oldest);
57
+ }
58
+ }
59
+ }
60
+
61
+ /**
62
+ * Get a cached file by path. Returns null if not in cache.
63
+ */
64
+ get(filePath: string): CachedEdit | null {
65
+ return this.cache.get(filePath) || null;
66
+ }
67
+
68
+ /**
69
+ * Check if a file is in the cache.
70
+ */
71
+ has(filePath: string): boolean {
72
+ return this.cache.has(filePath);
73
+ }
74
+
75
+ /**
76
+ * Get all cached edits, ordered by most recently edited first.
77
+ */
78
+ getRecent(limit: number = 10): CachedEdit[] {
79
+ const entries = Array.from(this.cache.values());
80
+ // Map preserves insertion order; last inserted = most recent
81
+ return entries.reverse().slice(0, limit);
82
+ }
83
+
84
+ /**
85
+ * Get file paths of recently edited files, most recent first.
86
+ */
87
+ getRecentPaths(limit: number = 10): string[] {
88
+ return this.getRecent(limit).map((e) => e.filePath);
89
+ }
90
+
91
+ /**
92
+ * Get files that match a search query (case-insensitive path match).
93
+ * Recently edited files that match are boosted in search results.
94
+ */
95
+ searchByPath(query: string): CachedEdit[] {
96
+ const lower = query.toLowerCase();
97
+ return Array.from(this.cache.values())
98
+ .filter((e) => e.filePath.toLowerCase().includes(lower))
99
+ .reverse(); // most recent first
100
+ }
101
+
102
+ /**
103
+ * Search cached file contents for a text pattern.
104
+ */
105
+ searchContent(pattern: string): Array<{ entry: CachedEdit; matches: number }> {
106
+ const results: Array<{ entry: CachedEdit; matches: number }> = [];
107
+ const regex = new RegExp(pattern, "gi");
108
+
109
+ for (const entry of this.cache.values()) {
110
+ const matches = (entry.content.match(regex) || []).length;
111
+ if (matches > 0) {
112
+ results.push({ entry, matches });
113
+ }
114
+ }
115
+
116
+ // Sort by match count (descending), then recency
117
+ return results.sort((a, b) => b.matches - a.matches || b.entry.editedAt - a.entry.editedAt);
118
+ }
119
+
120
+ /**
121
+ * Remove a file from the cache.
122
+ */
123
+ remove(filePath: string): boolean {
124
+ return this.cache.delete(filePath);
125
+ }
126
+
127
+ /**
128
+ * Clear the entire cache.
129
+ */
130
+ clear(): void {
131
+ this.cache.clear();
132
+ }
133
+
134
+ /**
135
+ * Get cache statistics.
136
+ */
137
+ stats(): { size: number; maxSize: number; totalEdits: number; oldestEditAt: number | null } {
138
+ let totalEdits = 0;
139
+ let oldestEditAt: number | null = null;
140
+
141
+ for (const entry of this.cache.values()) {
142
+ totalEdits += entry.editCount;
143
+ if (oldestEditAt === null || entry.editedAt < oldestEditAt) {
144
+ oldestEditAt = entry.editedAt;
145
+ }
146
+ }
147
+
148
+ return { size: this.cache.size, maxSize: this.maxSize, totalEdits, oldestEditAt };
149
+ }
150
+ }
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Indexing Types — interfaces for the codebase indexing system.
3
+ */
4
+
5
+ export interface IndexTag {
6
+ directory: string;
7
+ branch: string;
8
+ artifactId: string;
9
+ }
10
+
11
+ export interface ChunkWithMeta {
12
+ content: string;
13
+ path: string;
14
+ startLine: number;
15
+ endLine: number;
16
+ cacheKey: string;
17
+ }
18
+
19
+ export interface SearchResult {
20
+ path: string;
21
+ content: string;
22
+ startLine: number;
23
+ endLine: number;
24
+ score: number;
25
+ source: "fts" | "embedding" | "recent";
26
+ }
27
+
28
+ export interface IndexingProgress {
29
+ phase: string;
30
+ current: number;
31
+ total: number;
32
+ message: string;
33
+ }
34
+
35
+ export type IndexingProgressCallback = (progress: IndexingProgress) => void;
36
+
37
+ export interface IndexStats {
38
+ totalFiles: number;
39
+ totalChunks: number;
40
+ ftsEntries: number;
41
+ embeddingEntries: number;
42
+ lastIndexed: number;
43
+ indexSizeBytes: number;
44
+ }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * MCP (Model Context Protocol) Server Support
3
+ *
4
+ * MCP is an open protocol that lets AI models connect to external
5
+ * data sources and tools. Think of it as "USB for AI" — a standard
6
+ * way to plug in capabilities.
7
+ *
8
+ * How it works:
9
+ * 1. User configures MCP servers in .cdoing/mcp.json or settings
10
+ * 2. On startup, we connect to each configured server
11
+ * 3. Discover available tools from each server
12
+ * 4. Register those tools so the agent can call them
13
+ * 5. Route tool calls through the MCP protocol
14
+ *
15
+ * Configuration format (.cdoing/mcp.json):
16
+ * {
17
+ * "servers": [
18
+ * {
19
+ * "name": "jira",
20
+ * "command": "npx",
21
+ * "args": ["-y", "@modelcontextprotocol/server-jira"],
22
+ * "env": { "JIRA_URL": "https://..." }
23
+ * }
24
+ * ]
25
+ * }
26
+ *
27
+ * Learning note: MCP uses JSON-RPC over stdio or SSE (Server-Sent Events).
28
+ * Each server is a separate process that we spawn and communicate with
29
+ * through its stdin/stdout. This isolation means a buggy MCP server
30
+ * can't crash the main agent.
31
+ */
32
+
33
+ export { McpManager, type McpServerConfig, type McpTool } from "./manager";
@@ -0,0 +1,385 @@
1
+ /**
2
+ * MCP Manager — Manages connections to MCP (Model Context Protocol) servers.
3
+ *
4
+ * Handles:
5
+ * - Loading server configurations
6
+ * - Spawning and connecting to server processes
7
+ * - Tool discovery (listing available tools from each server)
8
+ * - Routing tool calls to the correct server
9
+ * - Graceful shutdown of server processes
10
+ *
11
+ * Learning note: This uses JSON-RPC 2.0 over stdio for communication.
12
+ * Each message is a JSON object separated by newlines. The protocol
13
+ * is stateless — each request gets exactly one response.
14
+ */
15
+
16
+ import * as fs from "fs";
17
+ import * as path from "path";
18
+ import * as os from "os";
19
+ import { spawn, type ChildProcess } from "child_process";
20
+
21
+ /**
22
+ * Configuration for a single MCP server.
23
+ */
24
+ export interface McpServerConfig {
25
+ /** Human-readable name for this server */
26
+ name: string;
27
+
28
+ /** Command to run the server (e.g., "npx", "python") */
29
+ command: string;
30
+
31
+ /** Arguments to pass to the command */
32
+ args?: string[];
33
+
34
+ /** Environment variables for the server process */
35
+ env?: Record<string, string>;
36
+
37
+ /** Working directory for the server */
38
+ cwd?: string;
39
+
40
+ /** Whether this server is enabled (default: true) */
41
+ enabled?: boolean;
42
+ }
43
+
44
+ /**
45
+ * A tool discovered from an MCP server.
46
+ */
47
+ export interface McpTool {
48
+ /** Tool name (namespaced: "server_name.tool_name") */
49
+ name: string;
50
+
51
+ /** Human-readable description */
52
+ description: string;
53
+
54
+ /** JSON Schema for the tool's input parameters */
55
+ inputSchema: Record<string, unknown>;
56
+
57
+ /** Which MCP server provides this tool */
58
+ serverName: string;
59
+ }
60
+
61
+ /**
62
+ * Internal state for a connected MCP server.
63
+ */
64
+ interface McpConnection {
65
+ config: McpServerConfig;
66
+ process: ChildProcess;
67
+ tools: McpTool[];
68
+ requestId: number;
69
+ pendingRequests: Map<number, {
70
+ resolve: (value: unknown) => void;
71
+ reject: (reason: Error) => void;
72
+ }>;
73
+ buffer: string;
74
+ }
75
+
76
+ export class McpManager {
77
+ /** Active server connections */
78
+ private connections = new Map<string, McpConnection>();
79
+
80
+ /** Working directory for resolving config paths */
81
+ private workingDir: string;
82
+
83
+ constructor(workingDir: string) {
84
+ this.workingDir = workingDir;
85
+ }
86
+
87
+ /**
88
+ * Load MCP server configurations from config files.
89
+ *
90
+ * Checks these locations (in order):
91
+ * 1. .cdoing/mcp.json (project-specific)
92
+ * 2. ~/.cdoing/mcp.json (global)
93
+ *
94
+ * Learning note: Project configs take precedence over global ones.
95
+ * This lets you have project-specific MCP servers (e.g., a Jira
96
+ * server for one project, a Notion server for another).
97
+ */
98
+ loadConfig(): McpServerConfig[] {
99
+ const configs: McpServerConfig[] = [];
100
+ const paths = [
101
+ path.join(this.workingDir, ".cdoing", "mcp.json"),
102
+ path.join(os.homedir(), ".cdoing", "mcp.json"),
103
+ ];
104
+
105
+ for (const configPath of paths) {
106
+ try {
107
+ if (fs.existsSync(configPath)) {
108
+ const raw = fs.readFileSync(configPath, "utf-8");
109
+ const parsed = JSON.parse(raw);
110
+
111
+ if (parsed.servers && Array.isArray(parsed.servers)) {
112
+ for (const server of parsed.servers) {
113
+ // Don't add duplicates (project config overrides global)
114
+ if (!configs.some((c) => c.name === server.name)) {
115
+ configs.push(server);
116
+ }
117
+ }
118
+ }
119
+ }
120
+ } catch {
121
+ // Skip invalid config files
122
+ }
123
+ }
124
+
125
+ return configs;
126
+ }
127
+
128
+ /**
129
+ * Connect to all configured MCP servers.
130
+ * Spawns each server process and discovers its tools.
131
+ */
132
+ async connectAll(): Promise<void> {
133
+ const configs = this.loadConfig();
134
+
135
+ for (const config of configs) {
136
+ if (config.enabled === false) continue;
137
+
138
+ try {
139
+ await this.connect(config);
140
+ } catch (err) {
141
+ console.error(`[MCP] Failed to connect to ${config.name}:`, err);
142
+ }
143
+ }
144
+ }
145
+
146
+ /**
147
+ * Connect to a single MCP server.
148
+ *
149
+ * Learning note: We spawn the server as a child process and
150
+ * communicate via JSON-RPC over stdio. The "initialize" handshake
151
+ * tells the server our capabilities, and "tools/list" discovers
152
+ * what tools it offers.
153
+ */
154
+ async connect(config: McpServerConfig): Promise<void> {
155
+ // Spawn the server process
156
+ const child = spawn(config.command, config.args || [], {
157
+ cwd: config.cwd || this.workingDir,
158
+ env: { ...process.env, ...config.env },
159
+ stdio: ["pipe", "pipe", "pipe"],
160
+ });
161
+
162
+ const connection: McpConnection = {
163
+ config,
164
+ process: child,
165
+ tools: [],
166
+ requestId: 0,
167
+ pendingRequests: new Map(),
168
+ buffer: "",
169
+ };
170
+
171
+ // Handle incoming messages from the server
172
+ child.stdout?.on("data", (data: Buffer) => {
173
+ connection.buffer += data.toString();
174
+
175
+ // Process complete JSON-RPC messages (delimited by newlines)
176
+ let newlineIndex: number;
177
+ while ((newlineIndex = connection.buffer.indexOf("\n")) >= 0) {
178
+ const line = connection.buffer.substring(0, newlineIndex).trim();
179
+ connection.buffer = connection.buffer.substring(newlineIndex + 1);
180
+
181
+ if (!line) continue;
182
+
183
+ try {
184
+ const message = JSON.parse(line);
185
+ this.handleServerMessage(config.name, message, connection);
186
+ } catch {
187
+ // Skip non-JSON lines (e.g., server startup logs)
188
+ }
189
+ }
190
+ });
191
+
192
+ // Handle server errors
193
+ child.stderr?.on("data", (data: Buffer) => {
194
+ // Log but don't crash — server stderr is often debug output
195
+ const text = data.toString().trim();
196
+ if (text) {
197
+ console.error(`[MCP:${config.name}] ${text}`);
198
+ }
199
+ });
200
+
201
+ child.on("exit", (code) => {
202
+ this.connections.delete(config.name);
203
+ });
204
+
205
+ this.connections.set(config.name, connection);
206
+
207
+ // Perform MCP handshake
208
+ try {
209
+ await this.sendRequest(config.name, "initialize", {
210
+ protocolVersion: "2024-11-05",
211
+ capabilities: {},
212
+ clientInfo: { name: "cdoing-agent", version: "0.1.0" },
213
+ });
214
+
215
+ // Discover tools
216
+ const toolsResult = await this.sendRequest(config.name, "tools/list", {}) as any;
217
+ if (toolsResult?.tools) {
218
+ connection.tools = toolsResult.tools.map((t: any) => ({
219
+ name: `${config.name}_${t.name}`,
220
+ description: `[${config.name}] ${t.description || t.name}`,
221
+ inputSchema: t.inputSchema || { type: "object", properties: {} },
222
+ serverName: config.name,
223
+ }));
224
+ }
225
+ } catch (err) {
226
+ // If handshake fails, clean up
227
+ child.kill();
228
+ this.connections.delete(config.name);
229
+ throw err;
230
+ }
231
+ }
232
+
233
+ /**
234
+ * Send a JSON-RPC request to an MCP server.
235
+ *
236
+ * Learning note: JSON-RPC uses numeric IDs to match requests
237
+ * with responses. We store pending requests in a Map and resolve
238
+ * the Promise when we get the matching response.
239
+ */
240
+ private sendRequest(serverName: string, method: string, params: unknown): Promise<unknown> {
241
+ const connection = this.connections.get(serverName);
242
+ if (!connection) {
243
+ return Promise.reject(new Error(`MCP server not connected: ${serverName}`));
244
+ }
245
+
246
+ return new Promise((resolve, reject) => {
247
+ const id = ++connection.requestId;
248
+ connection.pendingRequests.set(id, { resolve, reject });
249
+
250
+ const message = JSON.stringify({
251
+ jsonrpc: "2.0",
252
+ id,
253
+ method,
254
+ params,
255
+ });
256
+
257
+ connection.process.stdin?.write(message + "\n");
258
+
259
+ // Timeout after 30 seconds
260
+ setTimeout(() => {
261
+ const pending = connection.pendingRequests.get(id);
262
+ if (pending) {
263
+ connection.pendingRequests.delete(id);
264
+ pending.reject(new Error(`MCP request timeout: ${method}`));
265
+ }
266
+ }, 30000);
267
+ });
268
+ }
269
+
270
+ /**
271
+ * Handle an incoming message from an MCP server.
272
+ */
273
+ private handleServerMessage(
274
+ serverName: string,
275
+ message: any,
276
+ connection: McpConnection,
277
+ ): void {
278
+ // JSON-RPC response (has an "id" field)
279
+ if (message.id !== undefined) {
280
+ const pending = connection.pendingRequests.get(message.id);
281
+ if (pending) {
282
+ connection.pendingRequests.delete(message.id);
283
+ if (message.error) {
284
+ pending.reject(new Error(message.error.message || "MCP error"));
285
+ } else {
286
+ pending.resolve(message.result);
287
+ }
288
+ }
289
+ }
290
+ // JSON-RPC notification (no "id" field) — log but ignore
291
+ }
292
+
293
+ /**
294
+ * Get all tools from all connected MCP servers.
295
+ */
296
+ getAllTools(): McpTool[] {
297
+ const tools: McpTool[] = [];
298
+ for (const connection of this.connections.values()) {
299
+ tools.push(...connection.tools);
300
+ }
301
+ return tools;
302
+ }
303
+
304
+ /**
305
+ * Call a tool on an MCP server.
306
+ *
307
+ * @param toolName - Namespaced tool name (e.g., "jira_create_issue")
308
+ * @param input - Tool input parameters
309
+ * @returns Tool execution result
310
+ */
311
+ async callTool(toolName: string, input: Record<string, unknown>): Promise<{
312
+ success: boolean;
313
+ output: string;
314
+ error?: string;
315
+ }> {
316
+ // Find which server provides this tool
317
+ for (const connection of this.connections.values()) {
318
+ const tool = connection.tools.find((t) => t.name === toolName);
319
+ if (tool) {
320
+ // Strip the server name prefix to get the original tool name
321
+ const originalName = toolName.replace(`${tool.serverName}_`, "");
322
+
323
+ try {
324
+ const result = await this.sendRequest(tool.serverName, "tools/call", {
325
+ name: originalName,
326
+ arguments: input,
327
+ }) as any;
328
+
329
+ // MCP tool results have a "content" array
330
+ const output = result?.content
331
+ ?.map((c: any) => c.text || JSON.stringify(c))
332
+ .join("\n") || JSON.stringify(result);
333
+
334
+ return { success: true, output };
335
+ } catch (err) {
336
+ const message = err instanceof Error ? err.message : String(err);
337
+ return { success: false, output: "", error: message };
338
+ }
339
+ }
340
+ }
341
+
342
+ return { success: false, output: "", error: `Unknown MCP tool: ${toolName}` };
343
+ }
344
+
345
+ /**
346
+ * Check if a tool name belongs to an MCP server.
347
+ */
348
+ isMcpTool(toolName: string): boolean {
349
+ for (const connection of this.connections.values()) {
350
+ if (connection.tools.some((t) => t.name === toolName)) {
351
+ return true;
352
+ }
353
+ }
354
+ return false;
355
+ }
356
+
357
+ /**
358
+ * Get connected server names.
359
+ */
360
+ getConnectedServers(): string[] {
361
+ return Array.from(this.connections.keys());
362
+ }
363
+
364
+ /**
365
+ * Disconnect from all MCP servers.
366
+ * Sends a graceful shutdown signal to each server process.
367
+ */
368
+ async disconnectAll(): Promise<void> {
369
+ for (const [name, connection] of this.connections) {
370
+ try {
371
+ connection.process.kill("SIGTERM");
372
+ } catch {
373
+ // Process may already be dead
374
+ }
375
+ }
376
+ this.connections.clear();
377
+ }
378
+
379
+ /**
380
+ * Update the working directory.
381
+ */
382
+ setWorkingDir(dir: string): void {
383
+ this.workingDir = dir;
384
+ }
385
+ }