@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,48 @@
1
+ import { execSync } from "child_process";
2
+ import type { ContextProvider, ContextResult, ContextResolveOptions } from "./types";
3
+
4
+ /**
5
+ * @clip — pastes clipboard content into the prompt.
6
+ * Works on macOS (pbpaste), Linux with xclip/xsel/wl-paste.
7
+ */
8
+ export class ClipboardContextProvider implements ContextProvider {
9
+ id = "clipboard";
10
+ trigger = "@clip";
11
+ description = "Paste clipboard content into the message";
12
+ requiresArg = false;
13
+
14
+ async resolve(_arg?: string, _opts?: ContextResolveOptions): Promise<ContextResult> {
15
+ let content = "";
16
+
17
+ try {
18
+ const platform = process.platform;
19
+ if (platform === "darwin") {
20
+ content = execSync("pbpaste", { encoding: "utf8" });
21
+ } else if (platform === "linux") {
22
+ // Try wl-paste (Wayland), then xclip, then xsel
23
+ try {
24
+ content = execSync("wl-paste --no-newline 2>/dev/null", { encoding: "utf8" });
25
+ } catch {
26
+ try {
27
+ content = execSync("xclip -selection clipboard -o 2>/dev/null", { encoding: "utf8" });
28
+ } catch {
29
+ content = execSync("xsel --clipboard --output 2>/dev/null", { encoding: "utf8" });
30
+ }
31
+ }
32
+ } else {
33
+ return { label: "Clipboard", content: "", metadata: { source: "unsupported platform" } };
34
+ }
35
+ } catch {
36
+ return { label: "Clipboard", content: "(clipboard is empty or unavailable)" };
37
+ }
38
+
39
+ const trimmed = content.trim();
40
+ if (!trimmed) return { label: "Clipboard", content: "(clipboard is empty)" };
41
+
42
+ return {
43
+ label: "Clipboard",
44
+ content: `<clipboard>\n${trimmed}\n</clipboard>`,
45
+ metadata: { source: "system clipboard", itemCount: trimmed.split("\n").length },
46
+ };
47
+ }
48
+ }
@@ -0,0 +1,274 @@
1
+ /**
2
+ * Codebase Context Provider — @codebase
3
+ *
4
+ * Semantic search across the entire repository.
5
+ * Finds the most relevant files/code for a given query.
6
+ *
7
+ * How it works:
8
+ * 1. User types @codebase followed by a search query
9
+ * 2. We search file names and content using glob + grep
10
+ * 3. Rank results by relevance (filename match > content match)
11
+ * 4. Return the top N most relevant code snippets
12
+ *
13
+ * Learning note: This is a text-based search approach (not embedding-based).
14
+ * For true semantic search, you'd want vector embeddings + a vector DB.
15
+ * But text search with smart ranking gets you 80% of the way there
16
+ * and works offline without any setup.
17
+ *
18
+ * Future enhancement: Add optional embedding-based indexing for
19
+ * projects that configure it.
20
+ */
21
+
22
+ import * as fs from "fs";
23
+ import * as path from "path";
24
+ import { execSync } from "child_process";
25
+ import type { ContextProvider, ContextResult, ContextResolveOptions } from "./types";
26
+
27
+ /** Max files to include in results */
28
+ const MAX_RESULTS = 10;
29
+
30
+ /** Max chars per file snippet */
31
+ const SNIPPET_LIMIT = 2000;
32
+
33
+ /** Total max chars for all results */
34
+ const TOTAL_LIMIT = 20000;
35
+
36
+ /** File extensions to search (common code files) */
37
+ const CODE_EXTENSIONS = new Set([
38
+ ".ts", ".tsx", ".js", ".jsx", ".py", ".rs", ".go", ".java",
39
+ ".rb", ".php", ".cs", ".cpp", ".c", ".h", ".hpp", ".swift",
40
+ ".kt", ".scala", ".r", ".html", ".css", ".scss", ".less",
41
+ ".json", ".yaml", ".yml", ".toml", ".xml", ".sql", ".sh",
42
+ ".bash", ".zsh", ".md", ".mdx", ".txt", ".vue", ".svelte",
43
+ ]);
44
+
45
+ /** Directories to skip during search */
46
+ const SKIP_DIRS = new Set([
47
+ "node_modules", ".git", "dist", "build", ".next", ".nuxt",
48
+ "__pycache__", ".cache", ".turbo", "coverage", "venv",
49
+ ".venv", ".tox", "target", "vendor",
50
+ ]);
51
+
52
+ export class CodebaseContextProvider implements ContextProvider {
53
+ id = "codebase";
54
+ trigger = "@codebase";
55
+ description = "Search the entire codebase for relevant code";
56
+ requiresArg = true;
57
+
58
+ async resolve(arg?: string, options?: ContextResolveOptions): Promise<ContextResult> {
59
+ const query = arg?.trim();
60
+ if (!query) {
61
+ return {
62
+ label: "Codebase Search",
63
+ content: "[Please provide a search query after @codebase, e.g.: @codebase authentication flow]",
64
+ metadata: { source: "codebase" },
65
+ };
66
+ }
67
+
68
+ const workingDir = options?.workingDir || process.cwd();
69
+
70
+ // Search using ripgrep if available, fall back to simple search
71
+ const results = await this.search(query, workingDir);
72
+
73
+ if (results.length === 0) {
74
+ return {
75
+ label: `Codebase: "${query}"`,
76
+ content: `[No results found for "${query}" in the codebase.]`,
77
+ metadata: { source: "codebase", itemCount: 0 },
78
+ };
79
+ }
80
+
81
+ // Format results with code snippets
82
+ const sections: string[] = [];
83
+ let totalChars = 0;
84
+
85
+ for (const result of results.slice(0, MAX_RESULTS)) {
86
+ if (totalChars >= TOTAL_LIMIT) {
87
+ sections.push(`\n... [more results omitted]`);
88
+ break;
89
+ }
90
+
91
+ const relPath = path.relative(workingDir, result.file);
92
+ const ext = path.extname(result.file).slice(1);
93
+ const lang = ext || "text";
94
+
95
+ let snippet = result.snippet;
96
+ if (snippet.length > SNIPPET_LIMIT) {
97
+ snippet = snippet.substring(0, SNIPPET_LIMIT) + "\n... [truncated]";
98
+ }
99
+
100
+ sections.push(`### ${relPath}${result.line ? `:${result.line}` : ""}\n\n\`\`\`${lang}\n${snippet}\n\`\`\``);
101
+ totalChars += snippet.length;
102
+ }
103
+
104
+ return {
105
+ label: `Codebase: "${query}" (${results.length} results)`,
106
+ content: `## Codebase Search: "${query}"\n\n${sections.join("\n\n")}`,
107
+ metadata: {
108
+ source: "codebase",
109
+ itemCount: results.length,
110
+ truncated: results.length > MAX_RESULTS,
111
+ },
112
+ };
113
+ }
114
+
115
+ /**
116
+ * Search the codebase for a query string.
117
+ * Uses ripgrep (rg) if available for fast searching,
118
+ * otherwise falls back to a simple Node.js file walker.
119
+ */
120
+ private async search(
121
+ query: string,
122
+ workingDir: string,
123
+ ): Promise<Array<{ file: string; line?: number; snippet: string; score: number }>> {
124
+ // Try ripgrep first (much faster for large codebases)
125
+ try {
126
+ return this.searchWithRipgrep(query, workingDir);
127
+ } catch {
128
+ // Fall back to simple search
129
+ return this.searchSimple(query, workingDir);
130
+ }
131
+ }
132
+
133
+ /**
134
+ * Fast search using ripgrep (rg).
135
+ * Learning note: ripgrep respects .gitignore by default,
136
+ * which is exactly what we want.
137
+ */
138
+ private searchWithRipgrep(
139
+ query: string,
140
+ workingDir: string,
141
+ ): Array<{ file: string; line?: number; snippet: string; score: number }> {
142
+ // Use ripgrep with context lines around matches
143
+ const escapedQuery = query.replace(/['"\\]/g, "\\$&");
144
+ const output = execSync(
145
+ `rg --ignore-case --max-count=3 --context=3 --with-filename --line-number "${escapedQuery}" .`,
146
+ {
147
+ cwd: workingDir,
148
+ encoding: "utf-8",
149
+ timeout: 10000,
150
+ maxBuffer: 5 * 1024 * 1024,
151
+ },
152
+ );
153
+
154
+ // Parse ripgrep output into structured results
155
+ const results = new Map<string, { file: string; line?: number; snippet: string; score: number }>();
156
+
157
+ const lines = output.split("\n");
158
+ let currentFile = "";
159
+ let currentSnippet = "";
160
+ let currentLine: number | undefined;
161
+
162
+ for (const line of lines) {
163
+ // ripgrep format: "file:line:content" or "file-line-content" (context)
164
+ const match = line.match(/^(.+?)[:-](\d+)[:-](.*)$/);
165
+ if (match) {
166
+ const [, file, lineNum, content] = match;
167
+ const absFile = path.resolve(workingDir, file);
168
+
169
+ if (file !== currentFile) {
170
+ // Save previous file's results
171
+ if (currentFile && currentSnippet) {
172
+ const existing = results.get(currentFile);
173
+ if (!existing || currentSnippet.length > existing.snippet.length) {
174
+ results.set(currentFile, {
175
+ file: path.resolve(workingDir, currentFile),
176
+ line: currentLine,
177
+ snippet: currentSnippet.trim(),
178
+ score: 1,
179
+ });
180
+ }
181
+ }
182
+ currentFile = file;
183
+ currentSnippet = "";
184
+ currentLine = parseInt(lineNum, 10);
185
+ }
186
+ currentSnippet += content + "\n";
187
+ } else if (line === "--") {
188
+ // Separator between matches in the same file
189
+ currentSnippet += "...\n";
190
+ }
191
+ }
192
+
193
+ // Don't forget the last file
194
+ if (currentFile && currentSnippet) {
195
+ results.set(currentFile, {
196
+ file: path.resolve(workingDir, currentFile),
197
+ line: currentLine,
198
+ snippet: currentSnippet.trim(),
199
+ score: 1,
200
+ });
201
+ }
202
+
203
+ return Array.from(results.values());
204
+ }
205
+
206
+ /**
207
+ * Simple fallback search using Node.js file system.
208
+ * Slower but works everywhere without external dependencies.
209
+ */
210
+ private searchSimple(
211
+ query: string,
212
+ workingDir: string,
213
+ ): Array<{ file: string; line?: number; snippet: string; score: number }> {
214
+ const results: Array<{ file: string; line?: number; snippet: string; score: number }> = [];
215
+ const queryLower = query.toLowerCase();
216
+
217
+ // Walk directory tree
218
+ const walk = (dir: string, depth: number): void => {
219
+ if (depth > 5 || results.length >= MAX_RESULTS * 2) return;
220
+
221
+ let entries: fs.Dirent[];
222
+ try {
223
+ entries = fs.readdirSync(dir, { withFileTypes: true });
224
+ } catch {
225
+ return;
226
+ }
227
+
228
+ for (const entry of entries) {
229
+ if (entry.name.startsWith(".") && entry.name !== ".cdoing") continue;
230
+ if (SKIP_DIRS.has(entry.name)) continue;
231
+
232
+ const fullPath = path.join(dir, entry.name);
233
+
234
+ if (entry.isDirectory()) {
235
+ walk(fullPath, depth + 1);
236
+ } else if (entry.isFile()) {
237
+ const ext = path.extname(entry.name);
238
+ if (!CODE_EXTENSIONS.has(ext)) continue;
239
+
240
+ try {
241
+ const content = fs.readFileSync(fullPath, "utf-8");
242
+ const contentLower = content.toLowerCase();
243
+
244
+ // Check if query appears in content
245
+ const index = contentLower.indexOf(queryLower);
246
+ if (index >= 0) {
247
+ // Extract a snippet around the match
248
+ const linesBefore = content.substring(0, index).split("\n");
249
+ const lineNum = linesBefore.length;
250
+ const allLines = content.split("\n");
251
+ const start = Math.max(0, lineNum - 4);
252
+ const end = Math.min(allLines.length, lineNum + 4);
253
+ const snippet = allLines.slice(start, end).join("\n");
254
+
255
+ // Score based on match quality
256
+ let score = 1;
257
+ if (entry.name.toLowerCase().includes(queryLower)) score += 5; // Filename match
258
+ if (content.toLowerCase().split(queryLower).length > 2) score += 2; // Multiple matches
259
+
260
+ results.push({ file: fullPath, line: lineNum, snippet, score });
261
+ }
262
+ } catch {
263
+ // Skip unreadable files
264
+ }
265
+ }
266
+ }
267
+ };
268
+
269
+ walk(workingDir, 0);
270
+
271
+ // Sort by score (highest first) and return top results
272
+ return results.sort((a, b) => b.score - a.score).slice(0, MAX_RESULTS);
273
+ }
274
+ }
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Diff Context Provider — @diff
3
+ *
4
+ * Provides current working changes as context.
5
+ * Usage:
6
+ * @diff — unstaged working changes
7
+ * @diff staged — staged changes
8
+ * @diff main — diff against main branch
9
+ */
10
+
11
+ import { exec } from "child_process";
12
+ import type { ContextProvider, ContextResult, ContextResolveOptions } from "./types";
13
+
14
+ const MAX_DIFF_CHARS = 30000;
15
+
16
+ export class DiffContextProvider implements ContextProvider {
17
+ id = "diff";
18
+ trigger = "@diff";
19
+ description = "Current git diff (working changes, staged, or vs branch)";
20
+ requiresArg = false;
21
+
22
+ async resolve(arg?: string, options?: ContextResolveOptions): Promise<ContextResult> {
23
+ const workingDir = options?.workingDir || process.cwd();
24
+ const target = (arg || "").trim();
25
+
26
+ let command = "git diff";
27
+ let label = "Working changes";
28
+
29
+ if (target === "staged") {
30
+ command = "git diff --cached";
31
+ label = "Staged changes";
32
+ } else if (target) {
33
+ // Sanitize
34
+ if (!/^[a-zA-Z0-9_.\/~^@{}\-]+$/.test(target)) {
35
+ return { label: "Diff", content: `Invalid git ref: "${target}"` };
36
+ }
37
+ command = `git diff ${target}`;
38
+ label = `Diff vs ${target}`;
39
+ }
40
+
41
+ try {
42
+ const diff = await new Promise<string>((resolve, reject) => {
43
+ exec(command, { cwd: workingDir, timeout: 15000, maxBuffer: 5 * 1024 * 1024 }, (err, stdout, stderr) => {
44
+ if (err && !stdout) return reject(new Error(stderr?.trim() || err.message));
45
+ resolve(stdout);
46
+ });
47
+ });
48
+
49
+ const trimmedDiff = diff.trim();
50
+ if (!trimmedDiff) {
51
+ return { label, content: `## ${label}\nNo changes.` };
52
+ }
53
+
54
+ const truncated = trimmedDiff.length > MAX_DIFF_CHARS
55
+ ? trimmedDiff.substring(0, MAX_DIFF_CHARS) + "\n... [truncated]"
56
+ : trimmedDiff;
57
+
58
+ return {
59
+ label,
60
+ content: `## ${label}\n\`\`\`diff\n${truncated}\n\`\`\``,
61
+ };
62
+ } catch (err) {
63
+ return { label: "Diff", content: `Diff error: ${(err as Error).message}` };
64
+ }
65
+ }
66
+ }
@@ -0,0 +1,160 @@
1
+ /**
2
+ * Docs Context Provider — @docs
3
+ *
4
+ * Fetches documentation for packages/libraries and injects into context.
5
+ * Usage:
6
+ * @docs react — fetch React docs summary
7
+ * @docs express/Router — fetch specific section
8
+ * @docs https://... — fetch docs from URL directly
9
+ */
10
+
11
+ import * as fs from "fs";
12
+ import * as path from "path";
13
+ import type { ContextProvider, ContextResult, ContextResolveOptions } from "./types";
14
+
15
+ const MAX_CHARS = 30000;
16
+
17
+ /**
18
+ * Docs registry is loaded from ~/.cdoing/docs-registry.json if it exists.
19
+ * Users can add their own package → URL mappings there.
20
+ * Format: { "react": "https://...", "express": "https://..." }
21
+ */
22
+ function loadDocsRegistry(): Record<string, string> {
23
+ try {
24
+ const registryPath = require("path").join(require("os").homedir(), ".cdoing", "docs-registry.json");
25
+ if (require("fs").existsSync(registryPath)) {
26
+ return JSON.parse(require("fs").readFileSync(registryPath, "utf-8"));
27
+ }
28
+ } catch { /* ignore */ }
29
+ return {};
30
+ }
31
+
32
+ export class DocsContextProvider implements ContextProvider {
33
+ id = "docs";
34
+ trigger = "@docs";
35
+ description = "Fetch documentation for a package or URL (@docs react, @docs https://...)";
36
+ requiresArg = true;
37
+
38
+ async resolve(arg?: string, options?: ContextResolveOptions): Promise<ContextResult> {
39
+ if (!arg?.trim()) {
40
+ return {
41
+ label: "Docs",
42
+ content: `Usage: @docs <package-name> or @docs <url>\n\nSupported packages: ${Object.keys(loadDocsRegistry()).join(", ")}`,
43
+ };
44
+ }
45
+
46
+ const query = arg.trim().toLowerCase();
47
+
48
+ // Direct URL
49
+ if (query.startsWith("http://") || query.startsWith("https://")) {
50
+ return this.fetchUrl(query);
51
+ }
52
+
53
+ // Check registry
54
+ const registryUrl = loadDocsRegistry()[query];
55
+ if (registryUrl) {
56
+ return this.fetchUrl(registryUrl, query);
57
+ }
58
+
59
+ // Try to find local docs (README.md in node_modules)
60
+ const workingDir = options?.workingDir || process.cwd();
61
+ const localResult = this.findLocalDocs(workingDir, query);
62
+ if (localResult) return localResult;
63
+
64
+ // Try npm registry as fallback
65
+ return this.fetchNpmReadme(query);
66
+ }
67
+
68
+ private async fetchUrl(url: string, packageName?: string): Promise<ContextResult> {
69
+ try {
70
+ const response = await fetch(url, {
71
+ headers: { "User-Agent": "cdoing-agent/1.0" },
72
+ signal: AbortSignal.timeout(10000),
73
+ });
74
+
75
+ if (!response.ok) {
76
+ return {
77
+ label: `Docs: ${packageName || url}`,
78
+ content: `Failed to fetch docs: HTTP ${response.status}`,
79
+ };
80
+ }
81
+
82
+ let text = await response.text();
83
+ if (text.length > MAX_CHARS) {
84
+ text = text.substring(0, MAX_CHARS) + "\n\n... [truncated]";
85
+ }
86
+
87
+ return {
88
+ label: `Docs: ${packageName || url}`,
89
+ content: `<docs source="${packageName || url}">\n${text}\n</docs>`,
90
+ metadata: { source: url, truncated: text.length >= MAX_CHARS },
91
+ };
92
+ } catch (err) {
93
+ return {
94
+ label: `Docs: ${packageName || url}`,
95
+ content: `Failed to fetch docs: ${(err as Error).message}`,
96
+ };
97
+ }
98
+ }
99
+
100
+ private findLocalDocs(workingDir: string, packageName: string): ContextResult | null {
101
+ const candidates = [
102
+ path.join(workingDir, "node_modules", packageName, "README.md"),
103
+ path.join(workingDir, "node_modules", packageName, "readme.md"),
104
+ path.join(workingDir, "node_modules", packageName, "Readme.md"),
105
+ path.join(workingDir, "node_modules", `@${packageName.split("/")[0]}`, packageName.split("/")[1] || "", "README.md"),
106
+ ];
107
+
108
+ for (const candidate of candidates) {
109
+ if (fs.existsSync(candidate)) {
110
+ try {
111
+ let content = fs.readFileSync(candidate, "utf-8");
112
+ if (content.length > MAX_CHARS) {
113
+ content = content.substring(0, MAX_CHARS) + "\n\n... [truncated]";
114
+ }
115
+ return {
116
+ label: `Docs: ${packageName} (local)`,
117
+ content: `<docs source="${packageName}" local="true">\n${content}\n</docs>`,
118
+ metadata: { source: candidate },
119
+ };
120
+ } catch { /* skip */ }
121
+ }
122
+ }
123
+
124
+ return null;
125
+ }
126
+
127
+ private async fetchNpmReadme(packageName: string): Promise<ContextResult> {
128
+ try {
129
+ const response = await fetch(`https://registry.npmjs.org/${encodeURIComponent(packageName)}`, {
130
+ headers: { "Accept": "application/json", "User-Agent": "cdoing-agent/1.0" },
131
+ signal: AbortSignal.timeout(10000),
132
+ });
133
+
134
+ if (!response.ok) {
135
+ return {
136
+ label: `Docs: ${packageName}`,
137
+ content: `Package "${packageName}" not found. Available packages: ${Object.keys(loadDocsRegistry()).join(", ")}`,
138
+ };
139
+ }
140
+
141
+ const data = await response.json() as { readme?: string; description?: string };
142
+ let readme = data.readme || data.description || "(no documentation found)";
143
+
144
+ if (readme.length > MAX_CHARS) {
145
+ readme = readme.substring(0, MAX_CHARS) + "\n\n... [truncated]";
146
+ }
147
+
148
+ return {
149
+ label: `Docs: ${packageName} (npm)`,
150
+ content: `<docs source="${packageName}" npm="true">\n${readme}\n</docs>`,
151
+ metadata: { source: `npm:${packageName}`, truncated: readme.length >= MAX_CHARS },
152
+ };
153
+ } catch (err) {
154
+ return {
155
+ label: `Docs: ${packageName}`,
156
+ content: `Failed to fetch docs for "${packageName}": ${(err as Error).message}`,
157
+ };
158
+ }
159
+ }
160
+ }
@@ -0,0 +1,54 @@
1
+ import * as fs from "fs";
2
+ import * as path from "path";
3
+ import type { ContextProvider, ContextResult, ContextResolveOptions } from "./types";
4
+
5
+ const MAX_BYTES = 100_000; // ~100 KB cap
6
+
7
+ /**
8
+ * @file <path> — reads a file and injects its content into the prompt.
9
+ * Also handles bare relative/absolute paths that look like files.
10
+ */
11
+ export class FileIncludeContextProvider implements ContextProvider {
12
+ id = "file";
13
+ trigger = "@file";
14
+ description = "Include a file's contents in the message (@file src/foo.ts)";
15
+ requiresArg = true;
16
+
17
+ async resolve(arg?: string, opts?: ContextResolveOptions): Promise<ContextResult> {
18
+ const base = opts?.workingDir || process.cwd();
19
+ const filePath = arg ? path.resolve(base, arg.trim()) : "";
20
+
21
+ if (!filePath) {
22
+ return { label: "File", content: "(no file path provided — usage: @file path/to/file)" };
23
+ }
24
+
25
+ if (!fs.existsSync(filePath)) {
26
+ return { label: "File", content: `(file not found: ${filePath})` };
27
+ }
28
+
29
+ const stat = fs.statSync(filePath);
30
+ if (stat.isDirectory()) {
31
+ // List directory contents instead
32
+ const entries = fs.readdirSync(filePath).slice(0, 100).join("\n");
33
+ return {
34
+ label: "Directory",
35
+ content: `<directory path="${filePath}">\n${entries}\n</directory>`,
36
+ };
37
+ }
38
+
39
+ const raw = fs.readFileSync(filePath);
40
+ const truncated = raw.length > MAX_BYTES;
41
+ const text = truncated
42
+ ? raw.slice(0, MAX_BYTES).toString("utf8") + "\n... [truncated]"
43
+ : raw.toString("utf8");
44
+
45
+ const rel = path.relative(base, filePath);
46
+ const ext = path.extname(filePath).slice(1) || "text";
47
+
48
+ return {
49
+ label: "File",
50
+ content: `<file path="${rel}">\n\`\`\`${ext}\n${text}\n\`\`\`\n</file>`,
51
+ metadata: { source: rel, truncated },
52
+ };
53
+ }
54
+ }