whale-code 6.4.0 → 6.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (187) hide show
  1. package/bin/swagmanager-mcp.js +7 -0
  2. package/dist/cli/app.js +30 -2
  3. package/dist/cli/chat/ChatApp.d.ts +4 -4
  4. package/dist/cli/chat/ChatApp.js +114 -44
  5. package/dist/cli/chat/ChatInput.d.ts +13 -6
  6. package/dist/cli/chat/ChatInput.js +433 -89
  7. package/dist/cli/chat/MemoryManager.d.ts +15 -0
  8. package/dist/cli/chat/MemoryManager.js +61 -0
  9. package/dist/cli/chat/MessageList.d.ts +8 -0
  10. package/dist/cli/chat/MessageList.js +1 -1
  11. package/dist/cli/chat/NodeManager.d.ts +30 -0
  12. package/dist/cli/chat/NodeManager.js +89 -0
  13. package/dist/cli/chat/NodeSelector.d.ts +19 -0
  14. package/dist/cli/chat/NodeSelector.js +37 -0
  15. package/dist/cli/chat/PlanApproval.d.ts +17 -0
  16. package/dist/cli/chat/PlanApproval.js +82 -0
  17. package/dist/cli/chat/SessionManager.d.ts +16 -0
  18. package/dist/cli/chat/SessionManager.js +43 -0
  19. package/dist/cli/chat/SlashMenu.d.ts +38 -0
  20. package/dist/cli/chat/SlashMenu.js +208 -0
  21. package/dist/cli/chat/StatusBar.d.ts +16 -0
  22. package/dist/cli/chat/StatusBar.js +22 -0
  23. package/dist/cli/chat/ThemeSelector.d.ts +14 -0
  24. package/dist/cli/chat/ThemeSelector.js +29 -0
  25. package/dist/cli/chat/ToolIndicator.d.ts +8 -0
  26. package/dist/cli/chat/ToolIndicator.js +33 -9
  27. package/dist/cli/chat/hooks/useAgentLoop.d.ts +2 -1
  28. package/dist/cli/chat/hooks/useAgentLoop.js +22 -17
  29. package/dist/cli/chat/hooks/useSlashCommands.d.ts +19 -0
  30. package/dist/cli/chat/hooks/useSlashCommands.js +254 -15
  31. package/dist/cli/commands/config-cmd.js +4 -25
  32. package/dist/cli/commands/db.d.ts +13 -0
  33. package/dist/cli/commands/db.js +243 -0
  34. package/dist/cli/commands/doctor.js +6 -9
  35. package/dist/cli/commands/mcp.js +1 -20
  36. package/dist/cli/services/agent-events.d.ts +22 -1
  37. package/dist/cli/services/agent-events.js +9 -0
  38. package/dist/cli/services/agent-loop.js +66 -2
  39. package/dist/cli/services/agent-worker-base.js +21 -6
  40. package/dist/cli/services/api-retry.d.ts +25 -0
  41. package/dist/cli/services/api-retry.js +91 -0
  42. package/dist/cli/services/auth-service.d.ts +1 -1
  43. package/dist/cli/services/auth-service.js +40 -19
  44. package/dist/cli/services/background-processes.js +26 -2
  45. package/dist/cli/services/config-store.d.ts +13 -1
  46. package/dist/cli/services/config-store.js +116 -13
  47. package/dist/cli/services/format-server-response.js +12 -6
  48. package/dist/cli/services/ink-resize-fix.d.ts +18 -0
  49. package/dist/cli/services/ink-resize-fix.js +66 -0
  50. package/dist/cli/services/interactive-tools.d.ts +14 -0
  51. package/dist/cli/services/interactive-tools.js +47 -2
  52. package/dist/cli/services/keybinding-manager.js +1 -1
  53. package/dist/cli/services/local-tools.js +35 -2
  54. package/dist/cli/services/server-tools.js +175 -3
  55. package/dist/cli/services/subagent.js +15 -3
  56. package/dist/cli/services/system-prompt.js +5 -3
  57. package/dist/cli/services/task-decomposer.d.ts +35 -0
  58. package/dist/cli/services/task-decomposer.js +199 -0
  59. package/dist/cli/services/team-lead.d.ts +18 -0
  60. package/dist/cli/services/team-lead.js +80 -0
  61. package/dist/cli/services/teammate.js +5 -5
  62. package/dist/cli/services/telemetry.d.ts +8 -2
  63. package/dist/cli/services/telemetry.js +116 -92
  64. package/dist/cli/services/tools/agent-tools.d.ts +1 -0
  65. package/dist/cli/services/tools/agent-tools.js +50 -4
  66. package/dist/cli/services/tools/file-ops.d.ts +2 -0
  67. package/dist/cli/services/tools/file-ops.js +71 -19
  68. package/dist/cli/services/tools/shell-exec.js +22 -12
  69. package/dist/cli/shared/Theme.d.ts +1 -2
  70. package/dist/cli/shared/Theme.js +1 -1
  71. package/dist/cli/shared/WhaleBanner.d.ts +4 -1
  72. package/dist/cli/shared/WhaleBanner.js +12 -8
  73. package/dist/cli/shared/markdown.d.ts +5 -4
  74. package/dist/cli/shared/markdown.js +376 -334
  75. package/dist/cli/shared/theme-manager.d.ts +27 -0
  76. package/dist/cli/shared/theme-manager.js +178 -0
  77. package/dist/cli/shared/theme-presets.d.ts +16 -0
  78. package/dist/cli/shared/theme-presets.js +265 -0
  79. package/dist/index.js +0 -51
  80. package/dist/node/adapters/imessage.d.ts +10 -0
  81. package/dist/node/adapters/imessage.js +45 -6
  82. package/dist/node/cli.js +459 -8
  83. package/dist/node/config.d.ts +17 -0
  84. package/dist/node/gateway-client.d.ts +55 -0
  85. package/dist/node/gateway-client.js +201 -0
  86. package/dist/node/portal/clipboard.d.ts +28 -0
  87. package/dist/node/portal/clipboard.js +183 -0
  88. package/dist/node/portal/discovery.d.ts +29 -0
  89. package/dist/node/portal/discovery.js +61 -0
  90. package/dist/node/portal/forward.d.ts +30 -0
  91. package/dist/node/portal/forward.js +90 -0
  92. package/dist/node/portal/index.d.ts +47 -0
  93. package/dist/node/portal/index.js +250 -0
  94. package/dist/node/portal/multiplexer.d.ts +48 -0
  95. package/dist/node/portal/multiplexer.js +207 -0
  96. package/dist/node/portal/permissions.d.ts +36 -0
  97. package/dist/node/portal/permissions.js +131 -0
  98. package/dist/node/portal/protocol.d.ts +140 -0
  99. package/dist/node/portal/protocol.js +193 -0
  100. package/dist/node/portal/screen.d.ts +18 -0
  101. package/dist/node/portal/screen.js +93 -0
  102. package/dist/node/portal/session.d.ts +68 -0
  103. package/dist/node/portal/session.js +127 -0
  104. package/dist/node/portal/shell.d.ts +26 -0
  105. package/dist/node/portal/shell.js +142 -0
  106. package/dist/node/portal/stream.d.ts +43 -0
  107. package/dist/node/portal/stream.js +90 -0
  108. package/dist/node/portal/transfer.d.ts +33 -0
  109. package/dist/node/portal/transfer.js +231 -0
  110. package/dist/node/portal/ui.d.ts +16 -0
  111. package/dist/node/portal/ui.js +148 -0
  112. package/dist/node/remote-desktop/compile-helper.d.ts +13 -0
  113. package/dist/node/remote-desktop/compile-helper.js +73 -0
  114. package/dist/node/remote-desktop/index.d.ts +67 -0
  115. package/dist/node/remote-desktop/index.js +220 -0
  116. package/dist/node/remote-desktop/protocol.d.ts +96 -0
  117. package/dist/node/remote-desktop/protocol.js +67 -0
  118. package/dist/node/runtime.d.ts +8 -1
  119. package/dist/node/runtime.js +117 -9
  120. package/dist/server/handlers/__test-utils__/test-db.d.ts +25 -0
  121. package/dist/server/handlers/__test-utils__/test-db.js +128 -0
  122. package/dist/server/handlers/api-keys.js +26 -2
  123. package/dist/server/handlers/browser.d.ts +0 -4
  124. package/dist/server/handlers/browser.js +0 -46
  125. package/dist/server/handlers/catalog.js +37 -14
  126. package/dist/server/handlers/clickhouse.d.ts +10 -0
  127. package/dist/server/handlers/clickhouse.js +215 -0
  128. package/dist/server/handlers/comms.d.ts +308 -4
  129. package/dist/server/handlers/comms.js +444 -11
  130. package/dist/server/handlers/creations.js +1 -1
  131. package/dist/server/handlers/crm.d.ts +54 -8
  132. package/dist/server/handlers/crm.js +353 -68
  133. package/dist/server/handlers/embeddings.js +3 -3
  134. package/dist/server/handlers/enrichment.js +39 -55
  135. package/dist/server/handlers/inventory.js +1 -1
  136. package/dist/server/handlers/kali.d.ts +9 -1
  137. package/dist/server/handlers/kali.js +50 -1
  138. package/dist/server/handlers/media.d.ts +8 -0
  139. package/dist/server/handlers/media.js +902 -0
  140. package/dist/server/handlers/meta-ads.js +6 -3
  141. package/dist/server/handlers/nodes.d.ts +2 -0
  142. package/dist/server/handlers/nodes.js +331 -40
  143. package/dist/server/handlers/operations.d.ts +4 -6
  144. package/dist/server/handlers/operations.js +99 -38
  145. package/dist/server/handlers/platform.js +224 -107
  146. package/dist/server/handlers/remove-bg.d.ts +6 -0
  147. package/dist/server/handlers/remove-bg.js +96 -0
  148. package/dist/server/handlers/storefront.d.ts +6 -0
  149. package/dist/server/handlers/storefront.js +477 -0
  150. package/dist/server/handlers/supply-chain.js +21 -3
  151. package/dist/server/handlers/workflow-steps.js +87 -31
  152. package/dist/server/handlers/workflows.js +4 -1
  153. package/dist/server/index.js +334 -88
  154. package/dist/server/lib/clickhouse-buffer.d.ts +48 -0
  155. package/dist/server/lib/clickhouse-buffer.js +175 -0
  156. package/dist/server/lib/clickhouse-client.d.ts +112 -0
  157. package/dist/server/lib/clickhouse-client.js +141 -0
  158. package/dist/server/lib/coa-renderer.d.ts +91 -0
  159. package/dist/server/lib/coa-renderer.js +411 -0
  160. package/dist/server/lib/compaction-service.js +45 -1
  161. package/dist/server/lib/pdf-renderer.d.ts +143 -0
  162. package/dist/server/lib/pdf-renderer.js +867 -0
  163. package/dist/server/lib/react-pdf-layout.d.ts +40 -0
  164. package/dist/server/lib/react-pdf-layout.js +437 -0
  165. package/dist/server/lib/server-agent-loop.d.ts +2 -0
  166. package/dist/server/lib/server-agent-loop.js +61 -15
  167. package/dist/server/lib/server-subagent.d.ts +3 -0
  168. package/dist/server/lib/server-subagent.js +7 -4
  169. package/dist/server/lib/supabase-client.js +51 -3
  170. package/dist/server/lib/template-resolver.js +14 -4
  171. package/dist/server/lib/utils.js +15 -0
  172. package/dist/server/local-agent-gateway.d.ts +44 -0
  173. package/dist/server/local-agent-gateway.js +389 -49
  174. package/dist/server/providers/anthropic.js +12 -2
  175. package/dist/server/providers/gemini.js +17 -2
  176. package/dist/server/proxy-handlers.js +151 -0
  177. package/dist/server/tool-router.d.ts +2 -2
  178. package/dist/server/tool-router.js +25 -35
  179. package/dist/shared/agent-core.d.ts +5 -2
  180. package/dist/shared/agent-core.js +30 -4
  181. package/dist/shared/api-client.js +54 -3
  182. package/dist/shared/sse-parser.d.ts +1 -1
  183. package/dist/shared/sse-parser.js +5 -2
  184. package/dist/shared/tool-dispatch.js +1 -1
  185. package/package.json +16 -10
  186. package/dist/server/handlers/__test-utils__/mock-supabase.d.ts +0 -11
  187. package/dist/server/handlers/__test-utils__/mock-supabase.js +0 -393
