@polterware/polter 0.3.1 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -3,6 +3,7 @@ import {
3
3
  allCommands,
4
4
  applyActions,
5
5
  commandExists,
6
+ createIpcServer,
6
7
  detectPkgManager,
7
8
  executePipeline,
8
9
  features,
@@ -10,6 +11,7 @@ import {
10
11
  findNearestPackageRoot,
11
12
  findPipelineByName,
12
13
  findPolterYaml,
14
+ generateProcessId,
13
15
  getAllPipelines,
14
16
  getCommandById,
15
17
  getCommandValue,
@@ -20,6 +22,8 @@ import {
20
22
  getOrCreateProjectConfig,
21
23
  getProcessOutput,
22
24
  getProjectConfigPath,
25
+ getSocketPath,
26
+ getToolDisplayName,
23
27
  getToolInfo,
24
28
  getToolLinkInfo,
25
29
  installMcpServer,
@@ -36,16 +40,18 @@ import {
36
40
  runInteractiveCommand,
37
41
  runSupabaseCommand,
38
42
  savePipeline,
43
+ startProcess,
39
44
  stopProcess,
45
+ translateCommand,
40
46
  writeProjectConfig
41
- } from "./chunk-7MIUDIAI.js";
47
+ } from "./chunk-ZHVOYB5M.js";
42
48
 
43
49
  // src/index.tsx
44
- import React27 from "react";
50
+ import React28 from "react";
45
51
  import { render } from "ink";
46
52
 
47
53
  // src/app.tsx
48
- import { Box as Box28, Text as Text32, useApp } from "ink";
54
+ import { Box as Box29, Text as Text33, useApp } from "ink";
49
55
 
50
56
  // src/hooks/useNavigation.ts
51
57
  import { useState, useCallback } from "react";
@@ -110,7 +116,7 @@ function useTerminalHeight() {
110
116
  }
111
117
 
112
118
  // src/components/GhostBanner.tsx
113
- import React from "react";
119
+ import React, { useState as useState3, useEffect as useEffect2 } from "react";
114
120
  import { Box, Text as Text2 } from "ink";
115
121
 
116
122
  // src/theme.ts
@@ -197,6 +203,12 @@ var toolColors = {
197
203
  git: "#F05032",
198
204
  pkg: "#CB3837"
199
205
  };
206
+ var pkgManagerColors = {
207
+ npm: "#CB3837",
208
+ pnpm: "#F69220",
209
+ yarn: "#2C8EBB",
210
+ bun: "#FBF0DF"
211
+ };
200
212
  var toolLabels = {
201
213
  supabase: "supabase",
202
214
  gh: "github",
@@ -204,8 +216,21 @@ var toolLabels = {
204
216
  git: "git",
205
217
  pkg: "pkg"
206
218
  };
219
+ function resolveToolColor(tool) {
220
+ if (tool === "pkg") {
221
+ const name = getToolDisplayName(tool);
222
+ return pkgManagerColors[name] ?? toolColors.pkg;
223
+ }
224
+ return toolColors[tool];
225
+ }
226
+ function resolveToolLabel(tool) {
227
+ if (tool === "pkg") {
228
+ return getToolDisplayName(tool);
229
+ }
230
+ return toolLabels[tool];
231
+ }
207
232
  function ToolBadge({ tool }) {
208
- return /* @__PURE__ */ jsx(Text, { color: toolColors[tool], dimColor: true, children: toolLabels[tool] });
233
+ return /* @__PURE__ */ jsx(Text, { color: resolveToolColor(tool), dimColor: true, children: resolveToolLabel(tool) });
209
234
  }
210
235
 
211
236
  // src/components/GhostBanner.tsx
@@ -219,6 +244,23 @@ var McpBadge = React.memo(function McpBadge2() {
219
244
  registered ? "ok" : "x"
220
245
  ] }) });
221
246
  });
