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
@@ -1,16 +1,83 @@
1
1
  import { Box, Text } from 'ink';
2
+ import { colors } from './theme.js';
2
3
 
4
+ // Small G logo for inline use
3
5
  export function Logo() {
4
- // Full G with 3D shadow - subdued slate color
5
- const slateColor = "#64748B"; // Slate 500 - stable, professional
6
6
  return (
7
- <Box flexDirection="column" marginRight={1}>
8
- <Text color={slateColor}> ██████╗ </Text>
9
- <Text color={slateColor}> ██╔════╝ </Text>
10
- <Text color={slateColor}> ██║ ███╗</Text>
11
- <Text color={slateColor}> ██║ ██║</Text>
12
- <Text color={slateColor}> ╚██████╔╝</Text>
13
- <Text color={slateColor}> ╚═════╝ </Text>
7
+ <Box marginRight={1}>
8
+ <Text bold color={colors.brand}>◆</Text>
9
+ </Box>
10
+ );
11
+ }
12
+
13
+ // Large ASCII art logo with elegant gradient
14
+ export function BigLogo() {
15
+ // Indigo gradient - brand colors
16
+ const c1 = '#818CF8'; // Indigo 400
17
+ const c2 = '#818CF8'; // Indigo 400
18
+ const c3 = '#A5B4FC'; // Indigo 300
19
+ const c4 = '#A5B4FC'; // Indigo 300
20
+ const c5 = '#C7D2FE'; // Indigo 200
21
+ const c6 = '#C7D2FE'; // Indigo 200
22
+ const c7 = '#C7D2FE'; // Indigo 200
23
+
24
+ // G E N C O D E
25
+ return (
26
+ <Box flexDirection="column">
27
+ <Text>
28
+ <Text color={c1}> ██████╗ </Text>
29
+ <Text color={c2}>███████╗</Text>
30
+ <Text color={c3}>███╗ ██╗</Text>
31
+ <Text color={c4}> ██████╗</Text>
32
+ <Text color={c5}> ██████╗ </Text>
33
+ <Text color={c6}>██████╗ </Text>
34
+ <Text color={c7}>███████╗</Text>
35
+ </Text>
36
+ <Text>
37
+ <Text color={c1}>██╔════╝ </Text>
38
+ <Text color={c2}>██╔════╝</Text>
39
+ <Text color={c3}>████╗ ██║</Text>
40
+ <Text color={c4}>██╔════╝</Text>
41
+ <Text color={c5}>██╔═══██╗</Text>
42
+ <Text color={c6}>██╔══██╗</Text>
43
+ <Text color={c7}>██╔════╝</Text>
44
+ </Text>
45
+ <Text>
46
+ <Text color={c1}>██║ ███╗</Text>
47
+ <Text color={c2}>█████╗ </Text>
48
+ <Text color={c3}>██╔██╗ ██║</Text>
49
+ <Text color={c4}>██║ </Text>
50
+ <Text color={c5}>██║ ██║</Text>
51
+ <Text color={c6}>██║ ██║</Text>
52
+ <Text color={c7}>█████╗ </Text>
53
+ </Text>
54
+ <Text>
55
+ <Text color={c1}>██║ ██║</Text>
56
+ <Text color={c2}>██╔══╝ </Text>
57
+ <Text color={c3}>██║╚██╗██║</Text>
58
+ <Text color={c4}>██║ </Text>
59
+ <Text color={c5}>██║ ██║</Text>
60
+ <Text color={c6}>██║ ██║</Text>
61
+ <Text color={c7}>██╔══╝ </Text>
62
+ </Text>
63
+ <Text>
64
+ <Text color={c1}>╚██████╔╝</Text>
65
+ <Text color={c2}>███████╗</Text>
66
+ <Text color={c3}>██║ ╚████║</Text>
67
+ <Text color={c4}>╚██████╗</Text>
68
+ <Text color={c5}>╚██████╔╝</Text>
69
+ <Text color={c6}>██████╔╝</Text>
70
+ <Text color={c7}>███████╗</Text>
71
+ </Text>
72
+ <Text>
73
+ <Text color={c1}> ╚═════╝ </Text>
74
+ <Text color={c2}>╚══════╝</Text>
75
+ <Text color={c3}>╚═╝ ╚═══╝</Text>
76
+ <Text color={c4}> ╚═════╝</Text>
77
+ <Text color={c5}> ╚═════╝ </Text>
78
+ <Text color={c6}>╚═════╝ </Text>
79
+ <Text color={c7}>╚══════╝</Text>
80
+ </Text>
14
81
  </Box>
15
82
  );
16
83
  }
