@projectarachne/cli 0.1.0-beta.1

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 (153) hide show
  1. package/LICENSE +75 -0
  2. package/README.md +40 -0
  3. package/bin/arachne +2 -0
  4. package/bin/arachne.js +2 -0
  5. package/dist/arachne.d.ts +3 -0
  6. package/dist/arachne.d.ts.map +1 -0
  7. package/dist/arachne.js +4 -0
  8. package/dist/arachne.js.map +1 -0
  9. package/dist/index.d.ts +4 -0
  10. package/dist/index.d.ts.map +1 -0
  11. package/dist/index.js +145 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/serve.d.ts +6 -0
  14. package/dist/serve.d.ts.map +1 -0
  15. package/dist/serve.js +83 -0
  16. package/dist/serve.js.map +1 -0
  17. package/dist/templates.d.ts +3 -0
  18. package/dist/templates.d.ts.map +1 -0
  19. package/dist/templates.js +22 -0
  20. package/dist/templates.js.map +1 -0
  21. package/dist/tui/App.d.ts +2 -0
  22. package/dist/tui/App.d.ts.map +1 -0
  23. package/dist/tui/App.js +1078 -0
  24. package/dist/tui/App.js.map +1 -0
  25. package/dist/tui/components/Box.d.ts +19 -0
  26. package/dist/tui/components/Box.d.ts.map +1 -0
  27. package/dist/tui/components/Box.js +9 -0
  28. package/dist/tui/components/Box.js.map +1 -0
  29. package/dist/tui/components/ChatInput.d.ts +12 -0
  30. package/dist/tui/components/ChatInput.d.ts.map +1 -0
  31. package/dist/tui/components/ChatInput.js +10 -0
  32. package/dist/tui/components/ChatInput.js.map +1 -0
  33. package/dist/tui/components/CommandInput.d.ts +15 -0
  34. package/dist/tui/components/CommandInput.d.ts.map +1 -0
  35. package/dist/tui/components/CommandInput.js +92 -0
  36. package/dist/tui/components/CommandInput.js.map +1 -0
  37. package/dist/tui/components/Content.d.ts +18 -0
  38. package/dist/tui/components/Content.d.ts.map +1 -0
  39. package/dist/tui/components/Content.js +22 -0
  40. package/dist/tui/components/Content.js.map +1 -0
  41. package/dist/tui/components/DagView.d.ts +8 -0
  42. package/dist/tui/components/DagView.d.ts.map +1 -0
  43. package/dist/tui/components/DagView.js +49 -0
  44. package/dist/tui/components/DagView.js.map +1 -0
  45. package/dist/tui/components/Footer.d.ts +9 -0
  46. package/dist/tui/components/Footer.d.ts.map +1 -0
  47. package/dist/tui/components/Footer.js +6 -0
  48. package/dist/tui/components/Footer.js.map +1 -0
  49. package/dist/tui/components/Frame.d.ts +6 -0
  50. package/dist/tui/components/Frame.d.ts.map +1 -0
  51. package/dist/tui/components/Frame.js +10 -0
  52. package/dist/tui/components/Frame.js.map +1 -0
  53. package/dist/tui/components/GoalInput.d.ts +10 -0
  54. package/dist/tui/components/GoalInput.d.ts.map +1 -0
  55. package/dist/tui/components/GoalInput.js +8 -0
  56. package/dist/tui/components/GoalInput.js.map +1 -0
  57. package/dist/tui/components/Header.d.ts +8 -0
  58. package/dist/tui/components/Header.d.ts.map +1 -0
  59. package/dist/tui/components/Header.js +8 -0
  60. package/dist/tui/components/Header.js.map +1 -0
  61. package/dist/tui/components/Help.d.ts +6 -0
  62. package/dist/tui/components/Help.d.ts.map +1 -0
  63. package/dist/tui/components/Help.js +8 -0
  64. package/dist/tui/components/Help.js.map +1 -0
  65. package/dist/tui/components/HomeView.d.ts +2 -0
  66. package/dist/tui/components/HomeView.d.ts.map +1 -0
  67. package/dist/tui/components/HomeView.js +8 -0
  68. package/dist/tui/components/HomeView.js.map +1 -0
  69. package/dist/tui/components/OutputView.d.ts +9 -0
  70. package/dist/tui/components/OutputView.d.ts.map +1 -0
  71. package/dist/tui/components/OutputView.js +40 -0
  72. package/dist/tui/components/OutputView.js.map +1 -0
  73. package/dist/tui/components/PlannerView.d.ts +17 -0
  74. package/dist/tui/components/PlannerView.d.ts.map +1 -0
  75. package/dist/tui/components/PlannerView.js +62 -0
  76. package/dist/tui/components/PlannerView.js.map +1 -0
  77. package/dist/tui/components/Settings.d.ts +6 -0
  78. package/dist/tui/components/Settings.d.ts.map +1 -0
  79. package/dist/tui/components/Settings.js +210 -0
  80. package/dist/tui/components/Settings.js.map +1 -0
  81. package/dist/tui/components/Sidebar.d.ts +12 -0
  82. package/dist/tui/components/Sidebar.d.ts.map +1 -0
  83. package/dist/tui/components/Sidebar.js +9 -0
  84. package/dist/tui/components/Sidebar.js.map +1 -0
  85. package/dist/tui/components/Splash.d.ts +7 -0
  86. package/dist/tui/components/Splash.d.ts.map +1 -0
  87. package/dist/tui/components/Splash.js +56 -0
  88. package/dist/tui/components/Splash.js.map +1 -0
  89. package/dist/tui/components/StepEditor.d.ts +13 -0
  90. package/dist/tui/components/StepEditor.d.ts.map +1 -0
  91. package/dist/tui/components/StepEditor.js +33 -0
  92. package/dist/tui/components/StepEditor.js.map +1 -0
  93. package/dist/tui/components/ThemeDesigner.d.ts +9 -0
  94. package/dist/tui/components/ThemeDesigner.d.ts.map +1 -0
  95. package/dist/tui/components/ThemeDesigner.js +45 -0
  96. package/dist/tui/components/ThemeDesigner.js.map +1 -0
  97. package/dist/tui/components/WorkflowPicker.d.ts +8 -0
  98. package/dist/tui/components/WorkflowPicker.d.ts.map +1 -0
  99. package/dist/tui/components/WorkflowPicker.js +11 -0
  100. package/dist/tui/components/WorkflowPicker.js.map +1 -0
  101. package/dist/tui/components/index.d.ts +20 -0
  102. package/dist/tui/components/index.d.ts.map +1 -0
  103. package/dist/tui/components/index.js +20 -0
  104. package/dist/tui/components/index.js.map +1 -0
  105. package/dist/tui/index.d.ts +2 -0
  106. package/dist/tui/index.d.ts.map +1 -0
  107. package/dist/tui/index.js +7 -0
  108. package/dist/tui/index.js.map +1 -0
  109. package/dist/tui/services/context.d.ts +11 -0
  110. package/dist/tui/services/context.d.ts.map +1 -0
  111. package/dist/tui/services/context.js +134 -0
  112. package/dist/tui/services/context.js.map +1 -0
  113. package/dist/tui/services/executor.d.ts +13 -0
  114. package/dist/tui/services/executor.d.ts.map +1 -0
  115. package/dist/tui/services/executor.js +186 -0
  116. package/dist/tui/services/executor.js.map +1 -0
  117. package/dist/tui/services/generateSteps.d.ts +7 -0
  118. package/dist/tui/services/generateSteps.d.ts.map +1 -0
  119. package/dist/tui/services/generateSteps.js +238 -0
  120. package/dist/tui/services/generateSteps.js.map +1 -0
  121. package/dist/tui/services/providers.d.ts +16 -0
  122. package/dist/tui/services/providers.d.ts.map +1 -0
  123. package/dist/tui/services/providers.js +246 -0
  124. package/dist/tui/services/providers.js.map +1 -0
  125. package/dist/tui/services/settings.d.ts +29 -0
  126. package/dist/tui/services/settings.d.ts.map +1 -0
  127. package/dist/tui/services/settings.js +107 -0
  128. package/dist/tui/services/settings.js.map +1 -0
  129. package/dist/tui/services/theme.d.ts +45 -0
  130. package/dist/tui/services/theme.d.ts.map +1 -0
  131. package/dist/tui/services/theme.js +205 -0
  132. package/dist/tui/services/theme.js.map +1 -0
  133. package/dist/tui/services/themeGenerator.d.ts +4 -0
  134. package/dist/tui/services/themeGenerator.d.ts.map +1 -0
  135. package/dist/tui/services/themeGenerator.js +93 -0
  136. package/dist/tui/services/themeGenerator.js.map +1 -0
  137. package/dist/tui/services/user.d.ts +3 -0
  138. package/dist/tui/services/user.d.ts.map +1 -0
  139. package/dist/tui/services/user.js +40 -0
  140. package/dist/tui/services/user.js.map +1 -0
  141. package/dist/tui/theme-context.d.ts +4 -0
  142. package/dist/tui/theme-context.d.ts.map +1 -0
  143. package/dist/tui/theme-context.js +6 -0
  144. package/dist/tui/theme-context.js.map +1 -0
  145. package/dist/tui/types.d.ts +81 -0
  146. package/dist/tui/types.d.ts.map +1 -0
  147. package/dist/tui/types.js +3 -0
  148. package/dist/tui/types.js.map +1 -0
  149. package/dist/tui.d.ts +2 -0
  150. package/dist/tui.d.ts.map +1 -0
  151. package/dist/tui.js +241 -0
  152. package/dist/tui.js.map +1 -0
  153. package/package.json +52 -0
