gencode-ai 0.1.2 → 0.2.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 (180) hide show
  1. package/README.md +15 -17
  2. package/dist/agent/agent.d.ts +43 -0
  3. package/dist/agent/agent.d.ts.map +1 -1
  4. package/dist/agent/agent.js +107 -4
  5. package/dist/agent/agent.js.map +1 -1
  6. package/dist/agent/index.d.ts +1 -0
  7. package/dist/agent/index.d.ts.map +1 -1
  8. package/dist/agent/types.d.ts +20 -1
  9. package/dist/agent/types.d.ts.map +1 -1
  10. package/dist/checkpointing/checkpoint-manager.d.ts +87 -0
  11. package/dist/checkpointing/checkpoint-manager.d.ts.map +1 -0
  12. package/dist/checkpointing/checkpoint-manager.js +281 -0
  13. package/dist/checkpointing/checkpoint-manager.js.map +1 -0
  14. package/dist/checkpointing/index.d.ts +29 -0
  15. package/dist/checkpointing/index.d.ts.map +1 -0
  16. package/dist/checkpointing/index.js +29 -0
  17. package/dist/checkpointing/index.js.map +1 -0
  18. package/dist/checkpointing/types.d.ts +98 -0
  19. package/dist/checkpointing/types.d.ts.map +1 -0
  20. package/dist/checkpointing/types.js +7 -0
  21. package/dist/checkpointing/types.js.map +1 -0
  22. package/dist/cli/components/App.d.ts.map +1 -1
  23. package/dist/cli/components/App.js +193 -7
  24. package/dist/cli/components/App.js.map +1 -1
  25. package/dist/cli/components/CommandSuggestions.d.ts.map +1 -1
  26. package/dist/cli/components/CommandSuggestions.js +5 -0
  27. package/dist/cli/components/CommandSuggestions.js.map +1 -1
  28. package/dist/cli/components/Messages.d.ts +7 -1
  29. package/dist/cli/components/Messages.d.ts.map +1 -1
  30. package/dist/cli/components/Messages.js +28 -2
  31. package/dist/cli/components/Messages.js.map +1 -1
  32. package/dist/cli/components/ModeIndicator.d.ts +42 -0
  33. package/dist/cli/components/ModeIndicator.d.ts.map +1 -0
  34. package/dist/cli/components/ModeIndicator.js +52 -0
  35. package/dist/cli/components/ModeIndicator.js.map +1 -0
  36. package/dist/cli/components/PlanApproval.d.ts +36 -0
  37. package/dist/cli/components/PlanApproval.d.ts.map +1 -0
  38. package/dist/cli/components/PlanApproval.js +154 -0
  39. package/dist/cli/components/PlanApproval.js.map +1 -0
  40. package/dist/cli/components/QuestionPrompt.d.ts +23 -0
  41. package/dist/cli/components/QuestionPrompt.d.ts.map +1 -0
  42. package/dist/cli/components/QuestionPrompt.js +231 -0
  43. package/dist/cli/components/QuestionPrompt.js.map +1 -0
  44. package/dist/cli/components/index.d.ts +1 -0
  45. package/dist/cli/components/index.d.ts.map +1 -1
  46. package/dist/cli/components/index.js +1 -0
  47. package/dist/cli/components/index.js.map +1 -1
  48. package/dist/cli/components/theme.d.ts +9 -0
  49. package/dist/cli/components/theme.d.ts.map +1 -1
  50. package/dist/cli/components/theme.js +14 -1
  51. package/dist/cli/components/theme.js.map +1 -1
  52. package/dist/index.d.ts +1 -0
  53. package/dist/index.d.ts.map +1 -1
  54. package/dist/index.js +2 -0
  55. package/dist/index.js.map +1 -1
  56. package/dist/permissions/types.d.ts.map +1 -1
  57. package/dist/permissions/types.js +2 -0
  58. package/dist/permissions/types.js.map +1 -1
  59. package/dist/planning/index.d.ts +13 -0
  60. package/dist/planning/index.d.ts.map +1 -0
  61. package/dist/planning/index.js +15 -0
  62. package/dist/planning/index.js.map +1 -0
  63. package/dist/planning/plan-file.d.ts +59 -0
  64. package/dist/planning/plan-file.d.ts.map +1 -0
  65. package/dist/planning/plan-file.js +278 -0
  66. package/dist/planning/plan-file.js.map +1 -0
  67. package/dist/planning/state.d.ts +127 -0
  68. package/dist/planning/state.d.ts.map +1 -0
  69. package/dist/planning/state.js +261 -0
  70. package/dist/planning/state.js.map +1 -0
  71. package/dist/planning/tools/enter-plan-mode.d.ts +25 -0
  72. package/dist/planning/tools/enter-plan-mode.d.ts.map +1 -0
  73. package/dist/planning/tools/enter-plan-mode.js +98 -0
  74. package/dist/planning/tools/enter-plan-mode.js.map +1 -0
  75. package/dist/planning/tools/exit-plan-mode.d.ts +24 -0
  76. package/dist/planning/tools/exit-plan-mode.d.ts.map +1 -0
  77. package/dist/planning/tools/exit-plan-mode.js +149 -0
  78. package/dist/planning/tools/exit-plan-mode.js.map +1 -0
  79. package/dist/planning/types.d.ts +100 -0
  80. package/dist/planning/types.d.ts.map +1 -0
  81. package/dist/planning/types.js +28 -0
  82. package/dist/planning/types.js.map +1 -0
  83. package/dist/pricing/calculator.d.ts +21 -0
  84. package/dist/pricing/calculator.d.ts.map +1 -0
  85. package/dist/pricing/calculator.js +59 -0
  86. package/dist/pricing/calculator.js.map +1 -0
  87. package/dist/pricing/index.d.ts +7 -0
  88. package/dist/pricing/index.d.ts.map +1 -0
  89. package/dist/pricing/index.js +7 -0
  90. package/dist/pricing/index.js.map +1 -0
  91. package/dist/pricing/models.d.ts +20 -0
  92. package/dist/pricing/models.d.ts.map +1 -0
  93. package/dist/pricing/models.js +322 -0
  94. package/dist/pricing/models.js.map +1 -0
  95. package/dist/pricing/types.d.ts +30 -0
  96. package/dist/pricing/types.d.ts.map +1 -0
  97. package/dist/pricing/types.js +5 -0
  98. package/dist/pricing/types.js.map +1 -0
  99. package/dist/providers/anthropic.d.ts.map +1 -1
  100. package/dist/providers/anthropic.js +17 -10
  101. package/dist/providers/anthropic.js.map +1 -1
  102. package/dist/providers/gemini.d.ts.map +1 -1
  103. package/dist/providers/gemini.js +21 -14
  104. package/dist/providers/gemini.js.map +1 -1
  105. package/dist/providers/openai.d.ts.map +1 -1
  106. package/dist/providers/openai.js +12 -8
  107. package/dist/providers/openai.js.map +1 -1
  108. package/dist/providers/types.d.ts +2 -0
  109. package/dist/providers/types.d.ts.map +1 -1
  110. package/dist/providers/vertex-ai.d.ts.map +1 -1
  111. package/dist/providers/vertex-ai.js +17 -10
  112. package/dist/providers/vertex-ai.js.map +1 -1
  113. package/dist/session/manager.d.ts +4 -0
  114. package/dist/session/manager.d.ts.map +1 -1
  115. package/dist/session/manager.js +8 -0
  116. package/dist/session/manager.js.map +1 -1
  117. package/dist/tools/builtin/ask-user.d.ts +64 -0
  118. package/dist/tools/builtin/ask-user.d.ts.map +1 -0
  119. package/dist/tools/builtin/ask-user.js +148 -0
  120. package/dist/tools/builtin/ask-user.js.map +1 -0
  121. package/dist/tools/index.d.ts +19 -1
  122. package/dist/tools/index.d.ts.map +1 -1
  123. package/dist/tools/index.js +11 -0
  124. package/dist/tools/index.js.map +1 -1
  125. package/dist/tools/registry.d.ts +13 -0
  126. package/dist/tools/registry.d.ts.map +1 -1
  127. package/dist/tools/registry.js +79 -2
  128. package/dist/tools/registry.js.map +1 -1
  129. package/dist/tools/types.d.ts +17 -0
  130. package/dist/tools/types.d.ts.map +1 -1
  131. package/dist/tools/types.js.map +1 -1
  132. package/docs/cost-tracking-comparison.md +904 -0
  133. package/docs/operating-modes.md +96 -0
  134. package/docs/proposals/0012-ask-user-question.md +66 -1
  135. package/docs/proposals/0025-cost-tracking.md +60 -2
  136. package/docs/proposals/README.md +2 -2
  137. package/examples/test-ask-user.ts +167 -0
  138. package/examples/test-checkpointing.ts +121 -0
  139. package/examples/test-cost-tracking.ts +77 -0
  140. package/examples/test-interrupt-cleanup.ts +94 -0
  141. package/package.json +1 -1
  142. package/src/agent/agent.ts +130 -4
  143. package/src/agent/index.ts +1 -0
  144. package/src/agent/types.ts +19 -1
  145. package/src/checkpointing/checkpoint-manager.ts +327 -0
  146. package/src/checkpointing/index.ts +45 -0
  147. package/src/checkpointing/types.ts +104 -0
  148. package/src/cli/components/App.tsx +259 -8
  149. package/src/cli/components/CommandSuggestions.tsx +5 -0
  150. package/src/cli/components/Messages.tsx +66 -4
  151. package/src/cli/components/ModeIndicator.tsx +174 -0
  152. package/src/cli/components/PlanApproval.tsx +327 -0
  153. package/src/cli/components/QuestionPrompt.tsx +462 -0
  154. package/src/cli/components/index.ts +1 -0
  155. package/src/cli/components/theme.ts +14 -1
  156. package/src/index.ts +15 -0
  157. package/src/permissions/types.ts +2 -0
  158. package/src/planning/index.ts +53 -0
  159. package/src/planning/plan-file.ts +326 -0
  160. package/src/planning/state.ts +305 -0
  161. package/src/planning/tools/enter-plan-mode.ts +111 -0
  162. package/src/planning/tools/exit-plan-mode.ts +170 -0
  163. package/src/planning/types.ts +150 -0
  164. package/src/pricing/calculator.ts +71 -0
  165. package/src/pricing/index.ts +7 -0
  166. package/src/pricing/models.ts +334 -0
  167. package/src/pricing/types.ts +32 -0
  168. package/src/prompts/system/base.txt +42 -0
  169. package/src/prompts/tools/ask-user.txt +110 -0
  170. package/src/providers/anthropic.ts +21 -10
  171. package/src/providers/gemini.ts +25 -14
  172. package/src/providers/openai.ts +17 -8
  173. package/src/providers/types.ts +3 -0
  174. package/src/providers/vertex-ai.ts +21 -10
  175. package/src/session/manager.ts +9 -0
  176. package/src/tools/builtin/ask-user.ts +185 -0
  177. package/src/tools/index.ts +23 -0
  178. package/src/tools/registry.ts +95 -2
  179. package/src/tools/types.ts +18 -0
  180. package/.gencode/settings.local.json +0 -7