@@ -1,5 +1,12 @@
1
+ import { useState, useEffect } from 'react';
1
2
  import { Box, Text } from 'ink';
3
+ import InkSpinner from 'ink-spinner';
2
4
  import { colors, icons } from './theme.js';
5
+ import { renderMarkdown } from './markdown.js';
6
+
7
+ // Truncate string with ellipsis
8
+ const truncate = (str: string, maxLen: number) =>
9
+ str.length > maxLen ? str.slice(0, maxLen - 3) + '...' : str;
3
10
 
4
11
  // Word wrap text to terminal width
5
12
  function wrapText(text: string, width: number): string[] {
@@ -31,12 +38,15 @@ interface UserMessageProps {
31
38
 
32
39
  export function UserMessage({ text }: UserMessageProps) {
33
40
  const lines = text.trimEnd().split('\n');
41
+ // Subtle gray - ~8% darker than pure white
42
+ const inputBg = '#EFEFEF';
43
+
34
44
  return (
35
45
  <Box flexDirection="column" marginTop={1} marginBottom={0}>
36
46
  {lines.map((line, i) => (
37
- <Box key={i}>
47
+ <Box key={i} backgroundColor={inputBg}>
38
48
  <Text color={colors.brand}>{icons.userPrompt} </Text>
39
- <Text backgroundColor="#1E293B" color={colors.text}> {line} </Text>
49
+ <Text>{line}</Text>
40
50
  </Box>
41
51
  ))}
42
52
  </Box>
@@ -51,29 +61,40 @@ interface AssistantMessageProps {
51
61
  export function AssistantMessage({ text, streaming }: AssistantMessageProps) {
52
62
  if (!text) return null;
53
63
 
54
- // Get terminal width for wrapping
55
- const termWidth = process.stdout.columns || 80;
56
- const contentWidth = termWidth - 4; // Account for prefix
64
+ // Streaming: use simple text display (markdown incomplete during stream)
65
+ if (streaming) {
66
+ const termWidth = process.stdout.columns || 80;
67
+ const contentWidth = termWidth - 4;
68
+ const lines = wrapText(text.trimEnd(), contentWidth);
57
69
 
58
- // Wrap text to terminal width
59
- const lines = wrapText(text.trimEnd(), contentWidth);
70
+ return (
71
+ <Box flexDirection="column" marginTop={1} marginBottom={0}>
72
+ {lines.map((line, i) => (
73
+ <Box key={i}>
74
+ {i === 0 && <Text color={colors.success}>{icons.assistant} </Text>}
75
+ {i > 0 && <Text> </Text>}
76
+ <Text>
77
+ {line}
78
+ {i === lines.length - 1 && (
79
+ <Text color={colors.brandLight}>{icons.cursor}</Text>
80
+ )}
81
+ </Text>
82
+ </Box>
83
+ ))}
84
+ </Box>
85
+ );
86
+ }
87
+
88
+ // Completed: render with markdown
89
+ const rendered = renderMarkdown(text);
60
90
 
61
91
  return (
62
92
  <Box flexDirection="column" marginTop={1} marginBottom={0}>
63
- {lines.map((line, i) => (
64
- <Box key={i}>
65
- {i === 0 && <Text color={colors.success}>{icons.assistant} </Text>}
66
- {i > 0 && <Text> </Text>}
67
- <Text>
68
- {line}
69
- {streaming && i === lines.length - 1 ? (
70
- <Text color={colors.brandLight}>{icons.cursor}</Text>
71
- ) : (
72
- ''
73
- )}
74
- </Text>
75
- </Box>
76
- ))}
93
+ <Box>
94
+ <Text color={colors.success}>{icons.assistant}</Text>
95
+ <Text> </Text>
96
+ <Text wrap="wrap">{rendered}</Text>
97
+ </Box>
77
98
  </Box>
78
99
  );
79
100
  }
@@ -83,38 +104,126 @@ interface ToolCallProps {
83
104
  input: Record<string, unknown>;
84
105
  }
85
106
 
107
+ // Format tool input for display
108
+ function formatToolInput(name: string, input: Record<string, unknown>): string {
109
+ switch (name) {
110
+ case 'Read':
111
+ return input.file_path as string || '';
112
+ case 'Write':
113
+ case 'Edit':
114
+ return input.file_path as string || '';
115
+ case 'Glob':
116
+ return input.pattern as string || '';
117
+ case 'Grep':
118
+ return `"${input.pattern}"` + (input.path ? ` in ${input.path}` : '');
119
+ case 'Bash':
120
+ return truncate(input.command as string || '', 50);
121
+ case 'WebFetch':
122
+ return input.url as string || '';
123
+ case 'WebSearch':
124
+ return `"${input.query}"` || '';
125
+ case 'TodoWrite': {
126
+ const todos = input.todos as Array<{ content: string; status: string }> || [];
127
+ return `${todos.length} task${todos.length !== 1 ? 's' : ''}`;
128
+ }
129
+ default:
130
+ return truncate(JSON.stringify(input), 40);
131
+ }
132
+ }
133
+
86
134
  export function ToolCall({ name, input }: ToolCallProps) {
87
- const inputStr = JSON.stringify(input);
88
- const shortInput = inputStr.length > 50 ? inputStr.slice(0, 47) + '...' : inputStr;
135
+ // Hide TodoWrite (shown in TodoList component)
136
+ if (name === 'TodoWrite') return null;
137
+
138
+ const displayInput = formatToolInput(name, input);
89
139
 
90
140
  return (
91
- <Box marginLeft={2}>
92
- <Text dimColor>
93
- <Text color={colors.tool}>{icons.tool}</Text> {name}{' '}
94
- <Text color={colors.textMuted}>{shortInput}</Text>
141
+ <Box marginTop={1}>
142
+ <Text color={colors.tool}>{icons.tool}</Text>
143
+ <Text> </Text>
144
+ <Text bold>{name}</Text>
145
+ {displayInput && (
146
+ <>
147
+ <Text color={colors.textMuted}> </Text>
148
+ <Text color={colors.textSecondary}>{truncate(displayInput, 60)}</Text>
149
+ </>
150
+ )}
151
+ </Box>
152
+ );
153
+ }
154
+
155
+ // Pending tool call with spinning indicator
156
+ interface PendingToolCallProps {
157
+ name: string;
158
+ input: Record<string, unknown>;
159
+ }
160
+
161
+ export function PendingToolCall({ name, input }: PendingToolCallProps) {
162
+ // Hide TodoWrite (shown in TodoList component)
163
+ if (name === 'TodoWrite') return null;
164
+
165
+ const displayInput = formatToolInput(name, input);
166
+
167
+ return (
168
+ <Box marginTop={1}>
169
+ <Text color={colors.tool}>
170
+ <InkSpinner type="dots" />
95
171
  </Text>
172
+ <Text> </Text>
173
+ <Text bold>{name}</Text>
174
+ {displayInput && (
175
+ <>
176
+ <Text> </Text>
177
+ <Text color={colors.textSecondary}>{truncate(displayInput, 60)}</Text>
178
+ </>
179
+ )}
96
180
  </Box>
97
181
  );
98
182
  }
99
183
 
184
+ interface ToolResultMetadata {
185
+ title?: string;
186
+ subtitle?: string;
187
+ size?: number;
188
+ statusCode?: number;
189
+ contentType?: string;
190
+ duration?: number;
191
+ }
192
+
100
193
  interface ToolResultProps {
101
194
  name: string;
102
195
  success: boolean;
103
196
  output: string;
197
+ metadata?: ToolResultMetadata;
104
198
  }
105
199
 
106
- export function ToolResult({ name, success, output }: ToolResultProps) {
107
- const firstLine = output.split('\n')[0]?.trim() || '';
108
- const displayOutput = firstLine.length > 50 ? firstLine.slice(0, 47) + '...' : firstLine;
200
+ export function ToolResult({ name, success, output, metadata }: ToolResultProps) {
109
201
  const statusColor = success ? colors.success : colors.error;
110
- const statusIcon = success ? icons.success : icons.error;
202
+
203
+ // If metadata has subtitle (e.g., "Received 540.3KB (200 OK)"), show it
204
+ if (metadata?.subtitle) {
205
+ return (
206
+ <Box marginLeft={2}>
207
+ <Text color={colors.textMuted}>{icons.treeEnd}</Text>
208
+ <Text> </Text>
209
+ <Text color={statusColor}>{metadata.subtitle}</Text>
210
+ </Box>
211
+ );
212
+ }
213
+
214
+ // TodoWrite: Don't show result (TodoList component shows the full list)
215
+ if (name === 'TodoWrite') {
216
+ return null;
217
+ }
218
+
219
+ // Default: Show first line of output with status icon
220
+ const displayOutput = truncate(output.split('\n')[0]?.trim() || '', 60);
111
221
 
112
222
  return (
113
223
  <Box marginLeft={2}>
114
- <Text dimColor>
115
- <Text color={statusColor}>{statusIcon}</Text> {name}{' '}
116
- <Text color={colors.textMuted}>{displayOutput}</Text>
117
- </Text>
224
+ <Text color={colors.textMuted}>{icons.treeEnd}</Text>
225
+ <Text> </Text>
226
+ <Text color={statusColor}>{displayOutput || (success ? 'Done' : 'Failed')}</Text>
118
227
  </Box>
119
228
  );
120
229
  }
@@ -150,11 +259,10 @@ interface WelcomeMessageProps {
150
259
  model: string;
151
260
  }
152
261
 
153
- export function WelcomeMessage({ model }: WelcomeMessageProps) {
262
+ export function WelcomeMessage({ model: _model }: WelcomeMessageProps) {
154
263
  return (
155
264
  <Box marginTop={1} marginBottom={0}>
156
- <Text color={colors.textMuted}>Welcome to </Text>
157
- <Text color={colors.brand}>{model}</Text>
265
+ <Text color={colors.textMuted}>? for help · Ctrl+C to exit</Text>
158
266
  </Box>
159
267
  );
160
268
  }
@@ -1,22 +1,26 @@
1
1
  /**
2
- * Model Selector Component - Interactive model selection with fuzzy filter
2
+ * Model Selector Component - Interactive model selection from cached providers
3
3
  */
4
- import { useState, useEffect } from 'react';
4
+ import { useState, useEffect, useMemo } from 'react';
5
5
  import { Box, Text, useInput } from 'ink';
6
6
  import TextInput from 'ink-text-input';
7
7
  import { colors, icons } from './theme.js';
8
8
  import { LoadingSpinner } from './Spinner.js';
9
+ import { getProviderStore, type ModelInfo } from '../../providers/store.js';
10
+ import { getProvider } from '../../providers/registry.js';
11
+ import type { ProviderName } from '../../providers/index.js';
9
12
 
10
- interface Model {
11
- id: string;
12
- name: string;
13
+ interface ModelItem {
14
+ providerId: ProviderName;
15
+ providerName: string;
16
+ model: ModelInfo;
13
17
  }
14
18
 
15
19
  interface ModelSelectorProps {
16
20
  currentModel: string;
17
- onSelect: (modelId: string) => void;
21
+ onSelect: (modelId: string, providerId: ProviderName) => void;
18
22
  onCancel: () => void;
19
- listModels: () => Promise<Model[]>;
23
+ listModels: () => Promise<{ id: string; name: string }[]>; // Fallback for current provider
20
24
  }
21
25
 
22
26
  export function ModelSelector({
@@ -25,32 +29,86 @@ export function ModelSelector({
25
29
  onCancel,
26
30
  listModels,
27
31
  }: ModelSelectorProps) {
28
- const [models, setModels] = useState<Model[]>([]);
32
+ const store = getProviderStore();
29
33
  const [loading, setLoading] = useState(true);
30
- const [error, setError] = useState<string | null>(null);
31
34
  const [filter, setFilter] = useState('');
32
35
  const [selectedIndex, setSelectedIndex] = useState(0);
36
+ const [allModels, setAllModels] = useState<ModelItem[]>([]);
33
37
 
38
+ // Load models from cache
34
39
  useEffect(() => {
35
- const fetchModels = async () => {
36
- try {
37
- const result = await listModels();
38
- setModels(result);
39
- } catch (err) {
40
- setError(err instanceof Error ? err.message : 'Failed to fetch models');
41
- } finally {
42
- setLoading(false);
40
+ const loadModels = async () => {
41
+ const connectedProviders = store.getConnectedProviders();
42
+ const items: ModelItem[] = [];
43
+
44
+ for (const providerId of connectedProviders) {
45
+ const cachedModels = store.getModels(providerId);
46
+ const providerDef = getProvider(providerId);
47
+ const providerName = providerDef?.name || providerId;
48
+
49
+ for (const model of cachedModels) {
50
+ items.push({
51
+ providerId,
52
+ providerName,
53
+ model,
54
+ });
55
+ }
43
56
  }
57
+
58
+ // If no cached models, fallback to listModels for current provider
59
+ if (items.length === 0) {
60
+ try {
61
+ const models = await listModels();
62
+ for (const model of models) {
63
+ items.push({
64
+ providerId: 'anthropic' as ProviderName, // Default, will be overridden
65
+ providerName: 'Current Provider',
66
+ model,
67
+ });
68
+ }
69
+ } catch {
70
+ // Ignore errors
71
+ }
72
+ }
73
+
74
+ setAllModels(items);
75
+ setLoading(false);
44
76
  };
45
- fetchModels();
46
- }, [listModels]);
47
-
48
- // Fuzzy filter models
49
- const filtered = models.filter(
50
- (m) =>
51
- m.id.toLowerCase().includes(filter.toLowerCase()) ||
52
- m.name.toLowerCase().includes(filter.toLowerCase())
53
- );
77
+
78
+ loadModels();
79
+ }, [store, listModels]);
80
+
81
+ // Filter models
82
+ const filterLower = filter.toLowerCase();
83
+ const filtered = useMemo(() => {
84
+ return allModels.filter(
85
+ (item) =>
86
+ item.model.id.toLowerCase().includes(filterLower) ||
87
+ item.model.name.toLowerCase().includes(filterLower) ||
88
+ item.providerName.toLowerCase().includes(filterLower)
89
+ );
90
+ }, [allModels, filterLower]);
91
+
92
+ // Group by provider for display
93
+ const groupedModels = useMemo(() => {
94
+ const groups: Record<string, ModelItem[]> = {};
95
+ for (const item of filtered) {
96
+ if (!groups[item.providerId]) {
97
+ groups[item.providerId] = [];
98
+ }
99
+ groups[item.providerId].push(item);
100
+ }
101
+ return groups;
102
+ }, [filtered]);
103
+
104
+ // Flat list for navigation
105
+ const flatList = useMemo(() => {
106
+ const items: ModelItem[] = [];
107
+ for (const providerId of Object.keys(groupedModels)) {
108
+ items.push(...groupedModels[providerId]);
109
+ }
110
+ return items;
111
+ }, [groupedModels]);
54
112
 
55
113
  // Reset selection when filter changes
56
114
  useEffect(() => {
@@ -62,10 +120,11 @@ export function ModelSelector({
62
120
  if (key.upArrow) {
63
121
  setSelectedIndex((i) => Math.max(0, i - 1));
64
122
  } else if (key.downArrow) {
65
- setSelectedIndex((i) => Math.min(filtered.length - 1, i + 1));
123
+ setSelectedIndex((i) => Math.min(flatList.length - 1, i + 1));
66
124
  } else if (key.return) {
67
- if (filtered.length > 0) {
68
- onSelect(filtered[selectedIndex].id);
125
+ if (flatList.length > 0) {
126
+ const selected = flatList[selectedIndex];
127
+ onSelect(selected.model.id, selected.providerId);
69
128
  }
70
129
  } else if (key.escape) {
71
130
  onCancel();
@@ -81,43 +140,99 @@ export function ModelSelector({
81
140
  );
82
141
  }
83
142
 
84
- if (error) {
85
- onCancel();
86
- return null;
87
- }
143
+ // Count providers and models
144
+ const providerCount = Object.keys(groupedModels).length;
145
+ const modelCount = flatList.length;
88
146
 
89
- const maxVisible = 8;
147
+ // Calculate visible window
148
+ const maxVisible = 10;
90
149
  const startIndex = Math.max(
91
150
  0,
92
- Math.min(selectedIndex - Math.floor(maxVisible / 2), filtered.length - maxVisible)
151
+ Math.min(selectedIndex - Math.floor(maxVisible / 2), flatList.length - maxVisible)
93
152
  );
94
- const visibleModels = filtered.slice(startIndex, startIndex + maxVisible);
153
+ const endIndex = Math.min(startIndex + maxVisible, flatList.length);
154
+
155
+ // Build visible items with provider headers
156
+ let currentIdx = 0;
157
+ const renderItems: Array<{ type: 'header' | 'model'; content: string; item?: ModelItem }> = [];
158
+
159
+ for (const providerId of Object.keys(groupedModels)) {
160
+ const models = groupedModels[providerId];
161
+ const firstIdx = currentIdx;
162
+ const lastIdx = currentIdx + models.length - 1;
163
+
164
+ // Check if any model from this provider is in visible range
165
+ if (lastIdx >= startIndex && firstIdx < endIndex) {
166
+ // Add header if first visible item is from this provider
167
+ const providerDef = getProvider(providerId as ProviderName);
168
+ const connection = store.getConnection(providerId as ProviderName);
169
+ const headerText = `${providerDef?.name || providerId}${connection ? ` (${connection.method})` : ''}:`;
170
+
171
+ // Only add header if we're showing models from this provider
172
+ const visibleModelsFromProvider = models.filter((_, i) => {
173
+ const globalIdx = currentIdx + i;
174
+ return globalIdx >= startIndex && globalIdx < endIndex;
175
+ });
176
+
177
+ if (visibleModelsFromProvider.length > 0 && (firstIdx >= startIndex || renderItems.length === 0)) {
178
+ renderItems.push({ type: 'header', content: headerText });
179
+ }
180
+
181
+ for (let i = 0; i < models.length; i++) {
182
+ const globalIdx = currentIdx + i;
183
+ if (globalIdx >= startIndex && globalIdx < endIndex) {
184
+ renderItems.push({ type: 'model', content: '', item: models[i] });
185
+ }
186
+ }
187
+ }
188
+
189
+ currentIdx += models.length;
190
+ }
95
191
 
96
192
  return (
97
193
  <Box flexDirection="column">
98
- <Box>
99
- <Text color={colors.primary}>{icons.prompt} </Text>
100
- <TextInput
101
- value={filter}
102
- onChange={setFilter}
103
- placeholder="Type to filter models..."
104
- />
194
+ <Text color={colors.primary} bold>
195
+ Select Model
196
+ </Text>
197
+
198
+ <Box marginTop={1}>
199
+ <Text color={colors.textMuted}>{icons.prompt} </Text>
200
+ <TextInput value={filter} onChange={setFilter} placeholder="Filter models..." />
105
201
  </Box>
202
+
106
203
  <Box flexDirection="column" marginTop={1}>
107
- {visibleModels.length === 0 ? (
108
- <Text color={colors.textMuted}>No models match "{filter}"</Text>
204
+ {flatList.length === 0 ? (
205
+ <Box flexDirection="column">
206
+ <Text color={colors.textMuted}>No cached models.</Text>
207
+ <Text color={colors.textMuted}>Use /provider to connect and cache models.</Text>
208
+ </Box>
109
209
  ) : (
110
- visibleModels.map((m, i) => {
111
- const actualIndex = startIndex + i;
112
- const isSelected = actualIndex === selectedIndex;
113
- const isCurrent = m.id === currentModel;
210
+ renderItems.map((renderItem, i) => {
211
+ if (renderItem.type === 'header') {
212
+ return (
213
+ <Text key={`header-${i}`} color={colors.textSecondary}>
214
+ {renderItem.content}
215
+ </Text>
216
+ );
217
+ }
218
+
219
+ const item = renderItem.item!;
220
+ const globalIndex = flatList.findIndex(
221
+ (f) => f.providerId === item.providerId && f.model.id === item.model.id
222
+ );
223
+ const isSelected = globalIndex === selectedIndex;
224
+ const isCurrent = item.model.id === currentModel;
225
+
114
226
  return (
115
- <Box key={m.id}>
227
+ <Box key={`${item.providerId}-${item.model.id}`} paddingLeft={2}>
116
228
  <Text color={isSelected ? colors.primary : colors.textMuted}>
117
229
  {isSelected ? icons.arrow : ' '}
118
230
  </Text>
231
+ <Text color={isCurrent ? colors.primary : colors.textMuted}>
232
+ {isCurrent ? icons.radio : icons.radioEmpty}
233
+ </Text>
119
234
  <Text color={isSelected ? colors.text : colors.textSecondary} bold={isSelected}>
120
- {m.name}
235
+ {' '}{item.model.name || item.model.id}
121
236
  </Text>
122
237
  {isCurrent && <Text color={colors.success}> (current)</Text>}
123
238
  </Box>
@@ -125,9 +240,11 @@ export function ModelSelector({
125
240
  })
126
241
  )}
127
242
  </Box>
243
+
128
244
  <Box marginTop={1}>
129
245
  <Text color={colors.textMuted}>
130
- {filtered.length} models · ↑↓ navigate · Enter select · Esc cancel
246
+ {providerCount} provider{providerCount !== 1 ? 's' : ''} · {modelCount} model
247
+ {modelCount !== 1 ? 's' : ''} · ↑↓ navigate · Enter select · Esc cancel
131
248
  </Text>
132
249
  </Box>
133
250
  </Box>