@@ -0,0 +1,15 @@
1
+ /**
2
+ * MemoryManager — interactive memory list with delete
3
+ *
4
+ * Renders a navigable list of remembered facts.
5
+ * Press `d` to delete the selected memory, Esc to cancel.
6
+ *
7
+ * Follows StoreSelector pattern (useInput + manual index).
8
+ */
9
+ interface MemoryManagerProps {
10
+ memories: string[];
11
+ onDelete: (index: number) => void;
12
+ onCancel: () => void;
13
+ }
14
+ export declare function MemoryManager({ memories, onDelete, onCancel }: MemoryManagerProps): import("react/jsx-runtime").JSX.Element;
15
+ export {};
@@ -0,0 +1,61 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * MemoryManager — interactive memory list with delete
4
+ *
5
+ * Renders a navigable list of remembered facts.
6
+ * Press `d` to delete the selected memory, Esc to cancel.
7
+ *
8
+ * Follows StoreSelector pattern (useInput + manual index).
9
+ */
10
+ import { useState } from "react";
11
+ import { Box, Text, useInput } from "ink";
12
+ import { colors, symbols } from "../shared/Theme.js";
13
+ export function MemoryManager({ memories, onDelete, onCancel }) {
14
+ const [selectedIndex, setSelectedIndex] = useState(0);
15
+ const [confirmDelete, setConfirmDelete] = useState(false);
16
+ useInput((input, key) => {
17
+ // Delete confirmation
18
+ if (confirmDelete) {
19
+ if (input === "y" || input === "Y") {
20
+ setConfirmDelete(false);
21
+ onDelete(selectedIndex);
22
+ // Adjust selection if we deleted the last item
23
+ if (selectedIndex >= memories.length - 1 && selectedIndex > 0) {
24
+ setSelectedIndex(selectedIndex - 1);
25
+ }
26
+ return;
27
+ }
28
+ // Any other key cancels the confirmation
29
+ setConfirmDelete(false);
30
+ return;
31
+ }
32
+ if (key.escape) {
33
+ onCancel();
34
+ return;
35
+ }
36
+ if (key.upArrow) {
37
+ setSelectedIndex((prev) => Math.max(0, prev - 1));
38
+ return;
39
+ }
40
+ if (key.downArrow) {
41
+ setSelectedIndex((prev) => Math.min(memories.length - 1, prev + 1));
42
+ return;
43
+ }
44
+ if (input === "d" || input === "D") {
45
+ setConfirmDelete(true);
46
+ return;
47
+ }
48
+ });
49
+ const termWidth = process.stdout.columns || 80;
50
+ const boxWidth = Math.min(termWidth - 2, 64);
51
+ if (memories.length === 0) {
52
+ return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: colors.border, paddingX: 1, paddingY: 1, width: boxWidth, children: [_jsx(Text, { bold: true, color: colors.brand, children: "Memories" }), _jsx(Box, { height: 1 }), _jsx(Text, { dimColor: true, children: "No memories left." }), _jsx(Box, { height: 1 }), _jsx(Text, { dimColor: true, children: "esc to close" })] }));
53
+ }
54
+ return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: colors.border, paddingX: 1, paddingY: 1, width: boxWidth, children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, color: colors.brand, children: "Memories" }) }), _jsx(Box, { flexDirection: "column", children: memories.map((memory, i) => {
55
+ const isSelected = i === selectedIndex;
56
+ const display = memory.length > 50
57
+ ? memory.slice(0, 49) + "…"
58
+ : memory;
59
+ return (_jsxs(Box, { children: [_jsxs(Text, { color: isSelected ? colors.brand : colors.quaternary, children: [isSelected ? symbols.arrowRight : " ", " "] }), _jsx(Text, { color: isSelected ? colors.text : colors.secondary, bold: isSelected, children: display })] }, `${i}-${memory.slice(0, 20)}`));
60
+ }) }), _jsx(Box, { marginTop: 1, children: confirmDelete ? (_jsxs(Text, { color: colors.warning, children: ["Delete \"", memories[selectedIndex]?.slice(0, 30), "\u2026\"? y/n"] })) : (_jsxs(Text, { dimColor: true, children: [symbols.arrowRight, " d delete esc cancel"] })) })] }));
61
+ }
@@ -13,6 +13,14 @@ export interface ToolCall {
13
13
  result?: string;
14
14
  input?: Record<string, unknown>;
15
15
  durationMs?: number;
16
+ progress?: {
17
+ phase?: string;
18
+ elapsed_s: number;
19
+ stdout_lines: number;
20
+ stderr_lines: number;
21
+ stdout_bytes: number;
22
+ last_line?: string;
23
+ };
16
24
  }