@@ -0,0 +1,1078 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState, useEffect, useCallback, useRef, useMemo } from 'react';
3
+ import { Box, Text, useApp, useInput } from 'ink';
4
+ import { readdir, readFile, writeFile } from 'node:fs/promises';
5
+ import { join } from 'node:path';
6
+ import { parse, stringify } from 'yaml';
7
+ import { Header, Splash, CommandInput, Help, Settings as SettingsPanel, COMMANDS, Frame, ChatInput, ThemeDesigner, WorkflowPicker, PlannerView, } from './components/index.js';
8
+ import { generatePlanFromGoal } from './services/generateSteps.js';
9
+ import { scanProjectContext } from './services/context.js';
10
+ import { executeStep } from './services/executor.js';
11
+ import { getUserName } from './services/user.js';
12
+ import { detectProvidersSync } from './services/providers.js';
13
+ import { loadConfig } from './services/settings.js';
14
+ import { loadTheme } from './services/theme.js';
15
+ import { ThemeProvider } from './theme-context.js';
16
+ const loadWorkflowFiles = async (dir) => {
17
+ try {
18
+ const entries = await readdir(dir, { withFileTypes: true });
19
+ return entries
20
+ .filter((entry) => entry.isFile() &&
21
+ (entry.name.endsWith('.yaml') || entry.name.endsWith('.yml')))
22
+ .map((entry) => ({
23
+ name: entry.name,
24
+ path: join(dir, entry.name),
25
+ }))
26
+ .sort((a, b) => a.name.localeCompare(b.name));
27
+ }
28
+ catch {
29
+ return [];
30
+ }
31
+ };
32
+ const parseWorkflowFile = async (path) => {
33
+ try {
34
+ const content = await readFile(path, 'utf-8');
35
+ const parsed = parse(content);
36
+ if (!parsed)
37
+ return null;
38
+ const steps = (parsed.steps || parsed.nodes || []).map(
39
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
40
+ (s, index) => ({
41
+ id: s.id || `step_${index + 1}`,
42
+ provider: (s.provider || s.agent || s.agentId || 'codex'),
43
+ model: s.model,
44
+ title: s.title,
45
+ phase: s.phase || s.phaseTitle,
46
+ prompt: s.prompt || s.promptTemplate || '',
47
+ approvalMode: s.approvalMode,
48
+ status: 'pending',
49
+ }));
50
+ return {
51
+ name: parsed.name || path.split('/').pop() || 'Untitled',
52
+ description: parsed.description,
53
+ path,
54
+ steps,
55
+ dirty: false,
56
+ };
57
+ }
58
+ catch {
59
+ return null;
60
+ }
61
+ };
62
+ const buildPhasesFromSteps = (steps) => {
63
+ const phases = [];
64
+ const phaseMap = new Map();
65
+ steps.forEach((step) => {
66
+ const phaseTitle = step.phase || 'Execution';
67
+ let phase = phaseMap.get(phaseTitle);
68
+ if (!phase) {
69
+ phase = {
70
+ id: phaseTitle.toLowerCase().replace(/\s+/g, '-'),
71
+ title: phaseTitle,
72
+ steps: [],
73
+ };
74
+ phaseMap.set(phaseTitle, phase);
75
+ phases.push(phase);
76
+ }
77
+ phase.steps.push(step);
78
+ });
79
+ return phases;
80
+ };
81
+ const flattenPhases = (phases) => {
82
+ return phases.flatMap((phase) => phase.steps.map((step) => ({
83
+ ...step,
84
+ phase: phase.title,
85
+ })));
86
+ };
87
+ const wrapPromptLines = (text, width) => {
88
+ if (!text)
89
+ return [''];
90
+ const safeWidth = Math.max(10, width);
91
+ const lines = [];
92
+ text.split('\n').forEach((rawLine) => {
93
+ let line = rawLine.replace(/\t/g, ' ');
94
+ if (line.length === 0) {
95
+ lines.push('');
96
+ return;
97
+ }
98
+ while (line.length > safeWidth) {
99
+ lines.push(line.slice(0, safeWidth));
100
+ line = line.slice(safeWidth);
101
+ }
102
+ lines.push(line);
103
+ });
104
+ return lines;
105
+ };
106
+ const getPromptViewerLayout = (prompt) => {
107
+ const columns = process.stdout?.columns ?? 80;
108
+ const rows = process.stdout?.rows ?? 24;
109
+ const width = Math.max(50, Math.min(columns - 6, 100));
110
+ const height = Math.max(8, Math.min(Math.floor(rows * 0.6), 18));
111
+ const lines = wrapPromptLines(prompt, width - 4);
112
+ const maxScroll = Math.max(0, lines.length - height);
113
+ return { width, height, lines, maxScroll };
114
+ };
115
+ export const App = () => {
116
+ const { exit } = useApp();
117
+ const initialConfig = useMemo(() => loadConfig(), []);
118
+ const [theme, setTheme] = useState(() => loadTheme());
119
+ const [state, setState] = useState(() => ({
120
+ view: 'home',
121
+ workflow: null,
122
+ workflowFiles: [],
123
+ selectedFileIndex: 0,
124
+ selectedStepIndex: 0,
125
+ providers: detectProvidersSync(),
126
+ history: [],
127
+ output: [],
128
+ error: null,
129
+ focusArea: 'sidebar',
130
+ settings: {
131
+ defaultProvider: initialConfig.defaults.provider,
132
+ defaultModel: initialConfig.defaults.model,
133
+ defaultApprovalMode: initialConfig.defaults.approvalMode,
134
+ defaultRunMode: initialConfig.defaults.runMode,
135
+ },
136
+ }));
137
+ const lastEscAtRef = useRef(0);
138
+ const [planningOutput, setPlanningOutput] = useState([]);
139
+ const planningBufferRef = useRef('');
140
+ const [showPromptViewer, setShowPromptViewer] = useState(false);
141
+ const [promptScroll, setPromptScroll] = useState(0);
142
+ const [editingField, setEditingField] = useState(null);
143
+ const [isRunning, setIsRunning] = useState(false);
144
+ const [loading, setLoading] = useState(true);
145
+ const [goal, setGoal] = useState('');
146
+ const [isGenerating, setIsGenerating] = useState(false);
147
+ const [generatingStatus, setGeneratingStatus] = useState('');
148
+ const [projectContext, setProjectContext] = useState(null);
149
+ const [isPaused, setIsPaused] = useState(false);
150
+ const [showSplash, setShowSplash] = useState(true);
151
+ // Refs for synchronous access in async loops
152
+ const isRunningRef = useRef(false);
153
+ const isPausedRef = useRef(false);
154
+ const [userName, setUserName] = useState('');
155
+ const [showCommandPalette, setShowCommandPalette] = useState(false);
156
+ const [showHelp, setShowHelp] = useState(false);
157
+ const [showSettings, setShowSettings] = useState(false);
158
+ const [showThemeDesigner, setShowThemeDesigner] = useState(false);
159
+ const [chatInput, setChatInput] = useState('');
160
+ const [chatActive, setChatActive] = useState(false);
161
+ const reloadSettings = useCallback(() => {
162
+ const config = loadConfig();
163
+ setState((s) => ({
164
+ ...s,
165
+ settings: {
166
+ defaultProvider: config.defaults.provider,
167
+ defaultModel: config.defaults.model,
168
+ defaultApprovalMode: config.defaults.approvalMode,
169
+ defaultRunMode: config.defaults.runMode,
170
+ },
171
+ }));
172
+ }, []);
173
+ const reloadTheme = useCallback(() => {
174
+ setTheme(loadTheme());
175
+ }, []);
176
+ const closeSettings = useCallback(() => {
177
+ setShowSettings(false);
178
+ reloadSettings();
179
+ reloadTheme();
180
+ }, [reloadSettings, reloadTheme]);
181
+ const closeThemeDesigner = useCallback(() => {
182
+ setShowThemeDesigner(false);
183
+ reloadTheme();
184
+ }, [reloadTheme]);
185
+ // Initialize on mount
186
+ useEffect(() => {
187
+ const init = async () => {
188
+ // Get user name immediately
189
+ const name = getUserName();
190
+ setUserName(name);
191
+ setLoading(true);
192
+ const providers = detectProvidersSync();
193
+ const workflowFiles = await loadWorkflowFiles(process.cwd());
194
+ const context = await scanProjectContext(process.cwd());
195
+ setProjectContext(context);
196
+ setState((s) => ({ ...s, providers, workflowFiles }));
197
+ setLoading(false);
198
+ };
199
+ init();
200
+ }, []);
201
+ // Load workflow when selected file changes
202
+ const loadSelectedWorkflow = useCallback(async () => {
203
+ const file = state.workflowFiles[state.selectedFileIndex];
204
+ if (!file)
205
+ return;
206
+ const workflow = await parseWorkflowFile(file.path);
207
+ if (workflow) {
208
+ setState((s) => ({
209
+ ...s,
210
+ workflow,
211
+ selectedStepIndex: 0,
212
+ view: 'main',
213
+ }));
214
+ }
215
+ }, [state.workflowFiles, state.selectedFileIndex]);
216
+ // Handle keyboard input
217
+ useInput((input, key) => {
218
+ // During splash, any key skips it
219
+ if (showSplash) {
220
+ setShowSplash(false);
221
+ return;
222
+ }
223
+ // Close overlays first / handle escape flow
224
+ if (key.escape) {
225
+ const now = Date.now();
226
+ if (chatActive) {
227
+ setChatActive(false);
228
+ setChatInput('');
229
+ return;
230
+ }
231
+ if (showPromptViewer) {
232
+ setShowPromptViewer(false);
233
+ return;
234
+ }
235
+ if (showSettings) {
236
+ closeSettings();
237
+ return;
238
+ }
239
+ if (showThemeDesigner) {
240
+ closeThemeDesigner();
241
+ return;
242
+ }
243
+ if (showHelp) {
244
+ setShowHelp(false);
245
+ return;
246
+ }
247
+ if (showCommandPalette) {
248
+ setShowCommandPalette(false);
249
+ return;
250
+ }
251
+ if (editingField) {
252
+ setEditingField(null);
253
+ return;
254
+ }
255
+ if (state.view === 'main') {
256
+ setState((s) => ({ ...s, view: 'home', workflow: null }));
257
+ lastEscAtRef.current = now;
258
+ return;
259
+ }
260
+ if (state.view === 'load') {
261
+ setState((s) => ({ ...s, view: 'home' }));
262
+ lastEscAtRef.current = now;
263
+ return;
264
+ }
265
+ if (state.view === 'running' || state.view === 'output') {
266
+ setState((s) => ({ ...s, view: 'main' }));
267
+ return;
268
+ }
269
+ if (state.view === 'home') {
270
+ if (now - lastEscAtRef.current < 600) {
271
+ exit();
272
+ return;
273
+ }
274
+ lastEscAtRef.current = now;
275
+ return;
276
+ }
277
+ return;
278
+ }
279
+ // Prompt viewer input
280
+ if (showPromptViewer) {
281
+ const currentPrompt = state.workflow?.steps[state.selectedStepIndex]?.prompt ?? '';
282
+ const { maxScroll } = getPromptViewerLayout(currentPrompt);
283
+ if (key.upArrow) {
284
+ setPromptScroll((s) => Math.max(0, s - 1));
285
+ }
286
+ else if (key.downArrow) {
287
+ setPromptScroll((s) => Math.min(maxScroll, s + 1));
288
+ }
289
+ else if (input.toLowerCase() === 'e') {
290
+ if (state.workflow?.steps[state.selectedStepIndex]) {
291
+ setEditingField('prompt');
292
+ setShowPromptViewer(false);
293
+ }
294
+ }
295
+ return;
296
+ }
297
+ // Don't process other keys if overlays are open or typing in goal input
298
+ if (showHelp || showCommandPalette || showSettings || showThemeDesigner)
299
+ return;
300
+ if (chatActive)
301
+ return;
302
+ // Ctrl+C to cancel running workflow
303
+ if ((key.ctrl && input.toLowerCase() === 'c') || input === '\u0003') {
304
+ if (isRunning) {
305
+ setIsRunning(false);
306
+ isRunningRef.current = false;
307
+ setIsPaused(false);
308
+ isPausedRef.current = false;
309
+ setState((s) => ({
310
+ ...s,
311
+ output: [...s.output, '\n✗ Workflow cancelled by user.'],
312
+ }));
313
+ }
314
+ return;
315
+ }
316
+ // Global quit
317
+ if (input === 'q' && !editingField) {
318
+ exit();
319
+ return;
320
+ }
321
+ // Open command palette
322
+ if (input === '/' && !editingField) {
323
+ setShowCommandPalette(true);
324
+ setShowHelp(false);
325
+ setShowSettings(false);
326
+ setShowThemeDesigner(false);
327
+ return;
328
+ }
329
+ // Open help
330
+ if (input === '?' && !editingField) {
331
+ setShowHelp(true);
332
+ setShowCommandPalette(false);
333
+ setShowSettings(false);
334
+ setShowThemeDesigner(false);
335
+ return;
336
+ }
337
+ if (input.toLowerCase() === 'c' && !editingField && !isRunning && !isGenerating) {
338
+ setChatActive(true);
339
+ setChatInput('');
340
+ return;
341
+ }
342
+ // If editing, don't process other keys
343
+ if (editingField) {
344
+ if (key.return) {
345
+ setEditingField(null);
346
+ }
347
+ return;
348
+ }
349
+ // Load view navigation
350
+ if (state.view === 'load') {
351
+ if (key.upArrow) {
352
+ setState((s) => ({
353
+ ...s,
354
+ selectedFileIndex: Math.max(0, s.selectedFileIndex - 1),
355
+ }));
356
+ return;
357
+ }
358
+ if (key.downArrow) {
359
+ setState((s) => ({
360
+ ...s,
361
+ selectedFileIndex: Math.min(Math.max(0, s.workflowFiles.length - 1), s.selectedFileIndex + 1),
362
+ }));
363
+ return;
364
+ }
365
+ if (key.return) {
366
+ if (state.workflowFiles.length > 0) {
367
+ loadSelectedWorkflow();
368
+ }
369
+ else {
370
+ setState((s) => ({ ...s, error: 'No workflow files found.' }));
371
+ }
372
+ return;
373
+ }
374
+ }
375
+ if (key.tab &&
376
+ state.workflow &&
377
+ (state.view === 'main' || state.view === 'running' || state.view === 'output')) {
378
+ setState((s) => ({
379
+ ...s,
380
+ focusArea: s.focusArea === 'sidebar' ? 'content' : 'sidebar',
381
+ }));
382
+ return;
383
+ }
384
+ // Planner view - step navigation
385
+ if ((state.view === 'main' || state.view === 'running' || state.view === 'output') && state.workflow) {
386
+ if (state.focusArea === 'sidebar') {
387
+ if (key.upArrow) {
388
+ setState((s) => ({
389
+ ...s,
390
+ selectedStepIndex: Math.max(0, s.selectedStepIndex - 1),
391
+ }));
392
+ return;
393
+ }
394
+ if (key.downArrow) {
395
+ setState((s) => ({
396
+ ...s,
397
+ selectedStepIndex: Math.min((s.workflow?.steps.length || 1) - 1, s.selectedStepIndex + 1),
398
+ }));
399
+ return;
400
+ }
401
+ }
402
+ }
403
+ // Global shortcuts
404
+ switch (input.toLowerCase()) {
405
+ case 'a':
406
+ if (state.view === 'home') {
407
+ if (!isRunning && !isGenerating) {
408
+ setChatActive(true);
409
+ setChatInput('');
410
+ setGoal('');
411
+ }
412
+ }
413
+ else if (state.workflow?.steps[state.selectedStepIndex]) {
414
+ // Change provider
415
+ setEditingField('provider');
416
+ }
417
+ break;
418
+ case 'l':
419
+ if (state.workflowFiles.length > 0) {
420
+ setState((s) => ({ ...s, view: 'load' }));
421
+ }
422
+ else {
423
+ setState((s) => ({ ...s, error: 'No workflow files found.' }));
424
+ }
425
+ break;
426
+ case 'm':
427
+ if (state.view === 'home' || state.view === 'load') {
428
+ if (!isRunning && !isGenerating) {
429
+ setChatActive(false);
430
+ setChatInput('');
431
+ setGoal('');
432
+ createNewWorkflow(undefined, '');
433
+ }
434
+ }
435
+ else if (state.workflow?.steps[state.selectedStepIndex]) {
436
+ // Change model
437
+ setEditingField('model');
438
+ }
439
+ break;
440
+ case 'e':
441
+ // Edit prompt
442
+ if (state.workflow?.steps[state.selectedStepIndex]) {
443
+ setEditingField('prompt');
444
+ setShowPromptViewer(false);
445
+ }
446
+ break;
447
+ case '-':
448
+ // Delete step
449
+ deleteStep();
450
+ break;
451
+ case 's':
452
+ if ((state.view === 'home' || state.view === 'load') && !editingField) {
453
+ setShowSettings(true);
454
+ setShowCommandPalette(false);
455
+ setShowHelp(false);
456
+ setShowThemeDesigner(false);
457
+ }
458
+ else if (key.ctrl || !editingField) {
459
+ // Save
460
+ saveWorkflow();
461
+ }
462
+ break;
463
+ case 'r':
464
+ // Run or Resume
465
+ if (isRunning && isPaused) {
466
+ setIsPaused(false);
467
+ isPausedRef.current = false;
468
+ }
469
+ else if (!isRunning) {
470
+ runWorkflow();
471
+ }
472
+ break;
473
+ case 'p':
474
+ if (isRunning) {
475
+ if (isPaused) {
476
+ setIsPaused(false);
477
+ isPausedRef.current = false;
478
+ }
479
+ else {
480
+ setIsPaused(true);
481
+ isPausedRef.current = true;
482
+ }
483
+ }
484
+ else if (!isGenerating && state.view !== 'home') {
485
+ setChatActive(true);
486
+ setChatInput('');
487
+ }
488
+ break;
489
+ case 'v':
490
+ if (state.focusArea === 'content' &&
491
+ state.workflow?.steps[state.selectedStepIndex]) {
492
+ setPromptScroll(0);
493
+ setShowPromptViewer(true);
494
+ }
495
+ break;
496
+ case '+':
497
+ // Add step (alternative to separate action)
498
+ addStep();
499
+ break;
500
+ }
501
+ if (input === ' ') {
502
+ if (isRunning) {
503
+ if (isPaused) {
504
+ setIsPaused(false);
505
+ isPausedRef.current = false;
506
+ }
507
+ else {
508
+ setIsPaused(true);
509
+ isPausedRef.current = true;
510
+ }
511
+ }
512
+ }
513
+ });
514
+ const createNewWorkflow = (stepsOrPhases, description) => {
515
+ const defaultProvider = state.settings.defaultProvider;
516
+ const steps = Array.isArray(stepsOrPhases)
517
+ ? (stepsOrPhases.length > 0 && 'steps' in stepsOrPhases[0]
518
+ ? flattenPhases(stepsOrPhases)
519
+ : stepsOrPhases)
520
+ : undefined;
521
+ const newWorkflow = {
522
+ name: 'New Workflow',
523
+ description: description ?? goal ?? '',
524
+ steps: steps || [
525
+ {
526
+ id: 'step_1',
527
+ provider: defaultProvider,
528
+ prompt: '',
529
+ phase: 'Phase 1',
530
+ status: 'pending',
531
+ },
532
+ ],
533
+ dirty: true,
534
+ };
535
+ setState((s) => ({
536
+ ...s,
537
+ workflow: newWorkflow,
538
+ selectedStepIndex: 0,
539
+ focusArea: 'sidebar',
540
+ view: 'main',
541
+ }));
542
+ };
543
+ const generateWorkflow = async (goalText) => {
544
+ const targetGoal = goalText ?? goal;
545
+ if (!targetGoal.trim() || isGenerating || !projectContext)
546
+ return;
547
+ setIsGenerating(true);
548
+ setGeneratingStatus('Scanning project...');
549
+ setPlanningOutput([]);
550
+ planningBufferRef.current = '';
551
+ try {
552
+ // Re-scan context to get latest state
553
+ const context = await scanProjectContext(process.cwd());
554
+ setProjectContext(context);
555
+ // Use the user's default provider for planning
556
+ const defaultProviderName = state.settings.defaultProvider;
557
+ const planningProvider = state.providers.find((p) => p.name === defaultProviderName && p.available && p.supportsAgentic)
558
+ || state.providers.find((p) => p.available && p.supportsAgentic);
559
+ const provider = planningProvider?.name || 'codex';
560
+ const phases = await generatePlanFromGoal(targetGoal, context, provider, setGeneratingStatus, (chunk) => {
561
+ const text = chunk.replace(/\r/g, '');
562
+ const buffer = planningBufferRef.current + text;
563
+ const lines = buffer.split('\n');
564
+ planningBufferRef.current = lines.pop() ?? '';
565
+ const cleaned = lines
566
+ .map((line) => line.trimEnd())
567
+ .filter((line) => line.length > 0);
568
+ if (cleaned.length === 0)
569
+ return;
570
+ setPlanningOutput((prev) => {
571
+ const next = [...prev, ...cleaned];
572
+ return next.slice(-40);
573
+ });
574
+ });
575
+ setGeneratingStatus('Creating workflow...');
576
+ createNewWorkflow(phases, targetGoal);
577
+ setGoal('');
578
+ setChatInput('');
579
+ }
580
+ catch (error) {
581
+ setState((s) => ({
582
+ ...s,
583
+ error: `Generation failed: ${error}`,
584
+ }));
585
+ // Fall back to empty workflow
586
+ createNewWorkflow(undefined, targetGoal);
587
+ }
588
+ finally {
589
+ setIsGenerating(false);
590
+ setGeneratingStatus('');
591
+ }
592
+ };
593
+ const handleChatSubmit = async () => {
594
+ const text = chatInput.trim();
595
+ if (!text) {
596
+ setChatActive(false);
597
+ setChatInput('');
598
+ return;
599
+ }
600
+ if (text.startsWith('/')) {
601
+ const query = text.slice(1).trim().toLowerCase();
602
+ const cmd = COMMANDS.find((command) => command.name.toLowerCase() === query ||
603
+ command.aliases.some((alias) => alias.toLowerCase() === query));
604
+ if (cmd) {
605
+ handleCommand(cmd);
606
+ }
607
+ else {
608
+ setState((s) => ({ ...s, error: `Unknown command: /${query}` }));
609
+ }
610
+ setChatActive(false);
611
+ setChatInput('');
612
+ return;
613
+ }
614
+ setChatActive(false);
615
+ setGoal(text);
616
+ await generateWorkflow(text);
617
+ };
618
+ const addStep = () => {
619
+ if (!state.workflow)
620
+ return;
621
+ const defaultProvider = state.settings.defaultProvider;
622
+ const currentPhase = state.workflow.steps[state.selectedStepIndex]?.phase || 'Phase 1';
623
+ const newStep = {
624
+ id: `step_${state.workflow.steps.length + 1}`,
625
+ provider: defaultProvider,
626
+ prompt: '',
627
+ phase: currentPhase,
628
+ status: 'pending',
629
+ };
630
+ setState((s) => ({
631
+ ...s,
632
+ workflow: s.workflow
633
+ ? {
634
+ ...s.workflow,
635
+ steps: [...s.workflow.steps, newStep],
636
+ dirty: true,
637
+ }
638
+ : null,
639
+ selectedStepIndex: s.workflow ? s.workflow.steps.length : 0,
640
+ }));
641
+ };
642
+ const deleteStep = () => {
643
+ if (!state.workflow || state.workflow.steps.length <= 1)
644
+ return;
645
+ setState((s) => {
646
+ if (!s.workflow)
647
+ return s;
648
+ const newSteps = s.workflow.steps.filter((_, i) => i !== s.selectedStepIndex);
649
+ return {
650
+ ...s,
651
+ workflow: {
652
+ ...s.workflow,
653
+ steps: newSteps,
654
+ dirty: true,
655
+ },
656
+ selectedStepIndex: Math.min(s.selectedStepIndex, newSteps.length - 1),
657
+ };
658
+ });
659
+ };
660
+ const updateStepPrompt = (value) => {
661
+ setState((s) => {
662
+ if (!s.workflow)
663
+ return s;
664
+ const newSteps = [...s.workflow.steps];
665
+ newSteps[s.selectedStepIndex] = {
666
+ ...newSteps[s.selectedStepIndex],
667
+ prompt: value,
668
+ };
669
+ return {
670
+ ...s,
671
+ workflow: {
672
+ ...s.workflow,
673
+ steps: newSteps,
674
+ dirty: true,
675
+ },
676
+ };
677
+ });
678
+ };
679
+ const updateStepProvider = (provider) => {
680
+ setState((s) => {
681
+ if (!s.workflow)
682
+ return s;
683
+ const newSteps = [...s.workflow.steps];
684
+ const providerConfig = s.providers.find((p) => p.name === provider);
685
+ newSteps[s.selectedStepIndex] = {
686
+ ...newSteps[s.selectedStepIndex],
687
+ provider,
688
+ model: providerConfig?.defaultModel, // Reset to provider's default model
689
+ };
690
+ return {
691
+ ...s,
692
+ workflow: {
693
+ ...s.workflow,
694
+ steps: newSteps,
695
+ dirty: true,
696
+ },
697
+ };
698
+ });
699
+ setEditingField(null);
700
+ };
701
+ const updateStepModel = (model) => {
702
+ setState((s) => {
703
+ if (!s.workflow)
704
+ return s;
705
+ const newSteps = [...s.workflow.steps];
706
+ newSteps[s.selectedStepIndex] = {
707
+ ...newSteps[s.selectedStepIndex],
708
+ model,
709
+ };
710
+ return {
711
+ ...s,
712
+ workflow: {
713
+ ...s.workflow,
714
+ steps: newSteps,
715
+ dirty: true,
716
+ },
717
+ };
718
+ });
719
+ setEditingField(null);
720
+ };
721
+ const saveWorkflow = async () => {
722
+ if (!state.workflow)
723
+ return;
724
+ const yamlContent = stringify({
725
+ version: '1',
726
+ name: state.workflow.name,
727
+ description: state.workflow.description || '',
728
+ steps: state.workflow.steps.map((s) => ({
729
+ id: s.id,
730
+ provider: s.provider,
731
+ ...(s.model ? { model: s.model } : {}),
732
+ ...(s.title ? { title: s.title } : {}),
733
+ ...(s.phase ? { phase: s.phase } : {}),
734
+ prompt: s.prompt,
735
+ ...(s.approvalMode ? { approvalMode: s.approvalMode } : {}),
736
+ })),
737
+ });
738
+ const path = state.workflow.path || join(process.cwd(), 'workflow.yaml');
739
+ try {
740
+ await writeFile(path, yamlContent, 'utf-8');
741
+ setState((s) => ({
742
+ ...s,
743
+ workflow: s.workflow
744
+ ? { ...s.workflow, path, dirty: false }
745
+ : null,
746
+ }));
747
+ }
748
+ catch (error) {
749
+ setState((s) => ({
750
+ ...s,
751
+ error: `Failed to save: ${error}`,
752
+ }));
753
+ }
754
+ };
755
+ const runWorkflow = async () => {
756
+ if (!state.workflow || isRunning)
757
+ return;
758
+ const workingDir = projectContext?.workingDir || process.cwd();
759
+ const defaultApprovalMode = state.settings.defaultApprovalMode;
760
+ const defaultRunMode = state.settings.defaultRunMode;
761
+ const manualMode = defaultRunMode === 'manual';
762
+ const phases = buildPhasesFromSteps(state.workflow.steps);
763
+ const phaseByStepId = new Map();
764
+ phases.forEach((phase) => {
765
+ const lastStep = phase.steps[phase.steps.length - 1];
766
+ phase.steps.forEach((step) => {
767
+ phaseByStepId.set(step.id, {
768
+ title: phase.title,
769
+ lastStepId: lastStep?.id || step.id,
770
+ });
771
+ });
772
+ });
773
+ // Save first if dirty
774
+ if (state.workflow.dirty) {
775
+ await saveWorkflow();
776
+ }
777
+ setIsRunning(true);
778
+ isRunningRef.current = true;
779
+ setIsPaused(false);
780
+ isPausedRef.current = false;
781
+ setState((s) => ({
782
+ ...s,
783
+ view: 'running',
784
+ output: [],
785
+ workflow: s.workflow
786
+ ? {
787
+ ...s.workflow,
788
+ steps: s.workflow.steps.map((step) => ({
789
+ ...step,
790
+ status: 'pending',
791
+ output: undefined,
792
+ error: undefined,
793
+ commitHash: undefined,
794
+ })),
795
+ }
796
+ : null,
797
+ }));
798
+ // Add initial context info
799
+ setState((s) => ({
800
+ ...s,
801
+ output: [
802
+ `Working directory: ${workingDir}`,
803
+ `Project type: ${projectContext?.projectType || 'Unknown'}`,
804
+ `Git branch: ${projectContext?.gitBranch || 'N/A'}`,
805
+ `Approval mode: ${defaultApprovalMode}`,
806
+ `Run mode: ${defaultRunMode}`,
807
+ '─'.repeat(50),
808
+ '',
809
+ ],
810
+ }));
811
+ const workflow = state.workflow;
812
+ if (manualMode) {
813
+ setIsPaused(true);
814
+ isPausedRef.current = true;
815
+ setState((s) => ({
816
+ ...s,
817
+ output: [...s.output, 'Manual mode enabled. Press [R] to run the next step.'],
818
+ }));
819
+ }
820
+ for (let i = 0; i < workflow.steps.length; i++) {
821
+ // Check if cancelled
822
+ if (!isRunningRef.current)
823
+ break;
824
+ // Check if paused
825
+ if (isPausedRef.current) {
826
+ setState((s) => ({
827
+ ...s,
828
+ output: [...s.output, '\n⏸ Workflow paused. Press [R] to resume.'],
829
+ }));
830
+ // Wait for resume
831
+ while (isPausedRef.current && isRunningRef.current) {
832
+ await new Promise((resolve) => setTimeout(resolve, 100));
833
+ }
834
+ if (!isRunningRef.current)
835
+ break;
836
+ }
837
+ const step = workflow.steps[i];
838
+ const phaseMeta = phaseByStepId.get(step.id);
839
+ // Mark step as running
840
+ setState((s) => {
841
+ if (!s.workflow)
842
+ return s;
843
+ const newSteps = [...s.workflow.steps];
844
+ newSteps[i] = { ...newSteps[i], status: 'running' };
845
+ return {
846
+ ...s,
847
+ workflow: { ...s.workflow, steps: newSteps },
848
+ };
849
+ });
850
+ // Execute the step with real agent
851
+ const approvalMode = step.approvalMode || defaultApprovalMode;
852
+ const result = await executeStep(step, workingDir, approvalMode, projectContext, (output) => {
853
+ setState((s) => ({
854
+ ...s,
855
+ output: [...s.output, output],
856
+ }));
857
+ });
858
+ if (result.success) {
859
+ setState((s) => {
860
+ if (!s.workflow)
861
+ return s;
862
+ const newSteps = [...s.workflow.steps];
863
+ newSteps[i] = {
864
+ ...newSteps[i],
865
+ status: 'success',
866
+ output: result.output,
867
+ duration: result.duration,
868
+ commitHash: result.commitHash,
869
+ };
870
+ return {
871
+ ...s,
872
+ workflow: { ...s.workflow, steps: newSteps },
873
+ };
874
+ });
875
+ if (phaseMeta && phaseMeta.lastStepId === step.id) {
876
+ setState((s) => ({
877
+ ...s,
878
+ output: [...s.output, `✓ Phase complete: ${phaseMeta.title}`],
879
+ }));
880
+ }
881
+ if (manualMode && i < workflow.steps.length - 1) {
882
+ setIsPaused(true);
883
+ isPausedRef.current = true;
884
+ setState((s) => ({
885
+ ...s,
886
+ output: [...s.output, 'Manual mode: press [R] to run the next step.'],
887
+ }));
888
+ }
889
+ }
890
+ else {
891
+ setState((s) => {
892
+ if (!s.workflow)
893
+ return s;
894
+ const newSteps = [...s.workflow.steps];
895
+ newSteps[i] = {
896
+ ...newSteps[i],
897
+ status: 'failed',
898
+ error: result.error,
899
+ duration: result.duration,
900
+ };
901
+ return {
902
+ ...s,
903
+ workflow: { ...s.workflow, steps: newSteps },
904
+ };
905
+ });
906
+ if (phaseMeta) {
907
+ setState((s) => ({
908
+ ...s,
909
+ output: [...s.output, `✗ Phase failed: ${phaseMeta.title}`],
910
+ }));
911
+ }
912
+ // Stop on failure
913
+ break;
914
+ }
915
+ }
916
+ setIsRunning(false);
917
+ isRunningRef.current = false;
918
+ setState((s) => ({
919
+ ...s,
920
+ output: [...s.output, '', '═'.repeat(50), 'Workflow execution complete.'],
921
+ }));
922
+ };
923
+ // Handle command from command palette
924
+ const handleCommand = (command) => {
925
+ switch (command.name) {
926
+ case 'new':
927
+ if (!isRunning && !isGenerating) {
928
+ setChatActive(true);
929
+ setChatInput('');
930
+ setGoal('');
931
+ }
932
+ break;
933
+ case 'manual':
934
+ if (!isRunning && !isGenerating) {
935
+ setChatActive(false);
936
+ setChatInput('');
937
+ setGoal('');
938
+ createNewWorkflow(undefined, '');
939
+ }
940
+ break;
941
+ case 'open':
942
+ setState((s) => ({ ...s, view: 'load' }));
943
+ break;
944
+ case 'save':
945
+ saveWorkflow();
946
+ break;
947
+ case 'run':
948
+ runWorkflow();
949
+ break;
950
+ case 'plan':
951
+ if (!isRunning && !isGenerating) {
952
+ setChatActive(true);
953
+ setChatInput('');
954
+ }
955
+ break;
956
+ case 'add':
957
+ addStep();
958
+ break;
959
+ case 'edit':
960
+ if (state.workflow?.steps[state.selectedStepIndex]) {
961
+ setEditingField('prompt');
962
+ }
963
+ break;
964
+ case 'delete':
965
+ deleteStep();
966
+ break;
967
+ case 'settings':
968
+ setShowSettings(true);
969
+ setShowCommandPalette(false);
970
+ setShowHelp(false);
971
+ setShowThemeDesigner(false);
972
+ break;
973
+ case 'theme':
974
+ setShowThemeDesigner(true);
975
+ setShowCommandPalette(false);
976
+ setShowHelp(false);
977
+ setShowSettings(false);
978
+ break;
979
+ case 'help':
980
+ setShowHelp(true);
981
+ setShowCommandPalette(false);
982
+ setShowSettings(false);
983
+ setShowThemeDesigner(false);
984
+ break;
985
+ case 'quit':
986
+ exit();
987
+ break;
988
+ case 'refresh':
989
+ loadWorkflowFiles(process.cwd()).then((files) => {
990
+ setState((s) => ({ ...s, workflowFiles: files }));
991
+ });
992
+ break;
993
+ case 'clear':
994
+ setState((s) => ({ ...s, output: [] }));
995
+ break;
996
+ }
997
+ };
998
+ const showOverlay = showCommandPalette || showHelp || showSettings || showThemeDesigner || showPromptViewer;
999
+ const promptView = useMemo(() => {
1000
+ if (!showPromptViewer || !state.workflow)
1001
+ return null;
1002
+ const step = state.workflow.steps[state.selectedStepIndex];
1003
+ if (!step)
1004
+ return null;
1005
+ const layout = getPromptViewerLayout(step.prompt || '');
1006
+ const scroll = Math.min(promptScroll, layout.maxScroll);
1007
+ const visibleLines = layout.lines.slice(scroll, scroll + layout.height);
1008
+ return {
1009
+ title: step.title || step.id,
1010
+ prompt: step.prompt || '',
1011
+ scroll,
1012
+ visibleLines,
1013
+ ...layout,
1014
+ };
1015
+ }, [showPromptViewer, state.workflow, state.selectedStepIndex, promptScroll]);
1016
+ useEffect(() => {
1017
+ if (promptView && promptScroll > promptView.maxScroll) {
1018
+ setPromptScroll(promptView.maxScroll);
1019
+ }
1020
+ }, [promptScroll, promptView]);
1021
+ // Build commands with actions
1022
+ const commands = COMMANDS.map((cmd) => ({
1023
+ ...cmd,
1024
+ action: () => handleCommand(cmd),
1025
+ }));
1026
+ // Show splash screen
1027
+ if (showSplash) {
1028
+ return (_jsx(Splash, { userName: userName, onComplete: () => setShowSplash(false) }));
1029
+ }
1030
+ const renderChatContent = () => {
1031
+ if (isGenerating) {
1032
+ return (_jsxs(Box, { flexDirection: "column", padding: 1, gap: 1, flexGrow: 1, children: [_jsx(Text, { color: theme.colors.accent, bold: true, children: "Planning..." }), generatingStatus ? (_jsx(Text, { color: theme.colors.muted, children: generatingStatus })) : (_jsx(Text, { color: theme.colors.muted, children: "Preparing workflow plan." })), _jsx(Box, { flexDirection: "column", borderStyle: "round", borderColor: theme.colors.panel.border, paddingX: 1, paddingY: 0, flexGrow: 1, children: planningOutput.length === 0 ? (_jsx(Text, { color: theme.colors.muted, dimColor: true, children: "Waiting for model output..." })) : (planningOutput.slice(-12).map((line, index) => (_jsx(Text, { color: theme.colors.text, wrap: "wrap", children: line }, `${index}-${line.slice(0, 12)}`)))) })] }));
1033
+ }
1034
+ if (state.view === 'load') {
1035
+ return (_jsx(WorkflowPicker, { workflows: state.workflowFiles, selectedIndex: state.selectedFileIndex }));
1036
+ }
1037
+ if (state.view === 'home') {
1038
+ return _jsx(Box, { flexGrow: 1 });
1039
+ }
1040
+ if (state.workflow) {
1041
+ const phases = buildPhasesFromSteps(state.workflow.steps);
1042
+ return (_jsx(PlannerView, { phases: phases, selectedStepIndex: state.selectedStepIndex, detailsFocused: state.focusArea === 'content', providers: state.providers, output: state.output, isRunning: isRunning, editingField: editingField, onPromptChange: updateStepPrompt, onProviderSelect: updateStepProvider, onModelSelect: updateStepModel, onEditingChange: setEditingField }));
1043
+ }
1044
+ return (_jsx(Box, { padding: 1, children: _jsx(Text, { color: theme.colors.muted, children: "No workflow loaded." }) }));
1045
+ };
1046
+ const quickActions = state.view === 'home'
1047
+ ? [
1048
+ { key: 'A', label: 'Auto plan' },
1049
+ { key: 'M', label: 'Manual plan' },
1050
+ { key: 'L', label: 'Load workflow' },
1051
+ { key: 'S', label: 'Settings' },
1052
+ ]
1053
+ : state.view === 'load'
1054
+ ? [
1055
+ { key: 'P', label: 'Plan' },
1056
+ { key: 'M', label: 'Manual plan' },
1057
+ { key: 'L', label: 'Load workflow' },
1058
+ { key: 'S', label: 'Settings' },
1059
+ ]
1060
+ : [
1061
+ { key: 'A', label: 'Provider' },
1062
+ { key: 'M', label: 'Model' },
1063
+ { key: 'P', label: isRunning ? 'Pause' : 'Plan' },
1064
+ { key: 'S', label: 'Save' },
1065
+ { key: 'L', label: 'Load' },
1066
+ { key: 'R', label: isRunning ? (isPaused ? 'Resume' : 'Running') : 'Run' },
1067
+ { key: 'C', label: 'Input' },
1068
+ ];
1069
+ return (_jsx(ThemeProvider, { value: theme, children: _jsx(Frame, { children: _jsxs(Box, { flexDirection: "column", width: "100%", height: "100%", children: [_jsx(Header, { userName: userName, version: "0.1", gitBranch: projectContext?.gitBranch }), _jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [!showOverlay && (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: theme.colors.panel.borderFocus, paddingX: 1, paddingY: 1, flexGrow: 1, children: [state.workflow?.name && (_jsx(Box, { marginBottom: 1, children: _jsx(Text, { color: theme.colors.accent, bold: true, children: state.workflow.name }) })), _jsx(Box, { flexGrow: 1, children: renderChatContent() }), _jsx(Box, { marginTop: 1, children: _jsx(ChatInput, { value: chatInput, active: chatActive, disabled: isRunning || isGenerating, disabledMessage: isGenerating
1070
+ ? generatingStatus
1071
+ ? `Planning: ${generatingStatus}`
1072
+ : 'Planning...'
1073
+ : 'Input disabled while running.', placeholder: "Describe a goal or type /command", onChange: setChatInput, onSubmit: handleChatSubmit }) })] })), showPromptViewer && promptView && (_jsx(Box, { flexGrow: 1, alignItems: "center", justifyContent: "center", children: _jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: theme.colors.panel.borderFocus, paddingX: 2, paddingY: 1, width: promptView.width, children: [_jsxs(Box, { justifyContent: "space-between", marginBottom: 1, children: [_jsx(Text, { color: theme.colors.accent, bold: true, children: "Prompt Viewer" }), _jsx(Text, { color: theme.colors.muted, children: "[Esc] Close" })] }), _jsx(Text, { color: theme.colors.muted, dimColor: true, children: promptView.title }), _jsx(Box, { flexDirection: "column", borderStyle: "round", borderColor: theme.colors.panel.border, paddingX: 1, paddingY: 0, marginTop: 1, height: promptView.height, children: promptView.visibleLines.length === 0 ? (_jsx(Text, { color: theme.colors.muted, dimColor: true, children: "No prompt text." })) : (promptView.visibleLines.map((line, index) => (_jsx(Text, { color: theme.colors.text, children: line }, `${promptView.scroll + index}-${line.slice(0, 8)}`)))) }), _jsxs(Box, { marginTop: 1, justifyContent: "space-between", children: [_jsx(Text, { color: theme.colors.muted, dimColor: true, children: "\u2191\u2193 Scroll \u2022 E Edit" }), promptView.maxScroll > 0 && (_jsxs(Text, { color: theme.colors.muted, dimColor: true, children: [promptView.scroll + 1, "/", promptView.maxScroll + 1] }))] })] }) })), showCommandPalette && (_jsx(Box, { flexGrow: 1, alignItems: "center", justifyContent: "center", children: _jsx(CommandInput, { commands: commands, onClose: () => setShowCommandPalette(false), onExecute: (cmd) => {
1074
+ handleCommand(cmd);
1075
+ setShowCommandPalette(false);
1076
+ } }) })), showHelp && (_jsx(Box, { flexGrow: 1, alignItems: "center", justifyContent: "center", children: _jsx(Help, { onClose: () => setShowHelp(false) }) })), showSettings && (_jsx(Box, { flexGrow: 1, alignItems: "center", justifyContent: "center", children: _jsx(SettingsPanel, { onClose: closeSettings }) })), showThemeDesigner && (_jsx(Box, { flexGrow: 1, alignItems: "center", justifyContent: "center", children: _jsx(ThemeDesigner, { provider: state.settings.defaultProvider, onClose: closeThemeDesigner, onSaved: reloadTheme }) }))] }), !showOverlay && (_jsx(Box, { flexDirection: "row", gap: 3, paddingX: 1, paddingY: 1, children: quickActions.map((action) => (_jsxs(Text, { color: theme.colors.text, children: [_jsxs(Text, { color: theme.colors.warning, children: ["[", action.key, "]"] }), " ", action.label] }, action.key))) })), !showOverlay && (_jsxs(Box, { marginTop: 1, flexDirection: "row", justifyContent: "space-between", children: [state.view === 'home' ? (_jsx(Text, { color: theme.colors.muted, dimColor: true, children: projectContext?.workingDir || process.cwd() })) : (_jsx(Text, { children: " " })), _jsx(Text, { color: theme.colors.muted, dimColor: true, children: "[/] Commands [?] Help [Q] Quit" })] })), state.error && (_jsx(Box, { borderStyle: "round", borderColor: theme.colors.danger, paddingX: 1, marginTop: 1, children: _jsx(Text, { color: theme.colors.danger, children: state.error }) }))] }) }) }));
1077
+ };
1078
+ //# sourceMappingURL=App.js.map