@polterware/polter 0.0.0 → 0.1.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/dist/index.js CHANGED
@@ -1,36 +1,102 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ allCommands,
4
+ applyActions,
5
+ commandExists,
6
+ executePipeline,
7
+ features,
8
+ findCommandByValue,
9
+ findNearestPackageRoot,
10
+ findPipelineByName,
11
+ getAllPipelines,
12
+ getCommandById,
13
+ getCommandValue,
14
+ getCurrentStatus,
15
+ getFeatureById,
16
+ getFlagsForTool,
17
+ getOrCreateProjectConfig,
18
+ getProjectConfigPath,
19
+ getToolInfo,
20
+ parsePolterYaml,
21
+ planChanges,
22
+ resolveToolCommand,
23
+ runCommand,
24
+ runSupabaseCommand,
25
+ savePipeline,
26
+ writeProjectConfig
27
+ } from "./chunk-TWWRDI3Q.js";
2
28
 
3
29
  // src/index.tsx
4
- import React8 from "react";
30
+ import React19 from "react";
5
31
  import { render } from "ink";
6
32
 
7
33
  // src/app.tsx
8
- import { Box as Box14, Text as Text13, useApp } from "ink";
34
+ import { Box as Box21, Text as Text23, useApp } from "ink";
9
35
 
10
36
  // src/hooks/useNavigation.ts
11
37
  import { useState, useCallback } from "react";
12
38
  function useNavigation() {
13
- const [state, setState] = useState({
14
- screen: "main-menu",
15
- params: {}
16
- });
39
+ const [stack, setStack] = useState([
40
+ { screen: "home", params: {} }
41
+ ]);
42
+ const current = stack[stack.length - 1];
17
43
  const navigate = useCallback((screen, params) => {
18
- setState({
19
- screen,
20
- params: params ?? {}
21
- });
44
+ setStack((prev) => [...prev, { screen, params: params ?? {} }]);
22
45
  }, []);
23
46
  const goBack = useCallback(() => {
24
- setState({ screen: "main-menu", params: {} });
47
+ setStack((prev) => {
48
+ if (prev.length <= 1) return [{ screen: "home", params: {} }];
49
+ return prev.slice(0, -1);
50
+ });
51
+ }, []);
52
+ const goHome = useCallback(() => {
53
+ setStack([{ screen: "home", params: {} }]);
25
54
  }, []);
26
- return { screen: state.screen, params: state.params, navigate, goBack };
55
+ return {
56
+ screen: current.screen,
57
+ params: current.params,
58
+ navigate,
59
+ goBack,
60
+ goHome
61
+ };
27
62
  }
28
63
 
29
- // src/screens/MainMenu.tsx
30
- import { useEffect as useEffect2, useMemo as useMemo2, useState as useState3 } from "react";
31
- import { Box as Box4, Text as Text4 } from "ink";
64
+ // src/hooks/useTerminalDimensions.ts
65
+ import { useState as useState2, useEffect } from "react";
66
+ import { useStdout } from "ink";
67
+ function useTerminalDimensions() {
68
+ const { stdout } = useStdout();
69
+ const [dimensions, setDimensions] = useState2(() => ({
70
+ width: stdout?.columns ?? process.stdout.columns ?? 80,
71
+ height: stdout?.rows ?? process.stdout.rows ?? 24
72
+ }));
73
+ useEffect(() => {
74
+ const onResize = () => {
75
+ setDimensions({
76
+ width: stdout?.columns ?? process.stdout.columns ?? 80,
77
+ height: stdout?.rows ?? process.stdout.rows ?? 24
78
+ });
79
+ };
80
+ process.stdout.on("resize", onResize);
81
+ return () => {
82
+ process.stdout.off("resize", onResize);
83
+ };
84
+ }, [stdout]);
85
+ return dimensions;
86
+ }
87
+
88
+ // src/hooks/useTerminalWidth.ts
89
+ function useTerminalWidth() {
90
+ return useTerminalDimensions().width;
91
+ }
92
+
93
+ // src/hooks/useTerminalHeight.ts
94
+ function useTerminalHeight() {
95
+ return useTerminalDimensions().height;
96
+ }
32
97
 
33
98
  // src/components/GhostBanner.tsx
99
+ import React from "react";
34
100
  import { Box, Text } from "ink";
35
101
 
36
102
  // src/theme.ts
@@ -67,6 +133,33 @@ var colors = {
67
133
  white: pc.white,
68
134
  highlight: (s) => supabaseBg(pc.black(pc.bold(s)))
69
135
  };
136
+ var symbols = {
137
+ pointer: "\u203A",
138
+ pointerActive: "\u276F",
139
+ check: "\u2713",
140
+ cross: "\u2717",
141
+ bullet: "\u25CF",
142
+ bulletEmpty: "\u25CB",
143
+ pin: "\u{1F4CC}",
144
+ ghost: "\u{1F47B}",
145
+ exit: "\u{1F6AA}",
146
+ edit: "\u270F\uFE0F",
147
+ gear: "\u2699\uFE0F",
148
+ back: "\u2190",
149
+ arrowRight: "\u2192",
150
+ separator: "\u2500",
151
+ cornerTL: "\u256D",
152
+ cornerTR: "\u256E",
153
+ cornerBL: "\u2570",
154
+ cornerBR: "\u256F",
155
+ vertical: "\u2502",
156
+ horizontal: "\u2500"
157
+ };
158
+ var panel = {
159
+ borderFocused: SUPABASE_HEX,
160
+ borderDim: "#555555",
161
+ sidebarWidth: (termWidth) => Math.max(20, Math.min(35, Math.floor(termWidth * 0.3)))
162
+ };
70
163
  var ghost = {
71
164
  art: [
72
165
  " \u2584\u2584\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2584\u2584",
@@ -82,7 +175,84 @@ var ghost = {
82
175
 
83
176
  // src/components/GhostBanner.tsx
84
177
  import { jsx, jsxs } from "react/jsx-runtime";
85
- function GhostBanner() {
178
+ var ToolStatusBadges = React.memo(function ToolStatusBadges2() {
179
+ const tools = ["supabase", "gh", "vercel"].map(getToolInfo);
180
+ return /* @__PURE__ */ jsx(Box, { gap: 1, children: tools.map((t) => /* @__PURE__ */ jsxs(
181
+ Text,
182
+ {
183
+ color: t.installed ? inkColors.accent : "red",
184
+ dimColor: !t.installed,
185
+ children: [
186
+ t.id,
187
+ ":",
188
+ t.installed ? "ok" : "x"
189
+ ]
190
+ },
191
+ t.id
192
+ )) });
193
+ });
194
+ function GhostBanner({ width = 80, compact = false }) {
195
+ if (compact) {
196
+ if (width < 60) {
197
+ return /* @__PURE__ */ jsxs(Box, { children: [
198
+ /* @__PURE__ */ jsx(Text, { color: inkColors.accent, bold: true, children: "POLTER" }),
199
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
200
+ " v",
201
+ VERSION,
202
+ " "
203
+ ] }),
204
+ /* @__PURE__ */ jsx(ToolStatusBadges, {})
205
+ ] });
206
+ }
207
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 2, children: [
208
+ /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: ghost.art.map((line, i) => /* @__PURE__ */ jsx(Text, { color: inkColors.accent, children: line }, i)) }),
209
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "column", justifyContent: "center", children: [
210
+ /* @__PURE__ */ jsx(Text, { color: inkColors.accent, bold: true, children: "POLTER" }),
211
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
212
+ "v",
213
+ VERSION
214
+ ] }),
215
+ /* @__PURE__ */ jsx(ToolStatusBadges, {})
216
+ ] })
217
+ ] });
218
+ }
219
+ if (width < 50) {
220
+ return /* @__PURE__ */ jsxs(Box, { marginBottom: 1, children: [
221
+ /* @__PURE__ */ jsx(Text, { color: inkColors.accent, bold: true, children: "POLTER" }),
222
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
223
+ " v",
224
+ VERSION
225
+ ] })
226
+ ] });
227
+ }
228
+ if (width < 80) {
229
+ return /* @__PURE__ */ jsxs(
230
+ Box,
231
+ {
232
+ flexDirection: "column",
233
+ borderStyle: "single",
234
+ borderColor: inkColors.accent,
235
+ paddingX: 1,
236
+ marginBottom: 1,
237
+ children: [
238
+ /* @__PURE__ */ jsx(
239
+ Text,
240
+ {
241
+ backgroundColor: inkColors.accent,
242
+ color: inkColors.accentContrast,
243
+ bold: true,
244
+ children: " POLTER "
245
+ }
246
+ ),
247
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
248
+ "Version: ",
249
+ VERSION
250
+ ] }),
251
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "Project & infrastructure orchestrator" })
252
+ ]
253
+ }
254
+ );
255
+ }
86
256
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "row", alignItems: "flex-start", gap: 2, marginBottom: 1, children: [
87
257
  /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: ghost.art.map((line, i) => /* @__PURE__ */ jsx(Text, { color: inkColors.accent, children: line }, i)) }),
88
258
  /* @__PURE__ */ jsxs(
@@ -106,16 +276,45 @@ function GhostBanner() {
106
276
  "Version: ",
107
277
  VERSION
108
278
  ] }),
109
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "The modern interactive CLI for Supabase" })
279
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "Project & infrastructure orchestrator" })
110
280
  ]
111
281
  }
112
282
  )
113
283
  ] });
114
284
  }
115
285
 
286
+ // src/screens/Home.tsx
287
+ import { useEffect as useEffect3, useMemo as useMemo2, useState as useState4 } from "react";
288
+ import { Box as Box5, Text as Text5, useInput as useInput2 } from "ink";
289
+
290
+ // src/components/TabBar.tsx
291
+ import { Box as Box2, Text as Text2 } from "ink";
292
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
293
+ function TabBar({ tabs, activeIndex, width = 80 }) {
294
+ const narrow = width < 50;
295
+ const medium = width < 80;
296
+ return /* @__PURE__ */ jsx2(Box2, { gap: 1, children: tabs.map((tab, i) => {
297
+ const isActive = i === activeIndex;
298
+ const showLabel = narrow ? false : medium ? isActive : true;
299
+ const displayText = showLabel ? `${tab.icon} ${tab.label}` : tab.icon;
300
+ return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
301
+ /* @__PURE__ */ jsx2(
302
+ Text2,
303
+ {
304
+ color: isActive ? inkColors.accent : void 0,
305
+ bold: isActive,
306
+ dimColor: !isActive,
307
+ children: displayText
308
+ }
309
+ ),
310
+ isActive && !narrow && /* @__PURE__ */ jsx2(Text2, { color: inkColors.accent, children: "\u2550".repeat(displayText.length) })
311
+ ] }, tab.id);
312
+ }) });
313
+ }
314
+
116
315
  // src/components/SelectList.tsx
117
- import { useEffect, useMemo, useState as useState2 } from "react";
118
- import { Box as Box2, Text as Text2, useInput } from "ink";
316
+ import { useEffect as useEffect2, useMemo, useState as useState3 } from "react";
317
+ import { Box as Box3, Text as Text3, useInput } from "ink";
119
318
 
120
319
  // src/components/selectListSections.ts
