gencode-ai 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (356) hide show
  1. package/.gencode/settings.local.json +7 -0
  2. package/README.md +20 -102
  3. package/dist/agent/agent.d.ts +43 -2
  4. package/dist/agent/agent.d.ts.map +1 -1
  5. package/dist/agent/agent.js +90 -17
  6. package/dist/agent/agent.js.map +1 -1
  7. package/dist/agent/types.d.ts +9 -1
  8. package/dist/agent/types.d.ts.map +1 -1
  9. package/dist/cli/components/AllModelsSelector.d.ts +11 -0
  10. package/dist/cli/components/AllModelsSelector.d.ts.map +1 -0
  11. package/dist/cli/components/AllModelsSelector.js +153 -0
  12. package/dist/cli/components/AllModelsSelector.js.map +1 -0
  13. package/dist/cli/components/App.d.ts +8 -1
  14. package/dist/cli/components/App.d.ts.map +1 -1
  15. package/dist/cli/components/App.js +276 -40
  16. package/dist/cli/components/App.js.map +1 -1
  17. package/dist/cli/components/CommandSuggestions.d.ts.map +1 -1
  18. package/dist/cli/components/CommandSuggestions.js +3 -0
  19. package/dist/cli/components/CommandSuggestions.js.map +1 -1
  20. package/dist/cli/components/Header.d.ts +1 -1
  21. package/dist/cli/components/Header.d.ts.map +1 -1
  22. package/dist/cli/components/Header.js +4 -6
  23. package/dist/cli/components/Header.js.map +1 -1
  24. package/dist/cli/components/Logo.d.ts +1 -0
  25. package/dist/cli/components/Logo.d.ts.map +1 -1
  26. package/dist/cli/components/Logo.js +16 -3
  27. package/dist/cli/components/Logo.js.map +1 -1
  28. package/dist/cli/components/Messages.d.ts +17 -3
  29. package/dist/cli/components/Messages.d.ts.map +1 -1
  30. package/dist/cli/components/Messages.js +70 -18
  31. package/dist/cli/components/Messages.js.map +1 -1
  32. package/dist/cli/components/ModelSelector.d.ts +7 -7
  33. package/dist/cli/components/ModelSelector.d.ts.map +1 -1
  34. package/dist/cli/components/ModelSelector.js +116 -33
  35. package/dist/cli/components/ModelSelector.js.map +1 -1
  36. package/dist/cli/components/PermissionPrompt.d.ts +60 -0
  37. package/dist/cli/components/PermissionPrompt.d.ts.map +1 -0
  38. package/dist/cli/components/PermissionPrompt.js +192 -0
  39. package/dist/cli/components/PermissionPrompt.js.map +1 -0
  40. package/dist/cli/components/ProviderManager.d.ts +8 -0
  41. package/dist/cli/components/ProviderManager.d.ts.map +1 -0
  42. package/dist/cli/components/ProviderManager.js +280 -0
  43. package/dist/cli/components/ProviderManager.js.map +1 -0
  44. package/dist/cli/components/Spinner.d.ts +7 -2
  45. package/dist/cli/components/Spinner.d.ts.map +1 -1
  46. package/dist/cli/components/Spinner.js +116 -25
  47. package/dist/cli/components/Spinner.js.map +1 -1
  48. package/dist/cli/components/TodoList.d.ts +7 -0
  49. package/dist/cli/components/TodoList.d.ts.map +1 -0
  50. package/dist/cli/components/TodoList.js +34 -0
  51. package/dist/cli/components/TodoList.js.map +1 -0
  52. package/dist/cli/components/index.d.ts +1 -0
  53. package/dist/cli/components/index.d.ts.map +1 -1
  54. package/dist/cli/components/index.js +1 -0
  55. package/dist/cli/components/index.js.map +1 -1
  56. package/dist/cli/components/markdown.d.ts +9 -0
  57. package/dist/cli/components/markdown.d.ts.map +1 -0
  58. package/dist/cli/components/markdown.js +129 -0
  59. package/dist/cli/components/markdown.js.map +1 -0
  60. package/dist/cli/components/theme.d.ts +5 -0
  61. package/dist/cli/components/theme.d.ts.map +1 -1
  62. package/dist/cli/components/theme.js +7 -0
  63. package/dist/cli/components/theme.js.map +1 -1
  64. package/dist/cli/index.js +66 -12
  65. package/dist/cli/index.js.map +1 -1
  66. package/dist/config/index.d.ts +14 -4
  67. package/dist/config/index.d.ts.map +1 -1
  68. package/dist/config/index.js +19 -3
  69. package/dist/config/index.js.map +1 -1
  70. package/dist/config/levels.d.ts +49 -0
  71. package/dist/config/levels.d.ts.map +1 -0
  72. package/dist/config/levels.js +222 -0
  73. package/dist/config/levels.js.map +1 -0
  74. package/dist/config/loader.d.ts +46 -0
  75. package/dist/config/loader.d.ts.map +1 -0
  76. package/dist/config/loader.js +153 -0
  77. package/dist/config/loader.js.map +1 -0
  78. package/dist/config/manager.d.ts +115 -15
  79. package/dist/config/manager.d.ts.map +1 -1
  80. package/dist/config/manager.js +260 -34
  81. package/dist/config/manager.js.map +1 -1
  82. package/dist/config/manager.test.d.ts +5 -0
  83. package/dist/config/manager.test.d.ts.map +1 -0
  84. package/dist/config/manager.test.js +192 -0
  85. package/dist/config/manager.test.js.map +1 -0
  86. package/dist/config/merger.d.ts +56 -0
  87. package/dist/config/merger.d.ts.map +1 -0
  88. package/dist/config/merger.js +177 -0
  89. package/dist/config/merger.js.map +1 -0
  90. package/dist/config/providers-config.d.ts +28 -0
  91. package/dist/config/providers-config.d.ts.map +1 -0
  92. package/dist/config/providers-config.js +79 -0
  93. package/dist/config/providers-config.js.map +1 -0
  94. package/dist/config/test-utils.d.ts +24 -0
  95. package/dist/config/test-utils.d.ts.map +1 -0
  96. package/dist/config/test-utils.js +55 -0
  97. package/dist/config/test-utils.js.map +1 -0
  98. package/dist/config/types.d.ts +108 -9
  99. package/dist/config/types.d.ts.map +1 -1
  100. package/dist/config/types.js +53 -2
  101. package/dist/config/types.js.map +1 -1
  102. package/dist/memory/import-resolver.d.ts +46 -0
  103. package/dist/memory/import-resolver.d.ts.map +1 -0
  104. package/dist/memory/import-resolver.js +117 -0
  105. package/dist/memory/import-resolver.js.map +1 -0
  106. package/dist/memory/index.d.ts +7 -6
  107. package/dist/memory/index.d.ts.map +1 -1
  108. package/dist/memory/index.js +7 -5
  109. package/dist/memory/index.js.map +1 -1
  110. package/dist/memory/init-prompt.d.ts +22 -0
  111. package/dist/memory/init-prompt.d.ts.map +1 -0
  112. package/dist/memory/init-prompt.js +103 -0
  113. package/dist/memory/init-prompt.js.map +1 -0
  114. package/dist/memory/memory-manager.d.ts +119 -0
  115. package/dist/memory/memory-manager.d.ts.map +1 -0
  116. package/dist/memory/memory-manager.js +587 -0
  117. package/dist/memory/memory-manager.js.map +1 -0
  118. package/dist/memory/rules-parser.d.ts +38 -0
  119. package/dist/memory/rules-parser.d.ts.map +1 -0
  120. package/dist/memory/rules-parser.js +69 -0
  121. package/dist/memory/rules-parser.js.map +1 -0
  122. package/dist/memory/test-utils.d.ts +20 -0
  123. package/dist/memory/test-utils.d.ts.map +1 -0
  124. package/dist/memory/test-utils.js +44 -0
  125. package/dist/memory/test-utils.js.map +1 -0
  126. package/dist/memory/types.d.ts +70 -63
  127. package/dist/memory/types.d.ts.map +1 -1
  128. package/dist/memory/types.js +42 -2
  129. package/dist/memory/types.js.map +1 -1
  130. package/dist/permissions/audit.d.ts +82 -0
  131. package/dist/permissions/audit.d.ts.map +1 -0
  132. package/dist/permissions/audit.js +229 -0
  133. package/dist/permissions/audit.js.map +1 -0
  134. package/dist/permissions/index.d.ts +11 -1
  135. package/dist/permissions/index.d.ts.map +1 -1
  136. package/dist/permissions/index.js +15 -0
  137. package/dist/permissions/index.js.map +1 -1
  138. package/dist/permissions/manager.d.ts +149 -13
  139. package/dist/permissions/manager.d.ts.map +1 -1
  140. package/dist/permissions/manager.js +480 -35
  141. package/dist/permissions/manager.js.map +1 -1
  142. package/dist/permissions/manager.test.d.ts +5 -0
  143. package/dist/permissions/manager.test.d.ts.map +1 -0
  144. package/dist/permissions/manager.test.js +213 -0
  145. package/dist/permissions/manager.test.js.map +1 -0
  146. package/dist/permissions/persistence.d.ts +74 -0
  147. package/dist/permissions/persistence.d.ts.map +1 -0
  148. package/dist/permissions/persistence.js +248 -0
  149. package/dist/permissions/persistence.js.map +1 -0
  150. package/dist/permissions/persistence.test.d.ts +5 -0
  151. package/dist/permissions/persistence.test.d.ts.map +1 -0
  152. package/dist/permissions/persistence.test.js +171 -0
  153. package/dist/permissions/persistence.test.js.map +1 -0
  154. package/dist/permissions/prompt-matcher.d.ts +64 -0
  155. package/dist/permissions/prompt-matcher.d.ts.map +1 -0
  156. package/dist/permissions/prompt-matcher.js +415 -0
  157. package/dist/permissions/prompt-matcher.js.map +1 -0
  158. package/dist/permissions/prompt-matcher.test.d.ts +5 -0
  159. package/dist/permissions/prompt-matcher.test.d.ts.map +1 -0
  160. package/dist/permissions/prompt-matcher.test.js +107 -0
  161. package/dist/permissions/prompt-matcher.test.js.map +1 -0
  162. package/dist/permissions/types.d.ts +157 -0
  163. package/dist/permissions/types.d.ts.map +1 -1
  164. package/dist/permissions/types.js +43 -8
  165. package/dist/permissions/types.js.map +1 -1
  166. package/dist/prompts/index.d.ts +92 -0
  167. package/dist/prompts/index.d.ts.map +1 -0
  168. package/dist/prompts/index.js +241 -0
  169. package/dist/prompts/index.js.map +1 -0
  170. package/dist/providers/gemini.d.ts.map +1 -1
  171. package/dist/providers/gemini.js +14 -3
  172. package/dist/providers/gemini.js.map +1 -1
  173. package/dist/providers/index.d.ts +5 -3
  174. package/dist/providers/index.d.ts.map +1 -1
  175. package/dist/providers/index.js +13 -1
  176. package/dist/providers/index.js.map +1 -1
  177. package/dist/providers/registry.d.ts +66 -0
  178. package/dist/providers/registry.d.ts.map +1 -0
  179. package/dist/providers/registry.js +158 -0
  180. package/dist/providers/registry.js.map +1 -0
  181. package/dist/providers/search/brave.d.ts +14 -0
  182. package/dist/providers/search/brave.d.ts.map +1 -0
  183. package/dist/providers/search/brave.js +87 -0
  184. package/dist/providers/search/brave.js.map +1 -0
  185. package/dist/providers/search/exa.d.ts +12 -0
  186. package/dist/providers/search/exa.d.ts.map +1 -0
  187. package/dist/providers/search/exa.js +158 -0
  188. package/dist/providers/search/exa.js.map +1 -0
  189. package/dist/providers/search/index.d.ts +31 -0
  190. package/dist/providers/search/index.d.ts.map +1 -0
  191. package/dist/providers/search/index.js +75 -0
  192. package/dist/providers/search/index.js.map +1 -0
  193. package/dist/providers/search/serper.d.ts +14 -0
  194. package/dist/providers/search/serper.d.ts.map +1 -0
  195. package/dist/providers/search/serper.js +87 -0
  196. package/dist/providers/search/serper.js.map +1 -0
  197. package/dist/providers/search/types.d.ts +21 -0
  198. package/dist/providers/search/types.d.ts.map +1 -0
  199. package/dist/providers/search/types.js +5 -0
  200. package/dist/providers/search/types.js.map +1 -0
  201. package/dist/providers/store.d.ts +104 -0
  202. package/dist/providers/store.d.ts.map +1 -0
  203. package/dist/providers/store.js +171 -0
  204. package/dist/providers/store.js.map +1 -0
  205. package/dist/providers/types.d.ts +7 -1
  206. package/dist/providers/types.d.ts.map +1 -1
  207. package/dist/providers/vertex-ai.d.ts +33 -0
  208. package/dist/providers/vertex-ai.d.ts.map +1 -0
  209. package/dist/providers/vertex-ai.js +407 -0
  210. package/dist/providers/vertex-ai.js.map +1 -0
  211. package/dist/tools/builtin/bash.d.ts.map +1 -1
  212. package/dist/tools/builtin/bash.js +2 -1
  213. package/dist/tools/builtin/bash.js.map +1 -1
  214. package/dist/tools/builtin/edit.d.ts.map +1 -1
  215. package/dist/tools/builtin/edit.js +2 -1
  216. package/dist/tools/builtin/edit.js.map +1 -1
  217. package/dist/tools/builtin/glob.d.ts.map +1 -1
  218. package/dist/tools/builtin/glob.js +2 -1
  219. package/dist/tools/builtin/glob.js.map +1 -1
  220. package/dist/tools/builtin/grep.d.ts.map +1 -1
  221. package/dist/tools/builtin/grep.js +2 -1
  222. package/dist/tools/builtin/grep.js.map +1 -1
  223. package/dist/tools/builtin/read.d.ts.map +1 -1
  224. package/dist/tools/builtin/read.js +2 -1
  225. package/dist/tools/builtin/read.js.map +1 -1
  226. package/dist/tools/builtin/todowrite.d.ts +15 -0
  227. package/dist/tools/builtin/todowrite.d.ts.map +1 -0
  228. package/dist/tools/builtin/todowrite.js +88 -0
  229. package/dist/tools/builtin/todowrite.js.map +1 -0
  230. package/dist/tools/builtin/webfetch.d.ts +20 -0
  231. package/dist/tools/builtin/webfetch.d.ts.map +1 -0
  232. package/dist/tools/builtin/webfetch.js +228 -0
  233. package/dist/tools/builtin/webfetch.js.map +1 -0
  234. package/dist/tools/builtin/websearch.d.ts +17 -0
  235. package/dist/tools/builtin/websearch.d.ts.map +1 -0
  236. package/dist/tools/builtin/websearch.js +87 -0
  237. package/dist/tools/builtin/websearch.js.map +1 -0
  238. package/dist/tools/builtin/write.d.ts.map +1 -1
  239. package/dist/tools/builtin/write.js +2 -1
  240. package/dist/tools/builtin/write.js.map +1 -1
  241. package/dist/tools/index.d.ts +18 -0
  242. package/dist/tools/index.d.ts.map +1 -1
  243. package/dist/tools/index.js +28 -2
  244. package/dist/tools/index.js.map +1 -1
  245. package/dist/tools/types.d.ts +41 -0
  246. package/dist/tools/types.d.ts.map +1 -1
  247. package/dist/tools/types.js +16 -0
  248. package/dist/tools/types.js.map +1 -1
  249. package/dist/tools/utils/ssrf.d.ts +18 -0
  250. package/dist/tools/utils/ssrf.d.ts.map +1 -0
  251. package/dist/tools/utils/ssrf.js +70 -0
  252. package/dist/tools/utils/ssrf.js.map +1 -0
  253. package/docs/README.md +5 -4
  254. package/docs/config-system-comparison.md +707 -0
  255. package/docs/memory-system.md +238 -0
  256. package/docs/permissions.md +368 -0
  257. package/docs/proposals/0001-web-fetch-tool.md +32 -2
  258. package/docs/proposals/0002-web-search-tool.md +59 -2
  259. package/docs/proposals/0005-todo-system.md +350 -85
  260. package/docs/proposals/0006-memory-system.md +11 -10
  261. package/docs/proposals/0012-ask-user-question.md +941 -206
  262. package/docs/proposals/0023-permission-enhancements.md +61 -2
  263. package/docs/proposals/0041-configuration-system.md +587 -0
  264. package/docs/proposals/0042-prompt-optimization.md +866 -0
  265. package/docs/proposals/README.md +8 -6
  266. package/docs/providers.md +220 -0
  267. package/jest.config.js +26 -0
  268. package/package.json +14 -3
  269. package/src/agent/agent.ts +120 -18
  270. package/src/agent/types.ts +9 -1
  271. package/src/cli/components/App.tsx +369 -47
  272. package/src/cli/components/CommandSuggestions.tsx +3 -0
  273. package/src/cli/components/Header.tsx +11 -17
  274. package/src/cli/components/Logo.tsx +76 -9
  275. package/src/cli/components/Messages.tsx +146 -38
  276. package/src/cli/components/ModelSelector.tsx +169 -52
  277. package/src/cli/components/PermissionPrompt.tsx +388 -0
  278. package/src/cli/components/ProviderManager.tsx +534 -0
  279. package/src/cli/components/Spinner.tsx +138 -25
  280. package/src/cli/components/TodoList.tsx +54 -0
  281. package/src/cli/components/index.ts +6 -0
  282. package/src/cli/components/markdown.ts +157 -0
  283. package/src/cli/components/theme.ts +7 -0
  284. package/src/cli/index.tsx +76 -13
  285. package/src/config/index.ts +79 -4
  286. package/src/config/levels.test.ts +163 -0
  287. package/src/config/levels.ts +285 -0
  288. package/src/config/loader.test.ts +120 -0
  289. package/src/config/loader.ts +178 -0
  290. package/src/config/manager.test.ts +215 -0
  291. package/src/config/manager.ts +328 -40
  292. package/src/config/merger.test.ts +360 -0
  293. package/src/config/merger.ts +221 -0
  294. package/src/config/providers-config.ts +85 -0
  295. package/src/config/test-utils.ts +79 -0
  296. package/src/config/types.ts +186 -9
  297. package/src/memory/import-resolver.test.ts +117 -0
  298. package/src/memory/import-resolver.ts +149 -0
  299. package/src/memory/index.ts +11 -0
  300. package/src/memory/init-prompt.ts +113 -0
  301. package/src/memory/memory-manager.test.ts +198 -0
  302. package/src/memory/memory-manager.ts +716 -0
  303. package/src/memory/rules-parser.test.ts +182 -0
  304. package/src/memory/rules-parser.ts +82 -0
  305. package/src/memory/test-utils.ts +60 -0
  306. package/src/memory/types.ts +119 -0
  307. package/src/permissions/audit.ts +284 -0
  308. package/src/permissions/index.ts +20 -1
  309. package/src/permissions/manager.test.ts +260 -0
  310. package/src/permissions/manager.ts +592 -40
  311. package/src/permissions/persistence.test.ts +220 -0
  312. package/src/permissions/persistence.ts +301 -0
  313. package/src/permissions/prompt-matcher.test.ts +213 -0
  314. package/src/permissions/prompt-matcher.ts +472 -0
  315. package/src/permissions/types.ts +236 -8
  316. package/src/prompts/index.test.ts +279 -0
  317. package/src/prompts/index.ts +306 -0
  318. package/src/prompts/system/anthropic.txt +29 -0
  319. package/src/prompts/system/base.txt +124 -0
  320. package/src/prompts/system/gemini.txt +35 -0
  321. package/src/prompts/system/generic.txt +128 -0
  322. package/src/prompts/system/openai.txt +29 -0
  323. package/src/prompts/tools/bash.txt +60 -0
  324. package/src/prompts/tools/edit.txt +29 -0
  325. package/src/prompts/tools/glob.txt +35 -0
  326. package/src/prompts/tools/grep.txt +43 -0
  327. package/src/prompts/tools/read.txt +22 -0
  328. package/src/prompts/tools/todowrite.txt +71 -0
  329. package/src/prompts/tools/webfetch.txt +34 -0
  330. package/src/prompts/tools/websearch.txt +41 -0
  331. package/src/prompts/tools/write.txt +23 -0
  332. package/src/providers/gemini.ts +20 -4
  333. package/src/providers/index.ts +18 -3
  334. package/src/providers/registry.ts +198 -0
  335. package/src/providers/search/brave.ts +132 -0
  336. package/src/providers/search/exa.ts +217 -0
  337. package/src/providers/search/index.ts +79 -0
  338. package/src/providers/search/serper.ts +133 -0
  339. package/src/providers/search/types.ts +24 -0
  340. package/src/providers/store.ts +216 -0
  341. package/src/providers/types.ts +9 -1
  342. package/src/providers/vertex-ai.ts +594 -0
  343. package/src/tools/builtin/bash.ts +2 -1
  344. package/src/tools/builtin/edit.ts +2 -1
  345. package/src/tools/builtin/glob.ts +2 -1
  346. package/src/tools/builtin/grep.ts +2 -1
  347. package/src/tools/builtin/read.ts +2 -1
  348. package/src/tools/builtin/todowrite.ts +102 -0
  349. package/src/tools/builtin/webfetch.ts +261 -0
  350. package/src/tools/builtin/websearch.ts +103 -0
  351. package/src/tools/builtin/write.ts +2 -1
  352. package/src/tools/index.ts +28 -2
  353. package/src/tools/types.ts +32 -0
  354. package/src/tools/utils/ssrf.ts +79 -0
  355. package/tsconfig.json +1 -1
  356. package/CLAUDE.md +0 -70
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Shared test utilities for config tests
3
+ */
4
+
5
+ import * as fs from 'fs/promises';
6
+ import * as path from 'path';
7
+ import * as os from 'os';
8
+
9
+ export interface TestProject {
10
+ tempDir: string;
11
+ projectDir: string;
12
+ cleanup: () => Promise<void>;
13
+ }
14
+
15
+ /**
16
+ * Create a test project with temp directory and git marker
17
+ */
18
+ export async function createTestProject(prefix = 'gencode-test-'): Promise<TestProject> {
19
+ const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), prefix));
20
+ const projectDir = path.join(tempDir, 'project');
21
+
22
+ await fs.mkdir(projectDir, { recursive: true });
23
+ await fs.mkdir(path.join(projectDir, '.git'));
24
+
25
+ return {
26
+ tempDir,
27
+ projectDir,
28
+ cleanup: async () => {
29
+ await fs.rm(tempDir, { recursive: true, force: true });
30
+ delete process.env.GENCODE_CONFIG_DIRS;
31
+ },
32
+ };
33
+ }
34
+
35
+ /**
36
+ * Write JSON settings to a config directory
37
+ */
38
+ export async function writeSettings(
39
+ projectDir: string,
40
+ namespace: 'claude' | 'gencode',
41
+ settings: Record<string, unknown>,
42
+ local = false
43
+ ): Promise<string> {
44
+ const dir = path.join(projectDir, namespace === 'claude' ? '.claude' : '.gencode');
45
+ await fs.mkdir(dir, { recursive: true });
46
+
47
+ const filename = local ? 'settings.local.json' : 'settings.json';
48
+ const filePath = path.join(dir, filename);
49
+ await fs.writeFile(filePath, JSON.stringify(settings));
50
+
51
+ return filePath;
52
+ }
53
+
54
+ /**
55
+ * Write memory file to a directory
56
+ */
57
+ export async function writeMemory(
58
+ projectDir: string,
59
+ namespace: 'claude' | 'gencode',
60
+ content: string,
61
+ options: { local?: boolean; inDir?: boolean } = {}
62
+ ): Promise<string> {
63
+ const { local = false, inDir = true } = options;
64
+ const filename = namespace === 'claude'
65
+ ? (local ? 'CLAUDE.local.md' : 'CLAUDE.md')
66
+ : (local ? 'AGENT.local.md' : 'AGENT.md');
67
+
68
+ let filePath: string;
69
+ if (inDir) {
70
+ const dir = path.join(projectDir, namespace === 'claude' ? '.claude' : '.gencode');
71
+ await fs.mkdir(dir, { recursive: true });
72
+ filePath = path.join(dir, filename);
73
+ } else {
74
+ filePath = path.join(projectDir, filename);
75
+ }
76
+
77
+ await fs.writeFile(filePath, content);
78
+ return filePath;
79
+ }
@@ -1,25 +1,202 @@
1
1
  /**
2
- * Settings Types - User settings persistence (Claude Code style)
2
+ * Configuration Types - Multi-level configuration system (Claude Code compatible)
3
+ *
4
+ * Configuration hierarchy (priority from low to high):
5
+ * 1. User Level: ~/.gencode/ + ~/.claude/ (merged, gencode wins)
6
+ * 2. Extra Dirs: GENCODE_CONFIG_DIRS environment variable
7
+ * 3. Project Level: .gencode/ + .claude/ (merged, gencode wins)
8
+ * 4. Local Level: .gencode/*.local.* + .claude/*.local.* (merged, gencode wins)
9
+ * 5. CLI Arguments: Command line overrides
10
+ * 6. Managed Level: System-wide enforced settings (cannot be overridden)
3
11
  */