17
25
  export interface ChatMessage {
18
26
  role: "user" | "assistant";
@@ -76,5 +76,5 @@ function groupConsecutiveTools(toolCalls) {
76
76
  export const CompletedMessage = React.memo(function CompletedMessage({ msg, index, toolsExpanded }) {
77
77
  const cw = Math.max(20, contentWidth());
78
78
  const toolGroups = useMemo(() => msg.toolCalls ? groupConsecutiveTools(msg.toolCalls) : [], [msg.toolCalls]);
79
- return (_jsxs(Box, { flexDirection: "column", children: [msg.role === "user" && index > 0 && (_jsxs(_Fragment, { children: [_jsx(Text, { children: " " }), _jsx(Text, { color: colors.separator, children: "─".repeat(cw) })] })), msg.role === "user" ? (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { children: " " }), msg.images && msg.images.length > 0 && (_jsx(Box, { marginLeft: 2, children: msg.images.map((name, i) => (_jsxs(Text, { children: [_jsx(Text, { color: colors.indigo, children: "[" }), _jsx(Text, { color: colors.secondary, children: name }), _jsx(Text, { color: colors.indigo, children: "]" }), _jsx(Text, { children: " " })] }, i))) })), _jsxs(Text, { children: [_jsx(Text, { color: colors.brand, bold: true, children: "❯ " }), _jsx(Text, { color: colors.user, children: msg.text })] })] })) : (_jsxs(Box, { flexDirection: "column", children: [toolGroups.length > 0 && (_jsx(Box, { flexDirection: "column", marginLeft: 2, marginTop: 1, children: toolGroups.map((group, j) => (_jsx(ToolIndicator, { id: `done-${index}-${j}`, name: group.tool.name, status: group.tool.status, result: group.tool.result, input: group.tool.input, durationMs: group.tool.durationMs, expanded: toolsExpanded, count: group.count }, j))) })), msg.completedSubagents && msg.completedSubagents.length > 0 && (_jsx(CompletedSubagentTree, { agents: msg.completedSubagents })), msg.text && (_jsxs(Box, { flexDirection: "column", marginLeft: 2, children: [_jsx(Text, { children: " " }), _jsx(MarkdownText, { text: msg.text })] })), msg.usage && (_jsxs(_Fragment, { children: [_jsx(Text, { children: " " }), _jsxs(Text, { children: [" ", _jsxs(Text, { color: colors.quaternary, children: [formatTokens(msg.usage.input_tokens), _jsx(Text, { color: colors.indigo, children: "\u2191" }), " ", formatTokens(msg.usage.output_tokens), _jsx(Text, { color: colors.purple, children: "\u2193" }), msg.usage.thinking_tokens ? (_jsxs(_Fragment, { children: [" ", formatTokens(msg.usage.thinking_tokens), _jsx(Text, { color: colors.warning, children: "T" })] })) : null, msg.usage.cache_read_tokens ? (_jsxs(_Fragment, { children: [" ", formatTokens(msg.usage.cache_read_tokens), _jsx(Text, { color: colors.success, children: "C" })] })) : null] }), _jsxs(Text, { color: colors.quaternary, children: [" ", estimateCost(msg.usage.input_tokens, msg.usage.output_tokens, msg.usage.model, msg.usage.costUsd)] }), msg.toolCalls && msg.toolCalls.length > 0 ? (_jsxs(Text, { color: colors.quaternary, children: [" ", msg.toolCalls.length, " tool", msg.toolCalls.length !== 1 ? "s" : "", " ", formatMs(totalToolDuration(msg.toolCalls))] })) : null] })] }))] }))] }));
79
+ return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [msg.role === "user" && index > 0 && (_jsxs(_Fragment, { children: [_jsx(Text, { children: " " }), _jsx(Text, { color: colors.separator, children: "─".repeat(cw) })] })), msg.role === "user" ? (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { children: " " }), msg.images && msg.images.length > 0 && (_jsx(Box, { marginLeft: 2, children: msg.images.map((name, i) => (_jsxs(Text, { children: [_jsx(Text, { color: colors.indigo, children: "[" }), _jsx(Text, { color: colors.secondary, children: name }), _jsx(Text, { color: colors.indigo, children: "]" }), _jsx(Text, { children: " " })] }, i))) })), _jsxs(Text, { children: [_jsx(Text, { color: colors.brand, bold: true, children: "❯ " }), _jsx(Text, { color: colors.user, children: msg.text })] })] })) : (_jsxs(Box, { flexDirection: "column", children: [toolGroups.length > 0 && (_jsx(Box, { flexDirection: "column", marginLeft: 2, children: toolGroups.map((group, j) => (_jsx(ToolIndicator, { id: `done-${index}-${j}`, name: group.tool.name, status: group.tool.status, result: group.tool.result, input: group.tool.input, durationMs: group.tool.durationMs, expanded: toolsExpanded, count: group.count }, j))) })), msg.completedSubagents && msg.completedSubagents.length > 0 && (_jsx(CompletedSubagentTree, { agents: msg.completedSubagents })), msg.text && (_jsxs(Box, { flexDirection: "column", marginLeft: 2, children: [_jsx(Text, { children: " " }), _jsx(MarkdownText, { text: msg.text })] })), msg.usage && (_jsxs(_Fragment, { children: [_jsx(Text, { children: " " }), _jsxs(Text, { children: [" ", _jsxs(Text, { color: colors.quaternary, children: [formatTokens(msg.usage.input_tokens), _jsx(Text, { color: colors.indigo, children: "\u2191" }), " ", formatTokens(msg.usage.output_tokens), _jsx(Text, { color: colors.purple, children: "\u2193" }), msg.usage.thinking_tokens ? (_jsxs(_Fragment, { children: [" ", formatTokens(msg.usage.thinking_tokens), _jsx(Text, { color: colors.warning, children: "T" })] })) : null, msg.usage.cache_read_tokens ? (_jsxs(_Fragment, { children: [" ", formatTokens(msg.usage.cache_read_tokens), _jsx(Text, { color: colors.success, children: "C" })] })) : null] }), _jsxs(Text, { color: colors.quaternary, children: [" ", estimateCost(msg.usage.input_tokens, msg.usage.output_tokens, msg.usage.model, msg.usage.costUsd)] }), msg.toolCalls && msg.toolCalls.length > 0 ? (_jsxs(Text, { color: colors.quaternary, children: [" ", msg.toolCalls.length, " tool", msg.toolCalls.length !== 1 ? "s" : "", " ", formatMs(totalToolDuration(msg.toolCalls))] })) : null] })] }))] }))] }));
80
80
  });