121
320
  function isHeader(item) {
@@ -193,16 +392,21 @@ function countBoxedSectionLines(sections) {
193
392
  }
194
393
 
195
394
  // src/components/SelectList.tsx
196
- import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
395
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
197
396
  function SelectList({
198
397
  items,
199
398
  onSelect,
200
399
  onRightAction,
201
400
  onCancel,
202
401
  maxVisible = 16,
203
- labelWidth = 34,
204
- boxedSections = false
402
+ labelWidth: labelWidthProp,
403
+ boxedSections = false,
404
+ width = 80,
405
+ isInputActive = true,
406
+ arrowNavigation = false
205
407
  }) {
408
+ const labelWidth = labelWidthProp ?? Math.max(16, Math.floor(width * 0.45));
409
+ const isNarrow = width < 50;
206
410
  const isHeader2 = (item) => item.kind === "header";
207
411
  const isSelectable = (item) => item.selectable ?? !isHeader2(item);
208
412
  const isPinnedRow = (item) => item.section === "pinned-runs" || item.section === "pinned-commands";
@@ -215,8 +419,8 @@ function SelectList({
215
419
  }, []),
216
420
  [items]
217
421
  );
218
- const [selectedSelectableIndex, setSelectedSelectableIndex] = useState2(0);
219
- useEffect(() => {
422
+ const [selectedSelectableIndex, setSelectedSelectableIndex] = useState3(0);
423
+ useEffect2(() => {
220
424
  if (selectableIndexes.length === 0) {
221
425
  setSelectedSelectableIndex(0);
222
426
  return;
@@ -250,104 +454,104 @@ function SelectList({
250
454
  return next;
251
455
  });
252
456
  }
253
- if (key.return) {
457
+ if (key.return || arrowNavigation && key.rightArrow) {
254
458
  if (selectedItem) {
255
459
  onSelect(selectedItem.value, selectedItem);
256
460
  }
257
461
  }
258
- if (key.rightArrow && onRightAction && selectedItem?.rightActionable) {
462
+ if (arrowNavigation && key.leftArrow) {
463
+ if (onCancel) onCancel();
464
+ }
465
+ if ((input2 === "p" || input2 === "P") && onRightAction && selectedItem?.rightActionable) {
259
466
  onRightAction(selectedItem);
260
467
  }
261
468
  if (key.escape && onCancel) {
262
469
  onCancel();
263
470
  }
264
- });
265
- const getWindowStart = () => {
266
- if (items.length <= maxVisible) {
267
- return 0;
268
- }
269
- const safeSelectedIndex = selectedItemIndex >= 0 ? selectedItemIndex : 0;
270
- let start = Math.max(
271
- 0,
272
- Math.min(
273
- safeSelectedIndex - Math.floor(maxVisible / 2),
274
- items.length - maxVisible
275
- )
276
- );
277
- const firstVisible = items[start];
278
- if (start > 0 && firstVisible && !isHeader2(firstVisible)) {
279
- let previousHeaderIndex = -1;
280
- for (let i = start - 1; i >= 0; i--) {
281
- if (isHeader2(items[i])) {
282
- previousHeaderIndex = i;
283
- break;
471
+ }, { isActive: isInputActive });
472
+ const { windowStart, windowEnd, visibleItems, boxedLayout } = useMemo(() => {
473
+ let windowStartVal = 0;
474
+ if (items.length > maxVisible) {
475
+ const safeSelectedIndex = selectedItemIndex >= 0 ? selectedItemIndex : 0;
476
+ windowStartVal = Math.max(
477
+ 0,
478
+ Math.min(
479
+ safeSelectedIndex - Math.floor(maxVisible / 2),
480
+ items.length - maxVisible
481
+ )
482
+ );
483
+ const firstVisible = items[windowStartVal];
484
+ if (windowStartVal > 0 && firstVisible && !isHeader2(firstVisible)) {
485
+ let previousHeaderIndex = -1;
486
+ for (let i = windowStartVal - 1; i >= 0; i--) {
487
+ if (isHeader2(items[i])) {
488
+ previousHeaderIndex = i;
489
+ break;
490
+ }
491
+ }
492
+ if (previousHeaderIndex >= 0 && safeSelectedIndex - previousHeaderIndex < maxVisible) {
493
+ windowStartVal = previousHeaderIndex;
284
494
  }
285
495
  }
286
- if (previousHeaderIndex >= 0 && safeSelectedIndex - previousHeaderIndex < maxVisible) {
287
- start = previousHeaderIndex;
288
- }
289
- }
290
- return start;
291
- };
292
- const getWindowRange = () => {
293
- const initialStart = getWindowStart();
294
- const initialEnd = Math.min(initialStart + maxVisible, items.length);
295
- if (!boxedSections) {
296
- return [initialStart, initialEnd];
297
496
  }
298
- let start = initialStart;
497
+ const initialEnd = Math.min(windowStartVal + maxVisible, items.length);
498
+ let start = windowStartVal;
299
499
  let end = initialEnd;
300
- while (start < end) {
301
- const sections = buildBoxedSectionLayout(items, start, end);
302
- if (countBoxedSectionLines(sections) <= maxVisible) {
303
- break;
304
- }
305
- if (selectedItemIndex >= 0 && start === selectedItemIndex && end === selectedItemIndex + 1) {
306
- break;
307
- }
308
- const canTrimStart = selectedItemIndex > start;
309
- const canTrimEnd = selectedItemIndex < end - 1;
310
- if (canTrimEnd && (!canTrimStart || end - selectedItemIndex >= selectedItemIndex - start)) {
500
+ if (boxedSections) {
501
+ while (start < end) {
502
+ const sections = buildBoxedSectionLayout(items, start, end);
503
+ if (countBoxedSectionLines(sections) <= maxVisible) {
504
+ break;
505
+ }
506
+ if (selectedItemIndex >= 0 && start === selectedItemIndex && end === selectedItemIndex + 1) {
507
+ break;
508
+ }
509
+ const canTrimStart = selectedItemIndex > start;
510
+ const canTrimEnd = selectedItemIndex < end - 1;
511
+ if (canTrimEnd && (!canTrimStart || end - selectedItemIndex >= selectedItemIndex - start)) {
512
+ end -= 1;
513
+ continue;
514
+ }
515
+ if (canTrimStart) {
516
+ start += 1;
517
+ continue;
518
+ }
311
519
  end -= 1;
312
- continue;
313
- }
314
- if (canTrimStart) {
315
- start += 1;
316
- continue;
317
520
  }
318
- end -= 1;
319
521
  }
320
- return [start, end];
321
- };
322
- const [windowStart, windowEnd] = getWindowRange();
323
- const visibleItems = items.slice(windowStart, windowEnd);
324
- const boxedLayout = boxedSections ? buildBoxedSectionLayout(items, windowStart, windowEnd) : [];
522
+ return {
523
+ windowStart: start,
524
+ windowEnd: end,
525
+ visibleItems: items.slice(start, end),
526
+ boxedLayout: boxedSections ? buildBoxedSectionLayout(items, start, end) : []
527
+ };
528
+ }, [items, selectedItemIndex, maxVisible, boxedSections]);
325
529
  const showScrollUp = windowStart > 0;
326
530
  const showScrollDown = windowEnd < items.length;
327
531
  const renderSelectableRow = (item, globalIdx) => {
328
532
  const isSelected = globalIdx === selectedItemIndex;
329
- return /* @__PURE__ */ jsxs2(Box2, { gap: 1, children: [
330
- /* @__PURE__ */ jsx2(Text2, { color: isSelected ? inkColors.accent : void 0, children: isSelected ? "\u276F" : " " }),
331
- /* @__PURE__ */ jsx2(Box2, { width: labelWidth, children: /* @__PURE__ */ jsxs2(
332
- Text2,
533
+ return /* @__PURE__ */ jsxs3(Box3, { gap: 1, children: [
534
+ /* @__PURE__ */ jsx3(Text3, { color: isSelected ? inkColors.accent : void 0, children: isSelected ? "\u276F" : " " }),
535
+ /* @__PURE__ */ jsx3(Box3, { width: labelWidth, children: /* @__PURE__ */ jsxs3(
536
+ Text3,
333
537
  {
334
538
  color: isSelected ? inkColors.accent : isPinnedRow(item) ? "white" : void 0,
335
539
  bold: isSelected || isPinnedRow(item),
540
+ wrap: "truncate",
336
541
  children: [
337
542
  item.icon ? `${item.icon} ` : "",
338
543
  item.label
339
544
  ]
340
545
  }
341
546
  ) }),
342
- item.hint && /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: item.hint }),
343
- isSelected && item.rightActionable && /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "\u2192 pin/unpin" })
547
+ !isNarrow && item.hint && /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: item.hint })
344
548
  ] }, item.id ?? `${item.value}-${globalIdx}`);
345
549
  };
346
- return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
347
- showScrollUp && /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: " \u2191 more" }),
550
+ return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
551
+ showScrollUp && /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: " \u2191 more" }),
348
552
  boxedSections ? boxedLayout.map((section) => {
349
553
  if (section.type === "heading") {
350
- return /* @__PURE__ */ jsx2(Box2, { children: /* @__PURE__ */ jsx2(Text2, { color: inkColors.accent, bold: true, children: section.label }) }, section.key);
554
+ return /* @__PURE__ */ jsx3(Box3, { children: /* @__PURE__ */ jsx3(Text3, { color: inkColors.accent, bold: true, children: section.label }) }, section.key);
351
555
  }
352
556
  const hasSelectedRow = section.rows.some(
353
557
  (row) => row.globalIndex === selectedItemIndex
@@ -355,8 +559,8 @@ function SelectList({
355
559
  const isPinnedSection = section.rows.some(
356
560
  (row) => isPinnedRow(row.item)
357
561
  );
358
- return /* @__PURE__ */ jsxs2(
359
- Box2,
562
+ return /* @__PURE__ */ jsxs3(
563
+ Box3,
360
564
  {
361
565
  flexDirection: "column",
362
566
  borderStyle: "round",
@@ -364,8 +568,8 @@ function SelectList({
364
568
  borderDimColor: !hasSelectedRow && !isPinnedSection,
365
569
  paddingX: 1,
366
570
  children: [
367
- section.title && /* @__PURE__ */ jsx2(Text2, { color: inkColors.accent, bold: true, children: section.title }),
368
- /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", children: section.rows.map(
571
+ section.title && /* @__PURE__ */ jsx3(Text3, { color: inkColors.accent, bold: true, children: section.title }),
572
+ /* @__PURE__ */ jsx3(Box3, { flexDirection: "column", children: section.rows.map(
369
573
  (row) => renderSelectableRow(row.item, row.globalIndex)
370
574
  ) })
371
575
  ]
@@ -376,28 +580,28 @@ function SelectList({
376
580
  const globalIdx = windowStart + i;
377
581
  const selectable = isSelectable(item);
378
582
  if (!selectable) {
379
- return /* @__PURE__ */ jsx2(
380
- Box2,
583
+ return /* @__PURE__ */ jsx3(
584
+ Box3,
381
585
  {
382
586
  marginTop: i === 0 ? 0 : 1,
383
- children: /* @__PURE__ */ jsx2(Text2, { color: inkColors.accent, bold: true, children: item.label })
587
+ children: /* @__PURE__ */ jsx3(Text3, { color: inkColors.accent, bold: true, children: item.label })
384
588
  },
385
589
  item.id ?? `${item.value}-${globalIdx}`
386
590
  );
387
591
  }
388
592
  return renderSelectableRow(item, globalIdx);
389
593
  }),
390
- showScrollDown && /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: " \u2193 more" })
594
+ showScrollDown && /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: " \u2193 more" })
391
595
  ] });
392
596
  }
393
597
 
394
598
  // src/components/StatusBar.tsx
395
- import { Box as Box3, Text as Text3 } from "ink";
396
- import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
397
- function StatusBar({ hint }) {
398
- return /* @__PURE__ */ jsxs3(Box3, { marginTop: 1, justifyContent: "space-between", children: [
399
- /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: hint || "\u2191\u2193 navigate \xB7 Enter select \xB7 Esc back" }),
400
- /* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
599
+ import { Box as Box4, Text as Text4 } from "ink";
600
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
601
+ function StatusBar({ hint, width = 80 }) {
602
+ return /* @__PURE__ */ jsxs4(Box4, { marginTop: 1, justifyContent: "space-between", children: [
603
+ /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: hint || "\u2191\u2193 navigate \xB7 Enter select \xB7 Esc back" }),
604
+ width >= 60 && /* @__PURE__ */ jsxs4(Text4, { dimColor: true, children: [
401
605
  "polter v",
402
606
  VERSION
403
607
  ] })
@@ -459,184 +663,53 @@ function isPinnedRun(runCommand2) {
459
663
  return getPinnedRuns().includes(runCommand2);
460
664
  }
461
665
 
462
- // src/data/commands.ts
463
- var categories = {
464
- "quick-start": {
465
- icon: "\u{1F680}",
466
- label: "Quick Start",
467
- description: "Get started with Supabase",
468
- commands: [
469
- {
470
- value: "bootstrap",
471
- label: "bootstrap",
472
- hint: "Bootstrap from a starter template"
473
- },
474
- { value: "init", label: "init", hint: "Initialize a local project" },
475
- {
476
- value: "login",
477
- label: "login",
478
- hint: "Authenticate with access token"
479
- },
480
- { value: "logout", label: "logout", hint: "Remove local auth token" }
481
- ]
482
- },
483
- "local-dev": {
484
- icon: "\u{1F6E0}",
485
- label: "Local Dev",
486
- description: "Day-to-day local workflow",
487
- commands: [
488
- {
489
- value: "start",
490
- label: "start",
491
- hint: "Start local Supabase containers"
492
- },
493
- { value: "stop", label: "stop", hint: "Stop local Supabase containers" },
494
- { value: "status", label: "status", hint: "Show container status" },
495
- { value: "db", label: "db", hint: "Manage Postgres databases" },
496
- {
497
- value: "migration",
498
- label: "migration",
499
- hint: "Manage migration scripts"
500
- },
501
- { value: "seed", label: "seed", hint: "Seed from config.toml" },
502
- { value: "test", label: "test", hint: "Run tests on local stack" }
503
- ]
504
- },
505
- "inspect-generate": {
506
- icon: "\u{1F50D}",
507
- label: "Inspect & Generate",
508
- description: "Tooling and introspection",
509
- commands: [
510
- { value: "inspect", label: "inspect", hint: "Inspect project resources" },
511
- { value: "gen", label: "gen", hint: "Run code generation tools" },
512
- { value: "services", label: "services", hint: "Show service versions" },
513
- { value: "link", label: "link", hint: "Link to remote project" },
514
- { value: "unlink", label: "unlink", hint: "Unlink remote project" }
515
- ]
516
- },
517
- cloud: {
518
- icon: "\u2601\uFE0F",
519
- label: "Cloud Management",
520
- description: "Core cloud resources",
521
- commands: [
522
- {
523
- value: "projects",
524
- label: "projects",
525
- hint: "Manage Supabase projects"
526
- },
527
- { value: "functions", label: "functions", hint: "Manage Edge Functions" },
528
- { value: "secrets", label: "secrets", hint: "Manage project secrets" },
529
- {
530
- value: "config",
531
- label: "config",
532
- hint: "Manage project configuration"
533
- },
534
- { value: "storage", label: "storage", hint: "Manage Storage objects" }
535
- ]
536
- },
537
- networking: {
538
- icon: "\u{1F310}",
539
- label: "Networking & Security",
540
- description: "Network and security config",
541
- commands: [
542
- { value: "domains", label: "domains", hint: "Manage custom domains" },
543
- {
544
- value: "ssl-enforcement",
545
- label: "ssl-enforcement",
546
- hint: "Manage SSL config"
547
- },
548
- {
549
- value: "network-bans",
550
- label: "network-bans",
551
- hint: "Manage network bans"
552
- },
553
- {
554
- value: "network-restrictions",
555
- label: "network-restrictions",
556
- hint: "Manage network restrictions"
557
- },
558
- {
559
- value: "vanity-subdomains",
560
- label: "vanity-subdomains",
561
- hint: "Manage vanity subdomains"
562
- },
563
- {
564
- value: "encryption",
565
- label: "encryption",
566
- hint: "Manage encryption keys"
567
- }
568
- ]
569
- },
570
- "org-auth": {
571
- icon: "\u{1F465}",
572
- label: "Organizations & Admin",
573
- description: "Team management and admin tools",
574
- commands: [
575
- { value: "orgs", label: "orgs", hint: "Manage organizations" },
576
- { value: "sso", label: "sso", hint: "Manage Single Sign-On" },
577
- { value: "branches", label: "branches", hint: "Manage preview branches" },
578
- { value: "backups", label: "backups", hint: "Manage physical backups" },
579
- { value: "snippets", label: "snippets", hint: "Manage SQL snippets" },
580
- {
581
- value: "postgres-config",
582
- label: "postgres-config",
583
- hint: "Manage Postgres config"
584
- }
585
- ]
586
- },
587
- utilities: {
588
- icon: "\u2699\uFE0F",
589
- label: "Utilities",
590
- description: "Help and shell completions",
591
- commands: [
592
- {
593
- value: "completion",
594
- label: "completion",
595
- hint: "Generate shell completion script"
596
- },
597
- { value: "help", label: "help", hint: "Help about any command" }
598
- ]
666
+ // src/screens/homeModel.ts
667
+ function getFeatures() {
668
+ return features;
669
+ }
670
+ function buildPinnedOnlyItems(pinnedCommands, pinnedRuns) {
671
+ const items = [];
672
+ if (pinnedCommands.length > 0) {
673
+ items.push({
674
+ id: "pinned-commands-header",
675
+ value: "__pinned_commands_header__",
676
+ label: "\u{1F4CC} Commands",
677
+ kind: "header",
678
+ selectable: false
679
+ });
680
+ for (const command of pinnedCommands) {
681
+ const cmdDef = findCommandByValue(command);
682
+ const toolHint = cmdDef ? cmdDef.tool : "supabase";
683
+ const labelHint = cmdDef?.hint;
684
+ items.push({
685
+ id: `command:${command}`,
686
+ value: command,
687
+ label: command,
688
+ hint: [toolHint, labelHint].filter(Boolean).join(" \xB7 "),
689
+ icon: "\u{1F4CC}",
690
+ kind: "command",
691
+ rightActionable: true,
692
+ section: "pinned-commands"
693
+ });
694
+ }
599
695
  }
600
- };
601
-
602
- // src/screens/mainMenuModel.ts
603
- var commandCatalog = Object.entries(categories).flatMap(
604
- ([, category]) => category.commands.map((command) => ({
605
- categoryIcon: category.icon,
606
- categoryLabel: category.label,
607
- value: command.value,
608
- hint: command.hint
609
- }))
610
- );
611
- var commandInfoByValue = new Map(
612
- commandCatalog.map((entry) => [entry.value, entry])
613
- );
614
- function getCommandInfo(commandValue) {
615
- return commandInfoByValue.get(commandValue);
616
- }
617
- function buildMainMenuItems({
618
- pinnedCommands,
619
- pinnedRuns
620
- }) {
621
- const pinnedCommandSet = new Set(pinnedCommands);
622
- const nextItems = [];
623
696
  if (pinnedRuns.length > 0) {
624
- nextItems.push({
625
- id: "section-pinned-runs",
626
- value: "__section_pinned_runs__",
627
- label: "\u25B6 Pinned Runs",
697
+ items.push({
698
+ id: "pinned-runs-header",
699
+ value: "__pinned_runs_header__",
700
+ label: "\u25B6 Pipelines",
628
701
  kind: "header",
629
702
  selectable: false
630
703
  });
631
704
  for (const runCommand2 of pinnedRuns) {
632
705
  const baseCommand = runCommand2.split(" ").filter(Boolean)[0] ?? "";
633
- const info = getCommandInfo(baseCommand);
634
- const infoHint = info ? `${info.categoryIcon} ${info.categoryLabel} \xB7 exact pinned run` : "Exact pinned run";
635
- nextItems.push({
706
+ const cmdDef = findCommandByValue(baseCommand);
707
+ const toolHint = cmdDef ? cmdDef.tool : "supabase";
708
+ items.push({
636
709
  id: `run:${runCommand2}`,
637
710
  value: runCommand2,
638
711
  label: runCommand2,
639
- hint: infoHint,
712
+ hint: toolHint,
640
713
  icon: "\u25B6",
641
714
  kind: "run",
642
715
  rightActionable: true,
@@ -644,115 +717,122 @@ function buildMainMenuItems({
644
717
  });
645
718
  }
646
719
  }
647
- if (pinnedCommands.length > 0) {
648
- nextItems.push({
649
- id: "section-pinned-commands",
650
- value: "__section_pinned_commands__",
651
- label: "\u{1F4CC} Pinned Commands",
720
+ return items;
721
+ }
722
+ function buildHomeItems({
723
+ activeFeature,
724
+ pinnedCommands,
725
+ pinnedRuns,
726
+ showPinnedSection = true
727
+ }) {
728
+ const items = [];
729
+ if (showPinnedSection && (pinnedRuns.length > 0 || pinnedCommands.length > 0)) {
730
+ items.push({
731
+ id: "section-pinned",
732
+ value: "__section_pinned__",
733
+ label: "\u{1F4CC} Pinned",
652
734
  kind: "header",
653
735
  selectable: false
654
736
  });
655
- for (const command of pinnedCommands) {
656
- const info = getCommandInfo(command);
657
- const infoHint = info ? `${info.categoryIcon} ${info.categoryLabel}${info.hint ? ` \xB7 ${info.hint}` : ""}` : "Pinned command";
658
- nextItems.push({
659
- id: `command:${command}`,
660
- value: command,
661
- label: command,
662
- hint: infoHint,
663
- icon: "\u{1F4CC}",
664
- kind: "command",
665
- rightActionable: true,
666
- section: "pinned-commands"
667
- });
668
- }
737
+ items.push(...buildPinnedOnlyItems(pinnedCommands, pinnedRuns));
669
738
  }
670
- nextItems.push({
671
- id: "section-all-commands",
672
- value: "__section_all_commands__",
673
- label: "\u{1F4C2} All Commands",
739
+ items.push({
740
+ id: "section-commands",
741
+ value: "__section_commands__",
742
+ label: `\u{1F4C2} ${activeFeature.label} Commands`,
674
743
  kind: "header",
675
744
  selectable: false
676
745
  });
677
- for (const [categoryKey, category] of Object.entries(categories)) {
678
- nextItems.push({
679
- id: `category-${categoryKey}`,
680
- value: `__category_${categoryKey}__`,
681
- label: `${category.icon} ${category.label}`,
746
+ const toolOrder = { supabase: 0, vercel: 1, gh: 2, pulumi: 3 };
747
+ const toolIcons = { supabase: "\u{1F7E2}", vercel: "\u26AA", gh: "\u{1F535}", pulumi: "\u{1F7E3}" };
748
+ const grouped = /* @__PURE__ */ new Map();
749
+ for (const cmd of activeFeature.commands) {
750
+ const existing = grouped.get(cmd.tool) ?? [];
751
+ existing.push(cmd);
752
+ grouped.set(cmd.tool, existing);
753
+ }
754
+ const sortedTools = [...grouped.keys()].sort(
755
+ (a, b) => (toolOrder[a] ?? 99) - (toolOrder[b] ?? 99)
756
+ );
757
+ for (const tool of sortedTools) {
758
+ const cmds = grouped.get(tool);
759
+ const icon = toolIcons[tool] ?? "\u26AA";
760
+ items.push({
761
+ id: `tool-header-${tool}`,
762
+ value: `__tool_header_${tool}__`,
763
+ label: `${icon} ${tool}`,
682
764
  kind: "header",
683
765
  selectable: false
684
766
  });
685
- for (const command of category.commands) {
686
- const pinHint = pinnedCommandSet.has(command.value) ? "pinned" : void 0;
687
- nextItems.push({
688
- id: `cmd-${categoryKey}-${command.value}`,
689
- value: command.value,
690
- label: command.label,
691
- hint: [command.hint, pinHint].filter(Boolean).join(" \xB7 "),
767
+ for (const cmd of cmds) {
768
+ const value = getCommandValue(cmd);
769
+ const isPinned = pinnedCommands.includes(value);
770
+ items.push({
771
+ id: `cmd-${cmd.id}`,
772
+ value,
773
+ label: cmd.label,
774
+ hint: [cmd.hint, isPinned ? "pinned" : void 0].filter(Boolean).join(" \xB7 "),
692
775
  kind: "command",
693
776
  rightActionable: true,
694
- section: categoryKey,
695
- groupLabel: category.label
777
+ section: activeFeature.id,
778
+ groupLabel: tool
696
779
  });
697
780
  }
698
781
  }
699
- nextItems.push({
700
- id: "section-actions",
701
- value: "__section_actions__",
702
- label: "\u26A1 Actions",
703
- kind: "header",
704
- selectable: false
705
- });
706
- nextItems.push({
707
- id: "action-custom",
708
- value: "__action_custom__",
709
- label: "Custom Command",
710
- hint: "Free-form args or check version",
711
- icon: "\u270F\uFE0F",
712
- kind: "action"
713
- });
714
- nextItems.push({
715
- id: "action-update",
716
- value: "__action_update__",
717
- label: "Update Polter",
718
- hint: "Update the current repo install or the global install",
719
- icon: "\u2B06\uFE0F",
720
- kind: "action"
721
- });
722
- nextItems.push({
723
- id: "action-exit",
724
- value: "__action_exit__",
725
- label: "Exit",
726
- icon: "\u{1F6AA}",
727
- kind: "action"
728
- });
729
- return nextItems;
782
+ return items;
730
783
  }
731
784
 
732
- // src/screens/MainMenu.tsx
733
- import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
734
- function MainMenu({
785
+ // src/screens/Home.tsx
786
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
787
+ function Home({
735
788
  onNavigate,
736
- onExit
789
+ onExit,
790
+ width = 80,
791
+ height = 24
737
792
  }) {
738
- const [pinnedCommands, setPinnedCommands2] = useState3(
793
+ const allFeatures = useMemo2(() => getFeatures(), []);
794
+ const [activeTabIndex, setActiveTabIndex] = useState4(0);
795
+ const [pinnedCommands, setPinnedCommands2] = useState4(
739
796
  () => getPinnedCommands()
740
797
  );
741
- const [pinnedRuns, setPinnedRuns2] = useState3(
798
+ const [pinnedRuns, setPinnedRuns2] = useState4(
742
799
  () => getPinnedRuns()
743
800
  );
744
- const [pinFeedback, setPinFeedback] = useState3();
745
- useEffect2(() => {
801
+ const [pinFeedback, setPinFeedback] = useState4();
802
+ const activeFeature = allFeatures[activeTabIndex];
803
+ useEffect3(() => {
746
804
  if (!pinFeedback) return;
747
805
  const timeout = setTimeout(() => setPinFeedback(void 0), 1400);
748
806
  return () => clearTimeout(timeout);
749
807
  }, [pinFeedback]);
808
+ useInput2((_input, key) => {
809
+ if (key.leftArrow && !key.meta) {
810
+ setActiveTabIndex(
811
+ (prev) => prev > 0 ? prev - 1 : allFeatures.length - 1
812
+ );
813
+ }
814
+ if (key.rightArrow && !key.meta) {
815
+ setActiveTabIndex(
816
+ (prev) => prev < allFeatures.length - 1 ? prev + 1 : 0
817
+ );
818
+ }
819
+ if (key.tab) {
820
+ setActiveTabIndex(
821
+ (prev) => prev < allFeatures.length - 1 ? prev + 1 : 0
822
+ );
823
+ }
824
+ });
825
+ const tabs = useMemo2(
826
+ () => allFeatures.map((f) => ({ id: f.id, icon: f.icon, label: f.label })),
827
+ [allFeatures]
828
+ );
750
829
  const items = useMemo2(
751
- () => buildMainMenuItems({
830
+ () => buildHomeItems({
831
+ activeFeature,
752
832
  pinnedCommands,
753
833
  pinnedRuns
754
834
  }),
755
- [pinnedCommands, pinnedRuns]
835
+ [activeFeature, pinnedCommands, pinnedRuns]
756
836
  );
757
837
  const pinnedCommandSet = useMemo2(
758
838
  () => new Set(pinnedCommands),
@@ -766,78 +846,98 @@ function MainMenu({
766
846
  const handleSelect = (value, item) => {
767
847
  if (!item) return;
768
848
  if (item.kind === "command") {
769
- onNavigate("command-args", { command: value });
849
+ const cmdDef = findCommandByValue(value);
850
+ if (cmdDef) {
851
+ onNavigate("command-args", {
852
+ command: value,
853
+ commandId: cmdDef.id,
854
+ tool: cmdDef.tool
855
+ });
856
+ } else {
857
+ onNavigate("command-args", { command: value, tool: "supabase" });
858
+ }
770
859
  return;
771
860
  }
772
861
  if (item.kind === "run") {
773
862
  const args = value.split(" ").filter(Boolean);
774
863
  if (args.length > 0) {
775
- onNavigate("confirm-execute", { args });
864
+ const basePart = args[0] ?? "";
865
+ const cmdDef = findCommandByValue(basePart);
866
+ const tool = cmdDef?.tool ?? "supabase";
867
+ onNavigate("confirm-execute", { args, tool });
776
868
  }
777
869
  return;
778
870
  }
779
- if (value === "__action_custom__") {
780
- onNavigate("custom-command");
781
- return;
782
- }
783
- if (value === "__action_update__") {
784
- onNavigate("self-update");
785
- return;
786
- }
787
- if (value === "__action_exit__") {
788
- onExit();
871
+ switch (value) {
872
+ case "__action_custom__":
873
+ onNavigate("custom-command");
874
+ break;
875
+ case "__action_pipelines__":
876
+ onNavigate("pipeline-list");
877
+ break;
878
+ case "__action_tools__":
879
+ onNavigate("tool-status");
880
+ break;
881
+ case "__action_config__":
882
+ onNavigate("project-config");
883
+ break;
884
+ case "__action_update__":
885
+ onNavigate("self-update");
886
+ break;
887
+ case "__action_exit__":
888
+ onExit();
889
+ break;
789
890
  }
790
891
  };
791
892
  const handleRightAction = (item) => {
792
893
  if (item.kind === "command") {
793
- const command = item.value;
794
- const wasPinned = pinnedCommandSet.has(command);
795
- togglePinnedCommand(command);
894
+ const wasPinned = pinnedCommandSet.has(item.value);
895
+ togglePinnedCommand(item.value);
796
896
  refreshPins();
797
897
  setPinFeedback(
798
- wasPinned ? `Unpinned "${command}"` : `Pinned "${command}"`
898
+ wasPinned ? `Unpinned "${item.value}"` : `Pinned "${item.value}"`
799
899
  );
800
900
  return;
801
901
  }
802
902
  if (item.kind === "run") {
803
- const runCommand2 = item.value;
804
- const wasPinned = pinnedRunSet.has(runCommand2);
805
- togglePinnedRun(runCommand2);
903
+ const wasPinned = pinnedRunSet.has(item.value);
904
+ togglePinnedRun(item.value);
806
905
  refreshPins();
807
906
  setPinFeedback(
808
- wasPinned ? `Unpinned exact run "${runCommand2}"` : `Pinned exact run "${runCommand2}"`
907
+ wasPinned ? `Unpinned run "${item.value}"` : `Pinned run "${item.value}"`
809
908
  );
810
909
  }
811
910
  };
812
- return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
813
- /* @__PURE__ */ jsx4(GhostBanner, {}),
814
- /* @__PURE__ */ jsx4(Box4, { marginBottom: 1, children: /* @__PURE__ */ jsx4(Text4, { bold: true, children: "Choose a command, then press Enter to continue." }) }),
815
- pinFeedback && /* @__PURE__ */ jsx4(Box4, { marginBottom: 1, children: /* @__PURE__ */ jsxs4(Text4, { color: inkColors.accent, children: [
911
+ return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", children: [
912
+ /* @__PURE__ */ jsx5(Box5, { marginBottom: 1, children: /* @__PURE__ */ jsx5(TabBar, { tabs, activeIndex: activeTabIndex, width }) }),
913
+ pinFeedback && /* @__PURE__ */ jsx5(Box5, { marginBottom: 1, children: /* @__PURE__ */ jsxs5(Text5, { color: inkColors.accent, children: [
816
914
  "\u2713 ",
817
915
  pinFeedback
818
916
  ] }) }),
819
- /* @__PURE__ */ jsx4(
917
+ /* @__PURE__ */ jsx5(
820
918
  SelectList,
821
919
  {
822
920
  items,
823
921
  onSelect: handleSelect,
824
922
  onRightAction: handleRightAction,
825
- boxedSections: true
923
+ boxedSections: true,
924
+ width,
925
+ maxVisible: Math.max(8, height - 14)
826
926
  }
827
927
  ),
828
- /* @__PURE__ */ jsx4(StatusBar, { hint: "\u2191\u2193 navigate \xB7 Enter select \xB7 \u2192 pin/unpin \xB7 Esc back" })
928
+ /* @__PURE__ */ jsx5(StatusBar, { hint: "\u2190\u2192 tab \xB7 \u2191\u2193 navigate \xB7 Enter select \xB7 p pin", width })
829
929
  ] });
830
930
  }
831
931
 
832
932
  // src/screens/CommandArgs.tsx
833
- import { useEffect as useEffect3, useMemo as useMemo3, useState as useState5 } from "react";
834
- import { Box as Box6, Text as Text6 } from "ink";
933
+ import { useEffect as useEffect4, useMemo as useMemo3, useState as useState6 } from "react";
934
+ import { Box as Box7, Text as Text8 } from "ink";
835
935
 
836
936
  // src/components/TextPrompt.tsx
837
- import { useState as useState4 } from "react";
838
- import { Box as Box5, Text as Text5, useInput as useInput2 } from "ink";
937
+ import { useState as useState5 } from "react";
938
+ import { Box as Box6, Text as Text6, useInput as useInput3 } from "ink";
839
939
  import TextInputComponent from "ink-text-input";
840
- import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
940
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
841
941
  function TextPrompt({
842
942
  label,
843
943
  placeholder,
@@ -845,9 +945,9 @@ function TextPrompt({
845
945
  onCancel,
846
946
  validate
847
947
  }) {
848
- const [value, setValue] = useState4("");
849
- const [error, setError] = useState4();
850
- useInput2((_input, key) => {
948
+ const [value, setValue] = useState5("");
949
+ const [error, setError] = useState5();
950
+ useInput3((_input, key) => {
851
951
  if (key.escape && onCancel) {
852
952
  onCancel();
853
953
  }
@@ -863,14 +963,14 @@ function TextPrompt({
863
963
  setError(void 0);
864
964
  onSubmit(val);
865
965
  };
866
- return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", children: [
867
- /* @__PURE__ */ jsxs5(Box5, { gap: 1, children: [
868
- /* @__PURE__ */ jsx5(Text5, { color: inkColors.accent, bold: true, children: "?" }),
869
- /* @__PURE__ */ jsx5(Text5, { children: label })
966
+ return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
967
+ /* @__PURE__ */ jsxs6(Box6, { gap: 1, children: [
968
+ /* @__PURE__ */ jsx6(Text6, { color: inkColors.accent, bold: true, children: "?" }),
969
+ /* @__PURE__ */ jsx6(Text6, { children: label })
870
970
  ] }),
871
- /* @__PURE__ */ jsxs5(Box5, { gap: 1, marginLeft: 2, children: [
872
- /* @__PURE__ */ jsx5(Text5, { color: inkColors.accent, children: "\u276F" }),
873
- /* @__PURE__ */ jsx5(
971
+ /* @__PURE__ */ jsxs6(Box6, { gap: 1, marginLeft: 2, children: [
972
+ /* @__PURE__ */ jsx6(Text6, { color: inkColors.accent, children: "\u276F" }),
973
+ /* @__PURE__ */ jsx6(
874
974
  TextInputComponent,
875
975
  {
876
976
  value,
@@ -883,68 +983,30 @@ function TextPrompt({
883
983
  }
884
984
  )
885
985
  ] }),
886
- error && /* @__PURE__ */ jsx5(Box5, { marginLeft: 2, children: /* @__PURE__ */ jsxs5(Text5, { color: "red", children: [
986
+ error && /* @__PURE__ */ jsx6(Box6, { marginLeft: 2, children: /* @__PURE__ */ jsxs6(Text6, { color: "red", children: [
887
987
  "\u2717 ",
888
988
  error
889
989
  ] }) })
890
990
  ] });
891
991
  }
892
992
 
893
- // src/data/suggestedArgs.ts
894
- var suggestedArgsByCommand = {
895
- db: [
896
- { value: "pull", label: "pull", hint: "Pull schema from remote", args: ["pull"] },
897
- { value: "push", label: "push", hint: "Push local migrations", args: ["push"] },
898
- { value: "reset", label: "reset", hint: "Reset local database", args: ["reset"] },
899
- { value: "dump", label: "dump", hint: "Dump schema/data", args: ["dump"] },
900
- { value: "diff", label: "diff", hint: "Show migration diff", args: ["diff"] },
901
- { value: "lint", label: "lint", hint: "Lint SQL migrations", args: ["lint"] }
902
- ],
903
- migration: [
904
- { value: "new", label: "new <name>", hint: "Create migration file", args: ["new"] },
905
- { value: "up", label: "up", hint: "Apply pending migrations", args: ["up"] },
906
- { value: "down", label: "down", hint: "Rollback last migration", args: ["down"] },
907
- { value: "list", label: "list", hint: "List local migrations", args: ["list"] },
908
- { value: "repair", label: "repair", hint: "Repair migration history", args: ["repair"] },
909
- { value: "fetch", label: "fetch", hint: "Fetch migration history", args: ["fetch"] }
910
- ],
911
- projects: [
912
- { value: "list", label: "list", hint: "List projects", args: ["list"] },
913
- { value: "create", label: "create", hint: "Create project", args: ["create"] },
914
- { value: "delete", label: "delete", hint: "Delete project", args: ["delete"] }
915
- ],
916
- functions: [
917
- { value: "list", label: "list", hint: "List functions", args: ["list"] },
918
- { value: "new", label: "new <name>", hint: "Create a function", args: ["new"] },
919
- { value: "serve", label: "serve", hint: "Serve functions locally", args: ["serve"] },
920
- { value: "deploy", label: "deploy <name>", hint: "Deploy function", args: ["deploy"] },
921
- { value: "delete", label: "delete <name>", hint: "Delete function", args: ["delete"] }
922
- ],
923
- storage: [
924
- { value: "ls", label: "ls", hint: "List storage objects", args: ["ls"] },
925
- { value: "cp", label: "cp", hint: "Copy storage object", args: ["cp"] },
926
- { value: "mv", label: "mv", hint: "Move storage object", args: ["mv"] },
927
- { value: "rm", label: "rm", hint: "Remove storage object", args: ["rm"] }
928
- ],
929
- seed: [
930
- { value: "run", label: "run", hint: "Run configured seed file", args: ["run"] }
931
- ],
932
- orgs: [
933
- { value: "list", label: "list", hint: "List organizations", args: ["list"] },
934
- { value: "create", label: "create", hint: "Create organization", args: ["create"] }
935
- ],
936
- snippets: [
937
- { value: "list", label: "list", hint: "List SQL snippets", args: ["list"] },
938
- { value: "create", label: "create", hint: "Create SQL snippet", args: ["create"] },
939
- { value: "delete", label: "delete", hint: "Delete SQL snippet", args: ["delete"] }
940
- ],
941
- backups: [
942
- { value: "list", label: "list", hint: "List backups", args: ["list"] },
943
- { value: "download", label: "download", hint: "Download backup", args: ["download"] }
944
- ]
993
+ // src/components/ToolBadge.tsx
994
+ import { Text as Text7 } from "ink";
995
+ import { jsx as jsx7 } from "react/jsx-runtime";
996
+ var toolColors = {
997
+ supabase: "#3ECF8E",
998
+ gh: "#58A6FF",
999
+ vercel: "#FFFFFF",
1000
+ pulumi: "#8B5CF6"
1001
+ };
1002
+ var toolLabels = {
1003
+ supabase: "supabase",
1004
+ gh: "github",
1005
+ vercel: "vercel",
1006
+ pulumi: "pulumi"
945
1007
  };
946
- function getSuggestedArgOptions(command) {
947
- return suggestedArgsByCommand[command] ?? [];
1008
+ function ToolBadge({ tool }) {
1009
+ return /* @__PURE__ */ jsx7(Text7, { color: toolColors[tool], dimColor: true, children: toolLabels[tool] });
948
1010
  }
949
1011
 
950
1012
  // src/screens/commandArgsModel.ts
@@ -1000,7 +1062,7 @@ function buildCommandArgItems({
1000
1062
  value: "__run_base__",
1001
1063
  label: "Run without additional args",
1002
1064
  hint: [
1003
- `supabase ${command}`,
1065
+ `${command}`,
1004
1066
  pinnedRunSet.has(baseRunCommand) ? "pinned run" : void 0
1005
1067
  ].filter(Boolean).join(" \xB7 "),
1006
1068
  kind: "action",
@@ -1021,55 +1083,71 @@ function buildCommandArgItems({
1021
1083
  }
1022
1084
 
1023
1085
  // src/screens/CommandArgs.tsx
1024
- import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
1086
+ import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
1025
1087
  function CommandArgs({
1026
1088
  command,
1089
+ tool,
1027
1090
  onNavigate,
1028
- onBack
1091
+ onBack,
1092
+ width = 80,
1093
+ panelMode = false,
1094
+ isInputActive = true
1029
1095
  }) {
1030
- const suggestions = useMemo3(() => getSuggestedArgOptions(command), [command]);
1031
- const [pinnedRuns, setPinnedRuns2] = useState5(() => getPinnedRuns());
1032
- const [pinFeedback, setPinFeedback] = useState5();
1033
- const [phase, setPhase] = useState5(
1096
+ const cmdDef = useMemo3(() => findCommandByValue(command), [command]);
1097
+ const resolvedTool = tool ?? cmdDef?.tool ?? "supabase";
1098
+ const suggestions = useMemo3(
1099
+ () => cmdDef?.suggestedArgs ?? [],
1100
+ [cmdDef]
1101
+ );
1102
+ const [pinnedRuns, setPinnedRuns2] = useState6(() => getPinnedRuns());
1103
+ const [pinFeedback, setPinFeedback] = useState6();
1104
+ const [phase, setPhase] = useState6(
1034
1105
  suggestions.length > 0 ? "select" : "custom"
1035
1106
  );
1036
- useEffect3(() => {
1107
+ useEffect4(() => {
1037
1108
  setPhase(suggestions.length > 0 ? "select" : "custom");
1038
1109
  }, [command, suggestions.length]);
1039
- useEffect3(() => {
1110
+ useEffect4(() => {
1040
1111
  if (!pinFeedback) return;
1041
1112
  const timeout = setTimeout(() => setPinFeedback(void 0), 1400);
1042
1113
  return () => clearTimeout(timeout);
1043
1114
  }, [pinFeedback]);
1044
1115
  const navigateWithExtraArgs = (extraArgs) => {
1116
+ const baseArgs = cmdDef ? cmdDef.base : [command];
1045
1117
  onNavigate("flag-selection", {
1046
- args: [command, ...extraArgs],
1047
- command
1118
+ args: [...baseArgs, ...extraArgs],
1119
+ command,
1120
+ tool: resolvedTool
1048
1121
  });
1049
1122
  };
1050
1123
  if (!command) {
1051
- return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
1052
- /* @__PURE__ */ jsx6(Box6, { marginBottom: 1, children: /* @__PURE__ */ jsx6(Text6, { color: "red", children: "Command not provided." }) }),
1053
- /* @__PURE__ */ jsx6(
1124
+ return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
1125
+ /* @__PURE__ */ jsx8(Box7, { marginBottom: 1, children: /* @__PURE__ */ jsx8(Text8, { color: "red", children: "Command not provided." }) }),
1126
+ /* @__PURE__ */ jsx8(
1054
1127
  SelectList,
1055
1128
  {
1056
1129
  items: [{ value: "__back__", label: "\u2190 Back to menu" }],
1057
1130
  onSelect: onBack,
1058
- onCancel: onBack
1131
+ onCancel: onBack,
1132
+ width,
1133
+ isInputActive,
1134
+ arrowNavigation: panelMode
1059
1135
  }
1060
1136
  )
1061
1137
  ] });
1062
1138
  }
1063
1139
  if (phase === "custom") {
1064
- return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
1065
- /* @__PURE__ */ jsxs6(Box6, { marginBottom: 1, gap: 1, children: [
1066
- /* @__PURE__ */ jsx6(Text6, { color: inkColors.accent, bold: true, children: "Command" }),
1067
- /* @__PURE__ */ jsx6(Text6, { children: command })
1140
+ const toolLabel = resolvedTool === "supabase" ? "supabase" : resolvedTool;
1141
+ return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
1142
+ /* @__PURE__ */ jsxs7(Box7, { marginBottom: 1, gap: 1, children: [
1143
+ /* @__PURE__ */ jsx8(Text8, { color: inkColors.accent, bold: true, children: "Command" }),
1144
+ /* @__PURE__ */ jsx8(Text8, { children: command }),
1145
+ /* @__PURE__ */ jsx8(ToolBadge, { tool: resolvedTool })
1068
1146
  ] }),
1069
- /* @__PURE__ */ jsx6(
1147
+ /* @__PURE__ */ jsx8(
1070
1148
  TextPrompt,
1071
1149
  {
1072
- label: `Additional args for supabase ${command}?`,
1150
+ label: `Additional args for ${toolLabel} ${command}?`,
1073
1151
  placeholder: "e.g. list, pull, push (Enter to skip)",
1074
1152
  onSubmit: (extra) => {
1075
1153
  const extraArgs = extra.trim().split(" ").filter(Boolean);
@@ -1084,27 +1162,34 @@ function CommandArgs({
1084
1162
  }
1085
1163
  }
1086
1164
  ),
1087
- /* @__PURE__ */ jsx6(StatusBar, { hint: "Type args \xB7 Enter to continue \xB7 Esc to go back" })
1165
+ !panelMode && /* @__PURE__ */ jsx8(StatusBar, { hint: "Type args \xB7 Enter to continue \xB7 Esc to go back", width })
1088
1166
  ] });
1089
1167
  }
1168
+ const legacySuggestions = suggestions.map((s) => ({
1169
+ value: s.value,
1170
+ label: s.label,
1171
+ hint: s.hint,
1172
+ args: s.args
1173
+ }));
1090
1174
  const items = useMemo3(
1091
1175
  () => buildCommandArgItems({
1092
1176
  command,
1093
- suggestions,
1177
+ suggestions: legacySuggestions,
1094
1178
  pinnedRuns
1095
1179
  }),
1096
- [command, pinnedRuns, suggestions]
1180
+ [command, legacySuggestions, pinnedRuns]
1097
1181
  );
1098
- return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
1099
- /* @__PURE__ */ jsxs6(Box6, { marginBottom: 1, gap: 1, children: [
1100
- /* @__PURE__ */ jsx6(Text6, { color: inkColors.accent, bold: true, children: "Command" }),
1101
- /* @__PURE__ */ jsx6(Text6, { children: command })
1182
+ return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
1183
+ /* @__PURE__ */ jsxs7(Box7, { marginBottom: 1, gap: 1, children: [
1184
+ /* @__PURE__ */ jsx8(Text8, { color: inkColors.accent, bold: true, children: "Command" }),
1185
+ /* @__PURE__ */ jsx8(Text8, { children: command }),
1186
+ /* @__PURE__ */ jsx8(ToolBadge, { tool: resolvedTool })
1102
1187
  ] }),
1103
- pinFeedback && /* @__PURE__ */ jsx6(Box6, { marginBottom: 1, children: /* @__PURE__ */ jsxs6(Text6, { color: inkColors.accent, children: [
1188
+ pinFeedback && /* @__PURE__ */ jsx8(Box7, { marginBottom: 1, children: /* @__PURE__ */ jsxs7(Text8, { color: inkColors.accent, children: [
1104
1189
  "\u2713 ",
1105
1190
  pinFeedback
1106
1191
  ] }) }),
1107
- /* @__PURE__ */ jsx6(
1192
+ /* @__PURE__ */ jsx8(
1108
1193
  SelectList,
1109
1194
  {
1110
1195
  items,
@@ -1125,7 +1210,7 @@ function CommandArgs({
1125
1210
  const runCommand2 = getRunCommandFromArgsSelection(
1126
1211
  command,
1127
1212
  value,
1128
- suggestions
1213
+ legacySuggestions
1129
1214
  );
1130
1215
  if (runCommand2) {
1131
1216
  navigateWithExtraArgs(
@@ -1139,7 +1224,7 @@ function CommandArgs({
1139
1224
  const runCommand2 = getRunCommandFromArgsSelection(
1140
1225
  command,
1141
1226
  item.value,
1142
- suggestions
1227
+ legacySuggestions
1143
1228
  );
1144
1229
  if (!runCommand2) {
1145
1230
  return;
@@ -1152,56 +1237,103 @@ function CommandArgs({
1152
1237
  );
1153
1238
  },
1154
1239
  onCancel: onBack,
1155
- boxedSections: true
1240
+ boxedSections: true,
1241
+ width,
1242
+ isInputActive,
1243
+ arrowNavigation: panelMode
1156
1244
  }
1157
1245
  ),
1158
- /* @__PURE__ */ jsx6(StatusBar, { hint: "\u2191\u2193 navigate \xB7 Enter select \xB7 \u2192 pin run \xB7 Esc back" })
1246
+ !panelMode && /* @__PURE__ */ jsx8(StatusBar, { hint: "\u2191\u2193 navigate \xB7 Enter select \xB7 p pin \xB7 Esc back", width })
1159
1247
  ] });
1160
1248
  }
1161
1249
 
1162
1250
  // src/screens/CustomCommand.tsx
1163
- import { Box as Box7 } from "ink";
1164
- import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
1251
+ import { useState as useState7 } from "react";
1252
+ import { Box as Box8, Text as Text9 } from "ink";
1253
+ import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
1165
1254
  function CustomCommand({
1166
1255
  onNavigate,
1167
- onBack
1256
+ onBack,
1257
+ width = 80,
1258
+ panelMode = false,
1259
+ isInputActive = true
1168
1260
  }) {
1169
- return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
1170
- /* @__PURE__ */ jsx7(
1261
+ const [phase, setPhase] = useState7("tool-select");
1262
+ const [selectedTool, setSelectedTool] = useState7("supabase");
1263
+ if (phase === "tool-select") {
1264
+ return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
1265
+ /* @__PURE__ */ jsx9(Box8, { marginBottom: 1, children: /* @__PURE__ */ jsx9(Text9, { bold: true, color: inkColors.accent, children: "\u270F\uFE0F Custom Command" }) }),
1266
+ /* @__PURE__ */ jsx9(Text9, { dimColor: true, children: "Select tool:" }),
1267
+ /* @__PURE__ */ jsx9(
1268
+ SelectList,
1269
+ {
1270
+ items: [
1271
+ { value: "supabase", label: "Supabase CLI", hint: "supabase ..." },
1272
+ { value: "gh", label: "GitHub CLI", hint: "gh ..." },
1273
+ { value: "vercel", label: "Vercel CLI", hint: "vercel ..." },
1274
+ { value: "__back__", label: "\u2190 Back" }
1275
+ ],
1276
+ onSelect: (value) => {
1277
+ if (value === "__back__") {
1278
+ onBack();
1279
+ return;
1280
+ }
1281
+ setSelectedTool(value);
1282
+ setPhase("input");
1283
+ },
1284
+ onCancel: onBack,
1285
+ width,
1286
+ isInputActive,
1287
+ arrowNavigation: panelMode
1288
+ }
1289
+ ),
1290
+ !panelMode && /* @__PURE__ */ jsx9(StatusBar, { hint: "\u2191\u2193 navigate \xB7 Enter select \xB7 Esc back", width })
1291
+ ] });
1292
+ }
1293
+ return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
1294
+ /* @__PURE__ */ jsxs8(Box8, { marginBottom: 1, gap: 1, children: [
1295
+ /* @__PURE__ */ jsx9(Text9, { bold: true, color: inkColors.accent, children: "\u270F\uFE0F Custom Command" }),
1296
+ /* @__PURE__ */ jsxs8(Text9, { dimColor: true, children: [
1297
+ "(",
1298
+ selectedTool,
1299
+ ")"
1300
+ ] })
1301
+ ] }),
1302
+ /* @__PURE__ */ jsx9(
1171
1303
  TextPrompt,
1172
1304
  {
1173
- label: "Enter your Supabase command/flags:",
1174
- placeholder: "e.g. -v, status -o json, db pull",
1305
+ label: `Enter your ${selectedTool} command/flags:`,
1306
+ placeholder: `e.g. ${selectedTool === "supabase" ? "-v, status -o json, db pull" : selectedTool === "gh" ? "pr list, issue create" : "deploy, env ls"}`,
1175
1307
  validate: (val) => {
1176
1308
  if (!val || !val.trim()) return "Please enter a command";
1177
1309
  return void 0;
1178
1310
  },
1179
1311
  onSubmit: (value) => {
1180
1312
  const args = value.split(" ").filter(Boolean);
1181
- onNavigate("flag-selection", { args });
1313
+ onNavigate("flag-selection", { args, tool: selectedTool });
1182
1314
  },
1183
- onCancel: onBack
1315
+ onCancel: () => setPhase("tool-select")
1184
1316
  }
1185
1317
  ),
1186
- /* @__PURE__ */ jsx7(StatusBar, { hint: "Type a command \xB7 Enter to continue \xB7 Esc to go back" })
1318
+ !panelMode && /* @__PURE__ */ jsx9(StatusBar, { hint: "Type a command \xB7 Enter to continue \xB7 Esc to go back", width })
1187
1319
  ] });
1188
1320
  }
1189
1321
 
1190
1322
  // src/screens/FlagSelection.tsx
1191
- import { Box as Box9 } from "ink";
1323
+ import { Box as Box10 } from "ink";
1192
1324
 
1193
1325
  // src/components/FlagToggle.tsx
1194
- import { useState as useState6 } from "react";
1195
- import { Box as Box8, Text as Text7, useInput as useInput3 } from "ink";
1196
- import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
1326
+ import { useState as useState8 } from "react";
1327
+ import { Box as Box9, Text as Text10, useInput as useInput4 } from "ink";
1328
+ import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
1197
1329
  function FlagToggle({
1198
1330
  flags,
1199
1331
  onSubmit,
1200
1332
  onCancel
1201
1333
  }) {
1202
- const [cursor, setCursor] = useState6(0);
1203
- const [selected, setSelected] = useState6(/* @__PURE__ */ new Set());
1204
- useInput3((input2, key) => {
1334
+ const [cursor, setCursor] = useState8(0);
1335
+ const [selected, setSelected] = useState8(/* @__PURE__ */ new Set());
1336
+ useInput4((input2, key) => {
1205
1337
  if (key.upArrow || input2 === "k") {
1206
1338
  setCursor((prev) => prev > 0 ? prev - 1 : flags.length - 1);
1207
1339
  }
@@ -1227,106 +1359,99 @@ function FlagToggle({
1227
1359
  onCancel();
1228
1360
  }
1229
1361
  });
1230
- return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
1231
- /* @__PURE__ */ jsxs8(Box8, { marginBottom: 1, children: [
1232
- /* @__PURE__ */ jsx8(Text7, { bold: true, color: inkColors.accent, children: "\u2691 Global Flags" }),
1233
- /* @__PURE__ */ jsx8(Text7, { dimColor: true, children: " (Space to toggle, Enter to confirm)" })
1362
+ return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
1363
+ /* @__PURE__ */ jsxs9(Box9, { marginBottom: 1, children: [
1364
+ /* @__PURE__ */ jsx10(Text10, { bold: true, color: inkColors.accent, children: "\u2691 Global Flags" }),
1365
+ /* @__PURE__ */ jsx10(Text10, { dimColor: true, children: " (Space to toggle, Enter to confirm)" })
1234
1366
  ] }),
1235
1367
  flags.map((flag, i) => {
1236
1368
  const isActive = cursor === i;
1237
1369
  const isChecked = selected.has(flag.value);
1238
- return /* @__PURE__ */ jsxs8(Box8, { gap: 1, children: [
1239
- /* @__PURE__ */ jsx8(Text7, { color: isActive ? inkColors.accent : void 0, children: isActive ? "\u276F" : " " }),
1240
- /* @__PURE__ */ jsx8(
1241
- Text7,
1370
+ return /* @__PURE__ */ jsxs9(Box9, { gap: 1, children: [
1371
+ /* @__PURE__ */ jsx10(Text10, { color: isActive ? inkColors.accent : void 0, children: isActive ? "\u276F" : " " }),
1372
+ /* @__PURE__ */ jsx10(
1373
+ Text10,
1242
1374
  {
1243
1375
  color: isChecked || isActive ? inkColors.accent : void 0,
1244
1376
  bold: isChecked,
1245
1377
  children: isChecked ? "\u25C9" : "\u25CB"
1246
1378
  }
1247
1379
  ),
1248
- /* @__PURE__ */ jsx8(
1249
- Text7,
1380
+ /* @__PURE__ */ jsx10(
1381
+ Text10,
1250
1382
  {
1251
1383
  color: isActive ? inkColors.accent : void 0,
1252
1384
  bold: isActive,
1253
1385
  children: flag.label
1254
1386
  }
1255
1387
  ),
1256
- flag.hint && /* @__PURE__ */ jsx8(Text7, { dimColor: true, children: flag.hint })
1388
+ flag.hint && /* @__PURE__ */ jsx10(Text10, { dimColor: true, children: flag.hint })
1257
1389
  ] }, flag.value);
1258
1390
  }),
1259
- /* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: /* @__PURE__ */ jsx8(Text7, { dimColor: true, children: selected.size > 0 ? `${selected.size} flag${selected.size > 1 ? "s" : ""} selected` : "No flags selected (Enter to skip)" }) })
1391
+ /* @__PURE__ */ jsx10(Box9, { marginTop: 1, children: /* @__PURE__ */ jsx10(Text10, { dimColor: true, children: selected.size > 0 ? `${selected.size} flag${selected.size > 1 ? "s" : ""} selected` : "No flags selected (Enter to skip)" }) })
1260
1392
  ] });
1261
1393
  }
1262
1394
 
1263
- // src/data/flags.ts
1264
- var globalFlags = [
1265
- { value: "--debug", label: "--debug", hint: "Output debug logs to stderr" },
1266
- { value: "--yes", label: "--yes", hint: "Answer yes to all prompts" },
1267
- {
1268
- value: "--create-ticket",
1269
- label: "--create-ticket",
1270
- hint: "Create support ticket on error"
1271
- },
1272
- {
1273
- value: "--experimental",
1274
- label: "--experimental",
1275
- hint: "Enable experimental features"
1276
- }
1277
- ];
1278
-
1279
1395
  // src/screens/FlagSelection.tsx
1280
- import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
1396
+ import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
1281
1397
  function FlagSelection({
1282
1398
  args,
1399
+ tool = "supabase",
1283
1400
  onNavigate,
1284
- onBack
1401
+ onBack,
1402
+ width = 80,
1403
+ panelMode = false,
1404
+ isInputActive = true
1285
1405
  }) {
1286
- return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
1287
- /* @__PURE__ */ jsx9(
1406
+ const flags = getFlagsForTool(tool);
1407
+ if (flags.length === 0) {
1408
+ onNavigate("confirm-execute", { args, tool });
1409
+ return /* @__PURE__ */ jsx11(Box10, {});
1410
+ }
1411
+ return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", children: [
1412
+ /* @__PURE__ */ jsx11(
1288
1413
  FlagToggle,
1289
1414
  {
1290
- flags: globalFlags,
1415
+ flags,
1291
1416
  onSubmit: (selectedFlags) => {
1292
1417
  const finalArgs = selectedFlags.length > 0 ? [...args, ...selectedFlags] : args;
1293
- onNavigate("confirm-execute", { args: finalArgs });
1418
+ onNavigate("confirm-execute", { args: finalArgs, tool });
1294
1419
  },
1295
1420
  onCancel: onBack
1296
1421
  }
1297
1422
  ),
1298
- /* @__PURE__ */ jsx9(StatusBar, { hint: "Space toggle \xB7 Enter confirm \xB7 Esc back" })
1423
+ !panelMode && /* @__PURE__ */ jsx11(StatusBar, { hint: "Space toggle \xB7 Enter confirm \xB7 Esc back", width })
1299
1424
  ] });
1300
1425
  }
1301
1426
 
1302
1427
  // src/screens/CommandExecution.tsx
1303
- import { useState as useState8, useEffect as useEffect4 } from "react";
1304
- import { Box as Box12, Text as Text11 } from "ink";
1428
+ import { useState as useState10, useEffect as useEffect5 } from "react";
1429
+ import { Box as Box12, Text as Text14 } from "ink";
1305
1430
 
1306
1431
  // src/components/Spinner.tsx
1307
- import { Text as Text8 } from "ink";
1432
+ import { Text as Text11 } from "ink";
1308
1433
  import InkSpinner from "ink-spinner";
1309
- import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
1434
+ import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
1310
1435
  function Spinner({
1311
1436
  label = "Running...",
1312
1437
  color = inkColors.accent
1313
1438
  }) {
1314
- return /* @__PURE__ */ jsxs10(Text8, { children: [
1315
- /* @__PURE__ */ jsx10(Text8, { color, children: /* @__PURE__ */ jsx10(InkSpinner, { type: "dots" }) }),
1439
+ return /* @__PURE__ */ jsxs11(Text11, { children: [
1440
+ /* @__PURE__ */ jsx12(Text11, { color, children: /* @__PURE__ */ jsx12(InkSpinner, { type: "dots" }) }),
1316
1441
  " ",
1317
- /* @__PURE__ */ jsx10(Text8, { children: label })
1442
+ /* @__PURE__ */ jsx12(Text11, { children: label })
1318
1443
  ] });
1319
1444
  }
1320
1445
 
1321
1446
  // src/components/ConfirmPrompt.tsx
1322
- import { Box as Box10, Text as Text9, useInput as useInput4 } from "ink";
1323
- import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
1447
+ import { Box as Box11, Text as Text12, useInput as useInput5 } from "ink";
1448
+ import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
1324
1449
  function ConfirmPrompt({
1325
1450
  message,
1326
1451
  defaultValue = true,
1327
1452
  onConfirm
1328
1453
  }) {
1329
- useInput4((input2) => {
1454
+ useInput5((input2) => {
1330
1455
  if (input2 === "y" || input2 === "Y") {
1331
1456
  onConfirm(true);
1332
1457
  } else if (input2 === "n" || input2 === "N") {
@@ -1335,26 +1460,27 @@ function ConfirmPrompt({
1335
1460
  onConfirm(defaultValue);
1336
1461
  }
1337
1462
  });
1338
- return /* @__PURE__ */ jsxs11(Box10, { gap: 1, children: [
1339
- /* @__PURE__ */ jsx11(Text9, { color: inkColors.accent, bold: true, children: "?" }),
1340
- /* @__PURE__ */ jsx11(Text9, { children: message }),
1341
- /* @__PURE__ */ jsx11(Text9, { dimColor: true, children: defaultValue ? "(Y/n)" : "(y/N)" })
1463
+ return /* @__PURE__ */ jsxs12(Box11, { gap: 1, children: [
1464
+ /* @__PURE__ */ jsx13(Text12, { color: inkColors.accent, bold: true, children: "?" }),
1465
+ /* @__PURE__ */ jsx13(Text12, { children: message }),
1466
+ /* @__PURE__ */ jsx13(Text12, { dimColor: true, children: defaultValue ? "(Y/n)" : "(y/N)" })
1342
1467
  ] });
1343
1468
  }
1344
1469
 
1345
1470
  // src/components/Divider.tsx
1346
- import { Text as Text10 } from "ink";
1347
- import { jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
1471
+ import { Text as Text13 } from "ink";
1472
+ import { jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
1348
1473
  function Divider({
1349
1474
  label,
1350
- width = 50
1475
+ width
1351
1476
  }) {
1477
+ const effectiveWidth = width ?? (process.stdout.columns ?? 50);
1352
1478
  if (label) {
1353
1479
  const labelLen = label.length + 2;
1354
- const sideLen = Math.max(2, Math.floor((width - labelLen) / 2));
1480
+ const sideLen = Math.max(2, Math.floor((effectiveWidth - labelLen) / 2));
1355
1481
  const left = "\u2500".repeat(sideLen);
1356
1482
  const right = "\u2500".repeat(sideLen);
1357
- return /* @__PURE__ */ jsxs12(Text10, { dimColor: true, children: [
1483
+ return /* @__PURE__ */ jsxs13(Text13, { dimColor: true, children: [
1358
1484
  left,
1359
1485
  " ",
1360
1486
  label,
@@ -1362,112 +1488,30 @@ function Divider({
1362
1488
  right
1363
1489
  ] });
1364
1490
  }
1365
- return /* @__PURE__ */ jsx12(Text10, { dimColor: true, children: "\u2500".repeat(width) });
1366
- }
1367
-
1368
- // src/hooks/useCommand.ts
1369
- import { useState as useState7, useCallback as useCallback2 } from "react";
1370
-
1371
- // src/lib/runner.ts
1372
- import { spawn } from "child_process";
1373
- import { existsSync } from "fs";
1374
- import { delimiter, dirname, join, resolve } from "path";
1375
- function getSupabaseBinaryCandidates() {
1376
- if (process.platform === "win32") {
1377
- return ["supabase.cmd", "supabase.exe", "supabase"];
1378
- }
1379
- return ["supabase"];
1380
- }
1381
- function hasLocalSupabaseBinary(binDir) {
1382
- return getSupabaseBinaryCandidates().some(
1383
- (candidate) => existsSync(join(binDir, candidate))
1384
- );
1385
- }
1386
- function getPathEnvKey(env) {
1387
- return Object.keys(env).find((key) => key.toLowerCase() === "path") ?? "PATH";
1388
- }
1389
- function findLocalSupabaseBinDir(startDir = process.cwd()) {
1390
- let currentDir = resolve(startDir);
1391
- while (true) {
1392
- const binDir = join(currentDir, "node_modules", ".bin");
1393
- if (hasLocalSupabaseBinary(binDir)) {
1394
- return binDir;
1395
- }
1396
- const parentDir = dirname(currentDir);
1397
- if (parentDir === currentDir) {
1398
- return void 0;
1399
- }
1400
- currentDir = parentDir;
1401
- }
1402
- }
1403
- function resolveSupabaseCommand(startDir = process.cwd(), env = process.env) {
1404
- const localBinDir = findLocalSupabaseBinDir(startDir);
1405
- if (!localBinDir) {
1406
- return {
1407
- command: "supabase",
1408
- env: { ...env },
1409
- source: "path"
1410
- };
1411
- }
1412
- const pathKey = getPathEnvKey(env);
1413
- const currentPath = env[pathKey];
1414
- return {
1415
- command: "supabase",
1416
- env: {
1417
- ...env,
1418
- [pathKey]: currentPath ? `${localBinDir}${delimiter}${currentPath}` : localBinDir
1419
- },
1420
- source: "repository",
1421
- localBinDir
1422
- };
1423
- }
1424
- async function runCommand(execution, args, cwd = process.cwd()) {
1425
- return new Promise((resolve4) => {
1426
- let stdout = "";
1427
- let stderr = "";
1428
- const resolvedExecution = typeof execution === "string" ? { command: execution } : execution;
1429
- const child = spawn(resolvedExecution.command, args, {
1430
- cwd,
1431
- env: resolvedExecution.env,
1432
- shell: true,
1433
- stdio: ["inherit", "pipe", "pipe"]
1434
- });
1435
- child.stdout?.on("data", (data) => {
1436
- const text = data.toString();
1437
- stdout += text;
1438
- process.stdout.write(text);
1439
- });
1440
- child.stderr?.on("data", (data) => {
1441
- const text = data.toString();
1442
- stderr += text;
1443
- process.stderr.write(text);
1444
- });
1445
- child.on("error", (err) => {
1446
- resolve4({
1447
- exitCode: null,
1448
- signal: null,
1449
- stdout,
1450
- stderr,
1451
- spawnError: err.message
1452
- });
1453
- });
1454
- child.on("exit", (code, signal) => {
1455
- resolve4({ exitCode: code, signal, stdout, stderr });
1456
- });
1457
- });
1458
- }
1459
- async function runSupabaseCommand(args, cwd = process.cwd()) {
1460
- return runCommand(resolveSupabaseCommand(cwd), args, cwd);
1491
+ return /* @__PURE__ */ jsx14(Text13, { dimColor: true, children: "\u2500".repeat(effectiveWidth) });
1461
1492
  }
1462
1493
 
1463
1494
  // src/hooks/useCommand.ts
1495
+ import { useState as useState9, useCallback as useCallback2 } from "react";
1464
1496
  function useCommand(execution = "supabase", cwd = process.cwd()) {
1465
- const [status, setStatus] = useState7("idle");
1466
- const [result, setResult] = useState7(null);
1497
+ const [status, setStatus] = useState9("idle");
1498
+ const [result, setResult] = useState9(null);
1467
1499
  const run = useCallback2(async (args) => {
1468
1500
  setStatus("running");
1469
1501
  setResult(null);
1470
- const res = execution === "supabase" ? await runSupabaseCommand(args, cwd) : await runCommand(execution, args, cwd);
1502
+ let resolvedExecution;
1503
+ if (typeof execution === "string") {
1504
+ const toolIds2 = ["supabase", "gh", "vercel"];
1505
+ if (toolIds2.includes(execution)) {
1506
+ const resolved = resolveToolCommand(execution, cwd);
1507
+ resolvedExecution = { command: resolved.command, env: resolved.env };
1508
+ } else {
1509
+ resolvedExecution = execution;
1510
+ }
1511
+ } else {
1512
+ resolvedExecution = execution;
1513
+ }
1514
+ const res = await runCommand(resolvedExecution, args, cwd);
1471
1515
  setResult(res);
1472
1516
  if (res.spawnError || res.exitCode !== null && res.exitCode !== 0) {
1473
1517
  setStatus("error");
@@ -1484,43 +1528,41 @@ function useCommand(execution = "supabase", cwd = process.cwd()) {
1484
1528
  }
1485
1529
 
1486
1530
  // src/lib/clipboard.ts
1487
- import { spawn as spawn2, exec } from "child_process";
1488
- async function openInBrowser(url) {
1489
- return new Promise((resolve4) => {
1490
- const cmd = process.platform === "darwin" ? `open "${url}"` : process.platform === "win32" ? `start "${url}"` : `xdg-open "${url}"`;
1491
- exec(cmd, () => resolve4());
1492
- });
1493
- }
1531
+ import { spawn, exec } from "child_process";
1494
1532
  async function copyToClipboard(text) {
1495
- return new Promise((resolve4) => {
1533
+ return new Promise((resolve2) => {
1496
1534
  const cmd = process.platform === "darwin" ? "pbcopy" : process.platform === "win32" ? "clip" : "xclip -selection clipboard";
1497
- const child = spawn2(cmd, [], { shell: true });
1535
+ const child = spawn(cmd, [], { shell: true });
1498
1536
  child.stdin?.write(text);
1499
1537
  child.stdin?.end();
1500
- child.on("exit", () => resolve4());
1501
- child.on("error", () => resolve4());
1538
+ child.on("exit", () => resolve2());
1539
+ child.on("error", () => resolve2());
1502
1540
  });
1503
1541
  }
1504
1542
 
1505
1543
  // src/screens/CommandExecution.tsx
1506
- import { jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
1544
+ import { jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
1507
1545
  function CommandExecution({
1508
1546
  args: initialArgs,
1547
+ tool = "supabase",
1509
1548
  onBack,
1510
- onExit
1549
+ onExit,
1550
+ width = 80,
1551
+ panelMode = false,
1552
+ isInputActive = true
1511
1553
  }) {
1512
- const [phase, setPhase] = useState8("confirm");
1513
- const [currentArgs, setCurrentArgs] = useState8(initialArgs);
1514
- const [pinMessage, setPinMessage] = useState8();
1515
- const { status, result, run, reset } = useCommand();
1516
- const cmdDisplay = `supabase ${currentArgs.join(" ")}`;
1554
+ const [phase, setPhase] = useState10("confirm");
1555
+ const [currentArgs, setCurrentArgs] = useState10(initialArgs);
1556
+ const [pinMessage, setPinMessage] = useState10();
1557
+ const { status, result, run, reset } = useCommand(tool);
1558
+ const cmdDisplay = `${tool} ${currentArgs.join(" ")}`;
1517
1559
  const runCommand2 = currentArgs.join(" ");
1518
- useEffect4(() => {
1560
+ useEffect5(() => {
1519
1561
  if (phase === "running" && status === "idle") {
1520
1562
  run(currentArgs);
1521
1563
  }
1522
1564
  }, [phase, status, run, currentArgs]);
1523
- useEffect4(() => {
1565
+ useEffect5(() => {
1524
1566
  if (phase === "running" && status === "success") {
1525
1567
  if (isPinnedRun(runCommand2)) {
1526
1568
  setPhase("success");
@@ -1533,7 +1575,7 @@ function CommandExecution({
1533
1575
  }
1534
1576
  }, [phase, runCommand2, status]);
1535
1577
  if (phase === "confirm") {
1536
- return /* @__PURE__ */ jsx13(Box12, { flexDirection: "column", children: /* @__PURE__ */ jsx13(
1578
+ return /* @__PURE__ */ jsx15(Box12, { flexDirection: "column", children: /* @__PURE__ */ jsx15(
1537
1579
  ConfirmPrompt,
1538
1580
  {
1539
1581
  message: `Execute ${cmdDisplay}?`,
@@ -1549,25 +1591,26 @@ function CommandExecution({
1549
1591
  ) });
1550
1592
  }
1551
1593
  if (phase === "running") {
1552
- return /* @__PURE__ */ jsxs13(Box12, { flexDirection: "column", children: [
1553
- /* @__PURE__ */ jsx13(Divider, {}),
1554
- /* @__PURE__ */ jsxs13(Box12, { marginY: 1, gap: 1, children: [
1555
- /* @__PURE__ */ jsx13(Text11, { color: inkColors.accent, bold: true, children: "\u25B6" }),
1556
- /* @__PURE__ */ jsx13(Text11, { dimColor: true, children: "Running:" }),
1557
- /* @__PURE__ */ jsx13(Text11, { children: cmdDisplay })
1594
+ return /* @__PURE__ */ jsxs14(Box12, { flexDirection: "column", children: [
1595
+ /* @__PURE__ */ jsx15(Divider, { width }),
1596
+ /* @__PURE__ */ jsxs14(Box12, { marginY: 1, gap: 1, children: [
1597
+ /* @__PURE__ */ jsx15(Text14, { color: inkColors.accent, bold: true, children: "\u25B6" }),
1598
+ /* @__PURE__ */ jsx15(Text14, { dimColor: true, children: "Running:" }),
1599
+ /* @__PURE__ */ jsx15(Text14, { children: cmdDisplay }),
1600
+ /* @__PURE__ */ jsx15(ToolBadge, { tool })
1558
1601
  ] }),
1559
- /* @__PURE__ */ jsx13(Divider, {}),
1560
- /* @__PURE__ */ jsx13(Box12, { marginTop: 1, children: /* @__PURE__ */ jsx13(Spinner, { label: `Executing ${cmdDisplay}...` }) })
1602
+ /* @__PURE__ */ jsx15(Divider, { width }),
1603
+ /* @__PURE__ */ jsx15(Box12, { marginTop: 1, children: /* @__PURE__ */ jsx15(Spinner, { label: `Executing ${cmdDisplay}...` }) })
1561
1604
  ] });
1562
1605
  }
1563
1606
  if (phase === "success-pin-offer") {
1564
- return /* @__PURE__ */ jsxs13(Box12, { flexDirection: "column", children: [
1565
- /* @__PURE__ */ jsx13(Divider, {}),
1566
- /* @__PURE__ */ jsxs13(Box12, { marginY: 1, gap: 1, children: [
1567
- /* @__PURE__ */ jsx13(Text11, { color: inkColors.accent, bold: true, children: "\u2713" }),
1568
- /* @__PURE__ */ jsx13(Text11, { color: inkColors.accent, bold: true, children: "Command completed successfully!" })
1607
+ return /* @__PURE__ */ jsxs14(Box12, { flexDirection: "column", children: [
1608
+ /* @__PURE__ */ jsx15(Divider, { width }),
1609
+ /* @__PURE__ */ jsxs14(Box12, { marginY: 1, gap: 1, children: [
1610
+ /* @__PURE__ */ jsx15(Text14, { color: inkColors.accent, bold: true, children: "\u2713" }),
1611
+ /* @__PURE__ */ jsx15(Text14, { color: inkColors.accent, bold: true, children: "Command completed successfully!" })
1569
1612
  ] }),
1570
- /* @__PURE__ */ jsx13(
1613
+ /* @__PURE__ */ jsx15(
1571
1614
  ConfirmPrompt,
1572
1615
  {
1573
1616
  message: "Pin this exact command?",
@@ -1584,19 +1627,22 @@ function CommandExecution({
1584
1627
  ] });
1585
1628
  }
1586
1629
  if (phase === "success") {
1587
- return /* @__PURE__ */ jsxs13(Box12, { flexDirection: "column", children: [
1588
- /* @__PURE__ */ jsx13(Divider, {}),
1589
- /* @__PURE__ */ jsxs13(Box12, { marginY: 1, gap: 1, children: [
1590
- /* @__PURE__ */ jsx13(Text11, { color: inkColors.accent, bold: true, children: "\u2713" }),
1591
- /* @__PURE__ */ jsx13(Text11, { color: inkColors.accent, bold: true, children: "Command completed successfully!" })
1630
+ return /* @__PURE__ */ jsxs14(Box12, { flexDirection: "column", children: [
1631
+ /* @__PURE__ */ jsx15(Divider, { width }),
1632
+ /* @__PURE__ */ jsxs14(Box12, { marginY: 1, gap: 1, children: [
1633
+ /* @__PURE__ */ jsx15(Text14, { color: inkColors.accent, bold: true, children: "\u2713" }),
1634
+ /* @__PURE__ */ jsx15(Text14, { color: inkColors.accent, bold: true, children: "Command completed successfully!" })
1592
1635
  ] }),
1593
- pinMessage && /* @__PURE__ */ jsx13(Box12, { marginBottom: 1, children: /* @__PURE__ */ jsx13(Text11, { color: inkColors.accent, children: pinMessage }) }),
1594
- /* @__PURE__ */ jsx13(
1636
+ pinMessage && /* @__PURE__ */ jsx15(Box12, { marginBottom: 1, children: /* @__PURE__ */ jsx15(Text14, { color: inkColors.accent, children: pinMessage }) }),
1637
+ /* @__PURE__ */ jsx15(
1595
1638
  SelectList,
1596
1639
  {
1597
1640
  items: [{ value: "__back__", label: "\u2190 Back to menu" }],
1598
1641
  onSelect: onBack,
1599
- onCancel: onBack
1642
+ onCancel: onBack,
1643
+ width,
1644
+ isInputActive,
1645
+ arrowNavigation: panelMode
1600
1646
  }
1601
1647
  )
1602
1648
  ] });
@@ -1614,11 +1660,6 @@ function CommandExecution({
1614
1660
  }
1615
1661
  }
1616
1662
  errorItems.push(
1617
- {
1618
- value: "docs",
1619
- label: "\u{1F4D6} Open Supabase CLI docs",
1620
- hint: "Opens in browser"
1621
- },
1622
1663
  {
1623
1664
  value: "copy",
1624
1665
  label: "\u{1F4CB} Copy command to clipboard"
@@ -1626,44 +1667,42 @@ function CommandExecution({
1626
1667
  { value: "menu", label: "\u2190 Return to main menu" },
1627
1668
  { value: "exit", label: "\u{1F6AA} Exit Polter" }
1628
1669
  );
1629
- return /* @__PURE__ */ jsxs13(Box12, { flexDirection: "column", children: [
1630
- /* @__PURE__ */ jsx13(Divider, {}),
1631
- result?.spawnError ? /* @__PURE__ */ jsxs13(Box12, { flexDirection: "column", marginY: 1, children: [
1632
- /* @__PURE__ */ jsxs13(Box12, { gap: 1, children: [
1633
- /* @__PURE__ */ jsx13(Text11, { color: "red", bold: true, children: "\u2717" }),
1634
- /* @__PURE__ */ jsx13(Text11, { color: "red", bold: true, children: "Failed to start command" })
1670
+ return /* @__PURE__ */ jsxs14(Box12, { flexDirection: "column", children: [
1671
+ /* @__PURE__ */ jsx15(Divider, { width }),
1672
+ result?.spawnError ? /* @__PURE__ */ jsxs14(Box12, { flexDirection: "column", marginY: 1, children: [
1673
+ /* @__PURE__ */ jsxs14(Box12, { gap: 1, children: [
1674
+ /* @__PURE__ */ jsx15(Text14, { color: "red", bold: true, children: "\u2717" }),
1675
+ /* @__PURE__ */ jsx15(Text14, { color: "red", bold: true, children: "Failed to start command" })
1635
1676
  ] }),
1636
- /* @__PURE__ */ jsxs13(Box12, { marginLeft: 2, marginTop: 1, children: [
1637
- /* @__PURE__ */ jsx13(Text11, { dimColor: true, children: "Error: " }),
1638
- /* @__PURE__ */ jsx13(Text11, { color: "red", children: result.spawnError })
1677
+ /* @__PURE__ */ jsxs14(Box12, { marginLeft: 2, marginTop: 1, children: [
1678
+ /* @__PURE__ */ jsx15(Text14, { dimColor: true, children: "Error: " }),
1679
+ /* @__PURE__ */ jsx15(Text14, { color: "red", children: result.spawnError })
1639
1680
  ] }),
1640
- (result.spawnError.includes("ENOENT") || result.spawnError.includes("not found")) && /* @__PURE__ */ jsxs13(Box12, { flexDirection: "column", marginLeft: 2, marginTop: 1, children: [
1641
- /* @__PURE__ */ jsx13(Text11, { color: inkColors.accent, bold: true, children: "\u{1F4A1} Supabase CLI not found in this repository or PATH" }),
1642
- /* @__PURE__ */ jsxs13(Box12, { gap: 1, children: [
1643
- /* @__PURE__ */ jsx13(Text11, { dimColor: true, children: "Install it:" }),
1644
- /* @__PURE__ */ jsx13(Text11, { color: inkColors.accent, children: "https://supabase.com/docs/guides/cli" })
1645
- ] })
1646
- ] })
1647
- ] }) : /* @__PURE__ */ jsxs13(Box12, { flexDirection: "column", marginY: 1, children: [
1648
- /* @__PURE__ */ jsxs13(Box12, { gap: 1, children: [
1649
- /* @__PURE__ */ jsx13(Text11, { color: "red", bold: true, children: "\u2717" }),
1650
- /* @__PURE__ */ jsx13(Text11, { color: "red", children: "Command failed " }),
1651
- /* @__PURE__ */ jsx13(Text11, { dimColor: true, children: "(exit code " }),
1652
- /* @__PURE__ */ jsx13(Text11, { color: "red", bold: true, children: String(result?.exitCode) }),
1653
- /* @__PURE__ */ jsx13(Text11, { dimColor: true, children: ")" })
1681
+ (result.spawnError.includes("ENOENT") || result.spawnError.includes("not found")) && /* @__PURE__ */ jsx15(Box12, { flexDirection: "column", marginLeft: 2, marginTop: 1, children: /* @__PURE__ */ jsxs14(Text14, { color: inkColors.accent, bold: true, children: [
1682
+ "\u{1F4A1} ",
1683
+ tool,
1684
+ " CLI not found in this repository or PATH"
1685
+ ] }) })
1686
+ ] }) : /* @__PURE__ */ jsxs14(Box12, { flexDirection: "column", marginY: 1, children: [
1687
+ /* @__PURE__ */ jsxs14(Box12, { gap: 1, children: [
1688
+ /* @__PURE__ */ jsx15(Text14, { color: "red", bold: true, children: "\u2717" }),
1689
+ /* @__PURE__ */ jsx15(Text14, { color: "red", children: "Command failed " }),
1690
+ /* @__PURE__ */ jsx15(Text14, { dimColor: true, children: "(exit code " }),
1691
+ /* @__PURE__ */ jsx15(Text14, { color: "red", bold: true, children: String(result?.exitCode) }),
1692
+ /* @__PURE__ */ jsx15(Text14, { dimColor: true, children: ")" })
1654
1693
  ] }),
1655
- /* @__PURE__ */ jsxs13(Box12, { marginLeft: 2, marginTop: 1, children: [
1656
- /* @__PURE__ */ jsx13(Text11, { dimColor: true, children: "Command: " }),
1657
- /* @__PURE__ */ jsx13(Text11, { children: cmdDisplay })
1694
+ /* @__PURE__ */ jsxs14(Box12, { marginLeft: 2, marginTop: 1, children: [
1695
+ /* @__PURE__ */ jsx15(Text14, { dimColor: true, children: "Command: " }),
1696
+ /* @__PURE__ */ jsx15(Text14, { children: cmdDisplay })
1658
1697
  ] }),
1659
- !hasDebug && /* @__PURE__ */ jsxs13(Box12, { marginLeft: 2, marginTop: 1, gap: 1, children: [
1660
- /* @__PURE__ */ jsx13(Text11, { dimColor: true, children: "\u{1F4A1} Tip: retry with" }),
1661
- /* @__PURE__ */ jsx13(Text11, { color: inkColors.accent, children: "--debug" }),
1662
- /* @__PURE__ */ jsx13(Text11, { dimColor: true, children: "to see detailed logs" })
1698
+ !hasDebug && /* @__PURE__ */ jsxs14(Box12, { marginLeft: 2, marginTop: 1, gap: 1, children: [
1699
+ /* @__PURE__ */ jsx15(Text14, { dimColor: true, children: "\u{1F4A1} Tip: retry with" }),
1700
+ /* @__PURE__ */ jsx15(Text14, { color: inkColors.accent, children: "--debug" }),
1701
+ /* @__PURE__ */ jsx15(Text14, { dimColor: true, children: "to see detailed logs" })
1663
1702
  ] })
1664
1703
  ] }),
1665
- /* @__PURE__ */ jsx13(Box12, { marginTop: 1, marginBottom: 1, children: /* @__PURE__ */ jsx13(Text11, { bold: true, children: "What would you like to do?" }) }),
1666
- /* @__PURE__ */ jsx13(
1704
+ /* @__PURE__ */ jsx15(Box12, { marginTop: 1, marginBottom: 1, children: /* @__PURE__ */ jsx15(Text14, { bold: true, children: "What would you like to do?" }) }),
1705
+ /* @__PURE__ */ jsx15(
1667
1706
  SelectList,
1668
1707
  {
1669
1708
  items: errorItems,
@@ -1682,9 +1721,6 @@ function CommandExecution({
1682
1721
  setPhase("running");
1683
1722
  break;
1684
1723
  }
1685
- case "docs":
1686
- await openInBrowser("https://supabase.com/docs/guides/cli");
1687
- break;
1688
1724
  case "copy":
1689
1725
  await copyToClipboard(cmdDisplay);
1690
1726
  break;
@@ -1696,36 +1732,20 @@ function CommandExecution({
1696
1732
  break;
1697
1733
  }
1698
1734
  },
1699
- onCancel: onBack
1735
+ onCancel: onBack,
1736
+ width,
1737
+ isInputActive,
1738
+ arrowNavigation: panelMode
1700
1739
  }
1701
1740
  ),
1702
- /* @__PURE__ */ jsx13(StatusBar, {})
1741
+ !panelMode && /* @__PURE__ */ jsx15(StatusBar, { width })
1703
1742
  ] });
1704
1743
  }
1705
1744
 
1706
1745
  // src/screens/SelfUpdate.tsx
1707
- import { useEffect as useEffect5, useState as useState9 } from "react";
1708
- import { Box as Box13, Text as Text12 } from "ink";
1709
-
1710
- // src/lib/packageRoot.ts
1711
- import { existsSync as existsSync2 } from "fs";
1712
- import { dirname as dirname2, join as join2, resolve as resolve2 } from "path";
1713
- function findNearestPackageRoot(startDir = process.cwd()) {
1714
- let currentDir = resolve2(startDir);
1715
- while (true) {
1716
- if (existsSync2(join2(currentDir, "package.json"))) {
1717
- return currentDir;
1718
- }
1719
- const parentDir = dirname2(currentDir);
1720
- if (parentDir === currentDir) {
1721
- return void 0;
1722
- }
1723
- currentDir = parentDir;
1724
- }
1725
- }
1726
-
1727
- // src/screens/SelfUpdate.tsx
1728
- import { jsx as jsx14, jsxs as jsxs14 } from "react/jsx-runtime";
1746
+ import { useEffect as useEffect6, useState as useState11 } from "react";
1747
+ import { Box as Box13, Text as Text15 } from "ink";
1748
+ import { jsx as jsx16, jsxs as jsxs15 } from "react/jsx-runtime";
1729
1749
  var packageName = "@polterware/polter";
1730
1750
  var globalUpdateArgs = ["install", "-g", `${packageName}@latest`];
1731
1751
  var repositoryUpdateArgs = ["install", "-D", `${packageName}@latest`];
@@ -1734,25 +1754,28 @@ function getUpdateArgs(target) {
1734
1754
  }
1735
1755
  function SelfUpdate({
1736
1756
  onBack,
1737
- onExit
1757
+ onExit,
1758
+ width = 80,
1759
+ panelMode = false,
1760
+ isInputActive = true
1738
1761
  }) {
1739
1762
  const repositoryRoot = findNearestPackageRoot();
1740
- const [target, setTarget] = useState9(
1763
+ const [target, setTarget] = useState11(
1741
1764
  repositoryRoot ? "repository" : "global"
1742
1765
  );
1743
- const [phase, setPhase] = useState9(
1766
+ const [phase, setPhase] = useState11(
1744
1767
  repositoryRoot ? "target" : "confirm"
1745
1768
  );
1746
1769
  const updateArgs = getUpdateArgs(target);
1747
1770
  const updateDisplay = `npm ${updateArgs.join(" ")}`;
1748
1771
  const updateCwd = target === "repository" && repositoryRoot ? repositoryRoot : process.cwd();
1749
1772
  const { status, result, run, reset } = useCommand("npm", updateCwd);
1750
- useEffect5(() => {
1773
+ useEffect6(() => {
1751
1774
  if (phase === "running" && status === "idle") {
1752
1775
  run(updateArgs);
1753
1776
  }
1754
1777
  }, [phase, run, status, updateArgs]);
1755
- useEffect5(() => {
1778
+ useEffect6(() => {
1756
1779
  if (phase === "running" && status === "success") {
1757
1780
  setPhase("success");
1758
1781
  }
@@ -1761,9 +1784,9 @@ function SelfUpdate({
1761
1784
  }
1762
1785
  }, [phase, status]);
1763
1786
  if (phase === "target") {
1764
- return /* @__PURE__ */ jsxs14(Box13, { flexDirection: "column", children: [
1765
- /* @__PURE__ */ jsx14(Box13, { marginBottom: 1, children: /* @__PURE__ */ jsx14(Text12, { bold: true, children: "Choose where to update Polter." }) }),
1766
- /* @__PURE__ */ jsx14(
1787
+ return /* @__PURE__ */ jsxs15(Box13, { flexDirection: "column", children: [
1788
+ /* @__PURE__ */ jsx16(Box13, { marginBottom: 1, children: /* @__PURE__ */ jsx16(Text15, { bold: true, children: "Choose where to update Polter." }) }),
1789
+ /* @__PURE__ */ jsx16(
1767
1790
  SelectList,
1768
1791
  {
1769
1792
  items: [
@@ -1788,18 +1811,21 @@ function SelfUpdate({
1788
1811
  reset();
1789
1812
  setPhase("confirm");
1790
1813
  },
1791
- onCancel: onBack
1814
+ onCancel: onBack,
1815
+ width,
1816
+ isInputActive,
1817
+ arrowNavigation: panelMode
1792
1818
  }
1793
1819
  ),
1794
- repositoryRoot && /* @__PURE__ */ jsx14(Box13, { marginTop: 1, marginLeft: 2, children: /* @__PURE__ */ jsxs14(Text12, { dimColor: true, children: [
1820
+ repositoryRoot && /* @__PURE__ */ jsx16(Box13, { marginTop: 1, marginLeft: 2, children: /* @__PURE__ */ jsxs15(Text15, { dimColor: true, children: [
1795
1821
  "Repository root: ",
1796
1822
  repositoryRoot
1797
1823
  ] }) })
1798
1824
  ] });
1799
1825
  }
1800
1826
  if (phase === "confirm") {
1801
- return /* @__PURE__ */ jsxs14(Box13, { flexDirection: "column", children: [
1802
- /* @__PURE__ */ jsx14(
1827
+ return /* @__PURE__ */ jsxs15(Box13, { flexDirection: "column", children: [
1828
+ /* @__PURE__ */ jsx16(
1803
1829
  ConfirmPrompt,
1804
1830
  {
1805
1831
  message: `Run ${updateDisplay}?`,
@@ -1818,9 +1844,9 @@ function SelfUpdate({
1818
1844
  }
1819
1845
  }
1820
1846
  ),
1821
- /* @__PURE__ */ jsxs14(Box13, { marginTop: 1, marginLeft: 2, flexDirection: "column", children: [
1822
- /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: target === "repository" ? "This updates the dependency in the current repository." : "This updates the global npm install." }),
1823
- target === "repository" && repositoryRoot && /* @__PURE__ */ jsxs14(Text12, { dimColor: true, children: [
1847
+ /* @__PURE__ */ jsxs15(Box13, { marginTop: 1, marginLeft: 2, flexDirection: "column", children: [
1848
+ /* @__PURE__ */ jsx16(Text15, { dimColor: true, children: target === "repository" ? "This updates the dependency in the current repository." : "This updates the global npm install." }),
1849
+ target === "repository" && repositoryRoot && /* @__PURE__ */ jsxs15(Text15, { dimColor: true, children: [
1824
1850
  "Run location: ",
1825
1851
  repositoryRoot
1826
1852
  ] })
@@ -1828,32 +1854,32 @@ function SelfUpdate({
1828
1854
  ] });
1829
1855
  }
1830
1856
  if (phase === "running") {
1831
- return /* @__PURE__ */ jsxs14(Box13, { flexDirection: "column", children: [
1832
- /* @__PURE__ */ jsx14(Divider, {}),
1833
- /* @__PURE__ */ jsxs14(Box13, { marginY: 1, gap: 1, children: [
1834
- /* @__PURE__ */ jsx14(Text12, { color: inkColors.accent, bold: true, children: "\u25B6" }),
1835
- /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: "Running:" }),
1836
- /* @__PURE__ */ jsx14(Text12, { children: updateDisplay })
1857
+ return /* @__PURE__ */ jsxs15(Box13, { flexDirection: "column", children: [
1858
+ /* @__PURE__ */ jsx16(Divider, { width }),
1859
+ /* @__PURE__ */ jsxs15(Box13, { marginY: 1, gap: 1, children: [
1860
+ /* @__PURE__ */ jsx16(Text15, { color: inkColors.accent, bold: true, children: "\u25B6" }),
1861
+ /* @__PURE__ */ jsx16(Text15, { dimColor: true, children: "Running:" }),
1862
+ /* @__PURE__ */ jsx16(Text15, { children: updateDisplay })
1837
1863
  ] }),
1838
- /* @__PURE__ */ jsx14(Divider, {}),
1839
- /* @__PURE__ */ jsx14(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx14(Spinner, { label: "Updating Polter..." }) })
1864
+ /* @__PURE__ */ jsx16(Divider, { width }),
1865
+ /* @__PURE__ */ jsx16(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx16(Spinner, { label: "Updating Polter..." }) })
1840
1866
  ] });
1841
1867
  }
1842
1868
  if (phase === "success") {
1843
- return /* @__PURE__ */ jsxs14(Box13, { flexDirection: "column", children: [
1844
- /* @__PURE__ */ jsx14(Divider, {}),
1845
- /* @__PURE__ */ jsxs14(Box13, { marginY: 1, gap: 1, children: [
1846
- /* @__PURE__ */ jsx14(Text12, { color: inkColors.accent, bold: true, children: "\u2713" }),
1847
- /* @__PURE__ */ jsx14(Text12, { color: inkColors.accent, bold: true, children: "Update completed successfully!" })
1869
+ return /* @__PURE__ */ jsxs15(Box13, { flexDirection: "column", children: [
1870
+ /* @__PURE__ */ jsx16(Divider, { width }),
1871
+ /* @__PURE__ */ jsxs15(Box13, { marginY: 1, gap: 1, children: [
1872
+ /* @__PURE__ */ jsx16(Text15, { color: inkColors.accent, bold: true, children: "\u2713" }),
1873
+ /* @__PURE__ */ jsx16(Text15, { color: inkColors.accent, bold: true, children: "Update completed successfully!" })
1848
1874
  ] }),
1849
- /* @__PURE__ */ jsxs14(Box13, { marginBottom: 1, marginLeft: 2, flexDirection: "column", children: [
1850
- /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: "Restart Polter to use the latest version." }),
1851
- target === "repository" && repositoryRoot && /* @__PURE__ */ jsxs14(Text12, { dimColor: true, children: [
1875
+ /* @__PURE__ */ jsxs15(Box13, { marginBottom: 1, marginLeft: 2, flexDirection: "column", children: [
1876
+ /* @__PURE__ */ jsx16(Text15, { dimColor: true, children: "Restart Polter to use the latest version." }),
1877
+ target === "repository" && repositoryRoot && /* @__PURE__ */ jsxs15(Text15, { dimColor: true, children: [
1852
1878
  "Repository updated in: ",
1853
1879
  repositoryRoot
1854
1880
  ] })
1855
1881
  ] }),
1856
- /* @__PURE__ */ jsx14(
1882
+ /* @__PURE__ */ jsx16(
1857
1883
  SelectList,
1858
1884
  {
1859
1885
  items: [
@@ -1867,130 +1893,1915 @@ function SelfUpdate({
1867
1893
  }
1868
1894
  onBack();
1869
1895
  },
1896
+ onCancel: onBack,
1897
+ width,
1898
+ isInputActive,
1899
+ arrowNavigation: panelMode
1900
+ }
1901
+ )
1902
+ ] });
1903
+ }
1904
+ return /* @__PURE__ */ jsxs15(Box13, { flexDirection: "column", children: [
1905
+ /* @__PURE__ */ jsx16(Divider, { width }),
1906
+ result?.spawnError ? /* @__PURE__ */ jsxs15(Box13, { flexDirection: "column", marginY: 1, children: [
1907
+ /* @__PURE__ */ jsxs15(Box13, { gap: 1, children: [
1908
+ /* @__PURE__ */ jsx16(Text15, { color: "red", bold: true, children: "\u2717" }),
1909
+ /* @__PURE__ */ jsx16(Text15, { color: "red", bold: true, children: "Failed to start update" })
1910
+ ] }),
1911
+ /* @__PURE__ */ jsxs15(Box13, { marginLeft: 2, marginTop: 1, children: [
1912
+ /* @__PURE__ */ jsx16(Text15, { dimColor: true, children: "Error: " }),
1913
+ /* @__PURE__ */ jsx16(Text15, { color: "red", children: result.spawnError })
1914
+ ] })
1915
+ ] }) : /* @__PURE__ */ jsxs15(Box13, { flexDirection: "column", marginY: 1, children: [
1916
+ /* @__PURE__ */ jsxs15(Box13, { gap: 1, children: [
1917
+ /* @__PURE__ */ jsx16(Text15, { color: "red", bold: true, children: "\u2717" }),
1918
+ /* @__PURE__ */ jsx16(Text15, { color: "red", children: "Update failed " }),
1919
+ /* @__PURE__ */ jsx16(Text15, { dimColor: true, children: "(exit code " }),
1920
+ /* @__PURE__ */ jsx16(Text15, { color: "red", bold: true, children: String(result?.exitCode) }),
1921
+ /* @__PURE__ */ jsx16(Text15, { dimColor: true, children: ")" })
1922
+ ] }),
1923
+ /* @__PURE__ */ jsxs15(Box13, { marginLeft: 2, marginTop: 1, children: [
1924
+ /* @__PURE__ */ jsx16(Text15, { dimColor: true, children: "Command: " }),
1925
+ /* @__PURE__ */ jsx16(Text15, { children: updateDisplay })
1926
+ ] })
1927
+ ] }),
1928
+ /* @__PURE__ */ jsxs15(Box13, { marginBottom: 1, marginLeft: 2, flexDirection: "column", children: [
1929
+ /* @__PURE__ */ jsx16(Text15, { dimColor: true, children: "Manual fallback:" }),
1930
+ /* @__PURE__ */ jsx16(Text15, { color: inkColors.accent, children: updateDisplay }),
1931
+ target === "repository" && repositoryRoot && /* @__PURE__ */ jsxs15(Text15, { dimColor: true, children: [
1932
+ "Run location: ",
1933
+ repositoryRoot
1934
+ ] })
1935
+ ] }),
1936
+ /* @__PURE__ */ jsx16(Box13, { marginTop: 1, marginBottom: 1, children: /* @__PURE__ */ jsx16(Text15, { bold: true, children: "What would you like to do?" }) }),
1937
+ /* @__PURE__ */ jsx16(
1938
+ SelectList,
1939
+ {
1940
+ items: [
1941
+ { value: "retry", label: "\u{1F504} Retry update" },
1942
+ ...repositoryRoot ? [{ value: "target", label: "\u2194 Choose update target" }] : [],
1943
+ { value: "menu", label: "\u2190 Return to main menu" },
1944
+ { value: "exit", label: "\u{1F6AA} Exit Polter" }
1945
+ ],
1946
+ onSelect: (value) => {
1947
+ switch (value) {
1948
+ case "retry":
1949
+ reset();
1950
+ setPhase("running");
1951
+ break;
1952
+ case "target":
1953
+ reset();
1954
+ setPhase("target");
1955
+ break;
1956
+ case "menu":
1957
+ onBack();
1958
+ break;
1959
+ case "exit":
1960
+ onExit();
1961
+ break;
1962
+ }
1963
+ },
1964
+ onCancel: onBack,
1965
+ width,
1966
+ isInputActive,
1967
+ arrowNavigation: panelMode
1968
+ }
1969
+ ),
1970
+ !panelMode && /* @__PURE__ */ jsx16(StatusBar, { width })
1971
+ ] });
1972
+ }
1973
+
1974
+ // src/screens/ToolStatus.tsx
1975
+ import { useMemo as useMemo4 } from "react";
1976
+ import { Box as Box14, Text as Text16 } from "ink";
1977
+ import { jsx as jsx17, jsxs as jsxs16 } from "react/jsx-runtime";
1978
+ var toolIds = ["supabase", "gh", "vercel", "pulumi"];
1979
+ function ToolStatus({ onBack, width = 80, panelMode = false, isInputActive = true }) {
1980
+ const tools = useMemo4(() => toolIds.map(getToolInfo), []);
1981
+ return /* @__PURE__ */ jsxs16(Box14, { flexDirection: "column", children: [
1982
+ /* @__PURE__ */ jsx17(Box14, { marginBottom: 1, children: /* @__PURE__ */ jsx17(Text16, { bold: true, color: inkColors.accent, children: "\u{1F527} Tool Status" }) }),
1983
+ tools.map((tool) => /* @__PURE__ */ jsxs16(Box14, { gap: 1, marginLeft: 2, children: [
1984
+ /* @__PURE__ */ jsx17(Text16, { color: tool.installed ? inkColors.accent : "red", children: tool.installed ? "\u2713" : "\u2717" }),
1985
+ /* @__PURE__ */ jsx17(Box14, { width: 16, children: /* @__PURE__ */ jsx17(Text16, { bold: true, children: tool.label }) }),
1986
+ /* @__PURE__ */ jsx17(Text16, { dimColor: true, children: tool.installed ? tool.version ?? "installed" : "not found" })
1987
+ ] }, tool.id)),
1988
+ /* @__PURE__ */ jsx17(Box14, { marginTop: 1, children: /* @__PURE__ */ jsx17(
1989
+ SelectList,
1990
+ {
1991
+ items: [{ value: "__back__", label: "\u2190 Back" }],
1992
+ onSelect: onBack,
1993
+ onCancel: onBack,
1994
+ width,
1995
+ isInputActive,
1996
+ arrowNavigation: panelMode
1997
+ }
1998
+ ) }),
1999
+ !panelMode && /* @__PURE__ */ jsx17(StatusBar, { hint: "Enter to go back", width })
2000
+ ] });
2001
+ }
2002
+
2003
+ // src/screens/ProjectConfig.tsx
2004
+ import { useMemo as useMemo5, useState as useState13 } from "react";
2005
+ import { Box as Box15, Text as Text17 } from "ink";
2006
+
2007
+ // src/hooks/useEditor.ts
2008
+ import { useState as useState12, useCallback as useCallback3 } from "react";
2009
+ import { useStdin } from "ink";
2010
+
2011
+ // src/lib/editor.ts
2012
+ import { spawnSync, spawn as spawn2 } from "child_process";
2013
+ import { basename } from "path";
2014
+ var TERMINAL_EDITORS = /* @__PURE__ */ new Set([
2015
+ "vi",
2016
+ "vim",
2017
+ "nvim",
2018
+ "nano",
2019
+ "pico",
2020
+ "emacs",
2021
+ "micro",
2022
+ "helix",
2023
+ "hx",
2024
+ "joe",
2025
+ "mcedit"
2026
+ ]);
2027
+ function resolveEditor() {
2028
+ const raw = process.env["VISUAL"] || process.env["EDITOR"];
2029
+ if (raw) {
2030
+ const parts = raw.trim().split(/\s+/);
2031
+ return { command: parts[0], args: parts.slice(1) };
2032
+ }
2033
+ const fallback = process.platform === "win32" ? "notepad" : "nano";
2034
+ return { command: fallback, args: [] };
2035
+ }
2036
+ function isTerminalEditor(cmd) {
2037
+ return TERMINAL_EDITORS.has(basename(cmd));
2038
+ }
2039
+ function openInEditor(filePath) {
2040
+ const editor = resolveEditor();
2041
+ const terminal = isTerminalEditor(editor.command);
2042
+ if (terminal) {
2043
+ const result = spawnSync(editor.command, [...editor.args, filePath], {
2044
+ stdio: "inherit"
2045
+ });
2046
+ return { exitCode: result.status, isTerminal: true };
2047
+ }
2048
+ const child = spawn2(editor.command, [...editor.args, filePath], {
2049
+ detached: true,
2050
+ stdio: "ignore"
2051
+ });
2052
+ child.unref();
2053
+ return { exitCode: 0, isTerminal: false };
2054
+ }
2055
+
2056
+ // src/hooks/useEditor.ts
2057
+ function useEditor() {
2058
+ const { setRawMode } = useStdin();
2059
+ const [isEditing, setIsEditing] = useState12(false);
2060
+ const openEditor = useCallback3(async (filePath) => {
2061
+ const editor = resolveEditor();
2062
+ const terminal = isTerminalEditor(editor.command);
2063
+ setIsEditing(true);
2064
+ if (terminal) {
2065
+ setRawMode(false);
2066
+ }
2067
+ try {
2068
+ openInEditor(filePath);
2069
+ } finally {
2070
+ if (terminal) {
2071
+ setRawMode(true);
2072
+ }
2073
+ setIsEditing(false);
2074
+ }
2075
+ }, [setRawMode]);
2076
+ return { openEditor, isEditing };
2077
+ }
2078
+
2079
+ // src/screens/ProjectConfig.tsx
2080
+ import { jsx as jsx18, jsxs as jsxs17 } from "react/jsx-runtime";
2081
+ function ProjectConfig({
2082
+ onBack,
2083
+ width = 80,
2084
+ panelMode = false,
2085
+ isInputActive = true
2086
+ }) {
2087
+ const configPath = useMemo5(() => getProjectConfigPath(), []);
2088
+ const [config2, setConfig] = useState13(() => getOrCreateProjectConfig());
2089
+ const [phase, setPhase] = useState13("overview");
2090
+ const [feedback, setFeedback] = useState13();
2091
+ const { openEditor, isEditing } = useEditor();
2092
+ if (!configPath) {
2093
+ return /* @__PURE__ */ jsxs17(Box15, { flexDirection: "column", children: [
2094
+ /* @__PURE__ */ jsx18(Text17, { color: "red", children: "No package.json found. Run from a project directory." }),
2095
+ /* @__PURE__ */ jsx18(
2096
+ SelectList,
2097
+ {
2098
+ items: [{ value: "__back__", label: "\u2190 Back" }],
2099
+ onSelect: onBack,
2100
+ onCancel: onBack,
2101
+ width,
2102
+ isInputActive,
2103
+ arrowNavigation: panelMode
2104
+ }
2105
+ )
2106
+ ] });
2107
+ }
2108
+ if (phase === "edit-supabase-ref") {
2109
+ return /* @__PURE__ */ jsx18(Box15, { flexDirection: "column", children: /* @__PURE__ */ jsx18(
2110
+ TextPrompt,
2111
+ {
2112
+ label: "Supabase project ref:",
2113
+ placeholder: "e.g. abcdefghijklmnop",
2114
+ onSubmit: (val) => {
2115
+ const updated = {
2116
+ ...config2,
2117
+ tools: {
2118
+ ...config2.tools,
2119
+ supabase: { ...config2.tools.supabase, projectRef: val.trim() || void 0 }
2120
+ }
2121
+ };
2122
+ writeProjectConfig(updated);
2123
+ setConfig(updated);
2124
+ setFeedback("Supabase project ref updated");
2125
+ setPhase("overview");
2126
+ },
2127
+ onCancel: () => setPhase("overview")
2128
+ }
2129
+ ) });
2130
+ }
2131
+ if (phase === "edit-vercel-id") {
2132
+ return /* @__PURE__ */ jsx18(Box15, { flexDirection: "column", children: /* @__PURE__ */ jsx18(
2133
+ TextPrompt,
2134
+ {
2135
+ label: "Vercel project ID:",
2136
+ placeholder: "e.g. prj_xxxx",
2137
+ onSubmit: (val) => {
2138
+ const updated = {
2139
+ ...config2,
2140
+ tools: {
2141
+ ...config2.tools,
2142
+ vercel: { ...config2.tools.vercel, projectId: val.trim() || void 0 }
2143
+ }
2144
+ };
2145
+ writeProjectConfig(updated);
2146
+ setConfig(updated);
2147
+ setFeedback("Vercel project ID updated");
2148
+ setPhase("overview");
2149
+ },
2150
+ onCancel: () => setPhase("overview")
2151
+ }
2152
+ ) });
2153
+ }
2154
+ if (phase === "edit-gh-repo") {
2155
+ return /* @__PURE__ */ jsx18(Box15, { flexDirection: "column", children: /* @__PURE__ */ jsx18(
2156
+ TextPrompt,
2157
+ {
2158
+ label: "GitHub repo (owner/name):",
2159
+ placeholder: "e.g. myorg/my-app",
2160
+ onSubmit: (val) => {
2161
+ const updated = {
2162
+ ...config2,
2163
+ tools: {
2164
+ ...config2.tools,
2165
+ gh: { ...config2.tools.gh, repo: val.trim() || void 0 }
2166
+ }
2167
+ };
2168
+ writeProjectConfig(updated);
2169
+ setConfig(updated);
2170
+ setFeedback("GitHub repo updated");
2171
+ setPhase("overview");
2172
+ },
2173
+ onCancel: () => setPhase("overview")
2174
+ }
2175
+ ) });
2176
+ }
2177
+ return /* @__PURE__ */ jsxs17(Box15, { flexDirection: "column", children: [
2178
+ /* @__PURE__ */ jsx18(Box15, { marginBottom: 1, children: /* @__PURE__ */ jsx18(Text17, { bold: true, color: inkColors.accent, children: "\u2699\uFE0F Project Config" }) }),
2179
+ /* @__PURE__ */ jsxs17(Box15, { marginBottom: 1, marginLeft: 2, flexDirection: "column", children: [
2180
+ /* @__PURE__ */ jsxs17(Text17, { dimColor: true, children: [
2181
+ "Path: ",
2182
+ configPath.file
2183
+ ] }),
2184
+ /* @__PURE__ */ jsxs17(Text17, { children: [
2185
+ "Supabase ref: ",
2186
+ config2.tools.supabase?.projectRef ?? /* @__PURE__ */ jsx18(Text17, { dimColor: true, children: "not set" })
2187
+ ] }),
2188
+ /* @__PURE__ */ jsxs17(Text17, { children: [
2189
+ "Vercel ID: ",
2190
+ config2.tools.vercel?.projectId ?? /* @__PURE__ */ jsx18(Text17, { dimColor: true, children: "not set" })
2191
+ ] }),
2192
+ /* @__PURE__ */ jsxs17(Text17, { children: [
2193
+ "GitHub repo: ",
2194
+ config2.tools.gh?.repo ?? /* @__PURE__ */ jsx18(Text17, { dimColor: true, children: "not set" })
2195
+ ] })
2196
+ ] }),
2197
+ feedback && /* @__PURE__ */ jsx18(Box15, { marginBottom: 1, children: /* @__PURE__ */ jsxs17(Text17, { color: inkColors.accent, children: [
2198
+ "\u2713 ",
2199
+ feedback
2200
+ ] }) }),
2201
+ /* @__PURE__ */ jsx18(
2202
+ SelectList,
2203
+ {
2204
+ items: [
2205
+ { value: "supabase", label: "Set Supabase project ref" },
2206
+ { value: "vercel", label: "Set Vercel project ID" },
2207
+ { value: "gh", label: "Set GitHub repo" },
2208
+ { value: "editor", label: "Open config in editor" },
2209
+ { value: "__back__", label: "\u2190 Back" }
2210
+ ],
2211
+ onSelect: (value) => {
2212
+ switch (value) {
2213
+ case "supabase":
2214
+ setPhase("edit-supabase-ref");
2215
+ break;
2216
+ case "vercel":
2217
+ setPhase("edit-vercel-id");
2218
+ break;
2219
+ case "gh":
2220
+ setPhase("edit-gh-repo");
2221
+ break;
2222
+ case "editor":
2223
+ openEditor(configPath.file).then(() => {
2224
+ try {
2225
+ const reloaded = getOrCreateProjectConfig();
2226
+ setConfig(reloaded);
2227
+ setFeedback("Config reloaded from file");
2228
+ } catch {
2229
+ setFeedback("Warning: could not parse config after editing");
2230
+ }
2231
+ });
2232
+ break;
2233
+ default:
2234
+ onBack();
2235
+ }
2236
+ },
2237
+ onCancel: onBack,
2238
+ width,
2239
+ isInputActive,
2240
+ arrowNavigation: panelMode
2241
+ }
2242
+ ),
2243
+ !panelMode && /* @__PURE__ */ jsx18(StatusBar, { hint: "\u2191\u2193 navigate \xB7 Enter select \xB7 Esc back", width })
2244
+ ] });
2245
+ }
2246
+
2247
+ // src/screens/PipelineList.tsx
2248
+ import { useMemo as useMemo6 } from "react";
2249
+ import { Box as Box16, Text as Text18 } from "ink";
2250
+ import { jsx as jsx19, jsxs as jsxs18 } from "react/jsx-runtime";
2251
+ function PipelineList({
2252
+ onNavigate,
2253
+ onBack,
2254
+ width = 80,
2255
+ panelMode = false,
2256
+ isInputActive = true
2257
+ }) {
2258
+ const pipelines = useMemo6(() => getAllPipelines(), []);
2259
+ const items = useMemo6(() => {
2260
+ const list = [];
2261
+ if (pipelines.length === 0) {
2262
+ list.push({
2263
+ id: "empty-hint",
2264
+ value: "__empty__",
2265
+ label: "No pipelines yet",
2266
+ kind: "header",
2267
+ selectable: false
2268
+ });
2269
+ } else {
2270
+ list.push({
2271
+ id: "section-pipelines",
2272
+ value: "__section__",
2273
+ label: "\u{1F517} Saved Pipelines",
2274
+ kind: "header",
2275
+ selectable: false
2276
+ });
2277
+ for (const pipeline of pipelines) {
2278
+ list.push({
2279
+ id: `pipeline:${pipeline.id}`,
2280
+ value: pipeline.id,
2281
+ label: pipeline.name,
2282
+ hint: `${pipeline.steps.length} steps \xB7 ${pipeline.source}${pipeline.description ? ` \xB7 ${pipeline.description}` : ""}`,
2283
+ kind: "action"
2284
+ });
2285
+ }
2286
+ }
2287
+ list.push({
2288
+ id: "section-actions",
2289
+ value: "__section_actions__",
2290
+ label: "\u26A1 Actions",
2291
+ kind: "header",
2292
+ selectable: false
2293
+ });
2294
+ list.push({
2295
+ id: "action-new",
2296
+ value: "__new__",
2297
+ label: "Create Pipeline",
2298
+ hint: "Build a new command sequence",
2299
+ icon: "\u2795",
2300
+ kind: "action"
2301
+ });
2302
+ list.push({
2303
+ id: "action-back",
2304
+ value: "__back__",
2305
+ label: "\u2190 Back",
2306
+ kind: "action"
2307
+ });
2308
+ return list;
2309
+ }, [pipelines]);
2310
+ return /* @__PURE__ */ jsxs18(Box16, { flexDirection: "column", children: [
2311
+ /* @__PURE__ */ jsx19(Box16, { marginBottom: 1, children: /* @__PURE__ */ jsx19(Text18, { bold: true, color: inkColors.accent, children: "\u{1F517} Pipelines" }) }),
2312
+ /* @__PURE__ */ jsx19(
2313
+ SelectList,
2314
+ {
2315
+ items,
2316
+ onSelect: (value) => {
2317
+ if (value === "__back__") {
2318
+ onBack();
2319
+ return;
2320
+ }
2321
+ if (value === "__new__") {
2322
+ onNavigate("pipeline-builder");
2323
+ return;
2324
+ }
2325
+ onNavigate("pipeline-execution", { pipelineId: value });
2326
+ },
2327
+ onCancel: onBack,
2328
+ width,
2329
+ isInputActive,
2330
+ arrowNavigation: panelMode
2331
+ }
2332
+ ),
2333
+ !panelMode && /* @__PURE__ */ jsx19(StatusBar, { hint: "\u2191\u2193 navigate \xB7 Enter select \xB7 Esc back", width })
2334
+ ] });
2335
+ }
2336
+
2337
+ // src/screens/PipelineBuilder.tsx
2338
+ import { useState as useState14 } from "react";
2339
+ import { Box as Box17, Text as Text19 } from "ink";
2340
+ import { jsx as jsx20, jsxs as jsxs19 } from "react/jsx-runtime";
2341
+ var stepCounter = 0;
2342
+ function nextStepId() {
2343
+ stepCounter += 1;
2344
+ return `step-${Date.now()}-${stepCounter}`;
2345
+ }
2346
+ function PipelineBuilder({
2347
+ onBack,
2348
+ width = 80,
2349
+ height = 24,
2350
+ panelMode = false,
2351
+ isInputActive = true
2352
+ }) {
2353
+ const [phase, setPhase] = useState14("name");
2354
+ const [name, setName] = useState14("");
2355
+ const [steps, setSteps] = useState14([]);
2356
+ if (phase === "name") {
2357
+ return /* @__PURE__ */ jsxs19(Box17, { flexDirection: "column", children: [
2358
+ /* @__PURE__ */ jsx20(Box17, { marginBottom: 1, children: /* @__PURE__ */ jsx20(Text19, { bold: true, color: inkColors.accent, children: "\u{1F517} New Pipeline" }) }),
2359
+ /* @__PURE__ */ jsx20(
2360
+ TextPrompt,
2361
+ {
2362
+ label: "Pipeline name:",
2363
+ placeholder: "e.g. deploy-all",
2364
+ validate: (val) => {
2365
+ if (!val?.trim()) return "Name is required";
2366
+ return void 0;
2367
+ },
2368
+ onSubmit: (val) => {
2369
+ setName(val.trim());
2370
+ setPhase("add-step");
2371
+ },
1870
2372
  onCancel: onBack
1871
2373
  }
2374
+ ),
2375
+ !panelMode && /* @__PURE__ */ jsx20(StatusBar, { hint: "Type name \xB7 Enter to continue \xB7 Esc cancel", width })
2376
+ ] });
2377
+ }
2378
+ if (phase === "add-step") {
2379
+ const commandItems = [
2380
+ {
2381
+ id: "section-commands",
2382
+ value: "__section__",
2383
+ label: "Available Commands",
2384
+ kind: "header",
2385
+ selectable: false
2386
+ },
2387
+ ...allCommands.map((cmd) => ({
2388
+ id: cmd.id,
2389
+ value: cmd.id,
2390
+ label: `${cmd.tool}: ${cmd.label}`,
2391
+ hint: cmd.hint,
2392
+ kind: "command"
2393
+ })),
2394
+ {
2395
+ id: "section-done",
2396
+ value: "__section_done__",
2397
+ label: "\u26A1 Actions",
2398
+ kind: "header",
2399
+ selectable: false
2400
+ },
2401
+ {
2402
+ id: "action-done",
2403
+ value: "__done__",
2404
+ label: steps.length > 0 ? "\u2713 Done - save pipeline" : "\u2190 Cancel",
2405
+ kind: "action"
2406
+ }
2407
+ ];
2408
+ return /* @__PURE__ */ jsxs19(Box17, { flexDirection: "column", children: [
2409
+ /* @__PURE__ */ jsxs19(Box17, { marginBottom: 1, gap: 1, children: [
2410
+ /* @__PURE__ */ jsxs19(Text19, { bold: true, color: inkColors.accent, children: [
2411
+ "\u{1F517} ",
2412
+ name
2413
+ ] }),
2414
+ /* @__PURE__ */ jsxs19(Text19, { dimColor: true, children: [
2415
+ "(",
2416
+ steps.length,
2417
+ " steps)"
2418
+ ] })
2419
+ ] }),
2420
+ steps.length > 0 && /* @__PURE__ */ jsx20(Box17, { flexDirection: "column", marginBottom: 1, children: steps.map((step, i) => /* @__PURE__ */ jsxs19(Text19, { dimColor: true, children: [
2421
+ i + 1,
2422
+ ". ",
2423
+ step.commandId,
2424
+ step.args.length > 0 ? ` ${step.args.join(" ")}` : ""
2425
+ ] }, step.id)) }),
2426
+ /* @__PURE__ */ jsx20(Text19, { dimColor: true, children: "Select a command to add as step:" }),
2427
+ /* @__PURE__ */ jsx20(
2428
+ SelectList,
2429
+ {
2430
+ items: commandItems,
2431
+ onSelect: (value) => {
2432
+ if (value === "__done__") {
2433
+ if (steps.length > 0) {
2434
+ setPhase("review");
2435
+ } else {
2436
+ onBack();
2437
+ }
2438
+ return;
2439
+ }
2440
+ setSteps((prev) => [
2441
+ ...prev,
2442
+ {
2443
+ id: nextStepId(),
2444
+ commandId: value,
2445
+ args: [],
2446
+ flags: [],
2447
+ continueOnError: false
2448
+ }
2449
+ ]);
2450
+ },
2451
+ onCancel: () => {
2452
+ if (steps.length > 0) {
2453
+ setPhase("review");
2454
+ } else {
2455
+ onBack();
2456
+ }
2457
+ },
2458
+ maxVisible: Math.max(8, height - 14),
2459
+ width,
2460
+ isInputActive,
2461
+ arrowNavigation: panelMode
2462
+ }
2463
+ ),
2464
+ !panelMode && /* @__PURE__ */ jsx20(StatusBar, { hint: "\u2191\u2193 navigate \xB7 Enter add step \xB7 Esc done", width })
2465
+ ] });
2466
+ }
2467
+ const reviewItems = [
2468
+ {
2469
+ id: "action-save",
2470
+ value: "__save__",
2471
+ label: "\u2713 Save pipeline",
2472
+ kind: "action"
2473
+ },
2474
+ {
2475
+ id: "action-add-more",
2476
+ value: "__add__",
2477
+ label: "\u2795 Add more steps",
2478
+ kind: "action"
2479
+ },
2480
+ {
2481
+ id: "action-cancel",
2482
+ value: "__cancel__",
2483
+ label: "\u2190 Cancel",
2484
+ kind: "action"
2485
+ }
2486
+ ];
2487
+ return /* @__PURE__ */ jsxs19(Box17, { flexDirection: "column", children: [
2488
+ /* @__PURE__ */ jsx20(Box17, { marginBottom: 1, children: /* @__PURE__ */ jsxs19(Text19, { bold: true, color: inkColors.accent, children: [
2489
+ "\u{1F517} Review: ",
2490
+ name
2491
+ ] }) }),
2492
+ /* @__PURE__ */ jsx20(Box17, { flexDirection: "column", marginBottom: 1, children: steps.map((step, i) => /* @__PURE__ */ jsxs19(Text19, { children: [
2493
+ i + 1,
2494
+ ". ",
2495
+ step.commandId,
2496
+ step.args.length > 0 ? ` ${step.args.join(" ")}` : ""
2497
+ ] }, step.id)) }),
2498
+ /* @__PURE__ */ jsx20(
2499
+ SelectList,
2500
+ {
2501
+ items: reviewItems,
2502
+ onSelect: (value) => {
2503
+ if (value === "__save__") {
2504
+ const now = (/* @__PURE__ */ new Date()).toISOString();
2505
+ const pipeline = {
2506
+ id: `pipeline-${Date.now()}`,
2507
+ name,
2508
+ steps,
2509
+ createdAt: now,
2510
+ updatedAt: now
2511
+ };
2512
+ savePipeline(pipeline, "project");
2513
+ onBack();
2514
+ return;
2515
+ }
2516
+ if (value === "__add__") {
2517
+ setPhase("add-step");
2518
+ return;
2519
+ }
2520
+ onBack();
2521
+ },
2522
+ onCancel: onBack,
2523
+ width,
2524
+ isInputActive,
2525
+ arrowNavigation: panelMode
2526
+ }
2527
+ ),
2528
+ !panelMode && /* @__PURE__ */ jsx20(StatusBar, { hint: "\u2191\u2193 navigate \xB7 Enter select \xB7 Esc back", width })
2529
+ ] });
2530
+ }
2531
+
2532
+ // src/screens/PipelineExecution.tsx
2533
+ import { useState as useState15, useEffect as useEffect7, useMemo as useMemo7 } from "react";
2534
+ import { Box as Box20, Text as Text22 } from "ink";
2535
+
2536
+ // src/components/StepIndicator.tsx
2537
+ import { Box as Box18, Text as Text20 } from "ink";
2538
+ import { jsx as jsx21, jsxs as jsxs20 } from "react/jsx-runtime";
2539
+ var statusIcons = {
2540
+ pending: "\u25CB",
2541
+ running: "",
2542
+ success: "\u2713",
2543
+ error: "\u2717",
2544
+ skipped: "\u2298"
2545
+ };
2546
+ var statusColors = {
2547
+ pending: void 0,
2548
+ running: inkColors.accent,
2549
+ success: inkColors.accent,
2550
+ error: "red",
2551
+ skipped: void 0
2552
+ };
2553
+ function StepIndicator({
2554
+ label,
2555
+ status,
2556
+ index
2557
+ }) {
2558
+ return /* @__PURE__ */ jsxs20(Box18, { gap: 1, children: [
2559
+ /* @__PURE__ */ jsxs20(Text20, { dimColor: true, children: [
2560
+ String(index + 1).padStart(2, " "),
2561
+ "."
2562
+ ] }),
2563
+ status === "running" ? /* @__PURE__ */ jsx21(Spinner, { label: "" }) : /* @__PURE__ */ jsx21(
2564
+ Text20,
2565
+ {
2566
+ color: statusColors[status],
2567
+ dimColor: status === "pending" || status === "skipped",
2568
+ children: statusIcons[status]
2569
+ }
2570
+ ),
2571
+ /* @__PURE__ */ jsx21(
2572
+ Text20,
2573
+ {
2574
+ color: statusColors[status],
2575
+ dimColor: status === "pending" || status === "skipped",
2576
+ bold: status === "running",
2577
+ children: label
2578
+ }
2579
+ )
2580
+ ] });
2581
+ }
2582
+
2583
+ // src/components/PipelineProgress.tsx
2584
+ import { Box as Box19, Text as Text21 } from "ink";
2585
+ import { jsxs as jsxs21 } from "react/jsx-runtime";
2586
+ function PipelineProgressBar({
2587
+ total,
2588
+ completed,
2589
+ errors,
2590
+ width = 80
2591
+ }) {
2592
+ const barWidth = Math.max(10, width - 20);
2593
+ const filledCount = total > 0 ? Math.round(completed / total * barWidth) : 0;
2594
+ const filled = "\u2588".repeat(filledCount);
2595
+ const empty = "\u2591".repeat(barWidth - filledCount);
2596
+ return /* @__PURE__ */ jsxs21(Box19, { gap: 1, children: [
2597
+ /* @__PURE__ */ jsxs21(Text21, { color: errors > 0 ? "red" : inkColors.accent, children: [
2598
+ "[",
2599
+ filled,
2600
+ empty,
2601
+ "]"
2602
+ ] }),
2603
+ /* @__PURE__ */ jsxs21(Text21, { dimColor: true, children: [
2604
+ completed,
2605
+ "/",
2606
+ total,
2607
+ errors > 0 ? ` (${errors} failed)` : ""
2608
+ ] })
2609
+ ] });
2610
+ }
2611
+
2612
+ // src/screens/PipelineExecution.tsx
2613
+ import { Fragment, jsx as jsx22, jsxs as jsxs22 } from "react/jsx-runtime";
2614
+ function PipelineExecution({
2615
+ pipelineId,
2616
+ onBack,
2617
+ onExit,
2618
+ width = 80,
2619
+ panelMode = false,
2620
+ isInputActive = true
2621
+ }) {
2622
+ const pipeline = useMemo7(
2623
+ () => getAllPipelines().find((p) => p.id === pipelineId),
2624
+ [pipelineId]
2625
+ );
2626
+ const [phase, setPhase] = useState15("running");
2627
+ const [progress, setProgress] = useState15(null);
2628
+ const [results, setResults] = useState15([]);
2629
+ useEffect7(() => {
2630
+ if (!pipeline) return;
2631
+ executePipeline(pipeline, (p) => {
2632
+ setProgress({ ...p });
2633
+ if (p.done) {
2634
+ setResults(p.stepResults);
2635
+ setPhase("done");
2636
+ }
2637
+ }).catch(() => {
2638
+ setPhase("done");
2639
+ });
2640
+ }, [pipeline]);
2641
+ if (!pipeline) {
2642
+ return /* @__PURE__ */ jsxs22(Box20, { flexDirection: "column", children: [
2643
+ /* @__PURE__ */ jsxs22(Text22, { color: "red", children: [
2644
+ "Pipeline not found: ",
2645
+ pipelineId
2646
+ ] }),
2647
+ /* @__PURE__ */ jsx22(
2648
+ SelectList,
2649
+ {
2650
+ items: [{ value: "__back__", label: "\u2190 Back" }],
2651
+ onSelect: onBack,
2652
+ onCancel: onBack,
2653
+ width,
2654
+ isInputActive,
2655
+ arrowNavigation: panelMode
2656
+ }
1872
2657
  )
1873
2658
  ] });
1874
2659
  }
1875
- return /* @__PURE__ */ jsxs14(Box13, { flexDirection: "column", children: [
1876
- /* @__PURE__ */ jsx14(Divider, {}),
1877
- result?.spawnError ? /* @__PURE__ */ jsxs14(Box13, { flexDirection: "column", marginY: 1, children: [
1878
- /* @__PURE__ */ jsxs14(Box13, { gap: 1, children: [
1879
- /* @__PURE__ */ jsx14(Text12, { color: "red", bold: true, children: "\u2717" }),
1880
- /* @__PURE__ */ jsx14(Text12, { color: "red", bold: true, children: "Failed to start update" })
1881
- ] }),
1882
- /* @__PURE__ */ jsxs14(Box13, { marginLeft: 2, marginTop: 1, children: [
1883
- /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: "Error: " }),
1884
- /* @__PURE__ */ jsx14(Text12, { color: "red", children: result.spawnError })
1885
- ] })
1886
- ] }) : /* @__PURE__ */ jsxs14(Box13, { flexDirection: "column", marginY: 1, children: [
1887
- /* @__PURE__ */ jsxs14(Box13, { gap: 1, children: [
1888
- /* @__PURE__ */ jsx14(Text12, { color: "red", bold: true, children: "\u2717" }),
1889
- /* @__PURE__ */ jsx14(Text12, { color: "red", children: "Update failed " }),
1890
- /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: "(exit code " }),
1891
- /* @__PURE__ */ jsx14(Text12, { color: "red", bold: true, children: String(result?.exitCode) }),
1892
- /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: ")" })
1893
- ] }),
1894
- /* @__PURE__ */ jsxs14(Box13, { marginLeft: 2, marginTop: 1, children: [
1895
- /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: "Command: " }),
1896
- /* @__PURE__ */ jsx14(Text12, { children: updateDisplay })
1897
- ] })
1898
- ] }),
1899
- /* @__PURE__ */ jsxs14(Box13, { marginBottom: 1, marginLeft: 2, flexDirection: "column", children: [
1900
- /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: "Manual fallback:" }),
1901
- /* @__PURE__ */ jsx14(Text12, { color: inkColors.accent, children: updateDisplay }),
1902
- target === "repository" && repositoryRoot && /* @__PURE__ */ jsxs14(Text12, { dimColor: true, children: [
1903
- "Run location: ",
1904
- repositoryRoot
1905
- ] })
1906
- ] }),
1907
- /* @__PURE__ */ jsx14(Box13, { marginTop: 1, marginBottom: 1, children: /* @__PURE__ */ jsx14(Text12, { bold: true, children: "What would you like to do?" }) }),
1908
- /* @__PURE__ */ jsx14(
2660
+ const stepResults = progress?.stepResults ?? [];
2661
+ const completedCount = stepResults.filter(
2662
+ (s) => s.status === "success" || s.status === "error" || s.status === "skipped"
2663
+ ).length;
2664
+ const errorCount = stepResults.filter((s) => s.status === "error").length;
2665
+ function getStepLabel(step) {
2666
+ const cmdDef = getCommandById(step.commandId);
2667
+ const base = cmdDef ? `${cmdDef.tool}: ${cmdDef.label}` : step.commandId;
2668
+ return step.args.length > 0 ? `${base} ${step.args.join(" ")}` : base;
2669
+ }
2670
+ return /* @__PURE__ */ jsxs22(Box20, { flexDirection: "column", children: [
2671
+ /* @__PURE__ */ jsxs22(Box20, { marginBottom: 1, gap: 1, children: [
2672
+ /* @__PURE__ */ jsxs22(Text22, { bold: true, color: inkColors.accent, children: [
2673
+ "\u{1F517} ",
2674
+ pipeline.name
2675
+ ] }),
2676
+ /* @__PURE__ */ jsx22(Text22, { dimColor: true, children: phase === "running" ? "Running..." : "Complete" })
2677
+ ] }),
2678
+ /* @__PURE__ */ jsx22(
2679
+ PipelineProgressBar,
2680
+ {
2681
+ total: pipeline.steps.length,
2682
+ completed: completedCount,
2683
+ errors: errorCount,
2684
+ width
2685
+ }
2686
+ ),
2687
+ /* @__PURE__ */ jsx22(Divider, { width }),
2688
+ /* @__PURE__ */ jsx22(Box20, { flexDirection: "column", marginY: 1, children: pipeline.steps.map((step, i) => /* @__PURE__ */ jsx22(
2689
+ StepIndicator,
2690
+ {
2691
+ label: step.label ?? getStepLabel(step),
2692
+ status: stepResults[i]?.status ?? "pending",
2693
+ index: i
2694
+ },
2695
+ step.id
2696
+ )) }),
2697
+ phase === "done" && /* @__PURE__ */ jsxs22(Fragment, { children: [
2698
+ /* @__PURE__ */ jsx22(Divider, { width }),
2699
+ /* @__PURE__ */ jsx22(Box20, { marginY: 1, children: /* @__PURE__ */ jsx22(
2700
+ Text22,
2701
+ {
2702
+ color: errorCount > 0 ? "red" : inkColors.accent,
2703
+ bold: true,
2704
+ children: errorCount > 0 ? `Pipeline completed with ${errorCount} error(s)` : "Pipeline completed successfully!"
2705
+ }
2706
+ ) }),
2707
+ /* @__PURE__ */ jsx22(
2708
+ SelectList,
2709
+ {
2710
+ items: [
2711
+ { value: "__back__", label: "\u2190 Back" },
2712
+ { value: "__exit__", label: "\u{1F6AA} Exit" }
2713
+ ],
2714
+ onSelect: (value) => {
2715
+ if (value === "__exit__") {
2716
+ onExit();
2717
+ return;
2718
+ }
2719
+ onBack();
2720
+ },
2721
+ onCancel: onBack,
2722
+ width,
2723
+ isInputActive,
2724
+ arrowNavigation: panelMode
2725
+ }
2726
+ )
2727
+ ] }),
2728
+ !panelMode && /* @__PURE__ */ jsx22(
2729
+ StatusBar,
2730
+ {
2731
+ hint: phase === "running" ? "Running pipeline..." : "Enter select \xB7 Esc back",
2732
+ width
2733
+ }
2734
+ )
2735
+ ] });
2736
+ }
2737
+
2738
+ // src/app.tsx
2739
+ import { jsx as jsx23, jsxs as jsxs23 } from "react/jsx-runtime";
2740
+ function AppClassic() {
2741
+ const { screen, params, navigate, goBack, goHome } = useNavigation();
2742
+ const { exit } = useApp();
2743
+ const width = useTerminalWidth();
2744
+ const height = useTerminalHeight();
2745
+ const handleExit = () => {
2746
+ process.stdout.write(
2747
+ "\n" + colors.dim("Thank you for using ") + colors.primaryBold("Polter") + colors.dim("!") + "\n\n"
2748
+ );
2749
+ exit();
2750
+ };
2751
+ const renderScreen = () => {
2752
+ switch (screen) {
2753
+ case "home":
2754
+ return /* @__PURE__ */ jsx23(Home, { onNavigate: navigate, onExit: handleExit, width, height });
2755
+ case "command-args":
2756
+ return /* @__PURE__ */ jsx23(
2757
+ CommandArgs,
2758
+ {
2759
+ command: params.command ?? "",
2760
+ tool: params.tool,
2761
+ onNavigate: navigate,
2762
+ onBack: goBack,
2763
+ width
2764
+ }
2765
+ );
2766
+ case "custom-command":
2767
+ return /* @__PURE__ */ jsx23(CustomCommand, { onNavigate: navigate, onBack: goBack, width });
2768
+ case "flag-selection":
2769
+ return /* @__PURE__ */ jsx23(
2770
+ FlagSelection,
2771
+ {
2772
+ args: params.args ?? [],
2773
+ tool: params.tool,
2774
+ onNavigate: navigate,
2775
+ onBack: goBack,
2776
+ width
2777
+ }
2778
+ );
2779
+ case "confirm-execute":
2780
+ case "command-execution":
2781
+ return /* @__PURE__ */ jsx23(
2782
+ CommandExecution,
2783
+ {
2784
+ args: params.args ?? [],
2785
+ tool: params.tool,
2786
+ onBack: goBack,
2787
+ onExit: handleExit,
2788
+ width
2789
+ }
2790
+ );
2791
+ case "self-update":
2792
+ return /* @__PURE__ */ jsx23(SelfUpdate, { onBack: goBack, onExit: handleExit, width });
2793
+ case "tool-status":
2794
+ return /* @__PURE__ */ jsx23(ToolStatus, { onBack: goBack, width });
2795
+ case "project-config":
2796
+ return /* @__PURE__ */ jsx23(ProjectConfig, { onBack: goBack, width });
2797
+ case "pipeline-list":
2798
+ return /* @__PURE__ */ jsx23(PipelineList, { onNavigate: navigate, onBack: goBack, width });
2799
+ case "pipeline-builder":
2800
+ return /* @__PURE__ */ jsx23(PipelineBuilder, { onBack: goBack, width, height });
2801
+ case "pipeline-execution":
2802
+ return /* @__PURE__ */ jsx23(
2803
+ PipelineExecution,
2804
+ {
2805
+ pipelineId: params.pipelineId ?? "",
2806
+ onBack: goBack,
2807
+ onExit: handleExit,
2808
+ width
2809
+ }
2810
+ );
2811
+ default:
2812
+ return /* @__PURE__ */ jsx23(Box21, { children: /* @__PURE__ */ jsxs23(Text23, { color: "red", children: [
2813
+ "Unknown screen: ",
2814
+ screen
2815
+ ] }) });
2816
+ }
2817
+ };
2818
+ return /* @__PURE__ */ jsxs23(Box21, { flexDirection: "column", children: [
2819
+ /* @__PURE__ */ jsx23(GhostBanner, { width }),
2820
+ renderScreen()
2821
+ ] });
2822
+ }
2823
+
2824
+ // src/appPanel.tsx
2825
+ import React18 from "react";
2826
+ import { Box as Box29, Text as Text30, useApp as useApp2, useInput as useInput7 } from "ink";
2827
+
2828
+ // src/hooks/useFullscreen.ts
2829
+ import { useEffect as useEffect8 } from "react";
2830
+ var ENTER_ALT_SCREEN = "\x1B[?1049h";
2831
+ var LEAVE_ALT_SCREEN = "\x1B[?1049l";
2832
+ var HIDE_CURSOR = "\x1B[?25l";
2833
+ var SHOW_CURSOR = "\x1B[?25h";
2834
+ function useFullscreen() {
2835
+ useEffect8(() => {
2836
+ process.stdout.write(ENTER_ALT_SCREEN + HIDE_CURSOR);
2837
+ return () => {
2838
+ process.stdout.write(SHOW_CURSOR + LEAVE_ALT_SCREEN);
2839
+ };
2840
+ }, []);
2841
+ }
2842
+
2843
+ // src/hooks/usePanelNavigation.ts
2844
+ import { useState as useState16, useCallback as useCallback4 } from "react";
2845
+ function usePanelNavigation() {
2846
+ const [state, setState] = useState16({
2847
+ view: "feature",
2848
+ featureId: "database",
2849
+ innerScreen: "home",
2850
+ innerParams: {},
2851
+ innerStack: []
2852
+ });
2853
+ const selectSidebarItem = useCallback4((itemId) => {
2854
+ const featureIds = [
2855
+ "database",
2856
+ "functions",
2857
+ "deploy",
2858
+ "repo",
2859
+ "cicd",
2860
+ "auth-storage",
2861
+ "networking",
2862
+ "infrastructure",
2863
+ "setup"
2864
+ ];
2865
+ if (featureIds.includes(itemId)) {
2866
+ setState({
2867
+ view: "feature",
2868
+ featureId: itemId,
2869
+ innerScreen: "home",
2870
+ innerParams: {},
2871
+ innerStack: []
2872
+ });
2873
+ return;
2874
+ }
2875
+ const viewMap = {
2876
+ pinned: "pinned",
2877
+ "custom-command": "custom-command",
2878
+ pipelines: "pipelines",
2879
+ "tool-status": "tool-status",
2880
+ config: "config",
2881
+ "self-update": "self-update"
2882
+ };
2883
+ const view = viewMap[itemId];
2884
+ if (view) {
2885
+ setState({
2886
+ view,
2887
+ featureId: state.featureId,
2888
+ innerScreen: "home",
2889
+ innerParams: {},
2890
+ innerStack: []
2891
+ });
2892
+ }
2893
+ }, [state.featureId]);
2894
+ const navigateInner = useCallback4((screen, params) => {
2895
+ setState((prev) => ({
2896
+ ...prev,
2897
+ innerStack: [...prev.innerStack, { screen: prev.innerScreen, params: prev.innerParams }],
2898
+ innerScreen: screen,
2899
+ innerParams: params ?? {}
2900
+ }));
2901
+ }, []);
2902
+ const goBackInner = useCallback4(() => {
2903
+ setState((prev) => {
2904
+ if (prev.innerStack.length === 0) {
2905
+ return { ...prev, innerScreen: "home", innerParams: {} };
2906
+ }
2907
+ const newStack = [...prev.innerStack];
2908
+ const last = newStack.pop();
2909
+ return {
2910
+ ...prev,
2911
+ innerStack: newStack,
2912
+ innerScreen: last.screen,
2913
+ innerParams: last.params
2914
+ };
2915
+ });
2916
+ }, []);
2917
+ const goHomeInner = useCallback4(() => {
2918
+ setState((prev) => ({
2919
+ ...prev,
2920
+ innerScreen: "home",
2921
+ innerParams: {},
2922
+ innerStack: []
2923
+ }));
2924
+ }, []);
2925
+ return {
2926
+ ...state,
2927
+ selectSidebarItem,
2928
+ navigateInner,
2929
+ goBackInner,
2930
+ goHomeInner
2931
+ };
2932
+ }
2933
+
2934
+ // src/hooks/usePanelFocus.ts
2935
+ import { useState as useState17, useCallback as useCallback5 } from "react";
2936
+ function usePanelFocus() {
2937
+ const [focused, setFocused] = useState17("sidebar");
2938
+ const toggleFocus = useCallback5(() => {
2939
+ setFocused((prev) => prev === "sidebar" ? "main" : "sidebar");
2940
+ }, []);
2941
+ const focusSidebar = useCallback5(() => setFocused("sidebar"), []);
2942
+ const focusMain = useCallback5(() => setFocused("main"), []);
2943
+ return {
2944
+ focused,
2945
+ isSidebarFocused: focused === "sidebar",
2946
+ isMainFocused: focused === "main",
2947
+ toggleFocus,
2948
+ focusSidebar,
2949
+ focusMain
2950
+ };
2951
+ }
2952
+
2953
+ // src/hooks/useSidebarItems.ts
2954
+ import { useMemo as useMemo8 } from "react";
2955
+ function useSidebarItems(hasPins = false) {
2956
+ return useMemo8(() => {
2957
+ const items = [];
2958
+ if (hasPins) {
2959
+ items.push({ id: "pinned", label: "Pinned", icon: "\u{1F4CC}", type: "action" });
2960
+ items.push({ id: "__sep_pinned__", label: "---", icon: "", type: "separator" });
2961
+ }
2962
+ for (const feature of features) {
2963
+ items.push({
2964
+ id: feature.id,
2965
+ label: feature.label,
2966
+ icon: feature.icon,
2967
+ type: "feature"
2968
+ });
2969
+ }
2970
+ items.push({ id: "__sep__", label: "---", icon: "", type: "separator" });
2971
+ items.push({ id: "custom-command", label: "Custom Cmd", icon: "\u270F\uFE0F", type: "action" });
2972
+ items.push({ id: "pipelines", label: "Pipelines", icon: "\u{1F517}", type: "action" });
2973
+ items.push({ id: "tool-status", label: "Tool Status", icon: "\u{1F527}", type: "action" });
2974
+ items.push({ id: "config", label: "Config", icon: "\u2699\uFE0F", type: "action" });
2975
+ items.push({ id: "self-update", label: "Update", icon: "\u2B06\uFE0F", type: "action" });
2976
+ items.push({ id: "exit", label: "Exit", icon: "\u{1F6AA}", type: "action" });
2977
+ return items;
2978
+ }, [hasPins]);
2979
+ }
2980
+
2981
+ // src/hooks/useModal.ts
2982
+ import { useState as useState18, useCallback as useCallback6 } from "react";
2983
+ function useModal() {
2984
+ const [state, setState] = useState18(null);
2985
+ const openModal = useCallback6((content, title) => {
2986
+ setState({ content, title });
2987
+ }, []);
2988
+ const closeModal = useCallback6(() => {
2989
+ setState(null);
2990
+ }, []);
2991
+ return {
2992
+ isOpen: state !== null,
2993
+ modalContent: state?.content ?? null,
2994
+ modalTitle: state?.title ?? "",
2995
+ openModal,
2996
+ closeModal
2997
+ };
2998
+ }
2999
+
3000
+ // src/components/PanelLayout.tsx
3001
+ import { Box as Box22 } from "ink";
3002
+ import { jsx as jsx24, jsxs as jsxs24 } from "react/jsx-runtime";
3003
+ function PanelLayout({
3004
+ header,
3005
+ footer,
3006
+ sidebar,
3007
+ main: main2,
3008
+ width,
3009
+ height,
3010
+ bannerHeight,
3011
+ singlePanel = false
3012
+ }) {
3013
+ const footerHeight = 1;
3014
+ const contentHeight = Math.max(5, height - bannerHeight - footerHeight);
3015
+ const sidebarWidth = singlePanel ? 0 : panel.sidebarWidth(width);
3016
+ const mainWidth = singlePanel ? width : width - sidebarWidth;
3017
+ return /* @__PURE__ */ jsxs24(Box22, { flexDirection: "column", width, height, children: [
3018
+ header,
3019
+ /* @__PURE__ */ jsxs24(Box22, { flexDirection: "row", height: contentHeight, children: [
3020
+ !singlePanel && /* @__PURE__ */ jsx24(Box22, { width: sidebarWidth, height: contentHeight, children: sidebar }),
3021
+ /* @__PURE__ */ jsx24(Box22, { width: mainWidth, height: contentHeight, children: main2 })
3022
+ ] }),
3023
+ /* @__PURE__ */ jsx24(Box22, { height: footerHeight, children: footer })
3024
+ ] });
3025
+ }
3026
+
3027
+ // src/components/Panel.tsx
3028
+ import { Box as Box23, Text as Text24 } from "ink";
3029
+ import { jsx as jsx25, jsxs as jsxs25 } from "react/jsx-runtime";
3030
+ function Panel({
3031
+ id,
3032
+ title,
3033
+ width,
3034
+ height,
3035
+ focused = false,
3036
+ children
3037
+ }) {
3038
+ const borderColor = focused ? panel.borderFocused : panel.borderDim;
3039
+ return /* @__PURE__ */ jsxs25(
3040
+ Box23,
3041
+ {
3042
+ flexDirection: "column",
3043
+ width,
3044
+ height,
3045
+ borderStyle: "round",
3046
+ borderColor,
3047
+ overflow: "hidden",
3048
+ children: [
3049
+ title && /* @__PURE__ */ jsx25(Box23, { marginBottom: 0, children: /* @__PURE__ */ jsxs25(Text24, { color: focused ? inkColors.accent : void 0, bold: focused, dimColor: !focused, children: [
3050
+ " ",
3051
+ title,
3052
+ " "
3053
+ ] }) }),
3054
+ /* @__PURE__ */ jsx25(Box23, { flexDirection: "column", flexGrow: 1, overflow: "hidden", children })
3055
+ ]
3056
+ }
3057
+ );
3058
+ }
3059
+
3060
+ // src/components/Sidebar.tsx
3061
+ import { useEffect as useEffect9, useMemo as useMemo9, useState as useState19 } from "react";
3062
+ import { Box as Box24, Text as Text25, useInput as useInput6 } from "ink";
3063
+ import { jsx as jsx26, jsxs as jsxs26 } from "react/jsx-runtime";
3064
+ function Sidebar({
3065
+ items,
3066
+ selectedId,
3067
+ isFocused,
3068
+ height,
3069
+ onSelect,
3070
+ onHighlight
3071
+ }) {
3072
+ const selectableItems = useMemo9(
3073
+ () => items.filter((item) => item.type !== "separator"),
3074
+ [items]
3075
+ );
3076
+ const selectedIdx = selectableItems.findIndex((item) => item.id === selectedId);
3077
+ const [cursorIdx, setCursorIdx] = useState19(Math.max(0, selectedIdx));
3078
+ useEffect9(() => {
3079
+ const idx = selectableItems.findIndex((item) => item.id === selectedId);
3080
+ if (idx >= 0) setCursorIdx(idx);
3081
+ }, [selectedId, selectableItems]);
3082
+ useInput6(
3083
+ (input2, key) => {
3084
+ if (key.upArrow || input2 === "k") {
3085
+ setCursorIdx((prev) => {
3086
+ const next = prev > 0 ? prev - 1 : selectableItems.length - 1;
3087
+ const item = selectableItems[next];
3088
+ if (item && onHighlight) onHighlight(item.id);
3089
+ return next;
3090
+ });
3091
+ }
3092
+ if (key.downArrow || input2 === "j") {
3093
+ setCursorIdx((prev) => {
3094
+ const next = prev < selectableItems.length - 1 ? prev + 1 : 0;
3095
+ const item = selectableItems[next];
3096
+ if (item && onHighlight) onHighlight(item.id);
3097
+ return next;
3098
+ });
3099
+ }
3100
+ if (key.return || key.rightArrow) {
3101
+ const item = selectableItems[cursorIdx];
3102
+ if (item) {
3103
+ onSelect(item.id);
3104
+ }
3105
+ }
3106
+ },
3107
+ { isActive: isFocused }
3108
+ );
3109
+ let selectableIdx = 0;
3110
+ return /* @__PURE__ */ jsx26(Box24, { flexDirection: "column", paddingX: 1, children: items.map((item) => {
3111
+ if (item.type === "separator") {
3112
+ return /* @__PURE__ */ jsx26(Box24, { children: /* @__PURE__ */ jsx26(Text25, { dimColor: true, children: symbols.horizontal.repeat(10) }) }, item.id);
3113
+ }
3114
+ const thisIdx = selectableIdx;
3115
+ selectableIdx++;
3116
+ const isCursor = isFocused && thisIdx === cursorIdx;
3117
+ const isActive = item.id === selectedId;
3118
+ return /* @__PURE__ */ jsx26(Box24, { gap: 0, children: /* @__PURE__ */ jsxs26(
3119
+ Text25,
3120
+ {
3121
+ color: isCursor ? inkColors.accent : isActive ? inkColors.accent : void 0,
3122
+ bold: isCursor || isActive,
3123
+ dimColor: !isCursor && !isActive,
3124
+ children: [
3125
+ isCursor ? `${symbols.pointerActive} ` : " ",
3126
+ item.icon,
3127
+ " ",
3128
+ item.label
3129
+ ]
3130
+ }
3131
+ ) }, item.id);
3132
+ }) });
3133
+ }
3134
+
3135
+ // src/components/PanelFooter.tsx
3136
+ import { Box as Box25, Text as Text26 } from "ink";
3137
+ import { jsx as jsx27, jsxs as jsxs27 } from "react/jsx-runtime";
3138
+ function PanelFooter({ hints, width }) {
3139
+ return /* @__PURE__ */ jsx27(Box25, { width, children: /* @__PURE__ */ jsx27(Box25, { gap: 1, children: hints.map((hint) => /* @__PURE__ */ jsxs27(Box25, { gap: 0, children: [
3140
+ /* @__PURE__ */ jsx27(Text26, { color: inkColors.accent, bold: true, children: hint.key }),
3141
+ /* @__PURE__ */ jsxs27(Text26, { dimColor: true, children: [
3142
+ ":",
3143
+ hint.action
3144
+ ] })
3145
+ ] }, hint.key)) }) });
3146
+ }
3147
+
3148
+ // src/components/Modal.tsx
3149
+ import { Box as Box26, Text as Text27 } from "ink";
3150
+ import { jsx as jsx28, jsxs as jsxs28 } from "react/jsx-runtime";
3151
+ function Modal({
3152
+ title,
3153
+ width,
3154
+ height,
3155
+ children
3156
+ }) {
3157
+ const modalWidth = Math.min(width - 4, 60);
3158
+ const modalHeight = Math.min(height - 4, 20);
3159
+ const padX = Math.max(0, Math.floor((width - modalWidth) / 2));
3160
+ const padY = Math.max(0, Math.floor((height - modalHeight) / 2));
3161
+ return /* @__PURE__ */ jsxs28(Box26, { flexDirection: "column", width, height, children: [
3162
+ padY > 0 && /* @__PURE__ */ jsx28(Box26, { height: padY }),
3163
+ /* @__PURE__ */ jsx28(Box26, { marginLeft: padX, children: /* @__PURE__ */ jsxs28(
3164
+ Box26,
3165
+ {
3166
+ flexDirection: "column",
3167
+ width: modalWidth,
3168
+ height: modalHeight,
3169
+ borderStyle: "double",
3170
+ borderColor: inkColors.accent,
3171
+ paddingX: 1,
3172
+ children: [
3173
+ /* @__PURE__ */ jsx28(Box26, { marginBottom: 1, children: /* @__PURE__ */ jsx28(Text27, { color: inkColors.accent, bold: true, children: title }) }),
3174
+ /* @__PURE__ */ jsx28(Box26, { flexDirection: "column", flexGrow: 1, overflow: "hidden", children })
3175
+ ]
3176
+ }
3177
+ ) })
3178
+ ] });
3179
+ }
3180
+
3181
+ // src/components/FeatureCommands.tsx
3182
+ import { useEffect as useEffect10, useMemo as useMemo10, useState as useState20 } from "react";
3183
+ import { Box as Box27, Text as Text28 } from "ink";
3184
+ import { jsx as jsx29, jsxs as jsxs29 } from "react/jsx-runtime";
3185
+ function FeatureCommands({
3186
+ feature,
3187
+ onNavigate,
3188
+ onExit,
3189
+ onBack,
3190
+ onPinsChanged,
3191
+ width = 80,
3192
+ height = 24,
3193
+ isInputActive = true
3194
+ }) {
3195
+ const [pinnedCommands, setPinnedCommands2] = useState20(() => getPinnedCommands());
3196
+ const [pinnedRuns, setPinnedRuns2] = useState20(() => getPinnedRuns());
3197
+ const [pinFeedback, setPinFeedback] = useState20();
3198
+ useEffect10(() => {
3199
+ if (!pinFeedback) return;
3200
+ const timeout = setTimeout(() => setPinFeedback(void 0), 1400);
3201
+ return () => clearTimeout(timeout);
3202
+ }, [pinFeedback]);
3203
+ const items = useMemo10(
3204
+ () => buildHomeItems({
3205
+ activeFeature: feature,
3206
+ pinnedCommands,
3207
+ pinnedRuns,
3208
+ showPinnedSection: false
3209
+ }),
3210
+ [feature, pinnedCommands, pinnedRuns]
3211
+ );
3212
+ const pinnedCommandSet = useMemo10(() => new Set(pinnedCommands), [pinnedCommands]);
3213
+ const pinnedRunSet = useMemo10(() => new Set(pinnedRuns), [pinnedRuns]);
3214
+ const refreshPins = () => {
3215
+ setPinnedCommands2(getPinnedCommands());
3216
+ setPinnedRuns2(getPinnedRuns());
3217
+ };
3218
+ const handleSelect = (value, item) => {
3219
+ if (!item) return;
3220
+ if (item.kind === "command") {
3221
+ const cmdDef = findCommandByValue(value);
3222
+ if (cmdDef) {
3223
+ onNavigate("command-args", {
3224
+ command: value,
3225
+ commandId: cmdDef.id,
3226
+ tool: cmdDef.tool
3227
+ });
3228
+ } else {
3229
+ onNavigate("command-args", { command: value, tool: "supabase" });
3230
+ }
3231
+ return;
3232
+ }
3233
+ if (item.kind === "run") {
3234
+ const args = value.split(" ").filter(Boolean);
3235
+ if (args.length > 0) {
3236
+ const basePart = args[0] ?? "";
3237
+ const cmdDef = findCommandByValue(basePart);
3238
+ const tool = cmdDef?.tool ?? "supabase";
3239
+ onNavigate("confirm-execute", { args, tool });
3240
+ }
3241
+ return;
3242
+ }
3243
+ };
3244
+ const handleRightAction = (item) => {
3245
+ if (item.kind === "command") {
3246
+ const wasPinned = pinnedCommandSet.has(item.value);
3247
+ togglePinnedCommand(item.value);
3248
+ refreshPins();
3249
+ onPinsChanged?.();
3250
+ setPinFeedback(
3251
+ wasPinned ? `Unpinned "${item.value}"` : `Pinned "${item.value}"`
3252
+ );
3253
+ return;
3254
+ }
3255
+ if (item.kind === "run") {
3256
+ const wasPinned = pinnedRunSet.has(item.value);
3257
+ togglePinnedRun(item.value);
3258
+ refreshPins();
3259
+ onPinsChanged?.();
3260
+ setPinFeedback(
3261
+ wasPinned ? `Unpinned run "${item.value}"` : `Pinned run "${item.value}"`
3262
+ );
3263
+ }
3264
+ };
3265
+ return /* @__PURE__ */ jsxs29(Box27, { flexDirection: "column", paddingX: 1, children: [
3266
+ /* @__PURE__ */ jsx29(Box27, { marginBottom: 1, children: /* @__PURE__ */ jsxs29(Text28, { color: inkColors.accent, bold: true, children: [
3267
+ feature.icon,
3268
+ " ",
3269
+ feature.label
3270
+ ] }) }),
3271
+ pinFeedback && /* @__PURE__ */ jsx29(Box27, { marginBottom: 1, children: /* @__PURE__ */ jsxs29(Text28, { color: inkColors.accent, children: [
3272
+ "\u2713 ",
3273
+ pinFeedback
3274
+ ] }) }),
3275
+ /* @__PURE__ */ jsx29(
3276
+ SelectList,
3277
+ {
3278
+ items,
3279
+ onSelect: handleSelect,
3280
+ onRightAction: handleRightAction,
3281
+ boxedSections: true,
3282
+ width: Math.max(20, width - 4),
3283
+ maxVisible: Math.max(6, height - 6),
3284
+ onCancel: onBack,
3285
+ isInputActive,
3286
+ arrowNavigation: true
3287
+ }
3288
+ )
3289
+ ] });
3290
+ }
3291
+
3292
+ // src/components/PinnedCommands.tsx
3293
+ import { useEffect as useEffect11, useMemo as useMemo11, useState as useState21 } from "react";
3294
+ import { Box as Box28, Text as Text29 } from "ink";
3295
+ import { jsx as jsx30, jsxs as jsxs30 } from "react/jsx-runtime";
3296
+ function PinnedCommands({
3297
+ onNavigate,
3298
+ onBack,
3299
+ onPinsChanged,
3300
+ width = 80,
3301
+ height = 24,
3302
+ isInputActive = true
3303
+ }) {
3304
+ const [pinnedCommands, setPinnedCommands2] = useState21(() => getPinnedCommands());
3305
+ const [pinnedRuns, setPinnedRuns2] = useState21(() => getPinnedRuns());
3306
+ const [pinFeedback, setPinFeedback] = useState21();
3307
+ useEffect11(() => {
3308
+ if (!pinFeedback) return;
3309
+ const timeout = setTimeout(() => setPinFeedback(void 0), 1400);
3310
+ return () => clearTimeout(timeout);
3311
+ }, [pinFeedback]);
3312
+ const items = useMemo11(
3313
+ () => buildPinnedOnlyItems(pinnedCommands, pinnedRuns),
3314
+ [pinnedCommands, pinnedRuns]
3315
+ );
3316
+ const pinnedCommandSet = useMemo11(() => new Set(pinnedCommands), [pinnedCommands]);
3317
+ const pinnedRunSet = useMemo11(() => new Set(pinnedRuns), [pinnedRuns]);
3318
+ const refreshPins = () => {
3319
+ setPinnedCommands2(getPinnedCommands());
3320
+ setPinnedRuns2(getPinnedRuns());
3321
+ };
3322
+ const handleSelect = (value, item) => {
3323
+ if (!item) return;
3324
+ if (item.kind === "command") {
3325
+ const cmdDef = findCommandByValue(value);
3326
+ if (cmdDef) {
3327
+ onNavigate("command-args", {
3328
+ command: value,
3329
+ commandId: cmdDef.id,
3330
+ tool: cmdDef.tool
3331
+ });
3332
+ } else {
3333
+ onNavigate("command-args", { command: value, tool: "supabase" });
3334
+ }
3335
+ return;
3336
+ }
3337
+ if (item.kind === "run") {
3338
+ const args = value.split(" ").filter(Boolean);
3339
+ if (args.length > 0) {
3340
+ const basePart = args[0] ?? "";
3341
+ const cmdDef = findCommandByValue(basePart);
3342
+ const tool = cmdDef?.tool ?? "supabase";
3343
+ onNavigate("confirm-execute", { args, tool });
3344
+ }
3345
+ }
3346
+ };
3347
+ const handleRightAction = (item) => {
3348
+ if (item.kind === "command") {
3349
+ const wasPinned = pinnedCommandSet.has(item.value);
3350
+ togglePinnedCommand(item.value);
3351
+ refreshPins();
3352
+ onPinsChanged?.();
3353
+ setPinFeedback(
3354
+ wasPinned ? `Unpinned "${item.value}"` : `Pinned "${item.value}"`
3355
+ );
3356
+ return;
3357
+ }
3358
+ if (item.kind === "run") {
3359
+ const wasPinned = pinnedRunSet.has(item.value);
3360
+ togglePinnedRun(item.value);
3361
+ refreshPins();
3362
+ onPinsChanged?.();
3363
+ setPinFeedback(
3364
+ wasPinned ? `Unpinned run "${item.value}"` : `Pinned run "${item.value}"`
3365
+ );
3366
+ }
3367
+ };
3368
+ if (items.length === 0) {
3369
+ return /* @__PURE__ */ jsxs30(Box28, { flexDirection: "column", paddingX: 1, children: [
3370
+ /* @__PURE__ */ jsx30(Box28, { marginBottom: 1, children: /* @__PURE__ */ jsx30(Text29, { color: inkColors.accent, bold: true, children: "\u{1F4CC} Pinned" }) }),
3371
+ /* @__PURE__ */ jsx30(Text29, { color: "gray", children: "No pinned items. Press p on any command to pin it." })
3372
+ ] });
3373
+ }
3374
+ return /* @__PURE__ */ jsxs30(Box28, { flexDirection: "column", paddingX: 1, children: [
3375
+ /* @__PURE__ */ jsx30(Box28, { marginBottom: 1, children: /* @__PURE__ */ jsx30(Text29, { color: inkColors.accent, bold: true, children: "\u{1F4CC} Pinned" }) }),
3376
+ pinFeedback && /* @__PURE__ */ jsx30(Box28, { marginBottom: 1, children: /* @__PURE__ */ jsxs30(Text29, { color: inkColors.accent, children: [
3377
+ "\u2713 ",
3378
+ pinFeedback
3379
+ ] }) }),
3380
+ /* @__PURE__ */ jsx30(
1909
3381
  SelectList,
1910
3382
  {
1911
- items: [
1912
- { value: "retry", label: "\u{1F504} Retry update" },
1913
- ...repositoryRoot ? [{ value: "target", label: "\u2194 Choose update target" }] : [],
1914
- { value: "menu", label: "\u2190 Return to main menu" },
1915
- { value: "exit", label: "\u{1F6AA} Exit Polter" }
1916
- ],
1917
- onSelect: (value) => {
1918
- switch (value) {
1919
- case "retry":
1920
- reset();
1921
- setPhase("running");
1922
- break;
1923
- case "target":
1924
- reset();
1925
- setPhase("target");
1926
- break;
1927
- case "menu":
1928
- onBack();
1929
- break;
1930
- case "exit":
1931
- onExit();
1932
- break;
1933
- }
1934
- },
1935
- onCancel: onBack
3383
+ items,
3384
+ onSelect: handleSelect,
3385
+ onRightAction: handleRightAction,
3386
+ boxedSections: true,
3387
+ width: Math.max(20, width - 4),
3388
+ maxVisible: Math.max(6, height - 6),
3389
+ onCancel: onBack,
3390
+ isInputActive,
3391
+ arrowNavigation: true
1936
3392
  }
1937
- ),
1938
- /* @__PURE__ */ jsx14(StatusBar, {})
3393
+ )
1939
3394
  ] });
1940
3395
  }
1941
3396
 
1942
- // src/app.tsx
1943
- import { jsx as jsx15, jsxs as jsxs15 } from "react/jsx-runtime";
1944
- function App() {
1945
- const { screen, params, navigate, goBack } = useNavigation();
1946
- const { exit } = useApp();
3397
+ // src/appPanel.tsx
3398
+ import { jsx as jsx31, jsxs as jsxs31 } from "react/jsx-runtime";
3399
+ var FOOTER_HINTS = [
3400
+ { key: "\u2190\u2192", action: "panels" },
3401
+ { key: "j/k", action: "nav" },
3402
+ { key: "Enter", action: "select" },
3403
+ { key: "Esc", action: "back" },
3404
+ { key: "q", action: "quit" },
3405
+ { key: "p", action: "pin" },
3406
+ { key: "?", action: "help" }
3407
+ ];
3408
+ function AppPanel() {
3409
+ useFullscreen();
3410
+ const { width, height } = useTerminalDimensions();
3411
+ const { exit } = useApp2();
3412
+ const nav = usePanelNavigation();
3413
+ const focus = usePanelFocus();
3414
+ const [hasPins, setHasPins] = React18.useState(
3415
+ () => getPinnedCommands().length > 0 || getPinnedRuns().length > 0
3416
+ );
3417
+ const sidebarItems = useSidebarItems(hasPins);
3418
+ const modal = useModal();
3419
+ const refreshPins = React18.useCallback(() => {
3420
+ const newHasPins = getPinnedCommands().length > 0 || getPinnedRuns().length > 0;
3421
+ setHasPins(newHasPins);
3422
+ if (!newHasPins && nav.view === "pinned") {
3423
+ nav.selectSidebarItem("database");
3424
+ }
3425
+ }, [nav.view, nav.selectSidebarItem]);
3426
+ const singlePanel = width < 60 || height < 15;
1947
3427
  const handleExit = () => {
1948
3428
  process.stdout.write(
1949
3429
  "\n" + colors.dim("Thank you for using ") + colors.primaryBold("Polter") + colors.dim("!") + "\n\n"
1950
3430
  );
1951
3431
  exit();
1952
3432
  };
1953
- switch (screen) {
1954
- case "main-menu":
1955
- return /* @__PURE__ */ jsx15(MainMenu, { onNavigate: navigate, onExit: handleExit });
1956
- case "command-args":
1957
- return /* @__PURE__ */ jsx15(
1958
- CommandArgs,
1959
- {
1960
- command: params.command ?? "",
1961
- onNavigate: navigate,
1962
- onBack: goBack
1963
- }
3433
+ useInput7((input2, key) => {
3434
+ if (modal.isOpen) {
3435
+ if (key.escape || input2 === "q") {
3436
+ modal.closeModal();
3437
+ }
3438
+ return;
3439
+ }
3440
+ if (input2 === "q") {
3441
+ handleExit();
3442
+ return;
3443
+ }
3444
+ if (key.tab) {
3445
+ focus.toggleFocus();
3446
+ return;
3447
+ }
3448
+ if (key.leftArrow && focus.isMainFocused && nav.innerScreen === "home") {
3449
+ focus.focusSidebar();
3450
+ return;
3451
+ }
3452
+ if (key.escape && focus.isMainFocused && nav.innerScreen === "home") {
3453
+ focus.focusSidebar();
3454
+ return;
3455
+ }
3456
+ if (input2 === "?") {
3457
+ modal.openModal(
3458
+ /* @__PURE__ */ jsxs31(Box29, { flexDirection: "column", children: [
3459
+ /* @__PURE__ */ jsxs31(Text30, { children: [
3460
+ /* @__PURE__ */ jsx31(Text30, { bold: true, children: "\u2190/\u2192" }),
3461
+ " Move between sidebar and main panel"
3462
+ ] }),
3463
+ /* @__PURE__ */ jsxs31(Text30, { children: [
3464
+ /* @__PURE__ */ jsx31(Text30, { bold: true, children: "Tab" }),
3465
+ " Toggle sidebar / main panel"
3466
+ ] }),
3467
+ /* @__PURE__ */ jsxs31(Text30, { children: [
3468
+ /* @__PURE__ */ jsx31(Text30, { bold: true, children: "j/k" }),
3469
+ " Navigate up/down"
3470
+ ] }),
3471
+ /* @__PURE__ */ jsxs31(Text30, { children: [
3472
+ /* @__PURE__ */ jsx31(Text30, { bold: true, children: "Enter" }),
3473
+ " Select item"
3474
+ ] }),
3475
+ /* @__PURE__ */ jsxs31(Text30, { children: [
3476
+ /* @__PURE__ */ jsx31(Text30, { bold: true, children: "Esc" }),
3477
+ " Go back (or return to sidebar)"
3478
+ ] }),
3479
+ /* @__PURE__ */ jsxs31(Text30, { children: [
3480
+ /* @__PURE__ */ jsx31(Text30, { bold: true, children: "q" }),
3481
+ " Quit Polter"
3482
+ ] }),
3483
+ /* @__PURE__ */ jsxs31(Text30, { children: [
3484
+ /* @__PURE__ */ jsx31(Text30, { bold: true, children: "?" }),
3485
+ " Show this help"
3486
+ ] }),
3487
+ /* @__PURE__ */ jsxs31(Text30, { children: [
3488
+ /* @__PURE__ */ jsx31(Text30, { bold: true, children: "p" }),
3489
+ " Pin/unpin command"
3490
+ ] })
3491
+ ] }),
3492
+ "Keyboard Shortcuts"
1964
3493
  );
1965
- case "custom-command":
1966
- return /* @__PURE__ */ jsx15(CustomCommand, { onNavigate: navigate, onBack: goBack });
1967
- case "flag-selection":
1968
- return /* @__PURE__ */ jsx15(
1969
- FlagSelection,
1970
- {
1971
- args: params.args ?? [],
1972
- onNavigate: navigate,
1973
- onBack: goBack
3494
+ }
3495
+ });
3496
+ const activeSidebarId = (() => {
3497
+ switch (nav.view) {
3498
+ case "feature":
3499
+ return nav.featureId;
3500
+ case "pinned":
3501
+ return "pinned";
3502
+ case "custom-command":
3503
+ return "custom-command";
3504
+ case "pipelines":
3505
+ return "pipelines";
3506
+ case "tool-status":
3507
+ return "tool-status";
3508
+ case "config":
3509
+ return "config";
3510
+ case "self-update":
3511
+ return "self-update";
3512
+ default:
3513
+ return nav.featureId;
3514
+ }
3515
+ })();
3516
+ const handleSidebarHighlight = (itemId) => {
3517
+ if (itemId === "exit") return;
3518
+ nav.selectSidebarItem(itemId);
3519
+ };
3520
+ const handleSidebarSelect = (itemId) => {
3521
+ if (itemId === "exit") {
3522
+ handleExit();
3523
+ return;
3524
+ }
3525
+ nav.selectSidebarItem(itemId);
3526
+ focus.focusMain();
3527
+ };
3528
+ const bannerHeight = width < 60 ? 1 : 8;
3529
+ const footerHints = FOOTER_HINTS;
3530
+ const mainContentHeight = Math.max(5, height - bannerHeight - 1 - 2);
3531
+ const mainContentWidth = singlePanel ? width : width - Math.max(20, Math.min(35, Math.floor(width * 0.3)));
3532
+ const renderMainContent = () => {
3533
+ if (nav.innerScreen !== "home") {
3534
+ return renderInnerScreen();
3535
+ }
3536
+ switch (nav.view) {
3537
+ case "pinned":
3538
+ return /* @__PURE__ */ jsx31(
3539
+ PinnedCommands,
3540
+ {
3541
+ onNavigate: nav.navigateInner,
3542
+ onBack: focus.focusSidebar,
3543
+ onPinsChanged: refreshPins,
3544
+ width: mainContentWidth - 2,
3545
+ height: mainContentHeight,
3546
+ isInputActive: focus.isMainFocused
3547
+ }
3548
+ );
3549
+ case "feature": {
3550
+ const feature = getFeatureById(nav.featureId);
3551
+ if (!feature) {
3552
+ return /* @__PURE__ */ jsxs31(Text30, { color: "red", children: [
3553
+ "Feature not found: ",
3554
+ nav.featureId
3555
+ ] });
1974
3556
  }
1975
- );
1976
- case "confirm-execute":
1977
- case "command-execution":
1978
- return /* @__PURE__ */ jsx15(
1979
- CommandExecution,
3557
+ return /* @__PURE__ */ jsx31(
3558
+ FeatureCommands,
3559
+ {
3560
+ feature,
3561
+ onNavigate: nav.navigateInner,
3562
+ onExit: handleExit,
3563
+ onBack: focus.focusSidebar,
3564
+ onPinsChanged: refreshPins,
3565
+ width: mainContentWidth - 2,
3566
+ height: mainContentHeight,
3567
+ isInputActive: focus.isMainFocused
3568
+ }
3569
+ );
3570
+ }
3571
+ case "custom-command":
3572
+ return /* @__PURE__ */ jsx31(
3573
+ CustomCommand,
3574
+ {
3575
+ onNavigate: nav.navigateInner,
3576
+ onBack: focus.focusSidebar,
3577
+ width: mainContentWidth - 2,
3578
+ panelMode: true,
3579
+ isInputActive: focus.isMainFocused
3580
+ }
3581
+ );
3582
+ case "pipelines":
3583
+ return /* @__PURE__ */ jsx31(
3584
+ PipelineList,
3585
+ {
3586
+ onNavigate: nav.navigateInner,
3587
+ onBack: focus.focusSidebar,
3588
+ width: mainContentWidth - 2,
3589
+ panelMode: true,
3590
+ isInputActive: focus.isMainFocused
3591
+ }
3592
+ );
3593
+ case "tool-status":
3594
+ return /* @__PURE__ */ jsx31(
3595
+ ToolStatus,
3596
+ {
3597
+ onBack: focus.focusSidebar,
3598
+ width: mainContentWidth - 2,
3599
+ panelMode: true,
3600
+ isInputActive: focus.isMainFocused
3601
+ }
3602
+ );
3603
+ case "config":
3604
+ return /* @__PURE__ */ jsx31(
3605
+ ProjectConfig,
3606
+ {
3607
+ onBack: focus.focusSidebar,
3608
+ width: mainContentWidth - 2,
3609
+ panelMode: true,
3610
+ isInputActive: focus.isMainFocused
3611
+ }
3612
+ );
3613
+ case "self-update":
3614
+ return /* @__PURE__ */ jsx31(
3615
+ SelfUpdate,
3616
+ {
3617
+ onBack: focus.focusSidebar,
3618
+ onExit: handleExit,
3619
+ width: mainContentWidth - 2,
3620
+ panelMode: true,
3621
+ isInputActive: focus.isMainFocused
3622
+ }
3623
+ );
3624
+ default:
3625
+ return /* @__PURE__ */ jsx31(Text30, { children: "Select an item from the sidebar" });
3626
+ }
3627
+ };
3628
+ const renderInnerScreen = () => {
3629
+ const isActive = focus.isMainFocused;
3630
+ const w = mainContentWidth - 2;
3631
+ switch (nav.innerScreen) {
3632
+ case "command-args":
3633
+ return /* @__PURE__ */ jsx31(
3634
+ CommandArgs,
3635
+ {
3636
+ command: nav.innerParams.command ?? "",
3637
+ tool: nav.innerParams.tool,
3638
+ onNavigate: nav.navigateInner,
3639
+ onBack: nav.goBackInner,
3640
+ width: w,
3641
+ panelMode: true,
3642
+ isInputActive: isActive
3643
+ }
3644
+ );
3645
+ case "custom-command":
3646
+ return /* @__PURE__ */ jsx31(
3647
+ CustomCommand,
3648
+ {
3649
+ onNavigate: nav.navigateInner,
3650
+ onBack: nav.goBackInner,
3651
+ width: w,
3652
+ panelMode: true,
3653
+ isInputActive: isActive
3654
+ }
3655
+ );
3656
+ case "flag-selection":
3657
+ return /* @__PURE__ */ jsx31(
3658
+ FlagSelection,
3659
+ {
3660
+ args: nav.innerParams.args ?? [],
3661
+ tool: nav.innerParams.tool,
3662
+ onNavigate: nav.navigateInner,
3663
+ onBack: nav.goBackInner,
3664
+ width: w,
3665
+ panelMode: true,
3666
+ isInputActive: isActive
3667
+ }
3668
+ );
3669
+ case "confirm-execute":
3670
+ case "command-execution":
3671
+ return /* @__PURE__ */ jsx31(
3672
+ CommandExecution,
3673
+ {
3674
+ args: nav.innerParams.args ?? [],
3675
+ tool: nav.innerParams.tool,
3676
+ onBack: nav.goBackInner,
3677
+ onExit: handleExit,
3678
+ width: w,
3679
+ panelMode: true,
3680
+ isInputActive: isActive
3681
+ }
3682
+ );
3683
+ case "pipeline-list":
3684
+ return /* @__PURE__ */ jsx31(
3685
+ PipelineList,
3686
+ {
3687
+ onNavigate: nav.navigateInner,
3688
+ onBack: nav.goBackInner,
3689
+ width: w,
3690
+ panelMode: true,
3691
+ isInputActive: isActive
3692
+ }
3693
+ );
3694
+ case "pipeline-builder":
3695
+ return /* @__PURE__ */ jsx31(
3696
+ PipelineBuilder,
3697
+ {
3698
+ onBack: nav.goBackInner,
3699
+ width: w,
3700
+ height: mainContentHeight,
3701
+ panelMode: true,
3702
+ isInputActive: isActive
3703
+ }
3704
+ );
3705
+ case "pipeline-execution":
3706
+ return /* @__PURE__ */ jsx31(
3707
+ PipelineExecution,
3708
+ {
3709
+ pipelineId: nav.innerParams.pipelineId ?? "",
3710
+ onBack: nav.goBackInner,
3711
+ onExit: handleExit,
3712
+ width: w,
3713
+ panelMode: true,
3714
+ isInputActive: isActive
3715
+ }
3716
+ );
3717
+ case "self-update":
3718
+ return /* @__PURE__ */ jsx31(
3719
+ SelfUpdate,
3720
+ {
3721
+ onBack: nav.goBackInner,
3722
+ onExit: handleExit,
3723
+ width: w,
3724
+ panelMode: true,
3725
+ isInputActive: isActive
3726
+ }
3727
+ );
3728
+ case "tool-status":
3729
+ return /* @__PURE__ */ jsx31(
3730
+ ToolStatus,
3731
+ {
3732
+ onBack: nav.goBackInner,
3733
+ width: w,
3734
+ panelMode: true,
3735
+ isInputActive: isActive
3736
+ }
3737
+ );
3738
+ case "project-config":
3739
+ return /* @__PURE__ */ jsx31(
3740
+ ProjectConfig,
3741
+ {
3742
+ onBack: nav.goBackInner,
3743
+ width: w,
3744
+ panelMode: true,
3745
+ isInputActive: isActive
3746
+ }
3747
+ );
3748
+ default:
3749
+ return /* @__PURE__ */ jsxs31(Text30, { color: "red", children: [
3750
+ "Unknown screen: ",
3751
+ nav.innerScreen
3752
+ ] });
3753
+ }
3754
+ };
3755
+ if (modal.isOpen) {
3756
+ return /* @__PURE__ */ jsx31(Box29, { flexDirection: "column", width, height, children: /* @__PURE__ */ jsx31(Modal, { title: modal.modalTitle, width, height, children: modal.modalContent }) });
3757
+ }
3758
+ const header = /* @__PURE__ */ jsx31(GhostBanner, { width, compact: true });
3759
+ const sidebar = /* @__PURE__ */ jsx31(
3760
+ Panel,
3761
+ {
3762
+ id: "sidebar",
3763
+ title: "Menu",
3764
+ width: Math.max(20, Math.min(35, Math.floor(width * 0.3))),
3765
+ height: Math.max(5, height - bannerHeight - 1),
3766
+ focused: focus.isSidebarFocused,
3767
+ children: /* @__PURE__ */ jsx31(
3768
+ Sidebar,
1980
3769
  {
1981
- args: params.args ?? [],
1982
- onBack: goBack,
1983
- onExit: handleExit
3770
+ items: sidebarItems,
3771
+ selectedId: activeSidebarId,
3772
+ isFocused: focus.isSidebarFocused,
3773
+ height: Math.max(5, height - bannerHeight - 4),
3774
+ onSelect: handleSidebarSelect,
3775
+ onHighlight: handleSidebarHighlight
1984
3776
  }
1985
- );
1986
- case "self-update":
1987
- return /* @__PURE__ */ jsx15(SelfUpdate, { onBack: goBack, onExit: handleExit });
1988
- default:
1989
- return /* @__PURE__ */ jsx15(Box14, { children: /* @__PURE__ */ jsxs15(Text13, { color: "red", children: [
1990
- "Unknown screen: ",
1991
- screen
1992
- ] }) });
1993
- }
3777
+ )
3778
+ }
3779
+ );
3780
+ const main2 = /* @__PURE__ */ jsx31(
3781
+ Panel,
3782
+ {
3783
+ id: "main",
3784
+ title: nav.view === "feature" ? getFeatureById(nav.featureId)?.label : nav.view === "pinned" ? "Pinned" : nav.view,
3785
+ width: mainContentWidth,
3786
+ height: Math.max(5, height - bannerHeight - 1),
3787
+ focused: focus.isMainFocused,
3788
+ children: renderMainContent()
3789
+ }
3790
+ );
3791
+ const footer = /* @__PURE__ */ jsx31(PanelFooter, { hints: footerHints, width });
3792
+ return /* @__PURE__ */ jsx31(
3793
+ PanelLayout,
3794
+ {
3795
+ header,
3796
+ footer,
3797
+ sidebar,
3798
+ main: main2,
3799
+ width,
3800
+ height,
3801
+ bannerHeight,
3802
+ singlePanel
3803
+ }
3804
+ );
1994
3805
  }
1995
3806
 
1996
3807
  // src/lib/cliArgs.ts
@@ -2009,20 +3820,39 @@ function takeValue(args, index) {
2009
3820
  return { value: args[index + 1], nextIndex: index + 1 };
2010
3821
  }
2011
3822
  function parseCliArgs(argv) {
2012
- if (argv.length === 0) {
2013
- return { mode: "interactive", options: {} };
3823
+ const classic = argv.includes("--classic");
3824
+ const filteredArgv = argv.filter((a) => a !== "--classic");
3825
+ if (filteredArgv.length === 0) {
3826
+ return { mode: "interactive", options: {}, classic };
2014
3827
  }
2015
- if (argv[0] === "--help" || argv[0] === "help") {
3828
+ if (filteredArgv[0] === "--help" || filteredArgv[0] === "help") {
2016
3829
  return { mode: "help", options: {} };
2017
3830
  }
2018
- if (argv[0] !== "app") {
2019
- return { mode: "interactive", options: {} };
3831
+ const argv_ = filteredArgv;
3832
+ if (argv_[0] === "pipeline" && argv_[1] === "run" && argv_[2]) {
3833
+ return { mode: "pipeline-run", options: {}, pipelineName: argv_[2] };
3834
+ }
3835
+ if (argv_[0] === "config") {
3836
+ const edit = argv_.includes("--edit");
3837
+ return { mode: "config", options: {}, configEdit: edit, classic };
3838
+ }
3839
+ if (argv_[0] === "plan") {
3840
+ return { mode: "plan", options: {} };
3841
+ }
3842
+ if (argv_[0] === "apply") {
3843
+ return { mode: "apply", options: {} };
3844
+ }
3845
+ if (argv_[0] === "status") {
3846
+ return { mode: "status", options: {} };
3847
+ }
3848
+ if (argv_[0] !== "app") {
3849
+ return { mode: "interactive", options: {}, classic };
2020
3850
  }
2021
3851
  const options = {};
2022
- options.action = argv[1];
2023
- options.app = argv[2];
2024
- for (let index = 3; index < argv.length; index += 1) {
2025
- const token = argv[index];
3852
+ options.action = argv_[1];
3853
+ options.app = argv_[2];
3854
+ for (let index = 3; index < argv_.length; index += 1) {
3855
+ const token = argv_[index];
2026
3856
  if (token === "push" || token === "lint" || token === "reset" || token === "local-reset") {
2027
3857
  options.migrationAction = token;
2028
3858
  continue;
@@ -2044,25 +3874,25 @@ function parseCliArgs(argv) {
2044
3874
  continue;
2045
3875
  }
2046
3876
  if (token.startsWith("--path")) {
2047
- const parsed = takeValue(argv, index);
3877
+ const parsed = takeValue(argv_, index);
2048
3878
  options.path = parsed.value;
2049
3879
  index = parsed.nextIndex;
2050
3880
  continue;
2051
3881
  }
2052
3882
  if (token.startsWith("--version")) {
2053
- const parsed = takeValue(argv, index);
3883
+ const parsed = takeValue(argv_, index);
2054
3884
  options.version = parsed.value;
2055
3885
  index = parsed.nextIndex;
2056
3886
  continue;
2057
3887
  }
2058
3888
  if (token.startsWith("--artifact-url")) {
2059
- const parsed = takeValue(argv, index);
3889
+ const parsed = takeValue(argv_, index);
2060
3890
  options.artifactUrl = parsed.value;
2061
3891
  index = parsed.nextIndex;
2062
3892
  continue;
2063
3893
  }
2064
3894
  if (token.startsWith("--install-dir")) {
2065
- const parsed = takeValue(argv, index);
3895
+ const parsed = takeValue(argv_, index);
2066
3896
  options.installDir = parsed.value;
2067
3897
  index = parsed.nextIndex;
2068
3898
  continue;
@@ -2076,10 +3906,17 @@ function parseCliArgs(argv) {
2076
3906
  function printCliHelp() {
2077
3907
  process.stdout.write(
2078
3908
  [
2079
- "Polter",
3909
+ "Polter \u2014 Project & Infrastructure Orchestrator",
2080
3910
  "",
2081
3911
  "Usage:",
2082
- " polter",
3912
+ " polter Interactive mode",
3913
+ " polter pipeline run <name> Run a saved pipeline",
3914
+ " polter config Manage per-repo config",
3915
+ " polter config --edit Open config in $EDITOR",
3916
+ " polter plan Show declarative state diff",
3917
+ " polter apply Apply declarative state changes",
3918
+ " polter status Show current tool status",
3919
+ "",
2083
3920
  " polter app setup ops [--path <dir>] [--create-project|--use-existing-project] [--yes]",
2084
3921
  " polter app link ops [--path <dir>] [--relink]",
2085
3922
  " polter app migrate ops [push|lint|reset|local-reset] [--path <dir>] [--relink]",
@@ -2087,12 +3924,8 @@ function printCliHelp() {
2087
3924
  " polter app install ops [--version <version>] [--artifact-url <url>] [--install-dir <dir>] [--yes]",
2088
3925
  " polter app update ops [--version <version>] [--artifact-url <url>] [--install-dir <dir>] [--yes]",
2089
3926
  "",
2090
- "Notes:",
2091
- " - App workflows stay separate from the generic Supabase interactive menu.",
2092
- " - `install ops` is macOS-only and resolves the latest GitHub release from polterware/ops by default.",
2093
- " - `update ops` replaces the installed app bundle without touching the persisted runtime/app state.",
2094
- " - Use --artifact-url or POLTER_OPS_MACOS_ARTIFACT_URL to override the downloaded asset.",
2095
- " - Use POLTER_OPS_GITHUB_REPO=owner/repo to resolve releases from a different repository.",
3927
+ "Options:",
3928
+ " --classic Use classic single-screen UI",
2096
3929
  ""
2097
3930
  ].join("\n")
2098
3931
  );
@@ -2102,19 +3935,19 @@ function printCliHelp() {
2102
3935
  import pc3 from "picocolors";
2103
3936
 
2104
3937
  // src/apps/ops.ts
2105
- import { existsSync as existsSync3, mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
3938
+ import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
2106
3939
  import { mkdtemp, readdir, stat } from "fs/promises";
2107
- import { dirname as dirname3, join as join4, resolve as resolve3 } from "path";
3940
+ import { dirname, join as join2, resolve } from "path";
2108
3941
  import { tmpdir } from "os";
2109
3942
  import pc2 from "picocolors";
2110
3943
 
2111
3944
  // src/apps/bootstrapPaths.ts
2112
3945
  import { homedir } from "os";
2113
- import { join as join3 } from "path";
3946
+ import { join } from "path";
2114
3947
  function getOpsBootstrapPayloadPath() {
2115
3948
  const home = homedir();
2116
3949
  if (process.platform === "darwin") {
2117
- return join3(
3950
+ return join(
2118
3951
  home,
2119
3952
  "Library",
2120
3953
  "Application Support",
@@ -2124,10 +3957,10 @@ function getOpsBootstrapPayloadPath() {
2124
3957
  );
2125
3958
  }
2126
3959
  if (process.platform === "win32") {
2127
- const appData = process.env.APPDATA ?? join3(home, "AppData", "Roaming");
2128
- return join3(appData, "ops", "bootstrap", "supabase.json");
3960
+ const appData = process.env.APPDATA ?? join(home, "AppData", "Roaming");
3961
+ return join(appData, "ops", "bootstrap", "supabase.json");
2129
3962
  }
2130
- return join3(home, ".config", "ops", "bootstrap", "supabase.json");
3963
+ return join(home, ".config", "ops", "bootstrap", "supabase.json");
2131
3964
  }
2132
3965
 
2133
3966
  // src/apps/opsRelease.ts
@@ -2398,33 +4231,22 @@ async function promptSelect(label, options, defaultValue) {
2398
4231
  }
2399
4232
  }
2400
4233
 
2401
- // src/lib/system.ts
2402
- import { execSync } from "child_process";
2403
- function commandExists(command) {
2404
- try {
2405
- execSync(`command -v ${command}`, { stdio: "ignore" });
2406
- return true;
2407
- } catch {
2408
- return false;
2409
- }
2410
- }
2411
-
2412
4234
  // src/apps/ops.ts
2413
- var LINK_REF_FILE = join4("supabase", ".temp", "project-ref");
4235
+ var LINK_REF_FILE = join2("supabase", ".temp", "project-ref");
2414
4236
  function isOpsProjectRoot(candidate) {
2415
- return existsSync3(join4(candidate, "src-tauri", "tauri.conf.json")) && existsSync3(join4(candidate, "supabase", "migrations")) && existsSync3(join4(candidate, "package.json"));
4237
+ return existsSync(join2(candidate, "src-tauri", "tauri.conf.json")) && existsSync(join2(candidate, "supabase", "migrations")) && existsSync(join2(candidate, "package.json"));
2416
4238
  }
2417
4239
  function findNearestOpsRoot(startDir) {
2418
- let currentDir = resolve3(startDir);
4240
+ let currentDir = resolve(startDir);
2419
4241
  while (true) {
2420
4242
  if (isOpsProjectRoot(currentDir)) {
2421
4243
  return currentDir;
2422
4244
  }
2423
- const siblingCandidate = join4(currentDir, "ops");
4245
+ const siblingCandidate = join2(currentDir, "ops");
2424
4246
  if (isOpsProjectRoot(siblingCandidate)) {
2425
4247
  return siblingCandidate;
2426
4248
  }
2427
- const parentDir = dirname3(currentDir);
4249
+ const parentDir = dirname(currentDir);
2428
4250
  if (parentDir === currentDir) {
2429
4251
  return void 0;
2430
4252
  }
@@ -2432,7 +4254,7 @@ function findNearestOpsRoot(startDir) {
2432
4254
  }
2433
4255
  }
2434
4256
  function readEnvFile(envPath) {
2435
- if (!existsSync3(envPath)) {
4257
+ if (!existsSync(envPath)) {
2436
4258
  return {};
2437
4259
  }
2438
4260
  const content = readFileSync(envPath, "utf-8");
@@ -2466,8 +4288,8 @@ function assertProjectRoot(projectRoot) {
2466
4288
  return projectRoot;
2467
4289
  }
2468
4290
  function getLinkedProjectRef(projectRoot) {
2469
- const refPath = join4(projectRoot, LINK_REF_FILE);
2470
- if (!existsSync3(refPath)) {
4291
+ const refPath = join2(projectRoot, LINK_REF_FILE);
4292
+ if (!existsSync(refPath)) {
2471
4293
  return null;
2472
4294
  }
2473
4295
  const value = readFileSync(refPath, "utf-8").trim();
@@ -2524,7 +4346,7 @@ async function ensureSupabaseLink(projectRoot, forceRelink = false) {
2524
4346
  );
2525
4347
  }
2526
4348
  async function collectSupabaseConfig(projectRoot) {
2527
- const envPath = projectRoot ? join4(projectRoot, ".env.local") : void 0;
4349
+ const envPath = projectRoot ? join2(projectRoot, ".env.local") : void 0;
2528
4350
  const currentEnv = envPath ? readEnvFile(envPath) : {};
2529
4351
  const currentRef = projectRoot ? getLinkedProjectRef(projectRoot) : null;
2530
4352
  const url = await promptText("Supabase URL", {
@@ -2546,7 +4368,7 @@ async function collectSupabaseConfig(projectRoot) {
2546
4368
  };
2547
4369
  }
2548
4370
  function writeOpsEnv(projectRoot, config2) {
2549
- const envPath = join4(projectRoot, ".env.local");
4371
+ const envPath = join2(projectRoot, ".env.local");
2550
4372
  const currentEnv = readEnvFile(envPath);
2551
4373
  const nextEnv = {
2552
4374
  ...currentEnv,
@@ -2557,7 +4379,7 @@ function writeOpsEnv(projectRoot, config2) {
2557
4379
  }
2558
4380
  function writeBootstrapPayload(config2) {
2559
4381
  const payloadPath = getOpsBootstrapPayloadPath();
2560
- mkdirSync(dirname3(payloadPath), { recursive: true });
4382
+ mkdirSync(dirname(payloadPath), { recursive: true });
2561
4383
  writeFileSync(
2562
4384
  payloadPath,
2563
4385
  JSON.stringify(
@@ -2746,7 +4568,7 @@ async function extractArchive(archivePath, outputDir) {
2746
4568
  async function findFirstAppBundle(dir) {
2747
4569
  const entries = await readdir(dir);
2748
4570
  for (const entry of entries) {
2749
- const fullPath = join4(dir, entry);
4571
+ const fullPath = join2(dir, entry);
2750
4572
  const entryStat = await stat(fullPath);
2751
4573
  if (entryStat.isDirectory() && entry.endsWith(".app")) {
2752
4574
  return fullPath;
@@ -2762,9 +4584,9 @@ async function findFirstAppBundle(dir) {
2762
4584
  }
2763
4585
  async function deployMacosApp(context, options) {
2764
4586
  const artifact = await resolveOpsMacosArtifact(context.options);
2765
- const tempRoot = await mkdtemp(join4(tmpdir(), "polter-ops-"));
2766
- const archivePath = join4(tempRoot, artifact.fileName);
2767
- const extractDir = join4(tempRoot, "extract");
4587
+ const tempRoot = await mkdtemp(join2(tmpdir(), "polter-ops-"));
4588
+ const archivePath = join2(tempRoot, artifact.fileName);
4589
+ const extractDir = join2(tempRoot, "extract");
2768
4590
  mkdirSync(extractDir, { recursive: true });
2769
4591
  if (artifact.source === "github-release") {
2770
4592
  const releaseLabel = artifact.tagName ?? "latest";
@@ -2793,13 +4615,13 @@ async function deployMacosApp(context, options) {
2793
4615
  }
2794
4616
  const installDir = context.options.installDir ?? "/Applications";
2795
4617
  mkdirSync(installDir, { recursive: true });
2796
- const destination = join4(installDir, "ops.app");
2797
- if (options.requireExistingInstallation && !existsSync3(destination)) {
4618
+ const destination = join2(installDir, "ops.app");
4619
+ if (options.requireExistingInstallation && !existsSync(destination)) {
2798
4620
  throw new Error(
2799
4621
  `No existing Ops installation was found at ${destination}. Run \`polter app install ops\` first.`
2800
4622
  );
2801
4623
  }
2802
- if (existsSync3(destination)) {
4624
+ if (existsSync(destination)) {
2803
4625
  const confirmed = context.options.yes || await promptConfirm(`Replace existing installation at ${destination}?`, false);
2804
4626
  if (!confirmed) {
2805
4627
  process.stdout.write(`${pc2.yellow("Cancelled.")}
@@ -2854,7 +4676,7 @@ var opsProfile = {
2854
4676
  },
2855
4677
  resolveProjectRoot(startDir = process.cwd(), explicitPath) {
2856
4678
  if (explicitPath) {
2857
- const resolved = resolve3(explicitPath);
4679
+ const resolved = resolve(explicitPath);
2858
4680
  if (isOpsProjectRoot(resolved)) {
2859
4681
  return resolved;
2860
4682
  }
@@ -2915,6 +4737,7 @@ async function runAppCli(options) {
2915
4737
  }
2916
4738
 
2917
4739
  // src/index.tsx
4740
+ import pc4 from "picocolors";
2918
4741
  async function main() {
2919
4742
  const parsed = parseCliArgs(process.argv.slice(2));
2920
4743
  if (parsed.mode === "help") {
@@ -2925,7 +4748,132 @@ async function main() {
2925
4748
  const exitCode = await runAppCli(parsed.options);
2926
4749
  process.exit(exitCode);
2927
4750
  }
2928
- render(React8.createElement(App));
4751
+ if (parsed.mode === "pipeline-run") {
4752
+ const pipeline = findPipelineByName(parsed.pipelineName);
4753
+ if (!pipeline) {
4754
+ process.stderr.write(`Pipeline not found: ${parsed.pipelineName}
4755
+ `);
4756
+ process.exit(1);
4757
+ }
4758
+ process.stdout.write(pc4.bold(`Running pipeline: ${pipeline.name}
4759
+ `));
4760
+ const results = await executePipeline(pipeline, (progress) => {
4761
+ if (progress.done) return;
4762
+ const step = pipeline.steps[progress.currentStepIndex];
4763
+ if (step) {
4764
+ const cmdDef = getCommandById(step.commandId);
4765
+ const label = cmdDef ? `${cmdDef.tool}: ${cmdDef.label}` : step.commandId;
4766
+ process.stdout.write(` ${progress.currentStepIndex + 1}. ${label}...
4767
+ `);
4768
+ }
4769
+ });
4770
+ const errors = results.filter((r) => r.status === "error");
4771
+ if (errors.length > 0) {
4772
+ process.stderr.write(pc4.red(`
4773
+ Pipeline completed with ${errors.length} error(s)
4774
+ `));
4775
+ process.exit(1);
4776
+ }
4777
+ process.stdout.write(pc4.green("\nPipeline completed successfully!\n"));
4778
+ return;
4779
+ }
4780
+ if (parsed.mode === "status") {
4781
+ const tools = ["supabase", "gh", "vercel"];
4782
+ process.stdout.write(pc4.bold("Tool Status\n\n"));
4783
+ for (const toolId of tools) {
4784
+ const info = getToolInfo(toolId);
4785
+ const status = info.installed ? pc4.green(`\u2713 ${info.label} ${info.version ?? ""}`) : pc4.red(`\u2717 ${info.label} not found`);
4786
+ process.stdout.write(` ${status}
4787
+ `);
4788
+ }
4789
+ process.stdout.write(pc4.bold("\nProject Status\n\n"));
4790
+ const projectStatus = getCurrentStatus();
4791
+ if (projectStatus.supabase) {
4792
+ process.stdout.write(` Supabase: ${projectStatus.supabase.linked ? "linked" : "not linked"}
4793
+ `);
4794
+ }
4795
+ if (projectStatus.vercel) {
4796
+ process.stdout.write(` Vercel: ${projectStatus.vercel.linked ? "linked" : "not linked"}
4797
+ `);
4798
+ }
4799
+ if (projectStatus.github) {
4800
+ process.stdout.write(` GitHub: ${projectStatus.github.repo ?? "no repo"} ${projectStatus.github.authenticated ? "(authenticated)" : "(not authenticated)"}
4801
+ `);
4802
+ }
4803
+ return;
4804
+ }
4805
+ if (parsed.mode === "plan") {
4806
+ const yaml = parsePolterYaml();
4807
+ if (!yaml) {
4808
+ process.stderr.write("No polter.yaml found in current directory.\n");
4809
+ process.exit(1);
4810
+ }
4811
+ const plan = planChanges(yaml);
4812
+ if (plan.noChanges) {
4813
+ process.stdout.write(pc4.green("No changes needed. State is up to date.\n"));
4814
+ return;
4815
+ }
4816
+ process.stdout.write(pc4.bold(`Plan: ${plan.actions.length} action(s)
4817
+
4818
+ `));
4819
+ for (const action of plan.actions) {
4820
+ const prefix = action.action === "create" ? "+" : action.action === "delete" ? "-" : "~";
4821
+ const color = action.action === "create" ? pc4.green : action.action === "delete" ? pc4.red : pc4.yellow;
4822
+ process.stdout.write(` ${color(`${prefix} [${action.tool}] ${action.description}`)}
4823
+ `);
4824
+ }
4825
+ return;
4826
+ }
4827
+ if (parsed.mode === "apply") {
4828
+ const yaml = parsePolterYaml();
4829
+ if (!yaml) {
4830
+ process.stderr.write("No polter.yaml found in current directory.\n");
4831
+ process.exit(1);
4832
+ }
4833
+ const plan = planChanges(yaml);
4834
+ if (plan.noChanges) {
4835
+ process.stdout.write(pc4.green("No changes needed. State is up to date.\n"));
4836
+ return;
4837
+ }
4838
+ process.stdout.write(pc4.bold(`Applying ${plan.actions.length} action(s)...
4839
+
4840
+ `));
4841
+ const results = await applyActions(plan.actions, process.cwd(), (i, total, action) => {
4842
+ process.stdout.write(` [${i + 1}/${total}] ${action.description}...
4843
+ `);
4844
+ });
4845
+ const errors = results.filter((r) => !r.success);
4846
+ if (errors.length > 0) {
4847
+ process.stderr.write(pc4.red(`
4848
+ Apply completed with ${errors.length} error(s).
4849
+ `));
4850
+ for (const err of errors) {
4851
+ process.stderr.write(pc4.red(` \u2717 ${err.action.description}
4852
+ `));
4853
+ }
4854
+ process.exit(1);
4855
+ }
4856
+ process.stdout.write(pc4.green("\nAll changes applied successfully!\n"));
4857
+ return;
4858
+ }
4859
+ if (parsed.mode === "config") {
4860
+ if (parsed.configEdit) {
4861
+ const configPath = getProjectConfigPath();
4862
+ if (!configPath) {
4863
+ process.stderr.write("No package.json found. Run from a project directory.\n");
4864
+ process.exit(1);
4865
+ }
4866
+ getOrCreateProjectConfig();
4867
+ writeProjectConfig(getOrCreateProjectConfig());
4868
+ const result = openInEditor(configPath.file);
4869
+ process.exit(result.exitCode ?? 0);
4870
+ }
4871
+ const AppComponent2 = parsed.classic ? AppClassic : AppPanel;
4872
+ render(React19.createElement(AppComponent2));
4873
+ return;
4874
+ }
4875
+ const AppComponent = parsed.classic ? AppClassic : AppPanel;
4876
+ render(React19.createElement(AppComponent));
2929
4877
  }
2930
4878
  main().catch((error) => {
2931
4879
  const message = error instanceof Error ? error.message : String(error);