gencode-ai 0.1.3 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (260) hide show
  1. package/README.md +2 -1
  2. package/dist/agent/agent.d.ts +44 -2
  3. package/dist/agent/agent.d.ts.map +1 -1
  4. package/dist/agent/agent.js +130 -11
  5. package/dist/agent/agent.js.map +1 -1
  6. package/dist/agent/types.d.ts +11 -1
  7. package/dist/agent/types.d.ts.map +1 -1
  8. package/dist/checkpointing/checkpoint-manager.d.ts +87 -0
  9. package/dist/checkpointing/checkpoint-manager.d.ts.map +1 -0
  10. package/dist/checkpointing/checkpoint-manager.js +281 -0
  11. package/dist/checkpointing/checkpoint-manager.js.map +1 -0
  12. package/dist/checkpointing/index.d.ts +29 -0
  13. package/dist/checkpointing/index.d.ts.map +1 -0
  14. package/dist/checkpointing/index.js +29 -0
  15. package/dist/checkpointing/index.js.map +1 -0
  16. package/dist/checkpointing/types.d.ts +98 -0
  17. package/dist/checkpointing/types.d.ts.map +1 -0
  18. package/dist/checkpointing/types.js +7 -0
  19. package/dist/checkpointing/types.js.map +1 -0
  20. package/dist/cli/components/App.d.ts.map +1 -1
  21. package/dist/cli/components/App.js +171 -14
  22. package/dist/cli/components/App.js.map +1 -1
  23. package/dist/cli/components/CommandSuggestions.d.ts.map +1 -1
  24. package/dist/cli/components/CommandSuggestions.js +5 -0
  25. package/dist/cli/components/CommandSuggestions.js.map +1 -1
  26. package/dist/cli/components/Messages.d.ts +7 -1
  27. package/dist/cli/components/Messages.d.ts.map +1 -1
  28. package/dist/cli/components/Messages.js +12 -3
  29. package/dist/cli/components/Messages.js.map +1 -1
  30. package/dist/cli/components/ModeIndicator.d.ts +42 -0
  31. package/dist/cli/components/ModeIndicator.d.ts.map +1 -0
  32. package/dist/cli/components/ModeIndicator.js +52 -0
  33. package/dist/cli/components/ModeIndicator.js.map +1 -0
  34. package/dist/cli/components/ModelSelector.d.ts +4 -3
  35. package/dist/cli/components/ModelSelector.d.ts.map +1 -1
  36. package/dist/cli/components/ModelSelector.js +54 -37
  37. package/dist/cli/components/ModelSelector.js.map +1 -1
  38. package/dist/cli/components/PlanApproval.d.ts +36 -0
  39. package/dist/cli/components/PlanApproval.d.ts.map +1 -0
  40. package/dist/cli/components/PlanApproval.js +154 -0
  41. package/dist/cli/components/PlanApproval.js.map +1 -0
  42. package/dist/cli/components/ProviderManager.d.ts +2 -2
  43. package/dist/cli/components/ProviderManager.d.ts.map +1 -1
  44. package/dist/cli/components/ProviderManager.js +137 -156
  45. package/dist/cli/components/ProviderManager.js.map +1 -1
  46. package/dist/cli/components/theme.d.ts +2 -0
  47. package/dist/cli/components/theme.d.ts.map +1 -1
  48. package/dist/cli/components/theme.js +3 -0
  49. package/dist/cli/components/theme.js.map +1 -1
  50. package/dist/cli/index.js +30 -13
  51. package/dist/cli/index.js.map +1 -1
  52. package/dist/config/index.d.ts +2 -2
  53. package/dist/config/index.d.ts.map +1 -1
  54. package/dist/config/index.js +1 -1
  55. package/dist/config/index.js.map +1 -1
  56. package/dist/config/levels.d.ts +5 -5
  57. package/dist/config/levels.d.ts.map +1 -1
  58. package/dist/config/levels.js +20 -20
  59. package/dist/config/levels.js.map +1 -1
  60. package/dist/config/merger.js +1 -1
  61. package/dist/config/merger.js.map +1 -1
  62. package/dist/config/providers-config.d.ts +8 -5
  63. package/dist/config/providers-config.d.ts.map +1 -1
  64. package/dist/config/providers-config.js +19 -22
  65. package/dist/config/providers-config.js.map +1 -1
  66. package/dist/config/test-utils.d.ts +2 -2
  67. package/dist/config/test-utils.d.ts.map +1 -1
  68. package/dist/config/test-utils.js +4 -4
  69. package/dist/config/test-utils.js.map +1 -1
  70. package/dist/config/types.d.ts +23 -17
  71. package/dist/config/types.d.ts.map +1 -1
  72. package/dist/config/types.js +14 -14
  73. package/dist/config/types.js.map +1 -1
  74. package/dist/index.d.ts +1 -0
  75. package/dist/index.d.ts.map +1 -1
  76. package/dist/index.js +2 -0
  77. package/dist/index.js.map +1 -1
  78. package/dist/memory/memory-manager.d.ts +25 -12
  79. package/dist/memory/memory-manager.d.ts.map +1 -1
  80. package/dist/memory/memory-manager.js +241 -112
  81. package/dist/memory/memory-manager.js.map +1 -1
  82. package/dist/memory/test-utils.d.ts +1 -1
  83. package/dist/memory/test-utils.d.ts.map +1 -1
  84. package/dist/memory/test-utils.js +3 -3
  85. package/dist/memory/test-utils.js.map +1 -1
  86. package/dist/memory/types.d.ts +20 -10
  87. package/dist/memory/types.d.ts.map +1 -1
  88. package/dist/memory/types.js +13 -13
  89. package/dist/memory/types.js.map +1 -1
  90. package/dist/migration/migrate.d.ts +24 -0
  91. package/dist/migration/migrate.d.ts.map +1 -0
  92. package/dist/migration/migrate.js +164 -0
  93. package/dist/migration/migrate.js.map +1 -0
  94. package/dist/permissions/persistence.d.ts +2 -2
  95. package/dist/permissions/persistence.js +4 -4
  96. package/dist/permissions/persistence.js.map +1 -1
  97. package/dist/planning/index.d.ts +13 -0
  98. package/dist/planning/index.d.ts.map +1 -0
  99. package/dist/planning/index.js +15 -0
  100. package/dist/planning/index.js.map +1 -0
  101. package/dist/planning/plan-file.d.ts +59 -0
  102. package/dist/planning/plan-file.d.ts.map +1 -0
  103. package/dist/planning/plan-file.js +278 -0
  104. package/dist/planning/plan-file.js.map +1 -0
  105. package/dist/planning/state.d.ts +127 -0
  106. package/dist/planning/state.d.ts.map +1 -0
  107. package/dist/planning/state.js +261 -0
  108. package/dist/planning/state.js.map +1 -0
  109. package/dist/planning/tools/enter-plan-mode.d.ts +25 -0
  110. package/dist/planning/tools/enter-plan-mode.d.ts.map +1 -0
  111. package/dist/planning/tools/enter-plan-mode.js +98 -0
  112. package/dist/planning/tools/enter-plan-mode.js.map +1 -0
  113. package/dist/planning/tools/exit-plan-mode.d.ts +24 -0
  114. package/dist/planning/tools/exit-plan-mode.d.ts.map +1 -0
  115. package/dist/planning/tools/exit-plan-mode.js +149 -0
  116. package/dist/planning/tools/exit-plan-mode.js.map +1 -0
  117. package/dist/planning/types.d.ts +100 -0
  118. package/dist/planning/types.d.ts.map +1 -0
  119. package/dist/planning/types.js +28 -0
  120. package/dist/planning/types.js.map +1 -0
  121. package/dist/pricing/calculator.d.ts +21 -0
  122. package/dist/pricing/calculator.d.ts.map +1 -0
  123. package/dist/pricing/calculator.js +59 -0
  124. package/dist/pricing/calculator.js.map +1 -0
  125. package/dist/pricing/index.d.ts +7 -0
  126. package/dist/pricing/index.d.ts.map +1 -0
  127. package/dist/pricing/index.js +7 -0
  128. package/dist/pricing/index.js.map +1 -0
  129. package/dist/pricing/models.d.ts +20 -0
  130. package/dist/pricing/models.d.ts.map +1 -0
  131. package/dist/pricing/models.js +322 -0
  132. package/dist/pricing/models.js.map +1 -0
  133. package/dist/pricing/types.d.ts +30 -0
  134. package/dist/pricing/types.d.ts.map +1 -0
  135. package/dist/pricing/types.js +5 -0
  136. package/dist/pricing/types.js.map +1 -0
  137. package/dist/prompts/index.d.ts +5 -4
  138. package/dist/prompts/index.d.ts.map +1 -1
  139. package/dist/prompts/index.js +11 -8
  140. package/dist/prompts/index.js.map +1 -1
  141. package/dist/providers/anthropic.d.ts +2 -1
  142. package/dist/providers/anthropic.d.ts.map +1 -1
  143. package/dist/providers/anthropic.js +24 -10
  144. package/dist/providers/anthropic.js.map +1 -1
  145. package/dist/providers/gemini.d.ts +2 -1
  146. package/dist/providers/gemini.d.ts.map +1 -1
  147. package/dist/providers/gemini.js +28 -14
  148. package/dist/providers/gemini.js.map +1 -1
  149. package/dist/providers/index.d.ts +20 -10
  150. package/dist/providers/index.d.ts.map +1 -1
  151. package/dist/providers/index.js +48 -24
  152. package/dist/providers/index.js.map +1 -1
  153. package/dist/providers/openai.d.ts +2 -1
  154. package/dist/providers/openai.d.ts.map +1 -1
  155. package/dist/providers/openai.js +19 -8
  156. package/dist/providers/openai.js.map +1 -1
  157. package/dist/providers/registry.d.ts +48 -34
  158. package/dist/providers/registry.d.ts.map +1 -1
  159. package/dist/providers/registry.js +72 -88
  160. package/dist/providers/registry.js.map +1 -1
  161. package/dist/providers/store.d.ts +43 -17
  162. package/dist/providers/store.d.ts.map +1 -1
  163. package/dist/providers/store.js +112 -19
  164. package/dist/providers/store.js.map +1 -1
  165. package/dist/providers/types.d.ts +25 -0
  166. package/dist/providers/types.d.ts.map +1 -1
  167. package/dist/providers/vertex-ai.d.ts +15 -7
  168. package/dist/providers/vertex-ai.d.ts.map +1 -1
  169. package/dist/providers/vertex-ai.js +63 -23
  170. package/dist/providers/vertex-ai.js.map +1 -1
  171. package/dist/session/manager.d.ts +4 -0
  172. package/dist/session/manager.d.ts.map +1 -1
  173. package/dist/session/manager.js +8 -0
  174. package/dist/session/manager.js.map +1 -1
  175. package/dist/session/types.js +1 -1
  176. package/dist/session/types.js.map +1 -1
  177. package/dist/tools/index.d.ts +7 -1
  178. package/dist/tools/index.d.ts.map +1 -1
  179. package/dist/tools/index.js +7 -0
  180. package/dist/tools/index.js.map +1 -1
  181. package/dist/tools/registry.d.ts +13 -0
  182. package/dist/tools/registry.d.ts.map +1 -1
  183. package/dist/tools/registry.js +79 -2
  184. package/dist/tools/registry.js.map +1 -1
  185. package/docs/config-system-comparison.md +50 -50
  186. package/docs/cost-tracking-comparison.md +904 -0
  187. package/docs/memory-system.md +124 -31
  188. package/docs/operating-modes.md +96 -0
  189. package/docs/permissions.md +2 -2
  190. package/docs/proposals/0006-memory-system.md +4 -4
  191. package/docs/proposals/0008-checkpointing.md +109 -2
  192. package/docs/proposals/0011-custom-commands.md +2 -1
  193. package/docs/proposals/0021-skills-system.md +2 -1
  194. package/docs/proposals/0023-permission-enhancements.md +2 -2
  195. package/docs/proposals/0025-cost-tracking.md +60 -2
  196. package/docs/proposals/0033-enterprise-deployment.md +1 -1
  197. package/docs/proposals/0041-configuration-system.md +17 -19
  198. package/docs/proposals/0042-prompt-optimization.md +17 -9
  199. package/docs/proposals/README.md +6 -6
  200. package/docs/providers.md +94 -9
  201. package/examples/test-checkpointing.ts +121 -0
  202. package/examples/test-cost-tracking.ts +77 -0
  203. package/examples/test-interrupt-cleanup.ts +94 -0
  204. package/package.json +3 -2
  205. package/scripts/migrate.ts +449 -0
  206. package/src/agent/agent.ts +161 -12
  207. package/src/agent/types.ts +11 -1
  208. package/src/checkpointing/checkpoint-manager.ts +327 -0
  209. package/src/checkpointing/index.ts +45 -0
  210. package/src/checkpointing/types.ts +104 -0
  211. package/src/cli/components/App.tsx +221 -13
  212. package/src/cli/components/CommandSuggestions.tsx +5 -0
  213. package/src/cli/components/Messages.tsx +24 -5
  214. package/src/cli/components/ModeIndicator.tsx +174 -0
  215. package/src/cli/components/ModelSelector.tsx +62 -43
  216. package/src/cli/components/PlanApproval.tsx +327 -0
  217. package/src/cli/components/ProviderManager.tsx +278 -323
  218. package/src/cli/components/theme.ts +3 -0
  219. package/src/cli/index.tsx +36 -17
  220. package/src/config/index.ts +5 -3
  221. package/src/config/levels.test.ts +22 -22
  222. package/src/config/levels.ts +22 -22
  223. package/src/config/loader.test.ts +14 -14
  224. package/src/config/manager.test.ts +19 -19
  225. package/src/config/merger.test.ts +23 -23
  226. package/src/config/merger.ts +1 -1
  227. package/src/config/providers-config.ts +23 -21
  228. package/src/config/test-utils.ts +6 -6
  229. package/src/config/types.ts +30 -20
  230. package/src/index.ts +15 -0
  231. package/src/memory/memory-manager.test.ts +242 -24
  232. package/src/memory/memory-manager.ts +270 -141
  233. package/src/memory/test-utils.ts +4 -4
  234. package/src/memory/types.ts +28 -17
  235. package/src/permissions/persistence.ts +4 -4
  236. package/src/planning/index.ts +53 -0
  237. package/src/planning/plan-file.ts +326 -0
  238. package/src/planning/state.ts +305 -0
  239. package/src/planning/tools/enter-plan-mode.ts +111 -0
  240. package/src/planning/tools/exit-plan-mode.ts +170 -0
  241. package/src/planning/types.ts +150 -0
  242. package/src/pricing/calculator.ts +71 -0
  243. package/src/pricing/index.ts +7 -0
  244. package/src/pricing/models.ts +334 -0
  245. package/src/pricing/types.ts +32 -0
  246. package/src/prompts/index.ts +13 -9
  247. package/src/providers/anthropic.ts +30 -10
  248. package/src/providers/gemini.ts +34 -14
  249. package/src/providers/index.ts +76 -33
  250. package/src/providers/openai.ts +26 -8
  251. package/src/providers/registry.ts +116 -111
  252. package/src/providers/store.ts +130 -28
  253. package/src/providers/types.ts +36 -1
  254. package/src/providers/vertex-ai.ts +70 -23
  255. package/src/session/manager.ts +9 -0
  256. package/src/session/types.ts +1 -1
  257. package/src/tools/index.ts +8 -0
  258. package/src/tools/registry.ts +95 -2
  259. package/.gencode/settings.local.json +0 -7
  260. package/CLAUDE.md +0 -86
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Checkpointing System Type Definitions
3
+ *
4
+ * Provides automatic tracking of file changes with undo/rewind capabilities.
5
+ */
6
+
7
+ /**
8
+ * Type of file change
9
+ */
10
+ export type ChangeType = 'create' | 'modify' | 'delete';
11
+
12
+ /**
13
+ * A single file checkpoint recording a change
14
+ */
15
+ export interface FileCheckpoint {
16
+ /** Unique identifier for this checkpoint */
17
+ id: string;
18
+ /** Path to the file that was changed */
19
+ path: string;
20
+ /** Type of change made */
21
+ changeType: ChangeType;
22
+ /** When the change was made */
23
+ timestamp: Date;
24
+ /** File content before the change (null for create) */
25
+ previousContent: string | null;
26
+ /** File content after the change (null for delete) */
27
+ newContent: string | null;
28
+ /** Which tool made the change */
29
+ toolName: string;
30
+ }
31
+
32
+ /**
33
+ * A checkpoint session containing all checkpoints for a session
34
+ */
35
+ export interface CheckpointSession {
36
+ /** Session ID this checkpoint session belongs to */
37
+ sessionId: string;
38
+ /** All checkpoints in order */
39
+ checkpoints: FileCheckpoint[];
40
+ /** When this checkpoint session was created */
41
+ createdAt: Date;
42
+ }
43
+
44
+ /**
45
+ * Options for rewinding changes
46
+ */
47
+ export interface RewindOptions {
48
+ /** Rewind a specific checkpoint by ID */
49
+ checkpointId?: string;
50
+ /** Rewind changes to a specific file path */
51
+ path?: string;
52
+ /** Rewind all changes */
53
+ all?: boolean;
54
+ /** Rewind the last N changes */
55
+ count?: number;
56
+ }
57
+
58
+ /**
59
+ * Result of a rewind operation
60
+ */
61
+ export interface RewindResult {
62
+ /** Whether the rewind was successful */
63
+ success: boolean;
64
+ /** Files that were successfully reverted */
65
+ revertedFiles: Array<{
66
+ path: string;
67
+ action: 'restored' | 'deleted' | 'recreated';
68
+ }>;
69
+ /** Any errors that occurred during rewind */
70
+ errors: Array<{
71
+ path: string;
72
+ error: string;
73
+ }>;
74
+ }
75
+
76
+ /**
77
+ * Summary of changes in a checkpoint session
78
+ */
79
+ export interface CheckpointSummary {
80
+ /** Number of files created */
81
+ created: number;
82
+ /** Number of files modified */
83
+ modified: number;
84
+ /** Number of files deleted */
85
+ deleted: number;
86
+ /** Total number of checkpoints */
87
+ total: number;
88
+ }
89
+
90
+ /**
91
+ * Input for recording a file change
92
+ */
93
+ export interface RecordChangeInput {
94
+ /** Path to the file */
95
+ path: string;
96
+ /** Type of change */
97
+ changeType: ChangeType;
98
+ /** Content before change (null for create) */
99
+ previousContent: string | null;
100
+ /** Content after change (null for delete) */
101
+ newContent: string | null;
102
+ /** Tool that made the change */
103
+ toolName: string;
104
+ }
@@ -6,6 +6,8 @@ import { useState, useEffect, useCallback, useRef } from 'react';
6
6
  import { Box, Text, useApp, useInput, Static } from 'ink';
