@polterware/polter 0.2.4 → 0.3.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,37 +3,49 @@ import {
3
3
  allCommands,
4
4
  applyActions,
5
5
  commandExists,
6
+ detectPkgManager,
6
7
  executePipeline,
7
8
  features,
8
9
  findCommandByValue,
9
10
  findNearestPackageRoot,
10
11
  findPipelineByName,
12
+ findPolterYaml,
11
13
  getAllPipelines,
12
14
  getCommandById,
13
15
  getCommandValue,
14
16
  getCurrentStatus,
15
17
  getFeatureById,
16
18
  getFlagsForTool,
19
+ getMcpStatusInfo,
17
20
  getOrCreateProjectConfig,
21
+ getProcessOutput,
18
22
  getProjectConfigPath,
19
23
  getToolInfo,
20
24
  getToolLinkInfo,
25
+ installMcpServer,
26
+ installMcpServerSilent,
27
+ listProcesses,
28
+ mcpStatus,
21
29
  parsePolterYaml,
22
30
  planChanges,
31
+ removeMcpServer,
32
+ removeMcpServerSilent,
33
+ removeProcess,
23
34
  resolveToolCommand,
24
35
  runCommand,
25
36
  runInteractiveCommand,
26
37
  runSupabaseCommand,
27
38
  savePipeline,
39
+ stopProcess,
28
40
  writeProjectConfig
29
- } from "./chunk-2B2UWOVQ.js";
41
+ } from "./chunk-7MIUDIAI.js";
30
42
 
31
43
  // src/index.tsx
32
- import React20 from "react";
44
+ import React27 from "react";
33
45
  import { render } from "ink";
34
46
 
35
47
  // src/app.tsx
36
- import { Box as Box22, Text as Text26, useApp } from "ink";
48
+ import { Box as Box28, Text as Text32, useApp } from "ink";
37
49
 
38
50
  // src/hooks/useNavigation.ts
39
51
  import { useState, useCallback } from "react";
@@ -99,7 +111,7 @@ function useTerminalHeight() {
99
111
 
100
112
  // src/components/GhostBanner.tsx
101
113
  import React from "react";
102
- import { Box, Text } from "ink";
114
+ import { Box, Text as Text2 } from "ink";
103
115
 
104
116
  // src/theme.ts
105
117
  import { createRequire } from "module";
@@ -175,56 +187,93 @@ var ghost = {
175
187
  ]
176
188
  };
177
189
 
190
+ // src/components/ToolBadge.tsx
191
+ import { Text } from "ink";
192
+ import { jsx } from "react/jsx-runtime";
193
+ var toolColors = {
194
+ supabase: "#3ECF8E",
195
+ gh: "#58A6FF",
196
+ vercel: "#FFFFFF",
197
+ git: "#F05032",
198
+ pkg: "#CB3837"
199
+ };
200
+ var toolLabels = {
201
+ supabase: "supabase",
202
+ gh: "github",
203
+ vercel: "vercel",
204
+ git: "git",
205
+ pkg: "pkg"
206
+ };
207
+ function ToolBadge({ tool }) {
208
+ return /* @__PURE__ */ jsx(Text, { color: toolColors[tool], dimColor: true, children: toolLabels[tool] });
209
+ }
210
+
178
211
  // src/components/GhostBanner.tsx
179
- import { jsx, jsxs } from "react/jsx-runtime";
212
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
213
+ var McpBadge = React.memo(function McpBadge2() {
214
+ const info = getMcpStatusInfo();
215
+ const registered = info.scopes.some((s) => s.registered);
216
+ const color = registered ? "#3ECF8E" : "red";
217
+ return /* @__PURE__ */ jsx2(Box, { borderStyle: "round", borderColor: color, children: /* @__PURE__ */ jsxs(Text2, { color, dimColor: !registered, children: [
218
+ "mcp:",
219
+ registered ? "ok" : "x"
220
+ ] }) });
221
+ });
180
222
  var ToolStatusBadges = React.memo(function ToolStatusBadges2() {
181
223
  const tools = ["supabase", "gh", "vercel"].map((id) => getToolLinkInfo(id));
182
- return /* @__PURE__ */ jsx(Box, { gap: 1, children: tools.map((t) => /* @__PURE__ */ jsxs(
183
- Text,
184
- {
185
- color: t.linked ? inkColors.accent : t.installed ? "yellow" : "red",
186
- dimColor: !t.installed,
187
- children: [
224
+ return /* @__PURE__ */ jsxs(Box, { gap: 1, children: [
225
+ tools.map((t) => {
226
+ const color = t.linked ? toolColors[t.id] : t.installed ? "yellow" : "red";
227
+ return /* @__PURE__ */ jsx2(Box, { borderStyle: "round", borderColor: color, children: /* @__PURE__ */ jsxs(Text2, { color, dimColor: !t.installed, children: [
188
228
  t.id,
189
229
  ":",
190
230
  t.linked ? "linked" : t.installed ? "ok" : "x"
191
- ]
192
- },
193
- t.id
194
- )) });
231
+ ] }) }, t.id);
232
+ }),
233
+ /* @__PURE__ */ jsx2(McpBadge, {})
234
+ ] });
195
235
  });
196
236
  function GhostBanner({ width = 80, compact = false }) {
197
237
  if (compact) {
198
238
  if (width < 60) {
199
- return /* @__PURE__ */ jsxs(Box, { children: [
200
- /* @__PURE__ */ jsx(Text, { color: inkColors.accent, bold: true, children: "POLTER" }),
201
- /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
202
- " v",
203
- VERSION,
204
- " "
239
+ 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
245
+ ] }),
246
+ /* @__PURE__ */ jsx2(Text2, { color: "yellow", children: "alpha" })
205
247
  ] }),
206
- /* @__PURE__ */ jsx(ToolStatusBadges, {})
248
+ /* @__PURE__ */ jsx2(Box, { borderStyle: "round", borderColor: inkColors.accent, paddingX: 1, children: /* @__PURE__ */ jsx2(ToolStatusBadges, {}) })
207
249
  ] });
208
250
  }
209
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 2, children: [
210
- /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: ghost.art.map((line, i) => /* @__PURE__ */ jsx(Text, { color: inkColors.accent, children: line }, i)) }),
211
- /* @__PURE__ */ jsxs(Box, { flexDirection: "column", justifyContent: "center", children: [
212
- /* @__PURE__ */ jsx(Text, { color: inkColors.accent, bold: true, children: "POLTER" }),
213
- /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
214
- "v",
215
- VERSION
251
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "row", borderStyle: "round", borderColor: inkColors.accent, gap: 1, alignItems: "flex-start", children: [
252
+ /* @__PURE__ */ jsx2(Box, { flexDirection: "column", children: ghost.art.map((line, i) => /* @__PURE__ */ jsx2(Text2, { color: inkColors.accent, children: line }, i)) }),
253
+ /* @__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
259
+ ] }),
260
+ /* @__PURE__ */ jsx2(Text2, { color: "yellow", children: "alpha" })
216
261
  ] }),
217
- /* @__PURE__ */ jsx(ToolStatusBadges, {})
262
+ /* @__PURE__ */ jsx2(Box, { borderStyle: "round", borderColor: inkColors.accent, paddingX: 1, children: /* @__PURE__ */ jsx2(ToolStatusBadges, {}) })
218
263
  ] })
219
264
  ] });
220
265
  }
221
266
  if (width < 50) {
222
- return /* @__PURE__ */ jsxs(Box, { marginBottom: 1, children: [
223
- /* @__PURE__ */ jsx(Text, { color: inkColors.accent, bold: true, children: "POLTER" }),
224
- /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
225
- " v",
226
- VERSION
227
- ] })
267
+ 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
273
+ ] }),
274
+ /* @__PURE__ */ jsx2(Text2, { color: "yellow", children: "alpha" })
275
+ ] }),
276
+ /* @__PURE__ */ jsx2(Box, { borderStyle: "round", borderColor: inkColors.accent, paddingX: 1, children: /* @__PURE__ */ jsx2(ToolStatusBadges, {}) })
228
277
  ] });
229
278
  }
230
279
  if (width < 80) {
@@ -232,76 +281,59 @@ function GhostBanner({ width = 80, compact = false }) {
232
281
  Box,
233
282
  {
234
283
  flexDirection: "column",
235
- borderStyle: "single",
284
+ alignItems: "flex-start",
285
+ borderStyle: "round",
236
286
  borderColor: inkColors.accent,
237
- paddingX: 1,
238
287
  marginBottom: 1,
239
288
  children: [
240
- /* @__PURE__ */ jsx(
241
- Text,
242
- {
243
- backgroundColor: inkColors.accent,
244
- color: inkColors.accentContrast,
245
- bold: true,
246
- children: " POLTER "
247
- }
248
- ),
249
- /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
250
- "Version: ",
251
- VERSION
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
294
+ ] }),
295
+ /* @__PURE__ */ jsx2(Text2, { color: "yellow", children: "alpha" })
252
296
  ] }),
253
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "Project & infrastructure orchestrator" })
297
+ /* @__PURE__ */ jsx2(Box, { borderStyle: "round", borderColor: inkColors.accent, paddingX: 1, children: /* @__PURE__ */ jsx2(ToolStatusBadges, {}) }),
298
+ /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "Project & infrastructure orchestrator" })
254
299
  ]
255
300
  }
256
301
  );
257
302
  }
258
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "row", alignItems: "flex-start", gap: 2, marginBottom: 1, children: [
259
- /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: ghost.art.map((line, i) => /* @__PURE__ */ jsx(Text, { color: inkColors.accent, children: line }, i)) }),
260
- /* @__PURE__ */ jsxs(
261
- Box,
262
- {
263
- flexDirection: "column",
264
- borderStyle: "single",
265
- borderColor: inkColors.accent,
266
- paddingX: 1,
267
- children: [
268
- /* @__PURE__ */ jsx(
269
- Text,
270
- {
271
- backgroundColor: inkColors.accent,
272
- color: inkColors.accentContrast,
273
- bold: true,
274
- children: " POLTER "
275
- }
276
- ),
277
- /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
278
- "Version: ",
279
- VERSION
280
- ] }),
281
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "Project & infrastructure orchestrator" })
282
- ]
283
- }
284
- )
303
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "row", alignItems: "flex-start", borderStyle: "round", borderColor: inkColors.accent, gap: 1, marginBottom: 1, children: [
304
+ /* @__PURE__ */ jsx2(Box, { flexDirection: "column", children: ghost.art.map((line, i) => /* @__PURE__ */ jsx2(Text2, { color: inkColors.accent, children: line }, i)) }),
305
+ /* @__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
311
+ ] }),
312
+ /* @__PURE__ */ jsx2(Text2, { color: "yellow", children: "alpha" })
313
+ ] }),
314
+ /* @__PURE__ */ jsx2(Box, { borderStyle: "round", borderColor: inkColors.accent, paddingX: 1, children: /* @__PURE__ */ jsx2(ToolStatusBadges, {}) }),
315
+ /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: " Project & infrastructure orchestrator" })
316
+ ] })
285
317
  ] });
286
318
  }
287
319
 
288
320
  // src/screens/Home.tsx
289
321
  import { useEffect as useEffect3, useMemo as useMemo2, useState as useState4 } from "react";
290
- import { Box as Box5, Text as Text5, useInput as useInput2 } from "ink";
322
+ import { Box as Box5, Text as Text6, useInput as useInput2 } from "ink";
291
323
 
292
324
  // src/components/TabBar.tsx