4
12
 
5
- export type ProviderName = 'openai' | 'anthropic' | 'gemini';
13
+ import * as os from 'os';
14
+ import * as path from 'path';
15
+
16
+ // =============================================================================
17
+ // Provider Types
18
+ // =============================================================================
19
+
20
+ export type ProviderName = 'openai' | 'anthropic' | 'gemini' | 'vertex-ai';
21
+
22
+ // =============================================================================
23
+ // Settings Types
24
+ // =============================================================================
25
+
26
+ /**
27
+ * Permission rules for tools
28
+ */
29
+ export interface PermissionRules {
30
+ allow?: string[];
31
+ ask?: string[];
32
+ deny?: string[];
33
+ }
6
34
 
7
35
  /**
8
- * Settings file structure (~/.gencode/settings.json)
9
- * Similar to Claude Code's settings.json
36
+ * Settings file structure
37
+ * Compatible with Claude Code's settings.json
10
38
  */
11
39
  export interface Settings {
40
+ // Provider configuration
12
41
  model?: string;
13
42
  provider?: ProviderName;
14
- permissions?: {
15
- allow?: string[];
16
- deny?: string[];
43
+
44
+ // Permissions
45
+ permissions?: PermissionRules;
46
+
47
+ // UI/Display
48
+ theme?: string;
49
+ language?: string;
50
+ alwaysThinkingEnabled?: boolean;
51
+
52
+ // Environment variables
53
+ env?: Record<string, string>;
54
+
55
+ // Attribution for commits/PRs
56
+ attribution?: {
57
+ commit?: string;
58
+ pr?: string;
17
59
  };
60
+
61
+ // Plugin configuration
62
+ enabledPlugins?: Record<string, boolean>;
63
+ extraKnownMarketplaces?: Record<string, unknown>;
64
+
65
+ // Managed-only fields (cannot be overridden by lower levels)
66
+ strictKnownMarketplaces?: unknown[];
67
+
68
+ // Catch-all for future fields
69
+ [key: string]: unknown;
70
+ }
71
+
72
+ // =============================================================================
73
+ // Configuration Level Types
74
+ // =============================================================================
75
+
76
+ /**
77
+ * Configuration level identifiers
78
+ */
79
+ export type ConfigLevelType = 'managed' | 'user' | 'extra' | 'project' | 'local' | 'cli';
80
+
81
+ /**
82
+ * Represents a configuration level with its metadata
83
+ */
84
+ export interface ConfigLevel {
85
+ type: ConfigLevelType;
86
+ priority: number; // Higher number = higher priority
87
+ paths: string[]; // Paths to check (in order: gencode first, then claude)
88
+ description: string;
89
+ }
90
+
91
+ /**
92
+ * A loaded configuration source
93
+ */
94
+ export interface ConfigSource {
95
+ level: ConfigLevelType;
96
+ path: string;
97
+ namespace: 'gencode' | 'claude' | 'extra';
98
+ settings: Settings;
99
+ }
100
+
101
+ /**
102
+ * Result of merging all configuration sources
103
+ */
104
+ export interface MergedConfig {
105
+ settings: Settings;
106
+ sources: ConfigSource[];
107
+ managedDeny: string[]; // Deny rules that cannot be overridden
18
108
  }
19
109
 
110
+ // =============================================================================
111
+ // Constants
112
+ // =============================================================================
113
+
114
+ export const GENCODE_CONFIG_DIRS_ENV = 'GENCODE_CONFIG_DIRS';
115
+
116
+ // File names
117
+ export const SETTINGS_FILE_NAME = 'settings.json';
118
+ export const SETTINGS_LOCAL_FILE_NAME = 'settings.local.json';
119
+ export const MANAGED_SETTINGS_FILE_NAME = 'managed-settings.json';
120
+ export const PROVIDERS_FILE_NAME = 'providers.json';
121
+
122
+ // Directory names
123
+ export const GENCODE_DIR = '.gencode';
124
+ export const CLAUDE_DIR = '.claude';
125
+
126
+ // User directory paths
127
+ export const USER_GENCODE_DIR = path.join(os.homedir(), GENCODE_DIR);
128
+ export const USER_CLAUDE_DIR = path.join(os.homedir(), CLAUDE_DIR);
129
+
130
+ // Managed settings locations by platform
131
+ export function getManagedPaths(): { gencode: string; claude: string } {
132
+ const platform = os.platform();
133
+
134
+ if (platform === 'darwin') {
135
+ return {
136
+ gencode: '/Library/Application Support/GenCode',
137
+ claude: '/Library/Application Support/ClaudeCode',
138
+ };
139
+ } else if (platform === 'win32') {
140
+ return {
141
+ gencode: 'C:\\Program Files\\GenCode',
142
+ claude: 'C:\\Program Files\\ClaudeCode',
143
+ };
144
+ } else {
145
+ // Linux and other Unix-like systems
146
+ return {
147
+ gencode: '/etc/gencode',
148
+ claude: '/etc/claude-code',
149
+ };
150
+ }
151
+ }
152
+
153
+ // =============================================================================
154
+ // Legacy Types (for backward compatibility)
155
+ // =============================================================================
156
+
20
157
  export interface SettingsManagerOptions {
21
158
  settingsDir?: string;
22
159
  }
23
160
 
24
- export const DEFAULT_SETTINGS_DIR = '~/.gencode';
25
- export const SETTINGS_FILE_NAME = 'settings.json';
161
+ // Legacy exports
162
+ export const DEFAULT_SETTINGS_DIR = '~/.claude';
163
+ export const PROJECT_SETTINGS_DIR = '.claude';
164
+ export const FALLBACK_SETTINGS_DIR = '~/.gencode';
165
+ export const FALLBACK_PROJECT_DIR = '.gencode';
166
+
167
+ // =============================================================================
168
+ // Provider Connection Types
169
+ // =============================================================================
170
+
171
+ /**
172
+ * Provider connection info
173
+ */
174
+ export interface ProviderConnection {
175
+ method: 'api_key' | 'vertex' | 'oauth';
176
+ connectedAt: string;
177
+ }
178
+
179
+ /**
180
+ * Cached model info
181
+ */
182
+ export interface CachedModel {
183
+ id: string;
184
+ name: string;
185
+ description?: string;
186
+ }
187
+
188
+ /**
189
+ * Cached models for a provider
190
+ */
191
+ export interface ProviderModels {
192
+ cachedAt: string;
193
+ list: CachedModel[];
194
+ }
195
+
196
+ /**
197
+ * Providers config file structure (~/.gencode/providers.json)
198
+ */
199
+ export interface ProvidersConfig {
200
+ connections: Record<string, ProviderConnection>;
201
+ models: Record<string, ProviderModels>;
202
+ }
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Import Resolver Tests
3
+ *
4
+ * These tests focus on the core logic that can be tested without complex mocking.
5
+ * For integration tests, use the test-memory.ts script.
6
+ */
7
+
8
+ import { describe, it, expect, beforeEach } from '@jest/globals';
9
+ import { ImportResolver } from './import-resolver.js';
10
+ import { DEFAULT_MEMORY_CONFIG } from './types.js';
11
+
12
+ describe('ImportResolver', () => {
13
+ let resolver: ImportResolver;
14
+
15
+ beforeEach(() => {
16
+ resolver = new ImportResolver(DEFAULT_MEMORY_CONFIG);
17
+ resolver.setProjectRoot('/project');
18
+ });
19
+
20
+ describe('constructor', () => {
21
+ it('should create resolver with config', () => {
22
+ const resolver = new ImportResolver(DEFAULT_MEMORY_CONFIG);
23
+ expect(resolver).toBeDefined();
24
+ });
25
+ });
26
+
27
+ describe('setProjectRoot', () => {
28
+ it('should set project root', () => {
29
+ const resolver = new ImportResolver(DEFAULT_MEMORY_CONFIG);
30
+ resolver.setProjectRoot('/my/project');
31
+ expect(resolver).toBeDefined();
32
+ });
33
+ });
34
+
35
+ describe('reset', () => {
36
+ it('should reset resolved paths', () => {
37
+ const resolver = new ImportResolver(DEFAULT_MEMORY_CONFIG);
38
+ resolver.reset();
39
+ expect(resolver).toBeDefined();
40
+ });
41
+ });
42
+
43
+ describe('resolve', () => {
44
+ it('should return content unchanged when no @imports exist', async () => {
45
+ const content = `# Test File
46
+
47
+ This is a test file with no imports.
48
+
49
+ ## Section
50
+ Some content here.`;
51
+
52
+ const result = await resolver.resolve(content, '/project');
53
+ expect(result.content).toBe(content);
54
+ expect(result.importedPaths).toEqual([]);
55
+ expect(result.errors).toEqual([]);
56
+ });
57
+
58
+ it('should not treat email addresses as imports', async () => {
59
+ const content = `# Contact
60
+
61
+ Email: test@example.com
62
+ Support: help@company.org`;
63
+
64
+ const result = await resolver.resolve(content, '/project');
65
+ expect(result.content).toBe(content);
66
+ expect(result.importedPaths).toEqual([]);
67
+ expect(result.errors).toEqual([]);
68
+ });
69
+
70
+ it('should not treat inline @ mentions as imports', async () => {
71
+ const content = `Some text with @mention inside
72
+
73
+ Not an import because @ is not at line start`;
74
+
75
+ const result = await resolver.resolve(content, '/project');
76
+ expect(result.content).toBe(content);
77
+ expect(result.importedPaths).toEqual([]);
78
+ });
79
+
80
+ it('should handle content with no imports and special characters', async () => {
81
+ const content = `# Special Characters
82
+
83
+ Code: \`@decorator\`
84
+ Symbol: @todo
85
+ Email: user@domain.com`;
86
+
87
+ const result = await resolver.resolve(content, '/project');
88
+ expect(result.content).toBe(content);
89
+ expect(result.errors).toEqual([]);
90
+ });
91
+
92
+ it('should handle missing file gracefully', async () => {
93
+ const content = `@./nonexistent.md`;
94
+
95
+ const result = await resolver.resolve(content, '/project');
96
+
97
+ // Should have an error about failed import
98
+ expect(result.errors.some(e => e.includes('Failed to import'))).toBe(true);
99
+ });
100
+
101
+ it('should respect max depth limit', async () => {
102
+ // Create resolver with depth limit of 0
103
+ const limitedResolver = new ImportResolver({
104
+ ...DEFAULT_MEMORY_CONFIG,
105
+ maxImportDepth: 0,
106
+ });
107
+ limitedResolver.setProjectRoot('/project');
108
+
109
+ const content = `@./file.md`;
110
+
111
+ const result = await limitedResolver.resolve(content, '/project');
112
+
113
+ // Should have error about max depth
114
+ expect(result.errors.some(e => e.includes('depth'))).toBe(true);
115
+ });
116
+ });
117
+ });
@@ -0,0 +1,149 @@
1
+ /**
2
+ * Import Resolver - Handle @import syntax in memory files
3
+ *
4
+ * Supports:
5
+ * - @path/to/file.md - Relative imports
6
+ * - @./file.md - Current directory imports
7
+ * - @../parent/file.md - Parent directory imports
8
+ *
9
+ * Security:
10
+ * - Circular import detection
11
+ * - Max depth limit (5 levels)
12
+ * - Path traversal protection
13
+ */
14
+
15
+ import * as fs from 'fs/promises';
16
+ import * as path from 'path';
17
+ import type { MemoryConfig } from './types.js';
18
+
19
+ export interface ImportResult {
20
+ content: string;
21
+ importedPaths: string[];
22
+ errors: string[];
23
+ }
24
+
25
+ export class ImportResolver {
26
+ private config: MemoryConfig;
27
+ private resolvedPaths: Set<string> = new Set();
28
+ private projectRoot: string = '';
29
+
30
+ constructor(config: MemoryConfig) {
31
+ this.config = config;
32
+ }
33
+
34
+ /**
35
+ * Set the project root for path traversal protection
36
+ */
37
+ setProjectRoot(root: string): void {
38
+ this.projectRoot = root;
39
+ }
40
+
41
+ /**
42
+ * Resolve all @imports in content
43
+ */
44
+ async resolve(
45
+ content: string,
46
+ basePath: string,
47
+ depth: number = 0
48
+ ): Promise<ImportResult> {
49
+ const importedPaths: string[] = [];
50
+ const errors: string[] = [];
51
+
52
+ if (depth >= this.config.maxImportDepth) {
53
+ errors.push(`Max import depth (${this.config.maxImportDepth}) exceeded`);
54
+ return { content, importedPaths, errors };
55
+ }
56
+
57
+ // Match @import lines: lines starting with @ followed by a path
58
+ // Pattern: @path/to/file or @./file or @../file
59
+ const importRegex = /^@([^\s]+\.md)$/gm;
60
+ let resolvedContent = content;
61
+ const matches = [...content.matchAll(importRegex)];
62
+
63
+ for (const match of matches) {
64
+ const importPath = match[1].trim();
65
+ const absolutePath = this.resolvePath(importPath, basePath);
66
+
67
+ // Circular import detection
68
+ if (this.resolvedPaths.has(absolutePath)) {
69
+ errors.push(`Circular import detected: ${importPath}`);
70
+ resolvedContent = resolvedContent.replace(match[0], `<!-- Circular import: ${importPath} -->`);
71
+ continue;
72
+ }
73
+
74
+ // Path traversal security check
75
+ if (!this.isPathSafe(absolutePath)) {
76
+ errors.push(`Import path outside allowed scope: ${importPath}`);
77
+ resolvedContent = resolvedContent.replace(match[0], `<!-- Blocked import: ${importPath} -->`);
78
+ continue;
79
+ }
80
+
81
+ try {
82
+ const stat = await fs.stat(absolutePath);
83
+
84
+ // Size limit check
85
+ if (stat.size > this.config.maxFileSize) {
86
+ errors.push(`Import too large (${Math.round(stat.size / 1024)}KB): ${importPath}`);
87
+ resolvedContent = resolvedContent.replace(match[0], `<!-- Import too large: ${importPath} -->`);
88
+ continue;
89
+ }
90
+
91
+ this.resolvedPaths.add(absolutePath);
92
+ let importedContent = await fs.readFile(absolutePath, 'utf-8');
93
+ importedPaths.push(absolutePath);
94
+
95
+ // Recursively resolve imports in the imported file
96
+ const nested = await this.resolve(importedContent, path.dirname(absolutePath), depth + 1);
97
+
98
+ importedContent = nested.content;
99
+ importedPaths.push(...nested.importedPaths);
100
+ errors.push(...nested.errors);
101
+
102
+ // Replace @import with content
103
+ resolvedContent = resolvedContent.replace(match[0], importedContent);
104
+ } catch {
105
+ errors.push(`Failed to import: ${importPath}`);
106
+ resolvedContent = resolvedContent.replace(match[0], `<!-- Failed to import: ${importPath} -->`);
107
+ }
108
+ }
109
+
110
+ return { content: resolvedContent, importedPaths, errors };
111
+ }
112
+
113
+ /**
114
+ * Resolve import path to absolute path
115
+ */
116
+ private resolvePath(importPath: string, basePath: string): string {
117
+ if (path.isAbsolute(importPath)) {
118
+ return importPath;
119
+ }
120
+ return path.resolve(basePath, importPath);
121
+ }
122
+
123
+ /**
124
+ * Check if path is safe (within project or home directory)
125
+ */
126
+ private isPathSafe(targetPath: string): boolean {
127
+ const home = process.env.HOME || '';
128
+ const normalized = path.normalize(targetPath);
129
+
130
+ // Allow imports within project root
131
+ if (this.projectRoot && normalized.startsWith(this.projectRoot)) {
132
+ return true;
133
+ }
134
+
135
+ // Allow imports from home directory (for user-level imports)
136
+ if (home && normalized.startsWith(home)) {
137
+ return true;
138
+ }
139
+
140
+ return false;
141
+ }
142
+
143
+ /**
144
+ * Reset resolver state for new file
145
+ */
146
+ reset(): void {
147
+ this.resolvedPaths.clear();
148
+ }
149
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Memory System - Claude Code compatible memory management
3
+ *
4
+ * Provides hierarchical memory loading with AGENT.md (primary) and CLAUDE.md (fallback)
5
+ */
6
+
7
+ export * from './types.js';
8
+ export { MemoryManager } from './memory-manager.js';
9
+ export { ImportResolver } from './import-resolver.js';
10
+ export { parseRuleFrontmatter, matchesPatterns, activateRules, getActiveRules } from './rules-parser.js';
11
+ export { gatherContextFiles, buildInitPrompt, getContextSummary } from './init-prompt.js';
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Init Prompt Builder - Generate prompts for /init command
3
+ *
4
+ * Gathers context files (package.json, README.md, etc.) and builds
5
+ * a prompt for the AI to generate AGENT.md
6
+ */
7
+
8
+ import * as fs from 'fs/promises';
9
+ import * as path from 'path';
10
+ import { glob } from 'glob';
11
+
12
+ export interface ContextFiles {
13
+ [path: string]: string;
14
+ }
15
+
16
+ /**
17
+ * Gather context files for project analysis
18
+ */
19
+ export async function gatherContextFiles(cwd: string): Promise<ContextFiles> {
20
+ const patterns = [
21
+ 'package.json',
22
+ 'README.md',
23
+ 'CONTRIBUTING.md',
24
+ '.cursor/rules/**/*.md',
25
+ '.cursorrules',
26
+ '.github/copilot-instructions.md',
27
+ 'Cargo.toml',
28
+ 'go.mod',
29
+ 'pyproject.toml',
30
+ 'tsconfig.json',
31
+ 'Makefile',
32
+ 'docker-compose.yml',
33
+ 'docker-compose.yaml',
34
+ ];
35
+
36
+ const files: ContextFiles = {};
37
+
38
+ for (const pattern of patterns) {
39
+ try {
40
+ const matches = await glob(pattern, { cwd, absolute: false });
41
+ for (const match of matches.slice(0, 3)) {
42
+ // Limit per pattern
43
+ try {
44
+ const filePath = path.join(cwd, match);
45
+ const content = await fs.readFile(filePath, 'utf-8');
46
+ if (content.length < 50000) {
47
+ // 50KB limit per file
48
+ files[match] = content;
49
+ }
50
+ } catch {
51
+ // Skip files that can't be read
52
+ }
53
+ }
54
+ } catch {
55
+ // Skip patterns that fail
56
+ }
57
+ }
58
+
59
+ return files;
60
+ }
61
+
62
+ /**
63
+ * Build the init prompt for generating AGENT.md
64
+ */
65
+ export function buildInitPrompt(contextFiles: ContextFiles, existingAgentMd?: string): string {
66
+ const contextParts = Object.entries(contextFiles)
67
+ .map(([filePath, content]) => `### ${filePath}\n\`\`\`\n${content}\n\`\`\``)
68
+ .join('\n\n');
69
+
70
+ const existingSection = existingAgentMd
71
+ ? `\n\n## Existing AGENT.md\n\`\`\`markdown\n${existingAgentMd}\n\`\`\`\n`
72
+ : '';
73
+
74
+ return `Please analyze this codebase and create an AGENT.md file, which will be given to future AI assistants to operate in this repository.
75
+
76
+ ## Context Files
77
+
78
+ ${contextParts}
79
+ ${existingSection}
80
+ ## Instructions
81
+
82
+ What to add:
83
+ 1. Commands that will be commonly used, such as how to build, lint, and run tests.
84
+ Include the necessary commands to develop in this codebase, such as how to run a single test.
85
+ 2. High-level code architecture and structure so that future instances can be productive more quickly.
86
+ Focus on the "big picture" architecture that requires reading multiple files to understand.
87
+
88
+ Usage notes:
89
+ - ${existingAgentMd ? 'Suggest improvements to the existing AGENT.md.' : 'Create a new AGENT.md.'}
90
+ - Do not repeat yourself and do not include obvious instructions like "Provide helpful error messages" or "Write tests for new code".
91
+ - Avoid listing every component or file structure that can be easily discovered.
92
+ - Don't include generic development practices.
93
+ - Do not make up information unless it's expressly included in the files above.
94
+ - Keep it concise - aim for ~20-40 lines.
95
+ - Start the file with:
96
+
97
+ # AGENT.md
98
+
99
+ This file provides guidance to AI assistants when working with code in this repository.
100
+
101
+ After generating the content, use the Write tool to save it to ./AGENT.md`;
102
+ }
103
+
104
+ /**
105
+ * Get a summary of files found for user feedback
106
+ */
107
+ export function getContextSummary(contextFiles: ContextFiles): string {
108
+ const fileNames = Object.keys(contextFiles);
109
+ if (fileNames.length === 0) {
110
+ return 'No context files found';
111
+ }
112
+ return `Found ${fileNames.length} context file(s): ${fileNames.join(', ')}`;
113
+ }