whale-code 6.4.0 → 6.5.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/bin/swagmanager-mcp.js +51 -0
- package/dist/cli/app.js +30 -2
- package/dist/cli/chat/ChatApp.d.ts +4 -4
- package/dist/cli/chat/ChatApp.js +114 -44
- package/dist/cli/chat/ChatInput.d.ts +13 -6
- package/dist/cli/chat/ChatInput.js +433 -89
- package/dist/cli/chat/MemoryManager.d.ts +15 -0
- package/dist/cli/chat/MemoryManager.js +61 -0
- package/dist/cli/chat/MessageList.d.ts +8 -0
- package/dist/cli/chat/MessageList.js +1 -1
- package/dist/cli/chat/NodeManager.d.ts +30 -0
- package/dist/cli/chat/NodeManager.js +89 -0
- package/dist/cli/chat/NodeSelector.d.ts +19 -0
- package/dist/cli/chat/NodeSelector.js +37 -0
- package/dist/cli/chat/PlanApproval.d.ts +17 -0
- package/dist/cli/chat/PlanApproval.js +82 -0
- package/dist/cli/chat/SessionManager.d.ts +16 -0
- package/dist/cli/chat/SessionManager.js +43 -0
- package/dist/cli/chat/SlashMenu.d.ts +38 -0
- package/dist/cli/chat/SlashMenu.js +208 -0
- package/dist/cli/chat/StatusBar.d.ts +16 -0
- package/dist/cli/chat/StatusBar.js +22 -0
- package/dist/cli/chat/ThemeSelector.d.ts +14 -0
- package/dist/cli/chat/ThemeSelector.js +29 -0
- package/dist/cli/chat/ToolIndicator.d.ts +8 -0
- package/dist/cli/chat/ToolIndicator.js +33 -9
- package/dist/cli/chat/hooks/useAgentLoop.d.ts +2 -1
- package/dist/cli/chat/hooks/useAgentLoop.js +22 -17
- package/dist/cli/chat/hooks/useSlashCommands.d.ts +19 -0
- package/dist/cli/chat/hooks/useSlashCommands.js +254 -15
- package/dist/cli/commands/config-cmd.js +4 -25
- package/dist/cli/commands/db.d.ts +13 -0
- package/dist/cli/commands/db.js +243 -0
- package/dist/cli/commands/doctor.js +6 -9
- package/dist/cli/commands/mcp.js +1 -20
- package/dist/cli/services/agent-events.d.ts +22 -1
- package/dist/cli/services/agent-events.js +9 -0
- package/dist/cli/services/agent-loop.js +65 -8
- package/dist/cli/services/agent-worker-base.js +21 -6
- package/dist/cli/services/api-retry.d.ts +25 -0
- package/dist/cli/services/api-retry.js +91 -0
- package/dist/cli/services/auth-service.d.ts +1 -1
- package/dist/cli/services/auth-service.js +40 -19
- package/dist/cli/services/background-processes.js +26 -2
- package/dist/cli/services/config-store.d.ts +13 -1
- package/dist/cli/services/config-store.js +116 -13
- package/dist/cli/services/format-server-response.js +12 -6
- package/dist/cli/services/ink-resize-fix.d.ts +18 -0
- package/dist/cli/services/ink-resize-fix.js +66 -0
- package/dist/cli/services/interactive-tools.d.ts +14 -0
- package/dist/cli/services/interactive-tools.js +47 -2
- package/dist/cli/services/keybinding-manager.js +1 -1
- package/dist/cli/services/local-tools.js +35 -2
- package/dist/cli/services/server-tools.js +175 -3
- package/dist/cli/services/subagent.js +7 -6
- package/dist/cli/services/system-prompt.js +5 -3
- package/dist/cli/services/task-decomposer.d.ts +35 -0
- package/dist/cli/services/task-decomposer.js +199 -0
- package/dist/cli/services/team-lead.d.ts +18 -0
- package/dist/cli/services/team-lead.js +80 -0
- package/dist/cli/services/teammate.js +5 -5
- package/dist/cli/services/telemetry.d.ts +8 -2
- package/dist/cli/services/telemetry.js +116 -92
- package/dist/cli/services/tools/agent-tools.d.ts +1 -0
- package/dist/cli/services/tools/agent-tools.js +50 -4
- package/dist/cli/services/tools/file-ops.d.ts +2 -0
- package/dist/cli/services/tools/file-ops.js +85 -19
- package/dist/cli/services/tools/shell-exec.js +22 -12
- package/dist/cli/shared/Theme.d.ts +1 -2
- package/dist/cli/shared/Theme.js +1 -1
- package/dist/cli/shared/WhaleBanner.d.ts +4 -1
- package/dist/cli/shared/WhaleBanner.js +12 -8
- package/dist/cli/shared/markdown.d.ts +5 -4
- package/dist/cli/shared/markdown.js +376 -334
- package/dist/cli/shared/theme-manager.d.ts +27 -0
- package/dist/cli/shared/theme-manager.js +178 -0
- package/dist/cli/shared/theme-presets.d.ts +16 -0
- package/dist/cli/shared/theme-presets.js +265 -0
- package/dist/index.js +0 -51
- package/dist/node/adapters/imessage.d.ts +10 -0
- package/dist/node/adapters/imessage.js +45 -6
- package/dist/node/cli.js +459 -8
- package/dist/node/config.d.ts +17 -0
- package/dist/node/gateway-client.d.ts +55 -0
- package/dist/node/gateway-client.js +201 -0
- package/dist/node/portal/clipboard.d.ts +28 -0
- package/dist/node/portal/clipboard.js +183 -0
- package/dist/node/portal/discovery.d.ts +29 -0
- package/dist/node/portal/discovery.js +61 -0
- package/dist/node/portal/forward.d.ts +30 -0
- package/dist/node/portal/forward.js +90 -0
- package/dist/node/portal/index.d.ts +47 -0
- package/dist/node/portal/index.js +250 -0
- package/dist/node/portal/multiplexer.d.ts +48 -0
- package/dist/node/portal/multiplexer.js +207 -0
- package/dist/node/portal/permissions.d.ts +36 -0
- package/dist/node/portal/permissions.js +131 -0
- package/dist/node/portal/protocol.d.ts +140 -0
- package/dist/node/portal/protocol.js +193 -0
- package/dist/node/portal/screen.d.ts +18 -0
- package/dist/node/portal/screen.js +93 -0
- package/dist/node/portal/session.d.ts +68 -0
- package/dist/node/portal/session.js +127 -0
- package/dist/node/portal/shell.d.ts +26 -0
- package/dist/node/portal/shell.js +142 -0
- package/dist/node/portal/stream.d.ts +43 -0
- package/dist/node/portal/stream.js +90 -0
- package/dist/node/portal/transfer.d.ts +33 -0
- package/dist/node/portal/transfer.js +231 -0
- package/dist/node/portal/ui.d.ts +16 -0
- package/dist/node/portal/ui.js +148 -0
- package/dist/node/remote-desktop/compile-helper.d.ts +13 -0
- package/dist/node/remote-desktop/compile-helper.js +73 -0
- package/dist/node/remote-desktop/index.d.ts +67 -0
- package/dist/node/remote-desktop/index.js +220 -0
- package/dist/node/remote-desktop/protocol.d.ts +96 -0
- package/dist/node/remote-desktop/protocol.js +67 -0
- package/dist/node/runtime.d.ts +8 -1
- package/dist/node/runtime.js +117 -9
- package/dist/server/handlers/__test-utils__/test-db.d.ts +25 -0
- package/dist/server/handlers/__test-utils__/test-db.js +128 -0
- package/dist/server/handlers/api-keys.js +26 -2
- package/dist/server/handlers/browser.d.ts +0 -4
- package/dist/server/handlers/browser.js +0 -46
- package/dist/server/handlers/catalog.js +37 -14
- package/dist/server/handlers/clickhouse.d.ts +10 -0
- package/dist/server/handlers/clickhouse.js +215 -0
- package/dist/server/handlers/comms.d.ts +308 -4
- package/dist/server/handlers/comms.js +444 -11
- package/dist/server/handlers/creations.js +1 -1
- package/dist/server/handlers/crm.d.ts +54 -8
- package/dist/server/handlers/crm.js +353 -68
- package/dist/server/handlers/embeddings.js +3 -3
- package/dist/server/handlers/enrichment.js +39 -55
- package/dist/server/handlers/inventory.js +1 -1
- package/dist/server/handlers/kali.d.ts +9 -1
- package/dist/server/handlers/kali.js +50 -1
- package/dist/server/handlers/media.d.ts +8 -0
- package/dist/server/handlers/media.js +902 -0
- package/dist/server/handlers/meta-ads.js +6 -3
- package/dist/server/handlers/nodes.d.ts +2 -0
- package/dist/server/handlers/nodes.js +331 -40
- package/dist/server/handlers/operations.d.ts +4 -6
- package/dist/server/handlers/operations.js +99 -38
- package/dist/server/handlers/platform.js +224 -107
- package/dist/server/handlers/remove-bg.d.ts +6 -0
- package/dist/server/handlers/remove-bg.js +96 -0
- package/dist/server/handlers/storefront.d.ts +6 -0
- package/dist/server/handlers/storefront.js +477 -0
- package/dist/server/handlers/supply-chain.js +21 -3
- package/dist/server/handlers/workflow-steps.js +87 -31
- package/dist/server/handlers/workflows.js +4 -1
- package/dist/server/index.js +334 -88
- package/dist/server/lib/clickhouse-buffer.d.ts +48 -0
- package/dist/server/lib/clickhouse-buffer.js +175 -0
- package/dist/server/lib/clickhouse-client.d.ts +112 -0
- package/dist/server/lib/clickhouse-client.js +141 -0
- package/dist/server/lib/coa-renderer.d.ts +91 -0
- package/dist/server/lib/coa-renderer.js +411 -0
- package/dist/server/lib/compaction-service.js +46 -1
- package/dist/server/lib/pdf-renderer.d.ts +143 -0
- package/dist/server/lib/pdf-renderer.js +867 -0
- package/dist/server/lib/react-pdf-layout.d.ts +40 -0
- package/dist/server/lib/react-pdf-layout.js +437 -0
- package/dist/server/lib/server-agent-loop.d.ts +2 -0
- package/dist/server/lib/server-agent-loop.js +36 -17
- package/dist/server/lib/server-subagent.d.ts +3 -0
- package/dist/server/lib/server-subagent.js +9 -6
- package/dist/server/lib/supabase-client.js +51 -3
- package/dist/server/lib/template-resolver.js +14 -4
- package/dist/server/lib/utils.js +15 -0
- package/dist/server/local-agent-gateway.d.ts +44 -0
- package/dist/server/local-agent-gateway.js +389 -49
- package/dist/server/providers/anthropic.js +12 -2
- package/dist/server/providers/gemini.js +17 -2
- package/dist/server/proxy-handlers.js +151 -0
- package/dist/server/tool-router.d.ts +2 -2
- package/dist/server/tool-router.js +25 -35
- package/dist/shared/agent-core.d.ts +25 -2
- package/dist/shared/agent-core.js +66 -5
- package/dist/shared/api-client.js +54 -3
- package/dist/shared/sse-parser.d.ts +1 -1
- package/dist/shared/sse-parser.js +5 -2
- package/dist/shared/tool-dispatch.js +15 -1
- package/package.json +16 -10
- package/dist/server/handlers/__test-utils__/mock-supabase.d.ts +0 -11
- package/dist/server/handlers/__test-utils__/mock-supabase.js +0 -393
package/bin/swagmanager-mcp.js
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
* whale mcp list|add|remove Manage MCP servers
|
|
13
13
|
* whale doctor Run diagnostics
|
|
14
14
|
* whale config [key] [value] View/set configuration
|
|
15
|
+
* whale update Update to latest version
|
|
15
16
|
* whale help Show this help
|
|
16
17
|
* (non-TTY stdin) MCP stdio server for Claude Code / Cursor
|
|
17
18
|
*/
|
|
@@ -111,6 +112,7 @@ function showHelp() {
|
|
|
111
112
|
console.log(` whale doctor${d} Run diagnostics${r}`);
|
|
112
113
|
console.log(` whale init${d} Generate .whale/CLAUDE.md for project${r}`);
|
|
113
114
|
console.log(` whale config${d} View/set configuration${r}`);
|
|
115
|
+
console.log(` whale db${d} Local Supabase management${r}`);
|
|
114
116
|
console.log(` whale serve${d} Local agent WebSocket server${r}`);
|
|
115
117
|
console.log(` whale agent${d} Start local security agent${r}`);
|
|
116
118
|
console.log();
|
|
@@ -168,6 +170,24 @@ if (flags.version) {
|
|
|
168
170
|
process.exit(0);
|
|
169
171
|
}
|
|
170
172
|
|
|
173
|
+
// ── Background update check (non-blocking, TTY only) ──
|
|
174
|
+
const PACKAGE_NAME = "whale-code";
|
|
175
|
+
async function checkForUpdate() {
|
|
176
|
+
try {
|
|
177
|
+
const { execSync } = await import("child_process");
|
|
178
|
+
const latest = execSync(`npm view ${PACKAGE_NAME} version 2>/dev/null`, { encoding: "utf-8", timeout: 5000 }).trim();
|
|
179
|
+
if (latest && latest !== PKG_VERSION) {
|
|
180
|
+
const d = "\x1b[2m", r = "\x1b[0m", c = "\x1b[36m", B = "\x1b[1m";
|
|
181
|
+
console.log(`\n${d} Update available: ${r}${c}v${PKG_VERSION}${r}${d} → ${r}${c}${B}v${latest}${r}`);
|
|
182
|
+
console.log(`${d} Run ${r}${B}whale update${r}${d} or ${r}${B}npm i -g ${PACKAGE_NAME}@latest${r}\n`);
|
|
183
|
+
}
|
|
184
|
+
} catch { /* silent — network or npm not available */ }
|
|
185
|
+
}
|
|
186
|
+
if (process.stdin.isTTY && !flags.print && !flags.version && !flags.help) {
|
|
187
|
+
// Fire and forget — don't block startup
|
|
188
|
+
checkForUpdate();
|
|
189
|
+
}
|
|
190
|
+
|
|
171
191
|
// ── Help ──
|
|
172
192
|
if (flags.help && !subcommand) {
|
|
173
193
|
showHelp();
|
|
@@ -305,6 +325,37 @@ switch (command) {
|
|
|
305
325
|
break;
|
|
306
326
|
}
|
|
307
327
|
|
|
328
|
+
case "db": {
|
|
329
|
+
const { runDbCommand } = await import(join(distDir, "cli", "commands", "db.js"));
|
|
330
|
+
await runDbCommand(positionals.slice(1));
|
|
331
|
+
break;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
case "update":
|
|
335
|
+
case "upgrade": {
|
|
336
|
+
const { execSync } = await import("child_process");
|
|
337
|
+
const d = "\x1b[2m", r = "\x1b[0m", c = "\x1b[36m", g = "\x1b[32m", B = "\x1b[1m";
|
|
338
|
+
|
|
339
|
+
console.log(`${d} Checking for updates...${r}`);
|
|
340
|
+
try {
|
|
341
|
+
const latest = execSync(`npm view ${PACKAGE_NAME} version 2>/dev/null`, { encoding: "utf-8", timeout: 10000 }).trim();
|
|
342
|
+
if (latest === PKG_VERSION) {
|
|
343
|
+
console.log(`${g} ✓${r} whale-code v${PKG_VERSION} is already up to date`);
|
|
344
|
+
process.exit(0);
|
|
345
|
+
}
|
|
346
|
+
console.log(`${d} Updating ${r}v${PKG_VERSION}${d} → ${r}${B}v${latest}${r}`);
|
|
347
|
+
console.log();
|
|
348
|
+
execSync(`npm install -g ${PACKAGE_NAME}@latest`, { stdio: "inherit" });
|
|
349
|
+
console.log();
|
|
350
|
+
console.log(`${g} ✓${r} Updated to whale-code v${latest}`);
|
|
351
|
+
} catch (err) {
|
|
352
|
+
console.error(` ✗ Update failed: ${err.message}`);
|
|
353
|
+
console.error(` Try: sudo npm install -g ${PACKAGE_NAME}@latest`);
|
|
354
|
+
process.exit(1);
|
|
355
|
+
}
|
|
356
|
+
break;
|
|
357
|
+
}
|
|
358
|
+
|
|
308
359
|
case "agent": {
|
|
309
360
|
// Forward remaining args to local-agent CLI
|
|
310
361
|
// Rebuild process.argv so the agent sees: [node, script, subcommand, ...flags]
|
package/dist/cli/app.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { render } from "ink";
|
|
3
|
+
import chalk from "chalk";
|
|
3
4
|
import { initErrorLogger, setErrorLoggerUser, captureError } from "./services/error-logger.js";
|
|
4
5
|
import { loadConfig } from "./services/config-store.js";
|
|
5
6
|
export async function renderLogin() {
|
|
@@ -32,8 +33,35 @@ export async function renderChat(options) {
|
|
|
32
33
|
const err = reason instanceof Error ? reason : new Error(String(reason));
|
|
33
34
|
captureError({ error: err, severity: "error", tags: { source: "unhandledRejection" } });
|
|
34
35
|
});
|
|
35
|
-
|
|
36
|
-
await
|
|
36
|
+
// Auto-login if not authenticated — seamless browser OAuth
|
|
37
|
+
const { isLoggedIn } = await import("./services/auth-service.js");
|
|
38
|
+
if (!isLoggedIn()) {
|
|
39
|
+
const { signInWithBrowser } = await import("./services/browser-auth.js");
|
|
40
|
+
const brand = chalk.hex("#6366F1");
|
|
41
|
+
const dim = chalk.hex("#64748B");
|
|
42
|
+
const green = chalk.hex("#30D158");
|
|
43
|
+
console.log();
|
|
44
|
+
console.log(` ${brand.bold("◆")} Opening browser to sign in...`);
|
|
45
|
+
const result = await signInWithBrowser(undefined, {
|
|
46
|
+
onBrowserOpening: (url) => {
|
|
47
|
+
console.log(` ${dim(url)}`);
|
|
48
|
+
console.log();
|
|
49
|
+
console.log(` ${dim("Waiting for authentication...")}`);
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
if (!result.success) {
|
|
53
|
+
console.error(` ${chalk.red("✗")} ${result.error}`);
|
|
54
|
+
console.error(` ${dim("Run")} whale login ${dim("to try again.")}`);
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
console.log(` ${green("✓")} Signed in as ${result.config?.email || "unknown"}`);
|
|
58
|
+
if (result.config?.store_name) {
|
|
59
|
+
console.log(` ${dim("Store:")} ${result.config.store_name}`);
|
|
60
|
+
}
|
|
61
|
+
console.log();
|
|
62
|
+
}
|
|
63
|
+
// Clear screen — fresh start
|
|
64
|
+
process.stdout.write("\x1b[2J\x1b[3J\x1b[H");
|
|
37
65
|
// Apply options before starting chat
|
|
38
66
|
if (options?.model || options?.permissionMode) {
|
|
39
67
|
const agentLoop = await import("./services/agent-loop.js");
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ChatApp — whale code CLI
|
|
3
3
|
*
|
|
4
|
-
* Uses Ink's <Static> for completed messages — written to stdout once,
|
|
5
|
-
* never re-rendered. Only the active area (
|
|
6
|
-
* is
|
|
7
|
-
*
|
|
4
|
+
* Uses Ink's <Static> for ALL completed messages — written to stdout once,
|
|
5
|
+
* never re-rendered. Only the active area (running tools, streaming text,
|
|
6
|
+
* thinking spinner, input) is in Ink's render loop.
|
|
7
|
+
* This prevents screen overwriting and allows scrolling during generation.
|
|
8
8
|
*/
|
|
9
9
|
export declare function ChatApp(): import("react/jsx-runtime").JSX.Element;
|
package/dist/cli/chat/ChatApp.js
CHANGED
|
@@ -2,10 +2,10 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
2
2
|
/**
|
|
3
3
|
* ChatApp — whale code CLI
|
|
4
4
|
*
|
|
5
|
-
* Uses Ink's <Static> for completed messages — written to stdout once,
|
|
6
|
-
* never re-rendered. Only the active area (
|
|
7
|
-
* is
|
|
8
|
-
*
|
|
5
|
+
* Uses Ink's <Static> for ALL completed messages — written to stdout once,
|
|
6
|
+
* never re-rendered. Only the active area (running tools, streaming text,
|
|
7
|
+
* thinking spinner, input) is in Ink's render loop.
|
|
8
|
+
* This prevents screen overwriting and allows scrolling during generation.
|
|
9
9
|
*/
|
|
10
10
|
import { useState, useEffect, useRef, useMemo, useCallback } from "react";
|
|
11
11
|
import { Box, Text, Static, useApp, useInput } from "ink";
|
|
@@ -16,12 +16,23 @@ import { ToolIndicator } from "./ToolIndicator.js";
|
|
|
16
16
|
import { SubagentPanel } from "./SubagentPanel.js";
|
|
17
17
|
import { TeamPanel } from "./TeamPanel.js";
|
|
18
18
|
import { StreamingText } from "./StreamingText.js";
|
|
19
|
+
import { MarkdownText } from "./MarkdownText.js";
|
|
19
20
|
import { ChatInput } from "./ChatInput.js";
|
|
21
|
+
import { StatusBar } from "./StatusBar.js";
|
|
20
22
|
import { StoreSelector } from "./StoreSelector.js";
|
|
23
|
+
import { NodeSelector } from "./NodeSelector.js";
|
|
24
|
+
import { SessionManager } from "./SessionManager.js";
|
|
25
|
+
import { MemoryManager } from "./MemoryManager.js";
|
|
26
|
+
import { NodeManager } from "./NodeManager.js";
|
|
21
27
|
import { ModelSelector } from "./ModelSelector.js";
|
|
28
|
+
import { ThemeSelector } from "./ThemeSelector.js";
|
|
22
29
|
import { RewindViewer, RewindOutcome } from "./RewindViewer.js";
|
|
30
|
+
import { PlanApproval } from "./PlanApproval.js";
|
|
23
31
|
import { RewindManager } from "../services/rewind.js";
|
|
32
|
+
import { interactiveEvents, resolvePlanApproval } from "../services/interactive-tools.js";
|
|
24
33
|
import { colors, symbols } from "../shared/Theme.js";
|
|
34
|
+
import { initTheme, switchTheme } from "../shared/theme-manager.js";
|
|
35
|
+
import { WhaleBanner } from "../shared/WhaleBanner.js";
|
|
25
36
|
import { loadKeybindings, matchesBinding } from "../services/keybinding-manager.js";
|
|
26
37
|
import { loadConfig, updateConfig } from "../services/config-store.js";
|
|
27
38
|
import { createRequire } from "module";
|
|
@@ -30,22 +41,6 @@ import { dirname, join } from "path";
|
|
|
30
41
|
// Extracted hooks
|
|
31
42
|
import { useSlashCommands } from "./hooks/useSlashCommands.js";
|
|
32
43
|
import { useAgentLoop } from "./hooks/useAgentLoop.js";
|
|
33
|
-
// Thinking verbs — rotate randomly each render (Claude Code parity)
|
|
34
|
-
const THINKING_VERBS = [
|
|
35
|
-
"thinking…",
|
|
36
|
-
"reasoning…",
|
|
37
|
-
"considering…",
|
|
38
|
-
"analyzing…",
|
|
39
|
-
"evaluating…",
|
|
40
|
-
"pondering…",
|
|
41
|
-
"processing…",
|
|
42
|
-
"reflecting…",
|
|
43
|
-
"examining…",
|
|
44
|
-
"working…",
|
|
45
|
-
];
|
|
46
|
-
function randomVerb() {
|
|
47
|
-
return THINKING_VERBS[Math.floor(Math.random() * THINKING_VERBS.length)];
|
|
48
|
-
}
|
|
49
44
|
const PKG_NAME = "whale-code";
|
|
50
45
|
const __filename = fileURLToPath(import.meta.url);
|
|
51
46
|
const __dirname = dirname(__filename);
|
|
@@ -69,7 +64,16 @@ export function ChatApp() {
|
|
|
69
64
|
const [serverToolsAvailable, setServerToolsAvailable] = useState(0);
|
|
70
65
|
const [storeSelectMode, setStoreSelectMode] = useState(false);
|
|
71
66
|
const [storeList, setStoreList] = useState([]);
|
|
67
|
+
const [nodeSelectMode, setNodeSelectMode] = useState(false);
|
|
68
|
+
const [nodeList, setNodeList] = useState([]);
|
|
69
|
+
const [sessionManagerMode, setSessionManagerMode] = useState(false);
|
|
70
|
+
const [sessionList, setSessionList] = useState([]);
|
|
71
|
+
const [memoryManagerMode, setMemoryManagerMode] = useState(false);
|
|
72
|
+
const [memoryList, setMemoryList] = useState([]);
|
|
73
|
+
const [nodeManagerMode, setNodeManagerMode] = useState(false);
|
|
74
|
+
const [nodeManagerStatus, setNodeManagerStatus] = useState({ registered: false, running: false });
|
|
72
75
|
const [modelSelectMode, setModelSelectMode] = useState(false);
|
|
76
|
+
const [themeSelectMode, setThemeSelectMode] = useState(false);
|
|
73
77
|
const [currentModel, setCurrentModel] = useState(getModelShortName());
|
|
74
78
|
const [sessionId, setSessionId] = useState(null);
|
|
75
79
|
const [thinkingEnabled, setThinkingEnabled] = useState(() => {
|
|
@@ -80,34 +84,57 @@ export function ChatApp() {
|
|
|
80
84
|
return true;
|
|
81
85
|
}
|
|
82
86
|
});
|
|
83
|
-
// Stream activity indicator — show spinner when no new content for 800ms+
|
|
84
|
-
const [streamStale, setStreamStale] = useState(false);
|
|
85
87
|
const lastContentUpdateRef = useRef(Date.now());
|
|
86
|
-
//
|
|
88
|
+
// Unified streaming activity — verb cycling + elapsed timer (replaces separate thinking timer + StatusBar verb)
|
|
89
|
+
const FUN_VERBS = useMemo(() => [
|
|
90
|
+
"thinking", "pondering", "brewing", "cooking", "rowing", "juggling",
|
|
91
|
+
"weaving", "crafting", "forging", "sketching", "conjuring", "assembling",
|
|
92
|
+
"sculpting", "composing", "mixing", "polishing", "building", "wiring",
|
|
93
|
+
"spinning", "mapping", "plotting", "dreaming", "hurdling", "launching",
|
|
94
|
+
"surfing", "flying", "grabbing", "lifting", "twisting", "rolling",
|
|
95
|
+
], []);
|
|
96
|
+
const [streamElapsed, setStreamElapsed] = useState(0);
|
|
97
|
+
const [verbIndex, setVerbIndex] = useState(0);
|
|
98
|
+
const streamStartRef = useRef(null);
|
|
87
99
|
useEffect(() => {
|
|
88
|
-
if (
|
|
89
|
-
|
|
90
|
-
|
|
100
|
+
if (isStreaming) {
|
|
101
|
+
if (!streamStartRef.current)
|
|
102
|
+
streamStartRef.current = Date.now();
|
|
103
|
+
setStreamElapsed(0);
|
|
104
|
+
setVerbIndex(0);
|
|
105
|
+
const elapsedInterval = setInterval(() => {
|
|
106
|
+
if (streamStartRef.current) {
|
|
107
|
+
setStreamElapsed(Math.floor((Date.now() - streamStartRef.current) / 1000));
|
|
108
|
+
}
|
|
109
|
+
}, 1000);
|
|
110
|
+
const verbInterval = setInterval(() => {
|
|
111
|
+
setVerbIndex(i => (i + 1) % FUN_VERBS.length);
|
|
112
|
+
}, 4000);
|
|
113
|
+
return () => { clearInterval(elapsedInterval); clearInterval(verbInterval); };
|
|
91
114
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
}, [isStreaming]);
|
|
115
|
+
else {
|
|
116
|
+
streamStartRef.current = null;
|
|
117
|
+
setStreamElapsed(0);
|
|
118
|
+
setVerbIndex(0);
|
|
119
|
+
}
|
|
120
|
+
}, [isStreaming, FUN_VERBS]);
|
|
98
121
|
// Rewind state
|
|
99
122
|
const [showRewind, setShowRewind] = useState(false);
|
|
123
|
+
// Plan approval state
|
|
124
|
+
const [planApproval, setPlanApproval] = useState(null);
|
|
100
125
|
const rewindManagerRef = useRef(new RewindManager());
|
|
101
126
|
const turnIndexRef = useRef(0);
|
|
102
127
|
// Refs
|
|
103
128
|
const conversationRef = useRef([]);
|
|
104
129
|
const abortRef = useRef(null);
|
|
105
130
|
const accTextRef = useRef("");
|
|
106
|
-
const
|
|
131
|
+
const thinkingChunksRef = useRef(0);
|
|
132
|
+
const thinkingDoneRef = useRef(false);
|
|
107
133
|
// No timer cleanup needed — unified flush timer is managed inside useAgentLoop,
|
|
108
134
|
// stale polling interval is cleaned up by its own effect.
|
|
109
135
|
// ── Init ──
|
|
110
136
|
useEffect(() => {
|
|
137
|
+
initTheme();
|
|
111
138
|
const check = canUseAgent();
|
|
112
139
|
if (!check.ready) {
|
|
113
140
|
setError(check.reason || "Run 'whale login' to authenticate.");
|
|
@@ -120,8 +147,20 @@ export function ChatApp() {
|
|
|
120
147
|
getServerToolCount().then((count) => setServerToolsAvailable(count));
|
|
121
148
|
mcpClientManager.connectAll().catch(() => { });
|
|
122
149
|
}
|
|
150
|
+
// Listen for plan approval requests — push plan content to Static for natural scrolling
|
|
151
|
+
const handlePlanExited = (data) => {
|
|
152
|
+
// Push plan content into messages so it renders in the <Static> area
|
|
153
|
+
const content = data.planContent || "(empty plan)";
|
|
154
|
+
setMessages(prev => [...prev, {
|
|
155
|
+
role: "assistant",
|
|
156
|
+
text: ` ${symbols.arrowRight} Plan Review — ${data.planFile}\n\n${content}`,
|
|
157
|
+
}]);
|
|
158
|
+
setPlanApproval({ planContent: data.planContent || "", planFile: data.planFile });
|
|
159
|
+
};
|
|
160
|
+
interactiveEvents.on("planModeExited", handlePlanExited);
|
|
123
161
|
return () => {
|
|
124
162
|
mcpClientManager.disconnectAll().catch(() => { });
|
|
163
|
+
interactiveEvents.off("planModeExited", handlePlanExited);
|
|
125
164
|
};
|
|
126
165
|
}, []);
|
|
127
166
|
// ── Keys (configurable via ~/.swagmanager/keybindings.json) ──
|
|
@@ -153,24 +192,36 @@ export function ChatApp() {
|
|
|
153
192
|
});
|
|
154
193
|
}
|
|
155
194
|
});
|
|
195
|
+
// ESC abort is now handled by ChatInput's onInterrupt prop — stdin stays active
|
|
196
|
+
// even when disabled, so ESC always reaches the handler.
|
|
156
197
|
// ── Extracted hooks ──
|
|
157
|
-
const { handleCommand, handleStoreSelect, handleStoreCancel } = useSlashCommands({
|
|
198
|
+
const { handleCommand, handleStoreSelect, handleStoreCancel, handleNodeSelect, handleNodeCancel, handleSessionSelect, handleMemoryDelete, handleNodeToggle, handleNodeRegister, handleNodeClose, } = useSlashCommands({
|
|
158
199
|
exit, toolsExpanded, serverToolsAvailable, sessionId, thinkingEnabled,
|
|
159
200
|
conversationRef,
|
|
160
201
|
setMessages, setStreamingText, setActiveTools, setTeamState,
|
|
161
202
|
setStoreList, setStoreSelectMode, setModelSelectMode, setCurrentModel,
|
|
162
203
|
setSessionId, setThinkingEnabled, setUserLabel, setServerToolsAvailable,
|
|
163
204
|
setShowRewind,
|
|
205
|
+
setNodeSelectMode, setNodeList,
|
|
206
|
+
setSessionManagerMode, setSessionList,
|
|
207
|
+
setMemoryManagerMode, setMemoryList,
|
|
208
|
+
setThemeSelectMode,
|
|
209
|
+
setNodeManagerMode, setNodeManagerStatus,
|
|
164
210
|
rewindCheckpointCount: rewindManagerRef.current.getCheckpointCount(),
|
|
165
211
|
PKG_NAME, PKG_VERSION,
|
|
166
212
|
});
|
|
167
213
|
const { handleSend } = useAgentLoop({
|
|
168
214
|
isStreaming, thinkingEnabled, conversationRef, abortRef,
|
|
169
|
-
accTextRef, lastContentUpdateRef,
|
|
215
|
+
accTextRef, lastContentUpdateRef, thinkingChunksRef, thinkingDoneRef,
|
|
170
216
|
rewindManagerRef, turnIndexRef,
|
|
171
217
|
setMessages, setStreamingText, setIsStreaming, setActiveTools,
|
|
172
218
|
setSubagentActivity, setCompletedSubagents, setTeamState,
|
|
173
219
|
});
|
|
220
|
+
// ── Plan approval handler ──
|
|
221
|
+
const handlePlanDecision = useCallback((decision) => {
|
|
222
|
+
setPlanApproval(null);
|
|
223
|
+
resolvePlanApproval(decision);
|
|
224
|
+
}, []);
|
|
174
225
|
// ── Rewind handler ──
|
|
175
226
|
const handleRewind = useCallback((checkpointIndex, outcome) => {
|
|
176
227
|
const rm = rewindManagerRef.current;
|
|
@@ -235,27 +286,39 @@ export function ChatApp() {
|
|
|
235
286
|
// ── Render ──
|
|
236
287
|
const termWidth = process.stdout.columns || 80;
|
|
237
288
|
const contentWidth = Math.max(20, termWidth - 2);
|
|
238
|
-
|
|
289
|
+
// ALL completed messages go to Static — written to stdout once, never re-rendered.
|
|
290
|
+
// This keeps the dynamic area small (only active tools + streaming + input),
|
|
291
|
+
// preventing the constant screen overwriting that blocks scrolling.
|
|
292
|
+
const staticItems = useMemo(() => {
|
|
239
293
|
const items = [{ id: "header", type: "header" }];
|
|
240
|
-
|
|
241
|
-
for (let i = 0; i < cutoff; i++) {
|
|
294
|
+
for (let i = 0; i < messages.length; i++) {
|
|
242
295
|
items.push({ id: `msg-${i}`, type: "message", msg: messages[i], index: i });
|
|
243
296
|
}
|
|
244
|
-
|
|
245
|
-
return { staticItems: items, dynamicMessages: tail };
|
|
297
|
+
return items;
|
|
246
298
|
}, [messages]);
|
|
247
299
|
if (error) {
|
|
248
|
-
return (_jsxs(Box, { flexDirection: "column", paddingLeft: 1, paddingRight: 1, children: [_jsx(
|
|
300
|
+
return (_jsxs(Box, { flexDirection: "column", paddingLeft: 1, paddingRight: 1, children: [_jsx(WhaleBanner, {}), _jsx(Text, { children: " " }), _jsxs(Text, { color: colors.error, children: [" ", error] })] }));
|
|
249
301
|
}
|
|
250
302
|
if (!ready) {
|
|
251
303
|
return (_jsx(Box, { flexDirection: "column", paddingLeft: 1, paddingRight: 1, children: _jsx(Text, { color: colors.tertiary, children: "loading..." }) }));
|
|
252
304
|
}
|
|
305
|
+
// Plan approval no longer full-screen — plan content is pushed to Static (scrollable),
|
|
306
|
+
// PlanApproval renders as an overlay above the persistent input.
|
|
253
307
|
return (_jsxs(Box, { flexDirection: "column", paddingLeft: 1, paddingRight: 1, children: [_jsx(Static, { items: staticItems, children: (item) => {
|
|
254
308
|
if (item.type === "header") {
|
|
255
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { children: " " }), _jsxs(Text, { children: [
|
|
309
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(WhaleBanner, {}), _jsx(Text, { children: " " }), _jsxs(Text, { children: [" ", userLabel ? _jsxs(Text, { color: colors.dim, children: [userLabel, " "] }) : null, _jsx(Text, { color: colors.dim, children: currentModel }), thinkingEnabled ? _jsx(Text, { color: colors.warning, children: " thinking" }) : null, getPermissionMode() !== "default" && (_jsxs(Text, { color: getPermissionMode() === "yolo" ? colors.error : colors.info, children: [" ", getPermissionMode()] })), serverToolsAvailable > 0 ? (_jsxs(Text, { color: colors.tertiary, children: [" ", symbols.dot, " ", serverToolsAvailable, " tools"] })) : null] }), _jsx(Text, { children: " " })] }, item.id));
|
|
256
310
|
}
|
|
257
311
|
return _jsx(CompletedMessage, { msg: item.msg, index: item.index, toolsExpanded: toolsExpanded }, item.id);
|
|
258
|
-
} }),
|
|
312
|
+
} }), teamState ? (_jsx(TeamPanel, { team: teamState })) : (_jsxs(_Fragment, { children: [isStreaming && (_jsxs(Box, { marginLeft: 2, marginTop: 1, marginBottom: 1, children: [_jsx(Text, { color: colors.brand, children: _jsx(Spinner, { type: "dots" }) }), _jsxs(Text, { color: colors.dim, children: [" ", FUN_VERBS[verbIndex]] }), streamElapsed > 0 && (_jsxs(Text, { color: colors.quaternary, children: [" ", streamElapsed < 60 ? `${streamElapsed}s` : `${Math.floor(streamElapsed / 60)}:${(streamElapsed % 60).toString().padStart(2, "0")}`] }))] })), activeTools.length > 0 && (_jsx(Box, { flexDirection: "column", marginLeft: 2, children: activeTools.map((tc, i) => (_jsxs(Box, { flexDirection: "column", children: [_jsx(ToolIndicator, { id: `live-${tc.name}-${i}`, name: tc.name, status: tc.status, result: tc.result, input: tc.input, expanded: toolsExpanded, progress: tc.progress }), tc.name === "task" && tc.status === "running" && (subagentActivity.size > 0 || completedSubagents.length > 0) && (_jsx(SubagentPanel, { running: subagentActivity, completed: completedSubagents }))] }, `live-${tc.name}-${i}`))) })), toolsExpanded && !isStreaming && (() => {
|
|
313
|
+
// Find collapsed tool results from the last assistant turn
|
|
314
|
+
const lastAssistant = [...messages].reverse().find(m => m.role === "assistant" && m.toolCalls && m.toolCalls.length > 0);
|
|
315
|
+
if (!lastAssistant?.toolCalls)
|
|
316
|
+
return null;
|
|
317
|
+
const collapsed = lastAssistant.toolCalls.filter(tc => tc.result && tc.status === "success" && tc.result.split("\n").length > 12);
|
|
318
|
+
if (collapsed.length === 0)
|
|
319
|
+
return null;
|
|
320
|
+
return (_jsxs(Box, { flexDirection: "column", marginLeft: 2, marginBottom: 1, children: [_jsxs(Text, { color: colors.tertiary, children: ["─".repeat(Math.min(60, (process.stdout.columns || 80) - 6)), " ^O close"] }), collapsed.map((tc, i) => (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsx(Text, { color: colors.info, bold: true, children: tc.name }), _jsx(MarkdownText, { text: tc.result })] }, i))), _jsx(Text, { color: colors.tertiary, children: "─".repeat(Math.min(60, (process.stdout.columns || 80) - 6)) })] }));
|
|
321
|
+
})(), streamingText && (_jsx(Box, { marginLeft: 2, flexDirection: "column", children: _jsx(StreamingText, { text: streamingText }) }))] })), showRewind && (_jsx(RewindViewer, { checkpoints: rewindManagerRef.current.getCheckpoints(), onRewind: handleRewind, onCancel: () => setShowRewind(false) })), sessionManagerMode && (_jsx(SessionManager, { sessions: sessionList, onSelect: handleSessionSelect, onCancel: () => setSessionManagerMode(false) })), memoryManagerMode && (_jsx(MemoryManager, { memories: memoryList, onDelete: handleMemoryDelete, onCancel: () => setMemoryManagerMode(false) })), nodeManagerMode && (_jsx(NodeManager, { initialStatus: nodeManagerStatus, onToggle: handleNodeToggle, onRegister: handleNodeRegister, onClose: handleNodeClose })), storeSelectMode && (_jsx(StoreSelector, { stores: storeList, currentStoreId: loadConfig().store_id || "", onSelect: handleStoreSelect, onCancel: handleStoreCancel })), nodeSelectMode && (_jsx(NodeSelector, { nodes: nodeList, onSelect: handleNodeSelect, onCancel: handleNodeCancel })), modelSelectMode && (_jsx(ModelSelector, { currentModel: currentModel, onSelect: (model) => {
|
|
259
322
|
setModelSelectMode(false);
|
|
260
323
|
setModel(model.value);
|
|
261
324
|
setCurrentModel(model.value);
|
|
@@ -263,5 +326,12 @@ export function ChatApp() {
|
|
|
263
326
|
role: "assistant",
|
|
264
327
|
text: ` ${symbols.check} Model: ${model.label} (${model.modelId})`,
|
|
265
328
|
}]);
|
|
266
|
-
}, onCancel: () => setModelSelectMode(false) }))
|
|
329
|
+
}, onCancel: () => setModelSelectMode(false) })), themeSelectMode && (_jsx(ThemeSelector, { onSelect: (preset) => {
|
|
330
|
+
setThemeSelectMode(false);
|
|
331
|
+
switchTheme(preset.id);
|
|
332
|
+
setMessages((prev) => [...prev, {
|
|
333
|
+
role: "assistant",
|
|
334
|
+
text: ` ${symbols.check} Theme: ${preset.label}`,
|
|
335
|
+
}]);
|
|
336
|
+
}, onCancel: () => setThemeSelectMode(false) })), planApproval && (_jsx(PlanApproval, { planFile: planApproval.planFile, onDecision: handlePlanDecision })), _jsx(ChatInput, { onSubmit: handleSend, onCommand: handleCommand, disabled: isStreaming || !!planApproval, agentName: "whale code", onInterrupt: () => abortRef.current?.abort() }), _jsx(StatusBar, { permissionMode: getPermissionMode(), model: currentModel, thinkingEnabled: thinkingEnabled, serverTools: serverToolsAvailable, isStreaming: isStreaming, toolsExpanded: toolsExpanded })] }));
|
|
267
337
|
}
|
|
@@ -24,16 +24,23 @@ export interface FileAttachment {
|
|
|
24
24
|
path: string;
|
|
25
25
|
name: string;
|
|
26
26
|
}
|
|
27
|
-
export interface
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
export interface ContextAttachment {
|
|
28
|
+
id: string;
|
|
29
|
+
type: string;
|
|
30
|
+
title: string;
|
|
31
|
+
subtitle?: string;
|
|
32
|
+
icon: string;
|
|
33
|
+
sourceApp: string;
|
|
34
|
+
data: Record<string, unknown>;
|
|
35
|
+
timestamp: string;
|
|
30
36
|
}
|
|
31
|
-
export
|
|
37
|
+
export type { SlashCommand } from "./SlashMenu.js";
|
|
38
|
+
export declare const SLASH_COMMANDS: import("./SlashMenu.js").SlashCommand[];
|
|
32
39
|
interface ChatInputProps {
|
|
33
40
|
onSubmit: (message: string, images?: ImageAttachment[]) => void;
|
|
34
41
|
onCommand: (command: string) => void;
|
|
35
42
|
disabled: boolean;
|
|
36
43
|
agentName?: string;
|
|
44
|
+
onInterrupt?: () => void;
|
|
37
45
|
}
|
|
38
|
-
export declare function ChatInput({ onSubmit, onCommand, disabled, agentName }: ChatInputProps): import("react/jsx-runtime").JSX.Element;
|
|
39
|
-
export {};
|
|
46
|
+
export declare function ChatInput({ onSubmit, onCommand, disabled, agentName, onInterrupt }: ChatInputProps): import("react/jsx-runtime").JSX.Element;
|