7
7
  import { Agent } from '../../agent/index.js';
8
8
  import type { AgentConfig } from '../../agent/types.js';
9
+ import { formatTokens, formatCost } from '../../pricing/calculator.js';
10
+ import type { CostEstimate } from '../../pricing/types.js';
9
11
  import {
10
12
  UserMessage,
11
13
  AssistantMessage,
@@ -35,6 +37,11 @@ import type { Question, QuestionAnswer } from '../../tools/types.js';
35
37
  import type { ProviderName } from '../../providers/index.js';
36
38
  import type { ApprovalAction, ApprovalSuggestion } from '../../permissions/types.js';
37
39
  import { gatherContextFiles, buildInitPrompt, getContextSummary } from '../../memory/index.js';
40
+ // ModeIndicator kept for potential future use
41
+ import { PlanApproval } from './PlanApproval.js';
42
+ import type { ModeType, PlanApprovalOption, AllowedPrompt } from '../../planning/types.js';
43
+ // Planning utilities kept for potential future use
44
+ import { getCheckpointManager } from '../../checkpointing/index.js';
38
45
 
39
46
  // Types
40
47
  interface HistoryItem {
@@ -56,6 +63,14 @@ interface QuestionState {
56
63
  resolve: (answers: QuestionAnswer[]) => void;
57
64
  }
58
65
 
66
+ interface PlanApprovalState {
67
+ planSummary: string;
68
+ requestedPermissions: AllowedPrompt[];
69
+ filesToChange: Array<{ path: string; action: 'create' | 'modify' | 'delete' }>;
70
+ planFilePath: string;
71
+ resolve: (option: PlanApprovalOption, customInput?: string) => void;
72
+ }
73
+
59
74
  interface SettingsManager {
60
75
  save: (settings: { model?: string }) => Promise<void>;
61
76
  getCwd?: () => string;
@@ -108,6 +123,9 @@ const formatRelativeTime = (dateStr: string) => {
108
123
  // ============================================================================
109
124
  function HelpPanel() {
110
125
  const commands: [string, string][] = [
126
+ ['/plan [desc]', 'Enter plan mode'],
127
+ ['/normal', 'Exit to normal mode'],
128
+ ['/accept', 'Enter auto-accept mode'],
111
129
  ['/model [name]', 'Switch model'],
112
130
  ['/provider', 'Manage providers'],
113
131
  ['/sessions', 'List sessions'],
@@ -115,8 +133,10 @@ function HelpPanel() {
115
133
  ['/new', 'New session'],
116
134
  ['/save', 'Save session'],
117
135
  ['/clear', 'Clear chat'],
118
- ['/init', 'Generate AGENT.md'],
136
+ ['/init', 'Generate GEN.md'],
119
137
  ['/memory', 'Show memory files'],
138
+ ['/changes', 'List file changes'],
139
+ ['/rewind [n|all]', 'Undo file changes'],
120
140
  ];
121
141
 
122
142
  return (
@@ -246,12 +266,23 @@ export function App({ config, settingsManager, resumeLatest, permissionSettings
246
266
  const [showModelSelector, setShowModelSelector] = useState(false);
247
267
  const [showProviderManager, setShowProviderManager] = useState(false);
248
268
  const [currentModel, setCurrentModel] = useState(config.model);
269
+ const [currentProvider, setCurrentProvider] = useState(config.provider);
249
270
  const [cmdSuggestionIndex, setCmdSuggestionIndex] = useState(0);
250
271
  const [inputKey, setInputKey] = useState(0); // Force cursor to end after autocomplete
251
272
  const [pendingTool, setPendingTool] = useState<{ name: string; input: Record<string, unknown> } | null>(null);
252
273
  const pendingToolRef = useRef<{ name: string; input: Record<string, unknown> } | null>(null);
253
274
  const [todos, setTodos] = useState<ReturnType<typeof getTodos>>([]);
254
275
 
276
+ // Operating mode state (normal → plan → accept → normal)
277
+ const [currentMode, setCurrentMode] = useState<ModeType>('normal');
278
+ const currentModeRef = useRef<ModeType>('normal'); // Track mode for confirm callback
279
+ const [planApprovalState, setPlanApprovalState] = useState<PlanApprovalState | null>(null);
280
+
281
+ // Keep ref in sync with state
282
+ useEffect(() => {
283
+ currentModeRef.current = currentMode;
284
+ }, [currentMode]);
285
+
255
286
  // Check if showing command suggestions
256
287
  const showCmdSuggestions = input.startsWith('/') && !isProcessing;
257
288
  const cmdSuggestions = showCmdSuggestions ? getFilteredCommands(input) : [];
@@ -274,6 +305,11 @@ export function App({ config, settingsManager, resumeLatest, permissionSettings
274
305
 
275
306
  // Set enhanced confirm callback with approval options
276
307
  agent.setEnhancedConfirmCallback(async (tool, toolInput, suggestions) => {
308
+ // Auto-approve in accept mode
309
+ if (currentModeRef.current === 'accept') {
310
+ return 'allow_once';
311
+ }
312
+
277
313
  return new Promise<ApprovalAction>((resolve) => {
278
314
  setConfirmState({
279
315
  tool,
@@ -345,9 +381,14 @@ export function App({ config, settingsManager, resumeLatest, permissionSettings
345
381
  };
346
382
 
347
383
  // Handle model selection
348
- const handleModelSelect = async (model: string, providerId?: ProviderName) => {
349
- agent.setModel(model);
384
+ const handleModelSelect = async (model: string, providerId?: ProviderName, authMethod?: string) => {
385
+ agent.setModel(model, providerId, authMethod);
350
386
  setCurrentModel(model);
387
+
388
+ // Update provider state to keep UI in sync
389
+ const newProvider = agent.getProvider();
390
+ setCurrentProvider(newProvider);
391
+
351
392
  setShowModelSelector(false);
352
393
 
353
394
  if (providerId) {
@@ -442,6 +483,7 @@ export function App({ config, settingsManager, resumeLatest, permissionSettings
442
483
  // Direct model switch: /model gpt-4o
443
484
  agent.setModel(arg);
444
485
  setCurrentModel(arg);
486
+ setCurrentProvider(agent.getProvider());
445
487
  addHistory({ type: 'info', content: `Model: ${arg}` });
446
488
  } else {
447
489
  // Show interactive model selector
@@ -505,13 +547,13 @@ export function App({ config, settingsManager, resumeLatest, permissionSettings
505
547
  }
506
548
 
507
549
  case 'init': {
508
- // Gather context files and generate AGENT.md
550
+ // Gather context files and generate GEN.md
509
551
  addHistory({ type: 'info', content: 'Analyzing codebase...' });
510
552
 
511
553
  const contextFiles = await gatherContextFiles(cwd);
512
554
  addHistory({ type: 'info', content: getContextSummary(contextFiles) });
513
555
 
514
- // Check if AGENT.md already exists
556
+ // Check if GEN.md already exists
515
557
  const memoryManager = agent.getMemoryManager();
516
558
  const existingPath = await memoryManager.getExistingProjectMemoryPath(cwd);
517
559
  let existingContent: string | undefined;
@@ -531,7 +573,7 @@ export function App({ config, settingsManager, resumeLatest, permissionSettings
531
573
 
532
574
  // Build init prompt and run through agent
533
575
  const initPrompt = buildInitPrompt(contextFiles, existingContent);
534
- addHistory({ type: 'info', content: 'Generating AGENT.md...' });
576
+ addHistory({ type: 'info', content: 'Generating GEN.md...' });
535
577
  addHistory({ type: 'user', content: '/init' });
536
578
  await runAgent(initPrompt);
537
579
  return true;
@@ -565,6 +607,110 @@ export function App({ config, settingsManager, resumeLatest, permissionSettings
565
607
  return true;
566
608
  }
567
609
 
610
+ case 'plan': {
611
+ // Enter plan mode
612
+ await agent.enterPlanMode(arg);
613
+ setCurrentMode('plan');
614
+ return true;
615
+ }
616
+
617
+ case 'normal': {
618
+ // Exit to normal mode
619
+ if (agent.isPlanModeActive()) {
620
+ agent.exitPlanMode(false);
621
+ }
622
+ setCurrentMode('normal');
623
+ return true;
624
+ }
625
+
626
+ case 'accept': {
627
+ // Enter auto-accept mode
628
+ if (agent.isPlanModeActive()) {
629
+ agent.exitPlanMode(false);
630
+ }
631
+ setCurrentMode('accept');
632
+ return true;
633
+ }
634
+
635
+ case 'changes': {
636
+ // List file changes (checkpoints)
637
+ const checkpointManager = getCheckpointManager();
638
+ if (!checkpointManager.hasCheckpoints()) {
639
+ addHistory({ type: 'info', content: '\nNo file changes in this session' });
640
+ } else {
641
+ addHistory({ type: 'info', content: '\n' + checkpointManager.formatCheckpointList(false) });
642
+ }
643
+ return true;
644
+ }
645
+
646
+ case 'rewind': {
647
+ // Rewind file changes
648
+ const checkpointManager = getCheckpointManager();
649
+
650
+ if (!checkpointManager.hasCheckpoints()) {
651
+ addHistory({ type: 'info', content: 'No file changes to rewind' });
652
+ return true;
653
+ }
654
+
655
+ if (arg === 'all') {
656
+ // Rewind all changes
657
+ const result = await checkpointManager.rewind({ all: true });
658
+
659
+ // Build output message showing both successes and failures
660
+ const messages: string[] = ['']; // Start with empty line for spacing
661
+
662
+ if (result.revertedFiles.length > 0) {
663
+ const files = result.revertedFiles.map((f) => {
664
+ const fileName = f.path.split('/').pop() || f.path;
665
+ return ` • ${fileName} (${f.action})`;
666
+ }).join('\n');
667
+ messages.push(`Reverted ${result.revertedFiles.length} file(s):\n${files}`);
668
+ }
669
+
670
+ if (result.errors.length > 0) {
671
+ const errors = result.errors.map((e) => {
672
+ const fileName = e.path.split('/').pop() || e.path;
673
+ return ` • ${fileName}: ${e.error}`;
674
+ }).join('\n');
675
+ messages.push(`\nFailed to revert ${result.errors.length} file(s):\n${errors}`);
676
+ }
677
+
678
+ if (messages.length > 1) {
679
+ addHistory({ type: 'info', content: messages.join('\n') });
680
+ } else {
681
+ addHistory({ type: 'info', content: '\nNo changes to rewind' });
682
+ }
683
+ } else if (arg) {
684
+ // Rewind specific checkpoint by index
685
+ const index = parseInt(arg, 10);
686
+ if (!isNaN(index) && index >= 1) {
687
+ const checkpoints = checkpointManager.getCheckpoints();
688
+ if (index <= checkpoints.length) {
689
+ const checkpoint = checkpoints[index - 1];
690
+ const result = await checkpointManager.rewind({ checkpointId: checkpoint.id });
691
+ if (result.success && result.revertedFiles.length > 0) {
692
+ const f = result.revertedFiles[0];
693
+ const fileName = f.path.split('/').pop() || f.path;
694
+ addHistory({ type: 'info', content: `\nReverted: ${fileName} (${f.action})` });
695
+ } else if (result.errors.length > 0) {
696
+ const fileName = result.errors[0].path.split('/').pop() || result.errors[0].path;
697
+ addHistory({ type: 'info', content: `\nFailed: ${fileName} - ${result.errors[0].error}` });
698
+ } else {
699
+ addHistory({ type: 'info', content: '\nFailed to rewind change' });
700
+ }
701
+ } else {
702
+ addHistory({ type: 'info', content: '\nInvalid index: ${index}' });
703
+ }
704
+ } else {
705
+ addHistory({ type: 'info', content: '\nUsage: /rewind [n|all]' });
706
+ }
707
+ } else {
708
+ // Show changes and usage in one message
709
+ addHistory({ type: 'info', content: '\n' + checkpointManager.formatCheckpointList(true) });
710
+ }
711
+ return true;
712
+ }
713
+
568
714
  default:
569
715
  return false;
570
716
  }
@@ -661,9 +807,13 @@ export function App({ config, settingsManager, resumeLatest, permissionSettings
661
807
  streamingTextRef.current = '';
662
808
  setStreamingText('');
663
809
  }
664
- // Add completion message with duration
810
+ // Add completion message with duration and cost info
665
811
  const durationMs = Date.now() - startTime;
666
- addHistory({ type: 'completion', content: '', meta: { durationMs } });
812
+ addHistory({
813
+ type: 'completion',
814
+ content: '',
815
+ meta: { durationMs, usage: event.usage, cost: event.cost },
816
+ });
667
817
  setProcessingStartTime(undefined);
668
818
  break;
669
819
  }
@@ -707,8 +857,8 @@ export function App({ config, settingsManager, resumeLatest, permissionSettings
707
857
  }
708
858
 
709
859
  // Handle # prefix for quick memory adds
710
- // ## note -> user memory (~/.gencode/AGENT.md)
711
- // # note -> project memory (./AGENT.md)
860
+ // ## note -> user memory (~/.gen/GEN.md)
861
+ // # note -> project memory (./GEN.md)
712
862
  if (trimmed.startsWith('#') && !trimmed.startsWith('#!/')) {
713
863
  const memoryManager = agent.getMemoryManager();
714
864
  let level: 'user' | 'project';
@@ -774,9 +924,33 @@ export function App({ config, settingsManager, resumeLatest, permissionSettings
774
924
  // Clear pending tool (stop spinner)
775
925
  pendingToolRef.current = null;
776
926
  setPendingTool(null);
927
+ // Clean up incomplete tool_use messages to prevent API errors
928
+ agent.cleanupIncompleteMessages();
777
929
  addHistory({ type: 'info', content: 'Interrupted' });
778
930
  }
779
931
 
932
+ // Shift+Tab to cycle modes: normal → plan → accept → normal
933
+ if (key.shift && key.tab && !isProcessing && !confirmState && !questionState && !planApprovalState) {
934
+ const cycleMode = async () => {
935
+ const nextMode: Record<ModeType, ModeType> = {
936
+ normal: 'plan',
937
+ plan: 'accept',
938
+ accept: 'normal',
939
+ };
940
+ const newMode = nextMode[currentMode];
941
+
942
+ // Handle plan mode transitions
943
+ if (newMode === 'plan') {
944
+ await agent.enterPlanMode();
945
+ } else if (currentMode === 'plan') {
946
+ agent.exitPlanMode(false);
947
+ }
948
+
949
+ setCurrentMode(newMode);
950
+ };
951
+ cycleMode();
952
+ }
953
+
780
954
  // Command suggestion navigation
781
955
  if (showCmdSuggestions && cmdSuggestions.length > 0) {
782
956
  if (key.upArrow) {
@@ -853,7 +1027,13 @@ export function App({ config, settingsManager, resumeLatest, permissionSettings
853
1027
  }
854
1028
  return <InfoMessage text={item.content} />;
855
1029
  case 'completion':
856
- return <CompletionMessage durationMs={(item.meta?.durationMs as number) || 0} />;
1030
+ return (
1031
+ <CompletionMessage
1032
+ durationMs={(item.meta?.durationMs as number) || 0}
1033
+ usage={item.meta?.usage as { inputTokens: number; outputTokens: number } | undefined}
1034
+ cost={item.meta?.cost as CostEstimate | undefined}
1035
+ />
1036
+ );
857
1037
  case 'todos':
858
1038
  return <TodoList todos={item.meta?.todos as ReturnType<typeof getTodos>} />;
859
1039
  default:
@@ -862,7 +1042,7 @@ export function App({ config, settingsManager, resumeLatest, permissionSettings
862
1042
  };
863
1043
 
864
1044
  return (
865
- <Box flexDirection="column">
1045
+ <Box flexDirection="column" paddingBottom={2}>
866
1046
  <Static items={history}>
867
1047
  {(item) => <Box key={item.id}>{renderHistoryItem(item)}</Box>}
868
1048
  </Static>
@@ -889,10 +1069,27 @@ export function App({ config, settingsManager, resumeLatest, permissionSettings
889
1069
  />
890
1070
  )}
891
1071
 
1072
+ {/* Plan approval UI */}
1073
+ {planApprovalState && (
1074
+ <Box marginTop={1}>
1075
+ <PlanApproval
1076
+ planSummary={planApprovalState.planSummary}
1077
+ requestedPermissions={planApprovalState.requestedPermissions}
1078
+ filesToChange={planApprovalState.filesToChange}
1079
+ planFilePath={planApprovalState.planFilePath}
1080
+ onDecision={(option, customInput) => {
1081
+ planApprovalState.resolve(option, customInput);
1082
+ setPlanApprovalState(null);
1083
+ }}
1084
+ />
1085
+ </Box>
1086
+ )}
1087
+
892
1088
  {showModelSelector && (
893
1089
  <Box marginTop={1}>
894
1090
  <ModelSelector
895
1091
  currentModel={currentModel}
1092
+ currentProvider={currentProvider}
896
1093
  onSelect={handleModelSelect}
897
1094
  onCancel={handleModelCancel}
898
1095
  listModels={() => agent.listModels()}
@@ -907,6 +1104,7 @@ export function App({ config, settingsManager, resumeLatest, permissionSettings
907
1104
  onProviderChange={(providerId, model) => {
908
1105
  agent.setModel(model);
909
1106
  setCurrentModel(model);
1107
+ setCurrentProvider(agent.getProvider());
910
1108
  addHistory({ type: 'info', content: `Switched to ${providerId}: ${model}` });
911
1109
  }}
912
1110
  />
@@ -914,7 +1112,7 @@ export function App({ config, settingsManager, resumeLatest, permissionSettings
914
1112
  )}
915
1113
 
916
1114
  {!confirmState && !questionState && !showModelSelector && !showProviderManager && (
917
- <Box flexDirection="column" marginTop={1}>
1115
+ <Box flexDirection="column" marginTop={2}>
918
1116
  <PromptInput
919
1117
  key={inputKey}
920
1118
  value={input}
@@ -924,6 +1122,16 @@ export function App({ config, settingsManager, resumeLatest, permissionSettings
924
1122
  {showCmdSuggestions && cmdSuggestions.length > 0 && (
925
1123
  <CommandSuggestions input={input} selectedIndex={cmdSuggestionIndex} />
926
1124
  )}
1125
+ {currentMode === 'plan' && !isProcessing && (
1126
+ <Text color={colors.warning} dimColor>
1127
+ {icons.modePlan} plan mode on (shift+tab to cycle)
1128
+ </Text>
1129
+ )}
1130
+ {currentMode === 'accept' && !isProcessing && (
1131
+ <Text color={colors.success} dimColor>
1132
+ {icons.modeAccept} accept edits on (shift+tab to cycle)
1133
+ </Text>
1134
+ )}
927
1135
  </Box>
928
1136
  )}
929
1137
 
@@ -7,6 +7,9 @@ interface Command {
7
7
  }
8
8
 
9
9
  export const COMMANDS: Command[] = [
10
+ { name: '/plan', description: 'Enter plan mode (Shift+Tab to cycle)' },
11
+ { name: '/normal', description: 'Exit to normal mode' },
12
+ { name: '/accept', description: 'Enter auto-accept mode' },
10
13
  { name: '/model', description: 'Switch model' },
11
14
  { name: '/provider', description: 'Manage providers' },
12
15
  { name: '/permissions', description: 'View permission rules' },
@@ -20,6 +23,8 @@ export const COMMANDS: Command[] = [
20
23
  { name: '/help', description: 'Show help' },
21
24
  { name: '/init', description: 'Generate AGENT.md' },
22
25
  { name: '/memory', description: 'Show memory files' },
26
+ { name: '/changes', description: 'List file changes' },
27
+ { name: '/rewind', description: 'Undo file changes' },
23
28
  ];
24
29
 
25
30
  interface CommandSuggestionsProps {
@@ -3,6 +3,8 @@ import { Box, Text } from 'ink';
3
3
  import InkSpinner from 'ink-spinner';
4
4
  import { colors, icons } from './theme.js';
5
5
  import { renderMarkdown } from './markdown.js';
6
+ import { formatTokens, formatCost } from '../../pricing/calculator.js';
7
+ import type { CostEstimate } from '../../pricing/types.js';
6
8
 
7
9
  // Truncate string with ellipsis
8
10
  const truncate = (str: string, maxLen: number) =>
@@ -286,7 +288,7 @@ export function InfoMessage({ text, type = 'info' }: InfoMessageProps) {
286
288
  const { color, icon } = config[type];
287
289
 
288
290
  return (
289
- <Box>
291
+ <Box marginTop={1}>
290
292
  <Text color={color}>{icon} </Text>
291
293
  <Text color={colors.textSecondary}>{text}</Text>
292
294
  </Box>
@@ -346,16 +348,33 @@ function formatDuration(ms: number): string {
346
348
 
347
349
  interface CompletionMessageProps {
348
350
  durationMs: number;
351
+ usage?: {
352
+ inputTokens: number;
353
+ outputTokens: number;
354
+ };
355
+ cost?: CostEstimate;
349
356
  }
350
357
 
351
- export function CompletionMessage({ durationMs }: CompletionMessageProps) {
358
+ export function CompletionMessage({ durationMs, usage, cost }: CompletionMessageProps) {
352
359
  // Pick a random verb (stable per render via useMemo would be better, but keep simple)
353
360
  const verb = COMPLETION_VERBS[Math.floor(Math.random() * COMPLETION_VERBS.length)];
361
+
362
+ // Build the message parts
363
+ const parts = [`✻ ${verb} for ${formatDuration(durationMs)}`];
364
+
365
+ if (usage) {
366
+ parts.push(
367
+ `Tokens: ${formatTokens(usage.inputTokens)} in / ${formatTokens(usage.outputTokens)} out`
368
+ );
369
+ }
370
+
371
+ if (cost) {
372
+ parts.push(`(~${formatCost(cost.totalCost)})`);
373
+ }
374
+
354
375
  return (
355
376
  <Box marginTop={1}>
356
- <Text color={colors.textMuted}>
357
- ✻ {verb} for {formatDuration(durationMs)}
358
- </Text>
377
+ <Text color={colors.textMuted}>{parts.join(' • ')}</Text>
359
378
  </Box>
360
379
  );
361
380
  }