@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,106 @@
1
+ /**
2
+ * Folder Context Provider — @folder
3
+ *
4
+ * Includes all files from a directory as context.
5
+ * Usage:
6
+ * @folder src/utils — include all files from src/utils
7
+ * @folder src/api 5 — include files, max 5 files
8
+ */
9
+
10
+ import * as fs from "fs";
11
+ import * as path from "path";
12
+ import type { ContextProvider, ContextResult, ContextResolveOptions } from "./types";
13
+
14
+ const MAX_FILES = 20;
15
+ const MAX_FILE_CHARS = 3000;
16
+ const MAX_TOTAL_CHARS = 50000;
17
+
18
+ const SKIP_DIRS = new Set(["node_modules", ".git", "dist", "build", "__pycache__", ".cache", "coverage"]);
19
+
20
+ export class FolderContextProvider implements ContextProvider {
21
+ id = "folder";
22
+ trigger = "@folder";
23
+ description = "Include all files from a directory as context";
24
+ requiresArg = true;
25
+
26
+ async resolve(arg?: string, options?: ContextResolveOptions): Promise<ContextResult> {
27
+ if (!arg?.trim()) {
28
+ return { label: "Folder", content: "Usage: @folder <directory> [maxFiles]" };
29
+ }
30
+
31
+ const parts = arg.trim().split(/\s+/);
32
+ const dirPath = parts[0];
33
+ const maxFiles = parseInt(parts[1], 10) || MAX_FILES;
34
+ const workingDir = options?.workingDir || process.cwd();
35
+ const fullPath = path.isAbsolute(dirPath) ? dirPath : path.resolve(workingDir, dirPath);
36
+
37
+ if (!fs.existsSync(fullPath) || !fs.statSync(fullPath).isDirectory()) {
38
+ return { label: "Folder", content: `Directory not found: ${dirPath}` };
39
+ }
40
+
41
+ const files = this.collectFiles(fullPath, maxFiles);
42
+ if (files.length === 0) {
43
+ return { label: `Folder: ${dirPath}`, content: `No files found in ${dirPath}` };
44
+ }
45
+
46
+ let totalChars = 0;
47
+ const sections: string[] = [`## Folder: ${dirPath} (${files.length} files)`];
48
+
49
+ for (const file of files) {
50
+ if (totalChars >= MAX_TOTAL_CHARS) {
51
+ sections.push(`\n... [truncated, ${MAX_TOTAL_CHARS} char limit reached]`);
52
+ break;
53
+ }
54
+
55
+ const rel = path.relative(fullPath, file);
56
+ try {
57
+ let content = fs.readFileSync(file, "utf-8");
58
+ if (content.length > MAX_FILE_CHARS) {
59
+ content = content.substring(0, MAX_FILE_CHARS) + "\n... [truncated]";
60
+ }
61
+ const ext = path.extname(file).substring(1) || "txt";
62
+ sections.push(`\n### ${rel}\n\`\`\`${ext}\n${content}\n\`\`\``);
63
+ totalChars += content.length;
64
+ } catch {
65
+ sections.push(`\n### ${rel}\n(unable to read)`);
66
+ }
67
+ }
68
+
69
+ return {
70
+ label: `Folder: ${dirPath}`,
71
+ content: sections.join("\n"),
72
+ metadata: { itemCount: files.length },
73
+ };
74
+ }
75
+
76
+ private collectFiles(dir: string, limit: number): string[] {
77
+ const result: string[] = [];
78
+ this.walk(dir, result, limit);
79
+ return result;
80
+ }
81
+
82
+ private walk(dir: string, result: string[], limit: number): void {
83
+ if (result.length >= limit) return;
84
+
85
+ let entries: fs.Dirent[];
86
+ try {
87
+ entries = fs.readdirSync(dir, { withFileTypes: true });
88
+ } catch {
89
+ return;
90
+ }
91
+
92
+ // Files first, then directories
93
+ const files = entries.filter((e) => e.isFile());
94
+ const dirs = entries.filter((e) => e.isDirectory() && !SKIP_DIRS.has(e.name) && !e.name.startsWith("."));
95
+
96
+ for (const f of files) {
97
+ if (result.length >= limit) return;
98
+ result.push(path.join(dir, f.name));
99
+ }
100
+
101
+ for (const d of dirs) {
102
+ if (result.length >= limit) return;
103
+ this.walk(path.join(dir, d.name), result, limit);
104
+ }
105
+ }
106
+ }
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Git Context Provider — @git
3
+ *
4
+ * Provides git context: branch, status, recent commits, blame, log.
5
+ * Usage:
6
+ * @git — branch + status + recent commits
7
+ * @git log 20 — last 20 commits
8
+ * @git blame file — blame for a file
9
+ * @git branch — all branches
10
+ */
11
+
12
+ import { exec } from "child_process";
13
+ import type { ContextProvider, ContextResult, ContextResolveOptions } from "./types";
14
+
15
+ export class GitContextProvider implements ContextProvider {
16
+ id = "git";
17
+ trigger = "@git";
18
+ description = "Git context: branch, status, commits, blame";
19
+ requiresArg = false;
20
+
21
+ async resolve(arg?: string, options?: ContextResolveOptions): Promise<ContextResult> {
22
+ const workingDir = options?.workingDir || process.cwd();
23
+ const parts = (arg || "").trim().split(/\s+/);
24
+ const subcommand = parts[0] || "";
25
+
26
+ try {
27
+ switch (subcommand) {
28
+ case "log": {
29
+ const count = parseInt(parts[1], 10) || 10;
30
+ const log = await run(`git log --oneline -${count}`, workingDir);
31
+ return { label: `Git log (${count})`, content: `## Git Log (last ${count} commits)\n\`\`\`\n${log}\n\`\`\`` };
32
+ }
33
+ case "blame": {
34
+ const file = parts[1];
35
+ if (!file) return { label: "Git blame", content: "Usage: @git blame <file>" };
36
+ const blame = await run(`git blame --line-porcelain "${file}" | grep -E "^(author |summary |\t)" | head -100`, workingDir);
37
+ return { label: `Git blame: ${file}`, content: `## Git Blame: ${file}\n\`\`\`\n${blame}\n\`\`\`` };
38
+ }
39
+ case "branch": {
40
+ const branches = await run("git branch -a --no-color", workingDir);
41
+ return { label: "Git branches", content: `## Git Branches\n\`\`\`\n${branches}\n\`\`\`` };
42
+ }
43
+ default: {
44
+ // Default: branch + status + recent commits
45
+ const [branch, status, log] = await Promise.all([
46
+ run("git branch --show-current", workingDir),
47
+ run("git status --short", workingDir),
48
+ run("git log --oneline -10", workingDir),
49
+ ]);
50
+ const sections = [
51
+ `## Git Context`,
52
+ `**Branch:** ${branch.trim()}`,
53
+ `\n### Status\n\`\`\`\n${status.trim() || "(clean)"}\n\`\`\``,
54
+ `\n### Recent Commits\n\`\`\`\n${log.trim()}\n\`\`\``,
55
+ ];
56
+ return { label: `Git: ${branch.trim()}`, content: sections.join("\n") };
57
+ }
58
+ }
59
+ } catch (err) {
60
+ return { label: "Git", content: `Git error: ${(err as Error).message}` };
61
+ }
62
+ }
63
+ }
64
+
65
+ function run(command: string, cwd: string): Promise<string> {
66
+ return new Promise((resolve, reject) => {
67
+ exec(command, { cwd, timeout: 10000, maxBuffer: 1024 * 1024 }, (err, stdout, stderr) => {
68
+ if (err && !stdout) return reject(new Error(stderr?.trim() || err.message));
69
+ resolve(stdout);
70
+ });
71
+ });
72
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Context Providers — Pluggable system for injecting context into chat messages.
3
+ *
4
+ * Each provider implements a simple interface:
5
+ * - id: unique name (e.g., "terminal", "open", "url")
6
+ * - trigger: the @ keyword that activates it (e.g., "@terminal")
7
+ * - resolve(): fetches and returns the context content
8
+ *
9
+ * This module re-exports all built-in context providers and the
10
+ * registry that manages them.
11
+ *
12
+ * Learning note: This follows the Strategy Pattern — each provider
13
+ * encapsulates a different way to gather context, and the registry
14
+ * lets you swap/add providers without modifying consuming code.
15
+ */
16
+
17
+ export { ContextProviderRegistry } from "./registry";
18
+ export type { ContextProvider, ContextResult } from "./types";
19
+ export { TerminalContextProvider } from "./terminal";
20
+ export { OpenFilesContextProvider } from "./open-files";
21
+ export { UrlContextProvider } from "./url";
22
+ export { TreeContextProvider } from "./tree";
23
+ export { ProblemsContextProvider } from "./problems";
24
+ export { CodebaseContextProvider } from "./codebase";
25
+ export { ClipboardContextProvider } from "./clipboard";
26
+ export { FileIncludeContextProvider } from "./file-include";
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Open Files Context Provider — @open
3
+ *
4
+ * Attaches the content of all currently open editor tabs.
5
+ * Useful for providing broad context about what the user is working on.
6
+ *
7
+ * How it works:
8
+ * 1. Receives list of open file paths from the IDE
9
+ * 2. Reads each file's content
10
+ * 3. Formats them with file headers for the LLM
11
+ * 4. Applies smart truncation for large files
12
+ *
13
+ * Learning note: We truncate large files rather than skipping them
14
+ * entirely — the file header + first N lines still provides useful
15
+ * context about the file's purpose and structure.
16
+ */
17
+
18
+ import * as fs from "fs";
19
+ import * as path from "path";
20
+ import type { ContextProvider, ContextResult, ContextResolveOptions } from "./types";
21
+
22
+ /** Max chars per file before truncation */
23
+ const PER_FILE_LIMIT = 5000;
24
+
25
+ /** Max total chars for all open files combined */
26
+ const TOTAL_LIMIT = 30000;
27
+
28
+ export class OpenFilesContextProvider implements ContextProvider {
29
+ id = "open";
30
+ trigger = "@open";
31
+ description = "Include all open editor files";
32
+ requiresArg = false;
33
+
34
+ async resolve(_arg?: string, options?: ContextResolveOptions): Promise<ContextResult> {
35
+ const openFiles = options?.openFiles || [];
36
+ const workingDir = options?.workingDir || process.cwd();
37
+
38
+ if (openFiles.length === 0) {
39
+ return {
40
+ label: "Open Files",
41
+ content: "[No files are currently open in the editor.]",
42
+ metadata: { source: "editor", itemCount: 0 },
43
+ };
44
+ }
45
+
46
+ const sections: string[] = [];
47
+ let totalChars = 0;
48
+ let truncatedCount = 0;
49
+
50
+ for (const filePath of openFiles) {
51
+ // Resolve relative paths against working directory
52
+ const absPath = path.isAbsolute(filePath)
53
+ ? filePath
54
+ : path.join(workingDir, filePath);
55
+
56
+ // Read file content safely
57
+ let content: string;
58
+ try {
59
+ content = fs.readFileSync(absPath, "utf-8");
60
+ } catch {
61
+ sections.push(`### ${filePath}\n\n[Unable to read file]`);
62
+ continue;
63
+ }
64
+
65
+ // Truncate individual files that are too large
66
+ let wasTruncated = false;
67
+ if (content.length > PER_FILE_LIMIT) {
68
+ content = content.substring(0, PER_FILE_LIMIT);
69
+ wasTruncated = true;
70
+ truncatedCount++;
71
+ }
72
+
73
+ // Stop adding files if we've exceeded the total limit
74
+ if (totalChars + content.length > TOTAL_LIMIT) {
75
+ sections.push(`\n... [${openFiles.length - sections.length} more files omitted for context space]`);
76
+ break;
77
+ }
78
+
79
+ // Detect language from file extension for syntax highlighting
80
+ const ext = path.extname(filePath).slice(1);
81
+ const lang = EXT_TO_LANG[ext] || ext || "text";
82
+
83
+ sections.push(
84
+ `### ${filePath}${wasTruncated ? " (truncated)" : ""}\n\n\`\`\`${lang}\n${content}\n\`\`\``
85
+ );
86
+ totalChars += content.length;
87
+ }
88
+
89
+ return {
90
+ label: `Open Files (${openFiles.length})`,
91
+ content: `## Open Editor Files\n\n${sections.join("\n\n")}`,
92
+ metadata: {
93
+ source: "editor",
94
+ truncated: truncatedCount > 0,
95
+ itemCount: openFiles.length,
96
+ },
97
+ };
98
+ }
99
+ }
100
+
101
+ /** Map file extensions to markdown language identifiers */
102
+ const EXT_TO_LANG: Record<string, string> = {
103
+ ts: "typescript", tsx: "tsx", js: "javascript", jsx: "jsx",
104
+ py: "python", rs: "rust", go: "go", java: "java",
105
+ rb: "ruby", php: "php", cs: "csharp", cpp: "cpp",
106
+ c: "c", h: "c", hpp: "cpp", swift: "swift",
107
+ kt: "kotlin", scala: "scala", r: "r",
108
+ html: "html", css: "css", scss: "scss", less: "less",
109
+ json: "json", yaml: "yaml", yml: "yaml", toml: "toml",
110
+ xml: "xml", sql: "sql", sh: "bash", bash: "bash",
111
+ zsh: "bash", fish: "fish", ps1: "powershell",
112
+ md: "markdown", mdx: "markdown", txt: "text",
113
+ };
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Problems Context Provider — @problems
3
+ *
4
+ * Injects VS Code diagnostics (errors, warnings) into the conversation.
5
+ * Helps the AI understand what's broken without the user copying error messages.
6
+ *
7
+ * How it works:
8
+ * 1. VS Code extension collects diagnostics from all open files
9
+ * 2. Groups them by file and severity
10
+ * 3. Formats as a structured list for the LLM
11
+ *
12
+ * Learning note: Diagnostics come from VS Code's Language Server Protocol (LSP).
13
+ * Each diagnostic has a file path, line number, severity, and message.
14
+ * We pass them via the `options.diagnostics` array from the extension host.
15
+ */
16
+
17
+ import type { ContextProvider, ContextResult, ContextResolveOptions } from "./types";
18
+
19
+ /** Emoji icons for each severity level */
20
+ const SEVERITY_ICONS: Record<string, string> = {
21
+ error: "❌",
22
+ warning: "⚠️",
23
+ info: "ℹ️",
24
+ hint: "💡",
25
+ };
26
+
27
+ /** Priority order for sorting (errors first) */
28
+ const SEVERITY_ORDER: Record<string, number> = {
29
+ error: 0,
30
+ warning: 1,
31
+ info: 2,
32
+ hint: 3,
33
+ };
34
+
35
+ export class ProblemsContextProvider implements ContextProvider {
36
+ id = "problems";
37
+ trigger = "@problems";
38
+ description = "Include current file diagnostics (errors, warnings)";
39
+ requiresArg = false;
40
+
41
+ async resolve(_arg?: string, options?: ContextResolveOptions): Promise<ContextResult> {
42
+ const diagnostics = options?.diagnostics || [];
43
+
44
+ if (diagnostics.length === 0) {
45
+ return {
46
+ label: "Problems",
47
+ content: "[No diagnostics found. All files are clean!]",
48
+ metadata: { source: "diagnostics", itemCount: 0 },
49
+ };
50
+ }
51
+
52
+ // Sort by severity (errors first), then by file path
53
+ const sorted = [...diagnostics].sort((a, b) => {
54
+ const severityDiff = (SEVERITY_ORDER[a.severity] ?? 4) - (SEVERITY_ORDER[b.severity] ?? 4);
55
+ if (severityDiff !== 0) return severityDiff;
56
+ return a.file.localeCompare(b.file);
57
+ });
58
+
59
+ // Group diagnostics by file for cleaner formatting
60
+ const byFile = new Map<string, typeof diagnostics>();
61
+ for (const diag of sorted) {
62
+ const existing = byFile.get(diag.file) || [];
63
+ existing.push(diag);
64
+ byFile.set(diag.file, existing);
65
+ }
66
+
67
+ // Count by severity
68
+ const counts = { error: 0, warning: 0, info: 0, hint: 0 };
69
+ for (const diag of sorted) {
70
+ counts[diag.severity] = (counts[diag.severity] || 0) + 1;
71
+ }
72
+
73
+ // Format output
74
+ const sections: string[] = [];
75
+ for (const [file, diags] of byFile) {
76
+ const lines = diags.map((d) => {
77
+ const icon = SEVERITY_ICONS[d.severity] || "•";
78
+ return ` ${icon} Line ${d.line}: ${d.message}`;
79
+ });
80
+ sections.push(`### ${file}\n${lines.join("\n")}`);
81
+ }
82
+
83
+ // Build summary line
84
+ const summaryParts: string[] = [];
85
+ if (counts.error > 0) summaryParts.push(`${counts.error} error${counts.error > 1 ? "s" : ""}`);
86
+ if (counts.warning > 0) summaryParts.push(`${counts.warning} warning${counts.warning > 1 ? "s" : ""}`);
87
+ if (counts.info > 0) summaryParts.push(`${counts.info} info`);
88
+ if (counts.hint > 0) summaryParts.push(`${counts.hint} hint${counts.hint > 1 ? "s" : ""}`);
89
+ const summary = summaryParts.join(", ");
90
+
91
+ return {
92
+ label: `Problems (${summary})`,
93
+ content: `## Diagnostics: ${summary}\n\n${sections.join("\n\n")}`,
94
+ metadata: {
95
+ source: "diagnostics",
96
+ itemCount: diagnostics.length,
97
+ },
98
+ };
99
+ }
100
+ }
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Context Provider Registry — Manages all available @ context providers.
3
+ *
4
+ * Responsibilities:
5
+ * 1. Register/unregister providers
6
+ * 2. Match user input (e.g., "@terminal") to the right provider
7
+ * 3. Resolve context by delegating to the matched provider
8
+ *
9
+ * Learning note: This is a Service Locator pattern — it decouples
10
+ * the chat input from specific provider implementations. You can
11
+ * add new @ providers without touching the input handling code.
12
+ */
13
+
14
+ import type { ContextProvider, ContextResult, ContextResolveOptions } from "./types";
15
+
16
+ export class ContextProviderRegistry {
17
+ /** Map of provider ID → provider instance */
18
+ private providers = new Map<string, ContextProvider>();
19
+
20
+ /**
21
+ * Register a new context provider.
22
+ * Replaces any existing provider with the same ID.
23
+ */
24
+ register(provider: ContextProvider): void {
25
+ this.providers.set(provider.id, provider);
26
+ }
27
+
28
+ /**
29
+ * Remove a provider by ID.
30
+ */
31
+ unregister(id: string): void {
32
+ this.providers.delete(id);
33
+ }
34
+
35
+ /**
36
+ * Get a provider by its ID.
37
+ */
38
+ get(id: string): ContextProvider | undefined {
39
+ return this.providers.get(id);
40
+ }
41
+
42
+ /**
43
+ * Get all registered providers (for autocomplete suggestions).
44
+ */
45
+ getAll(): ContextProvider[] {
46
+ return Array.from(this.providers.values());
47
+ }
48
+
49
+ /**
50
+ * Match a trigger string (e.g., "@terminal", "@url https://...") to a provider.
51
+ *
52
+ * Returns the matched provider and any argument after the trigger,
53
+ * or null if no provider matches.
54
+ *
55
+ * Learning note: This parsing is intentionally simple — we split on
56
+ * the first space to separate trigger from argument.
57
+ */
58
+ match(input: string): { provider: ContextProvider; arg?: string } | null {
59
+ // Input should start with @
60
+ const trimmed = input.trim();
61
+ if (!trimmed.startsWith("@")) return null;
62
+
63
+ // Split into trigger and argument: "@url https://example.com" → ["@url", "https://example.com"]
64
+ const spaceIndex = trimmed.indexOf(" ");
65
+ const trigger = spaceIndex >= 0 ? trimmed.substring(0, spaceIndex) : trimmed;
66
+ const arg = spaceIndex >= 0 ? trimmed.substring(spaceIndex + 1).trim() : undefined;
67
+
68
+ // Find a provider whose trigger matches
69
+ for (const provider of this.providers.values()) {
70
+ if (provider.trigger === trigger) {
71
+ return { provider, arg };
72
+ }
73
+ }
74
+
75
+ return null;
76
+ }
77
+
78
+ /**
79
+ * Resolve context from a trigger string.
80
+ * Convenience method that combines match() + resolve().
81
+ */
82
+ async resolve(input: string, options?: ContextResolveOptions): Promise<ContextResult | null> {
83
+ const match = this.match(input);
84
+ if (!match) return null;
85
+
86
+ return match.provider.resolve(match.arg, options);
87
+ }
88
+
89
+ /**
90
+ * Get autocomplete suggestions for a partial @ input.
91
+ * E.g., "@ter" → ["@terminal"]
92
+ */
93
+ getSuggestions(partial: string): Array<{ trigger: string; description: string }> {
94
+ const query = partial.toLowerCase();
95
+ return this.getAll()
96
+ .filter((p) => p.trigger.toLowerCase().startsWith(query))
97
+ .map((p) => ({ trigger: p.trigger, description: p.description }));
98
+ }
99
+ }
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Terminal Context Provider — @terminal
3
+ *
4
+ * Injects recent terminal output into the conversation.
5
+ * Useful when the user wants the AI to analyze command output,
6
+ * debug errors, or understand build/test results.
7
+ *
8
+ * How it works:
9
+ * 1. In VS Code: captures output from the integrated terminal
10
+ * 2. In CLI: captures the last shell command output
11
+ * 3. Formats it with a clear header for the LLM
12
+ *
13
+ * Learning note: Terminal output is inherently ephemeral — we only
14
+ * capture what's available at resolve time. In VS Code, this comes
15
+ * from the Terminal API; in CLI, from the last !command output.
16
+ */
17
+
18
+ import type { ContextProvider, ContextResult, ContextResolveOptions } from "./types";
19
+
20
+ /** Default max characters for terminal output (prevents context window overflow) */
21
+ const DEFAULT_MAX_CHARS = 10000;
22
+
23
+ export class TerminalContextProvider implements ContextProvider {
24
+ id = "terminal";
25
+ trigger = "@terminal";
26
+ description = "Include recent terminal output";
27
+ requiresArg = false;
28
+
29
+ async resolve(_arg?: string, options?: ContextResolveOptions): Promise<ContextResult> {
30
+ const maxChars = options?.maxContentLength ?? DEFAULT_MAX_CHARS;
31
+ let output = options?.terminalOutput || "";
32
+
33
+ // If no terminal output is available, return a helpful message
34
+ if (!output.trim()) {
35
+ return {
36
+ label: "Terminal Output",
37
+ content: "[No recent terminal output available. Run a command first, then use @terminal.]",
38
+ metadata: { source: "terminal", itemCount: 0 },
39
+ };
40
+ }
41
+
42
+ // Truncate if too long — keep the tail (most recent output is most relevant)
43
+ let truncated = false;
44
+ if (output.length > maxChars) {
45
+ output = output.slice(-maxChars);
46
+ truncated = true;
47
+ }
48
+
49
+ return {
50
+ label: "Terminal Output",
51
+ content: `## Recent Terminal Output\n\n\`\`\`\n${output}\n\`\`\``,
52
+ metadata: {
53
+ source: "terminal",
54
+ truncated,
55
+ },
56
+ };
57
+ }
58
+ }