@polterware/polter 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -24,7 +24,7 @@ import {
24
24
  runSupabaseCommand,
25
25
  savePipeline,
26
26
  writeProjectConfig
27
- } from "./chunk-TWWRDI3Q.js";
27
+ } from "./chunk-2OZZNSKW.js";
28
28
 
29
29
  // src/index.tsx
30
30
  import React19 from "react";
@@ -743,8 +743,8 @@ function buildHomeItems({
743
743
  kind: "header",
744
744
  selectable: false
745
745
  });
746
- const toolOrder = { supabase: 0, vercel: 1, gh: 2, pulumi: 3 };
747
- const toolIcons = { supabase: "\u{1F7E2}", vercel: "\u26AA", gh: "\u{1F535}", pulumi: "\u{1F7E3}" };
746
+ const toolOrder = { supabase: 0, vercel: 1, gh: 2, git: 3 };
747
+ const toolIcons = { supabase: "\u{1F7E2}", vercel: "\u26AA", gh: "\u{1F535}", git: "\u{1F7E0}" };
748
748
  const grouped = /* @__PURE__ */ new Map();
749
749
  for (const cmd of activeFeature.commands) {
750
750
  const existing = grouped.get(cmd.tool) ?? [];
@@ -997,13 +997,13 @@ var toolColors = {
997
997
  supabase: "#3ECF8E",
998
998
  gh: "#58A6FF",
999
999
  vercel: "#FFFFFF",
1000
- pulumi: "#8B5CF6"
1000
+ git: "#F05032"
1001
1001
  };
1002
1002
  var toolLabels = {
1003
1003
  supabase: "supabase",
1004
1004
  gh: "github",
1005
1005
  vercel: "vercel",
1006
- pulumi: "pulumi"
1006
+ git: "git"
1007
1007
  };
1008
1008
  function ToolBadge({ tool }) {
1009
1009
  return /* @__PURE__ */ jsx7(Text7, { color: toolColors[tool], dimColor: true, children: toolLabels[tool] });
@@ -1255,24 +1255,28 @@ function CustomCommand({
1255
1255
  onNavigate,
1256
1256
  onBack,
1257
1257
  width = 80,
1258
+ height = 24,
1258
1259
  panelMode = false,
1259
1260
  isInputActive = true
1260
1261
  }) {
1261
1262
  const [phase, setPhase] = useState7("tool-select");
1262
1263
  const [selectedTool, setSelectedTool] = useState7("supabase");
1263
1264
  if (phase === "tool-select") {
1264
- return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
1265
+ const toolItems = [
1266
+ { value: "__section__", label: "\u{1F6E0} Select Tool", kind: "header", selectable: false },
1267
+ { value: "supabase", label: "Supabase CLI", hint: "supabase ...", kind: "action" },
1268
+ { value: "gh", label: "GitHub CLI", hint: "gh ...", kind: "action" },
1269
+ { value: "vercel", label: "Vercel CLI", hint: "vercel ...", kind: "action" },
1270
+ { value: "git", label: "Git", hint: "git ...", kind: "action" },
1271
+ ...!panelMode ? [{ value: "__back__", label: "\u2190 Back" }] : []
1272
+ ];
1273
+ return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", paddingX: panelMode ? 1 : 0, children: [
1265
1274
  /* @__PURE__ */ jsx9(Box8, { marginBottom: 1, children: /* @__PURE__ */ jsx9(Text9, { bold: true, color: inkColors.accent, children: "\u270F\uFE0F Custom Command" }) }),
1266
- /* @__PURE__ */ jsx9(Text9, { dimColor: true, children: "Select tool:" }),
1275
+ !panelMode && /* @__PURE__ */ jsx9(Text9, { dimColor: true, children: "Select tool:" }),
1267
1276
  /* @__PURE__ */ jsx9(
1268
1277
  SelectList,
1269
1278
  {
1270
- items: [
1271
- { value: "supabase", label: "Supabase CLI", hint: "supabase ..." },
1272
- { value: "gh", label: "GitHub CLI", hint: "gh ..." },
1273
- { value: "vercel", label: "Vercel CLI", hint: "vercel ..." },
1274
- { value: "__back__", label: "\u2190 Back" }
1275
- ],
1279
+ items: toolItems,
1276
1280
  onSelect: (value) => {
1277
1281
  if (value === "__back__") {
1278
1282
  onBack();
@@ -1282,7 +1286,9 @@ function CustomCommand({
1282
1286
  setPhase("input");
1283
1287
  },
1284
1288
  onCancel: onBack,
1285
- width,
1289
+ boxedSections: panelMode,
1290
+ width: panelMode ? Math.max(20, width - 4) : width,
1291
+ maxVisible: panelMode ? Math.max(6, height - 6) : void 0,
1286
1292
  isInputActive,
1287
1293
  arrowNavigation: panelMode
1288
1294
  }
@@ -1290,7 +1296,7 @@ function CustomCommand({
1290
1296
  !panelMode && /* @__PURE__ */ jsx9(StatusBar, { hint: "\u2191\u2193 navigate \xB7 Enter select \xB7 Esc back", width })
1291
1297
  ] });
1292
1298
  }
1293
- return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
1299
+ return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", paddingX: panelMode ? 1 : 0, children: [
1294
1300
  /* @__PURE__ */ jsxs8(Box8, { marginBottom: 1, gap: 1, children: [
1295
1301
  /* @__PURE__ */ jsx9(Text9, { bold: true, color: inkColors.accent, children: "\u270F\uFE0F Custom Command" }),
1296
1302
  /* @__PURE__ */ jsxs8(Text9, { dimColor: true, children: [
@@ -1329,7 +1335,9 @@ import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
1329
1335
  function FlagToggle({
1330
1336
  flags,
1331
1337
  onSubmit,
1332
- onCancel
1338
+ onCancel,
1339
+ isInputActive = true,
1340
+ arrowNavigation = false
1333
1341
  }) {
1334
1342
  const [cursor, setCursor] = useState8(0);
1335
1343
  const [selected, setSelected] = useState8(/* @__PURE__ */ new Set());
@@ -1358,7 +1366,10 @@ function FlagToggle({
1358
1366
  if (key.escape && onCancel) {
1359
1367
  onCancel();
1360
1368
  }
1361
- });
1369
+ if (arrowNavigation && key.leftArrow && onCancel) {
1370
+ onCancel();
1371
+ }
1372
+ }, { isActive: isInputActive });
1362
1373
  return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
1363
1374
  /* @__PURE__ */ jsxs9(Box9, { marginBottom: 1, children: [
1364
1375
  /* @__PURE__ */ jsx10(Text10, { bold: true, color: inkColors.accent, children: "\u2691 Global Flags" }),
@@ -1417,7 +1428,9 @@ function FlagSelection({
1417
1428
  const finalArgs = selectedFlags.length > 0 ? [...args, ...selectedFlags] : args;
1418
1429
  onNavigate("confirm-execute", { args: finalArgs, tool });
1419
1430
  },
1420
- onCancel: onBack
1431
+ onCancel: onBack,
1432
+ isInputActive,
1433
+ arrowNavigation: panelMode
1421
1434
  }
1422
1435
  ),
1423
1436
  !panelMode && /* @__PURE__ */ jsx11(StatusBar, { hint: "Space toggle \xB7 Enter confirm \xB7 Esc back", width })
@@ -1449,9 +1462,20 @@ import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
1449
1462
  function ConfirmPrompt({
1450
1463
  message,
1451
1464
  defaultValue = true,
1452
- onConfirm
1465
+ onConfirm,
1466
+ onCancel,
1467
+ isInputActive = true,
1468
+ arrowNavigation = false
1453
1469
  }) {
1454
- useInput5((input2) => {
1470
+ useInput5((input2, key) => {
1471
+ if (key.escape && onCancel) {
1472
+ onCancel();
1473
+ return;
1474
+ }
1475
+ if (arrowNavigation && key.leftArrow && onCancel) {
1476
+ onCancel();
1477
+ return;
1478
+ }
1455
1479
  if (input2 === "y" || input2 === "Y") {
1456
1480
  onConfirm(true);
1457
1481
  } else if (input2 === "n" || input2 === "N") {
@@ -1459,7 +1483,7 @@ function ConfirmPrompt({
1459
1483
  } else if (input2 === "\r") {
1460
1484
  onConfirm(defaultValue);
1461
1485
  }
1462
- });
1486
+ }, { isActive: isInputActive });
1463
1487
  return /* @__PURE__ */ jsxs12(Box11, { gap: 1, children: [
1464
1488
  /* @__PURE__ */ jsx13(Text12, { color: inkColors.accent, bold: true, children: "?" }),
1465
1489
  /* @__PURE__ */ jsx13(Text12, { children: message }),
@@ -1548,6 +1572,7 @@ function CommandExecution({
1548
1572
  onBack,
1549
1573
  onExit,
1550
1574
  width = 80,
1575
+ height = 24,
1551
1576
  panelMode = false,
1552
1577
  isInputActive = true
1553
1578
  }) {
@@ -1575,7 +1600,7 @@ function CommandExecution({
1575
1600
  }
1576
1601
  }, [phase, runCommand2, status]);
1577
1602
  if (phase === "confirm") {
1578
- return /* @__PURE__ */ jsx15(Box12, { flexDirection: "column", children: /* @__PURE__ */ jsx15(
1603
+ return /* @__PURE__ */ jsx15(Box12, { flexDirection: "column", paddingX: panelMode ? 1 : 0, children: /* @__PURE__ */ jsx15(
1579
1604
  ConfirmPrompt,
1580
1605
  {
1581
1606
  message: `Execute ${cmdDisplay}?`,
@@ -1586,26 +1611,29 @@ function CommandExecution({
1586
1611
  } else {
1587
1612
  onBack();
1588
1613
  }
1589
- }
1614
+ },
1615
+ onCancel: onBack,
1616
+ isInputActive,
1617
+ arrowNavigation: panelMode
1590
1618
  }
1591
1619
  ) });
1592
1620
  }
1593
1621
  if (phase === "running") {
1594
- return /* @__PURE__ */ jsxs14(Box12, { flexDirection: "column", children: [
1595
- /* @__PURE__ */ jsx15(Divider, { width }),
1622
+ return /* @__PURE__ */ jsxs14(Box12, { flexDirection: "column", paddingX: panelMode ? 1 : 0, children: [
1623
+ /* @__PURE__ */ jsx15(Divider, { width: panelMode ? width - 4 : width }),
1596
1624
  /* @__PURE__ */ jsxs14(Box12, { marginY: 1, gap: 1, children: [
1597
1625
  /* @__PURE__ */ jsx15(Text14, { color: inkColors.accent, bold: true, children: "\u25B6" }),
1598
1626
  /* @__PURE__ */ jsx15(Text14, { dimColor: true, children: "Running:" }),
1599
1627
  /* @__PURE__ */ jsx15(Text14, { children: cmdDisplay }),
1600
1628
  /* @__PURE__ */ jsx15(ToolBadge, { tool })
1601
1629
  ] }),
1602
- /* @__PURE__ */ jsx15(Divider, { width }),
1630
+ /* @__PURE__ */ jsx15(Divider, { width: panelMode ? width - 4 : width }),
1603
1631
  /* @__PURE__ */ jsx15(Box12, { marginTop: 1, children: /* @__PURE__ */ jsx15(Spinner, { label: `Executing ${cmdDisplay}...` }) })
1604
1632
  ] });
1605
1633
  }
1606
1634
  if (phase === "success-pin-offer") {
1607
- return /* @__PURE__ */ jsxs14(Box12, { flexDirection: "column", children: [
1608
- /* @__PURE__ */ jsx15(Divider, { width }),
1635
+ return /* @__PURE__ */ jsxs14(Box12, { flexDirection: "column", paddingX: panelMode ? 1 : 0, children: [
1636
+ /* @__PURE__ */ jsx15(Divider, { width: panelMode ? width - 4 : width }),
1609
1637
  /* @__PURE__ */ jsxs14(Box12, { marginY: 1, gap: 1, children: [
1610
1638
  /* @__PURE__ */ jsx15(Text14, { color: inkColors.accent, bold: true, children: "\u2713" }),
1611
1639
  /* @__PURE__ */ jsx15(Text14, { color: inkColors.accent, bold: true, children: "Command completed successfully!" })
@@ -1621,28 +1649,34 @@ function CommandExecution({
1621
1649
  setPinMessage("Exact command pinned to Pinned Runs.");
1622
1650
  }
1623
1651
  setPhase("success");
1624
- }
1652
+ },
1653
+ onCancel: () => setPhase("success"),
1654
+ isInputActive,
1655
+ arrowNavigation: panelMode
1625
1656
  }
1626
1657
  )
1627
1658
  ] });
1628
1659
  }
1629
1660
  if (phase === "success") {
1630
- return /* @__PURE__ */ jsxs14(Box12, { flexDirection: "column", children: [
1631
- /* @__PURE__ */ jsx15(Divider, { width }),
1661
+ const successItems = panelMode ? [] : [{ value: "__back__", label: "\u2190 Back to menu" }];
1662
+ return /* @__PURE__ */ jsxs14(Box12, { flexDirection: "column", paddingX: panelMode ? 1 : 0, children: [
1663
+ /* @__PURE__ */ jsx15(Divider, { width: panelMode ? width - 4 : width }),
1632
1664
  /* @__PURE__ */ jsxs14(Box12, { marginY: 1, gap: 1, children: [
1633
1665
  /* @__PURE__ */ jsx15(Text14, { color: inkColors.accent, bold: true, children: "\u2713" }),
1634
1666
  /* @__PURE__ */ jsx15(Text14, { color: inkColors.accent, bold: true, children: "Command completed successfully!" })
1635
1667
  ] }),
1636
1668
  pinMessage && /* @__PURE__ */ jsx15(Box12, { marginBottom: 1, children: /* @__PURE__ */ jsx15(Text14, { color: inkColors.accent, children: pinMessage }) }),
1637
- /* @__PURE__ */ jsx15(
1669
+ successItems.length > 0 && /* @__PURE__ */ jsx15(
1638
1670
  SelectList,
1639
1671
  {
1640
- items: [{ value: "__back__", label: "\u2190 Back to menu" }],
1672
+ items: successItems,
1641
1673
  onSelect: onBack,
1642
1674
  onCancel: onBack,
1643
- width,
1675
+ width: panelMode ? Math.max(20, width - 4) : width,
1676
+ maxVisible: panelMode ? Math.max(6, height - 6) : void 0,
1644
1677
  isInputActive,
1645
- arrowNavigation: panelMode
1678
+ arrowNavigation: panelMode,
1679
+ boxedSections: panelMode
1646
1680
  }
1647
1681
  )
1648
1682
  ] });
@@ -1659,16 +1693,16 @@ function CommandExecution({
1659
1693
  });
1660
1694
  }
1661
1695
  }
1662
- errorItems.push(
1663
- {
1664
- value: "copy",
1665
- label: "\u{1F4CB} Copy command to clipboard"
1666
- },
1667
- { value: "menu", label: "\u2190 Return to main menu" },
1668
- { value: "exit", label: "\u{1F6AA} Exit Polter" }
1669
- );
1670
- return /* @__PURE__ */ jsxs14(Box12, { flexDirection: "column", children: [
1671
- /* @__PURE__ */ jsx15(Divider, { width }),
1696
+ errorItems.push({
1697
+ value: "copy",
1698
+ label: "\u{1F4CB} Copy command to clipboard"
1699
+ });
1700
+ if (!panelMode) {
1701
+ errorItems.push({ value: "menu", label: "\u2190 Return to main menu" });
1702
+ }
1703
+ errorItems.push({ value: "exit", label: "\u{1F6AA} Exit Polter" });
1704
+ return /* @__PURE__ */ jsxs14(Box12, { flexDirection: "column", paddingX: panelMode ? 1 : 0, children: [
1705
+ /* @__PURE__ */ jsx15(Divider, { width: panelMode ? width - 4 : width }),
1672
1706
  result?.spawnError ? /* @__PURE__ */ jsxs14(Box12, { flexDirection: "column", marginY: 1, children: [
1673
1707
  /* @__PURE__ */ jsxs14(Box12, { gap: 1, children: [
1674
1708
  /* @__PURE__ */ jsx15(Text14, { color: "red", bold: true, children: "\u2717" }),
@@ -1733,7 +1767,9 @@ function CommandExecution({
1733
1767
  }
1734
1768
  },
1735
1769
  onCancel: onBack,
1736
- width,
1770
+ boxedSections: panelMode,
1771
+ width: panelMode ? Math.max(20, width - 4) : width,
1772
+ maxVisible: panelMode ? Math.max(6, height - 6) : void 0,
1737
1773
  isInputActive,
1738
1774
  arrowNavigation: panelMode
1739
1775
  }
@@ -1756,6 +1792,7 @@ function SelfUpdate({
1756
1792
  onBack,
1757
1793
  onExit,
1758
1794
  width = 80,
1795
+ height = 24,
1759
1796
  panelMode = false,
1760
1797
  isInputActive = true
1761
1798
  }) {
@@ -1784,24 +1821,28 @@ function SelfUpdate({
1784
1821
  }
1785
1822
  }, [phase, status]);
1786
1823
  if (phase === "target") {
1787
- return /* @__PURE__ */ jsxs15(Box13, { flexDirection: "column", children: [
1824
+ const targetItems = [
1825
+ { value: "__section__", label: "\u{1F4E6} Update Target", kind: "header", selectable: false },
1826
+ {
1827
+ value: "repository",
1828
+ label: "Current repository",
1829
+ hint: "Pin the latest version in package.json",
1830
+ kind: "action"
1831
+ },
1832
+ {
1833
+ value: "global",
1834
+ label: "Global install",
1835
+ hint: "Update the shared version available in PATH",
1836
+ kind: "action"
1837
+ },
1838
+ ...!panelMode ? [{ value: "back", label: "\u2190 Back to menu" }] : []
1839
+ ];
1840
+ return /* @__PURE__ */ jsxs15(Box13, { flexDirection: "column", paddingX: panelMode ? 1 : 0, children: [
1788
1841
  /* @__PURE__ */ jsx16(Box13, { marginBottom: 1, children: /* @__PURE__ */ jsx16(Text15, { bold: true, children: "Choose where to update Polter." }) }),
1789
1842
  /* @__PURE__ */ jsx16(
1790
1843
  SelectList,
1791
1844
  {
1792
- items: [
1793
- {
1794
- value: "repository",
1795
- label: "Current repository",
1796
- hint: "Pin the latest version in package.json"
1797
- },
1798
- {
1799
- value: "global",
1800
- label: "Global install",
1801
- hint: "Update the shared version available in PATH"
1802
- },
1803
- { value: "back", label: "\u2190 Back to menu" }
1804
- ],
1845
+ items: targetItems,
1805
1846
  onSelect: (value) => {
1806
1847
  if (value === "back") {
1807
1848
  onBack();
@@ -1812,7 +1853,9 @@ function SelfUpdate({
1812
1853
  setPhase("confirm");
1813
1854
  },
1814
1855
  onCancel: onBack,
1815
- width,
1856
+ boxedSections: panelMode,
1857
+ width: panelMode ? Math.max(20, width - 4) : width,
1858
+ maxVisible: panelMode ? Math.max(6, height - 6) : void 0,
1816
1859
  isInputActive,
1817
1860
  arrowNavigation: panelMode
1818
1861
  }
@@ -1824,7 +1867,7 @@ function SelfUpdate({
1824
1867
  ] });
1825
1868
  }
1826
1869
  if (phase === "confirm") {
1827
- return /* @__PURE__ */ jsxs15(Box13, { flexDirection: "column", children: [
1870
+ return /* @__PURE__ */ jsxs15(Box13, { flexDirection: "column", paddingX: panelMode ? 1 : 0, children: [
1828
1871
  /* @__PURE__ */ jsx16(
1829
1872
  ConfirmPrompt,
1830
1873
  {
@@ -1841,7 +1884,16 @@ function SelfUpdate({
1841
1884
  return;
1842
1885
  }
1843
1886
  onBack();
1844
- }
1887
+ },
1888
+ onCancel: () => {
1889
+ if (repositoryRoot) {
1890
+ setPhase("target");
1891
+ } else {
1892
+ onBack();
1893
+ }
1894
+ },
1895
+ isInputActive,
1896
+ arrowNavigation: panelMode
1845
1897
  }
1846
1898
  ),
1847
1899
  /* @__PURE__ */ jsxs15(Box13, { marginTop: 1, marginLeft: 2, flexDirection: "column", children: [
@@ -1854,20 +1906,25 @@ function SelfUpdate({
1854
1906
  ] });
1855
1907
  }
1856
1908
  if (phase === "running") {
1857
- return /* @__PURE__ */ jsxs15(Box13, { flexDirection: "column", children: [
1858
- /* @__PURE__ */ jsx16(Divider, { width }),
1909
+ return /* @__PURE__ */ jsxs15(Box13, { flexDirection: "column", paddingX: panelMode ? 1 : 0, children: [
1910
+ /* @__PURE__ */ jsx16(Divider, { width: panelMode ? width - 4 : width }),
1859
1911
  /* @__PURE__ */ jsxs15(Box13, { marginY: 1, gap: 1, children: [
1860
1912
  /* @__PURE__ */ jsx16(Text15, { color: inkColors.accent, bold: true, children: "\u25B6" }),
1861
1913
  /* @__PURE__ */ jsx16(Text15, { dimColor: true, children: "Running:" }),
1862
1914
  /* @__PURE__ */ jsx16(Text15, { children: updateDisplay })
1863
1915
  ] }),
1864
- /* @__PURE__ */ jsx16(Divider, { width }),
1916
+ /* @__PURE__ */ jsx16(Divider, { width: panelMode ? width - 4 : width }),
1865
1917
  /* @__PURE__ */ jsx16(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx16(Spinner, { label: "Updating Polter..." }) })
1866
1918
  ] });
1867
1919
  }
1868
1920
  if (phase === "success") {
1869
- return /* @__PURE__ */ jsxs15(Box13, { flexDirection: "column", children: [
1870
- /* @__PURE__ */ jsx16(Divider, { width }),
1921
+ const successItems = [
1922
+ { value: "__section__", label: "\u2705 Update Complete", kind: "header", selectable: false },
1923
+ ...!panelMode ? [{ value: "__back__", label: "\u2190 Back to menu", kind: "action" }] : [],
1924
+ { value: "__exit__", label: "\u{1F6AA} Exit Polter", kind: "action" }
1925
+ ];
1926
+ return /* @__PURE__ */ jsxs15(Box13, { flexDirection: "column", paddingX: panelMode ? 1 : 0, children: [
1927
+ /* @__PURE__ */ jsx16(Divider, { width: panelMode ? width - 4 : width }),
1871
1928
  /* @__PURE__ */ jsxs15(Box13, { marginY: 1, gap: 1, children: [
1872
1929
  /* @__PURE__ */ jsx16(Text15, { color: inkColors.accent, bold: true, children: "\u2713" }),
1873
1930
  /* @__PURE__ */ jsx16(Text15, { color: inkColors.accent, bold: true, children: "Update completed successfully!" })
@@ -1882,10 +1939,7 @@ function SelfUpdate({
1882
1939
  /* @__PURE__ */ jsx16(
1883
1940
  SelectList,
1884
1941
  {
1885
- items: [
1886
- { value: "__back__", label: "\u2190 Back to menu" },
1887
- { value: "__exit__", label: "\u{1F6AA} Exit Polter" }
1888
- ],
1942
+ items: successItems,
1889
1943
  onSelect: (value) => {
1890
1944
  if (value === "__exit__") {
1891
1945
  onExit();
@@ -1894,15 +1948,24 @@ function SelfUpdate({
1894
1948
  onBack();
1895
1949
  },
1896
1950
  onCancel: onBack,
1897
- width,
1951
+ boxedSections: panelMode,
1952
+ width: panelMode ? Math.max(20, width - 4) : width,
1953
+ maxVisible: panelMode ? Math.max(6, height - 6) : void 0,
1898
1954
  isInputActive,
1899
1955
  arrowNavigation: panelMode
1900
1956
  }
1901
1957
  )
1902
1958
  ] });
1903
1959
  }
1904
- return /* @__PURE__ */ jsxs15(Box13, { flexDirection: "column", children: [
1905
- /* @__PURE__ */ jsx16(Divider, { width }),
1960
+ const errorItems = [
1961
+ { value: "__section__", label: "\u{1F527} Recovery Options", kind: "header", selectable: false },
1962
+ { value: "retry", label: "\u{1F504} Retry update", kind: "action" },
1963
+ ...repositoryRoot ? [{ value: "target", label: "\u2194 Choose update target", kind: "action" }] : [],
1964
+ ...!panelMode ? [{ value: "menu", label: "\u2190 Return to main menu", kind: "action" }] : [],
1965
+ { value: "exit", label: "\u{1F6AA} Exit Polter", kind: "action" }
1966
+ ];
1967
+ return /* @__PURE__ */ jsxs15(Box13, { flexDirection: "column", paddingX: panelMode ? 1 : 0, children: [
1968
+ /* @__PURE__ */ jsx16(Divider, { width: panelMode ? width - 4 : width }),
1906
1969
  result?.spawnError ? /* @__PURE__ */ jsxs15(Box13, { flexDirection: "column", marginY: 1, children: [
1907
1970
  /* @__PURE__ */ jsxs15(Box13, { gap: 1, children: [
1908
1971
  /* @__PURE__ */ jsx16(Text15, { color: "red", bold: true, children: "\u2717" }),
@@ -1933,16 +1996,11 @@ function SelfUpdate({
1933
1996
  repositoryRoot
1934
1997
  ] })
1935
1998
  ] }),
1936
- /* @__PURE__ */ jsx16(Box13, { marginTop: 1, marginBottom: 1, children: /* @__PURE__ */ jsx16(Text15, { bold: true, children: "What would you like to do?" }) }),
1999
+ !panelMode && /* @__PURE__ */ jsx16(Box13, { marginTop: 1, marginBottom: 1, children: /* @__PURE__ */ jsx16(Text15, { bold: true, children: "What would you like to do?" }) }),
1937
2000
  /* @__PURE__ */ jsx16(
1938
2001
  SelectList,
1939
2002
  {
1940
- items: [
1941
- { value: "retry", label: "\u{1F504} Retry update" },
1942
- ...repositoryRoot ? [{ value: "target", label: "\u2194 Choose update target" }] : [],
1943
- { value: "menu", label: "\u2190 Return to main menu" },
1944
- { value: "exit", label: "\u{1F6AA} Exit Polter" }
1945
- ],
2003
+ items: errorItems,
1946
2004
  onSelect: (value) => {
1947
2005
  switch (value) {
1948
2006
  case "retry":
@@ -1962,7 +2020,9 @@ function SelfUpdate({
1962
2020
  }
1963
2021
  },
1964
2022
  onCancel: onBack,
1965
- width,
2023
+ boxedSections: panelMode,
2024
+ width: panelMode ? Math.max(20, width - 4) : width,
2025
+ maxVisible: panelMode ? Math.max(6, height - 6) : void 0,
1966
2026
  isInputActive,
1967
2027
  arrowNavigation: panelMode
1968
2028
  }
@@ -1975,9 +2035,19 @@ function SelfUpdate({
1975
2035
  import { useMemo as useMemo4 } from "react";
1976
2036
  import { Box as Box14, Text as Text16 } from "ink";
1977
2037
  import { jsx as jsx17, jsxs as jsxs16 } from "react/jsx-runtime";
1978
- var toolIds = ["supabase", "gh", "vercel", "pulumi"];
1979
- function ToolStatus({ onBack, width = 80, panelMode = false, isInputActive = true }) {
2038
+ var toolIds = ["supabase", "gh", "vercel", "git"];
2039
+ function ToolStatus({ onBack, width = 80, height = 24, panelMode = false, isInputActive = true }) {
1980
2040
  const tools = useMemo4(() => toolIds.map(getToolInfo), []);
2041
+ if (panelMode) {
2042
+ return /* @__PURE__ */ jsxs16(Box14, { flexDirection: "column", paddingX: 1, children: [
2043
+ /* @__PURE__ */ jsx17(Box14, { marginBottom: 1, children: /* @__PURE__ */ jsx17(Text16, { bold: true, color: inkColors.accent, children: "\u{1F527} Tool Status" }) }),
2044
+ tools.map((tool) => /* @__PURE__ */ jsxs16(Box14, { gap: 1, marginLeft: 2, children: [
2045
+ /* @__PURE__ */ jsx17(Text16, { color: tool.installed ? inkColors.accent : "red", children: tool.installed ? "\u2713" : "\u2717" }),
2046
+ /* @__PURE__ */ jsx17(Box14, { width: 16, children: /* @__PURE__ */ jsx17(Text16, { bold: true, children: tool.label }) }),
2047
+ /* @__PURE__ */ jsx17(Text16, { dimColor: true, children: tool.installed ? tool.version ?? "installed" : "not found" })
2048
+ ] }, tool.id))
2049
+ ] });
2050
+ }
1981
2051
  return /* @__PURE__ */ jsxs16(Box14, { flexDirection: "column", children: [
1982
2052
  /* @__PURE__ */ jsx17(Box14, { marginBottom: 1, children: /* @__PURE__ */ jsx17(Text16, { bold: true, color: inkColors.accent, children: "\u{1F527} Tool Status" }) }),
1983
2053
  tools.map((tool) => /* @__PURE__ */ jsxs16(Box14, { gap: 1, marginLeft: 2, children: [
@@ -1992,11 +2062,10 @@ function ToolStatus({ onBack, width = 80, panelMode = false, isInputActive = tru
1992
2062
  onSelect: onBack,
1993
2063
  onCancel: onBack,
1994
2064
  width,
1995
- isInputActive,
1996
- arrowNavigation: panelMode
2065
+ isInputActive
1997
2066
  }
1998
2067
  ) }),
1999
- !panelMode && /* @__PURE__ */ jsx17(StatusBar, { hint: "Enter to go back", width })
2068
+ /* @__PURE__ */ jsx17(StatusBar, { hint: "Enter to go back", width })
2000
2069
  ] });
2001
2070
  }
2002
2071
 
@@ -2081,6 +2150,7 @@ import { jsx as jsx18, jsxs as jsxs17 } from "react/jsx-runtime";
2081
2150
  function ProjectConfig({
2082
2151
  onBack,
2083
2152
  width = 80,
2153
+ height = 24,
2084
2154
  panelMode = false,
2085
2155
  isInputActive = true
2086
2156
  }) {
@@ -2090,23 +2160,22 @@ function ProjectConfig({
2090
2160
  const [feedback, setFeedback] = useState13();
2091
2161
  const { openEditor, isEditing } = useEditor();
2092
2162
  if (!configPath) {
2093
- return /* @__PURE__ */ jsxs17(Box15, { flexDirection: "column", children: [
2163
+ return /* @__PURE__ */ jsxs17(Box15, { flexDirection: "column", paddingX: panelMode ? 1 : 0, children: [
2094
2164
  /* @__PURE__ */ jsx18(Text17, { color: "red", children: "No package.json found. Run from a project directory." }),
2095
- /* @__PURE__ */ jsx18(
2165
+ !panelMode && /* @__PURE__ */ jsx18(
2096
2166
  SelectList,
2097
2167
  {
2098
2168
  items: [{ value: "__back__", label: "\u2190 Back" }],
2099
2169
  onSelect: onBack,
2100
2170
  onCancel: onBack,
2101
2171
  width,
2102
- isInputActive,
2103
- arrowNavigation: panelMode
2172
+ isInputActive
2104
2173
  }
2105
2174
  )
2106
2175
  ] });
2107
2176
  }
2108
2177
  if (phase === "edit-supabase-ref") {
2109
- return /* @__PURE__ */ jsx18(Box15, { flexDirection: "column", children: /* @__PURE__ */ jsx18(
2178
+ return /* @__PURE__ */ jsx18(Box15, { flexDirection: "column", paddingX: panelMode ? 1 : 0, children: /* @__PURE__ */ jsx18(
2110
2179
  TextPrompt,
2111
2180
  {
2112
2181
  label: "Supabase project ref:",
@@ -2129,7 +2198,7 @@ function ProjectConfig({
2129
2198
  ) });
2130
2199
  }
2131
2200
  if (phase === "edit-vercel-id") {
2132
- return /* @__PURE__ */ jsx18(Box15, { flexDirection: "column", children: /* @__PURE__ */ jsx18(
2201
+ return /* @__PURE__ */ jsx18(Box15, { flexDirection: "column", paddingX: panelMode ? 1 : 0, children: /* @__PURE__ */ jsx18(
2133
2202
  TextPrompt,
2134
2203
  {
2135
2204
  label: "Vercel project ID:",
@@ -2152,7 +2221,7 @@ function ProjectConfig({
2152
2221
  ) });
2153
2222
  }
2154
2223
  if (phase === "edit-gh-repo") {
2155
- return /* @__PURE__ */ jsx18(Box15, { flexDirection: "column", children: /* @__PURE__ */ jsx18(
2224
+ return /* @__PURE__ */ jsx18(Box15, { flexDirection: "column", paddingX: panelMode ? 1 : 0, children: /* @__PURE__ */ jsx18(
2156
2225
  TextPrompt,
2157
2226
  {
2158
2227
  label: "GitHub repo (owner/name):",
@@ -2174,6 +2243,77 @@ function ProjectConfig({
2174
2243
  }
2175
2244
  ) });
2176
2245
  }
2246
+ const configItems = [
2247
+ { value: "__section_current__", label: "\u{1F4CB} Current Values", kind: "header", selectable: false },
2248
+ { value: "__info_supabase__", label: `Supabase ref: ${config2.tools.supabase?.projectRef ?? "not set"}`, kind: "header", selectable: false },
2249
+ { value: "__info_vercel__", label: `Vercel ID: ${config2.tools.vercel?.projectId ?? "not set"}`, kind: "header", selectable: false },
2250
+ { value: "__info_gh__", label: `GitHub repo: ${config2.tools.gh?.repo ?? "not set"}`, kind: "header", selectable: false },
2251
+ { value: "__section_actions__", label: "\u26A1 Actions", kind: "header", selectable: false },
2252
+ { value: "supabase", label: "Set Supabase project ref", kind: "action" },
2253
+ { value: "vercel", label: "Set Vercel project ID", kind: "action" },
2254
+ { value: "gh", label: "Set GitHub repo", kind: "action" },
2255
+ { value: "editor", label: "Open config in editor", kind: "action" },
2256
+ ...!panelMode ? [{ value: "__back__", label: "\u2190 Back" }] : []
2257
+ ];
2258
+ const flatItems = [
2259
+ { value: "supabase", label: "Set Supabase project ref" },
2260
+ { value: "vercel", label: "Set Vercel project ID" },
2261
+ { value: "gh", label: "Set GitHub repo" },
2262
+ { value: "editor", label: "Open config in editor" },
2263
+ ...!panelMode ? [{ value: "__back__", label: "\u2190 Back" }] : []
2264
+ ];
2265
+ const handleSelect = (value) => {
2266
+ switch (value) {
2267
+ case "supabase":
2268
+ setPhase("edit-supabase-ref");
2269
+ break;
2270
+ case "vercel":
2271
+ setPhase("edit-vercel-id");
2272
+ break;
2273
+ case "gh":
2274
+ setPhase("edit-gh-repo");
2275
+ break;
2276
+ case "editor":
2277
+ openEditor(configPath.file).then(() => {
2278
+ try {
2279
+ const reloaded = getOrCreateProjectConfig();
2280
+ setConfig(reloaded);
2281
+ setFeedback("Config reloaded from file");
2282
+ } catch {
2283
+ setFeedback("Warning: could not parse config after editing");
2284
+ }
2285
+ });
2286
+ break;
2287
+ default:
2288
+ onBack();
2289
+ }
2290
+ };
2291
+ if (panelMode) {
2292
+ return /* @__PURE__ */ jsxs17(Box15, { flexDirection: "column", paddingX: 1, children: [
2293
+ /* @__PURE__ */ jsx18(Box15, { marginBottom: 1, children: /* @__PURE__ */ jsx18(Text17, { bold: true, color: inkColors.accent, children: "\u2699\uFE0F Project Config" }) }),
2294
+ /* @__PURE__ */ jsx18(Box15, { marginBottom: 1, marginLeft: 2, children: /* @__PURE__ */ jsxs17(Text17, { dimColor: true, children: [
2295
+ "Path: ",
2296
+ configPath.file
2297
+ ] }) }),
2298
+ feedback && /* @__PURE__ */ jsx18(Box15, { marginBottom: 1, children: /* @__PURE__ */ jsxs17(Text17, { color: inkColors.accent, children: [
2299
+ "\u2713 ",
2300
+ feedback
2301
+ ] }) }),
2302
+ /* @__PURE__ */ jsx18(
2303
+ SelectList,
2304
+ {
2305
+ items: configItems,
2306
+ onSelect: handleSelect,
2307
+ onCancel: onBack,
2308
+ boxedSections: true,
2309
+ width: Math.max(20, width - 4),
2310
+ maxVisible: Math.max(6, height - 6),
2311
+ isInputActive,
2312
+ arrowNavigation: true
2313
+ }
2314
+ )
2315
+ ] });
2316
+ }
2177
2317
  return /* @__PURE__ */ jsxs17(Box15, { flexDirection: "column", children: [
2178
2318
  /* @__PURE__ */ jsx18(Box15, { marginBottom: 1, children: /* @__PURE__ */ jsx18(Text17, { bold: true, color: inkColors.accent, children: "\u2699\uFE0F Project Config" }) }),
2179
2319
  /* @__PURE__ */ jsxs17(Box15, { marginBottom: 1, marginLeft: 2, flexDirection: "column", children: [
@@ -2201,46 +2341,15 @@ function ProjectConfig({
2201
2341
  /* @__PURE__ */ jsx18(
2202
2342
  SelectList,
2203
2343
  {
2204
- items: [
2205
- { value: "supabase", label: "Set Supabase project ref" },
2206
- { value: "vercel", label: "Set Vercel project ID" },
2207
- { value: "gh", label: "Set GitHub repo" },
2208
- { value: "editor", label: "Open config in editor" },
2209
- { value: "__back__", label: "\u2190 Back" }
2210
- ],
2211
- onSelect: (value) => {
2212
- switch (value) {
2213
- case "supabase":
2214
- setPhase("edit-supabase-ref");
2215
- break;
2216
- case "vercel":
2217
- setPhase("edit-vercel-id");
2218
- break;
2219
- case "gh":
2220
- setPhase("edit-gh-repo");
2221
- break;
2222
- case "editor":
2223
- openEditor(configPath.file).then(() => {
2224
- try {
2225
- const reloaded = getOrCreateProjectConfig();
2226
- setConfig(reloaded);
2227
- setFeedback("Config reloaded from file");
2228
- } catch {
2229
- setFeedback("Warning: could not parse config after editing");
2230
- }
2231
- });
2232
- break;
2233
- default:
2234
- onBack();
2235
- }
2236
- },
2344
+ items: flatItems,
2345
+ onSelect: handleSelect,
2237
2346
  onCancel: onBack,
2238
2347
  width,
2239
2348
  isInputActive,
2240
2349
  arrowNavigation: panelMode
2241
2350
  }
2242
2351
  ),
2243
- !panelMode && /* @__PURE__ */ jsx18(StatusBar, { hint: "\u2191\u2193 navigate \xB7 Enter select \xB7 Esc back", width })
2352
+ /* @__PURE__ */ jsx18(StatusBar, { hint: "\u2191\u2193 navigate \xB7 Enter select \xB7 Esc back", width })
2244
2353
  ] });
2245
2354
  }
2246
2355
 
@@ -2252,6 +2361,7 @@ function PipelineList({
2252
2361
  onNavigate,
2253
2362
  onBack,
2254
2363
  width = 80,
2364
+ height = 24,
2255
2365
  panelMode = false,
2256
2366
  isInputActive = true
2257
2367
  }) {
@@ -2299,15 +2409,17 @@ function PipelineList({
2299
2409
  icon: "\u2795",
2300
2410
  kind: "action"
2301
2411
  });
2302
- list.push({
2303
- id: "action-back",
2304
- value: "__back__",
2305
- label: "\u2190 Back",
2306
- kind: "action"
2307
- });
2412
+ if (!panelMode) {
2413
+ list.push({
2414
+ id: "action-back",
2415
+ value: "__back__",
2416
+ label: "\u2190 Back",
2417
+ kind: "action"
2418
+ });
2419
+ }
2308
2420
  return list;
2309
- }, [pipelines]);
2310
- return /* @__PURE__ */ jsxs18(Box16, { flexDirection: "column", children: [
2421
+ }, [pipelines, panelMode]);
2422
+ return /* @__PURE__ */ jsxs18(Box16, { flexDirection: "column", paddingX: panelMode ? 1 : 0, children: [
2311
2423
  /* @__PURE__ */ jsx19(Box16, { marginBottom: 1, children: /* @__PURE__ */ jsx19(Text18, { bold: true, color: inkColors.accent, children: "\u{1F517} Pipelines" }) }),
2312
2424
  /* @__PURE__ */ jsx19(
2313
2425
  SelectList,
@@ -2325,7 +2437,9 @@ function PipelineList({
2325
2437
  onNavigate("pipeline-execution", { pipelineId: value });
2326
2438
  },
2327
2439
  onCancel: onBack,
2328
- width,
2440
+ boxedSections: panelMode,
2441
+ width: panelMode ? Math.max(20, width - 4) : width,
2442
+ maxVisible: panelMode ? Math.max(6, height - 6) : void 0,
2329
2443
  isInputActive,
2330
2444
  arrowNavigation: panelMode
2331
2445
  }
@@ -2952,30 +3066,30 @@ function usePanelFocus() {
2952
3066
 
2953
3067
  // src/hooks/useSidebarItems.ts
2954
3068
  import { useMemo as useMemo8 } from "react";
2955
- function useSidebarItems(hasPins = false) {
3069
+ function useSidebarItems() {
2956
3070
  return useMemo8(() => {
2957
3071
  const items = [];
2958
- if (hasPins) {
2959
- items.push({ id: "pinned", label: "Pinned", icon: "\u{1F4CC}", type: "action" });
2960
- items.push({ id: "__sep_pinned__", label: "---", icon: "", type: "separator" });
2961
- }
3072
+ items.push({ id: "__sep_workflows__", label: "---", icon: "", type: "separator", sectionTitle: "Workflows" });
3073
+ items.push({ id: "pipelines", label: "Pipelines", icon: "\u{1F517}", type: "action", section: "workflows" });
3074
+ items.push({ id: "pinned", label: "Pinned", icon: "\u{1F4CC}", type: "action", section: "workflows" });
3075
+ items.push({ id: "custom-command", label: "Custom Cmd", icon: "\u270F\uFE0F", type: "action", section: "workflows" });
3076
+ items.push({ id: "__sep_features__", label: "---", icon: "", type: "separator", sectionTitle: "Features" });
2962
3077
  for (const feature of features) {
2963
3078
  items.push({
2964
3079
  id: feature.id,
2965
3080
  label: feature.label,
2966
3081
  icon: feature.icon,
2967
- type: "feature"
3082
+ type: "feature",
3083
+ section: "features"
2968
3084
  });
2969
3085
  }
2970
- items.push({ id: "__sep__", label: "---", icon: "", type: "separator" });
2971
- items.push({ id: "custom-command", label: "Custom Cmd", icon: "\u270F\uFE0F", type: "action" });
2972
- items.push({ id: "pipelines", label: "Pipelines", icon: "\u{1F517}", type: "action" });
2973
- items.push({ id: "tool-status", label: "Tool Status", icon: "\u{1F527}", type: "action" });
2974
- items.push({ id: "config", label: "Config", icon: "\u2699\uFE0F", type: "action" });
2975
- items.push({ id: "self-update", label: "Update", icon: "\u2B06\uFE0F", type: "action" });
2976
- items.push({ id: "exit", label: "Exit", icon: "\u{1F6AA}", type: "action" });
3086
+ items.push({ id: "__sep_system__", label: "---", icon: "", type: "separator", sectionTitle: "System" });
3087
+ items.push({ id: "tool-status", label: "Tool Status", icon: "\u{1F527}", type: "action", section: "system" });
3088
+ items.push({ id: "config", label: "Config", icon: "\u2699\uFE0F", type: "action", section: "system" });
3089
+ items.push({ id: "self-update", label: "Update", icon: "\u2B06\uFE0F", type: "action", section: "system" });
3090
+ items.push({ id: "exit", label: "Exit", icon: "\u{1F6AA}", type: "action", section: "system" });
2977
3091
  return items;
2978
- }, [hasPins]);
3092
+ }, []);
2979
3093
  }
2980
3094
 
2981
3095
  // src/hooks/useModal.ts
@@ -3061,6 +3175,19 @@ function Panel({
3061
3175
  import { useEffect as useEffect9, useMemo as useMemo9, useState as useState19 } from "react";
3062
3176
  import { Box as Box24, Text as Text25, useInput as useInput6 } from "ink";
3063
3177
  import { jsx as jsx26, jsxs as jsxs26 } from "react/jsx-runtime";
3178
+ function groupSections(items) {
3179
+ const groups = [];
3180
+ let current = null;
3181
+ for (const item of items) {
3182
+ if (item.type === "separator" && item.sectionTitle) {
3183
+ current = { title: item.sectionTitle, items: [] };
3184
+ groups.push(current);
3185
+ } else if (current) {
3186
+ current.items.push(item);
3187
+ }
3188
+ }
3189
+ return groups;
3190
+ }
3064
3191
  function Sidebar({
3065
3192
  items,
3066
3193
  selectedId,
@@ -3075,6 +3202,7 @@ function Sidebar({
3075
3202
  );
3076
3203
  const selectedIdx = selectableItems.findIndex((item) => item.id === selectedId);
3077
3204
  const [cursorIdx, setCursorIdx] = useState19(Math.max(0, selectedIdx));
3205
+ const sections = useMemo9(() => groupSections(items), [items]);
3078
3206
  useEffect9(() => {
3079
3207
  const idx = selectableItems.findIndex((item) => item.id === selectedId);
3080
3208
  if (idx >= 0) setCursorIdx(idx);
@@ -3106,29 +3234,47 @@ function Sidebar({
3106
3234
  },
3107
3235
  { isActive: isFocused }
3108
3236
  );
3109
- let selectableIdx = 0;
3110
- return /* @__PURE__ */ jsx26(Box24, { flexDirection: "column", paddingX: 1, children: items.map((item) => {
3111
- if (item.type === "separator") {
3112
- return /* @__PURE__ */ jsx26(Box24, { children: /* @__PURE__ */ jsx26(Text25, { dimColor: true, children: symbols.horizontal.repeat(10) }) }, item.id);
3113
- }
3114
- const thisIdx = selectableIdx;
3115
- selectableIdx++;
3116
- const isCursor = isFocused && thisIdx === cursorIdx;
3117
- const isActive = item.id === selectedId;
3118
- return /* @__PURE__ */ jsx26(Box24, { gap: 0, children: /* @__PURE__ */ jsxs26(
3119
- Text25,
3237
+ let flatIdx = 0;
3238
+ return /* @__PURE__ */ jsx26(Box24, { flexDirection: "column", gap: 2, children: sections.map((section) => {
3239
+ const sectionStartIdx = flatIdx;
3240
+ const sectionEndIdx = sectionStartIdx + section.items.length - 1;
3241
+ const hasCursorInSection = isFocused && cursorIdx >= sectionStartIdx && cursorIdx <= sectionEndIdx;
3242
+ const hasActiveInSection = section.items.some((item) => item.id === selectedId);
3243
+ const borderColor = hasCursorInSection || hasActiveInSection ? inkColors.accent : "#555555";
3244
+ const rendered = /* @__PURE__ */ jsxs26(
3245
+ Box24,
3120
3246
  {
3121
- color: isCursor ? inkColors.accent : isActive ? inkColors.accent : void 0,
3122
- bold: isCursor || isActive,
3123
- dimColor: !isCursor && !isActive,
3247
+ flexDirection: "column",
3248
+ borderStyle: "round",
3249
+ borderColor,
3250
+ paddingX: 1,
3124
3251
  children: [
3125
- isCursor ? `${symbols.pointerActive} ` : " ",
3126
- item.icon,
3127
- " ",
3128
- item.label
3252
+ /* @__PURE__ */ jsx26(Text25, { dimColor: true, bold: true, children: section.title }),
3253
+ section.items.map((item) => {
3254
+ const thisIdx = flatIdx;
3255
+ flatIdx++;
3256
+ const isCursor = isFocused && thisIdx === cursorIdx;
3257
+ const isActive = item.id === selectedId;
3258
+ return /* @__PURE__ */ jsx26(Box24, { gap: 0, children: /* @__PURE__ */ jsxs26(
3259
+ Text25,
3260
+ {
3261
+ color: isCursor ? inkColors.accent : isActive ? inkColors.accent : void 0,
3262
+ bold: isCursor || isActive,
3263
+ dimColor: !isCursor && !isActive,
3264
+ children: [
3265
+ isCursor ? `${symbols.pointerActive} ` : " ",
3266
+ item.icon,
3267
+ " ",
3268
+ item.label
3269
+ ]
3270
+ }
3271
+ ) }, item.id);
3272
+ })
3129
3273
  ]
3130
- }
3131
- ) }, item.id);
3274
+ },
3275
+ section.title
3276
+ );
3277
+ return rendered;
3132
3278
  }) });
3133
3279
  }
3134
3280
 
@@ -3396,6 +3542,58 @@ function PinnedCommands({
3396
3542
 
3397
3543
  // src/appPanel.tsx
3398
3544
  import { jsx as jsx31, jsxs as jsxs31 } from "react/jsx-runtime";
3545
+ var screenLabels = {
3546
+ "command-args": "Args",
3547
+ "flag-selection": "Flags",
3548
+ "confirm-execute": "Execute",
3549
+ "command-execution": "Execute",
3550
+ "custom-command": "Custom Cmd",
3551
+ "pipeline-list": "Pipelines",
3552
+ "pipeline-builder": "Builder",
3553
+ "pipeline-execution": "Run",
3554
+ "self-update": "Update",
3555
+ "tool-status": "Status",
3556
+ "project-config": "Config"
3557
+ };
3558
+ function buildBreadcrumb(nav) {
3559
+ let base;
3560
+ switch (nav.view) {
3561
+ case "feature":
3562
+ base = getFeatureById(nav.featureId)?.label ?? nav.featureId;
3563
+ break;
3564
+ case "pinned":
3565
+ base = "Pinned";
3566
+ break;
3567
+ case "custom-command":
3568
+ base = "Custom Cmd";
3569
+ break;
3570
+ case "pipelines":
3571
+ base = "Pipelines";
3572
+ break;
3573
+ case "tool-status":
3574
+ base = "Status";
3575
+ break;
3576
+ case "config":
3577
+ base = "Config";
3578
+ break;
3579
+ case "self-update":
3580
+ base = "Update";
3581
+ break;
3582
+ default:
3583
+ base = nav.view;
3584
+ }
3585
+ if (nav.innerScreen === "home") {
3586
+ return base;
3587
+ }
3588
+ const parts = [base];
3589
+ for (const entry of nav.innerStack) {
3590
+ if (entry.screen !== "home") {
3591
+ parts.push(screenLabels[entry.screen] ?? entry.screen);
3592
+ }
3593
+ }
3594
+ parts.push(screenLabels[nav.innerScreen] ?? nav.innerScreen);
3595
+ return parts.join(" \u203A ");
3596
+ }
3399
3597
  var FOOTER_HINTS = [
3400
3598
  { key: "\u2190\u2192", action: "panels" },
3401
3599
  { key: "j/k", action: "nav" },
@@ -3411,18 +3609,10 @@ function AppPanel() {
3411
3609
  const { exit } = useApp2();
3412
3610
  const nav = usePanelNavigation();
3413
3611
  const focus = usePanelFocus();
3414
- const [hasPins, setHasPins] = React18.useState(
3415
- () => getPinnedCommands().length > 0 || getPinnedRuns().length > 0
3416
- );
3417
- const sidebarItems = useSidebarItems(hasPins);
3612
+ const sidebarItems = useSidebarItems();
3418
3613
  const modal = useModal();
3419
3614
  const refreshPins = React18.useCallback(() => {
3420
- const newHasPins = getPinnedCommands().length > 0 || getPinnedRuns().length > 0;
3421
- setHasPins(newHasPins);
3422
- if (!newHasPins && nav.view === "pinned") {
3423
- nav.selectSidebarItem("database");
3424
- }
3425
- }, [nav.view, nav.selectSidebarItem]);
3615
+ }, []);
3426
3616
  const singlePanel = width < 60 || height < 15;
3427
3617
  const handleExit = () => {
3428
3618
  process.stdout.write(
@@ -3575,6 +3765,7 @@ function AppPanel() {
3575
3765
  onNavigate: nav.navigateInner,
3576
3766
  onBack: focus.focusSidebar,
3577
3767
  width: mainContentWidth - 2,
3768
+ height: mainContentHeight,
3578
3769
  panelMode: true,
3579
3770
  isInputActive: focus.isMainFocused
3580
3771
  }
@@ -3586,6 +3777,7 @@ function AppPanel() {
3586
3777
  onNavigate: nav.navigateInner,
3587
3778
  onBack: focus.focusSidebar,
3588
3779
  width: mainContentWidth - 2,
3780
+ height: mainContentHeight,
3589
3781
  panelMode: true,
3590
3782
  isInputActive: focus.isMainFocused
3591
3783
  }
@@ -3596,6 +3788,7 @@ function AppPanel() {
3596
3788
  {
3597
3789
  onBack: focus.focusSidebar,
3598
3790
  width: mainContentWidth - 2,
3791
+ height: mainContentHeight,
3599
3792
  panelMode: true,
3600
3793
  isInputActive: focus.isMainFocused
3601
3794
  }
@@ -3606,6 +3799,7 @@ function AppPanel() {
3606
3799
  {
3607
3800
  onBack: focus.focusSidebar,
3608
3801
  width: mainContentWidth - 2,
3802
+ height: mainContentHeight,
3609
3803
  panelMode: true,
3610
3804
  isInputActive: focus.isMainFocused
3611
3805
  }
@@ -3617,6 +3811,7 @@ function AppPanel() {
3617
3811
  onBack: focus.focusSidebar,
3618
3812
  onExit: handleExit,
3619
3813
  width: mainContentWidth - 2,
3814
+ height: mainContentHeight,
3620
3815
  panelMode: true,
3621
3816
  isInputActive: focus.isMainFocused
3622
3817
  }
@@ -3649,6 +3844,7 @@ function AppPanel() {
3649
3844
  onNavigate: nav.navigateInner,
3650
3845
  onBack: nav.goBackInner,
3651
3846
  width: w,
3847
+ height: mainContentHeight,
3652
3848
  panelMode: true,
3653
3849
  isInputActive: isActive
3654
3850
  }
@@ -3676,6 +3872,7 @@ function AppPanel() {
3676
3872
  onBack: nav.goBackInner,
3677
3873
  onExit: handleExit,
3678
3874
  width: w,
3875
+ height: mainContentHeight,
3679
3876
  panelMode: true,
3680
3877
  isInputActive: isActive
3681
3878
  }
@@ -3687,6 +3884,7 @@ function AppPanel() {
3687
3884
  onNavigate: nav.navigateInner,
3688
3885
  onBack: nav.goBackInner,
3689
3886
  width: w,
3887
+ height: mainContentHeight,
3690
3888
  panelMode: true,
3691
3889
  isInputActive: isActive
3692
3890
  }
@@ -3721,6 +3919,7 @@ function AppPanel() {
3721
3919
  onBack: nav.goBackInner,
3722
3920
  onExit: handleExit,
3723
3921
  width: w,
3922
+ height: mainContentHeight,
3724
3923
  panelMode: true,
3725
3924
  isInputActive: isActive
3726
3925
  }
@@ -3731,6 +3930,7 @@ function AppPanel() {
3731
3930
  {
3732
3931
  onBack: nav.goBackInner,
3733
3932
  width: w,
3933
+ height: mainContentHeight,
3734
3934
  panelMode: true,
3735
3935
  isInputActive: isActive
3736
3936
  }
@@ -3741,6 +3941,7 @@ function AppPanel() {
3741
3941
  {
3742
3942
  onBack: nav.goBackInner,
3743
3943
  width: w,
3944
+ height: mainContentHeight,
3744
3945
  panelMode: true,
3745
3946
  isInputActive: isActive
3746
3947
  }
@@ -3781,7 +3982,7 @@ function AppPanel() {
3781
3982
  Panel,
3782
3983
  {
3783
3984
  id: "main",
3784
- title: nav.view === "feature" ? getFeatureById(nav.featureId)?.label : nav.view === "pinned" ? "Pinned" : nav.view,
3985
+ title: buildBreadcrumb(nav),
3785
3986
  width: mainContentWidth,
3786
3987
  height: Math.max(5, height - bannerHeight - 1),
3787
3988
  focused: focus.isMainFocused,
@@ -3832,11 +4033,17 @@ function parseCliArgs(argv) {
3832
4033
  if (argv_[0] === "pipeline" && argv_[1] === "run" && argv_[2]) {
3833
4034
  return { mode: "pipeline-run", options: {}, pipelineName: argv_[2] };
3834
4035
  }
3835
- if (argv_[0] === "mcp" && argv_[1] === "install") {
3836
- let mcpScope = "local";
3837
- if (argv_.includes("--project")) mcpScope = "project";
3838
- else if (argv_.includes("--global")) mcpScope = "user";
3839
- return { mode: "mcp-install", options: {}, mcpScope };
4036
+ if (argv_[0] === "mcp") {
4037
+ const sub = argv_[1];
4038
+ if (sub === "status") {
4039
+ return { mode: "mcp", options: {}, mcpAction: "status" };
4040
+ }
4041
+ if (sub === "install" || sub === "update" || sub === "remove") {
4042
+ let mcpScope = "local";
4043
+ if (argv_.includes("--project")) mcpScope = "project";
4044
+ else if (argv_.includes("--global")) mcpScope = "user";
4045
+ return { mode: "mcp", options: {}, mcpAction: sub, mcpScope };
4046
+ }
3840
4047
  }
3841
4048
  if (argv_[0] === "config") {
3842
4049
  const edit = argv_.includes("--edit");
@@ -3925,6 +4132,9 @@ function printCliHelp() {
3925
4132
  " polter mcp install Install Polter MCP server into Claude Code (local scope)",
3926
4133
  " polter mcp install --project Install for this project only (shared via repo)",
3927
4134
  " polter mcp install --global Install globally for all projects",
4135
+ " polter mcp update Update MCP server to latest version",
4136
+ " polter mcp status Show MCP registration and version info",
4137
+ " polter mcp remove Remove MCP server registration",
3928
4138
  "",
3929
4139
  " polter app setup ops [--path <dir>] [--create-project|--use-existing-project] [--yes]",
3930
4140
  " polter app link ops [--path <dir>] [--relink]",
@@ -4750,8 +4960,10 @@ import { spawnSync as spawnSync2 } from "child_process";
4750
4960
  import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, existsSync as existsSync2 } from "fs";
4751
4961
  import { join as join3 } from "path";
4752
4962
  import { homedir as homedir2 } from "os";
4963
+ import { createRequire as createRequire2 } from "module";
4753
4964
  import pc4 from "picocolors";
4754
- var MCP_ARGS = ["npx", "-y", "-p", "@polterware/polter", "polter-mcp"];
4965
+ var require3 = createRequire2(import.meta.url);
4966
+ var MCP_ARGS = ["npx", "-y", "-p", "@polterware/polter@latest", "polter-mcp"];
4755
4967
  var SCOPE_LABELS = {
4756
4968
  local: "local (this machine)",
4757
4969
  project: "project (shared via repo)",
@@ -4789,7 +5001,7 @@ function tryManualInstall(scope) {
4789
5001
  const mcpServers = settings.mcpServers ?? {};
4790
5002
  mcpServers.polter = {
4791
5003
  command: "npx",
4792
- args: ["-y", "-p", "@polterware/polter", "polter-mcp"]
5004
+ args: ["-y", "-p", "@polterware/polter@latest", "polter-mcp"]
4793
5005
  };
4794
5006
  settings.mcpServers = mcpServers;
4795
5007
  writeFileSync2(settingsPath, JSON.stringify(settings, null, 2) + "\n");
@@ -4815,11 +5027,93 @@ async function installMcpServer(scope) {
4815
5027
  process.stdout.write(pc4.green("\n Done! Restart Claude Code to use Polter tools.\n"));
4816
5028
  } else {
4817
5029
  process.stderr.write(pc4.red("\n Failed to install. Add manually:\n\n"));
4818
- process.stderr.write(` ${pc4.dim(JSON.stringify({ mcpServers: { polter: { command: "npx", args: ["-y", "-p", "@polterware/polter", "polter-mcp"] } } }, null, 2))}
5030
+ process.stderr.write(` ${pc4.dim(JSON.stringify({ mcpServers: { polter: { command: "npx", args: ["-y", "-p", "@polterware/polter@latest", "polter-mcp"] } } }, null, 2))}
4819
5031
  `);
4820
5032
  process.exit(1);
4821
5033
  }
4822
5034
  }
5035
+ async function removeMcpServer(scope) {
5036
+ process.stdout.write(pc4.bold(`Removing Polter MCP server \u2014 ${SCOPE_LABELS[scope]}
5037
+
5038
+ `));
5039
+ if (commandExists("claude")) {
5040
+ const result = spawnSync2("claude", ["mcp", "remove", "-s", scope, "polter"], {
5041
+ stdio: "inherit",
5042
+ shell: true
5043
+ });
5044
+ if (result.status === 0) {
5045
+ process.stdout.write(pc4.green("\n Done! Polter MCP server removed.\n"));
5046
+ return;
5047
+ }
5048
+ process.stdout.write(pc4.yellow(" 'claude mcp remove' failed, falling back to manual removal...\n\n"));
5049
+ }
5050
+ const settingsPath = getSettingsPath(scope);
5051
+ if (!existsSync2(settingsPath)) {
5052
+ process.stdout.write(pc4.yellow(" No settings file found. Nothing to remove.\n"));
5053
+ return;
5054
+ }
5055
+ let settings;
5056
+ try {
5057
+ settings = JSON.parse(readFileSync2(settingsPath, "utf-8"));
5058
+ } catch {
5059
+ process.stderr.write(pc4.red(` Failed to parse ${settingsPath}
5060
+ `));
5061
+ process.exit(1);
5062
+ return;
5063
+ }
5064
+ const mcpServers = settings.mcpServers;
5065
+ if (!mcpServers?.polter) {
5066
+ process.stdout.write(pc4.yellow(" Polter MCP server not found in settings. Nothing to remove.\n"));
5067
+ return;
5068
+ }
5069
+ delete mcpServers.polter;
5070
+ settings.mcpServers = mcpServers;
5071
+ writeFileSync2(settingsPath, JSON.stringify(settings, null, 2) + "\n");
5072
+ process.stdout.write(pc4.green(" Done! Polter MCP server removed.\n"));
5073
+ }
5074
+ async function mcpStatus() {
5075
+ process.stdout.write(pc4.bold("Polter MCP Server Status\n\n"));
5076
+ const pkg = require3("../../package.json");
5077
+ process.stdout.write(` Installed version: ${pc4.cyan(pkg.version)}
5078
+ `);
5079
+ const latestResult = spawnSync2("npm", ["view", "@polterware/polter", "version"], {
5080
+ encoding: "utf-8",
5081
+ shell: true,
5082
+ timeout: 1e4
5083
+ });
5084
+ const latest = latestResult.stdout?.trim();
5085
+ if (latest) {
5086
+ const upToDate = latest === pkg.version;
5087
+ process.stdout.write(` Latest version: ${upToDate ? pc4.green(latest) : pc4.yellow(`${latest} (update available)`)}
5088
+ `);
5089
+ }
5090
+ process.stdout.write("\n");
5091
+ const scopes = [
5092
+ { label: "Project (.mcp.json)", path: join3(process.cwd(), ".mcp.json"), key: "project" },
5093
+ { label: "User (~/.claude/settings.json)", path: join3(homedir2(), ".claude", "settings.json"), key: "user" }
5094
+ ];
5095
+ for (const scope of scopes) {
5096
+ if (!existsSync2(scope.path)) {
5097
+ process.stdout.write(` ${scope.label}: ${pc4.dim("not found")}
5098
+ `);
5099
+ continue;
5100
+ }
5101
+ try {
5102
+ const settings = JSON.parse(readFileSync2(scope.path, "utf-8"));
5103
+ const mcpServers = settings.mcpServers;
5104
+ if (mcpServers?.polter) {
5105
+ process.stdout.write(` ${scope.label}: ${pc4.green("registered")}
5106
+ `);
5107
+ } else {
5108
+ process.stdout.write(` ${scope.label}: ${pc4.dim("not registered")}
5109
+ `);
5110
+ }
5111
+ } catch {
5112
+ process.stdout.write(` ${scope.label}: ${pc4.red("error reading file")}
5113
+ `);
5114
+ }
5115
+ }
5116
+ }
4823
5117
 
4824
5118
  // src/index.tsx
4825
5119
  import pc5 from "picocolors";
@@ -4829,8 +5123,20 @@ async function main() {
4829
5123
  printCliHelp();
4830
5124
  return;
4831
5125
  }
4832
- if (parsed.mode === "mcp-install") {
4833
- await installMcpServer(parsed.mcpScope ?? "local");
5126
+ if (parsed.mode === "mcp") {
5127
+ const scope = parsed.mcpScope ?? "local";
5128
+ switch (parsed.mcpAction) {
5129
+ case "install":
5130
+ case "update":
5131
+ await installMcpServer(scope);
5132
+ break;
5133
+ case "remove":
5134
+ await removeMcpServer(scope);
5135
+ break;
5136
+ case "status":
5137
+ await mcpStatus();
5138
+ break;
5139
+ }
4834
5140
  return;
4835
5141
  }
4836
5142
  if (parsed.mode === "app") {
@@ -4867,7 +5173,7 @@ Pipeline completed with ${errors.length} error(s)
4867
5173
  return;
4868
5174
  }
4869
5175
  if (parsed.mode === "status") {
4870
- const tools = ["supabase", "gh", "vercel"];
5176
+ const tools = ["supabase", "gh", "vercel", "git"];
4871
5177
  process.stdout.write(pc5.bold("Tool Status\n\n"));
4872
5178
  for (const toolId of tools) {
4873
5179
  const info = getToolInfo(toolId);