@polterware/polter 0.4.0 → 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,
@@ -21,6 +22,7 @@ import {
21
22
  getOrCreateProjectConfig,
22
23
  getProcessOutput,
23
24
  getProjectConfigPath,
25
+ getSocketPath,
24
26
  getToolDisplayName,
25
27
  getToolInfo,
26
28
  getToolLinkInfo,
@@ -42,7 +44,7 @@ import {
42
44
  stopProcess,
43
45
  translateCommand,
44
46
  writeProjectConfig
45
- } from "./chunk-YNOZDU75.js";
47
+ } from "./chunk-ZHVOYB5M.js";
46
48
 
47
49
  // src/index.tsx
48
50
  import React28 from "react";
@@ -1659,26 +1661,11 @@ function FlagSelection({
1659
1661
 
1660
1662
  // src/screens/CommandExecution.tsx
1661
1663
  import { useState as useState12, useEffect as useEffect9 } from "react";
1662
- import { Box as Box13, Text as Text17, useInput as useInput8 } from "ink";
1663
-
1664
- // src/components/Spinner.tsx
1665
- import { Text as Text12 } from "ink";
1666
- import InkSpinner from "ink-spinner";
1667
- import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
1668
- function Spinner({
1669
- label = "Running...",
1670
- color = inkColors.accent
1671
- }) {
1672
- return /* @__PURE__ */ jsxs11(Text12, { children: [
1673
- /* @__PURE__ */ jsx12(Text12, { color, children: /* @__PURE__ */ jsx12(InkSpinner, { type: "dots" }) }),
1674
- " ",
1675
- /* @__PURE__ */ jsx12(Text12, { children: label })
1676
- ] });
1677
- }
1664
+ import { Box as Box13, Text as Text15, useInput as useInput8 } from "ink";
1678
1665
 
1679
1666
  // src/components/ConfirmPrompt.tsx
1680
- import { Box as Box11, Text as Text13, useInput as useInput6 } from "ink";
1681
- 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";
1682
1669
  function ConfirmPrompt({
1683
1670
  message,
1684
1671
  defaultValue = true,
@@ -1704,16 +1691,16 @@ function ConfirmPrompt({
1704
1691
  onConfirm(defaultValue);
1705
1692
  }
1706
1693
  }, { isActive: isInputActive });
1707
- return /* @__PURE__ */ jsxs12(Box11, { gap: 1, children: [
1708
- /* @__PURE__ */ jsx13(Text13, { color: inkColors.accent, bold: true, children: "?" }),
1709
- /* @__PURE__ */ jsx13(Text13, { children: message }),
1710
- /* @__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)" })
1711
1698
  ] });
1712
1699
  }
1713
1700
 
1714
1701
  // src/components/Divider.tsx
1715
- import { Text as Text14 } from "ink";
1716
- 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";
1717
1704
  function Divider({
1718
1705
  label,
1719
1706
  width
@@ -1724,7 +1711,7 @@ function Divider({
1724
1711
  const sideLen = Math.max(2, Math.floor((effectiveWidth - labelLen) / 2));
1725
1712
  const left = "\u2500".repeat(sideLen);
1726
1713
  const right = "\u2500".repeat(sideLen);
1727
- return /* @__PURE__ */ jsxs13(Text14, { dimColor: true, children: [
1714
+ return /* @__PURE__ */ jsxs12(Text13, { dimColor: true, children: [
1728
1715
  left,
1729
1716
  " ",
1730
1717
  label,
@@ -1732,16 +1719,13 @@ function Divider({
1732
1719
  right
1733
1720
  ] });
1734
1721
  }
1735
- return /* @__PURE__ */ jsx14(Text14, { dimColor: true, children: "\u2500".repeat(effectiveWidth) });
1722
+ return /* @__PURE__ */ jsx13(Text13, { dimColor: true, children: "\u2500".repeat(effectiveWidth) });
1736
1723
  }
1737
1724
 
1738
- // src/components/CommandOutput.tsx
1739
- import { Text as Text16 } from "ink";
1740
-
1741
1725
  // src/components/ScrollableBox.tsx
1742
1726
  import { useState as useState10, useEffect as useEffect6, useRef } from "react";
1743
- import { Box as Box12, Text as Text15, useInput as useInput7 } from "ink";
1744
- import { jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
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";
1745
1729
  function ScrollableBox({
1746
1730
  height,
1747
1731
  isActive = true,
@@ -1780,42 +1764,10 @@ function ScrollableBox({
1780
1764
  const showScrollUp = scrollOffset > 0;
1781
1765
  const showScrollDown = scrollOffset + visibleCount < totalItems;
1782
1766
  const visible = children.slice(scrollOffset, scrollOffset + visibleCount);
1783
- return /* @__PURE__ */ jsxs14(Box12, { flexDirection: "column", height, children: [
1784
- 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" }),
1785
1769
  visible,
1786
- showScrollDown && /* @__PURE__ */ jsx15(Text15, { dimColor: true, children: " \u2193 more" })
1787
- ] });
1788
- }
1789
-
1790
- // src/lib/ansi.ts
1791
- var ANSI_RE = (
1792
- // biome-ignore lint/suspicious/noControlCharactersInRegex: intentional ANSI stripping
1793
- /[\u001B\u009B][[\]()#;?]*(?:(?:(?:[a-zA-Z\d]*(?:;[-a-zA-Z\d/#&.:=?%@~_]*)*)?\u0007)|(?:(?:\d{1,4}(?:;\d{0,4})*)?[\dA-PR-TZcf-nq-uy=><~]))/g
1794
- );
1795
- function stripAnsi(text) {
1796
- return text.replace(ANSI_RE, "");
1797
- }
1798
-
1799
- // src/components/CommandOutput.tsx
1800
- import { jsx as jsx16 } from "react/jsx-runtime";
1801
- function cleanLines(raw) {
1802
- const stripped = stripAnsi(raw);
1803
- return stripped.replace(/\r/g, "").split("\n").filter((line) => line.length > 0);
1804
- }
1805
- function CommandOutput({
1806
- stdout,
1807
- stderr,
1808
- height,
1809
- isActive = false
1810
- }) {
1811
- const outLines = stdout ? cleanLines(stdout) : [];
1812
- const errLines = stderr ? cleanLines(stderr) : [];
1813
- if (outLines.length === 0 && errLines.length === 0) {
1814
- return null;
1815
- }
1816
- return /* @__PURE__ */ jsx16(ScrollableBox, { height: Math.max(3, height), isActive, children: [
1817
- ...outLines.map((line, i) => /* @__PURE__ */ jsx16(Text16, { children: line }, `o-${i}`)),
1818
- ...errLines.map((line, i) => /* @__PURE__ */ jsx16(Text16, { color: "red", children: line }, `e-${i}`))
1770
+ showScrollDown && /* @__PURE__ */ jsx14(Text14, { dimColor: true, children: " \u2193 more" })
1819
1771
  ] });
1820
1772
  }
1821
1773
 
@@ -1945,6 +1897,15 @@ async function copyToClipboard(text) {
1945
1897
  });
1946
1898
  }
1947
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
+
1948
1909
  // src/lib/errorSuggestions.ts
1949
1910
  var KNOWN_TOOLS = ["supabase", "gh", "vercel", "git"];
1950
1911
  var BACKTICK_CMD = /(?:try\s+)?(?:run(?:ning)?|use|execute)\s+`([^`]+)`/gi;
@@ -1991,7 +1952,7 @@ ${stderr}`);
1991
1952
  }
1992
1953
 
1993
1954
  // src/screens/CommandExecution.tsx
1994
- import { jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
1955
+ import { jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
1995
1956
  function CommandExecution({
1996
1957
  args: initialArgs,
1997
1958
  tool = "supabase",
@@ -2016,27 +1977,38 @@ function CommandExecution({
2016
1977
  const { runInteractive } = useInteractiveRun();
2017
1978
  const [outputFocused, setOutputFocused] = useState12(false);
2018
1979
  const [copyMessage, setCopyMessage] = useState12();
1980
+ const [feedback, setFeedback] = useState12();
1981
+ const [aborting, setAborting] = useState12(false);
2019
1982
  const cmdDisplay = rawCommand ? `${rawCommand} ${currentArgs.join(" ")}`.trim() : `${getToolDisplayName(tool)} ${currentArgs.join(" ")}`;
2020
1983
  const runCommand2 = currentArgs.join(" ");
2021
1984
  useInput8(
2022
- (_input, key) => {
1985
+ (input2, key) => {
2023
1986
  if (key.escape) {
2024
1987
  abort();
2025
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;
2026
2001
  }
2027
2002
  },
2028
2003
  { isActive: isInputActive && phase === "running" }
2029
2004
  );
2030
2005
  useInput8(
2031
- (input2, key) => {
2032
- if (input2 === "o" && !outputFocused) {
2033
- setOutputFocused(true);
2034
- }
2035
- if (key.escape && outputFocused) {
2036
- setOutputFocused(false);
2006
+ (input2, _key) => {
2007
+ if (input2 === "/") {
2008
+ setOutputFocused((prev) => !prev);
2037
2009
  }
2038
2010
  },
2039
- { isActive: isInputActive && phase === "error-menu" }
2011
+ { isActive: isInputActive && (phase === "error-menu" || phase === "success") }
2040
2012
  );
2041
2013
  useEffect9(() => {
2042
2014
  if (phase === "background-started") {
@@ -2070,6 +2042,12 @@ function CommandExecution({
2070
2042
  setPhase("error-menu");
2071
2043
  }
2072
2044
  }, [phase, runCommand2, status]);
2045
+ useEffect9(() => {
2046
+ if (feedback) {
2047
+ const timer = setTimeout(() => setFeedback(void 0), 2e3);
2048
+ return () => clearTimeout(timer);
2049
+ }
2050
+ }, [feedback]);
2073
2051
  if (phase === "confirm") {
2074
2052
  const pinned = isPinnedRun(runCommand2);
2075
2053
  const confirmItems = [
@@ -2086,17 +2064,17 @@ function CommandExecution({
2086
2064
  },
2087
2065
  { value: "cancel", label: "\u2190 Cancel" }
2088
2066
  ];
2089
- const confirmContent = /* @__PURE__ */ jsxs15(Box13, { flexDirection: "column", children: [
2090
- /* @__PURE__ */ jsxs15(Box13, { marginBottom: 1, gap: 1, children: [
2091
- /* @__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: [
2092
2070
  "\u25B6",
2093
2071
  " ",
2094
2072
  cmdDisplay
2095
2073
  ] }),
2096
- /* @__PURE__ */ jsx17(ToolBadge, { tool })
2074
+ /* @__PURE__ */ jsx15(ToolBadge, { tool })
2097
2075
  ] }),
2098
- pinMessage && /* @__PURE__ */ jsx17(Box13, { marginBottom: 1, children: /* @__PURE__ */ jsx17(Text17, { color: inkColors.accent, children: pinMessage }) }),
2099
- /* @__PURE__ */ jsx17(
2076
+ pinMessage && /* @__PURE__ */ jsx15(Box13, { marginBottom: 1, children: /* @__PURE__ */ jsx15(Text15, { color: inkColors.accent, children: pinMessage }) }),
2077
+ /* @__PURE__ */ jsx15(
2100
2078
  SelectList,
2101
2079
  {
2102
2080
  items: confirmItems,
@@ -2139,7 +2117,7 @@ function CommandExecution({
2139
2117
  }
2140
2118
  )
2141
2119
  ] });
2142
- 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(
2143
2121
  Box13,
2144
2122
  {
2145
2123
  flexDirection: "column",
@@ -2152,54 +2130,71 @@ function CommandExecution({
2152
2130
  ) : confirmContent });
2153
2131
  }
2154
2132
  if (phase === "background-started") {
2155
- return /* @__PURE__ */ jsxs15(Box13, { flexDirection: "column", paddingX: panelMode ? 1 : 0, children: [
2156
- /* @__PURE__ */ jsxs15(Box13, { marginY: 1, gap: 1, children: [
2157
- /* @__PURE__ */ jsx17(Text17, { color: "#3ECF8E", bold: true, children: "\u2713" }),
2158
- /* @__PURE__ */ jsx17(Text17, { color: "#3ECF8E", bold: true, children: "Started in background" })
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" })
2159
2137
  ] }),
2160
- /* @__PURE__ */ jsxs15(Box13, { children: [
2161
- /* @__PURE__ */ jsx17(Text17, { dimColor: true, children: "Command: " }),
2162
- /* @__PURE__ */ jsx17(Text17, { children: cmdDisplay })
2138
+ /* @__PURE__ */ jsxs14(Box13, { children: [
2139
+ /* @__PURE__ */ jsx15(Text15, { dimColor: true, children: "Command: " }),
2140
+ /* @__PURE__ */ jsx15(Text15, { children: cmdDisplay })
2163
2141
  ] })
2164
2142
  ] });
2165
2143
  }
2166
2144
  if (phase === "running") {
2167
- const hasPartialOutput = partialStdout.length > 0 || partialStderr.length > 0;
2168
- const streamOutputHeight = Math.max(3, height - 10);
2169
- return /* @__PURE__ */ jsxs15(Box13, { flexDirection: "column", paddingX: panelMode ? 1 : 0, children: [
2170
- /* @__PURE__ */ jsx17(Divider, { width: panelMode ? width - 4 : width }),
2171
- /* @__PURE__ */ jsxs15(Box13, { marginY: 1, gap: 1, children: [
2172
- /* @__PURE__ */ jsx17(Text17, { color: inkColors.accent, bold: true, children: "\u25B6" }),
2173
- /* @__PURE__ */ jsx17(Text17, { dimColor: true, children: "Running:" }),
2174
- /* @__PURE__ */ jsx17(Text17, { children: cmdDisplay }),
2175
- /* @__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
+ ] })
2176
2161
  ] }),
2177
- /* @__PURE__ */ jsx17(Divider, { width: panelMode ? width - 4 : width }),
2178
- /* @__PURE__ */ jsx17(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx17(Spinner, { label: `Executing ${cmdDisplay}...` }) }),
2179
- hasPartialOutput && /* @__PURE__ */ jsx17(
2180
- CommandOutput,
2162
+ /* @__PURE__ */ jsx15(
2163
+ Box13,
2181
2164
  {
2182
- stdout: partialStdout,
2183
- stderr: partialStderr,
2184
- height: streamOutputHeight,
2185
- isActive: false
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
+ )
2186
2179
  }
2187
2180
  ),
2188
- /* @__PURE__ */ jsxs15(Box13, { marginTop: 1, children: [
2189
- /* @__PURE__ */ jsx17(Text17, { dimColor: true, children: "Press " }),
2190
- /* @__PURE__ */ jsx17(Text17, { color: inkColors.accent, children: "Esc" }),
2191
- /* @__PURE__ */ jsx17(Text17, { dimColor: true, children: " to abort" })
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" })
2192
2187
  ] })
2193
2188
  ] });
2194
2189
  }
2195
2190
  if (phase === "success-pin-offer") {
2196
- return /* @__PURE__ */ jsxs15(Box13, { flexDirection: "column", paddingX: panelMode ? 1 : 0, children: [
2197
- /* @__PURE__ */ jsx17(Divider, { width: panelMode ? width - 4 : width }),
2198
- /* @__PURE__ */ jsxs15(Box13, { marginY: 1, gap: 1, children: [
2199
- /* @__PURE__ */ jsx17(Text17, { color: inkColors.accent, bold: true, children: "\u2713" }),
2200
- /* @__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!" })
2201
2196
  ] }),
2202
- /* @__PURE__ */ jsx17(
2197
+ /* @__PURE__ */ jsx15(
2203
2198
  ConfirmPrompt,
2204
2199
  {
2205
2200
  message: "Pin this exact command?",
@@ -2222,24 +2217,49 @@ function CommandExecution({
2222
2217
  const successItems = [
2223
2218
  { value: "__back__", label: "\u2190 Back to menu" }
2224
2219
  ];
2225
- const outputHeight = Math.max(3, height - 12);
2226
- return /* @__PURE__ */ jsxs15(Box13, { flexDirection: "column", paddingX: panelMode ? 1 : 0, children: [
2227
- /* @__PURE__ */ jsx17(Divider, { width: panelMode ? width - 4 : width }),
2228
- /* @__PURE__ */ jsxs15(Box13, { marginY: 1, gap: 1, children: [
2229
- /* @__PURE__ */ jsx17(Text17, { color: inkColors.accent, bold: true, children: "\u2713" }),
2230
- /* @__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" })
2231
2233
  ] }),
2232
- pinMessage && /* @__PURE__ */ jsx17(Box13, { marginBottom: 1, children: /* @__PURE__ */ jsx17(Text17, { color: inkColors.accent, children: pinMessage }) }),
2233
- /* @__PURE__ */ jsx17(
2234
- CommandOutput,
2234
+ pinMessage && /* @__PURE__ */ jsx15(Box13, { marginBottom: 1, children: /* @__PURE__ */ jsx15(Text15, { color: inkColors.accent, children: pinMessage }) }),
2235
+ /* @__PURE__ */ jsx15(
2236
+ Box13,
2235
2237
  {
2236
- stdout: result?.stdout,
2237
- stderr: result?.stderr,
2238
- height: outputHeight,
2239
- 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
+ )
2240
2252
  }
2241
2253
  ),
2242
- /* @__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(
2243
2263
  SelectList,
2244
2264
  {
2245
2265
  items: successItems,
@@ -2247,10 +2267,10 @@ function CommandExecution({
2247
2267
  onCancel: onHome ?? onBack,
2248
2268
  width: panelMode ? Math.max(20, width - 4) : width,
2249
2269
  maxVisible: panelMode ? Math.max(6, height - 6) : void 0,
2250
- isInputActive,
2270
+ isInputActive: isInputActive && !outputFocused,
2251
2271
  arrowNavigation: panelMode,
2252
2272
  boxedSections: panelMode,
2253
- panelFocused: isInputActive
2273
+ panelFocused: isInputActive && !outputFocused
2254
2274
  }
2255
2275
  )
2256
2276
  ] });
@@ -2313,65 +2333,67 @@ function CommandExecution({
2313
2333
  value: "menu",
2314
2334
  label: "\u2190 Back to menu"
2315
2335
  });
2316
- return /* @__PURE__ */ jsxs15(Box13, { flexDirection: "column", paddingX: panelMode ? 1 : 0, children: [
2317
- /* @__PURE__ */ jsx17(Divider, { width: panelMode ? width - 4 : width }),
2318
- result?.spawnError ? /* @__PURE__ */ jsxs15(Box13, { flexDirection: "column", marginY: 1, children: [
2319
- /* @__PURE__ */ jsxs15(Box13, { gap: 1, children: [
2320
- /* @__PURE__ */ jsx17(Text17, { color: "red", bold: true, children: "\u2717" }),
2321
- /* @__PURE__ */ jsx17(Text17, { color: "red", bold: true, children: "Failed to start command" })
2322
- ] }),
2323
- /* @__PURE__ */ jsxs15(Box13, { marginLeft: 2, marginTop: 1, children: [
2324
- /* @__PURE__ */ jsx17(Text17, { dimColor: true, children: "Error: " }),
2325
- /* @__PURE__ */ jsx17(Text17, { color: "red", children: result.spawnError })
2326
- ] }),
2327
- (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: [
2328
- "\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",
2329
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 ",
2330
2359
  tool,
2331
2360
  " CLI not found in this repository or PATH"
2332
- ] }) })
2333
- ] }) : /* @__PURE__ */ jsxs15(Box13, { flexDirection: "column", marginY: 1, children: [
2334
- /* @__PURE__ */ jsxs15(Box13, { gap: 1, children: [
2335
- /* @__PURE__ */ jsx17(Text17, { color: "red", bold: true, children: "\u2717" }),
2336
- /* @__PURE__ */ jsx17(Text17, { color: "red", children: "Command failed " }),
2337
- /* @__PURE__ */ jsx17(Text17, { dimColor: true, children: "(exit code " }),
2338
- /* @__PURE__ */ jsx17(Text17, { color: "red", bold: true, children: String(result?.exitCode) }),
2339
- /* @__PURE__ */ jsx17(Text17, { dimColor: true, children: ")" })
2340
- ] }),
2341
- /* @__PURE__ */ jsxs15(Box13, { marginLeft: 2, marginTop: 1, children: [
2342
- /* @__PURE__ */ jsx17(Text17, { dimColor: true, children: "Command: " }),
2343
- /* @__PURE__ */ jsx17(Text17, { children: cmdDisplay })
2344
- ] }),
2345
- !hasDebug && /* @__PURE__ */ jsxs15(Box13, { marginLeft: 2, marginTop: 1, gap: 1, children: [
2346
- /* @__PURE__ */ jsxs15(Text17, { dimColor: true, children: [
2347
- "\u{1F4A1}",
2348
- " Tip: retry with"
2349
- ] }),
2350
- /* @__PURE__ */ jsx17(Text17, { color: inkColors.accent, children: "--debug" }),
2351
- /* @__PURE__ */ jsx17(Text17, { dimColor: true, children: "to see detailed logs" })
2352
2361
  ] })
2353
2362
  ] }),
2354
- /* @__PURE__ */ jsx17(
2355
- 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,
2356
2370
  {
2357
- stdout: result?.stdout,
2358
- stderr: result?.stderr,
2359
- height: Math.max(3, height - 14 - errorItems.length - (suggestions.length > 0 ? suggestions.length + 4 : 0) - (copyMessage ? 1 : 0)),
2360
- 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
+ )
2361
2385
  }
2362
2386
  ),
2363
- copyMessage && /* @__PURE__ */ jsx17(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx17(Text17, { color: inkColors.accent, children: copyMessage }) }),
2364
- outputFocused ? /* @__PURE__ */ jsxs15(Box13, { marginTop: 1, children: [
2365
- /* @__PURE__ */ jsx17(Text17, { dimColor: true, children: "j/k scroll \xB7 " }),
2366
- /* @__PURE__ */ jsx17(Text17, { color: inkColors.accent, children: "Esc" }),
2367
- /* @__PURE__ */ jsx17(Text17, { dimColor: true, children: " back to menu" })
2368
- ] }) : /* @__PURE__ */ jsxs15(Box13, { marginTop: 1, marginBottom: 1, children: [
2369
- /* @__PURE__ */ jsx17(Text17, { bold: true, children: "What would you like to do?" }),
2370
- /* @__PURE__ */ jsx17(Text17, { dimColor: true, children: " (press " }),
2371
- /* @__PURE__ */ jsx17(Text17, { color: inkColors.accent, children: "o" }),
2372
- /* @__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" })
2373
2395
  ] }),
2374
- /* @__PURE__ */ jsx17(
2396
+ /* @__PURE__ */ jsx15(
2375
2397
  SelectList,
2376
2398
  {
2377
2399
  items: errorItems,
@@ -2395,6 +2417,8 @@ function CommandExecution({
2395
2417
  case "retry":
2396
2418
  setPinMessage(void 0);
2397
2419
  setCopyMessage(void 0);
2420
+ setAborting(false);
2421
+ setFeedback(void 0);
2398
2422
  reset();
2399
2423
  setPhase("running");
2400
2424
  break;
@@ -2403,6 +2427,8 @@ function CommandExecution({
2403
2427
  setCurrentArgs(newArgs);
2404
2428
  setPinMessage(void 0);
2405
2429
  setCopyMessage(void 0);
2430
+ setAborting(false);
2431
+ setFeedback(void 0);
2406
2432
  reset();
2407
2433
  setPhase("running");
2408
2434
  break;
@@ -2441,13 +2467,54 @@ function CommandExecution({
2441
2467
  panelFocused: isInputActive && !outputFocused
2442
2468
  }
2443
2469
  ),
2444
- !panelMode && /* @__PURE__ */ jsx17(StatusBar, { width })
2470
+ !panelMode && /* @__PURE__ */ jsx15(StatusBar, { width })
2445
2471
  ] });
2446
2472
  }
2447
2473
 
2448
2474
  // src/screens/SelfUpdate.tsx
2449
2475
  import { useEffect as useEffect10, useState as useState13 } from "react";
2450
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
2451
2518
  import { jsx as jsx18, jsxs as jsxs16 } from "react/jsx-runtime";
2452
2519
  var packageName = "@polterware/polter";
2453
2520
  var globalUpdateArgs = ["install", "-g", `${packageName}@latest`];
@@ -7576,13 +7643,21 @@ Apply completed with ${errors.length} error(s).
7576
7643
  process.exit(result.exitCode ?? 0);
7577
7644
  }
7578
7645
  const AppComponent2 = parsed.classic ? AppClassic : AppPanel;
7646
+ const socketPath2 = getSocketPath();
7647
+ const ipc2 = socketPath2 ? createIpcServer(socketPath2) : null;
7648
+ if (ipc2) await ipc2.start();
7579
7649
  const inst = render(React28.createElement(AppComponent2));
7580
7650
  await inst.waitUntilExit();
7651
+ if (ipc2) await ipc2.stop();
7581
7652
  process.exit(0);
7582
7653
  }
7583
7654
  const AppComponent = parsed.classic ? AppClassic : AppPanel;
7655
+ const socketPath = getSocketPath();
7656
+ const ipc = socketPath ? createIpcServer(socketPath) : null;
7657
+ if (ipc) await ipc.start();
7584
7658
  const instance = render(React28.createElement(AppComponent));
7585
7659
  await instance.waitUntilExit();
7660
+ if (ipc) await ipc.stop();
7586
7661
  process.exit(0);
7587
7662
  }
7588
7663
  main().catch((error) => {