247
+ function ProcessBadge() {
248
+ const [count, setCount] = useState3(() => {
249
+ return listProcesses().filter((p) => p.status === "running").length;
250
+ });
251
+ useEffect2(() => {
252
+ const id = setInterval(() => {
253
+ const c = listProcesses().filter((p) => p.status === "running").length;
254
+ setCount(c);
255
+ }, 2e3);
256
+ return () => clearInterval(id);
257
+ }, []);
258
+ const color = count > 0 ? "#3ECF8E" : inkColors.accent;
259
+ return /* @__PURE__ */ jsx2(Box, { borderStyle: "round", borderColor: color, borderDimColor: count === 0, children: /* @__PURE__ */ jsxs(Text2, { color, dimColor: count === 0, children: [
260
+ "runs:",
261
+ count
262
+ ] }) });
263
+ }
222
264
  var ToolStatusBadges = React.memo(function ToolStatusBadges2() {
223
265
  const tools = ["supabase", "gh", "vercel"].map((id) => getToolLinkInfo(id));
224
266
  return /* @__PURE__ */ jsxs(Box, { gap: 1, children: [
@@ -237,13 +279,16 @@ function GhostBanner({ width = 80, compact = false }) {
237
279
  if (compact) {
238
280
  if (width < 60) {
239
281
  return /* @__PURE__ */ jsxs(Box, { borderStyle: "round", borderColor: inkColors.accent, flexDirection: "column", alignItems: "flex-start", children: [
240
- /* @__PURE__ */ jsxs(Box, { borderStyle: "round", borderColor: inkColors.accent, paddingX: 1, gap: 2, children: [
241
- /* @__PURE__ */ jsx2(Text2, { color: inkColors.accent, bold: true, children: "POLTER" }),
242
- /* @__PURE__ */ jsxs(Text2, { dimColor: true, children: [
243
- "v",
244
- VERSION
282
+ /* @__PURE__ */ jsxs(Box, { gap: 1, children: [
283
+ /* @__PURE__ */ jsxs(Box, { borderStyle: "round", borderColor: inkColors.accent, paddingX: 1, gap: 2, children: [
284
+ /* @__PURE__ */ jsx2(Text2, { color: inkColors.accent, bold: true, children: "POLTER" }),
285
+ /* @__PURE__ */ jsxs(Text2, { dimColor: true, children: [
286
+ "v",
287
+ VERSION
288
+ ] }),
289
+ /* @__PURE__ */ jsx2(Text2, { color: "yellow", children: "alpha" })
245
290
  ] }),
246
- /* @__PURE__ */ jsx2(Text2, { color: "yellow", children: "alpha" })
291
+ /* @__PURE__ */ jsx2(ProcessBadge, {})
247
292
  ] }),
248
293
  /* @__PURE__ */ jsx2(Box, { borderStyle: "round", borderColor: inkColors.accent, paddingX: 1, children: /* @__PURE__ */ jsx2(ToolStatusBadges, {}) })
249
294
  ] });
@@ -251,13 +296,16 @@ function GhostBanner({ width = 80, compact = false }) {
251
296
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "row", borderStyle: "round", borderColor: inkColors.accent, gap: 1, alignItems: "flex-start", children: [
252
297
  /* @__PURE__ */ jsx2(Box, { flexDirection: "column", children: ghost.art.map((line, i) => /* @__PURE__ */ jsx2(Text2, { color: inkColors.accent, children: line }, i)) }),
253
298
  /* @__PURE__ */ jsxs(Box, { flexDirection: "column", alignItems: "flex-start", children: [
254
- /* @__PURE__ */ jsxs(Box, { borderStyle: "round", borderColor: inkColors.accent, paddingX: 1, gap: 2, children: [
255
- /* @__PURE__ */ jsx2(Text2, { color: inkColors.accent, bold: true, children: "POLTER" }),
256
- /* @__PURE__ */ jsxs(Text2, { dimColor: true, children: [
257
- "v",
258
- VERSION
299
+ /* @__PURE__ */ jsxs(Box, { gap: 1, children: [
300
+ /* @__PURE__ */ jsxs(Box, { borderStyle: "round", borderColor: inkColors.accent, paddingX: 1, gap: 2, children: [
301
+ /* @__PURE__ */ jsx2(Text2, { color: inkColors.accent, bold: true, children: "POLTER" }),
302
+ /* @__PURE__ */ jsxs(Text2, { dimColor: true, children: [
303
+ "v",
304
+ VERSION
305
+ ] }),
306
+ /* @__PURE__ */ jsx2(Text2, { color: "yellow", children: "alpha" })
259
307
  ] }),
260
- /* @__PURE__ */ jsx2(Text2, { color: "yellow", children: "alpha" })
308
+ /* @__PURE__ */ jsx2(ProcessBadge, {})
261
309
  ] }),
262
310
  /* @__PURE__ */ jsx2(Box, { borderStyle: "round", borderColor: inkColors.accent, paddingX: 1, children: /* @__PURE__ */ jsx2(ToolStatusBadges, {}) })
263
311
  ] })
@@ -265,13 +313,16 @@ function GhostBanner({ width = 80, compact = false }) {
265
313
  }
266
314
  if (width < 50) {
267
315
  return /* @__PURE__ */ jsxs(Box, { marginBottom: 1, borderStyle: "round", borderColor: inkColors.accent, flexDirection: "column", alignItems: "flex-start", children: [
268
- /* @__PURE__ */ jsxs(Box, { borderStyle: "round", borderColor: inkColors.accent, paddingX: 1, gap: 2, children: [
269
- /* @__PURE__ */ jsx2(Text2, { color: inkColors.accent, bold: true, children: "POLTER" }),
270
- /* @__PURE__ */ jsxs(Text2, { dimColor: true, children: [
271
- "v",
272
- VERSION
316
+ /* @__PURE__ */ jsxs(Box, { gap: 1, children: [
317
+ /* @__PURE__ */ jsxs(Box, { borderStyle: "round", borderColor: inkColors.accent, paddingX: 1, gap: 2, children: [
318
+ /* @__PURE__ */ jsx2(Text2, { color: inkColors.accent, bold: true, children: "POLTER" }),
319
+ /* @__PURE__ */ jsxs(Text2, { dimColor: true, children: [
320
+ "v",
321
+ VERSION
322
+ ] }),
323
+ /* @__PURE__ */ jsx2(Text2, { color: "yellow", children: "alpha" })
273
324
  ] }),
274
- /* @__PURE__ */ jsx2(Text2, { color: "yellow", children: "alpha" })
325
+ /* @__PURE__ */ jsx2(ProcessBadge, {})
275
326
  ] }),
276
327
  /* @__PURE__ */ jsx2(Box, { borderStyle: "round", borderColor: inkColors.accent, paddingX: 1, children: /* @__PURE__ */ jsx2(ToolStatusBadges, {}) })
277
328
  ] });
@@ -286,13 +337,16 @@ function GhostBanner({ width = 80, compact = false }) {
286
337
  borderColor: inkColors.accent,
287
338
  marginBottom: 1,
288
339
  children: [
289
- /* @__PURE__ */ jsxs(Box, { borderStyle: "round", borderColor: inkColors.accent, paddingX: 1, gap: 2, children: [
290
- /* @__PURE__ */ jsx2(Text2, { color: inkColors.accent, bold: true, children: "POLTER" }),
291
- /* @__PURE__ */ jsxs(Text2, { dimColor: true, children: [
292
- "v",
293
- VERSION
340
+ /* @__PURE__ */ jsxs(Box, { gap: 1, children: [
341
+ /* @__PURE__ */ jsxs(Box, { borderStyle: "round", borderColor: inkColors.accent, paddingX: 1, gap: 2, children: [
342
+ /* @__PURE__ */ jsx2(Text2, { color: inkColors.accent, bold: true, children: "POLTER" }),
343
+ /* @__PURE__ */ jsxs(Text2, { dimColor: true, children: [
344
+ "v",
345
+ VERSION
346
+ ] }),
347
+ /* @__PURE__ */ jsx2(Text2, { color: "yellow", children: "alpha" })
294
348
  ] }),
295
- /* @__PURE__ */ jsx2(Text2, { color: "yellow", children: "alpha" })
349
+ /* @__PURE__ */ jsx2(ProcessBadge, {})
296
350
  ] }),
297
351
  /* @__PURE__ */ jsx2(Box, { borderStyle: "round", borderColor: inkColors.accent, paddingX: 1, children: /* @__PURE__ */ jsx2(ToolStatusBadges, {}) }),
298
352
  /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "Project & infrastructure orchestrator" })
@@ -303,13 +357,16 @@ function GhostBanner({ width = 80, compact = false }) {
303
357
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "row", alignItems: "flex-start", borderStyle: "round", borderColor: inkColors.accent, gap: 1, marginBottom: 1, children: [
304
358
  /* @__PURE__ */ jsx2(Box, { flexDirection: "column", children: ghost.art.map((line, i) => /* @__PURE__ */ jsx2(Text2, { color: inkColors.accent, children: line }, i)) }),
305
359
  /* @__PURE__ */ jsxs(Box, { flexDirection: "column", alignItems: "flex-start", children: [
306
- /* @__PURE__ */ jsxs(Box, { borderStyle: "round", borderColor: inkColors.accent, paddingX: 1, gap: 2, children: [
307
- /* @__PURE__ */ jsx2(Text2, { color: inkColors.accent, bold: true, children: "POLTER" }),
308
- /* @__PURE__ */ jsxs(Text2, { dimColor: true, children: [
309
- "v",
310
- VERSION
360
+ /* @__PURE__ */ jsxs(Box, { gap: 1, children: [
361
+ /* @__PURE__ */ jsxs(Box, { borderStyle: "round", borderColor: inkColors.accent, paddingX: 1, gap: 2, children: [
362
+ /* @__PURE__ */ jsx2(Text2, { color: inkColors.accent, bold: true, children: "POLTER" }),
363
+ /* @__PURE__ */ jsxs(Text2, { dimColor: true, children: [
364
+ "v",
365
+ VERSION
366
+ ] }),
367
+ /* @__PURE__ */ jsx2(Text2, { color: "yellow", children: "alpha" })
311
368
  ] }),
312
- /* @__PURE__ */ jsx2(Text2, { color: "yellow", children: "alpha" })
369
+ /* @__PURE__ */ jsx2(ProcessBadge, {})
313
370
  ] }),
314
371
  /* @__PURE__ */ jsx2(Box, { borderStyle: "round", borderColor: inkColors.accent, paddingX: 1, children: /* @__PURE__ */ jsx2(ToolStatusBadges, {}) }),
315
372
  /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: " Project & infrastructure orchestrator" })
@@ -318,7 +375,7 @@ function GhostBanner({ width = 80, compact = false }) {
318
375
  }
319
376
 
320
377
  // src/screens/Home.tsx
321
- import { useEffect as useEffect3, useMemo as useMemo2, useState as useState4 } from "react";
378
+ import { useEffect as useEffect4, useMemo as useMemo2, useState as useState5 } from "react";
322
379
  import { Box as Box5, Text as Text6, useInput as useInput2 } from "ink";
323
380
 
324
381
  // src/components/TabBar.tsx
@@ -347,7 +404,7 @@ function TabBar({ tabs, activeIndex, width = 80 }) {
347
404
  }
348
405
 
349
406
  // src/components/SelectList.tsx
350
- import { useEffect as useEffect2, useMemo, useState as useState3 } from "react";
407
+ import { useEffect as useEffect3, useMemo, useState as useState4 } from "react";
351
408
  import { Box as Box3, Text as Text4, useInput } from "ink";
352
409
 
353
410
  // src/components/selectListSections.ts
@@ -454,8 +511,8 @@ function SelectList({
454
511
  }, []),
455
512
  [items]
456
513
  );
457
- const [selectedSelectableIndex, setSelectedSelectableIndex] = useState3(0);
458
- useEffect2(() => {
514
+ const [selectedSelectableIndex, setSelectedSelectableIndex] = useState4(0);
515
+ useEffect3(() => {
459
516
  if (selectableIndexes.length === 0) {
460
517
  setSelectedSelectableIndex(0);
461
518
  return;
@@ -707,13 +764,13 @@ function buildPinnedOnlyItems(pinnedCommands, pinnedRuns) {
707
764
  if (pinnedCommands.length > 0) {
708
765
  for (const command of pinnedCommands) {
709
766
  const cmdDef = findCommandByValue(command);
710
- const toolHint = cmdDef ? cmdDef.tool : "supabase";
767
+ const toolDisplay = cmdDef ? getToolDisplayName(cmdDef.tool) : "supabase";
711
768
  const labelHint = cmdDef?.hint;
712
769
  items.push({
713
770
  id: `command:${command}`,
714
771
  value: command,
715
- label: cmdDef ? `${cmdDef.tool} ${command}` : command,
716
- hint: [toolHint, labelHint].filter(Boolean).join(" \xB7 "),
772
+ label: cmdDef ? `${toolDisplay} ${command}` : command,
773
+ hint: [toolDisplay, labelHint].filter(Boolean).join(" \xB7 "),
717
774
  icon: "\u{1F4CC}",
718
775
  kind: "command",
719
776
  rightActionable: true,
@@ -725,12 +782,12 @@ function buildPinnedOnlyItems(pinnedCommands, pinnedRuns) {
725
782
  for (const runCommand2 of pinnedRuns) {
726
783
  const baseCommand = runCommand2.split(" ").filter(Boolean)[0] ?? "";
727
784
  const cmdDef = findCommandByValue(baseCommand);
728
- const toolHint = cmdDef ? cmdDef.tool : "supabase";
785
+ const toolDisplay = cmdDef ? getToolDisplayName(cmdDef.tool) : "supabase";
729
786
  items.push({
730
787
  id: `run:${runCommand2}`,
731
788
  value: runCommand2,
732
- label: cmdDef ? `${cmdDef.tool} ${runCommand2}` : runCommand2,
733
- hint: toolHint,
789
+ label: cmdDef ? `${toolDisplay} ${runCommand2}` : runCommand2,
790
+ hint: toolDisplay,
734
791
  icon: "\u25B6",
735
792
  kind: "run",
736
793
  rightActionable: true,
@@ -767,8 +824,8 @@ function buildHomeItems({
767
824
  selectable: false
768
825
  });
769
826
  }
770
- const toolOrder = { supabase: 0, vercel: 1, gh: 2, git: 3 };
771
- const toolIcons = { supabase: "\u{1F7E2}", vercel: "\u26AA", gh: "\u{1F535}", git: "\u{1F7E0}" };
827
+ const toolOrder = { supabase: 0, vercel: 1, gh: 2, git: 3, pkg: 4 };
828
+ const toolIcons = { supabase: "\u{1F7E2}", vercel: "\u26AA", gh: "\u{1F535}", git: "\u{1F7E0}", pkg: "\u{1F4E6}" };
772
829
  const grouped = /* @__PURE__ */ new Map();
773
830
  for (const cmd of activeFeature.commands) {
774
831
  const existing = grouped.get(cmd.tool) ?? [];
@@ -784,7 +841,7 @@ function buildHomeItems({
784
841
  items.push({
785
842
  id: `tool-header-${tool}`,
786
843
  value: `__tool_header_${tool}__`,
787
- label: `${icon} ${tool}`,
844
+ label: `${icon} ${getToolDisplayName(tool)}`,
788
845
  kind: "header",
789
846
  selectable: false
790
847
  });
@@ -883,16 +940,16 @@ function Home({
883
940
  height = 24
884
941
  }) {
885
942
  const allFeatures = useMemo2(() => getFeatures(), []);
886
- const [activeTabIndex, setActiveTabIndex] = useState4(0);
887
- const [pinnedCommands, setPinnedCommands2] = useState4(
943
+ const [activeTabIndex, setActiveTabIndex] = useState5(0);
944
+ const [pinnedCommands, setPinnedCommands2] = useState5(
888
945
  () => getPinnedCommands()
889
946
  );
890
- const [pinnedRuns, setPinnedRuns2] = useState4(
947
+ const [pinnedRuns, setPinnedRuns2] = useState5(
891
948
  () => getPinnedRuns()
892
949
  );
893
- const [pinFeedback, setPinFeedback] = useState4();
950
+ const [pinFeedback, setPinFeedback] = useState5();
894
951
  const activeFeature = allFeatures[activeTabIndex];
895
- useEffect3(() => {
952
+ useEffect4(() => {
896
953
  if (!pinFeedback) return;
897
954
  const timeout = setTimeout(() => setPinFeedback(void 0), 1400);
898
955
  return () => clearTimeout(timeout);
@@ -1031,11 +1088,11 @@ function Home({
1031
1088
  }
1032
1089
 
1033
1090
  // src/screens/CommandArgs.tsx
1034
- import { useEffect as useEffect4, useMemo as useMemo3, useState as useState6 } from "react";
1091
+ import { useEffect as useEffect5, useMemo as useMemo3, useState as useState7 } from "react";
1035
1092
  import { Box as Box7, Text as Text8, useInput as useInput4 } from "ink";
1036
1093
 
1037
1094
  // src/components/TextPrompt.tsx
1038
- import { useState as useState5 } from "react";
1095
+ import { useState as useState6 } from "react";
1039
1096
  import { Box as Box6, Text as Text7, useInput as useInput3 } from "ink";
1040
1097
  import TextInputComponent from "ink-text-input";
1041
1098
  import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
@@ -1050,8 +1107,8 @@ function TextPrompt({
1050
1107
  boxed = false,
1051
1108
  focused = true
1052
1109
  }) {
1053
- const [value, setValue] = useState5("");
1054
- const [error, setError] = useState5();
1110
+ const [value, setValue] = useState6("");
1111
+ const [error, setError] = useState6();
1055
1112
  useInput3((_input, key) => {
1056
1113
  if (key.escape && onCancel) {
1057
1114
  onCancel();
@@ -1190,15 +1247,15 @@ function CommandArgs({
1190
1247
  () => cmdDef?.suggestedArgs ?? [],
1191
1248
  [cmdDef]
1192
1249
  );
1193
- const [pinnedRuns, setPinnedRuns2] = useState6(() => getPinnedRuns());
1194
- const [pinFeedback, setPinFeedback] = useState6();
1195
- const [phase, setPhase] = useState6(
1250
+ const [pinnedRuns, setPinnedRuns2] = useState7(() => getPinnedRuns());
1251
+ const [pinFeedback, setPinFeedback] = useState7();
1252
+ const [phase, setPhase] = useState7(
1196
1253
  suggestions.length > 0 ? "select" : "custom"
1197
1254
  );
1198
- useEffect4(() => {
1255
+ useEffect5(() => {
1199
1256
  setPhase(suggestions.length > 0 ? "select" : "custom");
1200
1257
  }, [command, suggestions.length]);
1201
- useEffect4(() => {
1258
+ useEffect5(() => {
1202
1259
  if (!pinFeedback) return;
1203
1260
  const timeout = setTimeout(() => setPinFeedback(void 0), 1400);
1204
1261
  return () => clearTimeout(timeout);
@@ -1340,7 +1397,7 @@ function CommandArgs({
1340
1397
  }
1341
1398
 
1342
1399
  // src/screens/CustomCommand.tsx
1343
- import { useState as useState7 } from "react";
1400
+ import { useState as useState8 } from "react";
1344
1401
  import { Box as Box8, Text as Text9 } from "ink";
1345
1402
  import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
1346
1403
  function CustomCommand({
@@ -1351,8 +1408,8 @@ function CustomCommand({
1351
1408
  panelMode = false,
1352
1409
  isInputActive = true
1353
1410
  }) {
1354
- const [phase, setPhase] = useState7("tool-select");
1355
- const [selectedTool, setSelectedTool] = useState7("supabase");
1411
+ const [phase, setPhase] = useState8("tool-select");
1412
+ const [selectedTool, setSelectedTool] = useState8("supabase");
1356
1413
  if (phase === "tool-select") {
1357
1414
  const toolItems = [
1358
1415
  { value: "__section__", label: "\u{1F6E0} Select Tool", kind: "header", selectable: false },
@@ -1360,6 +1417,8 @@ function CustomCommand({
1360
1417
  { value: "gh", label: "GitHub CLI", hint: "gh ...", kind: "action" },
1361
1418
  { value: "vercel", label: "Vercel CLI", hint: "vercel ...", kind: "action" },
1362
1419
  { value: "git", label: "Git", hint: "git ...", kind: "action" },
1420
+ { value: "__free_header__", label: "", kind: "header", selectable: false },
1421
+ { value: "free", label: "\u2328 Free command", hint: "any command", kind: "action" },
1363
1422
  ...!panelMode ? [{ value: "__back__", label: "\u2190 Back" }] : []
1364
1423
  ];
1365
1424
  return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", paddingX: panelMode ? 1 : 0, children: [
@@ -1374,6 +1433,10 @@ function CustomCommand({
1374
1433
  onBack();
1375
1434
  return;
1376
1435
  }
1436
+ if (value === "free") {
1437
+ setPhase("free-input");
1438
+ return;
1439
+ }
1377
1440
  setSelectedTool(value);
1378
1441
  setPhase("input");
1379
1442
  },
@@ -1389,6 +1452,34 @@ function CustomCommand({
1389
1452
  !panelMode && /* @__PURE__ */ jsx9(StatusBar, { hint: "\u2191\u2193 navigate \xB7 Enter select \xB7 Esc back", width })
1390
1453
  ] });
1391
1454
  }
1455
+ if (phase === "free-input") {
1456
+ return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", paddingX: panelMode ? 1 : 0, children: [
1457
+ !panelMode && /* @__PURE__ */ jsx9(Box8, { marginBottom: 1, children: /* @__PURE__ */ jsx9(Text9, { bold: true, color: inkColors.accent, children: "\u2328 Free Command" }) }),
1458
+ /* @__PURE__ */ jsx9(
1459
+ TextPrompt,
1460
+ {
1461
+ label: "Enter full command:",
1462
+ placeholder: "e.g. docker ps, curl -s http://..., make build",
1463
+ validate: (val) => {
1464
+ if (!val || !val.trim()) return "Please enter a command";
1465
+ return void 0;
1466
+ },
1467
+ onSubmit: (value) => {
1468
+ const parts = value.split(" ").filter(Boolean);
1469
+ const cmd = parts[0];
1470
+ const args = parts.slice(1);
1471
+ onNavigate("command-execution", { args, rawCommand: cmd });
1472
+ },
1473
+ onCancel: () => setPhase("tool-select"),
1474
+ arrowNavigation: panelMode,
1475
+ isInputActive,
1476
+ boxed: panelMode,
1477
+ focused: isInputActive
1478
+ }
1479
+ ),
1480
+ !panelMode && /* @__PURE__ */ jsx9(StatusBar, { hint: "Type full command \xB7 Enter to execute \xB7 Esc to go back", width })
1481
+ ] });
1482
+ }
1392
1483
  return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", paddingX: panelMode ? 1 : 0, children: [
1393
1484
  !panelMode && /* @__PURE__ */ jsxs8(Box8, { marginBottom: 1, gap: 1, children: [
1394
1485
  /* @__PURE__ */ jsx9(Text9, { bold: true, color: inkColors.accent, children: "\u270F\uFE0F Custom Command" }),
@@ -1409,7 +1500,7 @@ function CustomCommand({
1409
1500
  },
1410
1501
  onSubmit: (value) => {
1411
1502
  const args = value.split(" ").filter(Boolean);
1412
- onNavigate("flag-selection", { args, tool: selectedTool, interactive: true });
1503
+ onNavigate("command-execution", { args, tool: selectedTool });
1413
1504
  },
1414
1505
  onCancel: () => setPhase("tool-select"),
1415
1506
  arrowNavigation: panelMode,
@@ -1426,7 +1517,7 @@ function CustomCommand({
1426
1517
  import { Box as Box10, Text as Text11 } from "ink";
1427
1518
 
1428
1519
  // src/components/FlagToggle.tsx
1429
- import { useState as useState8 } from "react";
1520
+ import { useState as useState9 } from "react";
1430
1521
  import { Box as Box9, Text as Text10, useInput as useInput5 } from "ink";
1431
1522
  import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
1432
1523
  function FlagToggle({
@@ -1436,8 +1527,8 @@ function FlagToggle({
1436
1527
  isInputActive = true,
1437
1528
  arrowNavigation = false
1438
1529
  }) {
1439
- const [cursor, setCursor] = useState8(0);
1440
- const [selected, setSelected] = useState8(/* @__PURE__ */ new Set());
1530
+ const [cursor, setCursor] = useState9(0);
1531
+ const [selected, setSelected] = useState9(/* @__PURE__ */ new Set());
1441
1532
  useInput5((input2, key) => {
1442
1533
  if (key.upArrow || input2 === "k") {
1443
1534
  setCursor((prev) => prev > 0 ? prev - 1 : flags.length - 1);
@@ -1569,27 +1660,12 @@ function FlagSelection({
1569
1660
  }
1570
1661
 
1571
1662
  // src/screens/CommandExecution.tsx
1572
- import { useState as useState11, useEffect as useEffect7 } from "react";
1573
- import { Box as Box13, Text as Text17, useInput as useInput8 } from "ink";
1574
-
1575
- // src/components/Spinner.tsx
1576
- import { Text as Text12 } from "ink";
1577
- import InkSpinner from "ink-spinner";
1578
- import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
1579
- function Spinner({
1580
- label = "Running...",
1581
- color = inkColors.accent
1582
- }) {
1583
- return /* @__PURE__ */ jsxs11(Text12, { children: [
1584
- /* @__PURE__ */ jsx12(Text12, { color, children: /* @__PURE__ */ jsx12(InkSpinner, { type: "dots" }) }),
1585
- " ",
1586
- /* @__PURE__ */ jsx12(Text12, { children: label })
1587
- ] });
1588
- }
1663
+ import { useState as useState12, useEffect as useEffect9 } from "react";
1664
+ import { Box as Box13, Text as Text15, useInput as useInput8 } from "ink";
1589
1665
 
1590
1666
  // src/components/ConfirmPrompt.tsx
1591
- import { Box as Box11, Text as Text13, useInput as useInput6 } from "ink";
1592
- import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
1667
+ import { Box as Box11, Text as Text12, useInput as useInput6 } from "ink";
1668
+ import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
1593
1669
  function ConfirmPrompt({
1594
1670
  message,
1595
1671
  defaultValue = true,
@@ -1615,16 +1691,16 @@ function ConfirmPrompt({
1615
1691
  onConfirm(defaultValue);
1616
1692
  }
1617
1693
  }, { isActive: isInputActive });
1618
- return /* @__PURE__ */ jsxs12(Box11, { gap: 1, children: [
1619
- /* @__PURE__ */ jsx13(Text13, { color: inkColors.accent, bold: true, children: "?" }),
1620
- /* @__PURE__ */ jsx13(Text13, { children: message }),
1621
- /* @__PURE__ */ jsx13(Text13, { dimColor: true, children: defaultValue ? "(Y/n)" : "(y/N)" })
1694
+ return /* @__PURE__ */ jsxs11(Box11, { gap: 1, children: [
1695
+ /* @__PURE__ */ jsx12(Text12, { color: inkColors.accent, bold: true, children: "?" }),
1696
+ /* @__PURE__ */ jsx12(Text12, { children: message }),
1697
+ /* @__PURE__ */ jsx12(Text12, { dimColor: true, children: defaultValue ? "(Y/n)" : "(y/N)" })
1622
1698
  ] });
1623
1699
  }
1624
1700
 
1625
1701
  // src/components/Divider.tsx
1626
- import { Text as Text14 } from "ink";
1627
- import { jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
1702
+ import { Text as Text13 } from "ink";
1703
+ import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
1628
1704
  function Divider({
1629
1705
  label,
1630
1706
  width
@@ -1635,7 +1711,7 @@ function Divider({
1635
1711
  const sideLen = Math.max(2, Math.floor((effectiveWidth - labelLen) / 2));
1636
1712
  const left = "\u2500".repeat(sideLen);
1637
1713
  const right = "\u2500".repeat(sideLen);
1638
- return /* @__PURE__ */ jsxs13(Text14, { dimColor: true, children: [
1714
+ return /* @__PURE__ */ jsxs12(Text13, { dimColor: true, children: [
1639
1715
  left,
1640
1716
  " ",
1641
1717
  label,
@@ -1643,31 +1719,44 @@ function Divider({
1643
1719
  right
1644
1720
  ] });
1645
1721
  }
1646
- return /* @__PURE__ */ jsx14(Text14, { dimColor: true, children: "\u2500".repeat(effectiveWidth) });
1722
+ return /* @__PURE__ */ jsx13(Text13, { dimColor: true, children: "\u2500".repeat(effectiveWidth) });
1647
1723
  }
1648
1724
 
1649
- // src/components/CommandOutput.tsx
1650
- import { Text as Text16 } from "ink";
1651
-
1652
1725
  // src/components/ScrollableBox.tsx
1653
- import { useState as useState9 } from "react";
1654
- import { Box as Box12, Text as Text15, useInput as useInput7 } from "ink";
1655
- import { jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
1726
+ import { useState as useState10, useEffect as useEffect6, useRef } from "react";
1727
+ import { Box as Box12, Text as Text14, useInput as useInput7 } from "ink";
1728
+ import { jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
1656
1729
  function ScrollableBox({
1657
1730
  height,
1658
1731
  isActive = true,
1732
+ autoScrollToBottom = false,
1659
1733
  children
1660
1734
  }) {
1661
1735
  const totalItems = children.length;
1662
- const [scrollOffset, setScrollOffset] = useState9(0);
1736
+ const [scrollOffset, setScrollOffset] = useState10(0);
1663
1737
  const visibleCount = Math.max(1, height - 2);
1738
+ const maxOffset = Math.max(0, totalItems - visibleCount);
1739
+ const pinnedToBottom = useRef(autoScrollToBottom);
1740
+ useEffect6(() => {
1741
+ if (autoScrollToBottom && pinnedToBottom.current) {
1742
+ setScrollOffset(maxOffset);
1743
+ }
1744
+ }, [autoScrollToBottom, maxOffset]);
1664
1745
  useInput7(
1665
1746
  (input2, key) => {
1666
1747
  if (key.upArrow || input2 === "k") {
1667
- setScrollOffset((prev) => Math.max(0, prev - 1));
1748
+ setScrollOffset((prev) => {
1749
+ const next = Math.max(0, prev - 1);
1750
+ pinnedToBottom.current = next >= maxOffset;
1751
+ return next;
1752
+ });
1668
1753
  }
1669
1754
  if (key.downArrow || input2 === "j") {
1670
- setScrollOffset((prev) => Math.min(Math.max(0, totalItems - visibleCount), prev + 1));
1755
+ setScrollOffset((prev) => {
1756
+ const next = Math.min(maxOffset, prev + 1);
1757
+ pinnedToBottom.current = next >= maxOffset;
1758
+ return next;
1759
+ });
1671
1760
  }
1672
1761
  },
1673
1762
  { isActive }
@@ -1675,52 +1764,22 @@ function ScrollableBox({
1675
1764
  const showScrollUp = scrollOffset > 0;
1676
1765
  const showScrollDown = scrollOffset + visibleCount < totalItems;
1677
1766
  const visible = children.slice(scrollOffset, scrollOffset + visibleCount);
1678
- return /* @__PURE__ */ jsxs14(Box12, { flexDirection: "column", height, children: [
1679
- showScrollUp && /* @__PURE__ */ jsx15(Text15, { dimColor: true, children: " \u2191 more" }),
1767
+ return /* @__PURE__ */ jsxs13(Box12, { flexDirection: "column", height, children: [
1768
+ showScrollUp && /* @__PURE__ */ jsx14(Text14, { dimColor: true, children: " \u2191 more" }),
1680
1769
  visible,
1681
- showScrollDown && /* @__PURE__ */ jsx15(Text15, { dimColor: true, children: " \u2193 more" })
1682
- ] });
1683
- }
1684
-
1685
- // src/lib/ansi.ts
1686
- var ANSI_RE = (
1687
- // biome-ignore lint/suspicious/noControlCharactersInRegex: intentional ANSI stripping
1688
- /[\u001B\u009B][[\]()#;?]*(?:(?:(?:[a-zA-Z\d]*(?:;[-a-zA-Z\d/#&.:=?%@~_]*)*)?\u0007)|(?:(?:\d{1,4}(?:;\d{0,4})*)?[\dA-PR-TZcf-nq-uy=><~]))/g
1689
- );
1690
- function stripAnsi(text) {
1691
- return text.replace(ANSI_RE, "");
1692
- }
1693
-
1694
- // src/components/CommandOutput.tsx
1695
- import { jsx as jsx16 } from "react/jsx-runtime";
1696
- function cleanLines(raw) {
1697
- const stripped = stripAnsi(raw);
1698
- return stripped.replace(/\r/g, "").split("\n").filter((line) => line.length > 0);
1699
- }
1700
- function CommandOutput({
1701
- stdout,
1702
- stderr,
1703
- height,
1704
- isActive = false
1705
- }) {
1706
- const outLines = stdout ? cleanLines(stdout) : [];
1707
- const errLines = stderr ? cleanLines(stderr) : [];
1708
- if (outLines.length === 0 && errLines.length === 0) {
1709
- return null;
1710
- }
1711
- return /* @__PURE__ */ jsx16(ScrollableBox, { height: Math.max(3, height), isActive, children: [
1712
- ...outLines.map((line, i) => /* @__PURE__ */ jsx16(Text16, { children: line }, `o-${i}`)),
1713
- ...errLines.map((line, i) => /* @__PURE__ */ jsx16(Text16, { color: "red", children: line }, `e-${i}`))
1770
+ showScrollDown && /* @__PURE__ */ jsx14(Text14, { dimColor: true, children: " \u2193 more" })
1714
1771
  ] });
1715
1772
  }
1716
1773
 
1717
1774
  // src/hooks/useCommand.ts
1718
- import { useState as useState10, useCallback as useCallback2, useRef, useEffect as useEffect5 } from "react";
1775
+ import { useState as useState11, useCallback as useCallback2, useRef as useRef2, useEffect as useEffect7 } from "react";
1719
1776
  function useCommand(execution = "supabase", cwd = process.cwd(), options) {
1720
- const [status, setStatus] = useState10("idle");
1721
- const [result, setResult] = useState10(null);
1722
- const abortRef = useRef(null);
1723
- useEffect5(() => {
1777
+ const [status, setStatus] = useState11("idle");
1778
+ const [result, setResult] = useState11(null);
1779
+ const [partialStdout, setPartialStdout] = useState11("");
1780
+ const [partialStderr, setPartialStderr] = useState11("");
1781
+ const abortRef = useRef2(null);
1782
+ useEffect7(() => {
1724
1783
  return () => {
1725
1784
  abortRef.current?.();
1726
1785
  abortRef.current = null;
@@ -1729,9 +1788,11 @@ function useCommand(execution = "supabase", cwd = process.cwd(), options) {
1729
1788
  const run = useCallback2(async (args) => {
1730
1789
  setStatus("running");
1731
1790
  setResult(null);
1791
+ setPartialStdout("");
1792
+ setPartialStderr("");
1732
1793
  let resolvedExecution;
1733
1794
  if (typeof execution === "string") {
1734
- const toolIds2 = ["supabase", "gh", "vercel"];
1795
+ const toolIds2 = ["supabase", "gh", "vercel", "git", "pkg"];
1735
1796
  if (toolIds2.includes(execution)) {
1736
1797
  const resolved = resolveToolCommand(execution, cwd);
1737
1798
  resolvedExecution = { command: resolved.command, env: resolved.env };
@@ -1741,7 +1802,11 @@ function useCommand(execution = "supabase", cwd = process.cwd(), options) {
1741
1802
  } else {
1742
1803
  resolvedExecution = execution;
1743
1804
  }
1744
- const runOpts = options?.quiet ? { quiet: true } : void 0;
1805
+ const onData = (stdout, stderr) => {
1806
+ setPartialStdout(stdout);
1807
+ setPartialStderr(stderr);
1808
+ };
1809
+ const runOpts = { quiet: options?.quiet, onData };
1745
1810
  const handle = runCommand(resolvedExecution, args, cwd, runOpts);
1746
1811
  abortRef.current = handle.abort;
1747
1812
  const res = await handle.promise;
@@ -1761,21 +1826,23 @@ function useCommand(execution = "supabase", cwd = process.cwd(), options) {
1761
1826
  const reset = useCallback2(() => {
1762
1827
  setStatus("idle");
1763
1828
  setResult(null);
1829
+ setPartialStdout("");
1830
+ setPartialStderr("");
1764
1831
  }, []);
1765
- return { status, result, run, reset, abort };
1832
+ return { status, result, run, reset, abort, partialStdout, partialStderr };
1766
1833
  }
1767
1834
 
1768
1835
  // src/hooks/useInteractiveRun.ts
1769
1836
  import { useCallback as useCallback3 } from "react";
1770
1837
 
1771
1838
  // src/hooks/useFullscreen.ts
1772
- import { useEffect as useEffect6 } from "react";
1839
+ import { useEffect as useEffect8 } from "react";
1773
1840
  var ENTER_ALT_SCREEN = "\x1B[?1049h";
1774
1841
  var LEAVE_ALT_SCREEN = "\x1B[?1049l";
1775
1842
  var HIDE_CURSOR = "\x1B[?25l";
1776
1843
  var SHOW_CURSOR = "\x1B[?25h";
1777
1844
  function useFullscreen() {
1778
- useEffect6(() => {
1845
+ useEffect8(() => {
1779
1846
  process.stdout.write(ENTER_ALT_SCREEN + HIDE_CURSOR);
1780
1847
  return () => {
1781
1848
  process.stdout.write(SHOW_CURSOR + LEAVE_ALT_SCREEN);
@@ -1830,6 +1897,15 @@ async function copyToClipboard(text) {
1830
1897
  });
1831
1898
  }
1832
1899
 
1900
+ // src/lib/ansi.ts
1901
+ var ANSI_RE = (
1902
+ // biome-ignore lint/suspicious/noControlCharactersInRegex: intentional ANSI stripping
1903
+ /[\u001B\u009B][[\]()#;?]*(?:(?:(?:[a-zA-Z\d]*(?:;[-a-zA-Z\d/#&.:=?%@~_]*)*)?\u0007)|(?:(?:\d{1,4}(?:;\d{0,4})*)?[\dA-PR-TZcf-nq-uy=><~]))/g
1904
+ );
1905
+ function stripAnsi(text) {
1906
+ return text.replace(ANSI_RE, "");
1907
+ }
1908
+
1833
1909
  // src/lib/errorSuggestions.ts
1834
1910
  var KNOWN_TOOLS = ["supabase", "gh", "vercel", "git"];
1835
1911
  var BACKTICK_CMD = /(?:try\s+)?(?:run(?:ning)?|use|execute)\s+`([^`]+)`/gi;
@@ -1876,10 +1952,11 @@ ${stderr}`);
1876
1952
  }
1877
1953
 
1878
1954
  // src/screens/CommandExecution.tsx
1879
- import { jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
1955
+ import { jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
1880
1956
  function CommandExecution({
1881
1957
  args: initialArgs,
1882
1958
  tool = "supabase",
1959
+ rawCommand,
1883
1960
  interactive = false,
1884
1961
  onBack,
1885
1962
  onHome,
@@ -1890,38 +1967,56 @@ function CommandExecution({
1890
1967
  panelMode = false,
1891
1968
  isInputActive = true
1892
1969
  }) {
1893
- const [phase, setPhase] = useState11("confirm");
1894
- const [currentArgs, setCurrentArgs] = useState11(initialArgs);
1895
- const [pinMessage, setPinMessage] = useState11();
1896
- const { status, result, run, reset, abort } = useCommand(tool, process.cwd(), {
1970
+ const [phase, setPhase] = useState12("confirm");
1971
+ const [currentArgs, setCurrentArgs] = useState12(initialArgs);
1972
+ const [pinMessage, setPinMessage] = useState12();
1973
+ const execution = rawCommand ?? tool;
1974
+ const { status, result, run, reset, abort, partialStdout, partialStderr } = useCommand(execution, process.cwd(), {
1897
1975
  quiet: panelMode
1898
1976
  });
1899
1977
  const { runInteractive } = useInteractiveRun();
1900
- const [outputFocused, setOutputFocused] = useState11(false);
1901
- const [copyMessage, setCopyMessage] = useState11();
1902
- const cmdDisplay = `${tool} ${currentArgs.join(" ")}`;
1978
+ const [outputFocused, setOutputFocused] = useState12(false);
1979
+ const [copyMessage, setCopyMessage] = useState12();
1980
+ const [feedback, setFeedback] = useState12();
1981
+ const [aborting, setAborting] = useState12(false);
1982
+ const cmdDisplay = rawCommand ? `${rawCommand} ${currentArgs.join(" ")}`.trim() : `${getToolDisplayName(tool)} ${currentArgs.join(" ")}`;
1903
1983
  const runCommand2 = currentArgs.join(" ");
1904
1984
  useInput8(
1905
- (_input, key) => {
1985
+ (input2, key) => {
1906
1986
  if (key.escape) {
1907
1987
  abort();
1908
1988
  onBack();
1989
+ return;
1990
+ }
1991
+ if (input2 === "x" && !aborting) {
1992
+ abort();
1993
+ setAborting(true);
1994
+ setFeedback("Aborting...");
1995
+ return;
1996
+ }
1997
+ if (input2 === "c") {
1998
+ const output2 = [partialStdout, partialStderr].filter(Boolean).join("\n");
1999
+ copyToClipboard(output2).then(() => setFeedback("Copied to clipboard"));
2000
+ return;
1909
2001
  }
1910
2002
  },
1911
2003
  { isActive: isInputActive && phase === "running" }
1912
2004
  );
1913
2005
  useInput8(
1914
- (input2, key) => {
1915
- if (input2 === "o" && !outputFocused) {
1916
- setOutputFocused(true);
1917
- }
1918
- if (key.escape && outputFocused) {
1919
- setOutputFocused(false);
2006
+ (input2, _key) => {
2007
+ if (input2 === "/") {
2008
+ setOutputFocused((prev) => !prev);
1920
2009
  }
1921
2010
  },
1922
- { isActive: isInputActive && phase === "error-menu" }
2011
+ { isActive: isInputActive && (phase === "error-menu" || phase === "success") }
1923
2012
  );
1924
- useEffect7(() => {
2013
+ useEffect9(() => {
2014
+ if (phase === "background-started") {
2015
+ const t = setTimeout(() => onBack(), 1500);
2016
+ return () => clearTimeout(t);
2017
+ }
2018
+ }, [phase, onBack]);
2019
+ useEffect9(() => {
1925
2020
  if (phase === "running" && status === "idle") {
1926
2021
  if (panelMode && interactive) {
1927
2022
  const interactiveResult = runInteractive(tool, currentArgs);
@@ -1935,7 +2030,7 @@ function CommandExecution({
1935
2030
  }
1936
2031
  }
1937
2032
  }, [phase, status, run, currentArgs, panelMode, interactive, tool, runInteractive]);
1938
- useEffect7(() => {
2033
+ useEffect9(() => {
1939
2034
  if (phase === "running" && status === "success") {
1940
2035
  if (isPinnedRun(runCommand2)) {
1941
2036
  setPhase("success");
@@ -1947,6 +2042,12 @@ function CommandExecution({
1947
2042
  setPhase("error-menu");
1948
2043
  }
1949
2044
  }, [phase, runCommand2, status]);
2045
+ useEffect9(() => {
2046
+ if (feedback) {
2047
+ const timer = setTimeout(() => setFeedback(void 0), 2e3);
2048
+ return () => clearTimeout(timer);
2049
+ }
2050
+ }, [feedback]);
1950
2051
  if (phase === "confirm") {
1951
2052
  const pinned = isPinnedRun(runCommand2);
1952
2053
  const confirmItems = [
@@ -1956,19 +2057,24 @@ function CommandExecution({
1956
2057
  label: pinned ? "\u{1F4CC} Unpin command" : "\u{1F4CC} Pin command",
1957
2058
  hint: "Save to quick access"
1958
2059
  },
2060
+ {
2061
+ value: "background",
2062
+ label: "\u23E9 Run in background",
2063
+ hint: "Start as background process"
2064
+ },
1959
2065
  { value: "cancel", label: "\u2190 Cancel" }
1960
2066
  ];
1961
- const confirmContent = /* @__PURE__ */ jsxs15(Box13, { flexDirection: "column", children: [
1962
- /* @__PURE__ */ jsxs15(Box13, { marginBottom: 1, gap: 1, children: [
1963
- /* @__PURE__ */ jsxs15(Text17, { color: inkColors.accent, bold: true, children: [
2067
+ const confirmContent = /* @__PURE__ */ jsxs14(Box13, { flexDirection: "column", children: [
2068
+ /* @__PURE__ */ jsxs14(Box13, { marginBottom: 1, gap: 1, children: [
2069
+ /* @__PURE__ */ jsxs14(Text15, { color: inkColors.accent, bold: true, children: [
1964
2070
  "\u25B6",
1965
2071
  " ",
1966
2072
  cmdDisplay
1967
2073
  ] }),
1968
- /* @__PURE__ */ jsx17(ToolBadge, { tool })
2074
+ /* @__PURE__ */ jsx15(ToolBadge, { tool })
1969
2075
  ] }),
1970
- pinMessage && /* @__PURE__ */ jsx17(Box13, { marginBottom: 1, children: /* @__PURE__ */ jsx17(Text17, { color: inkColors.accent, children: pinMessage }) }),
1971
- /* @__PURE__ */ jsx17(
2076
+ pinMessage && /* @__PURE__ */ jsx15(Box13, { marginBottom: 1, children: /* @__PURE__ */ jsx15(Text15, { color: inkColors.accent, children: pinMessage }) }),
2077
+ /* @__PURE__ */ jsx15(
1972
2078
  SelectList,
1973
2079
  {
1974
2080
  items: confirmItems,
@@ -1984,6 +2090,19 @@ function CommandExecution({
1984
2090
  isPinnedRun(runCommand2) ? "\u2713 Command pinned" : "\u2713 Command unpinned"
1985
2091
  );
1986
2092
  break;
2093
+ case "background": {
2094
+ const cwd = process.cwd();
2095
+ if (rawCommand) {
2096
+ const id = generateProcessId(rawCommand, currentArgs);
2097
+ startProcess(id, rawCommand, currentArgs, cwd);
2098
+ } else {
2099
+ const resolved = resolveToolCommand(tool, cwd);
2100
+ const id = generateProcessId(resolved.command, currentArgs);
2101
+ startProcess(id, resolved.command, currentArgs, cwd, resolved.env);
2102
+ }
2103
+ setPhase("background-started");
2104
+ break;
2105
+ }
1987
2106
  case "cancel":
1988
2107
  onBack();
1989
2108
  break;
@@ -1998,7 +2117,7 @@ function CommandExecution({
1998
2117
  }
1999
2118
  )
2000
2119
  ] });
2001
- return /* @__PURE__ */ jsx17(Box13, { flexDirection: "column", paddingX: panelMode ? 1 : 0, children: panelMode ? /* @__PURE__ */ jsx17(
2120
+ return /* @__PURE__ */ jsx15(Box13, { flexDirection: "column", paddingX: panelMode ? 1 : 0, children: panelMode ? /* @__PURE__ */ jsx15(
2002
2121
  Box13,
2003
2122
  {
2004
2123
  flexDirection: "column",
@@ -2010,32 +2129,72 @@ function CommandExecution({
2010
2129
  }
2011
2130
  ) : confirmContent });
2012
2131
  }
2132
+ if (phase === "background-started") {
2133
+ return /* @__PURE__ */ jsxs14(Box13, { flexDirection: "column", paddingX: panelMode ? 1 : 0, children: [
2134
+ /* @__PURE__ */ jsxs14(Box13, { marginY: 1, gap: 1, children: [
2135
+ /* @__PURE__ */ jsx15(Text15, { color: "#3ECF8E", bold: true, children: "\u2713" }),
2136
+ /* @__PURE__ */ jsx15(Text15, { color: "#3ECF8E", bold: true, children: "Started in background" })
2137
+ ] }),
2138
+ /* @__PURE__ */ jsxs14(Box13, { children: [
2139
+ /* @__PURE__ */ jsx15(Text15, { dimColor: true, children: "Command: " }),
2140
+ /* @__PURE__ */ jsx15(Text15, { children: cmdDisplay })
2141
+ ] })
2142
+ ] });
2143
+ }
2013
2144
  if (phase === "running") {
2014
- return /* @__PURE__ */ jsxs15(Box13, { flexDirection: "column", paddingX: panelMode ? 1 : 0, children: [
2015
- /* @__PURE__ */ jsx17(Divider, { width: panelMode ? width - 4 : width }),
2016
- /* @__PURE__ */ jsxs15(Box13, { marginY: 1, gap: 1, children: [
2017
- /* @__PURE__ */ jsx17(Text17, { color: inkColors.accent, bold: true, children: "\u25B6" }),
2018
- /* @__PURE__ */ jsx17(Text17, { dimColor: true, children: "Running:" }),
2019
- /* @__PURE__ */ jsx17(Text17, { children: cmdDisplay }),
2020
- /* @__PURE__ */ jsx17(ToolBadge, { tool })
2145
+ const outputText = [partialStdout, partialStderr].filter(Boolean).join("\n");
2146
+ const outputLines = outputText ? stripAnsi(outputText).split("\n") : [];
2147
+ const logBoxHeight = Math.max(3, height - 7);
2148
+ const cardWidth = Math.max(30, (panelMode ? width - 4 : width) - 2);
2149
+ return /* @__PURE__ */ jsxs14(Box13, { flexDirection: "column", paddingX: panelMode ? 1 : 0, children: [
2150
+ /* @__PURE__ */ jsxs14(Box13, { marginBottom: 1, gap: 1, children: [
2151
+ /* @__PURE__ */ jsxs14(Text15, { color: inkColors.accent, bold: true, children: [
2152
+ "\u25B6",
2153
+ " ",
2154
+ cmdDisplay
2155
+ ] }),
2156
+ /* @__PURE__ */ jsx15(ToolBadge, { tool }),
2157
+ /* @__PURE__ */ jsxs14(Text15, { color: aborting ? "yellow" : "green", children: [
2158
+ "\u25CF ",
2159
+ aborting ? "aborting" : "running"
2160
+ ] })
2021
2161
  ] }),
2022
- /* @__PURE__ */ jsx17(Divider, { width: panelMode ? width - 4 : width }),
2023
- /* @__PURE__ */ jsx17(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx17(Spinner, { label: `Executing ${cmdDisplay}...` }) }),
2024
- /* @__PURE__ */ jsxs15(Box13, { marginTop: 1, children: [
2025
- /* @__PURE__ */ jsx17(Text17, { dimColor: true, children: "Press " }),
2026
- /* @__PURE__ */ jsx17(Text17, { color: inkColors.accent, children: "Esc" }),
2027
- /* @__PURE__ */ jsx17(Text17, { dimColor: true, children: " to abort" })
2162
+ /* @__PURE__ */ jsx15(
2163
+ Box13,
2164
+ {
2165
+ flexDirection: "column",
2166
+ borderStyle: "round",
2167
+ borderColor: inkColors.accent,
2168
+ paddingX: 1,
2169
+ width: cardWidth,
2170
+ children: /* @__PURE__ */ jsx15(
2171
+ ScrollableBox,
2172
+ {
2173
+ height: logBoxHeight,
2174
+ isActive: isInputActive,
2175
+ autoScrollToBottom: true,
2176
+ children: outputLines.length === 0 ? [/* @__PURE__ */ jsx15(Text15, { dimColor: true, children: "Waiting for output..." }, "empty")] : outputLines.map((line, i) => /* @__PURE__ */ jsx15(Text15, { wrap: "truncate", children: line }, i))
2177
+ }
2178
+ )
2179
+ }
2180
+ ),
2181
+ feedback && /* @__PURE__ */ jsx15(Box13, { children: /* @__PURE__ */ jsx15(Text15, { color: inkColors.accent, children: feedback }) }),
2182
+ /* @__PURE__ */ jsxs14(Box13, { marginTop: 1, gap: 2, children: [
2183
+ /* @__PURE__ */ jsx15(Text15, { dimColor: true, children: "\u2191\u2193:scroll" }),
2184
+ /* @__PURE__ */ jsx15(Text15, { dimColor: true, children: "x:abort" }),
2185
+ /* @__PURE__ */ jsx15(Text15, { dimColor: true, children: "c:copy" }),
2186
+ /* @__PURE__ */ jsx15(Text15, { dimColor: true, children: "Esc:back" })
2028
2187
  ] })
2029
2188
  ] });
2030
2189
  }
2031
2190
  if (phase === "success-pin-offer") {
2032
- return /* @__PURE__ */ jsxs15(Box13, { flexDirection: "column", paddingX: panelMode ? 1 : 0, children: [
2033
- /* @__PURE__ */ jsx17(Divider, { width: panelMode ? width - 4 : width }),
2034
- /* @__PURE__ */ jsxs15(Box13, { marginY: 1, gap: 1, children: [
2035
- /* @__PURE__ */ jsx17(Text17, { color: inkColors.accent, bold: true, children: "\u2713" }),
2036
- /* @__PURE__ */ jsx17(Text17, { color: inkColors.accent, bold: true, children: "Command completed successfully!" })
2191
+ return /* @__PURE__ */ jsxs14(Box13, { flexDirection: "column", paddingX: panelMode ? 1 : 0, children: [
2192
+ /* @__PURE__ */ jsx15(Divider, { width: panelMode ? width - 4 : width }),
2193
+ /* @__PURE__ */ jsxs14(Box13, { marginY: 1, gap: 1, children: [
2194
+ /* @__PURE__ */ jsx15(Text15, { color: inkColors.accent, bold: true, children: "\u2713" }),
2195
+ /* @__PURE__ */ jsx15(Text15, { color: inkColors.accent, bold: true, children: "Command completed successfully!" })
2037
2196
  ] }),
2038
- /* @__PURE__ */ jsx17(
2197
+ /* @__PURE__ */ jsx15(
2039
2198
  ConfirmPrompt,
2040
2199
  {
2041
2200
  message: "Pin this exact command?",
@@ -2058,24 +2217,49 @@ function CommandExecution({
2058
2217
  const successItems = [
2059
2218
  { value: "__back__", label: "\u2190 Back to menu" }
2060
2219
  ];
2061
- const outputHeight = Math.max(3, height - 12);
2062
- return /* @__PURE__ */ jsxs15(Box13, { flexDirection: "column", paddingX: panelMode ? 1 : 0, children: [
2063
- /* @__PURE__ */ jsx17(Divider, { width: panelMode ? width - 4 : width }),
2064
- /* @__PURE__ */ jsxs15(Box13, { marginY: 1, gap: 1, children: [
2065
- /* @__PURE__ */ jsx17(Text17, { color: inkColors.accent, bold: true, children: "\u2713" }),
2066
- /* @__PURE__ */ jsx17(Text17, { color: inkColors.accent, bold: true, children: "Command completed successfully!" })
2220
+ const successOutput = [result?.stdout, result?.stderr].filter(Boolean).join("\n");
2221
+ const successLines = successOutput ? stripAnsi(successOutput).split("\n") : [];
2222
+ const successLogHeight = Math.max(3, height - 9 - (pinMessage ? 1 : 0));
2223
+ const successCardWidth = Math.max(30, (panelMode ? width - 4 : width) - 2);
2224
+ return /* @__PURE__ */ jsxs14(Box13, { flexDirection: "column", paddingX: panelMode ? 1 : 0, children: [
2225
+ /* @__PURE__ */ jsxs14(Box13, { marginBottom: 1, gap: 1, children: [
2226
+ /* @__PURE__ */ jsxs14(Text15, { color: inkColors.accent, bold: true, children: [
2227
+ "\u2713",
2228
+ " ",
2229
+ cmdDisplay
2230
+ ] }),
2231
+ /* @__PURE__ */ jsx15(ToolBadge, { tool }),
2232
+ /* @__PURE__ */ jsx15(Text15, { color: "green", children: "\u25CF completed" })
2067
2233
  ] }),
2068
- pinMessage && /* @__PURE__ */ jsx17(Box13, { marginBottom: 1, children: /* @__PURE__ */ jsx17(Text17, { color: inkColors.accent, children: pinMessage }) }),
2069
- /* @__PURE__ */ jsx17(
2070
- CommandOutput,
2234
+ pinMessage && /* @__PURE__ */ jsx15(Box13, { marginBottom: 1, children: /* @__PURE__ */ jsx15(Text15, { color: inkColors.accent, children: pinMessage }) }),
2235
+ /* @__PURE__ */ jsx15(
2236
+ Box13,
2071
2237
  {
2072
- stdout: result?.stdout,
2073
- stderr: result?.stderr,
2074
- height: outputHeight,
2075
- isActive: isInputActive
2238
+ flexDirection: "column",
2239
+ borderStyle: "round",
2240
+ borderColor: outputFocused ? inkColors.accent : panel.borderDim,
2241
+ paddingX: 1,
2242
+ width: successCardWidth,
2243
+ children: /* @__PURE__ */ jsx15(
2244
+ ScrollableBox,
2245
+ {
2246
+ height: successLogHeight,
2247
+ isActive: isInputActive && outputFocused,
2248
+ autoScrollToBottom: true,
2249
+ children: successLines.length === 0 ? [/* @__PURE__ */ jsx15(Text15, { dimColor: true, children: "No output" }, "empty")] : successLines.map((line, i) => /* @__PURE__ */ jsx15(Text15, { wrap: "truncate", children: line }, i))
2250
+ }
2251
+ )
2076
2252
  }
2077
2253
  ),
2078
- /* @__PURE__ */ jsx17(
2254
+ /* @__PURE__ */ jsxs14(Box13, { marginTop: 1, gap: 2, children: [
2255
+ /* @__PURE__ */ jsxs14(Text15, { dimColor: true, children: [
2256
+ "/:",
2257
+ outputFocused ? "menu" : "scroll"
2258
+ ] }),
2259
+ outputFocused && /* @__PURE__ */ jsx15(Text15, { dimColor: true, children: "\u2191\u2193:scroll" }),
2260
+ /* @__PURE__ */ jsx15(Text15, { dimColor: true, children: "Esc:back" })
2261
+ ] }),
2262
+ /* @__PURE__ */ jsx15(
2079
2263
  SelectList,
2080
2264
  {
2081
2265
  items: successItems,
@@ -2083,10 +2267,10 @@ function CommandExecution({
2083
2267
  onCancel: onHome ?? onBack,
2084
2268
  width: panelMode ? Math.max(20, width - 4) : width,
2085
2269
  maxVisible: panelMode ? Math.max(6, height - 6) : void 0,
2086
- isInputActive,
2270
+ isInputActive: isInputActive && !outputFocused,
2087
2271
  arrowNavigation: panelMode,
2088
2272
  boxedSections: panelMode,
2089
- panelFocused: isInputActive
2273
+ panelFocused: isInputActive && !outputFocused
2090
2274
  }
2091
2275
  )
2092
2276
  ] });
@@ -2149,65 +2333,67 @@ function CommandExecution({
2149
2333
  value: "menu",
2150
2334
  label: "\u2190 Back to menu"
2151
2335
  });
2152
- return /* @__PURE__ */ jsxs15(Box13, { flexDirection: "column", paddingX: panelMode ? 1 : 0, children: [
2153
- /* @__PURE__ */ jsx17(Divider, { width: panelMode ? width - 4 : width }),
2154
- result?.spawnError ? /* @__PURE__ */ jsxs15(Box13, { flexDirection: "column", marginY: 1, children: [
2155
- /* @__PURE__ */ jsxs15(Box13, { gap: 1, children: [
2156
- /* @__PURE__ */ jsx17(Text17, { color: "red", bold: true, children: "\u2717" }),
2157
- /* @__PURE__ */ jsx17(Text17, { color: "red", bold: true, children: "Failed to start command" })
2158
- ] }),
2159
- /* @__PURE__ */ jsxs15(Box13, { marginLeft: 2, marginTop: 1, children: [
2160
- /* @__PURE__ */ jsx17(Text17, { dimColor: true, children: "Error: " }),
2161
- /* @__PURE__ */ jsx17(Text17, { color: "red", children: result.spawnError })
2162
- ] }),
2163
- (result.spawnError.includes("ENOENT") || result.spawnError.includes("not found")) && /* @__PURE__ */ jsx17(Box13, { flexDirection: "column", marginLeft: 2, marginTop: 1, children: /* @__PURE__ */ jsxs15(Text17, { color: inkColors.accent, bold: true, children: [
2164
- "\u{1F4A1}",
2336
+ const errorOutput = [result?.stdout, result?.stderr].filter(Boolean).join("\n");
2337
+ const errorLines = errorOutput ? stripAnsi(errorOutput).split("\n") : [];
2338
+ const errorLogHeight = Math.max(3, height - 8 - Math.min(errorItems.length, 6) - (copyMessage ? 1 : 0));
2339
+ const errorCardWidth = Math.max(30, (panelMode ? width - 4 : width) - 2);
2340
+ const errorStatusLabel = result?.spawnError ? "spawn error" : `exit ${result?.exitCode}`;
2341
+ return /* @__PURE__ */ jsxs14(Box13, { flexDirection: "column", paddingX: panelMode ? 1 : 0, children: [
2342
+ /* @__PURE__ */ jsxs14(Box13, { marginBottom: 1, gap: 1, children: [
2343
+ /* @__PURE__ */ jsxs14(Text15, { color: "red", bold: true, children: [
2344
+ "\u2717",
2165
2345
  " ",
2346
+ cmdDisplay
2347
+ ] }),
2348
+ /* @__PURE__ */ jsx15(ToolBadge, { tool }),
2349
+ /* @__PURE__ */ jsxs14(Text15, { color: "red", children: [
2350
+ "\u25CF ",
2351
+ errorStatusLabel
2352
+ ] })
2353
+ ] }),
2354
+ result?.spawnError && /* @__PURE__ */ jsxs14(Box13, { marginBottom: 1, children: [
2355
+ /* @__PURE__ */ jsx15(Text15, { dimColor: true, children: "Error: " }),
2356
+ /* @__PURE__ */ jsx15(Text15, { color: "red", children: result.spawnError }),
2357
+ (result.spawnError.includes("ENOENT") || result.spawnError.includes("not found")) && /* @__PURE__ */ jsxs14(Text15, { color: inkColors.accent, children: [
2358
+ " \u2014 ",
2166
2359
  tool,
2167
2360
  " CLI not found in this repository or PATH"
2168
- ] }) })
2169
- ] }) : /* @__PURE__ */ jsxs15(Box13, { flexDirection: "column", marginY: 1, children: [
2170
- /* @__PURE__ */ jsxs15(Box13, { gap: 1, children: [
2171
- /* @__PURE__ */ jsx17(Text17, { color: "red", bold: true, children: "\u2717" }),
2172
- /* @__PURE__ */ jsx17(Text17, { color: "red", children: "Command failed " }),
2173
- /* @__PURE__ */ jsx17(Text17, { dimColor: true, children: "(exit code " }),
2174
- /* @__PURE__ */ jsx17(Text17, { color: "red", bold: true, children: String(result?.exitCode) }),
2175
- /* @__PURE__ */ jsx17(Text17, { dimColor: true, children: ")" })
2176
- ] }),
2177
- /* @__PURE__ */ jsxs15(Box13, { marginLeft: 2, marginTop: 1, children: [
2178
- /* @__PURE__ */ jsx17(Text17, { dimColor: true, children: "Command: " }),
2179
- /* @__PURE__ */ jsx17(Text17, { children: cmdDisplay })
2180
- ] }),
2181
- !hasDebug && /* @__PURE__ */ jsxs15(Box13, { marginLeft: 2, marginTop: 1, gap: 1, children: [
2182
- /* @__PURE__ */ jsxs15(Text17, { dimColor: true, children: [
2183
- "\u{1F4A1}",
2184
- " Tip: retry with"
2185
- ] }),
2186
- /* @__PURE__ */ jsx17(Text17, { color: inkColors.accent, children: "--debug" }),
2187
- /* @__PURE__ */ jsx17(Text17, { dimColor: true, children: "to see detailed logs" })
2188
2361
  ] })
2189
2362
  ] }),
2190
- /* @__PURE__ */ jsx17(
2191
- CommandOutput,
2363
+ !result?.spawnError && !hasDebug && /* @__PURE__ */ jsxs14(Box13, { marginBottom: 1, gap: 1, children: [
2364
+ /* @__PURE__ */ jsx15(Text15, { dimColor: true, children: "\u{1F4A1} Tip: retry with" }),
2365
+ /* @__PURE__ */ jsx15(Text15, { color: inkColors.accent, children: "--debug" }),
2366
+ /* @__PURE__ */ jsx15(Text15, { dimColor: true, children: "to see detailed logs" })
2367
+ ] }),
2368
+ /* @__PURE__ */ jsx15(
2369
+ Box13,
2192
2370
  {
2193
- stdout: result?.stdout,
2194
- stderr: result?.stderr,
2195
- height: Math.max(3, height - 14 - errorItems.length - (suggestions.length > 0 ? suggestions.length + 4 : 0) - (copyMessage ? 1 : 0)),
2196
- isActive: isInputActive && outputFocused
2371
+ flexDirection: "column",
2372
+ borderStyle: "round",
2373
+ borderColor: outputFocused ? "red" : panel.borderDim,
2374
+ paddingX: 1,
2375
+ width: errorCardWidth,
2376
+ children: /* @__PURE__ */ jsx15(
2377
+ ScrollableBox,
2378
+ {
2379
+ height: errorLogHeight,
2380
+ isActive: isInputActive && outputFocused,
2381
+ autoScrollToBottom: true,
2382
+ children: errorLines.length === 0 ? [/* @__PURE__ */ jsx15(Text15, { dimColor: true, children: "No output" }, "empty")] : errorLines.map((line, i) => /* @__PURE__ */ jsx15(Text15, { wrap: "truncate", children: line }, i))
2383
+ }
2384
+ )
2197
2385
  }
2198
2386
  ),
2199
- copyMessage && /* @__PURE__ */ jsx17(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx17(Text17, { color: inkColors.accent, children: copyMessage }) }),
2200
- outputFocused ? /* @__PURE__ */ jsxs15(Box13, { marginTop: 1, children: [
2201
- /* @__PURE__ */ jsx17(Text17, { dimColor: true, children: "j/k scroll \xB7 " }),
2202
- /* @__PURE__ */ jsx17(Text17, { color: inkColors.accent, children: "Esc" }),
2203
- /* @__PURE__ */ jsx17(Text17, { dimColor: true, children: " back to menu" })
2204
- ] }) : /* @__PURE__ */ jsxs15(Box13, { marginTop: 1, marginBottom: 1, children: [
2205
- /* @__PURE__ */ jsx17(Text17, { bold: true, children: "What would you like to do?" }),
2206
- /* @__PURE__ */ jsx17(Text17, { dimColor: true, children: " (press " }),
2207
- /* @__PURE__ */ jsx17(Text17, { color: inkColors.accent, children: "o" }),
2208
- /* @__PURE__ */ jsx17(Text17, { dimColor: true, children: " to scroll output)" })
2387
+ copyMessage && /* @__PURE__ */ jsx15(Box13, { children: /* @__PURE__ */ jsx15(Text15, { color: inkColors.accent, children: copyMessage }) }),
2388
+ /* @__PURE__ */ jsxs14(Box13, { marginTop: 1, gap: 2, children: [
2389
+ /* @__PURE__ */ jsxs14(Text15, { dimColor: true, children: [
2390
+ "/:",
2391
+ outputFocused ? "menu" : "scroll"
2392
+ ] }),
2393
+ outputFocused && /* @__PURE__ */ jsx15(Text15, { dimColor: true, children: "\u2191\u2193:scroll" }),
2394
+ /* @__PURE__ */ jsx15(Text15, { dimColor: true, children: "Esc:back" })
2209
2395
  ] }),
2210
- /* @__PURE__ */ jsx17(
2396
+ /* @__PURE__ */ jsx15(
2211
2397
  SelectList,
2212
2398
  {
2213
2399
  items: errorItems,
@@ -2231,6 +2417,8 @@ function CommandExecution({
2231
2417
  case "retry":
2232
2418
  setPinMessage(void 0);
2233
2419
  setCopyMessage(void 0);
2420
+ setAborting(false);
2421
+ setFeedback(void 0);
2234
2422
  reset();
2235
2423
  setPhase("running");
2236
2424
  break;
@@ -2239,6 +2427,8 @@ function CommandExecution({
2239
2427
  setCurrentArgs(newArgs);
2240
2428
  setPinMessage(void 0);
2241
2429
  setCopyMessage(void 0);
2430
+ setAborting(false);
2431
+ setFeedback(void 0);
2242
2432
  reset();
2243
2433
  setPhase("running");
2244
2434
  break;
@@ -2277,13 +2467,54 @@ function CommandExecution({
2277
2467
  panelFocused: isInputActive && !outputFocused
2278
2468
  }
2279
2469
  ),
2280
- !panelMode && /* @__PURE__ */ jsx17(StatusBar, { width })
2470
+ !panelMode && /* @__PURE__ */ jsx15(StatusBar, { width })
2281
2471
  ] });
2282
2472
  }
2283
2473
 
2284
2474
  // src/screens/SelfUpdate.tsx
2285
- import { useEffect as useEffect8, useState as useState12 } from "react";
2475
+ import { useEffect as useEffect10, useState as useState13 } from "react";
2286
2476
  import { Box as Box14, Text as Text18 } from "ink";
2477
+
2478
+ // src/components/Spinner.tsx
2479
+ import { Text as Text16 } from "ink";
2480
+ import InkSpinner from "ink-spinner";
2481
+ import { jsx as jsx16, jsxs as jsxs15 } from "react/jsx-runtime";
2482
+ function Spinner({
2483
+ label = "Running...",
2484
+ color = inkColors.accent
2485
+ }) {
2486
+ return /* @__PURE__ */ jsxs15(Text16, { children: [
2487
+ /* @__PURE__ */ jsx16(Text16, { color, children: /* @__PURE__ */ jsx16(InkSpinner, { type: "dots" }) }),
2488
+ " ",
2489
+ /* @__PURE__ */ jsx16(Text16, { children: label })
2490
+ ] });
2491
+ }
2492
+
2493
+ // src/components/CommandOutput.tsx
2494
+ import { Text as Text17 } from "ink";
2495
+ import { jsx as jsx17 } from "react/jsx-runtime";
2496
+ function cleanLines(raw) {
2497
+ const stripped = stripAnsi(raw);
2498
+ return stripped.replace(/\r/g, "").split("\n").filter((line) => line.length > 0);
2499
+ }
2500
+ function CommandOutput({
2501
+ stdout,
2502
+ stderr,
2503
+ height,
2504
+ isActive = false
2505
+ }) {
2506
+ const outLines = stdout ? cleanLines(stdout) : [];
2507
+ const errLines = stderr ? cleanLines(stderr) : [];
2508
+ if (outLines.length === 0 && errLines.length === 0) {
2509
+ return null;
2510
+ }
2511
+ return /* @__PURE__ */ jsx17(ScrollableBox, { height: Math.max(3, height), isActive, children: [
2512
+ ...outLines.map((line, i) => /* @__PURE__ */ jsx17(Text17, { children: line }, `o-${i}`)),
2513
+ ...errLines.map((line, i) => /* @__PURE__ */ jsx17(Text17, { color: "red", children: line }, `e-${i}`))
2514
+ ] });
2515
+ }
2516
+
2517
+ // src/screens/SelfUpdate.tsx
2287
2518
  import { jsx as jsx18, jsxs as jsxs16 } from "react/jsx-runtime";
2288
2519
  var packageName = "@polterware/polter";
2289
2520
  var globalUpdateArgs = ["install", "-g", `${packageName}@latest`];
@@ -2300,10 +2531,10 @@ function SelfUpdate({
2300
2531
  isInputActive = true
2301
2532
  }) {
2302
2533
  const repositoryRoot = findNearestPackageRoot();
2303
- const [target, setTarget] = useState12(
2534
+ const [target, setTarget] = useState13(
2304
2535
  repositoryRoot ? "repository" : "global"
2305
2536
  );
2306
- const [phase, setPhase] = useState12(
2537
+ const [phase, setPhase] = useState13(
2307
2538
  repositoryRoot ? "target" : "confirm"
2308
2539
  );
2309
2540
  const updateArgs = getUpdateArgs(target);
@@ -2312,12 +2543,12 @@ function SelfUpdate({
2312
2543
  const { status, result, run, reset } = useCommand("npm", updateCwd, {
2313
2544
  quiet: panelMode
2314
2545
  });
2315
- useEffect8(() => {
2546
+ useEffect10(() => {
2316
2547
  if (phase === "running" && status === "idle") {
2317
2548
  run(updateArgs);
2318
2549
  }
2319
2550
  }, [phase, run, status, updateArgs]);
2320
- useEffect8(() => {
2551
+ useEffect10(() => {
2321
2552
  if (phase === "running" && status === "success") {
2322
2553
  setPhase("success");
2323
2554
  }
@@ -2558,15 +2789,15 @@ function SelfUpdate({
2558
2789
  }
2559
2790
 
2560
2791
  // src/screens/ToolStatus.tsx
2561
- import { useEffect as useEffect9, useState as useState13 } from "react";
2792
+ import { useEffect as useEffect11, useState as useState14 } from "react";
2562
2793
  import { Box as Box15, Text as Text19 } from "ink";
2563
2794
  import { jsx as jsx19, jsxs as jsxs17 } from "react/jsx-runtime";
2564
2795
  var toolIds = ["supabase", "gh", "vercel", "git", "pkg"];
2565
2796
  var linkableTools = /* @__PURE__ */ new Set(["supabase", "gh", "vercel", "pkg"]);
2566
2797
  function ToolStatus({ onBack, onNavigate, width = 80, height = 24, panelMode = false, isInputActive = true }) {
2567
- const [tools, setTools] = useState13(null);
2568
- const [mcpInfo, setMcpInfo] = useState13(null);
2569
- useEffect9(() => {
2798
+ const [tools, setTools] = useState14(null);
2799
+ const [mcpInfo, setMcpInfo] = useState14(null);
2800
+ useEffect11(() => {
2570
2801
  const t = setTimeout(() => {
2571
2802
  setTools(toolIds.map((id) => getToolLinkInfo(id)));
2572
2803
  setMcpInfo(getMcpStatusInfo());
@@ -2652,11 +2883,11 @@ function ToolStatus({ onBack, onNavigate, width = 80, height = 24, panelMode = f
2652
2883
  }
2653
2884
 
2654
2885
  // src/screens/ProjectConfig.tsx
2655
- import { useMemo as useMemo4, useState as useState15 } from "react";
2886
+ import { useMemo as useMemo4, useState as useState16 } from "react";
2656
2887
  import { Box as Box16, Text as Text20 } from "ink";
2657
2888
 
2658
2889
  // src/hooks/useEditor.ts
2659
- import { useState as useState14, useCallback as useCallback4 } from "react";
2890
+ import { useState as useState15, useCallback as useCallback4 } from "react";
2660
2891
  import { useStdin } from "ink";
2661
2892
 
2662
2893
  // src/lib/editor.ts
@@ -2707,7 +2938,7 @@ function openInEditor(filePath) {
2707
2938
  // src/hooks/useEditor.ts
2708
2939
  function useEditor() {
2709
2940
  const { setRawMode } = useStdin();
2710
- const [isEditing, setIsEditing] = useState14(false);
2941
+ const [isEditing, setIsEditing] = useState15(false);
2711
2942
  const openEditor = useCallback4(async (filePath) => {
2712
2943
  const editor = resolveEditor();
2713
2944
  const terminal = isTerminalEditor(editor.command);
@@ -2737,11 +2968,11 @@ function ProjectConfig({
2737
2968
  isInputActive = true
2738
2969
  }) {
2739
2970
  const configPath = useMemo4(() => getProjectConfigPath(), []);
2740
- const [config2, setConfig] = useState15(() => getOrCreateProjectConfig());
2741
- const [phase, setPhase] = useState15("overview");
2742
- const [feedback, setFeedback] = useState15();
2743
- const [envKey, setEnvKey] = useState15("");
2744
- const [selectedEnvKey, setSelectedEnvKey] = useState15("");
2971
+ const [config2, setConfig] = useState16(() => getOrCreateProjectConfig());
2972
+ const [phase, setPhase] = useState16("overview");
2973
+ const [feedback, setFeedback] = useState16();
2974
+ const [envKey, setEnvKey] = useState16("");
2975
+ const [selectedEnvKey, setSelectedEnvKey] = useState16("");
2745
2976
  const { openEditor, isEditing } = useEditor();
2746
2977
  const detectedPkg = useMemo4(() => detectPkgManager(), []);
2747
2978
  if (!configPath) {
@@ -3223,7 +3454,7 @@ function PipelineList({
3223
3454
  }
3224
3455
 
3225
3456
  // src/screens/PipelineBuilder.tsx
3226
- import { useState as useState16 } from "react";
3457
+ import { useState as useState17 } from "react";
3227
3458
  import { Box as Box18, Text as Text22 } from "ink";
3228
3459
  import { jsx as jsx22, jsxs as jsxs20 } from "react/jsx-runtime";
3229
3460
  var stepCounter = 0;
@@ -3238,9 +3469,9 @@ function PipelineBuilder({
3238
3469
  panelMode = false,
3239
3470
  isInputActive = true
3240
3471
  }) {
3241
- const [phase, setPhase] = useState16("name");
3242
- const [name, setName] = useState16("");
3243
- const [steps, setSteps] = useState16([]);
3472
+ const [phase, setPhase] = useState17("name");
3473
+ const [name, setName] = useState17("");
3474
+ const [steps, setSteps] = useState17([]);
3244
3475
  if (phase === "name") {
3245
3476
  return /* @__PURE__ */ jsxs20(Box18, { flexDirection: "column", paddingX: panelMode ? 1 : 0, children: [
3246
3477
  /* @__PURE__ */ jsx22(Box18, { marginBottom: 1, children: /* @__PURE__ */ jsx22(Text22, { bold: true, color: inkColors.accent, children: "\u{1F517} New Pipeline" }) }),
@@ -3426,7 +3657,7 @@ function PipelineBuilder({
3426
3657
  }
3427
3658
 
3428
3659
  // src/screens/PipelineExecution.tsx
3429
- import { useState as useState17, useEffect as useEffect10, useMemo as useMemo6 } from "react";
3660
+ import { useState as useState18, useEffect as useEffect12, useMemo as useMemo6 } from "react";
3430
3661
  import { Box as Box21, Text as Text25 } from "ink";
3431
3662
 
3432
3663
  // src/components/StepIndicator.tsx
@@ -3519,10 +3750,10 @@ function PipelineExecution({
3519
3750
  () => getAllPipelines().find((p) => p.id === pipelineId),
3520
3751
  [pipelineId]
3521
3752
  );
3522
- const [phase, setPhase] = useState17("running");
3523
- const [progress, setProgress] = useState17(null);
3524
- const [results, setResults] = useState17([]);
3525
- useEffect10(() => {
3753
+ const [phase, setPhase] = useState18("running");
3754
+ const [progress, setProgress] = useState18(null);
3755
+ const [results, setResults] = useState18([]);
3756
+ useEffect12(() => {
3526
3757
  if (!pipeline) return;
3527
3758
  executePipeline(pipeline, (p) => {
3528
3759
  setProgress({ ...p });
@@ -3636,7 +3867,7 @@ function PipelineExecution({
3636
3867
  }
3637
3868
 
3638
3869
  // src/screens/McpManage.tsx
3639
- import { useState as useState18, useCallback as useCallback5 } from "react";
3870
+ import { useState as useState19, useCallback as useCallback5 } from "react";
3640
3871
  import { Box as Box22, Text as Text26 } from "ink";
3641
3872
  import { jsx as jsx25, jsxs as jsxs24 } from "react/jsx-runtime";
3642
3873
  function McpManage({
@@ -3646,10 +3877,10 @@ function McpManage({
3646
3877
  panelMode = false,
3647
3878
  isInputActive = true
3648
3879
  }) {
3649
- const [status, setStatus] = useState18(() => getMcpStatusInfo());
3650
- const [phase, setPhase] = useState18("overview");
3651
- const [action, setAction] = useState18(null);
3652
- const [result, setResult] = useState18(null);
3880
+ const [status, setStatus] = useState19(() => getMcpStatusInfo());
3881
+ const [phase, setPhase] = useState19("overview");
3882
+ const [action, setAction] = useState19(null);
3883
+ const [result, setResult] = useState19(null);
3653
3884
  const refreshStatus = useCallback5(() => {
3654
3885
  setStatus(getMcpStatusInfo());
3655
3886
  }, []);
@@ -3791,7 +4022,7 @@ function McpManage({
3791
4022
  }
3792
4023
 
3793
4024
  // src/screens/ProcessList.tsx
3794
- import { useState as useState19, useEffect as useEffect11, useCallback as useCallback6 } from "react";
4025
+ import { useState as useState20, useEffect as useEffect13, useCallback as useCallback6 } from "react";
3795
4026
  import { Box as Box23, Text as Text27, useInput as useInput9 } from "ink";
3796
4027
  import { homedir } from "os";
3797
4028
  import { jsx as jsx26, jsxs as jsxs25 } from "react/jsx-runtime";
@@ -3831,22 +4062,22 @@ function ProcessList({
3831
4062
  panelMode = false,
3832
4063
  isInputActive = true
3833
4064
  }) {
3834
- const [processes, setProcesses] = useState19(() => listProcesses());
3835
- const [selectedIndex, setSelectedIndex] = useState19(0);
3836
- const [feedback, setFeedback] = useState19();
3837
- useEffect11(() => {
4065
+ const [processes, setProcesses] = useState20(() => listProcesses());
4066
+ const [selectedIndex, setSelectedIndex] = useState20(0);
4067
+ const [feedback, setFeedback] = useState20();
4068
+ useEffect13(() => {
3838
4069
  const interval = setInterval(() => {
3839
4070
  setProcesses(listProcesses());
3840
4071
  }, 2e3);
3841
4072
  return () => clearInterval(interval);
3842
4073
  }, []);
3843
- useEffect11(() => {
4074
+ useEffect13(() => {
3844
4075
  if (feedback) {
3845
4076
  const timer = setTimeout(() => setFeedback(void 0), 2e3);
3846
4077
  return () => clearTimeout(timer);
3847
4078
  }
3848
4079
  }, [feedback]);
3849
- useEffect11(() => {
4080
+ useEffect13(() => {
3850
4081
  if (selectedIndex >= processes.length && processes.length > 0) {
3851
4082
  setSelectedIndex(processes.length - 1);
3852
4083
  }
@@ -3996,7 +4227,7 @@ function ProcessList({
3996
4227
  }
3997
4228
 
3998
4229
  // src/screens/ProcessLogs.tsx
3999
- import { useState as useState20, useEffect as useEffect12, useCallback as useCallback7 } from "react";
4230
+ import { useState as useState21, useEffect as useEffect14, useCallback as useCallback7 } from "react";
4000
4231
  import { Box as Box24, Text as Text28, useInput as useInput10 } from "ink";
4001
4232
  import { spawn as spawn3 } from "child_process";
4002
4233
  import { jsx as jsx27, jsxs as jsxs26 } from "react/jsx-runtime";
@@ -4015,16 +4246,16 @@ function ProcessLogs({
4015
4246
  panelMode = false,
4016
4247
  isInputActive = true
4017
4248
  }) {
4018
- const [stream, setStream] = useState20("both");
4019
- const [lines, setLines] = useState20([]);
4020
- const [proc, setProc] = useState20();
4021
- const [feedback, setFeedback] = useState20();
4249
+ const [stream, setStream] = useState21("both");
4250
+ const [lines, setLines] = useState21([]);
4251
+ const [proc, setProc] = useState21();
4252
+ const [feedback, setFeedback] = useState21();
4022
4253
  const logBoxHeight = Math.max(3, height - 6);
4023
- useEffect12(() => {
4254
+ useEffect14(() => {
4024
4255
  const refresh = () => {
4025
4256
  try {
4026
- const output2 = getProcessOutput(processId, logBoxHeight, stream);
4027
- const combined = stream === "stderr" ? output2.stderr : stream === "stdout" ? output2.stdout : [...output2.stdout, ...output2.stderr].slice(-logBoxHeight);
4257
+ const output2 = getProcessOutput(processId, 1e3, stream);
4258
+ const combined = stream === "stderr" ? output2.stderr : stream === "stdout" ? output2.stdout : [...output2.stdout, ...output2.stderr].slice(-1e3);
4028
4259
  setLines(combined);
4029
4260
  } catch {
4030
4261
  setLines([`Process "${processId}" not found`]);
@@ -4035,8 +4266,8 @@ function ProcessLogs({
4035
4266
  refresh();
4036
4267
  const interval = setInterval(refresh, 1e3);
4037
4268
  return () => clearInterval(interval);
4038
- }, [processId, logBoxHeight, stream]);
4039
- useEffect12(() => {
4269
+ }, [processId, stream]);
4270
+ useEffect14(() => {
4040
4271
  if (feedback) {
4041
4272
  const timer = setTimeout(() => setFeedback(void 0), 2e3);
4042
4273
  return () => clearTimeout(timer);
@@ -4104,10 +4335,16 @@ function ProcessLogs({
4104
4335
  borderStyle: "round",
4105
4336
  borderColor: inkColors.accent,
4106
4337
  paddingX: 1,
4107
- height: logBoxHeight + 2,
4108
4338
  width: cardWidth,
4109
- overflowY: "hidden",
4110
- children: lines.length === 0 ? /* @__PURE__ */ jsx27(Text28, { dimColor: true, children: "No output yet..." }) : lines.map((line, i) => /* @__PURE__ */ jsx27(Text28, { wrap: "truncate", children: line }, i))
4339
+ children: /* @__PURE__ */ jsx27(
4340
+ ScrollableBox,
4341
+ {
4342
+ height: logBoxHeight,
4343
+ isActive: isInputActive,
4344
+ autoScrollToBottom: true,
4345
+ children: lines.length === 0 ? [/* @__PURE__ */ jsx27(Text28, { dimColor: true, children: "No output yet..." }, "empty")] : lines.map((line, i) => /* @__PURE__ */ jsx27(Text28, { wrap: "truncate", children: line }, i))
4346
+ }
4347
+ )
4111
4348
  }
4112
4349
  ),
4113
4350
  feedback && /* @__PURE__ */ jsx27(Box24, { children: /* @__PURE__ */ jsx27(Text28, { color: inkColors.accent, children: feedback }) }),
@@ -4117,6 +4354,7 @@ function ProcessLogs({
4117
4354
  /* @__PURE__ */ jsx27(Text28, { bold: true, color: inkColors.accent, children: stream }),
4118
4355
  "]"
4119
4356
  ] }),
4357
+ /* @__PURE__ */ jsx27(Text28, { dimColor: true, children: "\u2191\u2193:scroll" }),
4120
4358
  /* @__PURE__ */ jsx27(Text28, { dimColor: true, children: "s:toggle" }),
4121
4359
  /* @__PURE__ */ jsx27(Text28, { dimColor: true, children: "x:stop" }),
4122
4360
  /* @__PURE__ */ jsx27(Text28, { dimColor: true, children: "c:copy" }),
@@ -4126,7 +4364,7 @@ function ProcessLogs({
4126
4364
  }
4127
4365
 
4128
4366
  // src/screens/DeclarativePlan.tsx
4129
- import { useEffect as useEffect13, useState as useState21 } from "react";
4367
+ import { useEffect as useEffect15, useState as useState22 } from "react";
4130
4368
  import { Box as Box25, Text as Text29 } from "ink";
4131
4369
  import { jsx as jsx28, jsxs as jsxs27 } from "react/jsx-runtime";
4132
4370
  function DeclarativePlan({
@@ -4137,10 +4375,10 @@ function DeclarativePlan({
4137
4375
  panelMode = false,
4138
4376
  isInputActive = true
4139
4377
  }) {
4140
- const [phase, setPhase] = useState21("loading");
4141
- const [actions, setActions] = useState21([]);
4142
- const [applyProgress, setApplyProgress] = useState21("");
4143
- const [applyResults, setApplyResults] = useState21([]);
4378
+ const [phase, setPhase] = useState22("loading");
4379
+ const [actions, setActions] = useState22([]);
4380
+ const [applyProgress, setApplyProgress] = useState22("");
4381
+ const [applyResults, setApplyResults] = useState22([]);
4144
4382
  const { openEditor } = useEditor();
4145
4383
  const loadPlan = () => {
4146
4384
  setPhase("loading");
@@ -4160,7 +4398,7 @@ function DeclarativePlan({
4160
4398
  setPhase("plan-view");
4161
4399
  }, 0);
4162
4400
  };
4163
- useEffect13(() => {
4401
+ useEffect15(() => {
4164
4402
  loadPlan();
4165
4403
  }, []);
4166
4404
  if (phase === "loading") {
@@ -4354,7 +4592,7 @@ function DeclarativePlan({
4354
4592
  }
4355
4593
 
4356
4594
  // src/screens/DeclarativeStatus.tsx
4357
- import { useEffect as useEffect14, useState as useState22 } from "react";
4595
+ import { useEffect as useEffect16, useState as useState23 } from "react";
4358
4596
  import { Box as Box26, Text as Text30 } from "ink";
4359
4597
  import { jsx as jsx29, jsxs as jsxs28 } from "react/jsx-runtime";
4360
4598
  function DeclarativeStatus({
@@ -4364,10 +4602,10 @@ function DeclarativeStatus({
4364
4602
  panelMode = false,
4365
4603
  isInputActive = true
4366
4604
  }) {
4367
- const [phase, setPhase] = useState22("loading");
4368
- const [status, setStatus] = useState22({});
4369
- const [config2, setConfig] = useState22(null);
4370
- const [pkgInfo, setPkgInfo] = useState22(null);
4605
+ const [phase, setPhase] = useState23("loading");
4606
+ const [status, setStatus] = useState23({});
4607
+ const [config2, setConfig] = useState23(null);
4608
+ const [pkgInfo, setPkgInfo] = useState23(null);
4371
4609
  const load = () => {
4372
4610
  setPhase("loading");
4373
4611
  setTimeout(() => {
@@ -4380,7 +4618,7 @@ function DeclarativeStatus({
4380
4618
  setPhase("display");
4381
4619
  }, 0);
4382
4620
  };
4383
- useEffect14(() => {
4621
+ useEffect16(() => {
4384
4622
  load();
4385
4623
  }, []);
4386
4624
  if (phase === "loading") {
@@ -4486,7 +4724,7 @@ function DeclarativeStatus({
4486
4724
  }
4487
4725
 
4488
4726
  // src/screens/InitScaffold.tsx
4489
- import { useEffect as useEffect15, useState as useState23 } from "react";
4727
+ import { useEffect as useEffect17, useState as useState24 } from "react";
4490
4728
  import { Box as Box27, Text as Text31 } from "ink";
4491
4729
  import { writeFileSync } from "fs";
4492
4730
  import { join } from "path";
@@ -4630,9 +4868,9 @@ function InitScaffold({
4630
4868
  panelMode = false,
4631
4869
  isInputActive = true
4632
4870
  }) {
4633
- const [phase, setPhase] = useState23("detecting");
4634
- const [yamlString, setYamlString] = useState23("");
4635
- const [overwrite, setOverwrite] = useState23(false);
4871
+ const [phase, setPhase] = useState24("detecting");
4872
+ const [yamlString, setYamlString] = useState24("");
4873
+ const [overwrite, setOverwrite] = useState24(false);
4636
4874
  const { openEditor } = useEditor();
4637
4875
  const cwd = process.cwd();
4638
4876
  const yamlPath = join(cwd, "polter.yaml");
@@ -4678,7 +4916,7 @@ function InitScaffold({
4678
4916
  setPhase("preview");
4679
4917
  }, 0);
4680
4918
  };
4681
- useEffect15(() => {
4919
+ useEffect17(() => {
4682
4920
  detect();
4683
4921
  }, []);
4684
4922
  if (phase === "detecting") {
@@ -4790,8 +5028,164 @@ function InitScaffold({
4790
5028
  ] });
4791
5029
  }
4792
5030
 
4793
- // src/app.tsx
5031
+ // src/screens/ScriptPicker.tsx
5032
+ import { useState as useState25, useEffect as useEffect18, useMemo as useMemo8 } from "react";
5033
+ import { Box as Box28, Text as Text32, useInput as useInput11 } from "ink";
5034
+ import { readFileSync, existsSync } from "fs";
5035
+ import { join as join2 } from "path";
4794
5036
  import { jsx as jsx31, jsxs as jsxs30 } from "react/jsx-runtime";
5037
+ function readScripts(cwd) {
5038
+ const pkgPath = join2(cwd, "package.json");
5039
+ if (!existsSync(pkgPath)) return [];
5040
+ try {
5041
+ const raw = JSON.parse(readFileSync(pkgPath, "utf-8"));
5042
+ const scripts = raw.scripts ?? {};
5043
+ return Object.entries(scripts).map(([name, command]) => ({ name, command }));
5044
+ } catch {
5045
+ return [];
5046
+ }
5047
+ }
5048
+ function ScriptPicker({
5049
+ onNavigate,
5050
+ onBack,
5051
+ width = 80,
5052
+ height = 24,
5053
+ panelMode = false,
5054
+ isInputActive = true
5055
+ }) {
5056
+ const cwd = process.cwd();
5057
+ const scripts = useMemo8(() => readScripts(cwd), [cwd]);
5058
+ const mgr = useMemo8(() => detectPkgManager(cwd), [cwd]);
5059
+ const [selectedIndex, setSelectedIndex] = useState25(0);
5060
+ const [feedback, setFeedback] = useState25();
5061
+ useEffect18(() => {
5062
+ if (feedback) {
5063
+ const timer = setTimeout(() => setFeedback(void 0), 3e3);
5064
+ return () => clearTimeout(timer);
5065
+ }
5066
+ }, [feedback]);
5067
+ useEffect18(() => {
5068
+ if (selectedIndex >= scripts.length && scripts.length > 0) {
5069
+ setSelectedIndex(scripts.length - 1);
5070
+ }
5071
+ }, [scripts.length, selectedIndex]);
5072
+ useInput11((input2, key) => {
5073
+ if (!isInputActive) return;
5074
+ if (key.escape || key.leftArrow && !key.ctrl) {
5075
+ onBack();
5076
+ return;
5077
+ }
5078
+ if (scripts.length === 0) return;
5079
+ if (key.upArrow || input2 === "k") {
5080
+ setSelectedIndex((i) => Math.max(0, i - 1));
5081
+ return;
5082
+ }
5083
+ if (key.downArrow || input2 === "j") {
5084
+ setSelectedIndex((i) => Math.min(scripts.length - 1, i + 1));
5085
+ return;
5086
+ }
5087
+ if (key.return || key.rightArrow) {
5088
+ const script = scripts[selectedIndex];
5089
+ if (script) {
5090
+ const resolved = resolveToolCommand("pkg", cwd);
5091
+ try {
5092
+ const translated = translateCommand(["run", script.name], mgr.id);
5093
+ onNavigate("command-execution", {
5094
+ tool: "pkg",
5095
+ args: translated.args
5096
+ });
5097
+ } catch {
5098
+ setFeedback(`"run" is not supported by ${mgr.id}`);
5099
+ }
5100
+ }
5101
+ return;
5102
+ }
5103
+ if (input2 === "b") {
5104
+ const script = scripts[selectedIndex];
5105
+ if (script) {
5106
+ try {
5107
+ const translated = translateCommand(["run", script.name], mgr.id);
5108
+ const id = generateProcessId(mgr.command, translated.args);
5109
+ startProcess(id, mgr.command, translated.args, cwd);
5110
+ setFeedback(`Started ${mgr.id} run ${script.name} as background process`);
5111
+ } catch (err) {
5112
+ setFeedback(err instanceof Error ? err.message : "Failed to start process");
5113
+ }
5114
+ }
5115
+ return;
5116
+ }
5117
+ });
5118
+ const contentWidth = Math.max(30, (panelMode ? width - 4 : width) - 2);
5119
+ if (scripts.length === 0) {
5120
+ return /* @__PURE__ */ jsxs30(Box28, { flexDirection: "column", paddingX: panelMode ? 1 : 0, children: [
5121
+ !panelMode && /* @__PURE__ */ jsx31(Box28, { marginBottom: 1, children: /* @__PURE__ */ jsxs30(Text32, { bold: true, color: inkColors.accent, children: [
5122
+ "\u{1F4DC}",
5123
+ " ",
5124
+ mgr.id,
5125
+ " scripts"
5126
+ ] }) }),
5127
+ /* @__PURE__ */ jsx31(
5128
+ Box28,
5129
+ {
5130
+ flexDirection: "column",
5131
+ borderStyle: "round",
5132
+ borderColor: panel.borderDim,
5133
+ borderDimColor: true,
5134
+ paddingX: 1,
5135
+ width: contentWidth,
5136
+ children: /* @__PURE__ */ jsx31(Text32, { dimColor: true, children: "No scripts found in package.json" })
5137
+ }
5138
+ ),
5139
+ /* @__PURE__ */ jsx31(Box28, { marginTop: 1, gap: 2, children: /* @__PURE__ */ jsx31(Text32, { dimColor: true, children: "Esc:back" }) })
5140
+ ] });
5141
+ }
5142
+ const headerHeight = panelMode ? 0 : 2;
5143
+ const footerHeight = 2;
5144
+ const feedbackHeight = feedback ? 2 : 0;
5145
+ const availableHeight = height - headerHeight - footerHeight - feedbackHeight;
5146
+ const itemHeight = 1;
5147
+ const visibleCount = Math.max(1, Math.floor(availableHeight / itemHeight));
5148
+ const windowStart = Math.max(0, Math.min(selectedIndex - Math.floor(visibleCount / 2), scripts.length - visibleCount));
5149
+ const visibleScripts = scripts.slice(windowStart, windowStart + visibleCount);
5150
+ return /* @__PURE__ */ jsxs30(Box28, { flexDirection: "column", paddingX: panelMode ? 1 : 0, children: [
5151
+ !panelMode && /* @__PURE__ */ jsx31(Box28, { marginBottom: 1, children: /* @__PURE__ */ jsxs30(Text32, { bold: true, color: inkColors.accent, children: [
5152
+ "\u{1F4DC}",
5153
+ " ",
5154
+ mgr.id,
5155
+ " scripts"
5156
+ ] }) }),
5157
+ feedback && /* @__PURE__ */ jsx31(Box28, { marginBottom: 1, children: /* @__PURE__ */ jsx31(Text32, { color: inkColors.accent, children: feedback }) }),
5158
+ /* @__PURE__ */ jsx31(Box28, { flexDirection: "column", width: contentWidth, children: visibleScripts.map((script) => {
5159
+ const idx = scripts.indexOf(script);
5160
+ const isFocused = idx === selectedIndex;
5161
+ return /* @__PURE__ */ jsxs30(Box28, { gap: 1, children: [
5162
+ /* @__PURE__ */ jsxs30(Text32, { color: isFocused ? inkColors.accent : void 0, bold: isFocused, children: [
5163
+ isFocused ? "\u25B6" : " ",
5164
+ " ",
5165
+ script.name
5166
+ ] }),
5167
+ /* @__PURE__ */ jsx31(Text32, { dimColor: true, children: script.command })
5168
+ ] }, script.name);
5169
+ }) }),
5170
+ scripts.length > visibleCount && /* @__PURE__ */ jsx31(Box28, { children: /* @__PURE__ */ jsxs30(Text32, { dimColor: true, children: [
5171
+ windowStart > 0 ? "\u25B2 " : " ",
5172
+ windowStart + visibleCount < scripts.length ? "\u25BC " : " ",
5173
+ scripts.length,
5174
+ " scripts"
5175
+ ] }) }),
5176
+ /* @__PURE__ */ jsxs30(Box28, { marginTop: 1, gap: 2, children: [
5177
+ /* @__PURE__ */ jsxs30(Text32, { dimColor: true, children: [
5178
+ "\u21B5",
5179
+ ":run"
5180
+ ] }),
5181
+ /* @__PURE__ */ jsx31(Text32, { dimColor: true, children: "b:background" }),
5182
+ /* @__PURE__ */ jsx31(Text32, { dimColor: true, children: "Esc:back" })
5183
+ ] })
5184
+ ] });
5185
+ }
5186
+
5187
+ // src/app.tsx
5188
+ import { jsx as jsx32, jsxs as jsxs31 } from "react/jsx-runtime";
4795
5189
  function AppClassic() {
4796
5190
  const { screen, params, navigate, goBack, goHome } = useNavigation();
4797
5191
  const { exit } = useApp();
@@ -4806,9 +5200,9 @@ function AppClassic() {
4806
5200
  const renderScreen = () => {
4807
5201
  switch (screen) {
4808
5202
  case "home":
4809
- return /* @__PURE__ */ jsx31(Home, { onNavigate: navigate, onExit: handleExit, width, height });
5203
+ return /* @__PURE__ */ jsx32(Home, { onNavigate: navigate, onExit: handleExit, width, height });
4810
5204
  case "command-args":
4811
- return /* @__PURE__ */ jsx31(
5205
+ return /* @__PURE__ */ jsx32(
4812
5206
  CommandArgs,
4813
5207
  {
4814
5208
  command: params.command ?? "",
@@ -4819,9 +5213,9 @@ function AppClassic() {
4819
5213
  }
4820
5214
  );
4821
5215
  case "custom-command":
4822
- return /* @__PURE__ */ jsx31(CustomCommand, { onNavigate: navigate, onBack: goBack, width });
5216
+ return /* @__PURE__ */ jsx32(CustomCommand, { onNavigate: navigate, onBack: goBack, width });
4823
5217
  case "flag-selection":
4824
- return /* @__PURE__ */ jsx31(
5218
+ return /* @__PURE__ */ jsx32(
4825
5219
  FlagSelection,
4826
5220
  {
4827
5221
  args: params.args ?? [],
@@ -4833,11 +5227,12 @@ function AppClassic() {
4833
5227
  );
4834
5228
  case "confirm-execute":
4835
5229
  case "command-execution":
4836
- return /* @__PURE__ */ jsx31(
5230
+ return /* @__PURE__ */ jsx32(
4837
5231
  CommandExecution,
4838
5232
  {
4839
5233
  args: params.args ?? [],
4840
5234
  tool: params.tool,
5235
+ rawCommand: params.rawCommand,
4841
5236
  onBack: goBack,
4842
5237
  onExit: handleExit,
4843
5238
  onRunSuggestion: (sugTool, sugArgs) => {
@@ -4848,23 +5243,23 @@ function AppClassic() {
4848
5243
  }
4849
5244
  );
4850
5245
  case "self-update":
4851
- return /* @__PURE__ */ jsx31(SelfUpdate, { onBack: goBack, onExit: handleExit, width });
5246
+ return /* @__PURE__ */ jsx32(SelfUpdate, { onBack: goBack, onExit: handleExit, width });
4852
5247
  case "tool-status":
4853
- return /* @__PURE__ */ jsx31(ToolStatus, { onBack: goBack, onNavigate: navigate, width });
5248
+ return /* @__PURE__ */ jsx32(ToolStatus, { onBack: goBack, onNavigate: navigate, width });
4854
5249
  case "mcp-manage":
4855
- return /* @__PURE__ */ jsx31(McpManage, { onBack: goBack, width });
5250
+ return /* @__PURE__ */ jsx32(McpManage, { onBack: goBack, width });
4856
5251
  case "process-list":
4857
- return /* @__PURE__ */ jsx31(ProcessList, { onNavigate: navigate, onBack: goBack, width, height });
5252
+ return /* @__PURE__ */ jsx32(ProcessList, { onNavigate: navigate, onBack: goBack, width, height });
4858
5253
  case "process-logs":
4859
- return /* @__PURE__ */ jsx31(ProcessLogs, { processId: params.processId ?? "", onBack: goBack, width, height });
5254
+ return /* @__PURE__ */ jsx32(ProcessLogs, { processId: params.processId ?? "", onBack: goBack, width, height });
4860
5255
  case "project-config":
4861
- return /* @__PURE__ */ jsx31(ProjectConfig, { onBack: goBack, width });
5256
+ return /* @__PURE__ */ jsx32(ProjectConfig, { onBack: goBack, width });
4862
5257
  case "pipeline-list":
4863
- return /* @__PURE__ */ jsx31(PipelineList, { onNavigate: navigate, onBack: goBack, width });
5258
+ return /* @__PURE__ */ jsx32(PipelineList, { onNavigate: navigate, onBack: goBack, width });
4864
5259
  case "pipeline-builder":
4865
- return /* @__PURE__ */ jsx31(PipelineBuilder, { onBack: goBack, width, height });
5260
+ return /* @__PURE__ */ jsx32(PipelineBuilder, { onBack: goBack, width, height });
4866
5261
  case "pipeline-execution":
4867
- return /* @__PURE__ */ jsx31(
5262
+ return /* @__PURE__ */ jsx32(
4868
5263
  PipelineExecution,
4869
5264
  {
4870
5265
  pipelineId: params.pipelineId ?? "",
@@ -4874,32 +5269,34 @@ function AppClassic() {
4874
5269
  }
4875
5270
  );
4876
5271
  case "declarative-plan":
4877
- return /* @__PURE__ */ jsx31(DeclarativePlan, { onBack: goBack, onNavigate: navigate, width, height });
5272
+ return /* @__PURE__ */ jsx32(DeclarativePlan, { onBack: goBack, onNavigate: navigate, width, height });
4878
5273
  case "declarative-status":
4879
- return /* @__PURE__ */ jsx31(DeclarativeStatus, { onBack: goBack, width, height });
5274
+ return /* @__PURE__ */ jsx32(DeclarativeStatus, { onBack: goBack, width, height });
4880
5275
  case "init-scaffold":
4881
- return /* @__PURE__ */ jsx31(InitScaffold, { onBack: goBack, onNavigate: navigate, width, height });
5276
+ return /* @__PURE__ */ jsx32(InitScaffold, { onBack: goBack, onNavigate: navigate, width, height });
5277
+ case "script-picker":
5278
+ return /* @__PURE__ */ jsx32(ScriptPicker, { onNavigate: navigate, onBack: goBack, width, height });
4882
5279
  default:
4883
- return /* @__PURE__ */ jsx31(Box28, { children: /* @__PURE__ */ jsxs30(Text32, { color: "red", children: [
5280
+ return /* @__PURE__ */ jsx32(Box29, { children: /* @__PURE__ */ jsxs31(Text33, { color: "red", children: [
4884
5281
  "Unknown screen: ",
4885
5282
  screen
4886
5283
  ] }) });
4887
5284
  }
4888
5285
  };
4889
- return /* @__PURE__ */ jsxs30(Box28, { flexDirection: "column", children: [
4890
- /* @__PURE__ */ jsx31(GhostBanner, { width }),
5286
+ return /* @__PURE__ */ jsxs31(Box29, { flexDirection: "column", children: [
5287
+ /* @__PURE__ */ jsx32(GhostBanner, { width }),
4891
5288
  renderScreen()
4892
5289
  ] });
4893
5290
  }
4894
5291
 
4895
5292
  // src/appPanel.tsx
4896
- import React26 from "react";
4897
- import { Box as Box37, Text as Text39, useApp as useApp2, useInput as useInput12 } from "ink";
5293
+ import React27 from "react";
5294
+ import { Box as Box38, Text as Text40, useApp as useApp2, useInput as useInput13 } from "ink";
4898
5295
 
4899
5296
  // src/hooks/usePanelNavigation.ts
4900
- import { useState as useState24, useCallback as useCallback8 } from "react";
5297
+ import { useState as useState26, useCallback as useCallback8 } from "react";
4901
5298
  function usePanelNavigation() {
4902
- const [state, setState] = useState24({
5299
+ const [state, setState] = useState26({
4903
5300
  view: "pipelines",
4904
5301
  featureId: "database",
4905
5302
  innerScreen: "home",
@@ -4937,6 +5334,7 @@ function usePanelNavigation() {
4937
5334
  config: "config",
4938
5335
  "self-update": "self-update",
4939
5336
  processes: "processes",
5337
+ scripts: "scripts",
4940
5338
  declarative: "declarative"
4941
5339
  };
4942
5340
  const view = viewMap[itemId];
@@ -5004,9 +5402,9 @@ function usePanelNavigation() {
5004
5402
  }
5005
5403
 
5006
5404
  // src/hooks/usePanelFocus.ts
5007
- import { useState as useState25, useCallback as useCallback9 } from "react";
5405
+ import { useState as useState27, useCallback as useCallback9 } from "react";
5008
5406
  function usePanelFocus() {
5009
- const [focused, setFocused] = useState25("sidebar");
5407
+ const [focused, setFocused] = useState27("sidebar");
5010
5408
  const toggleFocus = useCallback9(() => {
5011
5409
  setFocused((prev) => prev === "sidebar" ? "main" : "sidebar");
5012
5410
  }, []);
@@ -5023,15 +5421,16 @@ function usePanelFocus() {
5023
5421
  }
5024
5422
 
5025
5423
  // src/hooks/useSidebarItems.ts
5026
- import { useMemo as useMemo8 } from "react";
5424
+ import { useMemo as useMemo9 } from "react";
5027
5425
  function useSidebarItems() {
5028
- return useMemo8(() => {
5426
+ return useMemo9(() => {
5029
5427
  const items = [];
5030
5428
  items.push({ id: "__sep_workflows__", label: "---", icon: "", type: "separator", sectionTitle: "Workflows" });
5031
5429
  items.push({ id: "pipelines", label: "Pipelines", icon: "\u{1F517}", type: "action", section: "workflows" });
5032
5430
  items.push({ id: "pinned", label: "Pinned", icon: "\u{1F4CC}", type: "action", section: "workflows" });
5033
5431
  items.push({ id: "custom-command", label: "Custom Cmd", icon: "\u270F\uFE0F", type: "action", section: "workflows" });
5034
5432
  items.push({ id: "processes", label: "Processes", icon: "\u{1F4BB}", type: "action", section: "workflows" });
5433
+ items.push({ id: "scripts", label: "Scripts", icon: "\u{1F4DC}", type: "action", section: "workflows" });
5035
5434
  items.push({ id: "__sep_features__", label: "---", icon: "", type: "separator", sectionTitle: "Features" });
5036
5435
  for (const feature of features) {
5037
5436
  items.push({
@@ -5052,9 +5451,9 @@ function useSidebarItems() {
5052
5451
  }
5053
5452
 
5054
5453
  // src/hooks/useModal.ts
5055
- import { useState as useState26, useCallback as useCallback10 } from "react";
5454
+ import { useState as useState28, useCallback as useCallback10 } from "react";
5056
5455
  function useModal() {
5057
- const [state, setState] = useState26(null);
5456
+ const [state, setState] = useState28(null);
5058
5457
  const openModal = useCallback10((content, title) => {
5059
5458
  setState({ content, title });
5060
5459
  }, []);
@@ -5071,8 +5470,8 @@ function useModal() {
5071
5470
  }
5072
5471
 
5073
5472
  // src/components/PanelLayout.tsx
5074
- import { Box as Box29 } from "ink";
5075
- import { jsx as jsx32, jsxs as jsxs31 } from "react/jsx-runtime";
5473
+ import { Box as Box30 } from "ink";
5474
+ import { jsx as jsx33, jsxs as jsxs32 } from "react/jsx-runtime";
5076
5475
  function PanelLayout({
5077
5476
  header,
5078
5477
  footer,
@@ -5087,19 +5486,19 @@ function PanelLayout({
5087
5486
  const contentHeight = Math.max(5, height - bannerHeight - footerHeight);
5088
5487
  const sidebarWidth = singlePanel ? 0 : panel.sidebarWidth(width);
5089
5488
  const mainWidth = singlePanel ? width : width - sidebarWidth;
5090
- return /* @__PURE__ */ jsxs31(Box29, { flexDirection: "column", width, height, children: [
5489
+ return /* @__PURE__ */ jsxs32(Box30, { flexDirection: "column", width, height, children: [
5091
5490
  header,
5092
- /* @__PURE__ */ jsxs31(Box29, { flexDirection: "row", height: contentHeight, children: [
5093
- !singlePanel && /* @__PURE__ */ jsx32(Box29, { width: sidebarWidth, height: contentHeight, children: sidebar }),
5094
- /* @__PURE__ */ jsx32(Box29, { width: mainWidth, height: contentHeight, children: main2 })
5491
+ /* @__PURE__ */ jsxs32(Box30, { flexDirection: "row", height: contentHeight, children: [
5492
+ !singlePanel && /* @__PURE__ */ jsx33(Box30, { width: sidebarWidth, height: contentHeight, children: sidebar }),
5493
+ /* @__PURE__ */ jsx33(Box30, { width: mainWidth, height: contentHeight, children: main2 })
5095
5494
  ] }),
5096
- /* @__PURE__ */ jsx32(Box29, { height: footerHeight, children: footer })
5495
+ /* @__PURE__ */ jsx33(Box30, { height: footerHeight, children: footer })
5097
5496
  ] });
5098
5497
  }
5099
5498
 
5100
5499
  // src/components/Panel.tsx
5101
- import { Box as Box30, Text as Text33 } from "ink";
5102
- import { jsx as jsx33, jsxs as jsxs32 } from "react/jsx-runtime";
5500
+ import { Box as Box31, Text as Text34 } from "ink";
5501
+ import { jsx as jsx34, jsxs as jsxs33 } from "react/jsx-runtime";
5103
5502
  function Panel({
5104
5503
  id,
5105
5504
  title,
@@ -5109,8 +5508,8 @@ function Panel({
5109
5508
  children
5110
5509
  }) {
5111
5510
  const borderColor = focused ? panel.borderFocused : panel.borderDim;
5112
- return /* @__PURE__ */ jsxs32(
5113
- Box30,
5511
+ return /* @__PURE__ */ jsxs33(
5512
+ Box31,
5114
5513
  {
5115
5514
  flexDirection: "column",
5116
5515
  width,
@@ -5119,21 +5518,21 @@ function Panel({
5119
5518
  borderColor,
5120
5519
  overflow: "hidden",
5121
5520
  children: [
5122
- title && /* @__PURE__ */ jsx33(Box30, { marginBottom: 0, children: /* @__PURE__ */ jsxs32(Text33, { color: focused ? inkColors.accent : void 0, bold: focused, dimColor: !focused, children: [
5521
+ title && /* @__PURE__ */ jsx34(Box31, { marginBottom: 0, children: /* @__PURE__ */ jsxs33(Text34, { color: focused ? inkColors.accent : void 0, bold: focused, dimColor: !focused, children: [
5123
5522
  " ",
5124
5523
  title,
5125
5524
  " "
5126
5525
  ] }) }),
5127
- /* @__PURE__ */ jsx33(Box30, { flexDirection: "column", flexGrow: 1, overflow: "hidden", children })
5526
+ /* @__PURE__ */ jsx34(Box31, { flexDirection: "column", flexGrow: 1, overflow: "hidden", children })
5128
5527
  ]
5129
5528
  }
5130
5529
  );
5131
5530
  }
5132
5531
 
5133
5532
  // src/components/Sidebar.tsx
5134
- import { useEffect as useEffect16, useMemo as useMemo9, useState as useState27 } from "react";
5135
- import { Box as Box31, Text as Text34, useInput as useInput11 } from "ink";
5136
- import { jsx as jsx34, jsxs as jsxs33 } from "react/jsx-runtime";
5533
+ import { useEffect as useEffect19, useMemo as useMemo10, useState as useState29 } from "react";
5534
+ import { Box as Box32, Text as Text35, useInput as useInput12 } from "ink";
5535
+ import { jsx as jsx35, jsxs as jsxs34 } from "react/jsx-runtime";
5137
5536
  function groupSections(items) {
5138
5537
  const groups = [];
5139
5538
  let current = null;
@@ -5155,18 +5554,18 @@ function Sidebar({
5155
5554
  onSelect,
5156
5555
  onHighlight
5157
5556
  }) {
5158
- const selectableItems = useMemo9(
5557
+ const selectableItems = useMemo10(
5159
5558
  () => items.filter((item) => item.type !== "separator"),
5160
5559
  [items]
5161
5560
  );
5162
5561
  const selectedIdx = selectableItems.findIndex((item) => item.id === selectedId);
5163
- const [cursorIdx, setCursorIdx] = useState27(Math.max(0, selectedIdx));
5164
- const sections = useMemo9(() => groupSections(items), [items]);
5165
- useEffect16(() => {
5562
+ const [cursorIdx, setCursorIdx] = useState29(Math.max(0, selectedIdx));
5563
+ const sections = useMemo10(() => groupSections(items), [items]);
5564
+ useEffect19(() => {
5166
5565
  const idx = selectableItems.findIndex((item) => item.id === selectedId);
5167
5566
  if (idx >= 0) setCursorIdx(idx);
5168
5567
  }, [selectedId, selectableItems]);
5169
- useInput11(
5568
+ useInput12(
5170
5569
  (input2, key) => {
5171
5570
  if (key.upArrow || input2 === "k") {
5172
5571
  setCursorIdx((prev) => {
@@ -5194,28 +5593,28 @@ function Sidebar({
5194
5593
  { isActive: isFocused }
5195
5594
  );
5196
5595
  let flatIdx = 0;
5197
- return /* @__PURE__ */ jsx34(Box31, { flexDirection: "column", gap: 0, children: sections.map((section) => {
5596
+ return /* @__PURE__ */ jsx35(Box32, { flexDirection: "column", gap: 0, children: sections.map((section) => {
5198
5597
  const sectionStartIdx = flatIdx;
5199
5598
  const sectionEndIdx = sectionStartIdx + section.items.length - 1;
5200
5599
  const hasCursorInSection = isFocused && cursorIdx >= sectionStartIdx && cursorIdx <= sectionEndIdx;
5201
5600
  const hasActiveInSection = section.items.some((item) => item.id === selectedId);
5202
5601
  const borderColor = hasCursorInSection || hasActiveInSection ? inkColors.accent : "#555555";
5203
- const rendered = /* @__PURE__ */ jsxs33(
5204
- Box31,
5602
+ const rendered = /* @__PURE__ */ jsxs34(
5603
+ Box32,
5205
5604
  {
5206
5605
  flexDirection: "column",
5207
5606
  borderStyle: "round",
5208
5607
  borderColor,
5209
5608
  paddingX: 1,
5210
5609
  children: [
5211
- /* @__PURE__ */ jsx34(Text34, { dimColor: true, bold: true, children: section.title }),
5610
+ /* @__PURE__ */ jsx35(Text35, { dimColor: true, bold: true, children: section.title }),
5212
5611
  section.items.map((item) => {
5213
5612
  const thisIdx = flatIdx;
5214
5613
  flatIdx++;
5215
5614
  const isCursor = isFocused && thisIdx === cursorIdx;
5216
5615
  const isActive = item.id === selectedId;
5217
- return /* @__PURE__ */ jsx34(Box31, { gap: 0, children: /* @__PURE__ */ jsxs33(
5218
- Text34,
5616
+ return /* @__PURE__ */ jsx35(Box32, { gap: 0, children: /* @__PURE__ */ jsxs34(
5617
+ Text35,
5219
5618
  {
5220
5619
  color: isCursor ? inkColors.accent : isActive ? inkColors.accent : void 0,
5221
5620
  bold: isCursor || isActive,
@@ -5238,12 +5637,12 @@ function Sidebar({
5238
5637
  }
5239
5638
 
5240
5639
  // src/components/PanelFooter.tsx
5241
- import { Box as Box32, Text as Text35 } from "ink";
5242
- import { jsx as jsx35, jsxs as jsxs34 } from "react/jsx-runtime";
5640
+ import { Box as Box33, Text as Text36 } from "ink";
5641
+ import { jsx as jsx36, jsxs as jsxs35 } from "react/jsx-runtime";
5243
5642
  function PanelFooter({ hints, width }) {
5244
- return /* @__PURE__ */ jsx35(Box32, { width, children: /* @__PURE__ */ jsx35(Box32, { gap: 1, children: hints.map((hint) => /* @__PURE__ */ jsxs34(Box32, { gap: 0, children: [
5245
- /* @__PURE__ */ jsx35(Text35, { color: inkColors.accent, bold: true, children: hint.key }),
5246
- /* @__PURE__ */ jsxs34(Text35, { dimColor: true, children: [
5643
+ return /* @__PURE__ */ jsx36(Box33, { width, children: /* @__PURE__ */ jsx36(Box33, { gap: 1, children: hints.map((hint) => /* @__PURE__ */ jsxs35(Box33, { gap: 0, children: [
5644
+ /* @__PURE__ */ jsx36(Text36, { color: inkColors.accent, bold: true, children: hint.key }),
5645
+ /* @__PURE__ */ jsxs35(Text36, { dimColor: true, children: [
5247
5646
  ":",
5248
5647
  hint.action
5249
5648
  ] })
@@ -5251,8 +5650,8 @@ function PanelFooter({ hints, width }) {
5251
5650
  }
5252
5651
 
5253
5652
  // src/components/Modal.tsx
5254
- import { Box as Box33, Text as Text36 } from "ink";
5255
- import { jsx as jsx36, jsxs as jsxs35 } from "react/jsx-runtime";
5653
+ import { Box as Box34, Text as Text37 } from "ink";
5654
+ import { jsx as jsx37, jsxs as jsxs36 } from "react/jsx-runtime";
5256
5655
  function Modal({
5257
5656
  title,
5258
5657
  width,
@@ -5263,10 +5662,10 @@ function Modal({
5263
5662
  const modalHeight = Math.min(height - 4, 20);
5264
5663
  const padX = Math.max(0, Math.floor((width - modalWidth) / 2));
5265
5664
  const padY = Math.max(0, Math.floor((height - modalHeight) / 2));
5266
- return /* @__PURE__ */ jsxs35(Box33, { flexDirection: "column", width, height, children: [
5267
- padY > 0 && /* @__PURE__ */ jsx36(Box33, { height: padY }),
5268
- /* @__PURE__ */ jsx36(Box33, { marginLeft: padX, children: /* @__PURE__ */ jsxs35(
5269
- Box33,
5665
+ return /* @__PURE__ */ jsxs36(Box34, { flexDirection: "column", width, height, children: [
5666
+ padY > 0 && /* @__PURE__ */ jsx37(Box34, { height: padY }),
5667
+ /* @__PURE__ */ jsx37(Box34, { marginLeft: padX, children: /* @__PURE__ */ jsxs36(
5668
+ Box34,
5270
5669
  {
5271
5670
  flexDirection: "column",
5272
5671
  width: modalWidth,
@@ -5275,8 +5674,8 @@ function Modal({
5275
5674
  borderColor: inkColors.accent,
5276
5675
  paddingX: 1,
5277
5676
  children: [
5278
- /* @__PURE__ */ jsx36(Box33, { marginBottom: 1, children: /* @__PURE__ */ jsx36(Text36, { color: inkColors.accent, bold: true, children: title }) }),
5279
- /* @__PURE__ */ jsx36(Box33, { flexDirection: "column", flexGrow: 1, overflow: "hidden", children })
5677
+ /* @__PURE__ */ jsx37(Box34, { marginBottom: 1, children: /* @__PURE__ */ jsx37(Text37, { color: inkColors.accent, bold: true, children: title }) }),
5678
+ /* @__PURE__ */ jsx37(Box34, { flexDirection: "column", flexGrow: 1, overflow: "hidden", children })
5280
5679
  ]
5281
5680
  }
5282
5681
  ) })
@@ -5284,9 +5683,9 @@ function Modal({
5284
5683
  }
5285
5684
 
5286
5685
  // src/components/FeatureCommands.tsx
5287
- import { useEffect as useEffect17, useMemo as useMemo10, useState as useState28 } from "react";
5288
- import { Box as Box34, Text as Text37 } from "ink";
5289
- import { jsx as jsx37, jsxs as jsxs36 } from "react/jsx-runtime";
5686
+ import { useEffect as useEffect20, useMemo as useMemo11, useState as useState30 } from "react";
5687
+ import { Box as Box35, Text as Text38 } from "ink";
5688
+ import { jsx as jsx38, jsxs as jsxs37 } from "react/jsx-runtime";
5290
5689
  function FeatureCommands({
5291
5690
  feature,
5292
5691
  onNavigate,
@@ -5297,15 +5696,15 @@ function FeatureCommands({
5297
5696
  height = 24,
5298
5697
  isInputActive = true
5299
5698
  }) {
5300
- const [pinnedCommands, setPinnedCommands2] = useState28(() => getPinnedCommands());
5301
- const [pinnedRuns, setPinnedRuns2] = useState28(() => getPinnedRuns());
5302
- const [pinFeedback, setPinFeedback] = useState28();
5303
- useEffect17(() => {
5699
+ const [pinnedCommands, setPinnedCommands2] = useState30(() => getPinnedCommands());
5700
+ const [pinnedRuns, setPinnedRuns2] = useState30(() => getPinnedRuns());
5701
+ const [pinFeedback, setPinFeedback] = useState30();
5702
+ useEffect20(() => {
5304
5703
  if (!pinFeedback) return;
5305
5704
  const timeout = setTimeout(() => setPinFeedback(void 0), 1400);
5306
5705
  return () => clearTimeout(timeout);
5307
5706
  }, [pinFeedback]);
5308
- const items = useMemo10(
5707
+ const items = useMemo11(
5309
5708
  () => buildHomeItems({
5310
5709
  activeFeature: feature,
5311
5710
  pinnedCommands,
@@ -5315,8 +5714,8 @@ function FeatureCommands({
5315
5714
  }),
5316
5715
  [feature, pinnedCommands, pinnedRuns]
5317
5716
  );
5318
- const pinnedCommandSet = useMemo10(() => new Set(pinnedCommands), [pinnedCommands]);
5319
- const pinnedRunSet = useMemo10(() => new Set(pinnedRuns), [pinnedRuns]);
5717
+ const pinnedCommandSet = useMemo11(() => new Set(pinnedCommands), [pinnedCommands]);
5718
+ const pinnedRunSet = useMemo11(() => new Set(pinnedRuns), [pinnedRuns]);
5320
5719
  const refreshPins = () => {
5321
5720
  setPinnedCommands2(getPinnedCommands());
5322
5721
  setPinnedRuns2(getPinnedRuns());
@@ -5368,12 +5767,12 @@ function FeatureCommands({
5368
5767
  );
5369
5768
  }
5370
5769
  };
5371
- return /* @__PURE__ */ jsxs36(Box34, { flexDirection: "column", paddingX: 1, children: [
5372
- pinFeedback && /* @__PURE__ */ jsx37(Box34, { marginBottom: 1, children: /* @__PURE__ */ jsxs36(Text37, { color: inkColors.accent, children: [
5770
+ return /* @__PURE__ */ jsxs37(Box35, { flexDirection: "column", paddingX: 1, children: [
5771
+ pinFeedback && /* @__PURE__ */ jsx38(Box35, { marginBottom: 1, children: /* @__PURE__ */ jsxs37(Text38, { color: inkColors.accent, children: [
5373
5772
  "\u2713 ",
5374
5773
  pinFeedback
5375
5774
  ] }) }),
5376
- /* @__PURE__ */ jsx37(
5775
+ /* @__PURE__ */ jsx38(
5377
5776
  SelectList,
5378
5777
  {
5379
5778
  items,
@@ -5392,9 +5791,9 @@ function FeatureCommands({
5392
5791
  }
5393
5792
 
5394
5793
  // src/components/PinnedCommands.tsx
5395
- import { useEffect as useEffect18, useMemo as useMemo11, useState as useState29 } from "react";
5396
- import { Box as Box35, Text as Text38 } from "ink";
5397
- import { jsx as jsx38, jsxs as jsxs37 } from "react/jsx-runtime";
5794
+ import { useEffect as useEffect21, useMemo as useMemo12, useState as useState31 } from "react";
5795
+ import { Box as Box36, Text as Text39 } from "ink";
5796
+ import { jsx as jsx39, jsxs as jsxs38 } from "react/jsx-runtime";
5398
5797
  function PinnedCommands({
5399
5798
  onNavigate,
5400
5799
  onBack,
@@ -5403,20 +5802,20 @@ function PinnedCommands({
5403
5802
  height = 24,
5404
5803
  isInputActive = true
5405
5804
  }) {
5406
- const [pinnedCommands, setPinnedCommands2] = useState29(() => getPinnedCommands());
5407
- const [pinnedRuns, setPinnedRuns2] = useState29(() => getPinnedRuns());
5408
- const [pinFeedback, setPinFeedback] = useState29();
5409
- useEffect18(() => {
5805
+ const [pinnedCommands, setPinnedCommands2] = useState31(() => getPinnedCommands());
5806
+ const [pinnedRuns, setPinnedRuns2] = useState31(() => getPinnedRuns());
5807
+ const [pinFeedback, setPinFeedback] = useState31();
5808
+ useEffect21(() => {
5410
5809
  if (!pinFeedback) return;
5411
5810
  const timeout = setTimeout(() => setPinFeedback(void 0), 1400);
5412
5811
  return () => clearTimeout(timeout);
5413
5812
  }, [pinFeedback]);
5414
- const items = useMemo11(
5813
+ const items = useMemo12(
5415
5814
  () => buildPinnedOnlyItems(pinnedCommands, pinnedRuns),
5416
5815
  [pinnedCommands, pinnedRuns]
5417
5816
  );
5418
- const pinnedCommandSet = useMemo11(() => new Set(pinnedCommands), [pinnedCommands]);
5419
- const pinnedRunSet = useMemo11(() => new Set(pinnedRuns), [pinnedRuns]);
5817
+ const pinnedCommandSet = useMemo12(() => new Set(pinnedCommands), [pinnedCommands]);
5818
+ const pinnedRunSet = useMemo12(() => new Set(pinnedRuns), [pinnedRuns]);
5420
5819
  const refreshPins = () => {
5421
5820
  setPinnedCommands2(getPinnedCommands());
5422
5821
  setPinnedRuns2(getPinnedRuns());
@@ -5468,14 +5867,14 @@ function PinnedCommands({
5468
5867
  }
5469
5868
  };
5470
5869
  if (items.length === 0) {
5471
- return /* @__PURE__ */ jsx38(Box35, { flexDirection: "column", paddingX: 1, children: /* @__PURE__ */ jsx38(Text38, { color: "gray", children: "No pinned items. Press p on any command to pin it." }) });
5870
+ return /* @__PURE__ */ jsx39(Box36, { flexDirection: "column", paddingX: 1, children: /* @__PURE__ */ jsx39(Text39, { color: "gray", children: "No pinned items. Press p on any command to pin it." }) });
5472
5871
  }
5473
- return /* @__PURE__ */ jsxs37(Box35, { flexDirection: "column", paddingX: 1, children: [
5474
- pinFeedback && /* @__PURE__ */ jsx38(Box35, { marginBottom: 1, children: /* @__PURE__ */ jsxs37(Text38, { color: inkColors.accent, children: [
5872
+ return /* @__PURE__ */ jsxs38(Box36, { flexDirection: "column", paddingX: 1, children: [
5873
+ pinFeedback && /* @__PURE__ */ jsx39(Box36, { marginBottom: 1, children: /* @__PURE__ */ jsxs38(Text39, { color: inkColors.accent, children: [
5475
5874
  "\u2713 ",
5476
5875
  pinFeedback
5477
5876
  ] }) }),
5478
- /* @__PURE__ */ jsx38(
5877
+ /* @__PURE__ */ jsx39(
5479
5878
  SelectList,
5480
5879
  {
5481
5880
  items,
@@ -5494,9 +5893,9 @@ function PinnedCommands({
5494
5893
  }
5495
5894
 
5496
5895
  // src/screens/DeclarativeHome.tsx
5497
- import { useMemo as useMemo12 } from "react";
5498
- import { Box as Box36 } from "ink";
5499
- import { jsx as jsx39 } from "react/jsx-runtime";
5896
+ import { useMemo as useMemo13 } from "react";
5897
+ import { Box as Box37 } from "ink";
5898
+ import { jsx as jsx40 } from "react/jsx-runtime";
5500
5899
  var ITEMS = [
5501
5900
  { value: "declarative-plan", label: "Plan / Apply", hint: "Diff and apply polter.yaml", kind: "action" },
5502
5901
  { value: "declarative-status", label: "Infrastructure Status", hint: "Live state from CLI tools", kind: "action" },
@@ -5509,11 +5908,11 @@ function DeclarativeHome({
5509
5908
  height = 24,
5510
5909
  isInputActive = true
5511
5910
  }) {
5512
- const handleSelect = useMemo12(
5911
+ const handleSelect = useMemo13(
5513
5912
  () => (value) => onNavigate(value),
5514
5913
  [onNavigate]
5515
5914
  );
5516
- return /* @__PURE__ */ jsx39(Box36, { flexDirection: "column", paddingX: 1, children: /* @__PURE__ */ jsx39(
5915
+ return /* @__PURE__ */ jsx40(Box37, { flexDirection: "column", paddingX: 1, children: /* @__PURE__ */ jsx40(
5517
5916
  SelectList,
5518
5917
  {
5519
5918
  items: ITEMS,
@@ -5529,7 +5928,7 @@ function DeclarativeHome({
5529
5928
  }
5530
5929
 
5531
5930
  // src/appPanel.tsx
5532
- import { jsx as jsx40, jsxs as jsxs38 } from "react/jsx-runtime";
5931
+ import { jsx as jsx41, jsxs as jsxs39 } from "react/jsx-runtime";
5533
5932
  var screenLabels = {
5534
5933
  "command-args": "Args",
5535
5934
  "flag-selection": "Flags",
@@ -5547,7 +5946,8 @@ var screenLabels = {
5547
5946
  "process-logs": "Logs",
5548
5947
  "declarative-plan": "Plan/Apply",
5549
5948
  "declarative-status": "Status",
5550
- "init-scaffold": "Init"
5949
+ "init-scaffold": "Init",
5950
+ "script-picker": "Scripts"
5551
5951
  };
5552
5952
  function buildBreadcrumb(nav) {
5553
5953
  let base;
@@ -5578,6 +5978,9 @@ function buildBreadcrumb(nav) {
5578
5978
  case "processes":
5579
5979
  base = "\u{1F4BB} Processes";
5580
5980
  break;
5981
+ case "scripts":
5982
+ base = "\u{1F4DC} Scripts";
5983
+ break;
5581
5984
  case "declarative":
5582
5985
  base = "\u{1F3D7}\uFE0F Infrastructure";
5583
5986
  break;
@@ -5613,7 +6016,7 @@ function AppPanel() {
5613
6016
  const focus = usePanelFocus();
5614
6017
  const sidebarItems = useSidebarItems();
5615
6018
  const modal = useModal();
5616
- const refreshPins = React26.useCallback(() => {
6019
+ const refreshPins = React27.useCallback(() => {
5617
6020
  }, []);
5618
6021
  const singlePanel = width < 60 || height < 15;
5619
6022
  const handleExit = () => {
@@ -5622,7 +6025,7 @@ function AppPanel() {
5622
6025
  );
5623
6026
  exit();
5624
6027
  };
5625
- useInput12((input2, key) => {
6028
+ useInput13((input2, key) => {
5626
6029
  if (modal.isOpen) {
5627
6030
  if (key.escape || input2 === "q") {
5628
6031
  modal.closeModal();
@@ -5647,37 +6050,37 @@ function AppPanel() {
5647
6050
  }
5648
6051
  if (input2 === "?") {
5649
6052
  modal.openModal(
5650
- /* @__PURE__ */ jsxs38(Box37, { flexDirection: "column", children: [
5651
- /* @__PURE__ */ jsxs38(Text39, { children: [
5652
- /* @__PURE__ */ jsx40(Text39, { bold: true, children: "\u2190/\u2192" }),
6053
+ /* @__PURE__ */ jsxs39(Box38, { flexDirection: "column", children: [
6054
+ /* @__PURE__ */ jsxs39(Text40, { children: [
6055
+ /* @__PURE__ */ jsx41(Text40, { bold: true, children: "\u2190/\u2192" }),
5653
6056
  " Move between sidebar and main panel"
5654
6057
  ] }),
5655
- /* @__PURE__ */ jsxs38(Text39, { children: [
5656
- /* @__PURE__ */ jsx40(Text39, { bold: true, children: "Tab" }),
6058
+ /* @__PURE__ */ jsxs39(Text40, { children: [
6059
+ /* @__PURE__ */ jsx41(Text40, { bold: true, children: "Tab" }),
5657
6060
  " Toggle sidebar / main panel"
5658
6061
  ] }),
5659
- /* @__PURE__ */ jsxs38(Text39, { children: [
5660
- /* @__PURE__ */ jsx40(Text39, { bold: true, children: "j/k" }),
6062
+ /* @__PURE__ */ jsxs39(Text40, { children: [
6063
+ /* @__PURE__ */ jsx41(Text40, { bold: true, children: "j/k" }),
5661
6064
  " Navigate up/down"
5662
6065
  ] }),
5663
- /* @__PURE__ */ jsxs38(Text39, { children: [
5664
- /* @__PURE__ */ jsx40(Text39, { bold: true, children: "Enter" }),
6066
+ /* @__PURE__ */ jsxs39(Text40, { children: [
6067
+ /* @__PURE__ */ jsx41(Text40, { bold: true, children: "Enter" }),
5665
6068
  " Select item"
5666
6069
  ] }),
5667
- /* @__PURE__ */ jsxs38(Text39, { children: [
5668
- /* @__PURE__ */ jsx40(Text39, { bold: true, children: "Esc" }),
6070
+ /* @__PURE__ */ jsxs39(Text40, { children: [
6071
+ /* @__PURE__ */ jsx41(Text40, { bold: true, children: "Esc" }),
5669
6072
  " Go back (or return to sidebar)"
5670
6073
  ] }),
5671
- /* @__PURE__ */ jsxs38(Text39, { children: [
5672
- /* @__PURE__ */ jsx40(Text39, { bold: true, children: "q" }),
6074
+ /* @__PURE__ */ jsxs39(Text40, { children: [
6075
+ /* @__PURE__ */ jsx41(Text40, { bold: true, children: "q" }),
5673
6076
  " Quit Polter"
5674
6077
  ] }),
5675
- /* @__PURE__ */ jsxs38(Text39, { children: [
5676
- /* @__PURE__ */ jsx40(Text39, { bold: true, children: "?" }),
6078
+ /* @__PURE__ */ jsxs39(Text40, { children: [
6079
+ /* @__PURE__ */ jsx41(Text40, { bold: true, children: "?" }),
5677
6080
  " Show this help"
5678
6081
  ] }),
5679
- /* @__PURE__ */ jsxs38(Text39, { children: [
5680
- /* @__PURE__ */ jsx40(Text39, { bold: true, children: "p" }),
6082
+ /* @__PURE__ */ jsxs39(Text40, { children: [
6083
+ /* @__PURE__ */ jsx41(Text40, { bold: true, children: "p" }),
5681
6084
  " Pin/unpin command"
5682
6085
  ] })
5683
6086
  ] }),
@@ -5703,6 +6106,8 @@ function AppPanel() {
5703
6106
  return "self-update";
5704
6107
  case "processes":
5705
6108
  return "processes";
6109
+ case "scripts":
6110
+ return "scripts";
5706
6111
  case "declarative":
5707
6112
  return "declarative";
5708
6113
  default:
@@ -5731,7 +6136,7 @@ function AppPanel() {
5731
6136
  }
5732
6137
  switch (nav.view) {
5733
6138
  case "pinned":
5734
- return /* @__PURE__ */ jsx40(
6139
+ return /* @__PURE__ */ jsx41(
5735
6140
  PinnedCommands,
5736
6141
  {
5737
6142
  onNavigate: nav.navigateInner,
@@ -5745,12 +6150,12 @@ function AppPanel() {
5745
6150
  case "feature": {
5746
6151
  const feature = getFeatureById(nav.featureId);
5747
6152
  if (!feature) {
5748
- return /* @__PURE__ */ jsxs38(Text39, { color: "red", children: [
6153
+ return /* @__PURE__ */ jsxs39(Text40, { color: "red", children: [
5749
6154
  "Feature not found: ",
5750
6155
  nav.featureId
5751
6156
  ] });
5752
6157
  }
5753
- return /* @__PURE__ */ jsx40(
6158
+ return /* @__PURE__ */ jsx41(
5754
6159
  FeatureCommands,
5755
6160
  {
5756
6161
  feature,
@@ -5765,7 +6170,7 @@ function AppPanel() {
5765
6170
  );
5766
6171
  }
5767
6172
  case "custom-command":
5768
- return /* @__PURE__ */ jsx40(
6173
+ return /* @__PURE__ */ jsx41(
5769
6174
  CustomCommand,
5770
6175
  {
5771
6176
  onNavigate: nav.navigateInner,
@@ -5777,7 +6182,7 @@ function AppPanel() {
5777
6182
  }
5778
6183
  );
5779
6184
  case "pipelines":
5780
- return /* @__PURE__ */ jsx40(
6185
+ return /* @__PURE__ */ jsx41(
5781
6186
  PipelineList,
5782
6187
  {
5783
6188
  onNavigate: nav.navigateInner,
@@ -5789,7 +6194,7 @@ function AppPanel() {
5789
6194
  }
5790
6195
  );
5791
6196
  case "tool-status":
5792
- return /* @__PURE__ */ jsx40(
6197
+ return /* @__PURE__ */ jsx41(
5793
6198
  ToolStatus,
5794
6199
  {
5795
6200
  onBack: focus.focusSidebar,
@@ -5801,7 +6206,7 @@ function AppPanel() {
5801
6206
  }
5802
6207
  );
5803
6208
  case "processes":
5804
- return /* @__PURE__ */ jsx40(
6209
+ return /* @__PURE__ */ jsx41(
5805
6210
  ProcessList,
5806
6211
  {
5807
6212
  onNavigate: nav.navigateInner,
@@ -5812,8 +6217,20 @@ function AppPanel() {
5812
6217
  isInputActive: focus.isMainFocused
5813
6218
  }
5814
6219
  );
6220
+ case "scripts":
6221
+ return /* @__PURE__ */ jsx41(
6222
+ ScriptPicker,
6223
+ {
6224
+ onNavigate: nav.navigateInner,
6225
+ onBack: focus.focusSidebar,
6226
+ width: mainContentWidth - 2,
6227
+ height: mainContentHeight,
6228
+ panelMode: true,
6229
+ isInputActive: focus.isMainFocused
6230
+ }
6231
+ );
5815
6232
  case "declarative":
5816
- return /* @__PURE__ */ jsx40(
6233
+ return /* @__PURE__ */ jsx41(
5817
6234
  DeclarativeHome,
5818
6235
  {
5819
6236
  onNavigate: nav.navigateInner,
@@ -5824,7 +6241,7 @@ function AppPanel() {
5824
6241
  }
5825
6242
  );
5826
6243
  case "config":
5827
- return /* @__PURE__ */ jsx40(
6244
+ return /* @__PURE__ */ jsx41(
5828
6245
  ProjectConfig,
5829
6246
  {
5830
6247
  onBack: focus.focusSidebar,
@@ -5835,7 +6252,7 @@ function AppPanel() {
5835
6252
  }
5836
6253
  );
5837
6254
  case "self-update":
5838
- return /* @__PURE__ */ jsx40(
6255
+ return /* @__PURE__ */ jsx41(
5839
6256
  SelfUpdate,
5840
6257
  {
5841
6258
  onBack: focus.focusSidebar,
@@ -5847,7 +6264,7 @@ function AppPanel() {
5847
6264
  }
5848
6265
  );
5849
6266
  default:
5850
- return /* @__PURE__ */ jsx40(Text39, { children: "Select an item from the sidebar" });
6267
+ return /* @__PURE__ */ jsx41(Text40, { children: "Select an item from the sidebar" });
5851
6268
  }
5852
6269
  };
5853
6270
  const renderInnerScreen = () => {
@@ -5855,7 +6272,7 @@ function AppPanel() {
5855
6272
  const w = mainContentWidth - 2;
5856
6273
  switch (nav.innerScreen) {
5857
6274
  case "command-args":
5858
- return /* @__PURE__ */ jsx40(
6275
+ return /* @__PURE__ */ jsx41(
5859
6276
  CommandArgs,
5860
6277
  {
5861
6278
  command: nav.innerParams.command ?? "",
@@ -5868,7 +6285,7 @@ function AppPanel() {
5868
6285
  }
5869
6286
  );
5870
6287
  case "custom-command":
5871
- return /* @__PURE__ */ jsx40(
6288
+ return /* @__PURE__ */ jsx41(
5872
6289
  CustomCommand,
5873
6290
  {
5874
6291
  onNavigate: nav.navigateInner,
@@ -5880,7 +6297,7 @@ function AppPanel() {
5880
6297
  }
5881
6298
  );
5882
6299
  case "flag-selection":
5883
- return /* @__PURE__ */ jsx40(
6300
+ return /* @__PURE__ */ jsx41(
5884
6301
  FlagSelection,
5885
6302
  {
5886
6303
  args: nav.innerParams.args ?? [],
@@ -5896,11 +6313,12 @@ function AppPanel() {
5896
6313
  );
5897
6314
  case "confirm-execute":
5898
6315
  case "command-execution":
5899
- return /* @__PURE__ */ jsx40(
6316
+ return /* @__PURE__ */ jsx41(
5900
6317
  CommandExecution,
5901
6318
  {
5902
6319
  args: nav.innerParams.args ?? [],
5903
6320
  tool: nav.innerParams.tool,
6321
+ rawCommand: nav.innerParams.rawCommand,
5904
6322
  interactive: nav.innerParams.interactive,
5905
6323
  onBack: nav.goBackInner,
5906
6324
  onHome: nav.goHomeInner,
@@ -5916,7 +6334,7 @@ function AppPanel() {
5916
6334
  `${nav.view}-${nav.innerParams.tool}-${(nav.innerParams.args ?? []).join("-")}`
5917
6335
  );
5918
6336
  case "pipeline-list":
5919
- return /* @__PURE__ */ jsx40(
6337
+ return /* @__PURE__ */ jsx41(
5920
6338
  PipelineList,
5921
6339
  {
5922
6340
  onNavigate: nav.navigateInner,
@@ -5928,7 +6346,7 @@ function AppPanel() {
5928
6346
  }
5929
6347
  );
5930
6348
  case "pipeline-builder":
5931
- return /* @__PURE__ */ jsx40(
6349
+ return /* @__PURE__ */ jsx41(
5932
6350
  PipelineBuilder,
5933
6351
  {
5934
6352
  onBack: nav.goBackInner,
@@ -5939,7 +6357,7 @@ function AppPanel() {
5939
6357
  }
5940
6358
  );
5941
6359
  case "pipeline-execution":
5942
- return /* @__PURE__ */ jsx40(
6360
+ return /* @__PURE__ */ jsx41(
5943
6361
  PipelineExecution,
5944
6362
  {
5945
6363
  pipelineId: nav.innerParams.pipelineId ?? "",
@@ -5951,7 +6369,7 @@ function AppPanel() {
5951
6369
  }
5952
6370
  );
5953
6371
  case "self-update":
5954
- return /* @__PURE__ */ jsx40(
6372
+ return /* @__PURE__ */ jsx41(
5955
6373
  SelfUpdate,
5956
6374
  {
5957
6375
  onBack: nav.goBackInner,
@@ -5963,7 +6381,7 @@ function AppPanel() {
5963
6381
  }
5964
6382
  );
5965
6383
  case "tool-status":
5966
- return /* @__PURE__ */ jsx40(
6384
+ return /* @__PURE__ */ jsx41(
5967
6385
  ToolStatus,
5968
6386
  {
5969
6387
  onBack: nav.goBackInner,
@@ -5975,7 +6393,7 @@ function AppPanel() {
5975
6393
  }
5976
6394
  );
5977
6395
  case "mcp-manage":
5978
- return /* @__PURE__ */ jsx40(
6396
+ return /* @__PURE__ */ jsx41(
5979
6397
  McpManage,
5980
6398
  {
5981
6399
  onBack: nav.goBackInner,
@@ -5986,7 +6404,7 @@ function AppPanel() {
5986
6404
  }
5987
6405
  );
5988
6406
  case "process-list":
5989
- return /* @__PURE__ */ jsx40(
6407
+ return /* @__PURE__ */ jsx41(
5990
6408
  ProcessList,
5991
6409
  {
5992
6410
  onNavigate: nav.navigateInner,
@@ -5998,7 +6416,7 @@ function AppPanel() {
5998
6416
  }
5999
6417
  );
6000
6418
  case "process-logs":
6001
- return /* @__PURE__ */ jsx40(
6419
+ return /* @__PURE__ */ jsx41(
6002
6420
  ProcessLogs,
6003
6421
  {
6004
6422
  processId: nav.innerParams.processId ?? "",
@@ -6010,7 +6428,7 @@ function AppPanel() {
6010
6428
  }
6011
6429
  );
6012
6430
  case "project-config":
6013
- return /* @__PURE__ */ jsx40(
6431
+ return /* @__PURE__ */ jsx41(
6014
6432
  ProjectConfig,
6015
6433
  {
6016
6434
  onBack: nav.goBackInner,
@@ -6021,7 +6439,7 @@ function AppPanel() {
6021
6439
  }
6022
6440
  );
6023
6441
  case "declarative-plan":
6024
- return /* @__PURE__ */ jsx40(
6442
+ return /* @__PURE__ */ jsx41(
6025
6443
  DeclarativePlan,
6026
6444
  {
6027
6445
  onBack: nav.goBackInner,
@@ -6033,7 +6451,7 @@ function AppPanel() {
6033
6451
  }
6034
6452
  );
6035
6453
  case "declarative-status":
6036
- return /* @__PURE__ */ jsx40(
6454
+ return /* @__PURE__ */ jsx41(
6037
6455
  DeclarativeStatus,
6038
6456
  {
6039
6457
  onBack: nav.goBackInner,
@@ -6044,7 +6462,7 @@ function AppPanel() {
6044
6462
  }
6045
6463
  );
6046
6464
  case "init-scaffold":
6047
- return /* @__PURE__ */ jsx40(
6465
+ return /* @__PURE__ */ jsx41(
6048
6466
  InitScaffold,
6049
6467
  {
6050
6468
  onBack: nav.goBackInner,
@@ -6055,18 +6473,30 @@ function AppPanel() {
6055
6473
  isInputActive: isActive
6056
6474
  }
6057
6475
  );
6476
+ case "script-picker":
6477
+ return /* @__PURE__ */ jsx41(
6478
+ ScriptPicker,
6479
+ {
6480
+ onNavigate: nav.navigateInner,
6481
+ onBack: nav.goBackInner,
6482
+ width: w,
6483
+ height: mainContentHeight,
6484
+ panelMode: true,
6485
+ isInputActive: isActive
6486
+ }
6487
+ );
6058
6488
  default:
6059
- return /* @__PURE__ */ jsxs38(Text39, { color: "red", children: [
6489
+ return /* @__PURE__ */ jsxs39(Text40, { color: "red", children: [
6060
6490
  "Unknown screen: ",
6061
6491
  nav.innerScreen
6062
6492
  ] });
6063
6493
  }
6064
6494
  };
6065
6495
  if (modal.isOpen) {
6066
- return /* @__PURE__ */ jsx40(Box37, { flexDirection: "column", width, height, children: /* @__PURE__ */ jsx40(Modal, { title: modal.modalTitle, width, height, children: modal.modalContent }) });
6496
+ return /* @__PURE__ */ jsx41(Box38, { flexDirection: "column", width, height, children: /* @__PURE__ */ jsx41(Modal, { title: modal.modalTitle, width, height, children: modal.modalContent }) });
6067
6497
  }
6068
- const header = /* @__PURE__ */ jsx40(GhostBanner, { width, compact: true });
6069
- const sidebar = /* @__PURE__ */ jsx40(
6498
+ const header = /* @__PURE__ */ jsx41(GhostBanner, { width, compact: true });
6499
+ const sidebar = /* @__PURE__ */ jsx41(
6070
6500
  Panel,
6071
6501
  {
6072
6502
  id: "sidebar",
@@ -6074,7 +6504,7 @@ function AppPanel() {
6074
6504
  width: Math.max(20, Math.min(35, Math.floor(width * 0.3))),
6075
6505
  height: Math.max(5, height - bannerHeight - 1),
6076
6506
  focused: focus.isSidebarFocused,
6077
- children: /* @__PURE__ */ jsx40(
6507
+ children: /* @__PURE__ */ jsx41(
6078
6508
  Sidebar,
6079
6509
  {
6080
6510
  items: sidebarItems,
@@ -6087,7 +6517,7 @@ function AppPanel() {
6087
6517
  )
6088
6518
  }
6089
6519
  );
6090
- const main2 = /* @__PURE__ */ jsx40(
6520
+ const main2 = /* @__PURE__ */ jsx41(
6091
6521
  Panel,
6092
6522
  {
6093
6523
  id: "main",
@@ -6098,8 +6528,8 @@ function AppPanel() {
6098
6528
  children: renderMainContent()
6099
6529
  }
6100
6530
  );
6101
- const footer = /* @__PURE__ */ jsx40(PanelFooter, { hints: footerHints, width });
6102
- return /* @__PURE__ */ jsx40(
6531
+ const footer = /* @__PURE__ */ jsx41(PanelFooter, { hints: footerHints, width });
6532
+ return /* @__PURE__ */ jsx41(
6103
6533
  PanelLayout,
6104
6534
  {
6105
6535
  header,
@@ -6263,19 +6693,19 @@ function printCliHelp() {
6263
6693
  import pc3 from "picocolors";
6264
6694
 
6265
6695
  // src/apps/ops.ts
6266
- import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync as writeFileSync2 } from "fs";
6696
+ import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, rmSync, writeFileSync as writeFileSync2 } from "fs";
6267
6697
  import { mkdtemp, readdir, stat } from "fs/promises";
6268
- import { dirname, join as join3, resolve } from "path";
6698
+ import { dirname, join as join4, resolve } from "path";
6269
6699
  import { tmpdir } from "os";
6270
6700
  import pc2 from "picocolors";
6271
6701
 
6272
6702
  // src/apps/bootstrapPaths.ts
6273
6703
  import { homedir as homedir2 } from "os";
6274
- import { join as join2 } from "path";
6704
+ import { join as join3 } from "path";
6275
6705
  function getOpsBootstrapPayloadPath() {
6276
6706
  const home = homedir2();
6277
6707
  if (process.platform === "darwin") {
6278
- return join2(
6708
+ return join3(
6279
6709
  home,
6280
6710
  "Library",
6281
6711
  "Application Support",
@@ -6285,10 +6715,10 @@ function getOpsBootstrapPayloadPath() {
6285
6715
  );
6286
6716
  }
6287
6717
  if (process.platform === "win32") {
6288
- const appData = process.env.APPDATA ?? join2(home, "AppData", "Roaming");
6289
- return join2(appData, "ops", "bootstrap", "supabase.json");
6718
+ const appData = process.env.APPDATA ?? join3(home, "AppData", "Roaming");
6719
+ return join3(appData, "ops", "bootstrap", "supabase.json");
6290
6720
  }
6291
- return join2(home, ".config", "ops", "bootstrap", "supabase.json");
6721
+ return join3(home, ".config", "ops", "bootstrap", "supabase.json");
6292
6722
  }
6293
6723
 
6294
6724
  // src/apps/opsRelease.ts
@@ -6560,9 +6990,9 @@ async function promptSelect(label, options, defaultValue) {
6560
6990
  }
6561
6991
 
6562
6992
  // src/apps/ops.ts
6563
- var LINK_REF_FILE = join3("supabase", ".temp", "project-ref");
6993
+ var LINK_REF_FILE = join4("supabase", ".temp", "project-ref");
6564
6994
  function isOpsProjectRoot(candidate) {
6565
- return existsSync(join3(candidate, "src-tauri", "tauri.conf.json")) && existsSync(join3(candidate, "supabase", "migrations")) && existsSync(join3(candidate, "package.json"));
6995
+ return existsSync2(join4(candidate, "src-tauri", "tauri.conf.json")) && existsSync2(join4(candidate, "supabase", "migrations")) && existsSync2(join4(candidate, "package.json"));
6566
6996
  }
6567
6997
  function findNearestOpsRoot(startDir) {
6568
6998
  let currentDir = resolve(startDir);
@@ -6570,7 +7000,7 @@ function findNearestOpsRoot(startDir) {
6570
7000
  if (isOpsProjectRoot(currentDir)) {
6571
7001
  return currentDir;
6572
7002
  }
6573
- const siblingCandidate = join3(currentDir, "ops");
7003
+ const siblingCandidate = join4(currentDir, "ops");
6574
7004
  if (isOpsProjectRoot(siblingCandidate)) {
6575
7005
  return siblingCandidate;
6576
7006
  }
@@ -6582,10 +7012,10 @@ function findNearestOpsRoot(startDir) {
6582
7012
  }
6583
7013
  }
6584
7014
  function readEnvFile(envPath) {
6585
- if (!existsSync(envPath)) {
7015
+ if (!existsSync2(envPath)) {
6586
7016
  return {};
6587
7017
  }
6588
- const content = readFileSync(envPath, "utf-8");
7018
+ const content = readFileSync2(envPath, "utf-8");
6589
7019
  const entries = {};
6590
7020
  for (const line of content.split("\n")) {
6591
7021
  const trimmed = line.trim();
@@ -6616,11 +7046,11 @@ function assertProjectRoot(projectRoot) {
6616
7046
  return projectRoot;
6617
7047
  }
6618
7048
  function getLinkedProjectRef(projectRoot) {
6619
- const refPath = join3(projectRoot, LINK_REF_FILE);
6620
- if (!existsSync(refPath)) {
7049
+ const refPath = join4(projectRoot, LINK_REF_FILE);
7050
+ if (!existsSync2(refPath)) {
6621
7051
  return null;
6622
7052
  }
6623
- const value = readFileSync(refPath, "utf-8").trim();
7053
+ const value = readFileSync2(refPath, "utf-8").trim();
6624
7054
  return value || null;
6625
7055
  }
6626
7056
  function getDbPasswordArgs() {
@@ -6674,7 +7104,7 @@ async function ensureSupabaseLink(projectRoot, forceRelink = false) {
6674
7104
  );
6675
7105
  }
6676
7106
  async function collectSupabaseConfig(projectRoot) {
6677
- const envPath = projectRoot ? join3(projectRoot, ".env.local") : void 0;
7107
+ const envPath = projectRoot ? join4(projectRoot, ".env.local") : void 0;
6678
7108
  const currentEnv = envPath ? readEnvFile(envPath) : {};
6679
7109
  const currentRef = projectRoot ? getLinkedProjectRef(projectRoot) : null;
6680
7110
  const url = await promptText("Supabase URL", {
@@ -6696,7 +7126,7 @@ async function collectSupabaseConfig(projectRoot) {
6696
7126
  };
6697
7127
  }
6698
7128
  function writeOpsEnv(projectRoot, config2) {
6699
- const envPath = join3(projectRoot, ".env.local");
7129
+ const envPath = join4(projectRoot, ".env.local");
6700
7130
  const currentEnv = readEnvFile(envPath);
6701
7131
  const nextEnv = {
6702
7132
  ...currentEnv,
@@ -6896,7 +7326,7 @@ async function extractArchive(archivePath, outputDir) {
6896
7326
  async function findFirstAppBundle(dir) {
6897
7327
  const entries = await readdir(dir);
6898
7328
  for (const entry of entries) {
6899
- const fullPath = join3(dir, entry);
7329
+ const fullPath = join4(dir, entry);
6900
7330
  const entryStat = await stat(fullPath);
6901
7331
  if (entryStat.isDirectory() && entry.endsWith(".app")) {
6902
7332
  return fullPath;
@@ -6912,9 +7342,9 @@ async function findFirstAppBundle(dir) {
6912
7342
  }
6913
7343
  async function deployMacosApp(context, options) {
6914
7344
  const artifact = await resolveOpsMacosArtifact(context.options);
6915
- const tempRoot = await mkdtemp(join3(tmpdir(), "polter-ops-"));
6916
- const archivePath = join3(tempRoot, artifact.fileName);
6917
- const extractDir = join3(tempRoot, "extract");
7345
+ const tempRoot = await mkdtemp(join4(tmpdir(), "polter-ops-"));
7346
+ const archivePath = join4(tempRoot, artifact.fileName);
7347
+ const extractDir = join4(tempRoot, "extract");
6918
7348
  mkdirSync(extractDir, { recursive: true });
6919
7349
  if (artifact.source === "github-release") {
6920
7350
  const releaseLabel = artifact.tagName ?? "latest";
@@ -6943,13 +7373,13 @@ async function deployMacosApp(context, options) {
6943
7373
  }
6944
7374
  const installDir = context.options.installDir ?? "/Applications";
6945
7375
  mkdirSync(installDir, { recursive: true });
6946
- const destination = join3(installDir, "ops.app");
6947
- if (options.requireExistingInstallation && !existsSync(destination)) {
7376
+ const destination = join4(installDir, "ops.app");
7377
+ if (options.requireExistingInstallation && !existsSync2(destination)) {
6948
7378
  throw new Error(
6949
7379
  `No existing Ops installation was found at ${destination}. Run \`polter app install ops\` first.`
6950
7380
  );
6951
7381
  }
6952
- if (existsSync(destination)) {
7382
+ if (existsSync2(destination)) {
6953
7383
  const confirmed = context.options.yes || await promptConfirm(`Replace existing installation at ${destination}?`, false);
6954
7384
  if (!confirmed) {
6955
7385
  process.stdout.write(`${pc2.yellow("Cancelled.")}
@@ -7213,11 +7643,22 @@ Apply completed with ${errors.length} error(s).
7213
7643
  process.exit(result.exitCode ?? 0);
7214
7644
  }
7215
7645
  const AppComponent2 = parsed.classic ? AppClassic : AppPanel;
7216
- render(React27.createElement(AppComponent2));
7217
- return;
7646
+ const socketPath2 = getSocketPath();
7647
+ const ipc2 = socketPath2 ? createIpcServer(socketPath2) : null;
7648
+ if (ipc2) await ipc2.start();
7649
+ const inst = render(React28.createElement(AppComponent2));
7650
+ await inst.waitUntilExit();
7651
+ if (ipc2) await ipc2.stop();
7652
+ process.exit(0);
7218
7653
  }
7219
7654
  const AppComponent = parsed.classic ? AppClassic : AppPanel;
7220
- render(React27.createElement(AppComponent));
7655
+ const socketPath = getSocketPath();
7656
+ const ipc = socketPath ? createIpcServer(socketPath) : null;
7657
+ if (ipc) await ipc.start();
7658
+ const instance = render(React28.createElement(AppComponent));
7659
+ await instance.waitUntilExit();
7660
+ if (ipc) await ipc.stop();
7661
+ process.exit(0);
7221
7662
  }
7222
7663
  main().catch((error) => {
7223
7664
  const message = error instanceof Error ? error.message : String(error);