@@ -0,0 +1,30 @@
1
+ /**
2
+ * NodeManager — animated interactive node status panel
3
+ *
4
+ * Stays open during operations, shows spinners in-place,
5
+ * and updates the status display after each action.
6
+ *
7
+ * Phases:
8
+ * idle → status + action button
9
+ * busy → spinner with action label
10
+ * result → updated status + success/failure flash
11
+ */
12
+ export interface NodeStatus {
13
+ registered: boolean;
14
+ running: boolean;
15
+ nodeId?: string;
16
+ port?: number;
17
+ }
18
+ export interface NodeActionResult {
19
+ success: boolean;
20
+ message: string;
21
+ newStatus: NodeStatus;
22
+ }
23
+ interface NodeManagerProps {
24
+ initialStatus: NodeStatus;
25
+ onToggle: () => Promise<NodeActionResult>;
26
+ onRegister: () => Promise<NodeActionResult>;
27
+ onClose: (resultMessage?: string) => void;
28
+ }
29
+ export declare function NodeManager({ initialStatus, onToggle, onRegister, onClose }: NodeManagerProps): import("react/jsx-runtime").JSX.Element;
30
+ export {};
@@ -0,0 +1,89 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * NodeManager — animated interactive node status panel
4
+ *
5
+ * Stays open during operations, shows spinners in-place,
6
+ * and updates the status display after each action.
7
+ *
8
+ * Phases:
9
+ * idle → status + action button
10
+ * busy → spinner with action label
11
+ * result → updated status + success/failure flash
12
+ */
13
+ import { useState, useEffect, useCallback } from "react";
14
+ import { Box, Text, useInput } from "ink";
15
+ import Spinner from "ink-spinner";
16
+ import { colors, symbols } from "../shared/Theme.js";
17
+ export function NodeManager({ initialStatus, onToggle, onRegister, onClose }) {
18
+ const [status, setStatus] = useState(initialStatus);
19
+ const [phase, setPhase] = useState("idle");
20
+ const [busyLabel, setBusyLabel] = useState("");
21
+ const [result, setResult] = useState(null);
22
+ const [lastResult, setLastResult] = useState();
23
+ // Auto-transition result → idle after 2.5s
24
+ useEffect(() => {
25
+ if (phase !== "result")
26
+ return;
27
+ const timer = setTimeout(() => setPhase("idle"), 2500);
28
+ return () => clearTimeout(timer);
29
+ }, [phase]);
30
+ const executeAction = useCallback(async (action) => {
31
+ const label = action === "register"
32
+ ? "Registering node"
33
+ : status.running
34
+ ? "Stopping node"
35
+ : "Starting node";
36
+ setPhase("busy");
37
+ setBusyLabel(label);
38
+ setResult(null);
39
+ try {
40
+ const res = action === "register" ? await onRegister() : await onToggle();
41
+ setStatus(res.newStatus);
42
+ setResult({ success: res.success, text: res.message });
43
+ setLastResult(res.message);
44
+ setPhase("result");
45
+ }
46
+ catch {
47
+ setResult({ success: false, text: "Unexpected error" });
48
+ setLastResult("Unexpected error");
49
+ setPhase("result");
50
+ }
51
+ }, [status.running, onToggle, onRegister]);
52
+ useInput((_input, key) => {
53
+ if (phase === "busy")
54
+ return; // ignore input during operation
55
+ if (key.escape) {
56
+ onClose(lastResult);
57
+ return;
58
+ }
59
+ if (key.return) {
60
+ if (!status.registered) {
61
+ executeAction("register");
62
+ }
63
+ else {
64
+ executeAction("toggle");
65
+ }
66
+ return;
67
+ }
68
+ });
69
+ const termWidth = process.stdout.columns || 80;
70
+ const boxWidth = Math.min(termWidth - 2, 52);
71
+ const port = status.port || 7890;
72
+ // Action label for the button
73
+ const actionLabel = !status.registered
74
+ ? "Register this machine"
75
+ : status.running
76
+ ? "Stop node"
77
+ : "Start node";
78
+ return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: phase === "busy" ? colors.brand : colors.border, paddingX: 1, paddingY: 1, width: boxWidth, children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, color: colors.brand, children: "Node" }) }), _jsxs(Box, { flexDirection: "column", marginLeft: 1, children: [_jsx(Text, { color: colors.secondary, children: "Local node" }), phase === "busy" ? (
79
+ /* ── Busy: animated spinner ── */
80
+ _jsxs(Box, { marginLeft: 1, marginTop: 0, children: [_jsx(Text, { color: colors.brand, children: _jsx(Spinner, { type: "dots" }) }), _jsxs(Text, { color: colors.secondary, children: [" ", busyLabel, "..."] })] })) : !status.registered ? (
81
+ /* ── Not registered ── */
82
+ _jsx(Box, { marginLeft: 1, children: _jsx(Text, { dimColor: true, children: "Not registered." }) })) : (
83
+ /* ── Registered: show status details ── */
84
+ _jsxs(Box, { flexDirection: "column", marginLeft: 1, children: [_jsxs(Box, { children: [_jsx(Text, { dimColor: true, children: "Status " }), status.running ? (_jsx(Text, { color: colors.success, children: "● running" })) : (_jsx(Text, { color: colors.dim, children: "○ stopped" })), status.running && (_jsxs(Text, { dimColor: true, children: [" (port ", port, ")"] }))] }), status.nodeId && (_jsxs(Box, { children: [_jsx(Text, { dimColor: true, children: "Node ID " }), _jsx(Text, { color: colors.secondary, children: status.nodeId.length > 24
85
+ ? status.nodeId.slice(0, 23) + "…"
86
+ : status.nodeId })] }))] })), phase === "result" && result && (_jsx(Box, { marginLeft: 1, marginTop: 0, children: _jsxs(Text, { color: result.success ? colors.success : colors.error, children: [result.success ? symbols.check : symbols.cross, " ", result.text] }) }))] }), phase !== "busy" && (_jsxs(Box, { marginTop: 1, children: [_jsxs(Text, { color: colors.brand, children: [symbols.arrowRight, " "] }), _jsx(Text, { bold: true, color: colors.text, children: actionLabel })] })), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: phase === "busy"
87
+ ? "please wait..."
88
+ : "enter execute esc close" }) })] }));
89
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * NodeSelector — inline node picker for /deploy-cluster
3
+ *
4
+ * Renders a SelectInput with available nodes. Esc to cancel.
5
+ * Follows StoreSelector pattern.
6
+ */
7
+ export interface NodeInfo {
8
+ id: string;
9
+ name: string;
10
+ status: string;
11
+ isLocal: boolean;
12
+ }
13
+ interface NodeSelectorProps {
14
+ nodes: NodeInfo[];
15
+ onSelect: (node: NodeInfo) => void;
16
+ onCancel: () => void;
17
+ }
18
+ export declare function NodeSelector({ nodes, onSelect, onCancel }: NodeSelectorProps): import("react/jsx-runtime").JSX.Element;
19
+ export {};
@@ -0,0 +1,37 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Box, Text, useInput } from "ink";
3
+ import SelectInput from "ink-select-input";
4
+ import { colors, symbols } from "../shared/Theme.js";
5
+ export function NodeSelector({ nodes, onSelect, onCancel }) {
6
+ useInput((_input, key) => {
7
+ if (key.escape)
8
+ onCancel();
9
+ });
10
+ // Sort: local first, then online, then offline
11
+ const sorted = [...nodes].sort((a, b) => {
12
+ if (a.isLocal && !b.isLocal)
13
+ return -1;
14
+ if (!a.isLocal && b.isLocal)
15
+ return 1;
16
+ if (a.status === "online" && b.status !== "online")
17
+ return -1;
18
+ if (a.status !== "online" && b.status === "online")
19
+ return 1;
20
+ return 0;
21
+ });
22
+ const items = sorted.map((n) => ({
23
+ label: n.name,
24
+ value: n.id,
25
+ }));
26
+ const handleSelect = (item) => {
27
+ const node = sorted.find((n) => n.id === item.value);
28
+ if (node)
29
+ onSelect(node);
30
+ };
31
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: colors.secondary, children: " Select node for deployment:" }), _jsx(Box, { height: 1 }), _jsx(SelectInput, { items: items, onSelect: handleSelect, indicatorComponent: ({ isSelected }) => (_jsxs(Text, { color: isSelected ? colors.brand : colors.quaternary, children: [isSelected ? symbols.arrowRight : " ", " "] })), itemComponent: ({ isSelected, label }) => {
32
+ const node = sorted.find((n) => n.name === label);
33
+ const isOnline = node?.status === "online";
34
+ const isLocal = node?.isLocal;
35
+ return (_jsxs(Box, { children: [_jsxs(Text, { color: isOnline ? colors.success : colors.dim, children: [isOnline ? symbols.dot : "○", " "] }), _jsx(Text, { color: isSelected ? colors.brand : colors.text, bold: isSelected, children: label }), isLocal && _jsx(Text, { color: colors.info, children: " (this machine)" }), !isOnline && _jsx(Text, { color: colors.dim, children: " offline" })] }));
36
+ } }), _jsx(Text, { color: colors.quaternary, children: " esc to cancel" })] }));
37
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * PlanApproval — slim action picker for plan approval
3
+ *
4
+ * Plan content is rendered in the Static area (normal terminal scrolling).
5
+ * This component only shows the action options:
6
+ * - Execute (clear context, keep plan — default)
7
+ * - Edit plan (re-enter plan mode)
8
+ * - Give feedback (text input for revision)
9
+ * - Cancel (abort)
10
+ */
11
+ import type { PlanApprovalDecision } from "../services/interactive-tools.js";
12
+ interface PlanApprovalProps {
13
+ planFile: string;
14
+ onDecision: (decision: PlanApprovalDecision) => void;
15
+ }
16
+ export declare function PlanApproval({ planFile, onDecision }: PlanApprovalProps): import("react/jsx-runtime").JSX.Element;
17
+ export {};
@@ -0,0 +1,82 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * PlanApproval — slim action picker for plan approval
4
+ *
5
+ * Plan content is rendered in the Static area (normal terminal scrolling).
6
+ * This component only shows the action options:
7
+ * - Execute (clear context, keep plan — default)
8
+ * - Edit plan (re-enter plan mode)
9
+ * - Give feedback (text input for revision)
10
+ * - Cancel (abort)
11
+ */
12
+ import { useState } from "react";
13
+ import { Box, Text, useInput } from "ink";
14
+ import { colors, symbols } from "../shared/Theme.js";
15
+ const OPTIONS = [
16
+ { label: "Execute", description: "Clear context & implement plan", action: "execute" },
17
+ { label: "Edit plan", description: "Re-enter plan mode", action: "edit" },
18
+ { label: "Give feedback", description: "Revise with your notes", action: "feedback" },
19
+ { label: "Cancel", description: "Abort plan", action: "cancel" },
20
+ ];
21
+ // ============================================================================
22
+ // FEEDBACK INPUT SUB-COMPONENT
23
+ // ============================================================================
24
+ function FeedbackInput({ onSubmit, onCancel, }) {
25
+ const [text, setText] = useState("");
26
+ useInput((input, key) => {
27
+ if (key.escape) {
28
+ onCancel();
29
+ return;
30
+ }
31
+ if (key.return) {
32
+ if (text.trim()) {
33
+ onSubmit(text.trim());
34
+ }
35
+ return;
36
+ }
37
+ if (key.backspace || key.delete) {
38
+ setText(prev => prev.slice(0, -1));
39
+ return;
40
+ }
41
+ if (!key.ctrl && !key.meta && input) {
42
+ setText(prev => prev + input);
43
+ }
44
+ });
45
+ return (_jsxs(Box, { marginLeft: 2, children: [_jsx(Text, { color: colors.brand, bold: true, children: "feedback: " }), _jsx(Text, { children: text }), _jsx(Text, { color: colors.brand, children: "|" }), _jsx(Text, { color: colors.quaternary, children: " enter submit esc back" })] }));
46
+ }
47
+ // ============================================================================
48
+ // MAIN COMPONENT
49
+ // ============================================================================
50
+ export function PlanApproval({ planFile, onDecision }) {
51
+ const [selectedIndex, setSelectedIndex] = useState(0);
52
+ const [mode, setMode] = useState("options");
53
+ const termWidth = process.stdout.columns || 80;
54
+ const divider = "─".repeat(Math.max(20, termWidth - 2));
55
+ useInput((input, key) => {
56
+ if (mode === "feedback")
57
+ return;
58
+ if (key.escape) {
59
+ onDecision({ action: "cancel" });
60
+ return;
61
+ }
62
+ if (key.upArrow) {
63
+ setSelectedIndex(prev => Math.max(0, prev - 1));
64
+ return;
65
+ }
66
+ if (key.downArrow) {
67
+ setSelectedIndex(prev => Math.min(OPTIONS.length - 1, prev + 1));
68
+ return;
69
+ }
70
+ if (key.return) {
71
+ const option = OPTIONS[selectedIndex];
72
+ if (option.action === "feedback") {
73
+ setMode("feedback");
74
+ }
75
+ else {
76
+ onDecision({ action: option.action });
77
+ }
78
+ return;
79
+ }
80
+ });
81
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { children: " " }), _jsx(Text, { color: colors.separator, children: divider }), _jsxs(Box, { marginLeft: 2, marginTop: 1, children: [_jsx(Text, { color: colors.brand, bold: true, children: "Plan ready" }), _jsxs(Text, { color: colors.dim, children: [" ", planFile] })] }), mode === "feedback" ? (_jsx(FeedbackInput, { onSubmit: (text) => onDecision({ action: "feedback", feedback: text }), onCancel: () => setMode("options") })) : (_jsx(Box, { marginLeft: 2, marginTop: 1, flexDirection: "column", children: OPTIONS.map((opt, i) => (_jsxs(Box, { children: [_jsxs(Text, { color: i === selectedIndex ? colors.brand : colors.quaternary, children: [i === selectedIndex ? symbols.arrowRight : " ", " "] }), _jsx(Text, { color: i === selectedIndex ? colors.text : colors.secondary, bold: i === selectedIndex, children: opt.label }), _jsxs(Text, { color: colors.dim, children: [" ", opt.description] })] }, opt.action))) })), _jsx(Box, { marginLeft: 2, marginTop: 1, children: _jsx(Text, { color: colors.quaternary, children: mode === "feedback" ? "enter submit esc back" : "↑↓ select enter confirm esc cancel" }) })] }));
82
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * SessionManager — interactive session picker
3
+ *
4
+ * Renders a navigable list of saved sessions.
5
+ * Enter resumes the selected session, Esc cancels.
6
+ *
7
+ * Follows StoreSelector pattern (useInput + manual index).
8
+ */
9
+ import type { SessionMeta } from "../services/session-persistence.js";
10
+ interface SessionManagerProps {
11
+ sessions: SessionMeta[];
12
+ onSelect: (session: SessionMeta) => void;
13
+ onCancel: () => void;
14
+ }
15
+ export declare function SessionManager({ sessions, onSelect, onCancel }: SessionManagerProps): import("react/jsx-runtime").JSX.Element;
16
+ export {};
@@ -0,0 +1,43 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * SessionManager — interactive session picker
4
+ *
5
+ * Renders a navigable list of saved sessions.
6
+ * Enter resumes the selected session, Esc cancels.
7
+ *
8
+ * Follows StoreSelector pattern (useInput + manual index).
9
+ */
10
+ import { useState } from "react";
11
+ import { Box, Text, useInput } from "ink";
12
+ import { colors, symbols } from "../shared/Theme.js";
13
+ export function SessionManager({ sessions, onSelect, onCancel }) {
14
+ const [selectedIndex, setSelectedIndex] = useState(0);
15
+ useInput((_input, key) => {
16
+ if (key.escape) {
17
+ onCancel();
18
+ return;
19
+ }
20
+ if (key.upArrow) {
21
+ setSelectedIndex((prev) => Math.max(0, prev - 1));
22
+ return;
23
+ }
24
+ if (key.downArrow) {
25
+ setSelectedIndex((prev) => Math.min(sessions.length - 1, prev + 1));
26
+ return;
27
+ }
28
+ if (key.return) {
29
+ onSelect(sessions[selectedIndex]);
30
+ return;
31
+ }
32
+ });
33
+ const termWidth = process.stdout.columns || 80;
34
+ const boxWidth = Math.min(termWidth - 2, 68);
35
+ return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: colors.border, paddingX: 1, paddingY: 1, width: boxWidth, children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, color: colors.brand, children: "Sessions" }) }), _jsx(Box, { flexDirection: "column", children: sessions.map((session, i) => {
36
+ const isSelected = i === selectedIndex;
37
+ const title = session.title.length > 35
38
+ ? session.title.slice(0, 34) + "…"
39
+ : session.title.padEnd(35);
40
+ const date = session.updatedAt?.slice(0, 10) || "";
41
+ return (_jsxs(Box, { children: [_jsxs(Text, { color: isSelected ? colors.brand : colors.quaternary, children: [isSelected ? symbols.arrowRight : " ", " "] }), _jsx(Text, { color: isSelected ? colors.text : colors.secondary, bold: isSelected, children: title }), _jsxs(Text, { dimColor: true, children: [" ", String(session.messageCount).padStart(3), " msgs"] }), _jsxs(Text, { dimColor: true, children: [" ", date] })] }, session.id));
42
+ }) }), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { dimColor: true, children: [symbols.arrowRight, " enter resume esc cancel"] }) })] }));
43
+ }
@@ -0,0 +1,38 @@
1
+ /**
2
+ * SlashMenu — Two-level categorized slash command menu
3
+ *
4
+ * Pure rendering component: zero internal state, zero keyboard handling.
5
+ * ChatInput owns all state and passes props.
6
+ *
7
+ * Level 1: Category list (6 rows)
8
+ * Level 2: Commands within a category (with breadcrumb header)
9
+ * Filter: Flat filtered list (existing behavior)
10
+ */
11
+ export interface SlashCommand {
12
+ name: string;
13
+ description: string;
14
+ }
15
+ interface CategoryDef {
16
+ label: string;
17
+ color: string;
18
+ commands: SlashCommand[];
19
+ }
20
+ export declare const CATEGORIES: CategoryDef[];
21
+ /** Flat array of all commands (for external consumers like /help) */
22
+ export declare const SLASH_COMMANDS: SlashCommand[];
23
+ export declare function getCategoryCount(): number;
24
+ export declare function getCategoryCommandCount(catIndex: number): number;
25
+ export declare function getCategoryLabel(catIndex: number): string;
26
+ export declare function getCategoryColor(catIndex: number): string;
27
+ export declare function getCategoryCommand(catIndex: number, cmdIndex: number): SlashCommand | null;
28
+ /** Count of selectable (command) items in a filtered list */
29
+ export declare function getFilteredSelectableCount(filter: string): number;
30
+ /** Get the command at a given selectable index (skipping headers) */
31
+ export declare function getCommandAtIndex(filter: string, selectableIndex: number): SlashCommand | null;
32
+ interface SlashMenuProps {
33
+ filter: string;
34
+ selectedIndex: number;
35
+ selectedCategory: number | null;
36
+ }
37
+ export declare function SlashMenu({ filter, selectedIndex, selectedCategory }: SlashMenuProps): import("react/jsx-runtime").JSX.Element | null;
38
+ export {};