@@ -0,0 +1,174 @@
1
+ /**
2
+ * Mode Indicator Component - Shows current mode (NORMAL/PLAN/ACCEPT)
3
+ *
4
+ * Design:
5
+ * ╭────────────────────────────────────╮
6
+ * │ ◉ NORMAL ○ PLAN ○ ACCEPT │
7
+ * ╰────────────────────────────────────╯
8
+ *
9
+ * Or with Shift+Tab toggle hint
10
+ */
11
+
12
+ import { Box, Text } from 'ink';
13
+ import { colors, icons } from './theme.js';
14
+ import type { ModeType } from '../../planning/types.js';
15
+
16
+ // ============================================================================
17
+ // Types
18
+ // ============================================================================
19
+
20
+ interface ModeIndicatorProps {
21
+ /** Current mode */
22
+ mode: ModeType;
23
+ /** Show toggle hint */
24
+ showHint?: boolean;
25
+ /** Compact mode (inline) */
26
+ compact?: boolean;
27
+ }
28
+
29
+ // ============================================================================
30
+ // Mode Indicator Component
31
+ // ============================================================================
32
+
33
+ /**
34
+ * Mode Indicator - Shows NORMAL/PLAN/ACCEPT mode toggle
35
+ */
36
+ export function ModeIndicator({ mode, showHint = false, compact = false }: ModeIndicatorProps) {
37
+ const isNormal = mode === 'normal';
38
+ const isPlan = mode === 'plan';
39
+ const isAccept = mode === 'accept';
40
+
41
+ if (compact) {
42
+ // Compact inline indicator
43
+ const modeLabel = isPlan ? 'PLAN' : isAccept ? 'ACCEPT' : '';
44
+ const modeColor = isPlan ? colors.warning : isAccept ? colors.success : colors.textMuted;
45
+
46
+ if (!modeLabel) return null;
47
+
48
+ return (
49
+ <Box>
50
+ <Text color={modeColor}>
51
+ {isPlan ? icons.modePlan : icons.modeAccept}
52
+ </Text>
53
+ <Text color={modeColor}> {modeLabel}</Text>
54
+ </Box>
55
+ );
56
+ }
57
+
58
+ return (
59
+ <Box flexDirection="column">
60
+ <Box borderStyle="round" borderColor={isPlan ? colors.warning : isAccept ? colors.success : colors.textMuted} paddingX={1}>
61
+ {/* NORMAL option */}
62
+ <Text color={isNormal ? colors.primary : colors.textMuted}>
63
+ {isNormal ? icons.radio : icons.radioEmpty}
64
+ </Text>
65
+ <Text color={isNormal ? colors.text : colors.textMuted} bold={isNormal}>
66
+ {' '}NORMAL
67
+ </Text>
68
+
69
+ <Text color={colors.textMuted}> </Text>
70
+
71
+ {/* PLAN option */}
72
+ <Text color={isPlan ? colors.warning : colors.textMuted}>
73
+ {isPlan ? icons.radio : icons.radioEmpty}
74
+ </Text>
75
+ <Text color={isPlan ? colors.text : colors.textMuted} bold={isPlan}>
76
+ {' '}PLAN
77
+ </Text>
78
+
79
+ <Text color={colors.textMuted}> </Text>
80
+
81
+ {/* ACCEPT option */}
82
+ <Text color={isAccept ? colors.success : colors.textMuted}>
83
+ {isAccept ? icons.radio : icons.radioEmpty}
84
+ </Text>
85
+ <Text color={isAccept ? colors.text : colors.textMuted} bold={isAccept}>
86
+ {' '}ACCEPT
87
+ </Text>
88
+ </Box>
89
+
90
+ {showHint && (
91
+ <Box marginTop={0}>
92
+ <Text color={colors.textMuted} dimColor> Shift+Tab to cycle modes</Text>
93
+ </Box>
94
+ )}
95
+ </Box>
96
+ );
97
+ }
98
+
99
+ // ============================================================================
100
+ // Mode Badge Component
101
+ // ============================================================================
102
+
103
+ interface ModeBadgeProps {
104
+ mode: ModeType;
105
+ }
106
+
107
+ /**
108
+ * Mode Badge - Compact badge for header display
109
+ */
110
+ export function ModeBadge({ mode }: ModeBadgeProps) {
111
+ const color = mode === 'plan' ? colors.warning : mode === 'accept' ? colors.success : colors.primary;
112
+ const label = mode === 'plan' ? 'PLAN' : mode === 'accept' ? 'ACCEPT' : 'NORMAL';
113
+
114
+ return (
115
+ <Text color={color} bold>
116
+ [{label}]
117
+ </Text>
118
+ );
119
+ }
120
+
121
+ // ============================================================================
122
+ // Plan Status Bar Component
123
+ // ============================================================================
124
+
125
+ interface PlanStatusBarProps {
126
+ /** Current planning phase */
127
+ phase: string;
128
+ /** Plan file path */
129
+ planFilePath?: string;
130
+ }
131
+
132
+ /**
133
+ * Plan Status Bar - Shows plan mode status
134
+ */
135
+ export function PlanStatusBar({ phase, planFilePath }: PlanStatusBarProps) {
136
+ // Shorten plan file path for display
137
+ const displayPath = planFilePath
138
+ ? planFilePath.replace(process.env.HOME || '', '~').split('/').slice(-2).join('/')
139
+ : '';
140
+
141
+ return (
142
+ <Box
143
+ borderStyle="round"
144
+ borderColor={colors.warning}
145
+ paddingX={1}
146
+ flexDirection="column"
147
+ >
148
+ {/* Status line */}
149
+ <Box>
150
+ <Text color={colors.warning}>PLAN MODE</Text>
151
+ <Text color={colors.textMuted}> │ </Text>
152
+ <Text color={colors.textSecondary}>Phase: </Text>
153
+ <Text color={colors.info}>{phase}</Text>
154
+ <Text color={colors.textMuted}> │ </Text>
155
+ <Text color={colors.textMuted}>Shift+Tab to switch</Text>
156
+ </Box>
157
+
158
+ {/* Tools info */}
159
+ <Box>
160
+ <Text color={colors.textMuted}>
161
+ Allowed: Read, Glob, Grep, WebFetch, WebSearch, TodoWrite
162
+ </Text>
163
+ </Box>
164
+
165
+ {/* Plan file */}
166
+ {displayPath && (
167
+ <Box>
168
+ <Text color={colors.textMuted}>Plan: </Text>
169
+ <Text color={colors.primary}>{displayPath}</Text>
170
+ </Box>
171
+ )}
172
+ </Box>
173
+ );
174
+ }
@@ -0,0 +1,327 @@
1
+ /**
2
+ * Plan Approval Component - Beautiful plan approval UI
3
+ *
4
+ * Displays the implementation plan and asks for user approval with options:
5
+ * 1. Yes, clear context and auto-accept edits (shift+tab)
6
+ * 2. Yes, auto-accept edits
7
+ * 3. Yes, manually approve edits
8
+ * 4. Type here to tell Claude what to change
9
+ *
10
+ * Based on Claude Code's plan approval UI.
11
+ */
12
+
13
+ import { useState, useEffect } from 'react';
14
+ import { Box, Text, useInput } from 'ink';
15
+ import { colors, icons } from './theme.js';
16
+ import type { PlanApprovalOption, AllowedPrompt } from '../../planning/types.js';
17
+
18
+ // ============================================================================
19
+ // Types
20
+ // ============================================================================
21
+
22
+ interface PlanApprovalProps {
23
+ /** Plan summary text */
24
+ planSummary: string;
25
+ /** Requested permissions from the plan */
26
+ requestedPermissions: AllowedPrompt[];
27
+ /** Files to be changed */
28
+ filesToChange: Array<{ path: string; action: 'create' | 'modify' | 'delete' }>;
29
+ /** Plan file path for display */
30
+ planFilePath: string;
31
+ /** Callback when user makes a decision */
32
+ onDecision: (option: PlanApprovalOption, customInput?: string) => void;
33
+ }
34
+
35
+ interface ApprovalOption {
36
+ label: string;
37
+ shortcut: string;
38
+ option: PlanApprovalOption;
39
+ description?: string;
40
+ }
41
+
42
+ // ============================================================================
43
+ // Constants
44
+ // ============================================================================
45
+
46
+ const APPROVAL_OPTIONS: ApprovalOption[] = [
47
+ {
48
+ label: 'Yes, clear context and auto-accept edits',
49
+ shortcut: 'shift+tab',
50
+ option: 'approve_clear',
51
+ description: 'Fresh start with automatic approval',
52
+ },
53
+ {
54
+ label: 'Yes, auto-accept edits',
55
+ shortcut: '1',
56
+ option: 'approve',
57
+ description: 'Continue with automatic approval',
58
+ },
59
+ {
60
+ label: 'Yes, manually approve edits',
61
+ shortcut: '2',
62
+ option: 'approve_manual',
63
+ description: 'Review each change before applying',
64
+ },
65
+ {
66
+ label: 'Type here to tell Claude what to change',
67
+ shortcut: '3',
68
+ option: 'modify',
69
+ description: 'Go back to modify the plan',
70
+ },
71
+ ];
72
+
73
+ // ============================================================================
74
+ // Helper Components
75
+ // ============================================================================
76
+
77
+ function FileChangesList({
78
+ files,
79
+ }: {
80
+ files: Array<{ path: string; action: 'create' | 'modify' | 'delete' }>;
81
+ }) {
82
+ if (files.length === 0) return null;
83
+
84
+ const getIcon = (action: string) => {
85
+ switch (action) {
86
+ case 'create':
87
+ return { icon: '+', color: colors.success };
88
+ case 'modify':
89
+ return { icon: '~', color: colors.warning };
90
+ case 'delete':
91
+ return { icon: '-', color: colors.error };
92
+ default:
93
+ return { icon: '?', color: colors.textMuted };
94
+ }
95
+ };
96
+
97
+ return (
98
+ <Box flexDirection="column" marginTop={1}>
99
+ <Text color={colors.textSecondary}>Files to change:</Text>
100
+ {files.slice(0, 8).map((file, i) => {
101
+ const { icon, color } = getIcon(file.action);
102
+ return (
103
+ <Text key={i}>
104
+ <Text color={color}> {icon} </Text>
105
+ <Text>{file.path}</Text>
106
+ <Text color={colors.textMuted}> ({file.action})</Text>
107
+ </Text>
108
+ );
109
+ })}
110
+ {files.length > 8 && (
111
+ <Text color={colors.textMuted}> ... and {files.length - 8} more files</Text>
112
+ )}
113
+ </Box>
114
+ );
115
+ }
116
+
117
+ function PermissionsList({ permissions }: { permissions: AllowedPrompt[] }) {
118
+ if (permissions.length === 0) return null;
119
+
120
+ return (
121
+ <Box flexDirection="column" marginTop={1}>
122
+ <Text color={colors.textSecondary}>Requested permissions:</Text>
123
+ {permissions.map((perm, i) => (
124
+ <Text key={i}>
125
+ <Text color={colors.textMuted}> - </Text>
126
+ <Text color={colors.tool}>{perm.tool}</Text>
127
+ <Text color={colors.textMuted}>(prompt: </Text>
128
+ <Text>{perm.prompt}</Text>
129
+ <Text color={colors.textMuted}>)</Text>
130
+ </Text>
131
+ ))}
132
+ </Box>
133
+ );
134
+ }
135
+
136
+ // ============================================================================
137
+ // Plan Approval Component
138
+ // ============================================================================
139
+
140
+ export function PlanApproval({
141
+ planSummary,
142
+ requestedPermissions,
143
+ filesToChange,
144
+ planFilePath,
145
+ onDecision,
146
+ }: PlanApprovalProps) {
147
+ const [selectedIndex, setSelectedIndex] = useState(0);
148
+ const [isTyping, setIsTyping] = useState(false);
149
+ const [customInput, setCustomInput] = useState('');
150
+
151
+ // Handle keyboard input
152
+ useInput((inputChar, key) => {
153
+ if (isTyping) {
154
+ // Handle typing mode
155
+ if (key.escape) {
156
+ setIsTyping(false);
157
+ setCustomInput('');
158
+ } else if (key.return && customInput.trim()) {
159
+ onDecision('modify', customInput.trim());
160
+ } else if (key.backspace || key.delete) {
161
+ setCustomInput((prev) => prev.slice(0, -1));
162
+ } else if (inputChar && !key.ctrl && !key.meta) {
163
+ setCustomInput((prev) => prev + inputChar);
164
+ }
165
+ return;
166
+ }
167
+
168
+ // Arrow navigation
169
+ if (key.upArrow) {
170
+ setSelectedIndex((i) => Math.max(0, i - 1));
171
+ } else if (key.downArrow) {
172
+ setSelectedIndex((i) => Math.min(APPROVAL_OPTIONS.length - 1, i + 1));
173
+ }
174
+
175
+ // Enter to select current option
176
+ if (key.return) {
177
+ const selected = APPROVAL_OPTIONS[selectedIndex];
178
+ if (selected.option === 'modify') {
179
+ setIsTyping(true);
180
+ } else {
181
+ onDecision(selected.option);
182
+ }
183
+ }
184
+
185
+ // Escape to cancel
186
+ if (key.escape) {
187
+ onDecision('cancel');
188
+ }
189
+
190
+ // Shift+Tab for approve_clear
191
+ if (key.shift && key.tab) {
192
+ onDecision('approve_clear');
193
+ }
194
+
195
+ // Number shortcuts
196
+ if (inputChar === '1') {
197
+ onDecision('approve');
198
+ } else if (inputChar === '2') {
199
+ onDecision('approve_manual');
200
+ } else if (inputChar === '3') {
201
+ setSelectedIndex(3);
202
+ setIsTyping(true);
203
+ }
204
+ });
205
+
206
+ // Shorten plan file path
207
+ const displayPath = planFilePath
208
+ .replace(process.env.HOME || '', '~')
209
+ .split('/')
210
+ .slice(-3)
211
+ .join('/');
212
+
213
+ return (
214
+ <Box flexDirection="column" borderStyle="round" borderColor={colors.warning} paddingX={1} paddingY={0}>
215
+ {/* Header */}
216
+ <Box marginBottom={1}>
217
+ <Text color={colors.warning} bold>Implementation Plan</Text>
218
+ </Box>
219
+
220
+ {/* Plan Summary */}
221
+ <Box flexDirection="column">
222
+ <Text color={colors.text}>{planSummary}</Text>
223
+ </Box>
224
+
225
+ {/* Files to change */}
226
+ <FileChangesList files={filesToChange} />
227
+
228
+ {/* Requested permissions */}
229
+ <PermissionsList permissions={requestedPermissions} />
230
+
231
+ {/* Question */}
232
+ <Box marginTop={1}>
233
+ <Text bold>Would you like to proceed?</Text>
234
+ </Box>
235
+
236
+ {/* Options */}
237
+ <Box flexDirection="column" marginTop={0} marginLeft={1}>
238
+ {APPROVAL_OPTIONS.map((opt, index) => {
239
+ const isSelected = index === selectedIndex;
240
+ return (
241
+ <Box key={opt.option}>
242
+ <Text color={isSelected ? colors.primary : colors.textMuted}>
243
+ {isSelected ? icons.radio : icons.radioEmpty}
244
+ </Text>
245
+ <Text color={colors.textMuted}> {String(index + 1)}. </Text>
246
+ <Text color={isSelected ? colors.text : colors.textSecondary}>
247
+ {opt.label}
248
+ </Text>
249
+ {opt.shortcut !== String(index + 1) && (
250
+ <Text color={colors.textMuted}> ({opt.shortcut})</Text>
251
+ )}
252
+ </Box>
253
+ );
254
+ })}
255
+ </Box>
256
+
257
+ {/* Typing mode */}
258
+ {isTyping && (
259
+ <Box marginTop={1} marginLeft={1}>
260
+ <Text color={colors.primary}>{icons.prompt} </Text>
261
+ <Text>{customInput}</Text>
262
+ <Text color={colors.primary}>{icons.cursor}</Text>
263
+ </Box>
264
+ )}
265
+
266
+ {/* Footer hint */}
267
+ <Box marginTop={1}>
268
+ <Text color={colors.textMuted}>
269
+ ctrl-g to edit in VS Code · {displayPath}
270
+ </Text>
271
+ </Box>
272
+ </Box>
273
+ );
274
+ }
275
+
276
+ // ============================================================================
277
+ // Plan Mode Toggle Notification
278
+ // ============================================================================
279
+
280
+ interface ModeChangeNotificationProps {
281
+ fromMode: 'build' | 'plan';
282
+ toMode: 'build' | 'plan';
283
+ planFilePath?: string;
284
+ }
285
+
286
+ export function ModeChangeNotification({
287
+ fromMode,
288
+ toMode,
289
+ planFilePath,
290
+ }: ModeChangeNotificationProps) {
291
+ const isPlanMode = toMode === 'plan';
292
+ const displayPath = planFilePath
293
+ ? planFilePath.replace(process.env.HOME || '', '~').split('/').slice(-2).join('/')
294
+ : '';
295
+
296
+ return (
297
+ <Box
298
+ flexDirection="column"
299
+ borderStyle="round"
300
+ borderColor={isPlanMode ? colors.warning : colors.success}
301
+ paddingX={1}
302
+ >
303
+ <Box>
304
+ <Text color={colors.textMuted}>{icons.radioEmpty} {fromMode.toUpperCase()}</Text>
305
+ <Text color={colors.textMuted}> {icons.arrow} </Text>
306
+ <Text color={isPlanMode ? colors.warning : colors.success} bold>
307
+ {icons.radio} {toMode.toUpperCase()}
308
+ </Text>
309
+ </Box>
310
+
311
+ <Box>
312
+ {isPlanMode ? (
313
+ <Text color={colors.warning}>Switched to PLAN mode</Text>
314
+ ) : (
315
+ <Text color={colors.success}>Switched to BUILD mode</Text>
316
+ )}
317
+ </Box>
318
+
319
+ {isPlanMode && displayPath && (
320
+ <Box>
321
+ <Text color={colors.textMuted}>Plan file: </Text>
322
+ <Text color={colors.primary}>{displayPath}</Text>
323
+ </Box>
324
+ )}
325
+ </Box>
326
+ );
327
+ }