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.
- package/README.md +2 -1
- package/dist/agent/agent.d.ts +44 -2
- package/dist/agent/agent.d.ts.map +1 -1
- package/dist/agent/agent.js +130 -11
- package/dist/agent/agent.js.map +1 -1
- package/dist/agent/types.d.ts +11 -1
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/checkpointing/checkpoint-manager.d.ts +87 -0
- package/dist/checkpointing/checkpoint-manager.d.ts.map +1 -0
- package/dist/checkpointing/checkpoint-manager.js +281 -0
- package/dist/checkpointing/checkpoint-manager.js.map +1 -0
- package/dist/checkpointing/index.d.ts +29 -0
- package/dist/checkpointing/index.d.ts.map +1 -0
- package/dist/checkpointing/index.js +29 -0
- package/dist/checkpointing/index.js.map +1 -0
- package/dist/checkpointing/types.d.ts +98 -0
- package/dist/checkpointing/types.d.ts.map +1 -0
- package/dist/checkpointing/types.js +7 -0
- package/dist/checkpointing/types.js.map +1 -0
- package/dist/cli/components/App.d.ts.map +1 -1
- package/dist/cli/components/App.js +171 -14
- package/dist/cli/components/App.js.map +1 -1
- package/dist/cli/components/CommandSuggestions.d.ts.map +1 -1
- package/dist/cli/components/CommandSuggestions.js +5 -0
- package/dist/cli/components/CommandSuggestions.js.map +1 -1
- package/dist/cli/components/Messages.d.ts +7 -1
- package/dist/cli/components/Messages.d.ts.map +1 -1
- package/dist/cli/components/Messages.js +12 -3
- package/dist/cli/components/Messages.js.map +1 -1
- package/dist/cli/components/ModeIndicator.d.ts +42 -0
- package/dist/cli/components/ModeIndicator.d.ts.map +1 -0
- package/dist/cli/components/ModeIndicator.js +52 -0
- package/dist/cli/components/ModeIndicator.js.map +1 -0
- package/dist/cli/components/ModelSelector.d.ts +4 -3
- package/dist/cli/components/ModelSelector.d.ts.map +1 -1
- package/dist/cli/components/ModelSelector.js +54 -37
- package/dist/cli/components/ModelSelector.js.map +1 -1
- package/dist/cli/components/PlanApproval.d.ts +36 -0
- package/dist/cli/components/PlanApproval.d.ts.map +1 -0
- package/dist/cli/components/PlanApproval.js +154 -0
- package/dist/cli/components/PlanApproval.js.map +1 -0
- package/dist/cli/components/ProviderManager.d.ts +2 -2
- package/dist/cli/components/ProviderManager.d.ts.map +1 -1
- package/dist/cli/components/ProviderManager.js +137 -156
- package/dist/cli/components/ProviderManager.js.map +1 -1
- package/dist/cli/components/theme.d.ts +2 -0
- package/dist/cli/components/theme.d.ts.map +1 -1
- package/dist/cli/components/theme.js +3 -0
- package/dist/cli/components/theme.js.map +1 -1
- package/dist/cli/index.js +30 -13
- package/dist/cli/index.js.map +1 -1
- package/dist/config/index.d.ts +2 -2
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +1 -1
- package/dist/config/index.js.map +1 -1
- package/dist/config/levels.d.ts +5 -5
- package/dist/config/levels.d.ts.map +1 -1
- package/dist/config/levels.js +20 -20
- package/dist/config/levels.js.map +1 -1
- package/dist/config/merger.js +1 -1
- package/dist/config/merger.js.map +1 -1
- package/dist/config/providers-config.d.ts +8 -5
- package/dist/config/providers-config.d.ts.map +1 -1
- package/dist/config/providers-config.js +19 -22
- package/dist/config/providers-config.js.map +1 -1
- package/dist/config/test-utils.d.ts +2 -2
- package/dist/config/test-utils.d.ts.map +1 -1
- package/dist/config/test-utils.js +4 -4
- package/dist/config/test-utils.js.map +1 -1
- package/dist/config/types.d.ts +23 -17
- package/dist/config/types.d.ts.map +1 -1
- package/dist/config/types.js +14 -14
- package/dist/config/types.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/memory/memory-manager.d.ts +25 -12
- package/dist/memory/memory-manager.d.ts.map +1 -1
- package/dist/memory/memory-manager.js +241 -112
- package/dist/memory/memory-manager.js.map +1 -1
- package/dist/memory/test-utils.d.ts +1 -1
- package/dist/memory/test-utils.d.ts.map +1 -1
- package/dist/memory/test-utils.js +3 -3
- package/dist/memory/test-utils.js.map +1 -1
- package/dist/memory/types.d.ts +20 -10
- package/dist/memory/types.d.ts.map +1 -1
- package/dist/memory/types.js +13 -13
- package/dist/memory/types.js.map +1 -1
- package/dist/migration/migrate.d.ts +24 -0
- package/dist/migration/migrate.d.ts.map +1 -0
- package/dist/migration/migrate.js +164 -0
- package/dist/migration/migrate.js.map +1 -0
- package/dist/permissions/persistence.d.ts +2 -2
- package/dist/permissions/persistence.js +4 -4
- package/dist/permissions/persistence.js.map +1 -1
- package/dist/planning/index.d.ts +13 -0
- package/dist/planning/index.d.ts.map +1 -0
- package/dist/planning/index.js +15 -0
- package/dist/planning/index.js.map +1 -0
- package/dist/planning/plan-file.d.ts +59 -0
- package/dist/planning/plan-file.d.ts.map +1 -0
- package/dist/planning/plan-file.js +278 -0
- package/dist/planning/plan-file.js.map +1 -0
- package/dist/planning/state.d.ts +127 -0
- package/dist/planning/state.d.ts.map +1 -0
- package/dist/planning/state.js +261 -0
- package/dist/planning/state.js.map +1 -0
- package/dist/planning/tools/enter-plan-mode.d.ts +25 -0
- package/dist/planning/tools/enter-plan-mode.d.ts.map +1 -0
- package/dist/planning/tools/enter-plan-mode.js +98 -0
- package/dist/planning/tools/enter-plan-mode.js.map +1 -0
- package/dist/planning/tools/exit-plan-mode.d.ts +24 -0
- package/dist/planning/tools/exit-plan-mode.d.ts.map +1 -0
- package/dist/planning/tools/exit-plan-mode.js +149 -0
- package/dist/planning/tools/exit-plan-mode.js.map +1 -0
- package/dist/planning/types.d.ts +100 -0
- package/dist/planning/types.d.ts.map +1 -0
- package/dist/planning/types.js +28 -0
- package/dist/planning/types.js.map +1 -0
- package/dist/pricing/calculator.d.ts +21 -0
- package/dist/pricing/calculator.d.ts.map +1 -0
- package/dist/pricing/calculator.js +59 -0
- package/dist/pricing/calculator.js.map +1 -0
- package/dist/pricing/index.d.ts +7 -0
- package/dist/pricing/index.d.ts.map +1 -0
- package/dist/pricing/index.js +7 -0
- package/dist/pricing/index.js.map +1 -0
- package/dist/pricing/models.d.ts +20 -0
- package/dist/pricing/models.d.ts.map +1 -0
- package/dist/pricing/models.js +322 -0
- package/dist/pricing/models.js.map +1 -0
- package/dist/pricing/types.d.ts +30 -0
- package/dist/pricing/types.d.ts.map +1 -0
- package/dist/pricing/types.js +5 -0
- package/dist/pricing/types.js.map +1 -0
- package/dist/prompts/index.d.ts +5 -4
- package/dist/prompts/index.d.ts.map +1 -1
- package/dist/prompts/index.js +11 -8
- package/dist/prompts/index.js.map +1 -1
- package/dist/providers/anthropic.d.ts +2 -1
- package/dist/providers/anthropic.d.ts.map +1 -1
- package/dist/providers/anthropic.js +24 -10
- package/dist/providers/anthropic.js.map +1 -1
- package/dist/providers/gemini.d.ts +2 -1
- package/dist/providers/gemini.d.ts.map +1 -1
- package/dist/providers/gemini.js +28 -14
- package/dist/providers/gemini.js.map +1 -1
- package/dist/providers/index.d.ts +20 -10
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js +48 -24
- package/dist/providers/index.js.map +1 -1
- package/dist/providers/openai.d.ts +2 -1
- package/dist/providers/openai.d.ts.map +1 -1
- package/dist/providers/openai.js +19 -8
- package/dist/providers/openai.js.map +1 -1
- package/dist/providers/registry.d.ts +48 -34
- package/dist/providers/registry.d.ts.map +1 -1
- package/dist/providers/registry.js +72 -88
- package/dist/providers/registry.js.map +1 -1
- package/dist/providers/store.d.ts +43 -17
- package/dist/providers/store.d.ts.map +1 -1
- package/dist/providers/store.js +112 -19
- package/dist/providers/store.js.map +1 -1
- package/dist/providers/types.d.ts +25 -0
- package/dist/providers/types.d.ts.map +1 -1
- package/dist/providers/vertex-ai.d.ts +15 -7
- package/dist/providers/vertex-ai.d.ts.map +1 -1
- package/dist/providers/vertex-ai.js +63 -23
- package/dist/providers/vertex-ai.js.map +1 -1
- package/dist/session/manager.d.ts +4 -0
- package/dist/session/manager.d.ts.map +1 -1
- package/dist/session/manager.js +8 -0
- package/dist/session/manager.js.map +1 -1
- package/dist/session/types.js +1 -1
- package/dist/session/types.js.map +1 -1
- package/dist/tools/index.d.ts +7 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +7 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/registry.d.ts +13 -0
- package/dist/tools/registry.d.ts.map +1 -1
- package/dist/tools/registry.js +79 -2
- package/dist/tools/registry.js.map +1 -1
- package/docs/config-system-comparison.md +50 -50
- package/docs/cost-tracking-comparison.md +904 -0
- package/docs/memory-system.md +124 -31
- package/docs/operating-modes.md +96 -0
- package/docs/permissions.md +2 -2
- package/docs/proposals/0006-memory-system.md +4 -4
- package/docs/proposals/0008-checkpointing.md +109 -2
- package/docs/proposals/0011-custom-commands.md +2 -1
- package/docs/proposals/0021-skills-system.md +2 -1
- package/docs/proposals/0023-permission-enhancements.md +2 -2
- package/docs/proposals/0025-cost-tracking.md +60 -2
- package/docs/proposals/0033-enterprise-deployment.md +1 -1
- package/docs/proposals/0041-configuration-system.md +17 -19
- package/docs/proposals/0042-prompt-optimization.md +17 -9
- package/docs/proposals/README.md +6 -6
- package/docs/providers.md +94 -9
- package/examples/test-checkpointing.ts +121 -0
- package/examples/test-cost-tracking.ts +77 -0
- package/examples/test-interrupt-cleanup.ts +94 -0
- package/package.json +3 -2
- package/scripts/migrate.ts +449 -0
- package/src/agent/agent.ts +161 -12
- package/src/agent/types.ts +11 -1
- package/src/checkpointing/checkpoint-manager.ts +327 -0
- package/src/checkpointing/index.ts +45 -0
- package/src/checkpointing/types.ts +104 -0
- package/src/cli/components/App.tsx +221 -13
- package/src/cli/components/CommandSuggestions.tsx +5 -0
- package/src/cli/components/Messages.tsx +24 -5
- package/src/cli/components/ModeIndicator.tsx +174 -0
- package/src/cli/components/ModelSelector.tsx +62 -43
- package/src/cli/components/PlanApproval.tsx +327 -0
- package/src/cli/components/ProviderManager.tsx +278 -323
- package/src/cli/components/theme.ts +3 -0
- package/src/cli/index.tsx +36 -17
- package/src/config/index.ts +5 -3
- package/src/config/levels.test.ts +22 -22
- package/src/config/levels.ts +22 -22
- package/src/config/loader.test.ts +14 -14
- package/src/config/manager.test.ts +19 -19
- package/src/config/merger.test.ts +23 -23
- package/src/config/merger.ts +1 -1
- package/src/config/providers-config.ts +23 -21
- package/src/config/test-utils.ts +6 -6
- package/src/config/types.ts +30 -20
- package/src/index.ts +15 -0
- package/src/memory/memory-manager.test.ts +242 -24
- package/src/memory/memory-manager.ts +270 -141
- package/src/memory/test-utils.ts +4 -4
- package/src/memory/types.ts +28 -17
- package/src/permissions/persistence.ts +4 -4
- package/src/planning/index.ts +53 -0
- package/src/planning/plan-file.ts +326 -0
- package/src/planning/state.ts +305 -0
- package/src/planning/tools/enter-plan-mode.ts +111 -0
- package/src/planning/tools/exit-plan-mode.ts +170 -0
- package/src/planning/types.ts +150 -0
- package/src/pricing/calculator.ts +71 -0
- package/src/pricing/index.ts +7 -0
- package/src/pricing/models.ts +334 -0
- package/src/pricing/types.ts +32 -0
- package/src/prompts/index.ts +13 -9
- package/src/providers/anthropic.ts +30 -10
- package/src/providers/gemini.ts +34 -14
- package/src/providers/index.ts +76 -33
- package/src/providers/openai.ts +26 -8
- package/src/providers/registry.ts +116 -111
- package/src/providers/store.ts +130 -28
- package/src/providers/types.ts +36 -1
- package/src/providers/vertex-ai.ts +70 -23
- package/src/session/manager.ts +9 -0
- package/src/session/types.ts +1 -1
- package/src/tools/index.ts +8 -0
- package/src/tools/registry.ts +95 -2
- package/.gencode/settings.local.json +0 -7
- package/CLAUDE.md +0 -86
|
@@ -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
|
+
}
|
|
@@ -7,24 +7,27 @@ import TextInput from 'ink-text-input';
|
|
|
7
7
|
import { colors, icons } from './theme.js';
|
|
8
8
|
import { LoadingSpinner } from './Spinner.js';
|
|
9
9
|
import { getProviderStore, type ModelInfo } from '../../providers/store.js';
|
|
10
|
-
import {
|
|
11
|
-
import type {
|
|
10
|
+
import { getProviderMeta } from '../../providers/registry.js';
|
|
11
|
+
import type { Provider, AuthMethod } from '../../providers/index.js';
|
|
12
12
|
|
|
13
13
|
interface ModelItem {
|
|
14
|
-
providerId:
|
|
14
|
+
providerId: Provider;
|
|
15
15
|
providerName: string;
|
|
16
|
+
authMethod: AuthMethod;
|
|
16
17
|
model: ModelInfo;
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
interface ModelSelectorProps {
|
|
20
21
|
currentModel: string;
|
|
21
|
-
|
|
22
|
+
currentProvider?: Provider; // Current provider for adding missing model placeholder
|
|
23
|
+
onSelect: (modelId: string, providerId: Provider, authMethod?: AuthMethod) => void;
|
|
22
24
|
onCancel: () => void;
|
|
23
25
|
listModels: () => Promise<{ id: string; name: string }[]>; // Fallback for current provider
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
export function ModelSelector({
|
|
27
29
|
currentModel,
|
|
30
|
+
currentProvider,
|
|
28
31
|
onSelect,
|
|
29
32
|
onCancel,
|
|
30
33
|
listModels,
|
|
@@ -42,14 +45,17 @@ export function ModelSelector({
|
|
|
42
45
|
const items: ModelItem[] = [];
|
|
43
46
|
|
|
44
47
|
for (const providerId of connectedProviders) {
|
|
48
|
+
const connection = store.getConnection(providerId);
|
|
49
|
+
const authMethod = connection?.authMethod || 'api_key';
|
|
45
50
|
const cachedModels = store.getModels(providerId);
|
|
46
|
-
const
|
|
47
|
-
const providerName =
|
|
51
|
+
const providerMeta = getProviderMeta(providerId);
|
|
52
|
+
const providerName = providerMeta?.name || providerId;
|
|
48
53
|
|
|
49
54
|
for (const model of cachedModels) {
|
|
50
55
|
items.push({
|
|
51
56
|
providerId,
|
|
52
57
|
providerName,
|
|
58
|
+
authMethod,
|
|
53
59
|
model,
|
|
54
60
|
});
|
|
55
61
|
}
|
|
@@ -61,8 +67,9 @@ export function ModelSelector({
|
|
|
61
67
|
const models = await listModels();
|
|
62
68
|
for (const model of models) {
|
|
63
69
|
items.push({
|
|
64
|
-
providerId: 'anthropic' as
|
|
70
|
+
providerId: 'anthropic' as Provider, // Default, will be overridden
|
|
65
71
|
providerName: 'Current Provider',
|
|
72
|
+
authMethod: 'api_key', // Default
|
|
66
73
|
model,
|
|
67
74
|
});
|
|
68
75
|
}
|
|
@@ -71,12 +78,32 @@ export function ModelSelector({
|
|
|
71
78
|
}
|
|
72
79
|
}
|
|
73
80
|
|
|
81
|
+
// Add current model if not in list (e.g., experimental models not cached)
|
|
82
|
+
const hasCurrentModel = items.some((item) => item.model.id === currentModel);
|
|
83
|
+
|
|
84
|
+
if (!hasCurrentModel && currentModel && currentProvider) {
|
|
85
|
+
const connection = store.getConnection(currentProvider);
|
|
86
|
+
const authMethod = connection?.authMethod || 'api_key';
|
|
87
|
+
const providerMeta = getProviderMeta(currentProvider);
|
|
88
|
+
const providerName = providerMeta?.name || currentProvider;
|
|
89
|
+
|
|
90
|
+
items.unshift({
|
|
91
|
+
providerId: currentProvider,
|
|
92
|
+
providerName,
|
|
93
|
+
authMethod,
|
|
94
|
+
model: {
|
|
95
|
+
id: currentModel,
|
|
96
|
+
name: currentModel, // Display logic will add "(current)" marker
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
74
101
|
setAllModels(items);
|
|
75
102
|
setLoading(false);
|
|
76
103
|
};
|
|
77
104
|
|
|
78
105
|
loadModels();
|
|
79
|
-
}, [store, listModels]);
|
|
106
|
+
}, [store, listModels, currentModel, currentProvider]);
|
|
80
107
|
|
|
81
108
|
// Filter models
|
|
82
109
|
const filterLower = filter.toLowerCase();
|
|
@@ -101,14 +128,21 @@ export function ModelSelector({
|
|
|
101
128
|
return groups;
|
|
102
129
|
}, [filtered]);
|
|
103
130
|
|
|
104
|
-
// Flat list for navigation
|
|
131
|
+
// Flat list for navigation - sort to put current model first
|
|
105
132
|
const flatList = useMemo(() => {
|
|
106
133
|
const items: ModelItem[] = [];
|
|
107
134
|
for (const providerId of Object.keys(groupedModels)) {
|
|
108
135
|
items.push(...groupedModels[providerId]);
|
|
109
136
|
}
|
|
110
|
-
|
|
111
|
-
|
|
137
|
+
// Sort: current model first, then alphabetically
|
|
138
|
+
return items.sort((a, b) => {
|
|
139
|
+
const aIsCurrent = a.model.id === currentModel;
|
|
140
|
+
const bIsCurrent = b.model.id === currentModel;
|
|
141
|
+
if (aIsCurrent && !bIsCurrent) return -1;
|
|
142
|
+
if (!aIsCurrent && bIsCurrent) return 1;
|
|
143
|
+
return (a.model.name || a.model.id).localeCompare(b.model.name || b.model.id);
|
|
144
|
+
});
|
|
145
|
+
}, [groupedModels, currentModel]);
|
|
112
146
|
|
|
113
147
|
// Reset selection when filter changes
|
|
114
148
|
useEffect(() => {
|
|
@@ -124,7 +158,7 @@ export function ModelSelector({
|
|
|
124
158
|
} else if (key.return) {
|
|
125
159
|
if (flatList.length > 0) {
|
|
126
160
|
const selected = flatList[selectedIndex];
|
|
127
|
-
onSelect(selected.model.id, selected.providerId);
|
|
161
|
+
onSelect(selected.model.id, selected.providerId, selected.authMethod);
|
|
128
162
|
}
|
|
129
163
|
} else if (key.escape) {
|
|
130
164
|
onCancel();
|
|
@@ -152,41 +186,24 @@ export function ModelSelector({
|
|
|
152
186
|
);
|
|
153
187
|
const endIndex = Math.min(startIndex + maxVisible, flatList.length);
|
|
154
188
|
|
|
155
|
-
// Build visible items with provider headers
|
|
156
|
-
let currentIdx = 0;
|
|
189
|
+
// Build visible items with provider headers from sorted flatList
|
|
157
190
|
const renderItems: Array<{ type: 'header' | 'model'; content: string; item?: ModelItem }> = [];
|
|
191
|
+
let lastProviderId: string | null = null;
|
|
158
192
|
|
|
159
|
-
for (
|
|
160
|
-
const
|
|
161
|
-
|
|
162
|
-
const lastIdx = currentIdx + models.length - 1;
|
|
163
|
-
|
|
164
|
-
// Check if any model from this provider is in visible range
|
|
165
|
-
if (lastIdx >= startIndex && firstIdx < endIndex) {
|
|
166
|
-
// Add header if first visible item is from this provider
|
|
167
|
-
const providerDef = getProvider(providerId as ProviderName);
|
|
168
|
-
const connection = store.getConnection(providerId as ProviderName);
|
|
169
|
-
const headerText = `${providerDef?.name || providerId}${connection ? ` (${connection.method})` : ''}:`;
|
|
170
|
-
|
|
171
|
-
// Only add header if we're showing models from this provider
|
|
172
|
-
const visibleModelsFromProvider = models.filter((_, i) => {
|
|
173
|
-
const globalIdx = currentIdx + i;
|
|
174
|
-
return globalIdx >= startIndex && globalIdx < endIndex;
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
if (visibleModelsFromProvider.length > 0 && (firstIdx >= startIndex || renderItems.length === 0)) {
|
|
178
|
-
renderItems.push({ type: 'header', content: headerText });
|
|
179
|
-
}
|
|
193
|
+
for (let i = startIndex; i < endIndex; i++) {
|
|
194
|
+
const item = flatList[i];
|
|
195
|
+
if (!item) continue;
|
|
180
196
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
}
|
|
197
|
+
// Add provider header when provider changes
|
|
198
|
+
const providerKey = `${item.providerId}:${item.authMethod}`;
|
|
199
|
+
if (providerKey !== lastProviderId) {
|
|
200
|
+
const providerMeta = getProviderMeta(item.providerId);
|
|
201
|
+
const headerText = `${providerMeta?.name || item.providerId} (${item.authMethod}):`;
|
|
202
|
+
renderItems.push({ type: 'header', content: headerText });
|
|
203
|
+
lastProviderId = providerKey;
|
|
187
204
|
}
|
|
188
205
|
|
|
189
|
-
|
|
206
|
+
renderItems.push({ type: 'model', content: '', item });
|
|
190
207
|
}
|
|
191
208
|
|
|
192
209
|
return (
|
|
@@ -228,11 +245,13 @@ export function ModelSelector({
|
|
|
228
245
|
<Text color={isSelected ? colors.primary : colors.textMuted}>
|
|
229
246
|
{isSelected ? icons.arrow : ' '}
|
|
230
247
|
</Text>
|
|
248
|
+
<Text> </Text>
|
|
231
249
|
<Text color={isCurrent ? colors.primary : colors.textMuted}>
|
|
232
250
|
{isCurrent ? icons.radio : icons.radioEmpty}
|
|
233
251
|
</Text>
|
|
252
|
+
<Text> </Text>
|
|
234
253
|
<Text color={isSelected ? colors.text : colors.textSecondary} bold={isSelected}>
|
|
235
|
-
{
|
|
254
|
+
{item.model.name || item.model.id}
|
|
236
255
|
</Text>
|
|
237
256
|
{isCurrent && <Text color={colors.success}> (current)</Text>}
|
|
238
257
|
</Box>
|
|
@@ -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
|
+
}
|