293
- import { Box as Box2, Text as Text2 } from "ink";
294
- import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
325
+ import { Box as Box2, Text as Text3 } from "ink";
326
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
295
327
  function TabBar({ tabs, activeIndex, width = 80 }) {
296
328
  const narrow = width < 50;
297
329
  const medium = width < 80;
298
- return /* @__PURE__ */ jsx2(Box2, { gap: 1, children: tabs.map((tab, i) => {
330
+ return /* @__PURE__ */ jsx3(Box2, { gap: 1, children: tabs.map((tab, i) => {
299
331
  const isActive = i === activeIndex;
300
332
  const showLabel = narrow ? false : medium ? isActive : true;
301
333
  const displayText = showLabel ? `${tab.icon} ${tab.label}` : tab.icon;
302
334
  return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
303
- /* @__PURE__ */ jsx2(
304
- Text2,
335
+ /* @__PURE__ */ jsx3(
336
+ Text3,
305
337
  {
306
338
  color: isActive ? inkColors.accent : void 0,
307
339
  bold: isActive,
@@ -309,14 +341,14 @@ function TabBar({ tabs, activeIndex, width = 80 }) {
309
341
  children: displayText
310
342
  }
311
343
  ),
312
- isActive && !narrow && /* @__PURE__ */ jsx2(Text2, { color: inkColors.accent, children: "\u2550".repeat(displayText.length) })
344
+ isActive && !narrow && /* @__PURE__ */ jsx3(Text3, { color: inkColors.accent, children: "\u2550".repeat(displayText.length) })
313
345
  ] }, tab.id);
314
346
  }) });
315
347
  }
316
348
 
317
349
  // src/components/SelectList.tsx
318
350
  import { useEffect as useEffect2, useMemo, useState as useState3 } from "react";
319
- import { Box as Box3, Text as Text3, useInput } from "ink";
351
+ import { Box as Box3, Text as Text4, useInput } from "ink";
320
352
 
321
353
  // src/components/selectListSections.ts
322
354
  function isHeader(item) {
@@ -394,7 +426,7 @@ function countBoxedSectionLines(sections) {
394
426
  }
395
427
 
396
428
  // src/components/SelectList.tsx
397
- import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
429
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
398
430
  function SelectList({
399
431
  items,
400
432
  onSelect,
@@ -534,9 +566,9 @@ function SelectList({
534
566
  const renderSelectableRow = (item, globalIdx) => {
535
567
  const isSelected = globalIdx === selectedItemIndex;
536
568
  return /* @__PURE__ */ jsxs3(Box3, { gap: 1, children: [
537
- /* @__PURE__ */ jsx3(Text3, { color: isSelected ? inkColors.accent : void 0, children: isSelected ? "\u276F" : " " }),
538
- /* @__PURE__ */ jsx3(Box3, { width: labelWidth, children: /* @__PURE__ */ jsxs3(
539
- Text3,
569
+ /* @__PURE__ */ jsx4(Text4, { color: isSelected ? inkColors.accent : void 0, children: isSelected ? "\u276F" : " " }),
570
+ /* @__PURE__ */ jsx4(Box3, { width: labelWidth, children: /* @__PURE__ */ jsxs3(
571
+ Text4,
540
572
  {
541
573
  color: isSelected ? inkColors.accent : isPinnedRow(item) ? "white" : void 0,
542
574
  bold: isSelected || isPinnedRow(item),
@@ -547,14 +579,14 @@ function SelectList({
547
579
  ]
548
580
  }
549
581
  ) }),
550
- !isNarrow && item.hint && /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: item.hint })
582
+ !isNarrow && item.hint && /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: item.hint })
551
583
  ] }, item.id ?? `${item.value}-${globalIdx}`);
552
584
  };
553
585
  return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
554
- showScrollUp && /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: " \u2191 more" }),
586
+ showScrollUp && /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: " \u2191 more" }),
555
587
  boxedSections ? boxedLayout.map((section) => {
556
588
  if (section.type === "heading") {
557
- return /* @__PURE__ */ jsx3(Box3, { children: /* @__PURE__ */ jsx3(Text3, { color: inkColors.accent, bold: true, children: section.label }) }, section.key);
589
+ return /* @__PURE__ */ jsx4(Box3, { children: /* @__PURE__ */ jsx4(Text4, { color: inkColors.accent, bold: true, children: section.label }) }, section.key);
558
590
  }
559
591
  const hasSelectedRow = section.rows.some(
560
592
  (row) => row.globalIndex === selectedItemIndex
@@ -571,8 +603,8 @@ function SelectList({
571
603
  borderDimColor: !hasSelectedRow && !isPinnedSection,
572
604
  paddingX: 1,
573
605
  children: [
574
- section.title && /* @__PURE__ */ jsx3(Text3, { color: inkColors.accent, bold: true, children: section.title }),
575
- /* @__PURE__ */ jsx3(Box3, { flexDirection: "column", children: section.rows.map(
606
+ section.title && /* @__PURE__ */ jsx4(Text4, { color: inkColors.accent, bold: true, children: section.title }),
607
+ /* @__PURE__ */ jsx4(Box3, { flexDirection: "column", children: section.rows.map(
576
608
  (row) => renderSelectableRow(row.item, row.globalIndex)
577
609
  ) })
578
610
  ]
@@ -583,28 +615,28 @@ function SelectList({
583
615
  const globalIdx = windowStart + i;
584
616
  const selectable = isSelectable(item);
585
617
  if (!selectable) {
586
- return /* @__PURE__ */ jsx3(
618
+ return /* @__PURE__ */ jsx4(
587
619
  Box3,
588
620
  {
589
621
  marginTop: i === 0 ? 0 : 1,
590
- children: /* @__PURE__ */ jsx3(Text3, { color: inkColors.accent, bold: true, children: item.label })
622
+ children: /* @__PURE__ */ jsx4(Text4, { color: inkColors.accent, bold: true, children: item.label })
591
623
  },
592
624
  item.id ?? `${item.value}-${globalIdx}`
593
625
  );
594
626
  }
595
627
  return renderSelectableRow(item, globalIdx);
596
628
  }),
597
- showScrollDown && /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: " \u2193 more" })
629
+ showScrollDown && /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: " \u2193 more" })
598
630
  ] });
599
631
  }
600
632
 
601
633
  // src/components/StatusBar.tsx
602
- import { Box as Box4, Text as Text4 } from "ink";
603
- import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
634
+ import { Box as Box4, Text as Text5 } from "ink";
635
+ import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
604
636
  function StatusBar({ hint, width = 80 }) {
605
637
  return /* @__PURE__ */ jsxs4(Box4, { marginTop: 1, justifyContent: "space-between", children: [
606
- /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: hint || "\u2191\u2193 navigate \xB7 Enter select \xB7 Esc back" }),
607
- width >= 60 && /* @__PURE__ */ jsxs4(Text4, { dimColor: true, children: [
638
+ /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: hint || "\u2191\u2193 navigate \xB7 Enter select \xB7 Esc back" }),
639
+ width >= 60 && /* @__PURE__ */ jsxs4(Text5, { dimColor: true, children: [
608
640
  "polter v",
609
641
  VERSION
610
642
  ] })
@@ -673,13 +705,6 @@ function getFeatures() {
673
705
  function buildPinnedOnlyItems(pinnedCommands, pinnedRuns) {
674
706
  const items = [];
675
707
  if (pinnedCommands.length > 0) {
676
- items.push({
677
- id: "pinned-commands-header",
678
- value: "__pinned_commands_header__",
679
- label: "\u{1F4CC} Commands",
680
- kind: "header",
681
- selectable: false
682
- });
683
708
  for (const command of pinnedCommands) {
684
709
  const cmdDef = findCommandByValue(command);
685
710
  const toolHint = cmdDef ? cmdDef.tool : "supabase";
@@ -687,7 +712,7 @@ function buildPinnedOnlyItems(pinnedCommands, pinnedRuns) {
687
712
  items.push({
688
713
  id: `command:${command}`,
689
714
  value: command,
690
- label: command,
715
+ label: cmdDef ? `${cmdDef.tool} ${command}` : command,
691
716
  hint: [toolHint, labelHint].filter(Boolean).join(" \xB7 "),
692
717
  icon: "\u{1F4CC}",
693
718
  kind: "command",
@@ -697,13 +722,6 @@ function buildPinnedOnlyItems(pinnedCommands, pinnedRuns) {
697
722
  }
698
723
  }
699
724
  if (pinnedRuns.length > 0) {
700
- items.push({
701
- id: "pinned-runs-header",
702
- value: "__pinned_runs_header__",
703
- label: "\u25B6 Pipelines",
704
- kind: "header",
705
- selectable: false
706
- });
707
725
  for (const runCommand2 of pinnedRuns) {
708
726
  const baseCommand = runCommand2.split(" ").filter(Boolean)[0] ?? "";
709
727
  const cmdDef = findCommandByValue(baseCommand);
@@ -711,7 +729,7 @@ function buildPinnedOnlyItems(pinnedCommands, pinnedRuns) {
711
729
  items.push({
712
730
  id: `run:${runCommand2}`,
713
731
  value: runCommand2,
714
- label: runCommand2,
732
+ label: cmdDef ? `${cmdDef.tool} ${runCommand2}` : runCommand2,
715
733
  hint: toolHint,
716
734
  icon: "\u25B6",
717
735
  kind: "run",
@@ -785,11 +803,79 @@ function buildHomeItems({
785
803
  });
786
804
  }
787
805
  }
806
+ items.push({
807
+ id: "section-system",
808
+ value: "__section_system__",
809
+ label: "\u2699\uFE0F System",
810
+ kind: "header",
811
+ selectable: false
812
+ });
813
+ items.push({
814
+ id: "action-declarative-plan",
815
+ value: "__action_declarative_plan__",
816
+ label: "Plan / Apply",
817
+ hint: "Declarative infrastructure",
818
+ kind: "action"
819
+ });
820
+ items.push({
821
+ id: "action-declarative-status",
822
+ value: "__action_declarative_status__",
823
+ label: "Infra Status",
824
+ hint: "Live state from CLI tools",
825
+ kind: "action"
826
+ });
827
+ items.push({
828
+ id: "action-init-scaffold",
829
+ value: "__action_init_scaffold__",
830
+ label: "Init polter.yaml",
831
+ hint: "Generate from detected state",
832
+ kind: "action"
833
+ });
834
+ items.push({
835
+ id: "action-pipelines",
836
+ value: "__action_pipelines__",
837
+ label: "Pipelines",
838
+ hint: "Multi-step workflows",
839
+ kind: "action"
840
+ });
841
+ items.push({
842
+ id: "action-tools",
843
+ value: "__action_tools__",
844
+ label: "Tool Status",
845
+ hint: "Check installed CLI tools",
846
+ kind: "action"
847
+ });
848
+ items.push({
849
+ id: "action-config",
850
+ value: "__action_config__",
851
+ label: "Project Config",
852
+ hint: "Tool refs and settings",
853
+ kind: "action"
854
+ });
855
+ items.push({
856
+ id: "action-custom",
857
+ value: "__action_custom__",
858
+ label: "Custom Command",
859
+ hint: "Free-form args",
860
+ kind: "action"
861
+ });
862
+ items.push({
863
+ id: "action-update",
864
+ value: "__action_update__",
865
+ label: "Update Polter",
866
+ kind: "action"
867
+ });
868
+ items.push({
869
+ id: "action-exit",
870
+ value: "__action_exit__",
871
+ label: "Exit",
872
+ kind: "action"
873
+ });
788
874
  return items;
789
875
  }
790
876
 
791
877
  // src/screens/Home.tsx
792
- import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
878
+ import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
793
879
  function Home({
794
880
  onNavigate,
795
881
  onExit,
@@ -890,6 +976,15 @@ function Home({
890
976
  case "__action_update__":
891
977
  onNavigate("self-update");
892
978
  break;
979
+ case "__action_declarative_plan__":
980
+ onNavigate("declarative-plan");
981
+ break;
982
+ case "__action_declarative_status__":
983
+ onNavigate("declarative-status");
984
+ break;
985
+ case "__action_init_scaffold__":
986
+ onNavigate("init-scaffold");
987
+ break;
893
988
  case "__action_exit__":
894
989
  onExit();
895
990
  break;
@@ -915,12 +1010,12 @@ function Home({
915
1010
  }
916
1011
  };
917
1012
  return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", children: [
918
- /* @__PURE__ */ jsx5(Box5, { marginBottom: 1, children: /* @__PURE__ */ jsx5(TabBar, { tabs, activeIndex: activeTabIndex, width }) }),
919
- pinFeedback && /* @__PURE__ */ jsx5(Box5, { marginBottom: 1, children: /* @__PURE__ */ jsxs5(Text5, { color: inkColors.accent, children: [
1013
+ /* @__PURE__ */ jsx6(Box5, { marginBottom: 1, children: /* @__PURE__ */ jsx6(TabBar, { tabs, activeIndex: activeTabIndex, width }) }),
1014
+ pinFeedback && /* @__PURE__ */ jsx6(Box5, { marginBottom: 1, children: /* @__PURE__ */ jsxs5(Text6, { color: inkColors.accent, children: [
920
1015
  "\u2713 ",
921
1016
  pinFeedback
922
1017
  ] }) }),
923
- /* @__PURE__ */ jsx5(
1018
+ /* @__PURE__ */ jsx6(
924
1019
  SelectList,
925
1020
  {
926
1021
  items,
@@ -931,7 +1026,7 @@ function Home({
931
1026
  maxVisible: Math.max(8, height - 14)
932
1027
  }
933
1028
  ),
934
- /* @__PURE__ */ jsx5(StatusBar, { hint: "\u2190\u2192 tab \xB7 \u2191\u2193 navigate \xB7 Enter select \xB7 p pin", width })
1029
+ /* @__PURE__ */ jsx6(StatusBar, { hint: "\u2190\u2192 tab \xB7 \u2191\u2193 navigate \xB7 Enter select \xB7 p pin", width })
935
1030
  ] });
936
1031
  }
937
1032
 
@@ -941,9 +1036,9 @@ import { Box as Box7, Text as Text8, useInput as useInput4 } from "ink";
941
1036
 
942
1037
  // src/components/TextPrompt.tsx
943
1038
  import { useState as useState5 } from "react";
944
- import { Box as Box6, Text as Text6, useInput as useInput3 } from "ink";
1039
+ import { Box as Box6, Text as Text7, useInput as useInput3 } from "ink";
945
1040
  import TextInputComponent from "ink-text-input";
946
- import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
1041
+ import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
947
1042
  function TextPrompt({
948
1043
  label,
949
1044
  placeholder,
@@ -964,6 +1059,9 @@ function TextPrompt({
964
1059
  if (arrowNavigation && key.leftArrow && value === "" && onCancel) {
965
1060
  onCancel();
966
1061
  }
1062
+ if (arrowNavigation && key.rightArrow) {
1063
+ handleSubmit(value);
1064
+ }
967
1065
  }, { isActive: isInputActive });
968
1066
  const handleSubmit = (val) => {
969
1067
  if (validate) {
@@ -978,12 +1076,12 @@ function TextPrompt({
978
1076
  };
979
1077
  const content = /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
980
1078
  /* @__PURE__ */ jsxs6(Box6, { gap: 1, children: [
981
- /* @__PURE__ */ jsx6(Text6, { color: inkColors.accent, bold: true, children: "?" }),
982
- /* @__PURE__ */ jsx6(Text6, { children: label })
1079
+ /* @__PURE__ */ jsx7(Text7, { color: inkColors.accent, bold: true, children: "?" }),
1080
+ /* @__PURE__ */ jsx7(Text7, { children: label })
983
1081
  ] }),
984
1082
  /* @__PURE__ */ jsxs6(Box6, { gap: 1, marginLeft: 2, children: [
985
- /* @__PURE__ */ jsx6(Text6, { color: inkColors.accent, children: "\u276F" }),
986
- /* @__PURE__ */ jsx6(
1083
+ /* @__PURE__ */ jsx7(Text7, { color: inkColors.accent, children: "\u276F" }),
1084
+ /* @__PURE__ */ jsx7(
987
1085
  TextInputComponent,
988
1086
  {
989
1087
  value,
@@ -996,36 +1094,17 @@ function TextPrompt({
996
1094
  }
997
1095
  )
998
1096
  ] }),
999
- error && /* @__PURE__ */ jsx6(Box6, { marginLeft: 2, children: /* @__PURE__ */ jsxs6(Text6, { color: "red", children: [
1097
+ error && /* @__PURE__ */ jsx7(Box6, { marginLeft: 2, children: /* @__PURE__ */ jsxs6(Text7, { color: "red", children: [
1000
1098
  "\u2717 ",
1001
1099
  error
1002
1100
  ] }) })
1003
1101
  ] });
1004
1102
  if (boxed) {
1005
- return /* @__PURE__ */ jsx6(Box6, { borderStyle: "round", borderColor: focused ? inkColors.accent : panel.borderDim, paddingX: 1, children: content });
1103
+ return /* @__PURE__ */ jsx7(Box6, { borderStyle: "round", borderColor: focused ? inkColors.accent : panel.borderDim, paddingX: 1, children: content });
1006
1104
  }
1007
1105
  return content;
1008
1106
  }
1009
1107
 
1010
- // src/components/ToolBadge.tsx
1011
- import { Text as Text7 } from "ink";
1012
- import { jsx as jsx7 } from "react/jsx-runtime";
1013
- var toolColors = {
1014
- supabase: "#3ECF8E",
1015
- gh: "#58A6FF",
1016
- vercel: "#FFFFFF",
1017
- git: "#F05032"
1018
- };
1019
- var toolLabels = {
1020
- supabase: "supabase",
1021
- gh: "github",
1022
- vercel: "vercel",
1023
- git: "git"
1024
- };
1025
- function ToolBadge({ tool }) {
1026
- return /* @__PURE__ */ jsx7(Text7, { color: toolColors[tool], dimColor: true, children: toolLabels[tool] });
1027
- }
1028
-
1029
1108
  // src/screens/commandArgsModel.ts
1030
1109
  function buildRunCommand(command, extraArgs) {
1031
1110
  return [command, ...extraArgs].join(" ");
@@ -1490,8 +1569,8 @@ function FlagSelection({
1490
1569
  }
1491
1570
 
1492
1571
  // src/screens/CommandExecution.tsx
1493
- import { useState as useState11, useEffect as useEffect6 } from "react";
1494
- import { Box as Box13, Text as Text17 } from "ink";
1572
+ import { useState as useState11, useEffect as useEffect7 } from "react";
1573
+ import { Box as Box13, Text as Text17, useInput as useInput8 } from "ink";
1495
1574
 
1496
1575
  // src/components/Spinner.tsx
1497
1576
  import { Text as Text12 } from "ink";
@@ -1636,10 +1715,17 @@ function CommandOutput({
1636
1715
  }
1637
1716
 
1638
1717
  // src/hooks/useCommand.ts
1639
- import { useState as useState10, useCallback as useCallback2 } from "react";
1718
+ import { useState as useState10, useCallback as useCallback2, useRef, useEffect as useEffect5 } from "react";
1640
1719
  function useCommand(execution = "supabase", cwd = process.cwd(), options) {
1641
1720
  const [status, setStatus] = useState10("idle");
1642
1721
  const [result, setResult] = useState10(null);
1722
+ const abortRef = useRef(null);
1723
+ useEffect5(() => {
1724
+ return () => {
1725
+ abortRef.current?.();
1726
+ abortRef.current = null;
1727
+ };
1728
+ }, []);
1643
1729
  const run = useCallback2(async (args) => {
1644
1730
  setStatus("running");
1645
1731
  setResult(null);
@@ -1656,7 +1742,10 @@ function useCommand(execution = "supabase", cwd = process.cwd(), options) {
1656
1742
  resolvedExecution = execution;
1657
1743
  }
1658
1744
  const runOpts = options?.quiet ? { quiet: true } : void 0;
1659
- const res = await runCommand(resolvedExecution, args, cwd, runOpts);
1745
+ const handle = runCommand(resolvedExecution, args, cwd, runOpts);
1746
+ abortRef.current = handle.abort;
1747
+ const res = await handle.promise;
1748
+ abortRef.current = null;
1660
1749
  setResult(res);
1661
1750
  if (res.spawnError || res.exitCode !== null && res.exitCode !== 0) {
1662
1751
  setStatus("error");
@@ -1665,24 +1754,28 @@ function useCommand(execution = "supabase", cwd = process.cwd(), options) {
1665
1754
  }
1666
1755
  return res;
1667
1756
  }, [cwd, execution, options?.quiet]);
1757
+ const abort = useCallback2(() => {
1758
+ abortRef.current?.();
1759
+ abortRef.current = null;
1760
+ }, []);
1668
1761
  const reset = useCallback2(() => {
1669
1762
  setStatus("idle");
1670
1763
  setResult(null);
1671
1764
  }, []);
1672
- return { status, result, run, reset };
1765
+ return { status, result, run, reset, abort };
1673
1766
  }
1674
1767
 
1675
1768
  // src/hooks/useInteractiveRun.ts
1676
1769
  import { useCallback as useCallback3 } from "react";
1677
1770
 
1678
1771
  // src/hooks/useFullscreen.ts
1679
- import { useEffect as useEffect5 } from "react";
1772
+ import { useEffect as useEffect6 } from "react";
1680
1773
  var ENTER_ALT_SCREEN = "\x1B[?1049h";
1681
1774
  var LEAVE_ALT_SCREEN = "\x1B[?1049l";
1682
1775
  var HIDE_CURSOR = "\x1B[?25l";
1683
1776
  var SHOW_CURSOR = "\x1B[?25h";
1684
1777
  function useFullscreen() {
1685
- useEffect5(() => {
1778
+ useEffect6(() => {
1686
1779
  process.stdout.write(ENTER_ALT_SCREEN + HIDE_CURSOR);
1687
1780
  return () => {
1688
1781
  process.stdout.write(SHOW_CURSOR + LEAVE_ALT_SCREEN);
@@ -1800,13 +1893,35 @@ function CommandExecution({
1800
1893
  const [phase, setPhase] = useState11("confirm");
1801
1894
  const [currentArgs, setCurrentArgs] = useState11(initialArgs);
1802
1895
  const [pinMessage, setPinMessage] = useState11();
1803
- const { status, result, run, reset } = useCommand(tool, process.cwd(), {
1896
+ const { status, result, run, reset, abort } = useCommand(tool, process.cwd(), {
1804
1897
  quiet: panelMode
1805
1898
  });
1806
1899
  const { runInteractive } = useInteractiveRun();
1900
+ const [outputFocused, setOutputFocused] = useState11(false);
1901
+ const [copyMessage, setCopyMessage] = useState11();
1807
1902
  const cmdDisplay = `${tool} ${currentArgs.join(" ")}`;
1808
1903
  const runCommand2 = currentArgs.join(" ");
1809
- useEffect6(() => {
1904
+ useInput8(
1905
+ (_input, key) => {
1906
+ if (key.escape) {
1907
+ abort();
1908
+ onBack();
1909
+ }
1910
+ },
1911
+ { isActive: isInputActive && phase === "running" }
1912
+ );
1913
+ useInput8(
1914
+ (input2, key) => {
1915
+ if (input2 === "o" && !outputFocused) {
1916
+ setOutputFocused(true);
1917
+ }
1918
+ if (key.escape && outputFocused) {
1919
+ setOutputFocused(false);
1920
+ }
1921
+ },
1922
+ { isActive: isInputActive && phase === "error-menu" }
1923
+ );
1924
+ useEffect7(() => {
1810
1925
  if (phase === "running" && status === "idle") {
1811
1926
  if (panelMode && interactive) {
1812
1927
  const interactiveResult = runInteractive(tool, currentArgs);
@@ -1820,7 +1935,7 @@ function CommandExecution({
1820
1935
  }
1821
1936
  }
1822
1937
  }, [phase, status, run, currentArgs, panelMode, interactive, tool, runInteractive]);
1823
- useEffect6(() => {
1938
+ useEffect7(() => {
1824
1939
  if (phase === "running" && status === "success") {
1825
1940
  if (isPinnedRun(runCommand2)) {
1826
1941
  setPhase("success");
@@ -1833,6 +1948,16 @@ function CommandExecution({
1833
1948
  }
1834
1949
  }, [phase, runCommand2, status]);
1835
1950
  if (phase === "confirm") {
1951
+ const pinned = isPinnedRun(runCommand2);
1952
+ const confirmItems = [
1953
+ { value: "execute", label: "\u25B6 Execute command" },
1954
+ {
1955
+ value: "pin",
1956
+ label: pinned ? "\u{1F4CC} Unpin command" : "\u{1F4CC} Pin command",
1957
+ hint: "Save to quick access"
1958
+ },
1959
+ { value: "cancel", label: "\u2190 Cancel" }
1960
+ ];
1836
1961
  const confirmContent = /* @__PURE__ */ jsxs15(Box13, { flexDirection: "column", children: [
1837
1962
  /* @__PURE__ */ jsxs15(Box13, { marginBottom: 1, gap: 1, children: [
1838
1963
  /* @__PURE__ */ jsxs15(Text17, { color: inkColors.accent, bold: true, children: [
@@ -1842,24 +1967,36 @@ function CommandExecution({
1842
1967
  ] }),
1843
1968
  /* @__PURE__ */ jsx17(ToolBadge, { tool })
1844
1969
  ] }),
1970
+ pinMessage && /* @__PURE__ */ jsx17(Box13, { marginBottom: 1, children: /* @__PURE__ */ jsx17(Text17, { color: inkColors.accent, children: pinMessage }) }),
1845
1971
  /* @__PURE__ */ jsx17(
1846
- ConfirmPrompt,
1972
+ SelectList,
1847
1973
  {
1848
- message: `Execute ${cmdDisplay}?`,
1849
- defaultValue: true,
1850
- onConfirm: (confirmed) => {
1851
- if (confirmed) {
1852
- setPhase("running");
1853
- } else {
1854
- onBack();
1974
+ items: confirmItems,
1975
+ onSelect: (action) => {
1976
+ switch (action) {
1977
+ case "execute":
1978
+ setPinMessage(void 0);
1979
+ setPhase("running");
1980
+ break;
1981
+ case "pin":
1982
+ togglePinnedRun(runCommand2);
1983
+ setPinMessage(
1984
+ isPinnedRun(runCommand2) ? "\u2713 Command pinned" : "\u2713 Command unpinned"
1985
+ );
1986
+ break;
1987
+ case "cancel":
1988
+ onBack();
1989
+ break;
1855
1990
  }
1856
1991
  },
1857
1992
  onCancel: onBack,
1993
+ width: panelMode ? Math.max(20, width - 4) : width,
1858
1994
  isInputActive,
1859
- arrowNavigation: panelMode
1995
+ arrowNavigation: panelMode,
1996
+ boxedSections: panelMode,
1997
+ panelFocused: isInputActive
1860
1998
  }
1861
- ),
1862
- /* @__PURE__ */ jsx17(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx17(Text17, { dimColor: true, children: "Enter to execute \xB7 n to cancel" }) })
1999
+ )
1863
2000
  ] });
1864
2001
  return /* @__PURE__ */ jsx17(Box13, { flexDirection: "column", paddingX: panelMode ? 1 : 0, children: panelMode ? /* @__PURE__ */ jsx17(
1865
2002
  Box13,
@@ -1883,7 +2020,12 @@ function CommandExecution({
1883
2020
  /* @__PURE__ */ jsx17(ToolBadge, { tool })
1884
2021
  ] }),
1885
2022
  /* @__PURE__ */ jsx17(Divider, { width: panelMode ? width - 4 : width }),
1886
- /* @__PURE__ */ jsx17(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx17(Spinner, { label: `Executing ${cmdDisplay}...` }) })
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" })
2028
+ ] })
1887
2029
  ] });
1888
2030
  }
1889
2031
  if (phase === "success-pin-offer") {
@@ -1994,6 +2136,15 @@ function CommandExecution({
1994
2136
  value: "copy",
1995
2137
  label: "\u{1F4CB} Copy command to clipboard"
1996
2138
  });
2139
+ errorItems.push({
2140
+ value: "copy-output",
2141
+ label: "\u{1F4C4} Copy output to clipboard"
2142
+ });
2143
+ errorItems.push({
2144
+ value: "__nav_header__",
2145
+ label: "",
2146
+ kind: "header"
2147
+ });
1997
2148
  errorItems.push({
1998
2149
  value: "menu",
1999
2150
  label: "\u2190 Back to menu"
@@ -2041,11 +2192,21 @@ function CommandExecution({
2041
2192
  {
2042
2193
  stdout: result?.stdout,
2043
2194
  stderr: result?.stderr,
2044
- height: Math.max(3, height - 13 - errorItems.length - (suggestions.length > 0 ? suggestions.length + 4 : 0)),
2045
- isActive: false
2195
+ height: Math.max(3, height - 14 - errorItems.length - (suggestions.length > 0 ? suggestions.length + 4 : 0) - (copyMessage ? 1 : 0)),
2196
+ isActive: isInputActive && outputFocused
2046
2197
  }
2047
2198
  ),
2048
- /* @__PURE__ */ jsx17(Box13, { marginTop: 1, marginBottom: 1, children: /* @__PURE__ */ jsx17(Text17, { bold: true, children: "What would you like to do?" }) }),
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)" })
2209
+ ] }),
2049
2210
  /* @__PURE__ */ jsx17(
2050
2211
  SelectList,
2051
2212
  {
@@ -2069,6 +2230,7 @@ function CommandExecution({
2069
2230
  switch (action) {
2070
2231
  case "retry":
2071
2232
  setPinMessage(void 0);
2233
+ setCopyMessage(void 0);
2072
2234
  reset();
2073
2235
  setPhase("running");
2074
2236
  break;
@@ -2076,6 +2238,7 @@ function CommandExecution({
2076
2238
  const newArgs = [...currentArgs, "--debug"];
2077
2239
  setCurrentArgs(newArgs);
2078
2240
  setPinMessage(void 0);
2241
+ setCopyMessage(void 0);
2079
2242
  reset();
2080
2243
  setPhase("running");
2081
2244
  break;
@@ -2089,7 +2252,14 @@ function CommandExecution({
2089
2252
  }
2090
2253
  case "copy":
2091
2254
  await copyToClipboard(cmdDisplay);
2255
+ setCopyMessage("\u2713 Command copied to clipboard");
2092
2256
  break;
2257
+ case "copy-output": {
2258
+ const output2 = [result?.stdout, result?.stderr].filter(Boolean).join("\n");
2259
+ await copyToClipboard(output2);
2260
+ setCopyMessage("\u2713 Output copied to clipboard");
2261
+ break;
2262
+ }
2093
2263
  case "menu":
2094
2264
  (onHome ?? onBack)();
2095
2265
  break;
@@ -2102,9 +2272,9 @@ function CommandExecution({
2102
2272
  boxedSections: panelMode,
2103
2273
  width: panelMode ? Math.max(20, width - 4) : width,
2104
2274
  maxVisible: panelMode ? Math.max(errorItems.length + (suggestions.length > 0 ? 4 : 0), height - 6) : void 0,
2105
- isInputActive,
2275
+ isInputActive: isInputActive && !outputFocused,
2106
2276
  arrowNavigation: panelMode,
2107
- panelFocused: isInputActive
2277
+ panelFocused: isInputActive && !outputFocused
2108
2278
  }
2109
2279
  ),
2110
2280
  !panelMode && /* @__PURE__ */ jsx17(StatusBar, { width })
@@ -2112,7 +2282,7 @@ function CommandExecution({
2112
2282
  }
2113
2283
 
2114
2284
  // src/screens/SelfUpdate.tsx
2115
- import { useEffect as useEffect7, useState as useState12 } from "react";
2285
+ import { useEffect as useEffect8, useState as useState12 } from "react";
2116
2286
  import { Box as Box14, Text as Text18 } from "ink";
2117
2287
  import { jsx as jsx18, jsxs as jsxs16 } from "react/jsx-runtime";
2118
2288
  var packageName = "@polterware/polter";
@@ -2142,12 +2312,12 @@ function SelfUpdate({
2142
2312
  const { status, result, run, reset } = useCommand("npm", updateCwd, {
2143
2313
  quiet: panelMode
2144
2314
  });
2145
- useEffect7(() => {
2315
+ useEffect8(() => {
2146
2316
  if (phase === "running" && status === "idle") {
2147
2317
  run(updateArgs);
2148
2318
  }
2149
2319
  }, [phase, run, status, updateArgs]);
2150
- useEffect7(() => {
2320
+ useEffect8(() => {
2151
2321
  if (phase === "running" && status === "success") {
2152
2322
  setPhase("success");
2153
2323
  }
@@ -2388,13 +2558,33 @@ function SelfUpdate({
2388
2558
  }
2389
2559
 
2390
2560
  // src/screens/ToolStatus.tsx
2391
- import { useMemo as useMemo4 } from "react";
2561
+ import { useEffect as useEffect9, useState as useState13 } from "react";
2392
2562
  import { Box as Box15, Text as Text19 } from "ink";
2393
2563
  import { jsx as jsx19, jsxs as jsxs17 } from "react/jsx-runtime";
2394
- var toolIds = ["supabase", "gh", "vercel", "git"];
2395
- var linkableTools = /* @__PURE__ */ new Set(["supabase", "gh", "vercel"]);
2396
- function ToolStatus({ onBack, width = 80, height = 24, panelMode = false, isInputActive = true }) {
2397
- const tools = useMemo4(() => toolIds.map((id) => getToolLinkInfo(id)), []);
2564
+ var toolIds = ["supabase", "gh", "vercel", "git", "pkg"];
2565
+ var linkableTools = /* @__PURE__ */ new Set(["supabase", "gh", "vercel", "pkg"]);
2566
+ 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(() => {
2570
+ const t = setTimeout(() => {
2571
+ setTools(toolIds.map((id) => getToolLinkInfo(id)));
2572
+ setMcpInfo(getMcpStatusInfo());
2573
+ }, 0);
2574
+ return () => clearTimeout(t);
2575
+ }, []);
2576
+ if (!tools || !mcpInfo) {
2577
+ return /* @__PURE__ */ jsx19(Box15, { flexDirection: "column", paddingX: panelMode ? 1 : 0, children: /* @__PURE__ */ jsx19(Text19, { color: inkColors.accent, children: "Loading tool status..." }) });
2578
+ }
2579
+ const mcpRegistered = mcpInfo.scopes.some((s) => s.registered);
2580
+ const mcpScopeHint = mcpInfo.scopes.filter((s) => s.registered).map((s) => s.scope).join(", ");
2581
+ const handleSelect = (value) => {
2582
+ if (value === "mcp-manage" && onNavigate) {
2583
+ onNavigate("mcp-manage");
2584
+ } else if (value === "__back__") {
2585
+ onBack();
2586
+ }
2587
+ };
2398
2588
  if (panelMode) {
2399
2589
  const statusItems = [
2400
2590
  { value: "__section__", label: "Installed Tools", kind: "header", selectable: false },
@@ -2403,14 +2593,20 @@ function ToolStatus({ onBack, width = 80, height = 24, panelMode = false, isInpu
2403
2593
  label: `${tool.installed ? "\u2713" : "\u2717"} ${tool.label}`,
2404
2594
  hint: tool.installed ? `${tool.version ?? "installed"}${linkableTools.has(tool.id) ? tool.linked ? ` \u2192 ${tool.project ? `linked (${tool.project})` : "linked"}` : " \u2192 not linked" : ""}` : "not found",
2405
2595
  kind: "action"
2406
- }))
2596
+ })),
2597
+ { value: "__mcp_section__", label: "MCP Server", kind: "header", selectable: false },
2598
+ {
2599
+ value: "mcp-manage",
2600
+ label: `${mcpRegistered ? "\u2713" : "\u2717"} polter-mcp`,
2601
+ hint: `v${mcpInfo.installedVersion}${mcpRegistered ? ` \u2192 ${mcpScopeHint}` : " \u2192 not registered"}`,
2602
+ kind: "action"
2603
+ }
2407
2604
  ];
2408
2605
  return /* @__PURE__ */ jsx19(Box15, { flexDirection: "column", paddingX: 1, children: /* @__PURE__ */ jsx19(
2409
2606
  SelectList,
2410
2607
  {
2411
2608
  items: statusItems,
2412
- onSelect: () => {
2413
- },
2609
+ onSelect: handleSelect,
2414
2610
  onCancel: onBack,
2415
2611
  boxedSections: true,
2416
2612
  width: Math.max(20, width - 4),
@@ -2422,32 +2618,45 @@ function ToolStatus({ onBack, width = 80, height = 24, panelMode = false, isInpu
2422
2618
  ) });
2423
2619
  }
2424
2620
  return /* @__PURE__ */ jsxs17(Box15, { flexDirection: "column", children: [
2425
- /* @__PURE__ */ jsx19(Box15, { marginBottom: 1, children: /* @__PURE__ */ jsx19(Text19, { bold: true, color: inkColors.accent, children: "\u{1F527} Tool Status" }) }),
2621
+ /* @__PURE__ */ jsx19(Box15, { marginBottom: 1, children: /* @__PURE__ */ jsx19(Text19, { bold: true, color: inkColors.accent, children: "Tool Status" }) }),
2426
2622
  tools.map((tool) => /* @__PURE__ */ jsxs17(Box15, { gap: 1, marginLeft: 2, children: [
2427
2623
  /* @__PURE__ */ jsx19(Text19, { color: tool.installed ? inkColors.accent : "red", children: tool.installed ? "\u2713" : "\u2717" }),
2428
2624
  /* @__PURE__ */ jsx19(Box15, { width: 16, children: /* @__PURE__ */ jsx19(Text19, { bold: true, children: tool.label }) }),
2429
2625
  /* @__PURE__ */ jsx19(Text19, { dimColor: true, children: tool.installed ? `${tool.version ?? "installed"}${linkableTools.has(tool.id) ? tool.linked ? ` \u2192 ${tool.project ?? "linked"}` : " \u2192 not linked" : ""}` : "not found" })
2430
2626
  ] }, tool.id)),
2627
+ /* @__PURE__ */ jsx19(Box15, { marginTop: 1, marginBottom: 1, children: /* @__PURE__ */ jsx19(Text19, { bold: true, color: inkColors.accent, children: " MCP Server" }) }),
2628
+ /* @__PURE__ */ jsxs17(Box15, { gap: 1, marginLeft: 2, children: [
2629
+ /* @__PURE__ */ jsx19(Text19, { color: mcpRegistered ? inkColors.accent : "red", children: mcpRegistered ? "\u2713" : "\u2717" }),
2630
+ /* @__PURE__ */ jsx19(Box15, { width: 16, children: /* @__PURE__ */ jsx19(Text19, { bold: true, children: "polter-mcp" }) }),
2631
+ /* @__PURE__ */ jsxs17(Text19, { dimColor: true, children: [
2632
+ "v",
2633
+ mcpInfo.installedVersion,
2634
+ mcpRegistered ? ` \u2192 ${mcpScopeHint}` : " \u2192 not registered"
2635
+ ] })
2636
+ ] }),
2431
2637
  /* @__PURE__ */ jsx19(Box15, { marginTop: 1, children: /* @__PURE__ */ jsx19(
2432
2638
  SelectList,
2433
2639
  {
2434
- items: [{ value: "__back__", label: "\u2190 Back" }],
2435
- onSelect: onBack,
2640
+ items: [
2641
+ { value: "mcp-manage", label: "Manage MCP Server \u2192" },
2642
+ { value: "__back__", label: "\u2190 Back" }
2643
+ ],
2644
+ onSelect: handleSelect,
2436
2645
  onCancel: onBack,
2437
2646
  width,
2438
2647
  isInputActive
2439
2648
  }
2440
2649
  ) }),
2441
- /* @__PURE__ */ jsx19(StatusBar, { hint: "Enter to go back", width })
2650
+ /* @__PURE__ */ jsx19(StatusBar, { hint: "Enter to select \\u00B7 Esc back", width })
2442
2651
  ] });
2443
2652
  }
2444
2653
 
2445
2654
  // src/screens/ProjectConfig.tsx
2446
- import { useMemo as useMemo5, useState as useState14 } from "react";
2655
+ import { useMemo as useMemo4, useState as useState15 } from "react";
2447
2656
  import { Box as Box16, Text as Text20 } from "ink";
2448
2657
 
2449
2658
  // src/hooks/useEditor.ts
2450
- import { useState as useState13, useCallback as useCallback4 } from "react";
2659
+ import { useState as useState14, useCallback as useCallback4 } from "react";
2451
2660
  import { useStdin } from "ink";
2452
2661
 
2453
2662
  // src/lib/editor.ts
@@ -2498,7 +2707,7 @@ function openInEditor(filePath) {
2498
2707
  // src/hooks/useEditor.ts
2499
2708
  function useEditor() {
2500
2709
  const { setRawMode } = useStdin();
2501
- const [isEditing, setIsEditing] = useState13(false);
2710
+ const [isEditing, setIsEditing] = useState14(false);
2502
2711
  const openEditor = useCallback4(async (filePath) => {
2503
2712
  const editor = resolveEditor();
2504
2713
  const terminal = isTerminalEditor(editor.command);
@@ -2519,7 +2728,7 @@ function useEditor() {
2519
2728
  }
2520
2729
 
2521
2730
  // src/screens/ProjectConfig.tsx
2522
- import { jsx as jsx20, jsxs as jsxs18 } from "react/jsx-runtime";
2731
+ import { Fragment, jsx as jsx20, jsxs as jsxs18 } from "react/jsx-runtime";
2523
2732
  function ProjectConfig({
2524
2733
  onBack,
2525
2734
  width = 80,
@@ -2527,11 +2736,14 @@ function ProjectConfig({
2527
2736
  panelMode = false,
2528
2737
  isInputActive = true
2529
2738
  }) {
2530
- const configPath = useMemo5(() => getProjectConfigPath(), []);
2531
- const [config2, setConfig] = useState14(() => getOrCreateProjectConfig());
2532
- const [phase, setPhase] = useState14("overview");
2533
- const [feedback, setFeedback] = useState14();
2739
+ 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("");
2534
2745
  const { openEditor, isEditing } = useEditor();
2746
+ const detectedPkg = useMemo4(() => detectPkgManager(), []);
2535
2747
  if (!configPath) {
2536
2748
  return /* @__PURE__ */ jsxs18(Box16, { flexDirection: "column", paddingX: panelMode ? 1 : 0, children: [
2537
2749
  /* @__PURE__ */ jsx20(Text20, { color: "red", children: "No package.json found. Run from a project directory." }),
@@ -2628,15 +2840,158 @@ function ProjectConfig({
2628
2840
  }
2629
2841
  ) });
2630
2842
  }
2843
+ if (phase === "edit-pkg-manager") {
2844
+ const pkgOptions = [
2845
+ { value: "npm", label: "npm" },
2846
+ { value: "pnpm", label: "pnpm" },
2847
+ { value: "yarn", label: "yarn" },
2848
+ { value: "bun", label: "bun" },
2849
+ { value: "__auto__", label: `auto-detect (${detectedPkg.id})` },
2850
+ { value: "__cancel__", label: "\u2190 Cancel" }
2851
+ ];
2852
+ return /* @__PURE__ */ jsxs18(Box16, { flexDirection: "column", paddingX: panelMode ? 1 : 0, children: [
2853
+ /* @__PURE__ */ jsx20(Box16, { marginBottom: 1, children: /* @__PURE__ */ jsx20(Text20, { bold: true, children: "Select package manager:" }) }),
2854
+ /* @__PURE__ */ jsx20(
2855
+ SelectList,
2856
+ {
2857
+ items: pkgOptions,
2858
+ onSelect: (value) => {
2859
+ if (value === "__cancel__") {
2860
+ setPhase("overview");
2861
+ return;
2862
+ }
2863
+ const manager = value === "__auto__" ? void 0 : value;
2864
+ const updated = {
2865
+ ...config2,
2866
+ tools: {
2867
+ ...config2.tools,
2868
+ pkg: manager ? { ...config2.tools.pkg, manager } : {}
2869
+ }
2870
+ };
2871
+ writeProjectConfig(updated);
2872
+ setConfig(updated);
2873
+ setFeedback(manager ? `Package manager set to ${manager}` : "Package manager cleared (auto-detect)");
2874
+ setPhase("overview");
2875
+ },
2876
+ onCancel: () => setPhase("overview"),
2877
+ width: panelMode ? Math.max(20, width - 4) : width,
2878
+ isInputActive,
2879
+ arrowNavigation: panelMode,
2880
+ panelFocused: panelMode ? isInputActive : void 0
2881
+ }
2882
+ )
2883
+ ] });
2884
+ }
2885
+ if (phase === "add-env-var") {
2886
+ return /* @__PURE__ */ jsx20(Box16, { flexDirection: "column", paddingX: panelMode ? 1 : 0, children: /* @__PURE__ */ jsx20(
2887
+ TextPrompt,
2888
+ {
2889
+ label: "Environment variable name:",
2890
+ placeholder: "e.g. API_KEY",
2891
+ onSubmit: (val) => {
2892
+ const key = val.trim();
2893
+ if (!key) {
2894
+ setPhase("overview");
2895
+ return;
2896
+ }
2897
+ setEnvKey(key);
2898
+ setPhase("add-env-value");
2899
+ },
2900
+ onCancel: () => setPhase("overview"),
2901
+ arrowNavigation: panelMode,
2902
+ isInputActive,
2903
+ boxed: panelMode,
2904
+ focused: isInputActive
2905
+ }
2906
+ ) });
2907
+ }
2908
+ if (phase === "add-env-value") {
2909
+ return /* @__PURE__ */ jsx20(Box16, { flexDirection: "column", paddingX: panelMode ? 1 : 0, children: /* @__PURE__ */ jsx20(
2910
+ TextPrompt,
2911
+ {
2912
+ label: `Value for ${envKey}:`,
2913
+ placeholder: "Enter value",
2914
+ onSubmit: (val) => {
2915
+ const updated = {
2916
+ ...config2,
2917
+ env: { ...config2.env, [envKey]: val }
2918
+ };
2919
+ writeProjectConfig(updated);
2920
+ setConfig(updated);
2921
+ setFeedback(`Set ${envKey}`);
2922
+ setEnvKey("");
2923
+ setPhase("overview");
2924
+ },
2925
+ onCancel: () => {
2926
+ setEnvKey("");
2927
+ setPhase("overview");
2928
+ },
2929
+ arrowNavigation: panelMode,
2930
+ isInputActive,
2931
+ boxed: panelMode,
2932
+ focused: isInputActive
2933
+ }
2934
+ ) });
2935
+ }
2936
+ if (phase === "manage-env-entry") {
2937
+ const currentVal = config2.env?.[selectedEnvKey] ?? "";
2938
+ const items = [
2939
+ { value: "__info__", label: `${selectedEnvKey} = ${currentVal}`, kind: "header", selectable: false },
2940
+ { value: "edit", label: "Edit value", kind: "action" },
2941
+ { value: "remove", label: "Remove", kind: "action" },
2942
+ { value: "__cancel__", label: "\u2190 Cancel" }
2943
+ ];
2944
+ return /* @__PURE__ */ jsx20(Box16, { flexDirection: "column", paddingX: panelMode ? 1 : 0, children: /* @__PURE__ */ jsx20(
2945
+ SelectList,
2946
+ {
2947
+ items,
2948
+ onSelect: (value) => {
2949
+ if (value === "edit") {
2950
+ setEnvKey(selectedEnvKey);
2951
+ setPhase("add-env-value");
2952
+ } else if (value === "remove") {
2953
+ const newEnv = { ...config2.env };
2954
+ delete newEnv[selectedEnvKey];
2955
+ const updated = { ...config2, env: newEnv };
2956
+ writeProjectConfig(updated);
2957
+ setConfig(updated);
2958
+ setFeedback(`Removed ${selectedEnvKey}`);
2959
+ setPhase("overview");
2960
+ } else {
2961
+ setPhase("overview");
2962
+ }
2963
+ },
2964
+ onCancel: () => setPhase("overview"),
2965
+ width: panelMode ? Math.max(20, width - 4) : width,
2966
+ isInputActive,
2967
+ arrowNavigation: panelMode,
2968
+ panelFocused: panelMode ? isInputActive : void 0
2969
+ }
2970
+ ) });
2971
+ }
2972
+ const envEntries = Object.entries(config2.env ?? {});
2973
+ const pkgDisplay = config2.tools.pkg?.manager ?? `auto-detect (${detectedPkg.id})`;
2631
2974
  const configItems = [
2632
2975
  { value: "__section_current__", label: "\u{1F4CB} Current Values", kind: "header", selectable: false },
2633
2976
  { value: "__info_supabase__", label: `Supabase ref: ${config2.tools.supabase?.projectRef ?? "not set"}`, kind: "header", selectable: false },
2634
2977
  { value: "__info_vercel__", label: `Vercel ID: ${config2.tools.vercel?.projectId ?? "not set"}`, kind: "header", selectable: false },
2635
2978
  { value: "__info_gh__", label: `GitHub repo: ${config2.tools.gh?.repo ?? "not set"}`, kind: "header", selectable: false },
2979
+ { value: "__info_pkg__", label: `Pkg manager: ${pkgDisplay}`, kind: "header", selectable: false },
2980
+ { value: "__info_pipelines__", label: `Pipelines: ${config2.pipelines.length}`, kind: "header", selectable: false },
2981
+ ...envEntries.length > 0 ? [
2982
+ { value: "__section_env__", label: "\u{1F510} Environment Variables", kind: "header", selectable: false },
2983
+ ...envEntries.map(([k, v]) => ({
2984
+ value: `env:${k}`,
2985
+ label: `${k} = ${v}`,
2986
+ kind: "action"
2987
+ }))
2988
+ ] : [],
2636
2989
  { value: "__section_actions__", label: "\u26A1 Actions", kind: "header", selectable: false },
2637
2990
  { value: "supabase", label: "Set Supabase project ref", kind: "action" },
2638
2991
  { value: "vercel", label: "Set Vercel project ID", kind: "action" },
2639
2992
  { value: "gh", label: "Set GitHub repo", kind: "action" },
2993
+ { value: "pkg-manager", label: "Set package manager", kind: "action" },
2994
+ { value: "add-env", label: "Add environment variable", kind: "action" },
2640
2995
  { value: "editor", label: "Open config in editor", kind: "action" },
2641
2996
  ...!panelMode ? [{ value: "__back__", label: "\u2190 Back" }] : []
2642
2997
  ];
@@ -2644,10 +2999,17 @@ function ProjectConfig({
2644
2999
  { value: "supabase", label: "Set Supabase project ref" },
2645
3000
  { value: "vercel", label: "Set Vercel project ID" },
2646
3001
  { value: "gh", label: "Set GitHub repo" },
3002
+ { value: "pkg-manager", label: "Set package manager" },
3003
+ { value: "add-env", label: "Add environment variable" },
2647
3004
  { value: "editor", label: "Open config in editor" },
2648
3005
  ...!panelMode ? [{ value: "__back__", label: "\u2190 Back" }] : []
2649
3006
  ];
2650
3007
  const handleSelect = (value) => {
3008
+ if (value.startsWith("env:")) {
3009
+ setSelectedEnvKey(value.slice(4));
3010
+ setPhase("manage-env-entry");
3011
+ return;
3012
+ }
2651
3013
  switch (value) {
2652
3014
  case "supabase":
2653
3015
  setPhase("edit-supabase-ref");
@@ -2658,6 +3020,12 @@ function ProjectConfig({
2658
3020
  case "gh":
2659
3021
  setPhase("edit-gh-repo");
2660
3022
  break;
3023
+ case "pkg-manager":
3024
+ setPhase("edit-pkg-manager");
3025
+ break;
3026
+ case "add-env":
3027
+ setPhase("add-env-var");
3028
+ break;
2661
3029
  case "editor":
2662
3030
  openEditor(configPath.file).then(() => {
2663
3031
  try {
@@ -2717,6 +3085,26 @@ function ProjectConfig({
2717
3085
  /* @__PURE__ */ jsxs18(Text20, { children: [
2718
3086
  "GitHub repo: ",
2719
3087
  config2.tools.gh?.repo ?? /* @__PURE__ */ jsx20(Text20, { dimColor: true, children: "not set" })
3088
+ ] }),
3089
+ /* @__PURE__ */ jsxs18(Text20, { children: [
3090
+ "Pkg manager: ",
3091
+ pkgDisplay
3092
+ ] }),
3093
+ /* @__PURE__ */ jsxs18(Text20, { children: [
3094
+ "Pipelines: ",
3095
+ config2.pipelines.length
3096
+ ] }),
3097
+ envEntries.length > 0 && /* @__PURE__ */ jsxs18(Fragment, { children: [
3098
+ /* @__PURE__ */ jsxs18(Text20, { bold: true, children: [
3099
+ "\n",
3100
+ "Environment Variables:"
3101
+ ] }),
3102
+ envEntries.map(([k, v]) => /* @__PURE__ */ jsxs18(Text20, { children: [
3103
+ " ",
3104
+ k,
3105
+ " = ",
3106
+ v
3107
+ ] }, k))
2720
3108
  ] })
2721
3109
  ] }),
2722
3110
  feedback && /* @__PURE__ */ jsx20(Box16, { marginBottom: 1, children: /* @__PURE__ */ jsxs18(Text20, { color: inkColors.accent, children: [
@@ -2739,7 +3127,7 @@ function ProjectConfig({
2739
3127
  }
2740
3128
 
2741
3129
  // src/screens/PipelineList.tsx
2742
- import { useMemo as useMemo6 } from "react";
3130
+ import { useMemo as useMemo5 } from "react";
2743
3131
  import { Box as Box17, Text as Text21 } from "ink";
2744
3132
  import { jsx as jsx21, jsxs as jsxs19 } from "react/jsx-runtime";
2745
3133
  function PipelineList({
@@ -2750,8 +3138,8 @@ function PipelineList({
2750
3138
  panelMode = false,
2751
3139
  isInputActive = true
2752
3140
  }) {
2753
- const pipelines = useMemo6(() => getAllPipelines(), []);
2754
- const items = useMemo6(() => {
3141
+ const pipelines = useMemo5(() => getAllPipelines(), []);
3142
+ const items = useMemo5(() => {
2755
3143
  const list = [];
2756
3144
  if (pipelines.length === 0) {
2757
3145
  list.push({
@@ -2835,7 +3223,7 @@ function PipelineList({
2835
3223
  }
2836
3224
 
2837
3225
  // src/screens/PipelineBuilder.tsx
2838
- import { useState as useState15 } from "react";
3226
+ import { useState as useState16 } from "react";
2839
3227
  import { Box as Box18, Text as Text22 } from "ink";
2840
3228
  import { jsx as jsx22, jsxs as jsxs20 } from "react/jsx-runtime";
2841
3229
  var stepCounter = 0;
@@ -2850,9 +3238,9 @@ function PipelineBuilder({
2850
3238
  panelMode = false,
2851
3239
  isInputActive = true
2852
3240
  }) {
2853
- const [phase, setPhase] = useState15("name");
2854
- const [name, setName] = useState15("");
2855
- const [steps, setSteps] = useState15([]);
3241
+ const [phase, setPhase] = useState16("name");
3242
+ const [name, setName] = useState16("");
3243
+ const [steps, setSteps] = useState16([]);
2856
3244
  if (phase === "name") {
2857
3245
  return /* @__PURE__ */ jsxs20(Box18, { flexDirection: "column", paddingX: panelMode ? 1 : 0, children: [
2858
3246
  /* @__PURE__ */ jsx22(Box18, { marginBottom: 1, children: /* @__PURE__ */ jsx22(Text22, { bold: true, color: inkColors.accent, children: "\u{1F517} New Pipeline" }) }),
@@ -3038,7 +3426,7 @@ function PipelineBuilder({
3038
3426
  }
3039
3427
 
3040
3428
  // src/screens/PipelineExecution.tsx
3041
- import { useState as useState16, useEffect as useEffect8, useMemo as useMemo7 } from "react";
3429
+ import { useState as useState17, useEffect as useEffect10, useMemo as useMemo6 } from "react";
3042
3430
  import { Box as Box21, Text as Text25 } from "ink";
3043
3431
 
3044
3432
  // src/components/StepIndicator.tsx
@@ -3118,7 +3506,7 @@ function PipelineProgressBar({
3118
3506
  }
3119
3507
 
3120
3508
  // src/screens/PipelineExecution.tsx
3121
- import { Fragment, jsx as jsx24, jsxs as jsxs23 } from "react/jsx-runtime";
3509
+ import { Fragment as Fragment2, jsx as jsx24, jsxs as jsxs23 } from "react/jsx-runtime";
3122
3510
  function PipelineExecution({
3123
3511
  pipelineId,
3124
3512
  onBack,
@@ -3127,14 +3515,14 @@ function PipelineExecution({
3127
3515
  panelMode = false,
3128
3516
  isInputActive = true
3129
3517
  }) {
3130
- const pipeline = useMemo7(
3518
+ const pipeline = useMemo6(
3131
3519
  () => getAllPipelines().find((p) => p.id === pipelineId),
3132
3520
  [pipelineId]
3133
3521
  );
3134
- const [phase, setPhase] = useState16("running");
3135
- const [progress, setProgress] = useState16(null);
3136
- const [results, setResults] = useState16([]);
3137
- useEffect8(() => {
3522
+ const [phase, setPhase] = useState17("running");
3523
+ const [progress, setProgress] = useState17(null);
3524
+ const [results, setResults] = useState17([]);
3525
+ useEffect10(() => {
3138
3526
  if (!pipeline) return;
3139
3527
  executePipeline(pipeline, (p) => {
3140
3528
  setProgress({ ...p });
@@ -3204,7 +3592,7 @@ function PipelineExecution({
3204
3592
  },
3205
3593
  step.id
3206
3594
  )) }),
3207
- phase === "done" && /* @__PURE__ */ jsxs23(Fragment, { children: [
3595
+ phase === "done" && /* @__PURE__ */ jsxs23(Fragment2, { children: [
3208
3596
  /* @__PURE__ */ jsx24(Divider, { width }),
3209
3597
  /* @__PURE__ */ jsx24(Box21, { marginY: 1, children: /* @__PURE__ */ jsx24(
3210
3598
  Text25,
@@ -3247,25 +3635,1180 @@ function PipelineExecution({
3247
3635
  ] });
3248
3636
  }
3249
3637
 
3250
- // src/app.tsx
3638
+ // src/screens/McpManage.tsx
3639
+ import { useState as useState18, useCallback as useCallback5 } from "react";
3640
+ import { Box as Box22, Text as Text26 } from "ink";
3251
3641
  import { jsx as jsx25, jsxs as jsxs24 } from "react/jsx-runtime";
3252
- function AppClassic() {
3253
- const { screen, params, navigate, goBack, goHome } = useNavigation();
3254
- const { exit } = useApp();
3255
- const width = useTerminalWidth();
3256
- const height = useTerminalHeight();
3257
- const handleExit = () => {
3258
- process.stdout.write(
3259
- "\n" + colors.dim("Thank you for using ") + colors.primaryBold("Polter") + colors.dim("!") + "\n\n"
3642
+ function McpManage({
3643
+ onBack,
3644
+ width = 80,
3645
+ height = 24,
3646
+ panelMode = false,
3647
+ isInputActive = true
3648
+ }) {
3649
+ const [status, setStatus] = useState18(() => getMcpStatusInfo());
3650
+ const [phase, setPhase] = useState18("overview");
3651
+ const [action, setAction] = useState18(null);
3652
+ const [result, setResult] = useState18(null);
3653
+ const refreshStatus = useCallback5(() => {
3654
+ setStatus(getMcpStatusInfo());
3655
+ }, []);
3656
+ const executeAction = useCallback5(async (act) => {
3657
+ setAction(act);
3658
+ setPhase("executing");
3659
+ try {
3660
+ const res = act.type === "install" ? await installMcpServerSilent(act.scope) : await removeMcpServerSilent(act.scope);
3661
+ setResult(res);
3662
+ } catch (err) {
3663
+ setResult({ success: false, message: err instanceof Error ? err.message : "Unknown error" });
3664
+ }
3665
+ setPhase("result");
3666
+ }, []);
3667
+ if (phase === "executing") {
3668
+ return /* @__PURE__ */ jsx25(Box22, { flexDirection: "column", paddingX: panelMode ? 1 : 0, children: /* @__PURE__ */ jsx25(Box22, { marginBottom: 1, children: /* @__PURE__ */ jsxs24(Text26, { color: inkColors.accent, children: [
3669
+ action?.type === "install" ? "Installing" : "Removing",
3670
+ " MCP server (",
3671
+ action?.scope,
3672
+ ")..."
3673
+ ] }) }) });
3674
+ }
3675
+ if (phase === "result" && result) {
3676
+ const resultItems = [
3677
+ { value: "back-overview", label: "\u2190 Back to overview" },
3678
+ ...!panelMode ? [{ value: "__back__", label: "\u2190 Back to Tool Status" }] : []
3679
+ ];
3680
+ return /* @__PURE__ */ jsxs24(Box22, { flexDirection: "column", paddingX: panelMode ? 1 : 0, children: [
3681
+ /* @__PURE__ */ jsx25(Box22, { marginBottom: 1, children: /* @__PURE__ */ jsxs24(Text26, { color: result.success ? inkColors.accent : "red", children: [
3682
+ result.success ? "\u2713" : "\u2717",
3683
+ " ",
3684
+ result.message
3685
+ ] }) }),
3686
+ /* @__PURE__ */ jsx25(
3687
+ SelectList,
3688
+ {
3689
+ items: resultItems,
3690
+ onSelect: (val) => {
3691
+ if (val === "back-overview") {
3692
+ refreshStatus();
3693
+ setPhase("overview");
3694
+ } else {
3695
+ onBack();
3696
+ }
3697
+ },
3698
+ onCancel: () => {
3699
+ refreshStatus();
3700
+ setPhase("overview");
3701
+ },
3702
+ width: panelMode ? Math.max(20, width - 4) : width,
3703
+ isInputActive,
3704
+ arrowNavigation: panelMode,
3705
+ panelFocused: panelMode ? isInputActive : void 0
3706
+ }
3707
+ ),
3708
+ !panelMode && /* @__PURE__ */ jsx25(StatusBar, { hint: "Enter select . Esc back", width })
3709
+ ] });
3710
+ }
3711
+ const projectScope = status.scopes.find((s) => s.scope === "project");
3712
+ const userScope = status.scopes.find((s) => s.scope === "user");
3713
+ const items = panelMode ? [
3714
+ { value: "__info__", label: `Version: v${status.installedVersion}`, kind: "header", selectable: false },
3715
+ { value: "__proj__", label: `Project: ${projectScope?.registered ? "registered" : "not registered"}`, kind: "header", selectable: false },
3716
+ { value: "__user__", label: `User: ${userScope?.registered ? "registered" : "not registered"}`, kind: "header", selectable: false },
3717
+ { value: "__actions__", label: "Actions", kind: "header", selectable: false },
3718
+ { value: "install-project", label: "Install (project scope)", kind: "action" },
3719
+ { value: "install-user", label: "Install (user scope)", kind: "action" },
3720
+ { value: "remove-project", label: "Remove (project scope)", kind: "action" },
3721
+ { value: "remove-user", label: "Remove (user scope)", kind: "action" }
3722
+ ] : [
3723
+ { value: "install-project", label: "Install (project scope)" },
3724
+ { value: "install-user", label: "Install (user scope)" },
3725
+ { value: "remove-project", label: "Remove (project scope)" },
3726
+ { value: "remove-user", label: "Remove (user scope)" },
3727
+ { value: "__back__", label: "\u2190 Back" }
3728
+ ];
3729
+ const handleSelect = (value) => {
3730
+ switch (value) {
3731
+ case "install-project":
3732
+ executeAction({ type: "install", scope: "project" });
3733
+ break;
3734
+ case "install-user":
3735
+ executeAction({ type: "install", scope: "user" });
3736
+ break;
3737
+ case "remove-project":
3738
+ executeAction({ type: "remove", scope: "project" });
3739
+ break;
3740
+ case "remove-user":
3741
+ executeAction({ type: "remove", scope: "user" });
3742
+ break;
3743
+ default:
3744
+ onBack();
3745
+ }
3746
+ };
3747
+ if (panelMode) {
3748
+ return /* @__PURE__ */ jsx25(Box22, { flexDirection: "column", paddingX: 1, children: /* @__PURE__ */ jsx25(
3749
+ SelectList,
3750
+ {
3751
+ items,
3752
+ onSelect: handleSelect,
3753
+ onCancel: onBack,
3754
+ boxedSections: true,
3755
+ width: Math.max(20, width - 4),
3756
+ maxVisible: Math.max(6, height - 6),
3757
+ isInputActive,
3758
+ arrowNavigation: true,
3759
+ panelFocused: isInputActive
3760
+ }
3761
+ ) });
3762
+ }
3763
+ return /* @__PURE__ */ jsxs24(Box22, { flexDirection: "column", children: [
3764
+ /* @__PURE__ */ jsx25(Box22, { marginBottom: 1, children: /* @__PURE__ */ jsx25(Text26, { bold: true, color: inkColors.accent, children: "MCP Server Management" }) }),
3765
+ /* @__PURE__ */ jsxs24(Box22, { marginLeft: 2, marginBottom: 1, flexDirection: "column", children: [
3766
+ /* @__PURE__ */ jsxs24(Text26, { children: [
3767
+ "Version: v",
3768
+ status.installedVersion
3769
+ ] }),
3770
+ /* @__PURE__ */ jsxs24(Text26, { children: [
3771
+ "Project: ",
3772
+ /* @__PURE__ */ jsx25(Text26, { color: projectScope?.registered ? inkColors.accent : void 0, dimColor: !projectScope?.registered, children: projectScope?.registered ? "registered" : "not registered" })
3773
+ ] }),
3774
+ /* @__PURE__ */ jsxs24(Text26, { children: [
3775
+ "User: ",
3776
+ /* @__PURE__ */ jsx25(Text26, { color: userScope?.registered ? inkColors.accent : void 0, dimColor: !userScope?.registered, children: userScope?.registered ? "registered" : "not registered" })
3777
+ ] })
3778
+ ] }),
3779
+ /* @__PURE__ */ jsx25(
3780
+ SelectList,
3781
+ {
3782
+ items,
3783
+ onSelect: handleSelect,
3784
+ onCancel: onBack,
3785
+ width,
3786
+ isInputActive
3787
+ }
3788
+ ),
3789
+ /* @__PURE__ */ jsx25(StatusBar, { hint: "Enter select . Esc back", width })
3790
+ ] });
3791
+ }
3792
+
3793
+ // src/screens/ProcessList.tsx
3794
+ import { useState as useState19, useEffect as useEffect11, useCallback as useCallback6 } from "react";
3795
+ import { Box as Box23, Text as Text27, useInput as useInput9 } from "ink";
3796
+ import { homedir } from "os";
3797
+ import { jsx as jsx26, jsxs as jsxs25 } from "react/jsx-runtime";
3798
+ function formatUptime(ms) {
3799
+ const seconds = Math.floor(ms / 1e3);
3800
+ const minutes = Math.floor(seconds / 60);
3801
+ const hours = Math.floor(minutes / 60);
3802
+ if (hours > 0) return `${hours}h ${minutes % 60}m`;
3803
+ if (minutes > 0) return `${minutes}m ${seconds % 60}s`;
3804
+ return `${seconds}s`;
3805
+ }
3806
+ function formatDuration(startedAt, exitedAt) {
3807
+ if (!exitedAt) return "\u2014";
3808
+ const ms = new Date(exitedAt).getTime() - new Date(startedAt).getTime();
3809
+ return formatUptime(ms);
3810
+ }
3811
+ function statusIcon(status) {
3812
+ switch (status) {
3813
+ case "running":
3814
+ return { icon: "\u25CF", color: "green" };
3815
+ case "exited":
3816
+ return { icon: "\u25CB", color: "gray" };
3817
+ case "errored":
3818
+ return { icon: "\u25CF", color: "red" };
3819
+ }
3820
+ }
3821
+ function shortenPath(p) {
3822
+ const home = homedir();
3823
+ return p.startsWith(home) ? "~" + p.slice(home.length) : p;
3824
+ }
3825
+ var CARD_HEIGHT = 6;
3826
+ function ProcessList({
3827
+ onNavigate,
3828
+ onBack,
3829
+ width = 80,
3830
+ height = 24,
3831
+ panelMode = false,
3832
+ isInputActive = true
3833
+ }) {
3834
+ const [processes, setProcesses] = useState19(() => listProcesses());
3835
+ const [selectedIndex, setSelectedIndex] = useState19(0);
3836
+ const [feedback, setFeedback] = useState19();
3837
+ useEffect11(() => {
3838
+ const interval = setInterval(() => {
3839
+ setProcesses(listProcesses());
3840
+ }, 2e3);
3841
+ return () => clearInterval(interval);
3842
+ }, []);
3843
+ useEffect11(() => {
3844
+ if (feedback) {
3845
+ const timer = setTimeout(() => setFeedback(void 0), 2e3);
3846
+ return () => clearTimeout(timer);
3847
+ }
3848
+ }, [feedback]);
3849
+ useEffect11(() => {
3850
+ if (selectedIndex >= processes.length && processes.length > 0) {
3851
+ setSelectedIndex(processes.length - 1);
3852
+ }
3853
+ }, [processes.length, selectedIndex]);
3854
+ const handleStop = useCallback6(async (id) => {
3855
+ try {
3856
+ await stopProcess(id);
3857
+ setProcesses(listProcesses());
3858
+ setFeedback(`Stopped: ${id}`);
3859
+ } catch (err) {
3860
+ setFeedback(`Error: ${err instanceof Error ? err.message : "unknown"}`);
3861
+ }
3862
+ }, []);
3863
+ const handleRemove = useCallback6((id) => {
3864
+ try {
3865
+ removeProcess(id);
3866
+ setProcesses(listProcesses());
3867
+ setFeedback(`Removed: ${id}`);
3868
+ } catch (err) {
3869
+ setFeedback(`Error: ${err instanceof Error ? err.message : "unknown"}`);
3870
+ }
3871
+ }, []);
3872
+ useInput9((input2, key) => {
3873
+ if (!isInputActive) return;
3874
+ if (key.escape || key.leftArrow && !key.ctrl) {
3875
+ onBack();
3876
+ return;
3877
+ }
3878
+ if (processes.length === 0) return;
3879
+ if (key.upArrow || input2 === "k") {
3880
+ setSelectedIndex((i) => Math.max(0, i - 1));
3881
+ return;
3882
+ }
3883
+ if (key.downArrow || input2 === "j") {
3884
+ setSelectedIndex((i) => Math.min(processes.length - 1, i + 1));
3885
+ return;
3886
+ }
3887
+ if (key.rightArrow || key.return) {
3888
+ const proc = processes[selectedIndex];
3889
+ if (proc) onNavigate("process-logs", { processId: proc.id });
3890
+ return;
3891
+ }
3892
+ if (input2 === "x") {
3893
+ const proc = processes[selectedIndex];
3894
+ if (proc?.status === "running") handleStop(proc.id);
3895
+ return;
3896
+ }
3897
+ if (input2 === "d") {
3898
+ const proc = processes[selectedIndex];
3899
+ if (proc && proc.status !== "running") handleRemove(proc.id);
3900
+ return;
3901
+ }
3902
+ });
3903
+ const cardWidth = Math.max(30, (panelMode ? width - 4 : width) - 2);
3904
+ if (processes.length === 0) {
3905
+ return /* @__PURE__ */ jsxs25(Box23, { flexDirection: "column", paddingX: panelMode ? 1 : 0, children: [
3906
+ !panelMode && /* @__PURE__ */ jsx26(Box23, { marginBottom: 1, children: /* @__PURE__ */ jsx26(Text27, { bold: true, color: inkColors.accent, children: "Processes" }) }),
3907
+ /* @__PURE__ */ jsxs25(
3908
+ Box23,
3909
+ {
3910
+ flexDirection: "column",
3911
+ borderStyle: "round",
3912
+ borderColor: panel.borderDim,
3913
+ borderDimColor: true,
3914
+ paddingX: 1,
3915
+ width: cardWidth,
3916
+ children: [
3917
+ /* @__PURE__ */ jsx26(Text27, { dimColor: true, children: "No tracked processes" }),
3918
+ /* @__PURE__ */ jsx26(Text27, { dimColor: true, children: "Start processes via MCP tools or CLI" })
3919
+ ]
3920
+ }
3921
+ ),
3922
+ /* @__PURE__ */ jsx26(Box23, { marginTop: 1, gap: 2, children: /* @__PURE__ */ jsx26(Text27, { dimColor: true, children: "Esc:back" }) })
3923
+ ] });
3924
+ }
3925
+ const footerHeight = 2;
3926
+ const headerHeight = panelMode ? 0 : 2;
3927
+ const feedbackHeight = feedback ? 2 : 0;
3928
+ const availableHeight = height - headerHeight - footerHeight - feedbackHeight;
3929
+ const visibleCards = Math.max(1, Math.floor(availableHeight / CARD_HEIGHT));
3930
+ const windowStart = Math.max(0, Math.min(selectedIndex - Math.floor(visibleCards / 2), processes.length - visibleCards));
3931
+ const visibleProcesses = processes.slice(windowStart, windowStart + visibleCards);
3932
+ return /* @__PURE__ */ jsxs25(Box23, { flexDirection: "column", paddingX: panelMode ? 1 : 0, children: [
3933
+ !panelMode && /* @__PURE__ */ jsx26(Box23, { marginBottom: 1, children: /* @__PURE__ */ jsx26(Text27, { bold: true, color: inkColors.accent, children: "Processes" }) }),
3934
+ feedback && /* @__PURE__ */ jsx26(Box23, { marginBottom: 1, children: /* @__PURE__ */ jsx26(Text27, { color: inkColors.accent, children: feedback }) }),
3935
+ visibleProcesses.map((proc) => {
3936
+ const idx = processes.indexOf(proc);
3937
+ const isFocused = idx === selectedIndex;
3938
+ const si = statusIcon(proc.status);
3939
+ const pid = proc.pid ? String(proc.pid) : "\u2014";
3940
+ const uptimeOrExit = proc.status === "running" ? `UP ${formatUptime(proc.uptime)}` : `EXIT ${proc.exitCode ?? "?"} DURATION ${formatDuration(proc.startedAt, proc.exitedAt)}`;
3941
+ return /* @__PURE__ */ jsxs25(
3942
+ Box23,
3943
+ {
3944
+ flexDirection: "column",
3945
+ borderStyle: "round",
3946
+ borderColor: isFocused ? inkColors.accent : panel.borderDim,
3947
+ borderDimColor: !isFocused,
3948
+ paddingX: 1,
3949
+ width: cardWidth,
3950
+ children: [
3951
+ /* @__PURE__ */ jsxs25(Box23, { justifyContent: "space-between", children: [
3952
+ /* @__PURE__ */ jsxs25(Text27, { bold: true, children: [
3953
+ si.icon,
3954
+ " ",
3955
+ proc.id
3956
+ ] }),
3957
+ /* @__PURE__ */ jsx26(Text27, { color: si.color, children: proc.status })
3958
+ ] }),
3959
+ /* @__PURE__ */ jsxs25(Text27, { dimColor: true, children: [
3960
+ " CMD ",
3961
+ proc.command,
3962
+ " ",
3963
+ proc.args.join(" ")
3964
+ ] }),
3965
+ /* @__PURE__ */ jsxs25(Text27, { dimColor: true, children: [
3966
+ " PID ",
3967
+ pid,
3968
+ " CWD ",
3969
+ shortenPath(proc.cwd)
3970
+ ] }),
3971
+ /* @__PURE__ */ jsxs25(Text27, { dimColor: true, children: [
3972
+ " ",
3973
+ uptimeOrExit
3974
+ ] })
3975
+ ]
3976
+ },
3977
+ proc.id
3978
+ );
3979
+ }),
3980
+ processes.length > visibleCards && /* @__PURE__ */ jsx26(Box23, { children: /* @__PURE__ */ jsxs25(Text27, { dimColor: true, children: [
3981
+ windowStart > 0 ? "\u25B2 " : " ",
3982
+ windowStart + visibleCards < processes.length ? "\u25BC " : " ",
3983
+ processes.length,
3984
+ " processes"
3985
+ ] }) }),
3986
+ /* @__PURE__ */ jsxs25(Box23, { marginTop: 1, gap: 2, children: [
3987
+ /* @__PURE__ */ jsxs25(Text27, { dimColor: true, children: [
3988
+ "\u2192",
3989
+ ":logs"
3990
+ ] }),
3991
+ /* @__PURE__ */ jsx26(Text27, { dimColor: true, children: "x:stop" }),
3992
+ /* @__PURE__ */ jsx26(Text27, { dimColor: true, children: "d:remove" }),
3993
+ /* @__PURE__ */ jsx26(Text27, { dimColor: true, children: "Esc:back" })
3994
+ ] })
3995
+ ] });
3996
+ }
3997
+
3998
+ // src/screens/ProcessLogs.tsx
3999
+ import { useState as useState20, useEffect as useEffect12, useCallback as useCallback7 } from "react";
4000
+ import { Box as Box24, Text as Text28, useInput as useInput10 } from "ink";
4001
+ import { spawn as spawn3 } from "child_process";
4002
+ import { jsx as jsx27, jsxs as jsxs26 } from "react/jsx-runtime";
4003
+ function copyToClipboard2(text) {
4004
+ const cmd = process.platform === "darwin" ? "pbcopy" : "xclip";
4005
+ const args = process.platform === "darwin" ? [] : ["-selection", "clipboard"];
4006
+ const proc = spawn3(cmd, args, { stdio: ["pipe", "ignore", "ignore"] });
4007
+ proc.stdin?.write(text);
4008
+ proc.stdin?.end();
4009
+ }
4010
+ function ProcessLogs({
4011
+ processId,
4012
+ onBack,
4013
+ width = 80,
4014
+ height = 24,
4015
+ panelMode = false,
4016
+ isInputActive = true
4017
+ }) {
4018
+ const [stream, setStream] = useState20("both");
4019
+ const [lines, setLines] = useState20([]);
4020
+ const [proc, setProc] = useState20();
4021
+ const [feedback, setFeedback] = useState20();
4022
+ const logBoxHeight = Math.max(3, height - 6);
4023
+ useEffect12(() => {
4024
+ const refresh = () => {
4025
+ 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);
4028
+ setLines(combined);
4029
+ } catch {
4030
+ setLines([`Process "${processId}" not found`]);
4031
+ }
4032
+ const all = listProcesses();
4033
+ setProc(all.find((p) => p.id === processId));
4034
+ };
4035
+ refresh();
4036
+ const interval = setInterval(refresh, 1e3);
4037
+ return () => clearInterval(interval);
4038
+ }, [processId, logBoxHeight, stream]);
4039
+ useEffect12(() => {
4040
+ if (feedback) {
4041
+ const timer = setTimeout(() => setFeedback(void 0), 2e3);
4042
+ return () => clearTimeout(timer);
4043
+ }
4044
+ }, [feedback]);
4045
+ const handleStop = useCallback7(async () => {
4046
+ try {
4047
+ await stopProcess(processId);
4048
+ setFeedback("Stopped");
4049
+ } catch (err) {
4050
+ setFeedback(`Error: ${err instanceof Error ? err.message : "unknown"}`);
4051
+ }
4052
+ }, [processId]);
4053
+ const handleCopy = useCallback7(() => {
4054
+ try {
4055
+ copyToClipboard2(lines.join("\n"));
4056
+ setFeedback("Copied to clipboard");
4057
+ } catch {
4058
+ setFeedback("Copy failed");
4059
+ }
4060
+ }, [lines]);
4061
+ useInput10((input2, key) => {
4062
+ if (!isInputActive) return;
4063
+ if (key.escape || key.leftArrow && !key.ctrl) {
4064
+ onBack();
4065
+ return;
4066
+ }
4067
+ if (input2 === "s") {
4068
+ setStream((prev) => {
4069
+ if (prev === "both") return "stdout";
4070
+ if (prev === "stdout") return "stderr";
4071
+ return "both";
4072
+ });
4073
+ return;
4074
+ }
4075
+ if (input2 === "x") {
4076
+ if (proc?.status === "running") handleStop();
4077
+ return;
4078
+ }
4079
+ if (input2 === "c") {
4080
+ handleCopy();
4081
+ return;
4082
+ }
4083
+ });
4084
+ const statusColor = proc?.status === "running" ? "green" : proc?.status === "errored" ? "red" : "gray";
4085
+ const statusIcon2 = proc?.status === "running" ? "\u25CF" : "\u25CB";
4086
+ const cardWidth = Math.max(30, (panelMode ? width - 4 : width) - 2);
4087
+ return /* @__PURE__ */ jsxs26(Box24, { flexDirection: "column", paddingX: panelMode ? 1 : 0, children: [
4088
+ /* @__PURE__ */ jsxs26(Box24, { marginBottom: 1, gap: 2, children: [
4089
+ /* @__PURE__ */ jsx27(Text28, { bold: true, color: inkColors.accent, children: processId }),
4090
+ /* @__PURE__ */ jsxs26(Text28, { color: statusColor, children: [
4091
+ statusIcon2,
4092
+ " ",
4093
+ proc?.status ?? "unknown"
4094
+ ] }),
4095
+ proc?.pid && /* @__PURE__ */ jsxs26(Text28, { dimColor: true, children: [
4096
+ "PID ",
4097
+ proc.pid
4098
+ ] })
4099
+ ] }),
4100
+ /* @__PURE__ */ jsx27(
4101
+ Box24,
4102
+ {
4103
+ flexDirection: "column",
4104
+ borderStyle: "round",
4105
+ borderColor: inkColors.accent,
4106
+ paddingX: 1,
4107
+ height: logBoxHeight + 2,
4108
+ 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))
4111
+ }
4112
+ ),
4113
+ feedback && /* @__PURE__ */ jsx27(Box24, { children: /* @__PURE__ */ jsx27(Text28, { color: inkColors.accent, children: feedback }) }),
4114
+ /* @__PURE__ */ jsxs26(Box24, { marginTop: 1, gap: 2, children: [
4115
+ /* @__PURE__ */ jsxs26(Text28, { dimColor: true, children: [
4116
+ "stream:[",
4117
+ /* @__PURE__ */ jsx27(Text28, { bold: true, color: inkColors.accent, children: stream }),
4118
+ "]"
4119
+ ] }),
4120
+ /* @__PURE__ */ jsx27(Text28, { dimColor: true, children: "s:toggle" }),
4121
+ /* @__PURE__ */ jsx27(Text28, { dimColor: true, children: "x:stop" }),
4122
+ /* @__PURE__ */ jsx27(Text28, { dimColor: true, children: "c:copy" }),
4123
+ /* @__PURE__ */ jsx27(Text28, { dimColor: true, children: "Esc:back" })
4124
+ ] })
4125
+ ] });
4126
+ }
4127
+
4128
+ // src/screens/DeclarativePlan.tsx
4129
+ import { useEffect as useEffect13, useState as useState21 } from "react";
4130
+ import { Box as Box25, Text as Text29 } from "ink";
4131
+ import { jsx as jsx28, jsxs as jsxs27 } from "react/jsx-runtime";
4132
+ function DeclarativePlan({
4133
+ onBack,
4134
+ onNavigate,
4135
+ width = 80,
4136
+ height = 24,
4137
+ panelMode = false,
4138
+ isInputActive = true
4139
+ }) {
4140
+ const [phase, setPhase] = useState21("loading");
4141
+ const [actions, setActions] = useState21([]);
4142
+ const [applyProgress, setApplyProgress] = useState21("");
4143
+ const [applyResults, setApplyResults] = useState21([]);
4144
+ const { openEditor } = useEditor();
4145
+ const loadPlan = () => {
4146
+ setPhase("loading");
4147
+ setTimeout(() => {
4148
+ const yamlPath = findPolterYaml();
4149
+ if (!yamlPath) {
4150
+ setPhase("no-yaml");
4151
+ return;
4152
+ }
4153
+ const yaml = parsePolterYaml();
4154
+ if (!yaml) {
4155
+ setPhase("no-yaml");
4156
+ return;
4157
+ }
4158
+ const plan = planChanges(yaml);
4159
+ setActions(plan.actions);
4160
+ setPhase("plan-view");
4161
+ }, 0);
4162
+ };
4163
+ useEffect13(() => {
4164
+ loadPlan();
4165
+ }, []);
4166
+ if (phase === "loading") {
4167
+ return /* @__PURE__ */ jsx28(Box25, { flexDirection: "column", paddingX: panelMode ? 1 : 0, children: /* @__PURE__ */ jsx28(Text29, { color: inkColors.accent, children: "Loading plan..." }) });
4168
+ }
4169
+ if (phase === "no-yaml") {
4170
+ const items = [
4171
+ ...onNavigate ? [{ value: "init", label: "Init from current state", kind: "action" }] : [],
4172
+ { value: "editor", label: "Open editor to create polter.yaml", kind: "action" },
4173
+ ...!panelMode ? [{ value: "__back__", label: "\u2190 Back" }] : []
4174
+ ];
4175
+ const handleSelect2 = (value) => {
4176
+ if (value === "init" && onNavigate) {
4177
+ onNavigate("init-scaffold");
4178
+ } else if (value === "editor") {
4179
+ const cwd = process.cwd();
4180
+ openEditor(`${cwd}/polter.yaml`).then(() => loadPlan());
4181
+ } else {
4182
+ onBack();
4183
+ }
4184
+ };
4185
+ return /* @__PURE__ */ jsxs27(Box25, { flexDirection: "column", paddingX: panelMode ? 1 : 0, children: [
4186
+ /* @__PURE__ */ jsx28(Box25, { marginBottom: 1, children: /* @__PURE__ */ jsx28(Text29, { color: "yellow", children: "No polter.yaml found in the current directory." }) }),
4187
+ /* @__PURE__ */ jsx28(
4188
+ SelectList,
4189
+ {
4190
+ items,
4191
+ onSelect: handleSelect2,
4192
+ onCancel: onBack,
4193
+ width: panelMode ? Math.max(20, width - 4) : width,
4194
+ isInputActive,
4195
+ arrowNavigation: panelMode,
4196
+ panelFocused: panelMode ? isInputActive : void 0
4197
+ }
4198
+ ),
4199
+ !panelMode && /* @__PURE__ */ jsx28(StatusBar, { hint: "Enter select \\u00B7 Esc back", width })
4200
+ ] });
4201
+ }
4202
+ if (phase === "applying") {
4203
+ return /* @__PURE__ */ jsxs27(Box25, { flexDirection: "column", paddingX: panelMode ? 1 : 0, children: [
4204
+ /* @__PURE__ */ jsx28(Box25, { marginBottom: 1, children: /* @__PURE__ */ jsx28(Text29, { bold: true, color: inkColors.accent, children: "Applying changes..." }) }),
4205
+ /* @__PURE__ */ jsx28(Text29, { children: applyProgress })
4206
+ ] });
4207
+ }
4208
+ if (phase === "results") {
4209
+ const resultItems = [
4210
+ { value: "__section_results__", label: "Results", kind: "header", selectable: false },
4211
+ ...applyResults.map((r, i) => ({
4212
+ value: `result-${i}`,
4213
+ label: `${r.success ? "\u2713" : "\u2717"} ${r.action.description}`,
4214
+ hint: r.success ? "success" : r.result.stderr || "failed",
4215
+ kind: "header",
4216
+ selectable: false
4217
+ })),
4218
+ { value: "__section_actions__", label: "Actions", kind: "header", selectable: false },
4219
+ { value: "replan", label: "Re-run plan", kind: "action" },
4220
+ ...!panelMode ? [{ value: "__back__", label: "\u2190 Back" }] : []
4221
+ ];
4222
+ const handleSelect2 = (value) => {
4223
+ if (value === "replan") {
4224
+ loadPlan();
4225
+ } else {
4226
+ onBack();
4227
+ }
4228
+ };
4229
+ if (panelMode) {
4230
+ return /* @__PURE__ */ jsx28(Box25, { flexDirection: "column", paddingX: 1, children: /* @__PURE__ */ jsx28(
4231
+ SelectList,
4232
+ {
4233
+ items: resultItems,
4234
+ onSelect: handleSelect2,
4235
+ onCancel: onBack,
4236
+ boxedSections: true,
4237
+ width: Math.max(20, width - 4),
4238
+ maxVisible: Math.max(6, height - 6),
4239
+ isInputActive,
4240
+ arrowNavigation: true,
4241
+ panelFocused: isInputActive
4242
+ }
4243
+ ) });
4244
+ }
4245
+ return /* @__PURE__ */ jsxs27(Box25, { flexDirection: "column", children: [
4246
+ /* @__PURE__ */ jsx28(Box25, { marginBottom: 1, children: /* @__PURE__ */ jsx28(Text29, { bold: true, color: inkColors.accent, children: "Apply Results" }) }),
4247
+ /* @__PURE__ */ jsx28(
4248
+ SelectList,
4249
+ {
4250
+ items: resultItems,
4251
+ onSelect: handleSelect2,
4252
+ onCancel: onBack,
4253
+ boxedSections: true,
4254
+ width,
4255
+ maxVisible: Math.max(8, height - 10),
4256
+ isInputActive
4257
+ }
4258
+ ),
4259
+ /* @__PURE__ */ jsx28(StatusBar, { hint: "Enter select \\u00B7 Esc back", width })
4260
+ ] });
4261
+ }
4262
+ const actionPrefix = (a) => {
4263
+ switch (a.action) {
4264
+ case "create":
4265
+ return "+";
4266
+ case "update":
4267
+ return "~";
4268
+ case "delete":
4269
+ return "-";
4270
+ }
4271
+ };
4272
+ const actionColor = (a) => {
4273
+ switch (a.action) {
4274
+ case "create":
4275
+ return "green";
4276
+ case "update":
4277
+ return "yellow";
4278
+ case "delete":
4279
+ return "red";
4280
+ }
4281
+ };
4282
+ const planItems = [
4283
+ {
4284
+ value: "__section_plan__",
4285
+ label: actions.length > 0 ? `Plan: ${actions.length} action${actions.length > 1 ? "s" : ""}` : "No changes needed",
4286
+ kind: "header",
4287
+ selectable: false
4288
+ },
4289
+ ...actions.map((a, i) => ({
4290
+ value: `action-${i}`,
4291
+ label: `${actionPrefix(a)} ${a.description}`,
4292
+ hint: `${a.tool} ${a.resource}`,
4293
+ kind: "header",
4294
+ selectable: false
4295
+ })),
4296
+ { value: "__section_actions__", label: "Actions", kind: "header", selectable: false },
4297
+ ...actions.length > 0 ? [{ value: "apply", label: "Apply all changes", kind: "action" }] : [],
4298
+ { value: "editor", label: "Open polter.yaml in editor", kind: "action" },
4299
+ { value: "refresh", label: "Refresh plan", kind: "action" },
4300
+ ...!panelMode ? [{ value: "__back__", label: "\u2190 Back" }] : []
4301
+ ];
4302
+ const handleSelect = (value) => {
4303
+ if (value === "apply") {
4304
+ setPhase("applying");
4305
+ applyActions(actions, process.cwd(), (completed, total, current) => {
4306
+ setApplyProgress(`[${completed + 1}/${total}] ${current.description}...`);
4307
+ }).then((results) => {
4308
+ setApplyResults(results);
4309
+ setPhase("results");
4310
+ });
4311
+ } else if (value === "editor") {
4312
+ const yamlPath = findPolterYaml();
4313
+ if (yamlPath) {
4314
+ openEditor(yamlPath).then(() => loadPlan());
4315
+ }
4316
+ } else if (value === "refresh") {
4317
+ loadPlan();
4318
+ } else {
4319
+ onBack();
4320
+ }
4321
+ };
4322
+ if (panelMode) {
4323
+ return /* @__PURE__ */ jsx28(Box25, { flexDirection: "column", paddingX: 1, children: /* @__PURE__ */ jsx28(
4324
+ SelectList,
4325
+ {
4326
+ items: planItems,
4327
+ onSelect: handleSelect,
4328
+ onCancel: onBack,
4329
+ boxedSections: true,
4330
+ width: Math.max(20, width - 4),
4331
+ maxVisible: Math.max(6, height - 6),
4332
+ isInputActive,
4333
+ arrowNavigation: true,
4334
+ panelFocused: isInputActive
4335
+ }
4336
+ ) });
4337
+ }
4338
+ return /* @__PURE__ */ jsxs27(Box25, { flexDirection: "column", children: [
4339
+ /* @__PURE__ */ jsx28(Box25, { marginBottom: 1, children: /* @__PURE__ */ jsx28(Text29, { bold: true, color: inkColors.accent, children: "Plan / Apply" }) }),
4340
+ /* @__PURE__ */ jsx28(
4341
+ SelectList,
4342
+ {
4343
+ items: planItems,
4344
+ onSelect: handleSelect,
4345
+ onCancel: onBack,
4346
+ boxedSections: true,
4347
+ width,
4348
+ maxVisible: Math.max(8, height - 10),
4349
+ isInputActive
4350
+ }
4351
+ ),
4352
+ /* @__PURE__ */ jsx28(StatusBar, { hint: "Enter select \\u00B7 Esc back", width })
4353
+ ] });
4354
+ }
4355
+
4356
+ // src/screens/DeclarativeStatus.tsx
4357
+ import { useEffect as useEffect14, useState as useState22 } from "react";
4358
+ import { Box as Box26, Text as Text30 } from "ink";
4359
+ import { jsx as jsx29, jsxs as jsxs28 } from "react/jsx-runtime";
4360
+ function DeclarativeStatus({
4361
+ onBack,
4362
+ width = 80,
4363
+ height = 24,
4364
+ panelMode = false,
4365
+ isInputActive = true
4366
+ }) {
4367
+ const [phase, setPhase] = useState22("loading");
4368
+ const [status, setStatus] = useState22({});
4369
+ const [config2, setConfig] = useState22(null);
4370
+ const [pkgInfo, setPkgInfo] = useState22(null);
4371
+ const load = () => {
4372
+ setPhase("loading");
4373
+ setTimeout(() => {
4374
+ const s = getCurrentStatus();
4375
+ const c = getOrCreateProjectConfig();
4376
+ const p = detectPkgManager();
4377
+ setStatus(s);
4378
+ setConfig(c);
4379
+ setPkgInfo(p);
4380
+ setPhase("display");
4381
+ }, 0);
4382
+ };
4383
+ useEffect14(() => {
4384
+ load();
4385
+ }, []);
4386
+ if (phase === "loading") {
4387
+ return /* @__PURE__ */ jsx29(Box26, { flexDirection: "column", paddingX: panelMode ? 1 : 0, children: /* @__PURE__ */ jsx29(Text30, { color: inkColors.accent, children: "Loading infrastructure status..." }) });
4388
+ }
4389
+ const configuredPkg = config2?.tools?.pkg?.manager;
4390
+ const items = [
4391
+ { value: "__section_supabase__", label: "Supabase", kind: "header", selectable: false },
4392
+ {
4393
+ value: "__info_sb_linked__",
4394
+ label: `Linked: ${status.supabase?.linked ? "yes" : "no"}`,
4395
+ kind: "header",
4396
+ selectable: false
4397
+ },
4398
+ {
4399
+ value: "__info_sb_ref__",
4400
+ label: `Project ref: ${status.supabase?.projectRef ?? "not set"}`,
4401
+ kind: "header",
4402
+ selectable: false
4403
+ },
4404
+ {
4405
+ value: "__info_sb_fns__",
4406
+ label: `Functions: ${status.supabase?.functions?.length ? status.supabase.functions.join(", ") : "none"}`,
4407
+ kind: "header",
4408
+ selectable: false
4409
+ },
4410
+ { value: "__section_vercel__", label: "Vercel", kind: "header", selectable: false },
4411
+ {
4412
+ value: "__info_vc_linked__",
4413
+ label: `Linked: ${status.vercel?.linked ? "yes" : "no"}`,
4414
+ kind: "header",
4415
+ selectable: false
4416
+ },
4417
+ {
4418
+ value: "__info_vc_id__",
4419
+ label: `Project ID: ${status.vercel?.projectId ?? "not set"}`,
4420
+ kind: "header",
4421
+ selectable: false
4422
+ },
4423
+ { value: "__section_github__", label: "GitHub", kind: "header", selectable: false },
4424
+ {
4425
+ value: "__info_gh_auth__",
4426
+ label: `Authenticated: ${status.github?.authenticated ? "yes" : "no"}`,
4427
+ kind: "header",
4428
+ selectable: false
4429
+ },
4430
+ {
4431
+ value: "__info_gh_repo__",
4432
+ label: `Repo: ${status.github?.repo ?? "not set"}`,
4433
+ kind: "header",
4434
+ selectable: false
4435
+ },
4436
+ { value: "__section_pkg__", label: "Package Manager", kind: "header", selectable: false },
4437
+ {
4438
+ value: "__info_pkg__",
4439
+ label: `Detected: ${pkgInfo?.id ?? "npm"}${configuredPkg ? ` (configured: ${configuredPkg})` : ""}`,
4440
+ kind: "header",
4441
+ selectable: false
4442
+ },
4443
+ { value: "__section_actions__", label: "Actions", kind: "header", selectable: false },
4444
+ { value: "refresh", label: "Refresh", kind: "action" },
4445
+ ...!panelMode ? [{ value: "__back__", label: "\u2190 Back" }] : []
4446
+ ];
4447
+ const handleSelect = (value) => {
4448
+ if (value === "refresh") {
4449
+ load();
4450
+ } else if (value === "__back__") {
4451
+ onBack();
4452
+ }
4453
+ };
4454
+ if (panelMode) {
4455
+ return /* @__PURE__ */ jsx29(Box26, { flexDirection: "column", paddingX: 1, children: /* @__PURE__ */ jsx29(
4456
+ SelectList,
4457
+ {
4458
+ items,
4459
+ onSelect: handleSelect,
4460
+ onCancel: onBack,
4461
+ boxedSections: true,
4462
+ width: Math.max(20, width - 4),
4463
+ maxVisible: Math.max(6, height - 6),
4464
+ isInputActive,
4465
+ arrowNavigation: true,
4466
+ panelFocused: isInputActive
4467
+ }
4468
+ ) });
4469
+ }
4470
+ return /* @__PURE__ */ jsxs28(Box26, { flexDirection: "column", children: [
4471
+ /* @__PURE__ */ jsx29(Box26, { marginBottom: 1, children: /* @__PURE__ */ jsx29(Text30, { bold: true, color: inkColors.accent, children: "Infrastructure Status" }) }),
4472
+ /* @__PURE__ */ jsx29(
4473
+ SelectList,
4474
+ {
4475
+ items,
4476
+ onSelect: handleSelect,
4477
+ onCancel: onBack,
4478
+ boxedSections: true,
4479
+ width,
4480
+ maxVisible: Math.max(8, height - 10),
4481
+ isInputActive
4482
+ }
4483
+ ),
4484
+ /* @__PURE__ */ jsx29(StatusBar, { hint: "Enter select \\u00B7 Esc back", width })
4485
+ ] });
4486
+ }
4487
+
4488
+ // src/screens/InitScaffold.tsx
4489
+ import { useEffect as useEffect15, useState as useState23 } from "react";
4490
+ import { Box as Box27, Text as Text31 } from "ink";
4491
+ import { writeFileSync } from "fs";
4492
+ import { join } from "path";
4493
+
4494
+ // src/lib/yamlWriter.ts
4495
+ function generatePolterYaml(config2) {
4496
+ const lines = [];
4497
+ lines.push(`version: ${config2.version}`);
4498
+ if (config2.project) {
4499
+ lines.push("");
4500
+ lines.push("project:");
4501
+ lines.push(` name: ${quote(config2.project.name)}`);
4502
+ }
4503
+ if (config2.supabase) {
4504
+ lines.push("");
4505
+ lines.push("supabase:");
4506
+ if (config2.supabase.project_ref) {
4507
+ lines.push(` project_ref: ${quote(config2.supabase.project_ref)}`);
4508
+ }
4509
+ if (config2.supabase.region) {
4510
+ lines.push(` region: ${quote(config2.supabase.region)}`);
4511
+ }
4512
+ if (config2.supabase.database) {
4513
+ lines.push(" database:");
4514
+ if (config2.supabase.database.migrations_dir) {
4515
+ lines.push(` migrations_dir: ${quote(config2.supabase.database.migrations_dir)}`);
4516
+ }
4517
+ }
4518
+ if (config2.supabase.functions && config2.supabase.functions.length > 0) {
4519
+ lines.push(" functions:");
4520
+ for (const fn of config2.supabase.functions) {
4521
+ lines.push(` - name: ${quote(fn.name)}`);
4522
+ if (fn.verify_jwt !== void 0) {
4523
+ lines.push(` verify_jwt: ${fn.verify_jwt}`);
4524
+ }
4525
+ }
4526
+ }
4527
+ if (config2.supabase.secrets && config2.supabase.secrets.length > 0) {
4528
+ lines.push(" secrets:");
4529
+ for (const secret of config2.supabase.secrets) {
4530
+ lines.push(` - ${quote(secret)}`);
4531
+ }
4532
+ }
4533
+ }
4534
+ if (config2.vercel) {
4535
+ lines.push("");
4536
+ lines.push("vercel:");
4537
+ if (config2.vercel.project_id) {
4538
+ lines.push(` project_id: ${quote(config2.vercel.project_id)}`);
4539
+ }
4540
+ if (config2.vercel.framework) {
4541
+ lines.push(` framework: ${quote(config2.vercel.framework)}`);
4542
+ }
4543
+ if (config2.vercel.domains && config2.vercel.domains.length > 0) {
4544
+ lines.push(" domains:");
4545
+ for (const domain of config2.vercel.domains) {
4546
+ lines.push(` - ${quote(domain)}`);
4547
+ }
4548
+ }
4549
+ if (config2.vercel.env) {
4550
+ lines.push(" env:");
4551
+ for (const [env, vars] of Object.entries(config2.vercel.env)) {
4552
+ lines.push(` ${env}:`);
4553
+ for (const [key, value] of Object.entries(vars)) {
4554
+ lines.push(` ${key}: ${quote(value)}`);
4555
+ }
4556
+ }
4557
+ }
4558
+ }
4559
+ if (config2.github) {
4560
+ lines.push("");
4561
+ lines.push("github:");
4562
+ if (config2.github.repo) {
4563
+ lines.push(` repo: ${quote(config2.github.repo)}`);
4564
+ }
4565
+ if (config2.github.branch_protection) {
4566
+ lines.push(" branch_protection:");
4567
+ for (const [branch, rules] of Object.entries(config2.github.branch_protection)) {
4568
+ lines.push(` ${branch}:`);
4569
+ if (rules.required_reviews !== void 0) {
4570
+ lines.push(` required_reviews: ${rules.required_reviews}`);
4571
+ }
4572
+ if (rules.require_status_checks !== void 0) {
4573
+ lines.push(` require_status_checks: ${rules.require_status_checks}`);
4574
+ }
4575
+ }
4576
+ }
4577
+ if (config2.github.secrets && config2.github.secrets.length > 0) {
4578
+ lines.push(" secrets:");
4579
+ for (const secret of config2.github.secrets) {
4580
+ lines.push(` - ${quote(secret)}`);
4581
+ }
4582
+ }
4583
+ }
4584
+ if (config2.pkg) {
4585
+ lines.push("");
4586
+ lines.push("pkg:");
4587
+ if (config2.pkg.manager) {
4588
+ lines.push(` manager: ${config2.pkg.manager}`);
4589
+ }
4590
+ }
4591
+ if (config2.pipelines) {
4592
+ lines.push("");
4593
+ lines.push("pipelines:");
4594
+ for (const [name, pipeline] of Object.entries(config2.pipelines)) {
4595
+ lines.push(` ${name}:`);
4596
+ if (pipeline.description) {
4597
+ lines.push(` description: ${quote(pipeline.description)}`);
4598
+ }
4599
+ lines.push(" steps:");
4600
+ for (const step of pipeline.steps) {
4601
+ lines.push(` - ${quote(step)}`);
4602
+ }
4603
+ }
4604
+ }
4605
+ lines.push("");
4606
+ return lines.join("\n");
4607
+ }
4608
+ function needsQuoting(value) {
4609
+ if (!value) return true;
4610
+ if (/^[\d]/.test(value) && /[^\d.]/.test(value)) return true;
4611
+ if (/[:#\[\]{}&*!|>'"%@`]/.test(value)) return true;
4612
+ if (value === "true" || value === "false" || value === "null") return true;
4613
+ if (value.includes(" ") && !value.startsWith('"')) return true;
4614
+ return false;
4615
+ }
4616
+ function quote(value) {
4617
+ if (needsQuoting(value)) {
4618
+ return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
4619
+ }
4620
+ return value;
4621
+ }
4622
+
4623
+ // src/screens/InitScaffold.tsx
4624
+ import { jsx as jsx30, jsxs as jsxs29 } from "react/jsx-runtime";
4625
+ function InitScaffold({
4626
+ onBack,
4627
+ onNavigate,
4628
+ width = 80,
4629
+ height = 24,
4630
+ panelMode = false,
4631
+ isInputActive = true
4632
+ }) {
4633
+ const [phase, setPhase] = useState23("detecting");
4634
+ const [yamlString, setYamlString] = useState23("");
4635
+ const [overwrite, setOverwrite] = useState23(false);
4636
+ const { openEditor } = useEditor();
4637
+ const cwd = process.cwd();
4638
+ const yamlPath = join(cwd, "polter.yaml");
4639
+ const detect = (forceOverwrite = false) => {
4640
+ setPhase("detecting");
4641
+ setTimeout(() => {
4642
+ const existing = findPolterYaml();
4643
+ if (existing && !forceOverwrite) {
4644
+ setPhase("already-exists");
4645
+ return;
4646
+ }
4647
+ const status = getCurrentStatus();
4648
+ const config2 = getOrCreateProjectConfig();
4649
+ const pkg = detectPkgManager();
4650
+ const yaml = {
4651
+ version: 1,
4652
+ project: { name: cwd.split("/").pop() ?? "my-project" }
4653
+ };
4654
+ if (status.supabase) {
4655
+ yaml.supabase = {};
4656
+ if (config2.tools.supabase?.projectRef) {
4657
+ yaml.supabase.project_ref = config2.tools.supabase.projectRef;
4658
+ }
4659
+ if (status.supabase.functions && status.supabase.functions.length > 0) {
4660
+ yaml.supabase.functions = status.supabase.functions.map((name) => ({ name }));
4661
+ }
4662
+ }
4663
+ if (status.vercel) {
4664
+ yaml.vercel = {};
4665
+ if (config2.tools.vercel?.projectId) {
4666
+ yaml.vercel.project_id = config2.tools.vercel.projectId;
4667
+ }
4668
+ }
4669
+ if (status.github) {
4670
+ yaml.github = {};
4671
+ if (status.github.repo) {
4672
+ yaml.github.repo = status.github.repo;
4673
+ }
4674
+ }
4675
+ yaml.pkg = { manager: config2.tools.pkg?.manager ?? pkg.id };
4676
+ const generated = generatePolterYaml(yaml);
4677
+ setYamlString(generated);
4678
+ setPhase("preview");
4679
+ }, 0);
4680
+ };
4681
+ useEffect15(() => {
4682
+ detect();
4683
+ }, []);
4684
+ if (phase === "detecting") {
4685
+ return /* @__PURE__ */ jsx30(Box27, { flexDirection: "column", paddingX: panelMode ? 1 : 0, children: /* @__PURE__ */ jsx30(Text31, { color: inkColors.accent, children: "Detecting project state..." }) });
4686
+ }
4687
+ if (phase === "already-exists") {
4688
+ const items2 = [
4689
+ { value: "editor", label: "Open in editor", kind: "action" },
4690
+ { value: "overwrite", label: "Overwrite with detected state", kind: "action" },
4691
+ ...!panelMode ? [{ value: "__back__", label: "\u2190 Back" }] : []
4692
+ ];
4693
+ const handleSelect2 = (value) => {
4694
+ if (value === "editor") {
4695
+ openEditor(yamlPath);
4696
+ } else if (value === "overwrite") {
4697
+ detect(true);
4698
+ } else {
4699
+ onBack();
4700
+ }
4701
+ };
4702
+ return /* @__PURE__ */ jsxs29(Box27, { flexDirection: "column", paddingX: panelMode ? 1 : 0, children: [
4703
+ /* @__PURE__ */ jsx30(Box27, { marginBottom: 1, children: /* @__PURE__ */ jsx30(Text31, { color: "yellow", children: "polter.yaml already exists." }) }),
4704
+ /* @__PURE__ */ jsx30(
4705
+ SelectList,
4706
+ {
4707
+ items: items2,
4708
+ onSelect: handleSelect2,
4709
+ onCancel: onBack,
4710
+ width: panelMode ? Math.max(20, width - 4) : width,
4711
+ isInputActive,
4712
+ arrowNavigation: panelMode,
4713
+ panelFocused: panelMode ? isInputActive : void 0
4714
+ }
4715
+ ),
4716
+ !panelMode && /* @__PURE__ */ jsx30(StatusBar, { hint: "Enter select \\u00B7 Esc back", width })
4717
+ ] });
4718
+ }
4719
+ if (phase === "preview") {
4720
+ const previewLines = yamlString.split("\n").slice(0, Math.max(8, height - 12));
4721
+ const items2 = [
4722
+ { value: "write", label: "Write polter.yaml", kind: "action" },
4723
+ { value: "edit-first", label: "Edit before writing", kind: "action" },
4724
+ ...!panelMode ? [{ value: "__back__", label: "\u2190 Back" }] : []
4725
+ ];
4726
+ const handleSelect2 = (value) => {
4727
+ if (value === "write") {
4728
+ writeFileSync(yamlPath, yamlString, "utf-8");
4729
+ setPhase("done");
4730
+ } else if (value === "edit-first") {
4731
+ writeFileSync(yamlPath, yamlString, "utf-8");
4732
+ openEditor(yamlPath);
4733
+ } else {
4734
+ onBack();
4735
+ }
4736
+ };
4737
+ return /* @__PURE__ */ jsxs29(Box27, { flexDirection: "column", paddingX: panelMode ? 1 : 0, children: [
4738
+ /* @__PURE__ */ jsx30(Box27, { marginBottom: 1, children: /* @__PURE__ */ jsx30(Text31, { bold: true, color: inkColors.accent, children: "Preview: polter.yaml" }) }),
4739
+ /* @__PURE__ */ jsx30(Box27, { flexDirection: "column", marginBottom: 1, marginLeft: panelMode ? 1 : 2, children: previewLines.map((line, i) => /* @__PURE__ */ jsx30(Text31, { dimColor: true, children: line }, i)) }),
4740
+ /* @__PURE__ */ jsx30(
4741
+ SelectList,
4742
+ {
4743
+ items: items2,
4744
+ onSelect: handleSelect2,
4745
+ onCancel: onBack,
4746
+ width: panelMode ? Math.max(20, width - 4) : width,
4747
+ isInputActive,
4748
+ arrowNavigation: panelMode,
4749
+ panelFocused: panelMode ? isInputActive : void 0
4750
+ }
4751
+ ),
4752
+ !panelMode && /* @__PURE__ */ jsx30(StatusBar, { hint: "Enter select \\u00B7 Esc back", width })
4753
+ ] });
4754
+ }
4755
+ if (phase === "writing") {
4756
+ return /* @__PURE__ */ jsx30(Box27, { flexDirection: "column", paddingX: panelMode ? 1 : 0, children: /* @__PURE__ */ jsx30(Text31, { color: inkColors.accent, children: "Writing polter.yaml..." }) });
4757
+ }
4758
+ const items = [
4759
+ ...onNavigate ? [{ value: "plan", label: "View plan", kind: "action" }] : [],
4760
+ { value: "editor", label: "Open in editor", kind: "action" },
4761
+ ...!panelMode ? [{ value: "__back__", label: "\u2190 Back" }] : []
4762
+ ];
4763
+ const handleSelect = (value) => {
4764
+ if (value === "plan" && onNavigate) {
4765
+ onNavigate("declarative-plan");
4766
+ } else if (value === "editor") {
4767
+ openEditor(yamlPath);
4768
+ } else {
4769
+ onBack();
4770
+ }
4771
+ };
4772
+ return /* @__PURE__ */ jsxs29(Box27, { flexDirection: "column", paddingX: panelMode ? 1 : 0, children: [
4773
+ /* @__PURE__ */ jsx30(Box27, { marginBottom: 1, children: /* @__PURE__ */ jsxs29(Text31, { color: inkColors.accent, children: [
4774
+ "\u2713",
4775
+ " polter.yaml created successfully!"
4776
+ ] }) }),
4777
+ /* @__PURE__ */ jsx30(
4778
+ SelectList,
4779
+ {
4780
+ items,
4781
+ onSelect: handleSelect,
4782
+ onCancel: onBack,
4783
+ width: panelMode ? Math.max(20, width - 4) : width,
4784
+ isInputActive,
4785
+ arrowNavigation: panelMode,
4786
+ panelFocused: panelMode ? isInputActive : void 0
4787
+ }
4788
+ ),
4789
+ !panelMode && /* @__PURE__ */ jsx30(StatusBar, { hint: "Enter select \\u00B7 Esc back", width })
4790
+ ] });
4791
+ }
4792
+
4793
+ // src/app.tsx
4794
+ import { jsx as jsx31, jsxs as jsxs30 } from "react/jsx-runtime";
4795
+ function AppClassic() {
4796
+ const { screen, params, navigate, goBack, goHome } = useNavigation();
4797
+ const { exit } = useApp();
4798
+ const width = useTerminalWidth();
4799
+ const height = useTerminalHeight();
4800
+ const handleExit = () => {
4801
+ process.stdout.write(
4802
+ "\n" + colors.dim("Thank you for using ") + colors.primaryBold("Polter") + colors.dim("!") + "\n\n"
3260
4803
  );
3261
4804
  exit();
3262
4805
  };
3263
4806
  const renderScreen = () => {
3264
4807
  switch (screen) {
3265
4808
  case "home":
3266
- return /* @__PURE__ */ jsx25(Home, { onNavigate: navigate, onExit: handleExit, width, height });
4809
+ return /* @__PURE__ */ jsx31(Home, { onNavigate: navigate, onExit: handleExit, width, height });
3267
4810
  case "command-args":
3268
- return /* @__PURE__ */ jsx25(
4811
+ return /* @__PURE__ */ jsx31(
3269
4812
  CommandArgs,
3270
4813
  {
3271
4814
  command: params.command ?? "",
@@ -3276,9 +4819,9 @@ function AppClassic() {
3276
4819
  }
3277
4820
  );
3278
4821
  case "custom-command":
3279
- return /* @__PURE__ */ jsx25(CustomCommand, { onNavigate: navigate, onBack: goBack, width });
4822
+ return /* @__PURE__ */ jsx31(CustomCommand, { onNavigate: navigate, onBack: goBack, width });
3280
4823
  case "flag-selection":
3281
- return /* @__PURE__ */ jsx25(
4824
+ return /* @__PURE__ */ jsx31(
3282
4825
  FlagSelection,
3283
4826
  {
3284
4827
  args: params.args ?? [],
@@ -3290,7 +4833,7 @@ function AppClassic() {
3290
4833
  );
3291
4834
  case "confirm-execute":
3292
4835
  case "command-execution":
3293
- return /* @__PURE__ */ jsx25(
4836
+ return /* @__PURE__ */ jsx31(
3294
4837
  CommandExecution,
3295
4838
  {
3296
4839
  args: params.args ?? [],
@@ -3305,17 +4848,23 @@ function AppClassic() {
3305
4848
  }
3306
4849
  );
3307
4850
  case "self-update":
3308
- return /* @__PURE__ */ jsx25(SelfUpdate, { onBack: goBack, onExit: handleExit, width });
4851
+ return /* @__PURE__ */ jsx31(SelfUpdate, { onBack: goBack, onExit: handleExit, width });
3309
4852
  case "tool-status":
3310
- return /* @__PURE__ */ jsx25(ToolStatus, { onBack: goBack, width });
4853
+ return /* @__PURE__ */ jsx31(ToolStatus, { onBack: goBack, onNavigate: navigate, width });
4854
+ case "mcp-manage":
4855
+ return /* @__PURE__ */ jsx31(McpManage, { onBack: goBack, width });
4856
+ case "process-list":
4857
+ return /* @__PURE__ */ jsx31(ProcessList, { onNavigate: navigate, onBack: goBack, width, height });
4858
+ case "process-logs":
4859
+ return /* @__PURE__ */ jsx31(ProcessLogs, { processId: params.processId ?? "", onBack: goBack, width, height });
3311
4860
  case "project-config":
3312
- return /* @__PURE__ */ jsx25(ProjectConfig, { onBack: goBack, width });
4861
+ return /* @__PURE__ */ jsx31(ProjectConfig, { onBack: goBack, width });
3313
4862
  case "pipeline-list":
3314
- return /* @__PURE__ */ jsx25(PipelineList, { onNavigate: navigate, onBack: goBack, width });
4863
+ return /* @__PURE__ */ jsx31(PipelineList, { onNavigate: navigate, onBack: goBack, width });
3315
4864
  case "pipeline-builder":
3316
- return /* @__PURE__ */ jsx25(PipelineBuilder, { onBack: goBack, width, height });
4865
+ return /* @__PURE__ */ jsx31(PipelineBuilder, { onBack: goBack, width, height });
3317
4866
  case "pipeline-execution":
3318
- return /* @__PURE__ */ jsx25(
4867
+ return /* @__PURE__ */ jsx31(
3319
4868
  PipelineExecution,
3320
4869
  {
3321
4870
  pipelineId: params.pipelineId ?? "",
@@ -3324,34 +4873,40 @@ function AppClassic() {
3324
4873
  width
3325
4874
  }
3326
4875
  );
4876
+ case "declarative-plan":
4877
+ return /* @__PURE__ */ jsx31(DeclarativePlan, { onBack: goBack, onNavigate: navigate, width, height });
4878
+ case "declarative-status":
4879
+ return /* @__PURE__ */ jsx31(DeclarativeStatus, { onBack: goBack, width, height });
4880
+ case "init-scaffold":
4881
+ return /* @__PURE__ */ jsx31(InitScaffold, { onBack: goBack, onNavigate: navigate, width, height });
3327
4882
  default:
3328
- return /* @__PURE__ */ jsx25(Box22, { children: /* @__PURE__ */ jsxs24(Text26, { color: "red", children: [
4883
+ return /* @__PURE__ */ jsx31(Box28, { children: /* @__PURE__ */ jsxs30(Text32, { color: "red", children: [
3329
4884
  "Unknown screen: ",
3330
4885
  screen
3331
4886
  ] }) });
3332
4887
  }
3333
4888
  };
3334
- return /* @__PURE__ */ jsxs24(Box22, { flexDirection: "column", children: [
3335
- /* @__PURE__ */ jsx25(GhostBanner, { width }),
4889
+ return /* @__PURE__ */ jsxs30(Box28, { flexDirection: "column", children: [
4890
+ /* @__PURE__ */ jsx31(GhostBanner, { width }),
3336
4891
  renderScreen()
3337
4892
  ] });
3338
4893
  }
3339
4894
 
3340
4895
  // src/appPanel.tsx
3341
- import React19 from "react";
3342
- import { Box as Box30, Text as Text33, useApp as useApp2, useInput as useInput9 } from "ink";
4896
+ import React26 from "react";
4897
+ import { Box as Box37, Text as Text39, useApp as useApp2, useInput as useInput12 } from "ink";
3343
4898
 
3344
4899
  // src/hooks/usePanelNavigation.ts
3345
- import { useState as useState17, useCallback as useCallback5 } from "react";
4900
+ import { useState as useState24, useCallback as useCallback8 } from "react";
3346
4901
  function usePanelNavigation() {
3347
- const [state, setState] = useState17({
4902
+ const [state, setState] = useState24({
3348
4903
  view: "pipelines",
3349
4904
  featureId: "database",
3350
4905
  innerScreen: "home",
3351
4906
  innerParams: {},
3352
4907
  innerStack: []
3353
4908
  });
3354
- const selectSidebarItem = useCallback5((itemId) => {
4909
+ const selectSidebarItem = useCallback8((itemId) => {
3355
4910
  const featureIds = [
3356
4911
  "database",
3357
4912
  "functions",
@@ -3360,6 +4915,7 @@ function usePanelNavigation() {
3360
4915
  "cicd",
3361
4916
  "auth-storage",
3362
4917
  "networking",
4918
+ "packages",
3363
4919
  "infrastructure",
3364
4920
  "setup"
3365
4921
  ];
@@ -3379,7 +4935,9 @@ function usePanelNavigation() {
3379
4935
  pipelines: "pipelines",
3380
4936
  "tool-status": "tool-status",
3381
4937
  config: "config",
3382
- "self-update": "self-update"
4938
+ "self-update": "self-update",
4939
+ processes: "processes",
4940
+ declarative: "declarative"
3383
4941
  };
3384
4942
  const view = viewMap[itemId];
3385
4943
  if (view) {
@@ -3392,7 +4950,7 @@ function usePanelNavigation() {
3392
4950
  });
3393
4951
  }
3394
4952
  }, [state.featureId]);
3395
- const navigateInner = useCallback5((screen, params) => {
4953
+ const navigateInner = useCallback8((screen, params) => {
3396
4954
  setState((prev) => ({
3397
4955
  ...prev,
3398
4956
  innerStack: [...prev.innerStack, { screen: prev.innerScreen, params: prev.innerParams }],
@@ -3400,7 +4958,7 @@ function usePanelNavigation() {
3400
4958
  innerParams: params ?? {}
3401
4959
  }));
3402
4960
  }, []);
3403
- const goBackInner = useCallback5(() => {
4961
+ const goBackInner = useCallback8(() => {
3404
4962
  setState((prev) => {
3405
4963
  if (prev.innerStack.length === 0) {
3406
4964
  return { ...prev, innerScreen: "home", innerParams: {} };
@@ -3415,7 +4973,7 @@ function usePanelNavigation() {
3415
4973
  };
3416
4974
  });
3417
4975
  }, []);
3418
- const goHomeInner = useCallback5(() => {
4976
+ const goHomeInner = useCallback8(() => {
3419
4977
  setState((prev) => ({
3420
4978
  ...prev,
3421
4979
  innerScreen: "home",
@@ -3423,7 +4981,7 @@ function usePanelNavigation() {
3423
4981
  innerStack: []
3424
4982
  }));
3425
4983
  }, []);
3426
- const switchViewAndNavigate = useCallback5(
4984
+ const switchViewAndNavigate = useCallback8(
3427
4985
  (view, screen, params) => {
3428
4986
  setState((prev) => ({
3429
4987
  ...prev,
@@ -3446,14 +5004,14 @@ function usePanelNavigation() {
3446
5004
  }
3447
5005
 
3448
5006
  // src/hooks/usePanelFocus.ts
3449
- import { useState as useState18, useCallback as useCallback6 } from "react";
5007
+ import { useState as useState25, useCallback as useCallback9 } from "react";
3450
5008
  function usePanelFocus() {
3451
- const [focused, setFocused] = useState18("sidebar");
3452
- const toggleFocus = useCallback6(() => {
5009
+ const [focused, setFocused] = useState25("sidebar");
5010
+ const toggleFocus = useCallback9(() => {
3453
5011
  setFocused((prev) => prev === "sidebar" ? "main" : "sidebar");
3454
5012
  }, []);
3455
- const focusSidebar = useCallback6(() => setFocused("sidebar"), []);
3456
- const focusMain = useCallback6(() => setFocused("main"), []);
5013
+ const focusSidebar = useCallback9(() => setFocused("sidebar"), []);
5014
+ const focusMain = useCallback9(() => setFocused("main"), []);
3457
5015
  return {
3458
5016
  focused,
3459
5017
  isSidebarFocused: focused === "sidebar",
@@ -3473,6 +5031,7 @@ function useSidebarItems() {
3473
5031
  items.push({ id: "pipelines", label: "Pipelines", icon: "\u{1F517}", type: "action", section: "workflows" });
3474
5032
  items.push({ id: "pinned", label: "Pinned", icon: "\u{1F4CC}", type: "action", section: "workflows" });
3475
5033
  items.push({ id: "custom-command", label: "Custom Cmd", icon: "\u270F\uFE0F", type: "action", section: "workflows" });
5034
+ items.push({ id: "processes", label: "Processes", icon: "\u{1F4BB}", type: "action", section: "workflows" });
3476
5035
  items.push({ id: "__sep_features__", label: "---", icon: "", type: "separator", sectionTitle: "Features" });
3477
5036
  for (const feature of features) {
3478
5037
  items.push({
@@ -3484,6 +5043,7 @@ function useSidebarItems() {
3484
5043
  });
3485
5044
  }
3486
5045
  items.push({ id: "__sep_system__", label: "---", icon: "", type: "separator", sectionTitle: "System" });
5046
+ items.push({ id: "declarative", label: "Infrastructure", icon: "\u{1F3D7}\uFE0F", type: "action", section: "system" });
3487
5047
  items.push({ id: "tool-status", label: "Tool Status", icon: "\u{1F527}", type: "action", section: "system" });
3488
5048
  items.push({ id: "config", label: "Config", icon: "\u2699\uFE0F", type: "action", section: "system" });
3489
5049
  items.push({ id: "self-update", label: "Update", icon: "\u2B06\uFE0F", type: "action", section: "system" });
@@ -3492,13 +5052,13 @@ function useSidebarItems() {
3492
5052
  }
3493
5053
 
3494
5054
  // src/hooks/useModal.ts
3495
- import { useState as useState19, useCallback as useCallback7 } from "react";
5055
+ import { useState as useState26, useCallback as useCallback10 } from "react";
3496
5056
  function useModal() {
3497
- const [state, setState] = useState19(null);
3498
- const openModal = useCallback7((content, title) => {
5057
+ const [state, setState] = useState26(null);
5058
+ const openModal = useCallback10((content, title) => {
3499
5059
  setState({ content, title });
3500
5060
  }, []);
3501
- const closeModal = useCallback7(() => {
5061
+ const closeModal = useCallback10(() => {
3502
5062
  setState(null);
3503
5063
  }, []);
3504
5064
  return {
@@ -3511,8 +5071,8 @@ function useModal() {
3511
5071
  }
3512
5072
 
3513
5073
  // src/components/PanelLayout.tsx
3514
- import { Box as Box23 } from "ink";
3515
- import { jsx as jsx26, jsxs as jsxs25 } from "react/jsx-runtime";
5074
+ import { Box as Box29 } from "ink";
5075
+ import { jsx as jsx32, jsxs as jsxs31 } from "react/jsx-runtime";
3516
5076
  function PanelLayout({
3517
5077
  header,
3518
5078
  footer,
@@ -3527,19 +5087,19 @@ function PanelLayout({
3527
5087
  const contentHeight = Math.max(5, height - bannerHeight - footerHeight);
3528
5088
  const sidebarWidth = singlePanel ? 0 : panel.sidebarWidth(width);
3529
5089
  const mainWidth = singlePanel ? width : width - sidebarWidth;
3530
- return /* @__PURE__ */ jsxs25(Box23, { flexDirection: "column", width, height, children: [
5090
+ return /* @__PURE__ */ jsxs31(Box29, { flexDirection: "column", width, height, children: [
3531
5091
  header,
3532
- /* @__PURE__ */ jsxs25(Box23, { flexDirection: "row", height: contentHeight, children: [
3533
- !singlePanel && /* @__PURE__ */ jsx26(Box23, { width: sidebarWidth, height: contentHeight, children: sidebar }),
3534
- /* @__PURE__ */ jsx26(Box23, { width: mainWidth, height: contentHeight, children: main2 })
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 })
3535
5095
  ] }),
3536
- /* @__PURE__ */ jsx26(Box23, { height: footerHeight, children: footer })
5096
+ /* @__PURE__ */ jsx32(Box29, { height: footerHeight, children: footer })
3537
5097
  ] });
3538
5098
  }
3539
5099
 
3540
5100
  // src/components/Panel.tsx
3541
- import { Box as Box24, Text as Text27 } from "ink";
3542
- import { jsx as jsx27, jsxs as jsxs26 } from "react/jsx-runtime";
5101
+ import { Box as Box30, Text as Text33 } from "ink";
5102
+ import { jsx as jsx33, jsxs as jsxs32 } from "react/jsx-runtime";
3543
5103
  function Panel({
3544
5104
  id,
3545
5105
  title,
@@ -3549,8 +5109,8 @@ function Panel({
3549
5109
  children
3550
5110
  }) {
3551
5111
  const borderColor = focused ? panel.borderFocused : panel.borderDim;
3552
- return /* @__PURE__ */ jsxs26(
3553
- Box24,
5112
+ return /* @__PURE__ */ jsxs32(
5113
+ Box30,
3554
5114
  {
3555
5115
  flexDirection: "column",
3556
5116
  width,
@@ -3559,21 +5119,21 @@ function Panel({
3559
5119
  borderColor,
3560
5120
  overflow: "hidden",
3561
5121
  children: [
3562
- title && /* @__PURE__ */ jsx27(Box24, { marginBottom: 0, children: /* @__PURE__ */ jsxs26(Text27, { color: focused ? inkColors.accent : void 0, bold: focused, dimColor: !focused, children: [
5122
+ title && /* @__PURE__ */ jsx33(Box30, { marginBottom: 0, children: /* @__PURE__ */ jsxs32(Text33, { color: focused ? inkColors.accent : void 0, bold: focused, dimColor: !focused, children: [
3563
5123
  " ",
3564
5124
  title,
3565
5125
  " "
3566
5126
  ] }) }),
3567
- /* @__PURE__ */ jsx27(Box24, { flexDirection: "column", flexGrow: 1, overflow: "hidden", children })
5127
+ /* @__PURE__ */ jsx33(Box30, { flexDirection: "column", flexGrow: 1, overflow: "hidden", children })
3568
5128
  ]
3569
5129
  }
3570
5130
  );
3571
5131
  }
3572
5132
 
3573
5133
  // src/components/Sidebar.tsx
3574
- import { useEffect as useEffect9, useMemo as useMemo9, useState as useState20 } from "react";
3575
- import { Box as Box25, Text as Text28, useInput as useInput8 } from "ink";
3576
- import { jsx as jsx28, jsxs as jsxs27 } from "react/jsx-runtime";
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";
3577
5137
  function groupSections(items) {
3578
5138
  const groups = [];
3579
5139
  let current = null;
@@ -3600,13 +5160,13 @@ function Sidebar({
3600
5160
  [items]
3601
5161
  );
3602
5162
  const selectedIdx = selectableItems.findIndex((item) => item.id === selectedId);
3603
- const [cursorIdx, setCursorIdx] = useState20(Math.max(0, selectedIdx));
5163
+ const [cursorIdx, setCursorIdx] = useState27(Math.max(0, selectedIdx));
3604
5164
  const sections = useMemo9(() => groupSections(items), [items]);
3605
- useEffect9(() => {
5165
+ useEffect16(() => {
3606
5166
  const idx = selectableItems.findIndex((item) => item.id === selectedId);
3607
5167
  if (idx >= 0) setCursorIdx(idx);
3608
5168
  }, [selectedId, selectableItems]);
3609
- useInput8(
5169
+ useInput11(
3610
5170
  (input2, key) => {
3611
5171
  if (key.upArrow || input2 === "k") {
3612
5172
  setCursorIdx((prev) => {
@@ -3634,28 +5194,28 @@ function Sidebar({
3634
5194
  { isActive: isFocused }
3635
5195
  );
3636
5196
  let flatIdx = 0;
3637
- return /* @__PURE__ */ jsx28(Box25, { flexDirection: "column", gap: 0, children: sections.map((section) => {
5197
+ return /* @__PURE__ */ jsx34(Box31, { flexDirection: "column", gap: 0, children: sections.map((section) => {
3638
5198
  const sectionStartIdx = flatIdx;
3639
5199
  const sectionEndIdx = sectionStartIdx + section.items.length - 1;
3640
5200
  const hasCursorInSection = isFocused && cursorIdx >= sectionStartIdx && cursorIdx <= sectionEndIdx;
3641
5201
  const hasActiveInSection = section.items.some((item) => item.id === selectedId);
3642
5202
  const borderColor = hasCursorInSection || hasActiveInSection ? inkColors.accent : "#555555";
3643
- const rendered = /* @__PURE__ */ jsxs27(
3644
- Box25,
5203
+ const rendered = /* @__PURE__ */ jsxs33(
5204
+ Box31,
3645
5205
  {
3646
5206
  flexDirection: "column",
3647
5207
  borderStyle: "round",
3648
5208
  borderColor,
3649
5209
  paddingX: 1,
3650
5210
  children: [
3651
- /* @__PURE__ */ jsx28(Text28, { dimColor: true, bold: true, children: section.title }),
5211
+ /* @__PURE__ */ jsx34(Text34, { dimColor: true, bold: true, children: section.title }),
3652
5212
  section.items.map((item) => {
3653
5213
  const thisIdx = flatIdx;
3654
5214
  flatIdx++;
3655
5215
  const isCursor = isFocused && thisIdx === cursorIdx;
3656
5216
  const isActive = item.id === selectedId;
3657
- return /* @__PURE__ */ jsx28(Box25, { gap: 0, children: /* @__PURE__ */ jsxs27(
3658
- Text28,
5217
+ return /* @__PURE__ */ jsx34(Box31, { gap: 0, children: /* @__PURE__ */ jsxs33(
5218
+ Text34,
3659
5219
  {
3660
5220
  color: isCursor ? inkColors.accent : isActive ? inkColors.accent : void 0,
3661
5221
  bold: isCursor || isActive,
@@ -3678,12 +5238,12 @@ function Sidebar({
3678
5238
  }
3679
5239
 
3680
5240
  // src/components/PanelFooter.tsx
3681
- import { Box as Box26, Text as Text29 } from "ink";
3682
- import { jsx as jsx29, jsxs as jsxs28 } from "react/jsx-runtime";
5241
+ import { Box as Box32, Text as Text35 } from "ink";
5242
+ import { jsx as jsx35, jsxs as jsxs34 } from "react/jsx-runtime";
3683
5243
  function PanelFooter({ hints, width }) {
3684
- return /* @__PURE__ */ jsx29(Box26, { width, children: /* @__PURE__ */ jsx29(Box26, { gap: 1, children: hints.map((hint) => /* @__PURE__ */ jsxs28(Box26, { gap: 0, children: [
3685
- /* @__PURE__ */ jsx29(Text29, { color: inkColors.accent, bold: true, children: hint.key }),
3686
- /* @__PURE__ */ jsxs28(Text29, { dimColor: true, children: [
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: [
3687
5247
  ":",
3688
5248
  hint.action
3689
5249
  ] })
@@ -3691,8 +5251,8 @@ function PanelFooter({ hints, width }) {
3691
5251
  }
3692
5252
 
3693
5253
  // src/components/Modal.tsx
3694
- import { Box as Box27, Text as Text30 } from "ink";
3695
- import { jsx as jsx30, jsxs as jsxs29 } from "react/jsx-runtime";
5254
+ import { Box as Box33, Text as Text36 } from "ink";
5255
+ import { jsx as jsx36, jsxs as jsxs35 } from "react/jsx-runtime";
3696
5256
  function Modal({
3697
5257
  title,
3698
5258
  width,
@@ -3703,10 +5263,10 @@ function Modal({
3703
5263
  const modalHeight = Math.min(height - 4, 20);
3704
5264
  const padX = Math.max(0, Math.floor((width - modalWidth) / 2));
3705
5265
  const padY = Math.max(0, Math.floor((height - modalHeight) / 2));
3706
- return /* @__PURE__ */ jsxs29(Box27, { flexDirection: "column", width, height, children: [
3707
- padY > 0 && /* @__PURE__ */ jsx30(Box27, { height: padY }),
3708
- /* @__PURE__ */ jsx30(Box27, { marginLeft: padX, children: /* @__PURE__ */ jsxs29(
3709
- Box27,
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,
3710
5270
  {
3711
5271
  flexDirection: "column",
3712
5272
  width: modalWidth,
@@ -3715,8 +5275,8 @@ function Modal({
3715
5275
  borderColor: inkColors.accent,
3716
5276
  paddingX: 1,
3717
5277
  children: [
3718
- /* @__PURE__ */ jsx30(Box27, { marginBottom: 1, children: /* @__PURE__ */ jsx30(Text30, { color: inkColors.accent, bold: true, children: title }) }),
3719
- /* @__PURE__ */ jsx30(Box27, { flexDirection: "column", flexGrow: 1, overflow: "hidden", 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 })
3720
5280
  ]
3721
5281
  }
3722
5282
  ) })
@@ -3724,9 +5284,9 @@ function Modal({
3724
5284
  }
3725
5285
 
3726
5286
  // src/components/FeatureCommands.tsx
3727
- import { useEffect as useEffect10, useMemo as useMemo10, useState as useState21 } from "react";
3728
- import { Box as Box28, Text as Text31 } from "ink";
3729
- import { jsx as jsx31, jsxs as jsxs30 } from "react/jsx-runtime";
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";
3730
5290
  function FeatureCommands({
3731
5291
  feature,
3732
5292
  onNavigate,
@@ -3737,10 +5297,10 @@ function FeatureCommands({
3737
5297
  height = 24,
3738
5298
  isInputActive = true
3739
5299
  }) {
3740
- const [pinnedCommands, setPinnedCommands2] = useState21(() => getPinnedCommands());
3741
- const [pinnedRuns, setPinnedRuns2] = useState21(() => getPinnedRuns());
3742
- const [pinFeedback, setPinFeedback] = useState21();
3743
- useEffect10(() => {
5300
+ const [pinnedCommands, setPinnedCommands2] = useState28(() => getPinnedCommands());
5301
+ const [pinnedRuns, setPinnedRuns2] = useState28(() => getPinnedRuns());
5302
+ const [pinFeedback, setPinFeedback] = useState28();
5303
+ useEffect17(() => {
3744
5304
  if (!pinFeedback) return;
3745
5305
  const timeout = setTimeout(() => setPinFeedback(void 0), 1400);
3746
5306
  return () => clearTimeout(timeout);
@@ -3808,12 +5368,12 @@ function FeatureCommands({
3808
5368
  );
3809
5369
  }
3810
5370
  };
3811
- return /* @__PURE__ */ jsxs30(Box28, { flexDirection: "column", paddingX: 1, children: [
3812
- pinFeedback && /* @__PURE__ */ jsx31(Box28, { marginBottom: 1, children: /* @__PURE__ */ jsxs30(Text31, { color: inkColors.accent, children: [
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: [
3813
5373
  "\u2713 ",
3814
5374
  pinFeedback
3815
5375
  ] }) }),
3816
- /* @__PURE__ */ jsx31(
5376
+ /* @__PURE__ */ jsx37(
3817
5377
  SelectList,
3818
5378
  {
3819
5379
  items,
@@ -3832,9 +5392,9 @@ function FeatureCommands({
3832
5392
  }
3833
5393
 
3834
5394
  // src/components/PinnedCommands.tsx
3835
- import { useEffect as useEffect11, useMemo as useMemo11, useState as useState22 } from "react";
3836
- import { Box as Box29, Text as Text32 } from "ink";
3837
- import { jsx as jsx32, jsxs as jsxs31 } from "react/jsx-runtime";
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";
3838
5398
  function PinnedCommands({
3839
5399
  onNavigate,
3840
5400
  onBack,
@@ -3843,10 +5403,10 @@ function PinnedCommands({
3843
5403
  height = 24,
3844
5404
  isInputActive = true
3845
5405
  }) {
3846
- const [pinnedCommands, setPinnedCommands2] = useState22(() => getPinnedCommands());
3847
- const [pinnedRuns, setPinnedRuns2] = useState22(() => getPinnedRuns());
3848
- const [pinFeedback, setPinFeedback] = useState22();
3849
- useEffect11(() => {
5406
+ const [pinnedCommands, setPinnedCommands2] = useState29(() => getPinnedCommands());
5407
+ const [pinnedRuns, setPinnedRuns2] = useState29(() => getPinnedRuns());
5408
+ const [pinFeedback, setPinFeedback] = useState29();
5409
+ useEffect18(() => {
3850
5410
  if (!pinFeedback) return;
3851
5411
  const timeout = setTimeout(() => setPinFeedback(void 0), 1400);
3852
5412
  return () => clearTimeout(timeout);
@@ -3908,14 +5468,14 @@ function PinnedCommands({
3908
5468
  }
3909
5469
  };
3910
5470
  if (items.length === 0) {
3911
- return /* @__PURE__ */ jsx32(Box29, { flexDirection: "column", paddingX: 1, children: /* @__PURE__ */ jsx32(Text32, { color: "gray", children: "No pinned items. Press p on any command to pin it." }) });
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." }) });
3912
5472
  }
3913
- return /* @__PURE__ */ jsxs31(Box29, { flexDirection: "column", paddingX: 1, children: [
3914
- pinFeedback && /* @__PURE__ */ jsx32(Box29, { marginBottom: 1, children: /* @__PURE__ */ jsxs31(Text32, { color: inkColors.accent, children: [
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: [
3915
5475
  "\u2713 ",
3916
5476
  pinFeedback
3917
5477
  ] }) }),
3918
- /* @__PURE__ */ jsx32(
5478
+ /* @__PURE__ */ jsx38(
3919
5479
  SelectList,
3920
5480
  {
3921
5481
  items,
@@ -3933,8 +5493,43 @@ function PinnedCommands({
3933
5493
  ] });
3934
5494
  }
3935
5495
 
5496
+ // 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";
5500
+ var ITEMS = [
5501
+ { value: "declarative-plan", label: "Plan / Apply", hint: "Diff and apply polter.yaml", kind: "action" },
5502
+ { value: "declarative-status", label: "Infrastructure Status", hint: "Live state from CLI tools", kind: "action" },
5503
+ { value: "init-scaffold", label: "Init polter.yaml", hint: "Generate from detected state", kind: "action" }
5504
+ ];
5505
+ function DeclarativeHome({
5506
+ onNavigate,
5507
+ onBack,
5508
+ width = 80,
5509
+ height = 24,
5510
+ isInputActive = true
5511
+ }) {
5512
+ const handleSelect = useMemo12(
5513
+ () => (value) => onNavigate(value),
5514
+ [onNavigate]
5515
+ );
5516
+ return /* @__PURE__ */ jsx39(Box36, { flexDirection: "column", paddingX: 1, children: /* @__PURE__ */ jsx39(
5517
+ SelectList,
5518
+ {
5519
+ items: ITEMS,
5520
+ onSelect: handleSelect,
5521
+ onCancel: onBack,
5522
+ width: Math.max(20, width - 4),
5523
+ maxVisible: Math.max(6, height - 4),
5524
+ isInputActive,
5525
+ arrowNavigation: true,
5526
+ panelFocused: isInputActive
5527
+ }
5528
+ ) });
5529
+ }
5530
+
3936
5531
  // src/appPanel.tsx
3937
- import { jsx as jsx33, jsxs as jsxs32 } from "react/jsx-runtime";
5532
+ import { jsx as jsx40, jsxs as jsxs38 } from "react/jsx-runtime";
3938
5533
  var screenLabels = {
3939
5534
  "command-args": "Args",
3940
5535
  "flag-selection": "Flags",
@@ -3946,7 +5541,13 @@ var screenLabels = {
3946
5541
  "pipeline-execution": "Run",
3947
5542
  "self-update": "Update",
3948
5543
  "tool-status": "Status",
3949
- "project-config": "Config"
5544
+ "project-config": "Config",
5545
+ "mcp-manage": "MCP",
5546
+ "process-list": "Processes",
5547
+ "process-logs": "Logs",
5548
+ "declarative-plan": "Plan/Apply",
5549
+ "declarative-status": "Status",
5550
+ "init-scaffold": "Init"
3950
5551
  };
3951
5552
  function buildBreadcrumb(nav) {
3952
5553
  let base;
@@ -3974,6 +5575,12 @@ function buildBreadcrumb(nav) {
3974
5575
  case "self-update":
3975
5576
  base = "\u2B06\uFE0F Update";
3976
5577
  break;
5578
+ case "processes":
5579
+ base = "\u{1F4BB} Processes";
5580
+ break;
5581
+ case "declarative":
5582
+ base = "\u{1F3D7}\uFE0F Infrastructure";
5583
+ break;
3977
5584
  default:
3978
5585
  base = nav.view;
3979
5586
  }
@@ -4006,7 +5613,7 @@ function AppPanel() {
4006
5613
  const focus = usePanelFocus();
4007
5614
  const sidebarItems = useSidebarItems();
4008
5615
  const modal = useModal();
4009
- const refreshPins = React19.useCallback(() => {
5616
+ const refreshPins = React26.useCallback(() => {
4010
5617
  }, []);
4011
5618
  const singlePanel = width < 60 || height < 15;
4012
5619
  const handleExit = () => {
@@ -4015,7 +5622,7 @@ function AppPanel() {
4015
5622
  );
4016
5623
  exit();
4017
5624
  };
4018
- useInput9((input2, key) => {
5625
+ useInput12((input2, key) => {
4019
5626
  if (modal.isOpen) {
4020
5627
  if (key.escape || input2 === "q") {
4021
5628
  modal.closeModal();
@@ -4040,37 +5647,37 @@ function AppPanel() {
4040
5647
  }
4041
5648
  if (input2 === "?") {
4042
5649
  modal.openModal(
4043
- /* @__PURE__ */ jsxs32(Box30, { flexDirection: "column", children: [
4044
- /* @__PURE__ */ jsxs32(Text33, { children: [
4045
- /* @__PURE__ */ jsx33(Text33, { bold: true, children: "\u2190/\u2192" }),
5650
+ /* @__PURE__ */ jsxs38(Box37, { flexDirection: "column", children: [
5651
+ /* @__PURE__ */ jsxs38(Text39, { children: [
5652
+ /* @__PURE__ */ jsx40(Text39, { bold: true, children: "\u2190/\u2192" }),
4046
5653
  " Move between sidebar and main panel"
4047
5654
  ] }),
4048
- /* @__PURE__ */ jsxs32(Text33, { children: [
4049
- /* @__PURE__ */ jsx33(Text33, { bold: true, children: "Tab" }),
5655
+ /* @__PURE__ */ jsxs38(Text39, { children: [
5656
+ /* @__PURE__ */ jsx40(Text39, { bold: true, children: "Tab" }),
4050
5657
  " Toggle sidebar / main panel"
4051
5658
  ] }),
4052
- /* @__PURE__ */ jsxs32(Text33, { children: [
4053
- /* @__PURE__ */ jsx33(Text33, { bold: true, children: "j/k" }),
5659
+ /* @__PURE__ */ jsxs38(Text39, { children: [
5660
+ /* @__PURE__ */ jsx40(Text39, { bold: true, children: "j/k" }),
4054
5661
  " Navigate up/down"
4055
5662
  ] }),
4056
- /* @__PURE__ */ jsxs32(Text33, { children: [
4057
- /* @__PURE__ */ jsx33(Text33, { bold: true, children: "Enter" }),
5663
+ /* @__PURE__ */ jsxs38(Text39, { children: [
5664
+ /* @__PURE__ */ jsx40(Text39, { bold: true, children: "Enter" }),
4058
5665
  " Select item"
4059
5666
  ] }),
4060
- /* @__PURE__ */ jsxs32(Text33, { children: [
4061
- /* @__PURE__ */ jsx33(Text33, { bold: true, children: "Esc" }),
5667
+ /* @__PURE__ */ jsxs38(Text39, { children: [
5668
+ /* @__PURE__ */ jsx40(Text39, { bold: true, children: "Esc" }),
4062
5669
  " Go back (or return to sidebar)"
4063
5670
  ] }),
4064
- /* @__PURE__ */ jsxs32(Text33, { children: [
4065
- /* @__PURE__ */ jsx33(Text33, { bold: true, children: "q" }),
5671
+ /* @__PURE__ */ jsxs38(Text39, { children: [
5672
+ /* @__PURE__ */ jsx40(Text39, { bold: true, children: "q" }),
4066
5673
  " Quit Polter"
4067
5674
  ] }),
4068
- /* @__PURE__ */ jsxs32(Text33, { children: [
4069
- /* @__PURE__ */ jsx33(Text33, { bold: true, children: "?" }),
5675
+ /* @__PURE__ */ jsxs38(Text39, { children: [
5676
+ /* @__PURE__ */ jsx40(Text39, { bold: true, children: "?" }),
4070
5677
  " Show this help"
4071
5678
  ] }),
4072
- /* @__PURE__ */ jsxs32(Text33, { children: [
4073
- /* @__PURE__ */ jsx33(Text33, { bold: true, children: "p" }),
5679
+ /* @__PURE__ */ jsxs38(Text39, { children: [
5680
+ /* @__PURE__ */ jsx40(Text39, { bold: true, children: "p" }),
4074
5681
  " Pin/unpin command"
4075
5682
  ] })
4076
5683
  ] }),
@@ -4094,6 +5701,10 @@ function AppPanel() {
4094
5701
  return "config";
4095
5702
  case "self-update":
4096
5703
  return "self-update";
5704
+ case "processes":
5705
+ return "processes";
5706
+ case "declarative":
5707
+ return "declarative";
4097
5708
  default:
4098
5709
  return nav.featureId;
4099
5710
  }
@@ -4110,7 +5721,7 @@ function AppPanel() {
4110
5721
  nav.selectSidebarItem(itemId);
4111
5722
  focus.focusMain();
4112
5723
  };
4113
- const bannerHeight = width < 60 ? 1 : 8;
5724
+ const bannerHeight = width < 60 ? 6 : 10;
4114
5725
  const footerHints = FOOTER_HINTS;
4115
5726
  const mainContentHeight = Math.max(5, height - bannerHeight - 1 - 2);
4116
5727
  const mainContentWidth = singlePanel ? width : width - Math.max(20, Math.min(35, Math.floor(width * 0.3)));
@@ -4120,7 +5731,7 @@ function AppPanel() {
4120
5731
  }
4121
5732
  switch (nav.view) {
4122
5733
  case "pinned":
4123
- return /* @__PURE__ */ jsx33(
5734
+ return /* @__PURE__ */ jsx40(
4124
5735
  PinnedCommands,
4125
5736
  {
4126
5737
  onNavigate: nav.navigateInner,
@@ -4134,12 +5745,12 @@ function AppPanel() {
4134
5745
  case "feature": {
4135
5746
  const feature = getFeatureById(nav.featureId);
4136
5747
  if (!feature) {
4137
- return /* @__PURE__ */ jsxs32(Text33, { color: "red", children: [
5748
+ return /* @__PURE__ */ jsxs38(Text39, { color: "red", children: [
4138
5749
  "Feature not found: ",
4139
5750
  nav.featureId
4140
5751
  ] });
4141
5752
  }
4142
- return /* @__PURE__ */ jsx33(
5753
+ return /* @__PURE__ */ jsx40(
4143
5754
  FeatureCommands,
4144
5755
  {
4145
5756
  feature,
@@ -4154,7 +5765,7 @@ function AppPanel() {
4154
5765
  );
4155
5766
  }
4156
5767
  case "custom-command":
4157
- return /* @__PURE__ */ jsx33(
5768
+ return /* @__PURE__ */ jsx40(
4158
5769
  CustomCommand,
4159
5770
  {
4160
5771
  onNavigate: nav.navigateInner,
@@ -4166,7 +5777,7 @@ function AppPanel() {
4166
5777
  }
4167
5778
  );
4168
5779
  case "pipelines":
4169
- return /* @__PURE__ */ jsx33(
5780
+ return /* @__PURE__ */ jsx40(
4170
5781
  PipelineList,
4171
5782
  {
4172
5783
  onNavigate: nav.navigateInner,
@@ -4178,9 +5789,22 @@ function AppPanel() {
4178
5789
  }
4179
5790
  );
4180
5791
  case "tool-status":
4181
- return /* @__PURE__ */ jsx33(
5792
+ return /* @__PURE__ */ jsx40(
4182
5793
  ToolStatus,
4183
5794
  {
5795
+ onBack: focus.focusSidebar,
5796
+ onNavigate: nav.navigateInner,
5797
+ width: mainContentWidth - 2,
5798
+ height: mainContentHeight,
5799
+ panelMode: true,
5800
+ isInputActive: focus.isMainFocused
5801
+ }
5802
+ );
5803
+ case "processes":
5804
+ return /* @__PURE__ */ jsx40(
5805
+ ProcessList,
5806
+ {
5807
+ onNavigate: nav.navigateInner,
4184
5808
  onBack: focus.focusSidebar,
4185
5809
  width: mainContentWidth - 2,
4186
5810
  height: mainContentHeight,
@@ -4188,8 +5812,19 @@ function AppPanel() {
4188
5812
  isInputActive: focus.isMainFocused
4189
5813
  }
4190
5814
  );
5815
+ case "declarative":
5816
+ return /* @__PURE__ */ jsx40(
5817
+ DeclarativeHome,
5818
+ {
5819
+ onNavigate: nav.navigateInner,
5820
+ onBack: focus.focusSidebar,
5821
+ width: mainContentWidth - 2,
5822
+ height: mainContentHeight,
5823
+ isInputActive: focus.isMainFocused
5824
+ }
5825
+ );
4191
5826
  case "config":
4192
- return /* @__PURE__ */ jsx33(
5827
+ return /* @__PURE__ */ jsx40(
4193
5828
  ProjectConfig,
4194
5829
  {
4195
5830
  onBack: focus.focusSidebar,
@@ -4200,7 +5835,7 @@ function AppPanel() {
4200
5835
  }
4201
5836
  );
4202
5837
  case "self-update":
4203
- return /* @__PURE__ */ jsx33(
5838
+ return /* @__PURE__ */ jsx40(
4204
5839
  SelfUpdate,
4205
5840
  {
4206
5841
  onBack: focus.focusSidebar,
@@ -4212,7 +5847,7 @@ function AppPanel() {
4212
5847
  }
4213
5848
  );
4214
5849
  default:
4215
- return /* @__PURE__ */ jsx33(Text33, { children: "Select an item from the sidebar" });
5850
+ return /* @__PURE__ */ jsx40(Text39, { children: "Select an item from the sidebar" });
4216
5851
  }
4217
5852
  };
4218
5853
  const renderInnerScreen = () => {
@@ -4220,7 +5855,7 @@ function AppPanel() {
4220
5855
  const w = mainContentWidth - 2;
4221
5856
  switch (nav.innerScreen) {
4222
5857
  case "command-args":
4223
- return /* @__PURE__ */ jsx33(
5858
+ return /* @__PURE__ */ jsx40(
4224
5859
  CommandArgs,
4225
5860
  {
4226
5861
  command: nav.innerParams.command ?? "",
@@ -4233,7 +5868,7 @@ function AppPanel() {
4233
5868
  }
4234
5869
  );
4235
5870
  case "custom-command":
4236
- return /* @__PURE__ */ jsx33(
5871
+ return /* @__PURE__ */ jsx40(
4237
5872
  CustomCommand,
4238
5873
  {
4239
5874
  onNavigate: nav.navigateInner,
@@ -4245,7 +5880,7 @@ function AppPanel() {
4245
5880
  }
4246
5881
  );
4247
5882
  case "flag-selection":
4248
- return /* @__PURE__ */ jsx33(
5883
+ return /* @__PURE__ */ jsx40(
4249
5884
  FlagSelection,
4250
5885
  {
4251
5886
  args: nav.innerParams.args ?? [],
@@ -4261,7 +5896,7 @@ function AppPanel() {
4261
5896
  );
4262
5897
  case "confirm-execute":
4263
5898
  case "command-execution":
4264
- return /* @__PURE__ */ jsx33(
5899
+ return /* @__PURE__ */ jsx40(
4265
5900
  CommandExecution,
4266
5901
  {
4267
5902
  args: nav.innerParams.args ?? [],
@@ -4281,7 +5916,7 @@ function AppPanel() {
4281
5916
  `${nav.view}-${nav.innerParams.tool}-${(nav.innerParams.args ?? []).join("-")}`
4282
5917
  );
4283
5918
  case "pipeline-list":
4284
- return /* @__PURE__ */ jsx33(
5919
+ return /* @__PURE__ */ jsx40(
4285
5920
  PipelineList,
4286
5921
  {
4287
5922
  onNavigate: nav.navigateInner,
@@ -4293,7 +5928,7 @@ function AppPanel() {
4293
5928
  }
4294
5929
  );
4295
5930
  case "pipeline-builder":
4296
- return /* @__PURE__ */ jsx33(
5931
+ return /* @__PURE__ */ jsx40(
4297
5932
  PipelineBuilder,
4298
5933
  {
4299
5934
  onBack: nav.goBackInner,
@@ -4304,7 +5939,7 @@ function AppPanel() {
4304
5939
  }
4305
5940
  );
4306
5941
  case "pipeline-execution":
4307
- return /* @__PURE__ */ jsx33(
5942
+ return /* @__PURE__ */ jsx40(
4308
5943
  PipelineExecution,
4309
5944
  {
4310
5945
  pipelineId: nav.innerParams.pipelineId ?? "",
@@ -4316,7 +5951,7 @@ function AppPanel() {
4316
5951
  }
4317
5952
  );
4318
5953
  case "self-update":
4319
- return /* @__PURE__ */ jsx33(
5954
+ return /* @__PURE__ */ jsx40(
4320
5955
  SelfUpdate,
4321
5956
  {
4322
5957
  onBack: nav.goBackInner,
@@ -4328,9 +5963,45 @@ function AppPanel() {
4328
5963
  }
4329
5964
  );
4330
5965
  case "tool-status":
4331
- return /* @__PURE__ */ jsx33(
5966
+ return /* @__PURE__ */ jsx40(
4332
5967
  ToolStatus,
4333
5968
  {
5969
+ onBack: nav.goBackInner,
5970
+ onNavigate: nav.navigateInner,
5971
+ width: w,
5972
+ height: mainContentHeight,
5973
+ panelMode: true,
5974
+ isInputActive: isActive
5975
+ }
5976
+ );
5977
+ case "mcp-manage":
5978
+ return /* @__PURE__ */ jsx40(
5979
+ McpManage,
5980
+ {
5981
+ onBack: nav.goBackInner,
5982
+ width: w,
5983
+ height: mainContentHeight,
5984
+ panelMode: true,
5985
+ isInputActive: isActive
5986
+ }
5987
+ );
5988
+ case "process-list":
5989
+ return /* @__PURE__ */ jsx40(
5990
+ ProcessList,
5991
+ {
5992
+ onNavigate: nav.navigateInner,
5993
+ onBack: nav.goBackInner,
5994
+ width: w,
5995
+ height: mainContentHeight,
5996
+ panelMode: true,
5997
+ isInputActive: isActive
5998
+ }
5999
+ );
6000
+ case "process-logs":
6001
+ return /* @__PURE__ */ jsx40(
6002
+ ProcessLogs,
6003
+ {
6004
+ processId: nav.innerParams.processId ?? "",
4334
6005
  onBack: nav.goBackInner,
4335
6006
  width: w,
4336
6007
  height: mainContentHeight,
@@ -4339,7 +6010,7 @@ function AppPanel() {
4339
6010
  }
4340
6011
  );
4341
6012
  case "project-config":
4342
- return /* @__PURE__ */ jsx33(
6013
+ return /* @__PURE__ */ jsx40(
4343
6014
  ProjectConfig,
4344
6015
  {
4345
6016
  onBack: nav.goBackInner,
@@ -4349,18 +6020,53 @@ function AppPanel() {
4349
6020
  isInputActive: isActive
4350
6021
  }
4351
6022
  );
6023
+ case "declarative-plan":
6024
+ return /* @__PURE__ */ jsx40(
6025
+ DeclarativePlan,
6026
+ {
6027
+ onBack: nav.goBackInner,
6028
+ onNavigate: nav.navigateInner,
6029
+ width: w,
6030
+ height: mainContentHeight,
6031
+ panelMode: true,
6032
+ isInputActive: isActive
6033
+ }
6034
+ );
6035
+ case "declarative-status":
6036
+ return /* @__PURE__ */ jsx40(
6037
+ DeclarativeStatus,
6038
+ {
6039
+ onBack: nav.goBackInner,
6040
+ width: w,
6041
+ height: mainContentHeight,
6042
+ panelMode: true,
6043
+ isInputActive: isActive
6044
+ }
6045
+ );
6046
+ case "init-scaffold":
6047
+ return /* @__PURE__ */ jsx40(
6048
+ InitScaffold,
6049
+ {
6050
+ onBack: nav.goBackInner,
6051
+ onNavigate: nav.navigateInner,
6052
+ width: w,
6053
+ height: mainContentHeight,
6054
+ panelMode: true,
6055
+ isInputActive: isActive
6056
+ }
6057
+ );
4352
6058
  default:
4353
- return /* @__PURE__ */ jsxs32(Text33, { color: "red", children: [
6059
+ return /* @__PURE__ */ jsxs38(Text39, { color: "red", children: [
4354
6060
  "Unknown screen: ",
4355
6061
  nav.innerScreen
4356
6062
  ] });
4357
6063
  }
4358
6064
  };
4359
6065
  if (modal.isOpen) {
4360
- return /* @__PURE__ */ jsx33(Box30, { flexDirection: "column", width, height, children: /* @__PURE__ */ jsx33(Modal, { title: modal.modalTitle, width, height, children: modal.modalContent }) });
6066
+ return /* @__PURE__ */ jsx40(Box37, { flexDirection: "column", width, height, children: /* @__PURE__ */ jsx40(Modal, { title: modal.modalTitle, width, height, children: modal.modalContent }) });
4361
6067
  }
4362
- const header = /* @__PURE__ */ jsx33(GhostBanner, { width, compact: true });
4363
- const sidebar = /* @__PURE__ */ jsx33(
6068
+ const header = /* @__PURE__ */ jsx40(GhostBanner, { width, compact: true });
6069
+ const sidebar = /* @__PURE__ */ jsx40(
4364
6070
  Panel,
4365
6071
  {
4366
6072
  id: "sidebar",
@@ -4368,7 +6074,7 @@ function AppPanel() {
4368
6074
  width: Math.max(20, Math.min(35, Math.floor(width * 0.3))),
4369
6075
  height: Math.max(5, height - bannerHeight - 1),
4370
6076
  focused: focus.isSidebarFocused,
4371
- children: /* @__PURE__ */ jsx33(
6077
+ children: /* @__PURE__ */ jsx40(
4372
6078
  Sidebar,
4373
6079
  {
4374
6080
  items: sidebarItems,
@@ -4381,7 +6087,7 @@ function AppPanel() {
4381
6087
  )
4382
6088
  }
4383
6089
  );
4384
- const main2 = /* @__PURE__ */ jsx33(
6090
+ const main2 = /* @__PURE__ */ jsx40(
4385
6091
  Panel,
4386
6092
  {
4387
6093
  id: "main",
@@ -4392,8 +6098,8 @@ function AppPanel() {
4392
6098
  children: renderMainContent()
4393
6099
  }
4394
6100
  );
4395
- const footer = /* @__PURE__ */ jsx33(PanelFooter, { hints: footerHints, width });
4396
- return /* @__PURE__ */ jsx33(
6101
+ const footer = /* @__PURE__ */ jsx40(PanelFooter, { hints: footerHints, width });
6102
+ return /* @__PURE__ */ jsx40(
4397
6103
  PanelLayout,
4398
6104
  {
4399
6105
  header,
@@ -4557,19 +6263,19 @@ function printCliHelp() {
4557
6263
  import pc3 from "picocolors";
4558
6264
 
4559
6265
  // src/apps/ops.ts
4560
- import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
6266
+ import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync as writeFileSync2 } from "fs";
4561
6267
  import { mkdtemp, readdir, stat } from "fs/promises";
4562
- import { dirname, join as join2, resolve } from "path";
6268
+ import { dirname, join as join3, resolve } from "path";
4563
6269
  import { tmpdir } from "os";
4564
6270
  import pc2 from "picocolors";
4565
6271
 
4566
6272
  // src/apps/bootstrapPaths.ts
4567
- import { homedir } from "os";
4568
- import { join } from "path";
6273
+ import { homedir as homedir2 } from "os";
6274
+ import { join as join2 } from "path";
4569
6275
  function getOpsBootstrapPayloadPath() {
4570
- const home = homedir();
6276
+ const home = homedir2();
4571
6277
  if (process.platform === "darwin") {
4572
- return join(
6278
+ return join2(
4573
6279
  home,
4574
6280
  "Library",
4575
6281
  "Application Support",
@@ -4579,10 +6285,10 @@ function getOpsBootstrapPayloadPath() {
4579
6285
  );
4580
6286
  }
4581
6287
  if (process.platform === "win32") {
4582
- const appData = process.env.APPDATA ?? join(home, "AppData", "Roaming");
4583
- return join(appData, "ops", "bootstrap", "supabase.json");
6288
+ const appData = process.env.APPDATA ?? join2(home, "AppData", "Roaming");
6289
+ return join2(appData, "ops", "bootstrap", "supabase.json");
4584
6290
  }
4585
- return join(home, ".config", "ops", "bootstrap", "supabase.json");
6291
+ return join2(home, ".config", "ops", "bootstrap", "supabase.json");
4586
6292
  }
4587
6293
 
4588
6294
  // src/apps/opsRelease.ts
@@ -4854,9 +6560,9 @@ async function promptSelect(label, options, defaultValue) {
4854
6560
  }
4855
6561
 
4856
6562
  // src/apps/ops.ts
4857
- var LINK_REF_FILE = join2("supabase", ".temp", "project-ref");
6563
+ var LINK_REF_FILE = join3("supabase", ".temp", "project-ref");
4858
6564
  function isOpsProjectRoot(candidate) {
4859
- return existsSync(join2(candidate, "src-tauri", "tauri.conf.json")) && existsSync(join2(candidate, "supabase", "migrations")) && existsSync(join2(candidate, "package.json"));
6565
+ return existsSync(join3(candidate, "src-tauri", "tauri.conf.json")) && existsSync(join3(candidate, "supabase", "migrations")) && existsSync(join3(candidate, "package.json"));
4860
6566
  }
4861
6567
  function findNearestOpsRoot(startDir) {
4862
6568
  let currentDir = resolve(startDir);
@@ -4864,7 +6570,7 @@ function findNearestOpsRoot(startDir) {
4864
6570
  if (isOpsProjectRoot(currentDir)) {
4865
6571
  return currentDir;
4866
6572
  }
4867
- const siblingCandidate = join2(currentDir, "ops");
6573
+ const siblingCandidate = join3(currentDir, "ops");
4868
6574
  if (isOpsProjectRoot(siblingCandidate)) {
4869
6575
  return siblingCandidate;
4870
6576
  }
@@ -4898,7 +6604,7 @@ function readEnvFile(envPath) {
4898
6604
  }
4899
6605
  function writeEnvFile(envPath, nextEnv) {
4900
6606
  const content = Object.entries(nextEnv).map(([key, value]) => `${key}=${value}`).join("\n");
4901
- writeFileSync(envPath, `${content}
6607
+ writeFileSync2(envPath, `${content}
4902
6608
  `);
4903
6609
  }
4904
6610
  function assertProjectRoot(projectRoot) {
@@ -4910,7 +6616,7 @@ function assertProjectRoot(projectRoot) {
4910
6616
  return projectRoot;
4911
6617
  }
4912
6618
  function getLinkedProjectRef(projectRoot) {
4913
- const refPath = join2(projectRoot, LINK_REF_FILE);
6619
+ const refPath = join3(projectRoot, LINK_REF_FILE);
4914
6620
  if (!existsSync(refPath)) {
4915
6621
  return null;
4916
6622
  }
@@ -4935,7 +6641,7 @@ async function ensurePrerequisites() {
4935
6641
  }
4936
6642
  }
4937
6643
  async function runOrThrow(execution, args, cwd, failureMessage) {
4938
- const result = await runCommand(execution, args, cwd);
6644
+ const result = await runCommand(execution, args, cwd).promise;
4939
6645
  if (result.spawnError || result.exitCode !== 0) {
4940
6646
  throw new Error(
4941
6647
  result.stderr.trim() || result.spawnError || failureMessage
@@ -4968,7 +6674,7 @@ async function ensureSupabaseLink(projectRoot, forceRelink = false) {
4968
6674
  );
4969
6675
  }
4970
6676
  async function collectSupabaseConfig(projectRoot) {
4971
- const envPath = projectRoot ? join2(projectRoot, ".env.local") : void 0;
6677
+ const envPath = projectRoot ? join3(projectRoot, ".env.local") : void 0;
4972
6678
  const currentEnv = envPath ? readEnvFile(envPath) : {};
4973
6679
  const currentRef = projectRoot ? getLinkedProjectRef(projectRoot) : null;
4974
6680
  const url = await promptText("Supabase URL", {
@@ -4990,7 +6696,7 @@ async function collectSupabaseConfig(projectRoot) {
4990
6696
  };
4991
6697
  }
4992
6698
  function writeOpsEnv(projectRoot, config2) {
4993
- const envPath = join2(projectRoot, ".env.local");
6699
+ const envPath = join3(projectRoot, ".env.local");
4994
6700
  const currentEnv = readEnvFile(envPath);
4995
6701
  const nextEnv = {
4996
6702
  ...currentEnv,
@@ -5002,7 +6708,7 @@ function writeOpsEnv(projectRoot, config2) {
5002
6708
  function writeBootstrapPayload(config2) {
5003
6709
  const payloadPath = getOpsBootstrapPayloadPath();
5004
6710
  mkdirSync(dirname(payloadPath), { recursive: true });
5005
- writeFileSync(
6711
+ writeFileSync2(
5006
6712
  payloadPath,
5007
6713
  JSON.stringify(
5008
6714
  {
@@ -5163,7 +6869,7 @@ async function downloadFile(url, destinationPath) {
5163
6869
  }
5164
6870
  const arrayBuffer = await response.arrayBuffer();
5165
6871
  const buffer = Buffer.from(arrayBuffer);
5166
- writeFileSync(destinationPath, buffer);
6872
+ writeFileSync2(destinationPath, buffer);
5167
6873
  return buffer.byteLength;
5168
6874
  }
5169
6875
  async function extractArchive(archivePath, outputDir) {
@@ -5190,7 +6896,7 @@ async function extractArchive(archivePath, outputDir) {
5190
6896
  async function findFirstAppBundle(dir) {
5191
6897
  const entries = await readdir(dir);
5192
6898
  for (const entry of entries) {
5193
- const fullPath = join2(dir, entry);
6899
+ const fullPath = join3(dir, entry);
5194
6900
  const entryStat = await stat(fullPath);
5195
6901
  if (entryStat.isDirectory() && entry.endsWith(".app")) {
5196
6902
  return fullPath;
@@ -5206,9 +6912,9 @@ async function findFirstAppBundle(dir) {
5206
6912
  }
5207
6913
  async function deployMacosApp(context, options) {
5208
6914
  const artifact = await resolveOpsMacosArtifact(context.options);
5209
- const tempRoot = await mkdtemp(join2(tmpdir(), "polter-ops-"));
5210
- const archivePath = join2(tempRoot, artifact.fileName);
5211
- const extractDir = join2(tempRoot, "extract");
6915
+ const tempRoot = await mkdtemp(join3(tmpdir(), "polter-ops-"));
6916
+ const archivePath = join3(tempRoot, artifact.fileName);
6917
+ const extractDir = join3(tempRoot, "extract");
5212
6918
  mkdirSync(extractDir, { recursive: true });
5213
6919
  if (artifact.source === "github-release") {
5214
6920
  const releaseLabel = artifact.tagName ?? "latest";
@@ -5237,7 +6943,7 @@ async function deployMacosApp(context, options) {
5237
6943
  }
5238
6944
  const installDir = context.options.installDir ?? "/Applications";
5239
6945
  mkdirSync(installDir, { recursive: true });
5240
- const destination = join2(installDir, "ops.app");
6946
+ const destination = join3(installDir, "ops.app");
5241
6947
  if (options.requireExistingInstallation && !existsSync(destination)) {
5242
6948
  throw new Error(
5243
6949
  `No existing Ops installation was found at ${destination}. Run \`polter app install ops\` first.`
@@ -5358,168 +7064,8 @@ async function runAppCli(options) {
5358
7064
  });
5359
7065
  }
5360
7066
 
5361
- // src/lib/mcpInstaller.ts
5362
- import { spawnSync as spawnSync2 } from "child_process";
5363
- import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, existsSync as existsSync2 } from "fs";
5364
- import { join as join3 } from "path";
5365
- import { homedir as homedir2 } from "os";
5366
- import { createRequire as createRequire2 } from "module";
5367
- import pc4 from "picocolors";
5368
- var require3 = createRequire2(import.meta.url);
5369
- var MCP_ARGS = ["npx", "-y", "-p", "@polterware/polter@latest", "polter-mcp"];
5370
- var SCOPE_LABELS = {
5371
- local: "local (this machine)",
5372
- project: "project (shared via repo)",
5373
- user: "global (all projects)"
5374
- };
5375
- function tryClaudeCli(scope) {
5376
- if (!commandExists("claude")) return false;
5377
- const result = spawnSync2("claude", ["mcp", "add", "-s", scope, "polter", "--", ...MCP_ARGS], {
5378
- stdio: "inherit",
5379
- shell: true
5380
- });
5381
- return result.status === 0;
5382
- }
5383
- function getSettingsPath(scope) {
5384
- if (scope === "project") {
5385
- return join3(process.cwd(), ".mcp.json");
5386
- }
5387
- return join3(homedir2(), ".claude", "settings.json");
5388
- }
5389
- function tryManualInstall(scope) {
5390
- const settingsPath = getSettingsPath(scope);
5391
- const dir = join3(settingsPath, "..");
5392
- let settings = {};
5393
- if (existsSync2(settingsPath)) {
5394
- try {
5395
- settings = JSON.parse(readFileSync2(settingsPath, "utf-8"));
5396
- } catch {
5397
- process.stderr.write(pc4.red(`Failed to parse ${settingsPath}
5398
- `));
5399
- return false;
5400
- }
5401
- } else {
5402
- mkdirSync2(dir, { recursive: true });
5403
- }
5404
- const mcpServers = settings.mcpServers ?? {};
5405
- mcpServers.polter = {
5406
- command: "npx",
5407
- args: ["-y", "-p", "@polterware/polter@latest", "polter-mcp"]
5408
- };
5409
- settings.mcpServers = mcpServers;
5410
- writeFileSync2(settingsPath, JSON.stringify(settings, null, 2) + "\n");
5411
- return true;
5412
- }
5413
- async function installMcpServer(scope) {
5414
- process.stdout.write(pc4.bold(`Installing Polter MCP server \u2014 ${SCOPE_LABELS[scope]}
5415
-
5416
- `));
5417
- if (commandExists("claude")) {
5418
- process.stdout.write(` Using 'claude mcp add -s ${scope}'...
5419
- `);
5420
- if (tryClaudeCli(scope)) {
5421
- process.stdout.write(pc4.green("\n Done! Restart Claude Code to use Polter tools.\n"));
5422
- return;
5423
- }
5424
- process.stdout.write(pc4.yellow(" 'claude mcp add' failed, falling back to manual install...\n\n"));
5425
- }
5426
- const settingsPath = getSettingsPath(scope);
5427
- process.stdout.write(` Writing to ${settingsPath}...
5428
- `);
5429
- if (tryManualInstall(scope)) {
5430
- process.stdout.write(pc4.green("\n Done! Restart Claude Code to use Polter tools.\n"));
5431
- } else {
5432
- process.stderr.write(pc4.red("\n Failed to install. Add manually:\n\n"));
5433
- process.stderr.write(` ${pc4.dim(JSON.stringify({ mcpServers: { polter: { command: "npx", args: ["-y", "-p", "@polterware/polter@latest", "polter-mcp"] } } }, null, 2))}
5434
- `);
5435
- process.exit(1);
5436
- }
5437
- }
5438
- async function removeMcpServer(scope) {
5439
- process.stdout.write(pc4.bold(`Removing Polter MCP server \u2014 ${SCOPE_LABELS[scope]}
5440
-
5441
- `));
5442
- if (commandExists("claude")) {
5443
- const result = spawnSync2("claude", ["mcp", "remove", "-s", scope, "polter"], {
5444
- stdio: "inherit",
5445
- shell: true
5446
- });
5447
- if (result.status === 0) {
5448
- process.stdout.write(pc4.green("\n Done! Polter MCP server removed.\n"));
5449
- return;
5450
- }
5451
- process.stdout.write(pc4.yellow(" 'claude mcp remove' failed, falling back to manual removal...\n\n"));
5452
- }
5453
- const settingsPath = getSettingsPath(scope);
5454
- if (!existsSync2(settingsPath)) {
5455
- process.stdout.write(pc4.yellow(" No settings file found. Nothing to remove.\n"));
5456
- return;
5457
- }
5458
- let settings;
5459
- try {
5460
- settings = JSON.parse(readFileSync2(settingsPath, "utf-8"));
5461
- } catch {
5462
- process.stderr.write(pc4.red(` Failed to parse ${settingsPath}
5463
- `));
5464
- process.exit(1);
5465
- return;
5466
- }
5467
- const mcpServers = settings.mcpServers;
5468
- if (!mcpServers?.polter) {
5469
- process.stdout.write(pc4.yellow(" Polter MCP server not found in settings. Nothing to remove.\n"));
5470
- return;
5471
- }
5472
- delete mcpServers.polter;
5473
- settings.mcpServers = mcpServers;
5474
- writeFileSync2(settingsPath, JSON.stringify(settings, null, 2) + "\n");
5475
- process.stdout.write(pc4.green(" Done! Polter MCP server removed.\n"));
5476
- }
5477
- async function mcpStatus() {
5478
- process.stdout.write(pc4.bold("Polter MCP Server Status\n\n"));
5479
- const pkg = require3("../../package.json");
5480
- process.stdout.write(` Installed version: ${pc4.cyan(pkg.version)}
5481
- `);
5482
- const latestResult = spawnSync2("npm", ["view", "@polterware/polter", "version"], {
5483
- encoding: "utf-8",
5484
- shell: true,
5485
- timeout: 1e4
5486
- });
5487
- const latest = latestResult.stdout?.trim();
5488
- if (latest) {
5489
- const upToDate = latest === pkg.version;
5490
- process.stdout.write(` Latest version: ${upToDate ? pc4.green(latest) : pc4.yellow(`${latest} (update available)`)}
5491
- `);
5492
- }
5493
- process.stdout.write("\n");
5494
- const scopes = [
5495
- { label: "Project (.mcp.json)", path: join3(process.cwd(), ".mcp.json"), key: "project" },
5496
- { label: "User (~/.claude/settings.json)", path: join3(homedir2(), ".claude", "settings.json"), key: "user" }
5497
- ];
5498
- for (const scope of scopes) {
5499
- if (!existsSync2(scope.path)) {
5500
- process.stdout.write(` ${scope.label}: ${pc4.dim("not found")}
5501
- `);
5502
- continue;
5503
- }
5504
- try {
5505
- const settings = JSON.parse(readFileSync2(scope.path, "utf-8"));
5506
- const mcpServers = settings.mcpServers;
5507
- if (mcpServers?.polter) {
5508
- process.stdout.write(` ${scope.label}: ${pc4.green("registered")}
5509
- `);
5510
- } else {
5511
- process.stdout.write(` ${scope.label}: ${pc4.dim("not registered")}
5512
- `);
5513
- }
5514
- } catch {
5515
- process.stdout.write(` ${scope.label}: ${pc4.red("error reading file")}
5516
- `);
5517
- }
5518
- }
5519
- }
5520
-
5521
7067
  // src/index.tsx
5522
- import pc5 from "picocolors";
7068
+ import pc4 from "picocolors";
5523
7069
  async function main() {
5524
7070
  const parsed = parseCliArgs(process.argv.slice(2));
5525
7071
  if (parsed.mode === "help") {
@@ -5553,7 +7099,7 @@ async function main() {
5553
7099
  `);
5554
7100
  process.exit(1);
5555
7101
  }
5556
- process.stdout.write(pc5.bold(`Running pipeline: ${pipeline.name}
7102
+ process.stdout.write(pc4.bold(`Running pipeline: ${pipeline.name}
5557
7103
  `));
5558
7104
  const results = await executePipeline(pipeline, (progress) => {
5559
7105
  if (progress.done) return;
@@ -5567,24 +7113,24 @@ async function main() {
5567
7113
  });
5568
7114
  const errors = results.filter((r) => r.status === "error");
5569
7115
  if (errors.length > 0) {
5570
- process.stderr.write(pc5.red(`
7116
+ process.stderr.write(pc4.red(`
5571
7117
  Pipeline completed with ${errors.length} error(s)
5572
7118
  `));
5573
7119
  process.exit(1);
5574
7120
  }
5575
- process.stdout.write(pc5.green("\nPipeline completed successfully!\n"));
7121
+ process.stdout.write(pc4.green("\nPipeline completed successfully!\n"));
5576
7122
  return;
5577
7123
  }
5578
7124
  if (parsed.mode === "status") {
5579
7125
  const tools = ["supabase", "gh", "vercel", "git"];
5580
- process.stdout.write(pc5.bold("Tool Status\n\n"));
7126
+ process.stdout.write(pc4.bold("Tool Status\n\n"));
5581
7127
  for (const toolId of tools) {
5582
7128
  const info = getToolInfo(toolId);
5583
- const status = info.installed ? pc5.green(`\u2713 ${info.label} ${info.version ?? ""}`) : pc5.red(`\u2717 ${info.label} not found`);
7129
+ const status = info.installed ? pc4.green(`\u2713 ${info.label} ${info.version ?? ""}`) : pc4.red(`\u2717 ${info.label} not found`);
5584
7130
  process.stdout.write(` ${status}
5585
7131
  `);
5586
7132
  }
5587
- process.stdout.write(pc5.bold("\nProject Status\n\n"));
7133
+ process.stdout.write(pc4.bold("\nProject Status\n\n"));
5588
7134
  const projectStatus = getCurrentStatus();
5589
7135
  if (projectStatus.supabase) {
5590
7136
  process.stdout.write(` Supabase: ${projectStatus.supabase.linked ? "linked" : "not linked"}
@@ -5608,15 +7154,15 @@ Pipeline completed with ${errors.length} error(s)
5608
7154
  }
5609
7155
  const plan = planChanges(yaml);
5610
7156
  if (plan.noChanges) {
5611
- process.stdout.write(pc5.green("No changes needed. State is up to date.\n"));
7157
+ process.stdout.write(pc4.green("No changes needed. State is up to date.\n"));
5612
7158
  return;
5613
7159
  }
5614
- process.stdout.write(pc5.bold(`Plan: ${plan.actions.length} action(s)
7160
+ process.stdout.write(pc4.bold(`Plan: ${plan.actions.length} action(s)
5615
7161
 
5616
7162
  `));
5617
7163
  for (const action of plan.actions) {
5618
7164
  const prefix = action.action === "create" ? "+" : action.action === "delete" ? "-" : "~";
5619
- const color = action.action === "create" ? pc5.green : action.action === "delete" ? pc5.red : pc5.yellow;
7165
+ const color = action.action === "create" ? pc4.green : action.action === "delete" ? pc4.red : pc4.yellow;
5620
7166
  process.stdout.write(` ${color(`${prefix} [${action.tool}] ${action.description}`)}
5621
7167
  `);
5622
7168
  }
@@ -5630,10 +7176,10 @@ Pipeline completed with ${errors.length} error(s)
5630
7176
  }
5631
7177
  const plan = planChanges(yaml);
5632
7178
  if (plan.noChanges) {
5633
- process.stdout.write(pc5.green("No changes needed. State is up to date.\n"));
7179
+ process.stdout.write(pc4.green("No changes needed. State is up to date.\n"));
5634
7180
  return;
5635
7181
  }
5636
- process.stdout.write(pc5.bold(`Applying ${plan.actions.length} action(s)...
7182
+ process.stdout.write(pc4.bold(`Applying ${plan.actions.length} action(s)...
5637
7183
 
5638
7184
  `));
5639
7185
  const results = await applyActions(plan.actions, process.cwd(), (i, total, action) => {
@@ -5642,16 +7188,16 @@ Pipeline completed with ${errors.length} error(s)
5642
7188
  });
5643
7189
  const errors = results.filter((r) => !r.success);
5644
7190
  if (errors.length > 0) {
5645
- process.stderr.write(pc5.red(`
7191
+ process.stderr.write(pc4.red(`
5646
7192
  Apply completed with ${errors.length} error(s).
5647
7193
  `));
5648
7194
  for (const err of errors) {
5649
- process.stderr.write(pc5.red(` \u2717 ${err.action.description}
7195
+ process.stderr.write(pc4.red(` \u2717 ${err.action.description}
5650
7196
  `));
5651
7197
  }
5652
7198
  process.exit(1);
5653
7199
  }
5654
- process.stdout.write(pc5.green("\nAll changes applied successfully!\n"));
7200
+ process.stdout.write(pc4.green("\nAll changes applied successfully!\n"));
5655
7201
  return;
5656
7202
  }
5657
7203
  if (parsed.mode === "config") {
@@ -5667,11 +7213,11 @@ Apply completed with ${errors.length} error(s).
5667
7213
  process.exit(result.exitCode ?? 0);
5668
7214
  }
5669
7215
  const AppComponent2 = parsed.classic ? AppClassic : AppPanel;
5670
- render(React20.createElement(AppComponent2));
7216
+ render(React27.createElement(AppComponent2));
5671
7217
  return;
5672
7218
  }
5673
7219
  const AppComponent = parsed.classic ? AppClassic : AppPanel;
5674
- render(React20.createElement(AppComponent));
7220
+ render(React27.createElement(AppComponent));
5675
7221
  }
5676
7222
  main().catch((error) => {
5677
7223
  const message = error instanceof Error ? error.message : String(error);