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
@@ -11,6 +11,7 @@ import {
11
11
  AssistantMessage,
12
12
  ToolCall,
13
13
  ToolResult,
14
+ PendingToolCall,
14
15
  InfoMessage,
15
16
  WelcomeMessage,
16
17
  CompletionMessage,
@@ -19,13 +20,24 @@ import { Header } from './Header.js';
19
20
  import { ProgressBar } from './Spinner.js';
20
21
  import { PromptInput, ConfirmPrompt } from './Input.js';
21
22
  import { ModelSelector } from './ModelSelector.js';
23
+ import { ProviderManager } from './ProviderManager.js';
22
24
  import { CommandSuggestions, getFilteredCommands } from './CommandSuggestions.js';
25
+ import {
26
+ PermissionPrompt,
27
+ PermissionRulesDisplay,
28
+ PermissionAuditDisplay,
29
+ } from './PermissionPrompt.js';
30
+ import { TodoList } from './TodoList.js';
23
31
  import { colors, icons } from './theme.js';
32
+ import { getTodos } from '../../tools/index.js';
33
+ import type { ProviderName } from '../../providers/index.js';
34
+ import type { ApprovalAction, ApprovalSuggestion } from '../../permissions/types.js';
35
+ import { gatherContextFiles, buildInitPrompt, getContextSummary } from '../../memory/index.js';
24
36
 
25
37
  // Types
26
38
  interface HistoryItem {
27
39
  id: string;
28
- type: 'header' | 'welcome' | 'user' | 'assistant' | 'tool_call' | 'tool_result' | 'info' | 'completion';
40
+ type: 'header' | 'welcome' | 'user' | 'assistant' | 'tool_call' | 'tool_result' | 'info' | 'completion' | 'todos';
29
41
  content: string;
30
42
  meta?: Record<string, unknown>;
31
43
  }
@@ -33,11 +45,14 @@ interface HistoryItem {
33
45
  interface ConfirmState {
34
46
  tool: string;
35
47
  input: Record<string, unknown>;
36
- resolve: (confirmed: boolean) => void;
48
+ suggestions: ApprovalSuggestion[];
49
+ resolve: (action: ApprovalAction) => void;
37
50
  }
38
51
 
39
52
  interface SettingsManager {
40
53
  save: (settings: { model?: string }) => Promise<void>;
54
+ getCwd?: () => string;
55
+ addPermissionRule?: (pattern: string, type: 'allow' | 'deny', level?: 'global' | 'project' | 'local') => Promise<void>;
41
56
  }
42
57
 
43
58
  interface Session {
@@ -46,10 +61,16 @@ interface Session {
46
61
  updatedAt: string;
47
62
  }
48
63
 
64
+ interface PermissionSettings {
65
+ allow?: string[];
66
+ deny?: string[];
67
+ }
68
+
49
69
  interface AppProps {
50
70
  config: AgentConfig;
51
71
  settingsManager?: SettingsManager;
52
72
  resumeLatest?: boolean;
73
+ permissionSettings?: PermissionSettings;
53
74
  }
54
75
 
55
76
  // ============================================================================
@@ -60,12 +81,28 @@ function useAgent(config: AgentConfig) {
60
81
  return agent;
61
82
  }
62
83
 
84
+ // ============================================================================
85
+ // Utils
86
+ // ============================================================================
87
+ const genId = () => Math.random().toString(36).slice(2);
88
+
89
+ const formatRelativeTime = (dateStr: string) => {
90
+ const diff = Date.now() - new Date(dateStr).getTime();
91
+ const mins = Math.floor(diff / 60000);
92
+ const hrs = Math.floor(mins / 60);
93
+ const days = Math.floor(hrs / 24);
94
+ if (mins < 60) return `${mins}m`;
95
+ if (hrs < 24) return `${hrs}h`;
96
+ return `${days}d`;
97
+ };
98
+
63
99
  // ============================================================================
64
100
  // Help Component
65
101
  // ============================================================================
66
102
  function HelpPanel() {
67
103
  const commands: [string, string][] = [
68
104
  ['/model [name]', 'Switch model'],
105
+ ['/provider', 'Manage providers'],
69
106
  ['/sessions', 'List sessions'],
70
107
  ['/resume [n]', 'Resume session'],
71
108
  ['/new', 'New session'],
@@ -92,16 +129,6 @@ interface SessionsTableProps {
92
129
  }
93
130
 
94
131
  function SessionsTable({ sessions }: SessionsTableProps) {
95
- const formatTime = (dateStr: string) => {
96
- const diff = Date.now() - new Date(dateStr).getTime();
97
- const mins = Math.floor(diff / 60000);
98
- const hrs = Math.floor(mins / 60);
99
- const days = Math.floor(hrs / 24);
100
- if (mins < 60) return `${mins}m`;
101
- if (hrs < 24) return `${hrs}h`;
102
- return `${days}d`;
103
- };
104
-
105
132
  return (
106
133
  <Box flexDirection="column">
107
134
  {sessions.slice(0, 6).map((s, i) => (
@@ -109,23 +136,74 @@ function SessionsTable({ sessions }: SessionsTableProps) {
109
136
  <Text color={colors.textMuted}>{String(i + 1).padEnd(2)}</Text>
110
137
  <Text color={colors.primary}>{s.id.slice(0, 7).padEnd(8)}</Text>
111
138
  <Text>{s.title.slice(0, 25).padEnd(26)}</Text>
112
- <Text color={colors.textMuted}>{formatTime(s.updatedAt)}</Text>
139
+ <Text color={colors.textMuted}>{formatRelativeTime(s.updatedAt)}</Text>
113
140
  </Text>
114
141
  ))}
115
142
  </Box>
116
143
  );
117
144
  }
118
145
 
146
+ // ============================================================================
147
+ // Memory Files Display Component
148
+ // ============================================================================
149
+ interface MemoryFileInfo {
150
+ path: string;
151
+ level: string;
152
+ size: number;
153
+ type: 'file' | 'rule';
154
+ }
155
+
156
+ function MemoryFilesDisplay({ files }: { files: MemoryFileInfo[] }) {
157
+ const formatSize = (bytes: number): string => {
158
+ if (bytes < 1024) return `${bytes}B`;
159
+ return `${(bytes / 1024).toFixed(1)}KB`;
160
+ };
161
+
162
+ const memoryFiles = files.filter((f) => f.type === 'file');
163
+ const ruleFiles = files.filter((f) => f.type === 'rule');
164
+
165
+ return (
166
+ <Box flexDirection="column">
167
+ {memoryFiles.length > 0 && (
168
+ <>
169
+ <Text color={colors.info}>Loaded Memory Files:</Text>
170
+ {memoryFiles.map((f, i) => (
171
+ <Text key={f.path}>
172
+ <Text color={colors.textMuted}> [{i + 1}] </Text>
173
+ <Text color={colors.primary}>{f.path} </Text>
174
+ <Text color={colors.textMuted}>({f.level}, {formatSize(f.size)})</Text>
175
+ </Text>
176
+ ))}
177
+ </>
178
+ )}
179
+ {ruleFiles.length > 0 && (
180
+ <Box flexDirection="column" marginTop={memoryFiles.length > 0 ? 1 : 0}>
181
+ <Text color={colors.info}>
182
+ Loaded Rules:
183
+ </Text>
184
+ {ruleFiles.map((f, i) => (
185
+ <Text key={f.path}>
186
+ <Text color={colors.textMuted}> [{i + 1}] </Text>
187
+ <Text color={colors.warning}>{f.path} </Text>
188
+ <Text color={colors.textMuted}>({f.level}, {formatSize(f.size)})</Text>
189
+ </Text>
190
+ ))}
191
+ </Box>
192
+ )}
193
+ {files.length === 0 && (
194
+ <Text color={colors.textMuted}>No memory files loaded</Text>
195
+ )}
196
+ </Box>
197
+ );
198
+ }
199
+
119
200
  // ============================================================================
120
201
  // Main App
121
202
  // ============================================================================
122
- export function App({ config, settingsManager, resumeLatest }: AppProps) {
203
+ export function App({ config, settingsManager, resumeLatest, permissionSettings }: AppProps) {
123
204
  const { exit } = useApp();
124
205
  const agent = useAgent(config);
125
206
 
126
- // Generate unique ID
127
- const genId = () => Math.random().toString(36).slice(2);
128
-
129
207
  // Initial header item
130
208
  const cwd = config.cwd || process.cwd();
131
209
  const home = process.env.HOME || '';
@@ -154,11 +232,17 @@ export function App({ config, settingsManager, resumeLatest }: AppProps) {
154
232
  const [isThinking, setIsThinking] = useState(false);
155
233
  const [streamingText, setStreamingText] = useState('');
156
234
  const streamingTextRef = useRef(''); // Track current streaming text for closure
235
+ const [processingStartTime, setProcessingStartTime] = useState<number | undefined>(undefined);
236
+ const [tokenCount, setTokenCount] = useState(0);
157
237
  const [confirmState, setConfirmState] = useState<ConfirmState | null>(null);
158
238
  const [showModelSelector, setShowModelSelector] = useState(false);
239
+ const [showProviderManager, setShowProviderManager] = useState(false);
159
240
  const [currentModel, setCurrentModel] = useState(config.model);
160
241
  const [cmdSuggestionIndex, setCmdSuggestionIndex] = useState(0);
161
242
  const [inputKey, setInputKey] = useState(0); // Force cursor to end after autocomplete
243
+ const [pendingTool, setPendingTool] = useState<{ name: string; input: Record<string, unknown> } | null>(null);
244
+ const pendingToolRef = useRef<{ name: string; input: Record<string, unknown> } | null>(null);
245
+ const [todos, setTodos] = useState<ReturnType<typeof getTodos>>([]);
162
246
 
163
247
  // Check if showing command suggestions
164
248
  const showCmdSuggestions = input.startsWith('/') && !isProcessing;
@@ -177,12 +261,30 @@ export function App({ config, settingsManager, resumeLatest }: AppProps) {
177
261
  // Initialize
178
262
  useEffect(() => {
179
263
  const init = async () => {
180
- agent.setConfirmCallback(async (tool: string, toolInput: unknown) => {
181
- return new Promise<boolean>((resolve) => {
182
- setConfirmState({ tool, input: toolInput as Record<string, unknown>, resolve });
264
+ // Initialize permission system with settings
265
+ await agent.initializePermissions(permissionSettings);
266
+
267
+ // Set enhanced confirm callback with approval options
268
+ agent.setEnhancedConfirmCallback(async (tool, toolInput, suggestions) => {
269
+ return new Promise<ApprovalAction>((resolve) => {
270
+ setConfirmState({
271
+ tool,
272
+ input: toolInput as Record<string, unknown>,
273
+ suggestions,
274
+ resolve,
275
+ });
183
276
  });
184
277
  });
185
278
 
279
+ // Set callback to save permission rules to settings.local.json
280
+ if (settingsManager?.addPermissionRule) {
281
+ agent.setSaveRuleCallback(async (tool, pattern) => {
282
+ // Format as Claude Code style pattern: Tool(pattern) or just Tool
283
+ const rulePattern = pattern ? `${tool}(${pattern})` : tool;
284
+ await settingsManager.addPermissionRule!(rulePattern, 'allow', 'local');
285
+ });
286
+ }
287
+
186
288
  if (resumeLatest) {
187
289
  const resumed = await agent.resumeLatest();
188
290
  if (resumed) {
@@ -191,22 +293,27 @@ export function App({ config, settingsManager, resumeLatest }: AppProps) {
191
293
  }
192
294
  };
193
295
  init();
194
- }, [agent, resumeLatest, addHistory]);
296
+ }, [agent, resumeLatest, addHistory, permissionSettings, settingsManager]);
195
297
 
196
- // Handle confirm
197
- const handleConfirm = (confirmed: boolean) => {
298
+ // Handle permission decision
299
+ const handlePermissionDecision = (action: ApprovalAction) => {
198
300
  if (confirmState) {
199
- confirmState.resolve(confirmed);
301
+ confirmState.resolve(action);
200
302
  setConfirmState(null);
201
303
  }
202
304
  };
203
305
 
204
306
  // Handle model selection
205
- const handleModelSelect = async (model: string) => {
307
+ const handleModelSelect = async (model: string, providerId?: ProviderName) => {
206
308
  agent.setModel(model);
207
309
  setCurrentModel(model);
208
310
  setShowModelSelector(false);
209
- addHistory({ type: 'info', content: `Model: ${model}` });
311
+
312
+ if (providerId) {
313
+ addHistory({ type: 'info', content: `${providerId}: ${model}` });
314
+ } else {
315
+ addHistory({ type: 'info', content: `Model: ${model}` });
316
+ }
210
317
 
211
318
  // Save to settings for next startup
212
319
  if (settingsManager) {
@@ -302,13 +409,118 @@ export function App({ config, settingsManager, resumeLatest }: AppProps) {
302
409
  return true;
303
410
  }
304
411
 
412
+ case 'provider': {
413
+ setShowProviderManager(true);
414
+ return true;
415
+ }
416
+
417
+ case 'permissions': {
418
+ const permManager = agent.getPermissionManager();
419
+
420
+ if (arg === 'audit') {
421
+ // Show audit log
422
+ const auditLog = permManager.getAuditLog(20);
423
+ if (auditLog.length === 0) {
424
+ addHistory({ type: 'info', content: 'No permission decisions recorded yet' });
425
+ } else {
426
+ const entries = auditLog.map((e) => ({
427
+ time: e.timestamp.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' }),
428
+ tool: e.tool,
429
+ input: e.inputSummary,
430
+ decision: e.decision,
431
+ rule: e.matchedRule,
432
+ }));
433
+ addHistory({
434
+ type: 'info',
435
+ content: '__PERMISSION_AUDIT__',
436
+ meta: { entries },
437
+ });
438
+ }
439
+ } else if (arg === 'stats') {
440
+ // Show statistics
441
+ const stats = permManager.getAuditStats();
442
+ addHistory({
443
+ type: 'info',
444
+ content: `Permissions: ${stats.allowed + stats.confirmed} allowed, ${stats.denied + stats.rejected} denied`,
445
+ });
446
+ } else {
447
+ // Show rules
448
+ const rules = permManager.getRules();
449
+ const prompts = permManager.getAllowedPrompts();
450
+ const displayRules = rules.map((r) => ({
451
+ type: r.scope === 'session' ? 'Session' : r.description?.startsWith('Settings') ? 'Settings' : 'Built-in',
452
+ tool: typeof r.tool === 'string' ? r.tool : r.tool.toString(),
453
+ pattern: typeof r.pattern === 'string' ? r.pattern : r.pattern?.toString(),
454
+ scope: r.scope ?? 'session',
455
+ mode: r.mode,
456
+ }));
457
+ addHistory({
458
+ type: 'info',
459
+ content: '__PERMISSIONS__',
460
+ meta: { rules: displayRules, prompts },
461
+ });
462
+ }
463
+ return true;
464
+ }
465
+
305
466
  case 'init': {
306
- addHistory({ type: 'info', content: '/init command not available in this version' });
467
+ // Gather context files and generate AGENT.md
468
+ addHistory({ type: 'info', content: 'Analyzing codebase...' });
469
+
470
+ const contextFiles = await gatherContextFiles(cwd);
471
+ addHistory({ type: 'info', content: getContextSummary(contextFiles) });
472
+
473
+ // Check if AGENT.md already exists
474
+ const memoryManager = agent.getMemoryManager();
475
+ const existingPath = await memoryManager.getExistingProjectMemoryPath(cwd);
476
+ let existingContent: string | undefined;
477
+
478
+ if (existingPath) {
479
+ try {
480
+ const fs = await import('fs/promises');
481
+ existingContent = await fs.readFile(existingPath, 'utf-8');
482
+ addHistory({
483
+ type: 'info',
484
+ content: `Found existing: ${existingPath.replace(cwd, '.')}`,
485
+ });
486
+ } catch {
487
+ // File doesn't exist or can't be read
488
+ }
489
+ }
490
+
491
+ // Build init prompt and run through agent
492
+ const initPrompt = buildInitPrompt(contextFiles, existingContent);
493
+ addHistory({ type: 'info', content: 'Generating AGENT.md...' });
494
+ addHistory({ type: 'user', content: '/init' });
495
+ await runAgent(initPrompt);
307
496
  return true;
308
497
  }
309
498
 
310
499
  case 'memory': {
311
- addHistory({ type: 'info', content: '/memory command not available in this version' });
500
+ // Show loaded memory files
501
+ const memoryManager = agent.getMemoryManager();
502
+ const loadedFiles = memoryManager.getLoadedFileList();
503
+
504
+ if (loadedFiles.length === 0) {
505
+ // Try to load memory first
506
+ await agent.loadMemory();
507
+ const filesAfterLoad = memoryManager.getLoadedFileList();
508
+ if (filesAfterLoad.length === 0) {
509
+ addHistory({ type: 'info', content: 'No memory files found' });
510
+ } else {
511
+ addHistory({
512
+ type: 'info',
513
+ content: '__MEMORY__',
514
+ meta: { files: filesAfterLoad },
515
+ });
516
+ }
517
+ } else {
518
+ addHistory({
519
+ type: 'info',
520
+ content: '__MEMORY__',
521
+ meta: { files: loadedFiles },
522
+ });
523
+ }
312
524
  return true;
313
525
  }
314
526
 
@@ -328,6 +540,8 @@ export function App({ config, settingsManager, resumeLatest }: AppProps) {
328
540
  streamingTextRef.current = '';
329
541
  interruptFlagRef.current = false;
330
542
  const startTime = Date.now();
543
+ setProcessingStartTime(startTime);
544
+ setTokenCount(0);
331
545
 
332
546
  try {
333
547
  for await (const event of agent.run(prompt)) {
@@ -341,6 +555,8 @@ export function App({ config, settingsManager, resumeLatest }: AppProps) {
341
555
  setIsThinking(false);
342
556
  streamingTextRef.current += event.text;
343
557
  setStreamingText(streamingTextRef.current);
558
+ // Estimate token count (roughly 4 chars per token)
559
+ setTokenCount((prev) => prev + Math.max(1, Math.ceil(event.text.length / 4)));
344
560
  break;
345
561
 
346
562
  case 'tool_start':
@@ -350,19 +566,46 @@ export function App({ config, settingsManager, resumeLatest }: AppProps) {
350
566
  streamingTextRef.current = '';
351
567
  setStreamingText('');
352
568
  }
353
- addHistory({
354
- type: 'tool_call',
355
- content: event.name,
356
- meta: { toolName: event.name, input: event.input },
357
- });
569
+ // Set pending tool for spinner animation (use both state and ref)
570
+ const toolInfo = { name: event.name, input: event.input as Record<string, unknown> };
571
+ pendingToolRef.current = toolInfo;
572
+ setPendingTool(toolInfo);
358
573
  break;
359
574
 
360
575
  case 'tool_result':
361
- addHistory({
362
- type: 'tool_result',
363
- content: event.result.output,
364
- meta: { toolName: event.name, success: event.result.success },
365
- });
576
+ // For TodoWrite: add todos first, then hide tool_call/tool_result
577
+ if (event.name === 'TodoWrite') {
578
+ const currentTodos = getTodos();
579
+ setTodos(currentTodos);
580
+ if (currentTodos.length > 0) {
581
+ addHistory({
582
+ type: 'todos',
583
+ content: '',
584
+ meta: { todos: currentTodos },
585
+ });
586
+ }
587
+ } else {
588
+ // Add tool_call to history (now completed) - use ref for correct value
589
+ if (pendingToolRef.current) {
590
+ addHistory({
591
+ type: 'tool_call',
592
+ content: pendingToolRef.current.name,
593
+ meta: { toolName: pendingToolRef.current.name, input: pendingToolRef.current.input },
594
+ });
595
+ }
596
+ // Add tool_result to history
597
+ addHistory({
598
+ type: 'tool_result',
599
+ content: event.result.output,
600
+ meta: {
601
+ toolName: event.name,
602
+ success: event.result.success,
603
+ metadata: event.result.metadata,
604
+ },
605
+ });
606
+ }
607
+ pendingToolRef.current = null;
608
+ setPendingTool(null);
366
609
  setIsThinking(true);
367
610
  break;
368
611
 
@@ -380,6 +623,7 @@ export function App({ config, settingsManager, resumeLatest }: AppProps) {
380
623
  // Add completion message with duration
381
624
  const durationMs = Date.now() - startTime;
382
625
  addHistory({ type: 'completion', content: '', meta: { durationMs } });
626
+ setProcessingStartTime(undefined);
383
627
  break;
384
628
  }
385
629
  }
@@ -421,6 +665,47 @@ export function App({ config, settingsManager, resumeLatest }: AppProps) {
421
665
  return;
422
666
  }
423
667
 
668
+ // Handle # prefix for quick memory adds
669
+ // ## note -> user memory (~/.gencode/AGENT.md)
670
+ // # note -> project memory (./AGENT.md)
671
+ if (trimmed.startsWith('#') && !trimmed.startsWith('#!/')) {
672
+ const memoryManager = agent.getMemoryManager();
673
+ let level: 'user' | 'project';
674
+ let content: string;
675
+
676
+ if (trimmed.startsWith('## ')) {
677
+ level = 'user';
678
+ content = trimmed.slice(3).trim();
679
+ } else if (trimmed.startsWith('# ')) {
680
+ level = 'project';
681
+ content = trimmed.slice(2).trim();
682
+ } else {
683
+ // Just # with no space, treat as project
684
+ level = 'project';
685
+ content = trimmed.slice(1).trim();
686
+ }
687
+
688
+ if (!content) {
689
+ addHistory({ type: 'info', content: 'Empty memory entry ignored' });
690
+ return;
691
+ }
692
+
693
+ try {
694
+ const savedPath = await memoryManager.quickAdd(content, level, cwd);
695
+ const displayPath = savedPath.replace(process.env.HOME || '', '~');
696
+ addHistory({
697
+ type: 'info',
698
+ content: `Added to ${level} memory: ${displayPath}`,
699
+ });
700
+ } catch (error) {
701
+ addHistory({
702
+ type: 'info',
703
+ content: `Failed to add to memory: ${error instanceof Error ? error.message : String(error)}`,
704
+ });
705
+ }
706
+ return;
707
+ }
708
+
424
709
  if (trimmed.startsWith('/')) {
425
710
  const handled = await handleCommand(trimmed);
426
711
  if (!handled) {
@@ -496,6 +781,7 @@ export function App({ config, settingsManager, resumeLatest }: AppProps) {
496
781
  name={(item.meta?.toolName as string) || ''}
497
782
  success={(item.meta?.success as boolean) ?? true}
498
783
  output={item.content}
784
+ metadata={item.meta?.metadata as Record<string, unknown> | undefined}
499
785
  />
500
786
  );
501
787
  case 'info':
@@ -503,9 +789,29 @@ export function App({ config, settingsManager, resumeLatest }: AppProps) {
503
789
  if (item.content === '__SESSIONS__' && item.meta?.input) {
504
790
  return <SessionsTable sessions={item.meta.input as Session[]} />;
505
791
  }
792
+ if (item.content === '__PERMISSIONS__' && item.meta?.rules) {
793
+ return (
794
+ <PermissionRulesDisplay
795
+ rules={item.meta.rules as { type: string; tool: string; pattern?: string; scope: string; mode: string }[]}
796
+ allowedPrompts={item.meta.prompts as { tool: string; prompt: string }[] | undefined}
797
+ />
798
+ );
799
+ }
800
+ if (item.content === '__PERMISSION_AUDIT__' && item.meta?.entries) {
801
+ return (
802
+ <PermissionAuditDisplay
803
+ entries={item.meta.entries as { time: string; tool: string; input: string; decision: string; rule?: string }[]}
804
+ />
805
+ );
806
+ }
807
+ if (item.content === '__MEMORY__' && item.meta?.files) {
808
+ return <MemoryFilesDisplay files={item.meta.files as MemoryFileInfo[]} />;
809
+ }
506
810
  return <InfoMessage text={item.content} />;
507
811
  case 'completion':
508
812
  return <CompletionMessage durationMs={(item.meta?.durationMs as number) || 0} />;
813
+ case 'todos':
814
+ return <TodoList todos={item.meta?.todos as ReturnType<typeof getTodos>} />;
509
815
  default:
510
816
  return null;
511
817
  }
@@ -517,15 +823,18 @@ export function App({ config, settingsManager, resumeLatest }: AppProps) {
517
823
  {(item) => <Box key={item.id}>{renderHistoryItem(item)}</Box>}
518
824
  </Static>
519
825
 
826
+ {pendingTool && !confirmState && <PendingToolCall name={pendingTool.name} input={pendingTool.input} />}
827
+
520
828
  {streamingText && <AssistantMessage text={streamingText} streaming />}
521
829
 
522
830
  {confirmState && (
523
- <Box flexDirection="column" marginTop={1}>
524
- <Text color={colors.warning}>
525
- {icons.warning} {confirmState.tool}
526
- </Text>
527
- <ConfirmPrompt message="Allow?" onConfirm={handleConfirm} />
528
- </Box>
831
+ <PermissionPrompt
832
+ tool={confirmState.tool}
833
+ input={confirmState.input}
834
+ suggestions={confirmState.suggestions}
835
+ onDecision={handlePermissionDecision}
836
+ projectPath={settingsManager?.getCwd?.() ?? process.cwd()}
837
+ />
529
838
  )}
530
839
 
531
840
  {showModelSelector && (
@@ -539,7 +848,20 @@ export function App({ config, settingsManager, resumeLatest }: AppProps) {
539
848
  </Box>
540
849
  )}
541
850
 
542
- {!confirmState && !showModelSelector && (
851
+ {showProviderManager && (
852
+ <Box marginTop={1}>
853
+ <ProviderManager
854
+ onClose={() => setShowProviderManager(false)}
855
+ onProviderChange={(providerId, model) => {
856
+ agent.setModel(model);
857
+ setCurrentModel(model);
858
+ addHistory({ type: 'info', content: `Switched to ${providerId}: ${model}` });
859
+ }}
860
+ />
861
+ </Box>
862
+ )}
863
+
864
+ {!confirmState && !showModelSelector && !showProviderManager && (
543
865
  <Box flexDirection="column" marginTop={1}>
544
866
  <PromptInput
545
867
  key={inputKey}
@@ -553,8 +875,8 @@ export function App({ config, settingsManager, resumeLatest }: AppProps) {
553
875
  </Box>
554
876
  )}
555
877
 
556
- {isProcessing ? (
557
- <ProgressBar />
878
+ {isProcessing && !confirmState ? (
879
+ <ProgressBar startTime={processingStartTime} tokenCount={tokenCount} isThinking={isThinking} />
558
880
  ) : showCmdSuggestions && cmdSuggestions.length > 0 ? (
559
881
  <Box marginTop={1}>
560
882
  <Text color={colors.textMuted}> Tab to complete · ↑↓ navigate</Text>
@@ -8,6 +8,9 @@ interface Command {
8
8
 
9
9
  export const COMMANDS: Command[] = [
10
10
  { name: '/model', description: 'Switch model' },
11
+ { name: '/provider', description: 'Manage providers' },
12
+ { name: '/permissions', description: 'View permission rules' },
13
+ { name: '/permissions audit', description: 'View permission audit log' },
11
14
  { name: '/sessions', description: 'List sessions' },
12
15
  { name: '/resume', description: 'Resume session' },
13
16
  { name: '/new', description: 'New session' },
@@ -1,6 +1,6 @@
1
1
  import { Box, Text } from 'ink';
2
2
  import { colors } from './theme.js';
3
- import { Logo } from './Logo.js';
3
+ import { BigLogo } from './Logo.js';
4
4
 
5
5
  interface HeaderProps {
6
6
  provider: string;
@@ -8,20 +8,14 @@ interface HeaderProps {
8
8
  cwd: string;
9
9
  }
10
10
 
11
- export function Header({ provider, model, cwd }: HeaderProps) {
12
- const home = process.env.HOME || '';
13
- const cwdDisplay = cwd.startsWith(home) ? '~' + cwd.slice(home.length) : cwd;
14
-
11
+ export function Header({ model, cwd }: HeaderProps) {
15
12
  return (
16
- <Box flexDirection="row" marginBottom={1} marginTop={1}>
17
- <Logo />
18
- <Box flexDirection="column" marginLeft={1}>
19
- <Box>
20
- <Text bold color={colors.text}>gencode </Text>
21
- <Text color={colors.textMuted}>v0.1.0</Text>
22
- </Box>
23
- <Text color={colors.textMuted}>{model} · API Usage Billing</Text>
24
- <Text color={colors.textMuted}>{cwdDisplay}</Text>
13
+ <Box flexDirection="column" marginTop={1}>
14
+ <BigLogo />
15
+ <Box marginTop={1}>
16
+ <Text color={colors.textSecondary}>{model}</Text>
17
+ <Text color={colors.textMuted}> · </Text>
18
+ <Text color={colors.textMuted}>{cwd}</Text>
25
19
  </Box>
26
20
  </Box>
27
21
  );
@@ -29,8 +23,8 @@ export function Header({ provider, model, cwd }: HeaderProps) {
29
23
 
30
24
  export function Welcome() {
31
25
  return (
32
- <Text color={colors.textMuted}>
33
- Type a message or /help. Ctrl+C to exit.
34
- </Text>
26
+ <Box marginTop={1}>
27
+ <Text color={colors.textMuted}>? for help · Ctrl+C to exit</Text>
28
+ </Box>
35
29
  );
36
30
  }