whale-code 6.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +95 -0
- package/bin/swag-agent.js +9 -0
- package/bin/swagmanager-mcp.js +321 -0
- package/dist/cli/app.d.ts +26 -0
- package/dist/cli/app.js +64 -0
- package/dist/cli/chat/AgentSelector.d.ts +14 -0
- package/dist/cli/chat/AgentSelector.js +14 -0
- package/dist/cli/chat/ChatApp.d.ts +9 -0
- package/dist/cli/chat/ChatApp.js +267 -0
- package/dist/cli/chat/ChatInput.d.ts +39 -0
- package/dist/cli/chat/ChatInput.js +509 -0
- package/dist/cli/chat/MarkdownText.d.ts +10 -0
- package/dist/cli/chat/MarkdownText.js +20 -0
- package/dist/cli/chat/MessageList.d.ts +37 -0
- package/dist/cli/chat/MessageList.js +80 -0
- package/dist/cli/chat/ModelSelector.d.ts +20 -0
- package/dist/cli/chat/ModelSelector.js +73 -0
- package/dist/cli/chat/RewindViewer.d.ts +26 -0
- package/dist/cli/chat/RewindViewer.js +185 -0
- package/dist/cli/chat/StoreSelector.d.ts +14 -0
- package/dist/cli/chat/StoreSelector.js +24 -0
- package/dist/cli/chat/StreamingText.d.ts +12 -0
- package/dist/cli/chat/StreamingText.js +12 -0
- package/dist/cli/chat/SubagentPanel.d.ts +45 -0
- package/dist/cli/chat/SubagentPanel.js +110 -0
- package/dist/cli/chat/TeamPanel.d.ts +21 -0
- package/dist/cli/chat/TeamPanel.js +42 -0
- package/dist/cli/chat/ToolIndicator.d.ts +25 -0
- package/dist/cli/chat/ToolIndicator.js +436 -0
- package/dist/cli/chat/hooks/useAgentLoop.d.ts +39 -0
- package/dist/cli/chat/hooks/useAgentLoop.js +382 -0
- package/dist/cli/chat/hooks/useSlashCommands.d.ts +37 -0
- package/dist/cli/chat/hooks/useSlashCommands.js +387 -0
- package/dist/cli/commands/config-cmd.d.ts +10 -0
- package/dist/cli/commands/config-cmd.js +99 -0
- package/dist/cli/commands/doctor.d.ts +14 -0
- package/dist/cli/commands/doctor.js +172 -0
- package/dist/cli/commands/init.d.ts +16 -0
- package/dist/cli/commands/init.js +278 -0
- package/dist/cli/commands/mcp.d.ts +12 -0
- package/dist/cli/commands/mcp.js +162 -0
- package/dist/cli/login/LoginApp.d.ts +7 -0
- package/dist/cli/login/LoginApp.js +157 -0
- package/dist/cli/print-mode.d.ts +31 -0
- package/dist/cli/print-mode.js +202 -0
- package/dist/cli/serve-mode.d.ts +37 -0
- package/dist/cli/serve-mode.js +636 -0
- package/dist/cli/services/agent-definitions.d.ts +25 -0
- package/dist/cli/services/agent-definitions.js +91 -0
- package/dist/cli/services/agent-events.d.ts +178 -0
- package/dist/cli/services/agent-events.js +175 -0
- package/dist/cli/services/agent-loop.d.ts +90 -0
- package/dist/cli/services/agent-loop.js +762 -0
- package/dist/cli/services/agent-worker-base.d.ts +97 -0
- package/dist/cli/services/agent-worker-base.js +220 -0
- package/dist/cli/services/auth-service.d.ts +30 -0
- package/dist/cli/services/auth-service.js +160 -0
- package/dist/cli/services/background-processes.d.ts +126 -0
- package/dist/cli/services/background-processes.js +318 -0
- package/dist/cli/services/browser-auth.d.ts +24 -0
- package/dist/cli/services/browser-auth.js +180 -0
- package/dist/cli/services/claude-md-loader.d.ts +16 -0
- package/dist/cli/services/claude-md-loader.js +58 -0
- package/dist/cli/services/config-store.d.ts +47 -0
- package/dist/cli/services/config-store.js +79 -0
- package/dist/cli/services/debug-log.d.ts +10 -0
- package/dist/cli/services/debug-log.js +52 -0
- package/dist/cli/services/error-logger.d.ts +58 -0
- package/dist/cli/services/error-logger.js +269 -0
- package/dist/cli/services/file-history.d.ts +21 -0
- package/dist/cli/services/file-history.js +83 -0
- package/dist/cli/services/format-server-response.d.ts +16 -0
- package/dist/cli/services/format-server-response.js +440 -0
- package/dist/cli/services/git-context.d.ts +11 -0
- package/dist/cli/services/git-context.js +66 -0
- package/dist/cli/services/hooks.d.ts +85 -0
- package/dist/cli/services/hooks.js +258 -0
- package/dist/cli/services/interactive-tools.d.ts +125 -0
- package/dist/cli/services/interactive-tools.js +260 -0
- package/dist/cli/services/keybinding-manager.d.ts +52 -0
- package/dist/cli/services/keybinding-manager.js +115 -0
- package/dist/cli/services/local-tools.d.ts +22 -0
- package/dist/cli/services/local-tools.js +697 -0
- package/dist/cli/services/lsp-manager.d.ts +18 -0
- package/dist/cli/services/lsp-manager.js +717 -0
- package/dist/cli/services/mcp-client.d.ts +48 -0
- package/dist/cli/services/mcp-client.js +157 -0
- package/dist/cli/services/memory-manager.d.ts +16 -0
- package/dist/cli/services/memory-manager.js +57 -0
- package/dist/cli/services/model-manager.d.ts +18 -0
- package/dist/cli/services/model-manager.js +71 -0
- package/dist/cli/services/model-router.d.ts +26 -0
- package/dist/cli/services/model-router.js +149 -0
- package/dist/cli/services/permission-modes.d.ts +13 -0
- package/dist/cli/services/permission-modes.js +43 -0
- package/dist/cli/services/rewind.d.ts +84 -0
- package/dist/cli/services/rewind.js +194 -0
- package/dist/cli/services/ripgrep.d.ts +28 -0
- package/dist/cli/services/ripgrep.js +138 -0
- package/dist/cli/services/sandbox.d.ts +29 -0
- package/dist/cli/services/sandbox.js +97 -0
- package/dist/cli/services/server-tools.d.ts +61 -0
- package/dist/cli/services/server-tools.js +543 -0
- package/dist/cli/services/session-persistence.d.ts +23 -0
- package/dist/cli/services/session-persistence.js +99 -0
- package/dist/cli/services/subagent-worker.d.ts +19 -0
- package/dist/cli/services/subagent-worker.js +41 -0
- package/dist/cli/services/subagent.d.ts +47 -0
- package/dist/cli/services/subagent.js +647 -0
- package/dist/cli/services/system-prompt.d.ts +7 -0
- package/dist/cli/services/system-prompt.js +198 -0
- package/dist/cli/services/team-lead.d.ts +73 -0
- package/dist/cli/services/team-lead.js +512 -0
- package/dist/cli/services/team-state.d.ts +77 -0
- package/dist/cli/services/team-state.js +398 -0
- package/dist/cli/services/teammate.d.ts +31 -0
- package/dist/cli/services/teammate.js +689 -0
- package/dist/cli/services/telemetry.d.ts +61 -0
- package/dist/cli/services/telemetry.js +209 -0
- package/dist/cli/services/tools/agent-tools.d.ts +14 -0
- package/dist/cli/services/tools/agent-tools.js +347 -0
- package/dist/cli/services/tools/file-ops.d.ts +15 -0
- package/dist/cli/services/tools/file-ops.js +487 -0
- package/dist/cli/services/tools/search-tools.d.ts +8 -0
- package/dist/cli/services/tools/search-tools.js +186 -0
- package/dist/cli/services/tools/shell-exec.d.ts +10 -0
- package/dist/cli/services/tools/shell-exec.js +168 -0
- package/dist/cli/services/tools/task-manager.d.ts +28 -0
- package/dist/cli/services/tools/task-manager.js +209 -0
- package/dist/cli/services/tools/web-tools.d.ts +11 -0
- package/dist/cli/services/tools/web-tools.js +395 -0
- package/dist/cli/setup/SetupApp.d.ts +9 -0
- package/dist/cli/setup/SetupApp.js +191 -0
- package/dist/cli/shared/MatrixIntro.d.ts +4 -0
- package/dist/cli/shared/MatrixIntro.js +83 -0
- package/dist/cli/shared/Theme.d.ts +74 -0
- package/dist/cli/shared/Theme.js +127 -0
- package/dist/cli/shared/WhaleBanner.d.ts +10 -0
- package/dist/cli/shared/WhaleBanner.js +12 -0
- package/dist/cli/shared/markdown.d.ts +21 -0
- package/dist/cli/shared/markdown.js +756 -0
- package/dist/cli/status/StatusApp.d.ts +4 -0
- package/dist/cli/status/StatusApp.js +105 -0
- package/dist/cli/stores/StoreApp.d.ts +7 -0
- package/dist/cli/stores/StoreApp.js +81 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +538 -0
- package/dist/local-agent/connection.d.ts +48 -0
- package/dist/local-agent/connection.js +332 -0
- package/dist/local-agent/discovery.d.ts +18 -0
- package/dist/local-agent/discovery.js +146 -0
- package/dist/local-agent/executor.d.ts +34 -0
- package/dist/local-agent/executor.js +241 -0
- package/dist/local-agent/index.d.ts +14 -0
- package/dist/local-agent/index.js +198 -0
- package/dist/node/adapters/base.d.ts +35 -0
- package/dist/node/adapters/base.js +10 -0
- package/dist/node/adapters/discord.d.ts +29 -0
- package/dist/node/adapters/discord.js +299 -0
- package/dist/node/adapters/email.d.ts +23 -0
- package/dist/node/adapters/email.js +218 -0
- package/dist/node/adapters/imessage.d.ts +17 -0
- package/dist/node/adapters/imessage.js +118 -0
- package/dist/node/adapters/slack.d.ts +26 -0
- package/dist/node/adapters/slack.js +259 -0
- package/dist/node/adapters/sms.d.ts +23 -0
- package/dist/node/adapters/sms.js +161 -0
- package/dist/node/adapters/telegram.d.ts +17 -0
- package/dist/node/adapters/telegram.js +101 -0
- package/dist/node/adapters/webchat.d.ts +27 -0
- package/dist/node/adapters/webchat.js +160 -0
- package/dist/node/adapters/whatsapp.d.ts +28 -0
- package/dist/node/adapters/whatsapp.js +230 -0
- package/dist/node/cli.d.ts +2 -0
- package/dist/node/cli.js +325 -0
- package/dist/node/config.d.ts +17 -0
- package/dist/node/config.js +31 -0
- package/dist/node/runtime.d.ts +50 -0
- package/dist/node/runtime.js +351 -0
- package/dist/server/handlers/__test-utils__/mock-supabase.d.ts +11 -0
- package/dist/server/handlers/__test-utils__/mock-supabase.js +393 -0
- package/dist/server/handlers/analytics.d.ts +17 -0
- package/dist/server/handlers/analytics.js +266 -0
- package/dist/server/handlers/api-keys.d.ts +6 -0
- package/dist/server/handlers/api-keys.js +221 -0
- package/dist/server/handlers/billing.d.ts +33 -0
- package/dist/server/handlers/billing.js +272 -0
- package/dist/server/handlers/browser.d.ts +10 -0
- package/dist/server/handlers/browser.js +517 -0
- package/dist/server/handlers/catalog.d.ts +99 -0
- package/dist/server/handlers/catalog.js +976 -0
- package/dist/server/handlers/comms.d.ts +254 -0
- package/dist/server/handlers/comms.js +588 -0
- package/dist/server/handlers/creations.d.ts +6 -0
- package/dist/server/handlers/creations.js +479 -0
- package/dist/server/handlers/crm.d.ts +89 -0
- package/dist/server/handlers/crm.js +538 -0
- package/dist/server/handlers/discovery.d.ts +6 -0
- package/dist/server/handlers/discovery.js +288 -0
- package/dist/server/handlers/embeddings.d.ts +92 -0
- package/dist/server/handlers/embeddings.js +197 -0
- package/dist/server/handlers/enrichment.d.ts +8 -0
- package/dist/server/handlers/enrichment.js +768 -0
- package/dist/server/handlers/image-gen.d.ts +6 -0
- package/dist/server/handlers/image-gen.js +409 -0
- package/dist/server/handlers/inventory.d.ts +319 -0
- package/dist/server/handlers/inventory.js +447 -0
- package/dist/server/handlers/kali.d.ts +10 -0
- package/dist/server/handlers/kali.js +210 -0
- package/dist/server/handlers/llm-providers.d.ts +6 -0
- package/dist/server/handlers/llm-providers.js +673 -0
- package/dist/server/handlers/local-agent.d.ts +6 -0
- package/dist/server/handlers/local-agent.js +118 -0
- package/dist/server/handlers/meta-ads.d.ts +111 -0
- package/dist/server/handlers/meta-ads.js +2279 -0
- package/dist/server/handlers/nodes.d.ts +33 -0
- package/dist/server/handlers/nodes.js +699 -0
- package/dist/server/handlers/operations.d.ts +138 -0
- package/dist/server/handlers/operations.js +131 -0
- package/dist/server/handlers/platform.d.ts +23 -0
- package/dist/server/handlers/platform.js +227 -0
- package/dist/server/handlers/supply-chain.d.ts +19 -0
- package/dist/server/handlers/supply-chain.js +327 -0
- package/dist/server/handlers/transcription.d.ts +17 -0
- package/dist/server/handlers/transcription.js +121 -0
- package/dist/server/handlers/video-gen.d.ts +6 -0
- package/dist/server/handlers/video-gen.js +466 -0
- package/dist/server/handlers/voice.d.ts +8 -0
- package/dist/server/handlers/voice.js +1146 -0
- package/dist/server/handlers/workflow-steps.d.ts +86 -0
- package/dist/server/handlers/workflow-steps.js +2349 -0
- package/dist/server/handlers/workflows.d.ts +7 -0
- package/dist/server/handlers/workflows.js +989 -0
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.js +2427 -0
- package/dist/server/lib/batch-client.d.ts +80 -0
- package/dist/server/lib/batch-client.js +467 -0
- package/dist/server/lib/code-worker-pool.d.ts +31 -0
- package/dist/server/lib/code-worker-pool.js +224 -0
- package/dist/server/lib/code-worker.d.ts +1 -0
- package/dist/server/lib/code-worker.js +188 -0
- package/dist/server/lib/compaction-service.d.ts +32 -0
- package/dist/server/lib/compaction-service.js +162 -0
- package/dist/server/lib/logger.d.ts +19 -0
- package/dist/server/lib/logger.js +46 -0
- package/dist/server/lib/otel.d.ts +38 -0
- package/dist/server/lib/otel.js +126 -0
- package/dist/server/lib/pg-rate-limiter.d.ts +21 -0
- package/dist/server/lib/pg-rate-limiter.js +86 -0
- package/dist/server/lib/prompt-sanitizer.d.ts +37 -0
- package/dist/server/lib/prompt-sanitizer.js +177 -0
- package/dist/server/lib/provider-capabilities.d.ts +85 -0
- package/dist/server/lib/provider-capabilities.js +190 -0
- package/dist/server/lib/provider-failover.d.ts +74 -0
- package/dist/server/lib/provider-failover.js +210 -0
- package/dist/server/lib/rate-limiter.d.ts +39 -0
- package/dist/server/lib/rate-limiter.js +147 -0
- package/dist/server/lib/server-agent-loop.d.ts +107 -0
- package/dist/server/lib/server-agent-loop.js +667 -0
- package/dist/server/lib/server-subagent.d.ts +78 -0
- package/dist/server/lib/server-subagent.js +203 -0
- package/dist/server/lib/session-checkpoint.d.ts +51 -0
- package/dist/server/lib/session-checkpoint.js +145 -0
- package/dist/server/lib/ssrf-guard.d.ts +13 -0
- package/dist/server/lib/ssrf-guard.js +240 -0
- package/dist/server/lib/supabase-client.d.ts +7 -0
- package/dist/server/lib/supabase-client.js +78 -0
- package/dist/server/lib/template-resolver.d.ts +31 -0
- package/dist/server/lib/template-resolver.js +215 -0
- package/dist/server/lib/utils.d.ts +16 -0
- package/dist/server/lib/utils.js +147 -0
- package/dist/server/local-agent-gateway.d.ts +82 -0
- package/dist/server/local-agent-gateway.js +426 -0
- package/dist/server/providers/anthropic.d.ts +20 -0
- package/dist/server/providers/anthropic.js +199 -0
- package/dist/server/providers/bedrock.d.ts +20 -0
- package/dist/server/providers/bedrock.js +194 -0
- package/dist/server/providers/gemini.d.ts +24 -0
- package/dist/server/providers/gemini.js +486 -0
- package/dist/server/providers/openai.d.ts +24 -0
- package/dist/server/providers/openai.js +522 -0
- package/dist/server/providers/registry.d.ts +32 -0
- package/dist/server/providers/registry.js +58 -0
- package/dist/server/providers/shared.d.ts +32 -0
- package/dist/server/providers/shared.js +124 -0
- package/dist/server/providers/types.d.ts +92 -0
- package/dist/server/providers/types.js +12 -0
- package/dist/server/proxy-handlers.d.ts +6 -0
- package/dist/server/proxy-handlers.js +89 -0
- package/dist/server/tool-router.d.ts +149 -0
- package/dist/server/tool-router.js +803 -0
- package/dist/server/validation.d.ts +24 -0
- package/dist/server/validation.js +301 -0
- package/dist/server/worker.d.ts +19 -0
- package/dist/server/worker.js +201 -0
- package/dist/setup.d.ts +8 -0
- package/dist/setup.js +181 -0
- package/dist/shared/agent-core.d.ts +157 -0
- package/dist/shared/agent-core.js +534 -0
- package/dist/shared/anthropic-types.d.ts +105 -0
- package/dist/shared/anthropic-types.js +7 -0
- package/dist/shared/api-client.d.ts +90 -0
- package/dist/shared/api-client.js +379 -0
- package/dist/shared/constants.d.ts +33 -0
- package/dist/shared/constants.js +80 -0
- package/dist/shared/sse-parser.d.ts +26 -0
- package/dist/shared/sse-parser.js +259 -0
- package/dist/shared/tool-dispatch.d.ts +52 -0
- package/dist/shared/tool-dispatch.js +191 -0
- package/dist/shared/types.d.ts +72 -0
- package/dist/shared/types.js +7 -0
- package/dist/updater.d.ts +25 -0
- package/dist/updater.js +140 -0
- package/dist/webchat/widget.d.ts +0 -0
- package/dist/webchat/widget.js +397 -0
- package/package.json +95 -0
- package/src/cli/services/builtin-skills/commit.md +19 -0
- package/src/cli/services/builtin-skills/review-pr.md +21 -0
- package/src/cli/services/builtin-skills/review.md +18 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ModelSelector — inline model picker for chat
|
|
3
|
+
*
|
|
4
|
+
* Renders a SelectInput with available LLM models, grouped by provider.
|
|
5
|
+
* Esc to cancel. Current model marked with dot.
|
|
6
|
+
*/
|
|
7
|
+
export interface ModelOption {
|
|
8
|
+
label: string;
|
|
9
|
+
value: string;
|
|
10
|
+
modelId: string;
|
|
11
|
+
provider: string;
|
|
12
|
+
}
|
|
13
|
+
export declare const MODEL_OPTIONS: ModelOption[];
|
|
14
|
+
interface ModelSelectorProps {
|
|
15
|
+
currentModel: string;
|
|
16
|
+
onSelect: (model: ModelOption) => void;
|
|
17
|
+
onCancel: () => void;
|
|
18
|
+
}
|
|
19
|
+
export declare function ModelSelector({ currentModel, onSelect, onCancel }: ModelSelectorProps): import("react/jsx-runtime").JSX.Element;
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,73 @@
|
|
|
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 const MODEL_OPTIONS = [
|
|
6
|
+
// Auto
|
|
7
|
+
{ label: "Auto (smart routing)", value: "auto", modelId: "auto", provider: "Auto" },
|
|
8
|
+
// Anthropic
|
|
9
|
+
{ label: "Opus 4.6", value: "opus", modelId: "claude-opus-4-6", provider: "Anthropic" },
|
|
10
|
+
{ label: "Sonnet 4.6", value: "sonnet", modelId: "claude-sonnet-4-6", provider: "Anthropic" },
|
|
11
|
+
{ label: "Sonnet 4", value: "sonnet-4", modelId: "claude-sonnet-4-20250514", provider: "Anthropic" },
|
|
12
|
+
{ label: "Haiku 4.5", value: "haiku", modelId: "claude-haiku-4-5-20251001", provider: "Anthropic" },
|
|
13
|
+
// Bedrock
|
|
14
|
+
{ label: "Sonnet 4.6", value: "bedrock-sonnet", modelId: "anthropic.claude-sonnet-4-6", provider: "Bedrock" },
|
|
15
|
+
{ label: "Sonnet 4", value: "bedrock-sonnet-4", modelId: "us.anthropic.claude-sonnet-4-20250514-v1:0", provider: "Bedrock" },
|
|
16
|
+
{ label: "Sonnet 4.5", value: "bedrock-sonnet-4.5", modelId: "us.anthropic.claude-sonnet-4-5-20250929-v1:0", provider: "Bedrock" },
|
|
17
|
+
{ label: "Haiku 4.5", value: "bedrock-haiku", modelId: "us.anthropic.claude-haiku-4-5-20251001-v1:0", provider: "Bedrock" },
|
|
18
|
+
// Gemini
|
|
19
|
+
{ label: "Gemini 3 Pro", value: "gemini-3-pro", modelId: "gemini-3-pro-preview", provider: "Gemini" },
|
|
20
|
+
{ label: "Gemini 3 Flash", value: "gemini-3-flash", modelId: "gemini-3-flash-preview", provider: "Gemini" },
|
|
21
|
+
{ label: "Gemini 2.5 Pro", value: "gemini-pro", modelId: "gemini-2.5-pro", provider: "Gemini" },
|
|
22
|
+
{ label: "Gemini 2.5 Flash", value: "gemini-flash", modelId: "gemini-2.5-flash", provider: "Gemini" },
|
|
23
|
+
{ label: "Gemini 2.5 Flash Lite", value: "gemini-flash-lite", modelId: "gemini-2.5-flash-lite", provider: "Gemini" },
|
|
24
|
+
// OpenAI
|
|
25
|
+
{ label: "GPT-5", value: "gpt-5", modelId: "gpt-5", provider: "OpenAI" },
|
|
26
|
+
{ label: "GPT-5 mini", value: "gpt-5-mini", modelId: "gpt-5-mini", provider: "OpenAI" },
|
|
27
|
+
{ label: "GPT-5 nano", value: "gpt-5-nano", modelId: "gpt-5-nano", provider: "OpenAI" },
|
|
28
|
+
{ label: "o3", value: "o3", modelId: "o3", provider: "OpenAI" },
|
|
29
|
+
{ label: "o4-mini", value: "o4-mini", modelId: "o4-mini", provider: "OpenAI" },
|
|
30
|
+
{ label: "GPT-4o", value: "gpt-4o", modelId: "gpt-4o", provider: "OpenAI" },
|
|
31
|
+
];
|
|
32
|
+
// Group labels to render section headers
|
|
33
|
+
const PROVIDER_ORDER = ["Auto", "Anthropic", "Bedrock", "Gemini", "OpenAI"];
|
|
34
|
+
export function ModelSelector({ currentModel, onSelect, onCancel }) {
|
|
35
|
+
useInput((_input, key) => {
|
|
36
|
+
if (key.escape)
|
|
37
|
+
onCancel();
|
|
38
|
+
});
|
|
39
|
+
// Build flat items list with separator entries for provider headers
|
|
40
|
+
const items = [];
|
|
41
|
+
for (const provider of PROVIDER_ORDER) {
|
|
42
|
+
const models = MODEL_OPTIONS.filter((m) => m.provider === provider);
|
|
43
|
+
if (models.length === 0)
|
|
44
|
+
continue;
|
|
45
|
+
items.push({ label: `──── ${provider} ────`, value: `__header_${provider}` });
|
|
46
|
+
for (const m of models) {
|
|
47
|
+
items.push({ label: m.label, value: m.value });
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const handleSelect = (item) => {
|
|
51
|
+
if (item.value.startsWith("__header_"))
|
|
52
|
+
return; // skip headers
|
|
53
|
+
const model = MODEL_OPTIONS.find((m) => m.value === item.value);
|
|
54
|
+
if (model)
|
|
55
|
+
onSelect(model);
|
|
56
|
+
};
|
|
57
|
+
// Find initial index (skip headers, land on current model)
|
|
58
|
+
const initialIdx = Math.max(0, items.findIndex((i) => i.value === currentModel));
|
|
59
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: colors.secondary, children: " Select model:" }), _jsx(Box, { height: 1 }), _jsx(SelectInput, { items: items, initialIndex: initialIdx, onSelect: handleSelect, indicatorComponent: ({ isSelected, item }) => {
|
|
60
|
+
const val = typeof item === "string" ? item : "";
|
|
61
|
+
if (val.startsWith("__header_"))
|
|
62
|
+
return _jsx(Text, { children: " " });
|
|
63
|
+
return (_jsxs(Text, { color: isSelected ? colors.brand : colors.quaternary, children: [isSelected ? symbols.arrowRight : " ", " "] }));
|
|
64
|
+
}, itemComponent: ({ isSelected, label, value }) => {
|
|
65
|
+
const val = typeof value === "string" ? value : "";
|
|
66
|
+
// Provider header
|
|
67
|
+
if (val.startsWith("__header_")) {
|
|
68
|
+
return _jsx(Text, { color: colors.dim, children: label });
|
|
69
|
+
}
|
|
70
|
+
const isCurrent = val === currentModel;
|
|
71
|
+
return (_jsxs(Box, { children: [_jsx(Text, { color: isSelected ? colors.brand : colors.text, bold: isSelected, children: label }), isCurrent && _jsxs(Text, { color: colors.success, children: [" ", symbols.dot, " current"] })] }));
|
|
72
|
+
} }), _jsx(Text, { color: colors.quaternary, children: " esc to cancel" })] }));
|
|
73
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RewindViewer — conversation timeline for rewind selection
|
|
3
|
+
*
|
|
4
|
+
* Displays checkpoints as a navigable list. Users select a point to
|
|
5
|
+
* rewind to with arrow keys and Enter. Esc cancels.
|
|
6
|
+
*
|
|
7
|
+
* UX modeled after Gemini CLI's rewind viewer:
|
|
8
|
+
* - Shows each turn with a summary of what happened
|
|
9
|
+
* - Lists file changes for each turn
|
|
10
|
+
* - Highlight selected checkpoint
|
|
11
|
+
* - Confirmation step with rewind options
|
|
12
|
+
*/
|
|
13
|
+
import type { RewindCheckpoint } from "../services/rewind.js";
|
|
14
|
+
export declare enum RewindOutcome {
|
|
15
|
+
RewindAndRevert = "rewind_and_revert",
|
|
16
|
+
RewindOnly = "rewind_only",
|
|
17
|
+
RevertOnly = "revert_only",
|
|
18
|
+
Cancel = "cancel"
|
|
19
|
+
}
|
|
20
|
+
interface RewindViewerProps {
|
|
21
|
+
checkpoints: RewindCheckpoint[];
|
|
22
|
+
onRewind: (checkpointIndex: number, outcome: RewindOutcome) => void;
|
|
23
|
+
onCancel: () => void;
|
|
24
|
+
}
|
|
25
|
+
export declare function RewindViewer({ checkpoints, onRewind, onCancel }: RewindViewerProps): import("react/jsx-runtime").JSX.Element;
|
|
26
|
+
export {};
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* RewindViewer — conversation timeline for rewind selection
|
|
4
|
+
*
|
|
5
|
+
* Displays checkpoints as a navigable list. Users select a point to
|
|
6
|
+
* rewind to with arrow keys and Enter. Esc cancels.
|
|
7
|
+
*
|
|
8
|
+
* UX modeled after Gemini CLI's rewind viewer:
|
|
9
|
+
* - Shows each turn with a summary of what happened
|
|
10
|
+
* - Lists file changes for each turn
|
|
11
|
+
* - Highlight selected checkpoint
|
|
12
|
+
* - Confirmation step with rewind options
|
|
13
|
+
*/
|
|
14
|
+
import { useState, useMemo } from "react";
|
|
15
|
+
import { Box, Text, useInput } from "ink";
|
|
16
|
+
import { colors, symbols } from "../shared/Theme.js";
|
|
17
|
+
// ============================================================================
|
|
18
|
+
// TYPES
|
|
19
|
+
// ============================================================================
|
|
20
|
+
export var RewindOutcome;
|
|
21
|
+
(function (RewindOutcome) {
|
|
22
|
+
RewindOutcome["RewindAndRevert"] = "rewind_and_revert";
|
|
23
|
+
RewindOutcome["RewindOnly"] = "rewind_only";
|
|
24
|
+
RewindOutcome["RevertOnly"] = "revert_only";
|
|
25
|
+
RewindOutcome["Cancel"] = "cancel";
|
|
26
|
+
})(RewindOutcome || (RewindOutcome = {}));
|
|
27
|
+
// ============================================================================
|
|
28
|
+
// HELPERS
|
|
29
|
+
// ============================================================================
|
|
30
|
+
function truncateSummary(text, maxLen) {
|
|
31
|
+
if (!text)
|
|
32
|
+
return "(no text)";
|
|
33
|
+
const cleaned = text.replace(/\n/g, " ").trim();
|
|
34
|
+
if (cleaned.length <= maxLen)
|
|
35
|
+
return cleaned;
|
|
36
|
+
return cleaned.slice(0, maxLen - 1) + "\u2026";
|
|
37
|
+
}
|
|
38
|
+
function formatFileOp(op, isNew) {
|
|
39
|
+
if (isNew)
|
|
40
|
+
return "created";
|
|
41
|
+
switch (op) {
|
|
42
|
+
case "write": return "written";
|
|
43
|
+
case "edit": return "edited";
|
|
44
|
+
case "multi_edit": return "edited";
|
|
45
|
+
default: return "modified";
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function formatTimeAgo(timestamp) {
|
|
49
|
+
const diff = Date.now() - timestamp;
|
|
50
|
+
const seconds = Math.floor(diff / 1000);
|
|
51
|
+
if (seconds < 60)
|
|
52
|
+
return "just now";
|
|
53
|
+
const minutes = Math.floor(seconds / 60);
|
|
54
|
+
if (minutes < 60)
|
|
55
|
+
return `${minutes}m ago`;
|
|
56
|
+
const hours = Math.floor(minutes / 60);
|
|
57
|
+
return `${hours}h ago`;
|
|
58
|
+
}
|
|
59
|
+
function basename(filePath) {
|
|
60
|
+
const parts = filePath.split("/");
|
|
61
|
+
return parts[parts.length - 1] || filePath;
|
|
62
|
+
}
|
|
63
|
+
// ============================================================================
|
|
64
|
+
// CONFIRMATION COMPONENT
|
|
65
|
+
// ============================================================================
|
|
66
|
+
function RewindConfirmation({ checkpoint, fileChangeCount, onConfirm, onCancel, }) {
|
|
67
|
+
const hasFileChanges = fileChangeCount > 0;
|
|
68
|
+
const options = useMemo(() => {
|
|
69
|
+
const opts = [];
|
|
70
|
+
if (hasFileChanges) {
|
|
71
|
+
opts.push({
|
|
72
|
+
label: "Rewind conversation and revert file changes",
|
|
73
|
+
value: RewindOutcome.RewindAndRevert,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
opts.push({
|
|
77
|
+
label: "Rewind conversation only",
|
|
78
|
+
value: RewindOutcome.RewindOnly,
|
|
79
|
+
});
|
|
80
|
+
if (hasFileChanges) {
|
|
81
|
+
opts.push({
|
|
82
|
+
label: "Revert file changes only",
|
|
83
|
+
value: RewindOutcome.RevertOnly,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
opts.push({
|
|
87
|
+
label: "Cancel (Esc)",
|
|
88
|
+
value: RewindOutcome.Cancel,
|
|
89
|
+
});
|
|
90
|
+
return opts;
|
|
91
|
+
}, [hasFileChanges]);
|
|
92
|
+
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
93
|
+
useInput((input, key) => {
|
|
94
|
+
if (key.escape) {
|
|
95
|
+
onCancel();
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
if (key.upArrow) {
|
|
99
|
+
setSelectedIndex(prev => Math.max(0, prev - 1));
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
if (key.downArrow) {
|
|
103
|
+
setSelectedIndex(prev => Math.min(options.length - 1, prev + 1));
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
if (key.return) {
|
|
107
|
+
onConfirm(options[selectedIndex].value);
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
const termWidth = process.stdout.columns || 80;
|
|
112
|
+
const boxWidth = Math.min(termWidth - 2, 60);
|
|
113
|
+
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: "Confirm Rewind" }) }), _jsxs(Box, { marginBottom: 1, flexDirection: "column", children: [_jsxs(Text, { dimColor: true, children: ["Rewind to turn ", checkpoint.turnIndex + 1, ": ", truncateSummary(checkpoint.summary, 40)] }), hasFileChanges && (_jsxs(Text, { dimColor: true, children: [fileChangeCount, " file change", fileChangeCount !== 1 ? "s" : "", " will be affected"] })), !hasFileChanges && (_jsx(Text, { dimColor: true, children: "No file changes to revert." }))] }), _jsx(Box, { marginBottom: 1, children: _jsx(Text, { children: "Select an action:" }) }), _jsx(Box, { 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 })] }, opt.value))) })] }));
|
|
114
|
+
}
|
|
115
|
+
// ============================================================================
|
|
116
|
+
// MAIN VIEWER COMPONENT
|
|
117
|
+
// ============================================================================
|
|
118
|
+
export function RewindViewer({ checkpoints, onRewind, onCancel }) {
|
|
119
|
+
// Start selection at the last checkpoint (current position)
|
|
120
|
+
const [selectedIndex, setSelectedIndex] = useState(checkpoints.length - 1);
|
|
121
|
+
const [confirmIndex, setConfirmIndex] = useState(null);
|
|
122
|
+
// Calculate visible window (scrolling for long lists)
|
|
123
|
+
const termHeight = process.stdout.rows || 24;
|
|
124
|
+
const maxVisible = Math.max(3, Math.min(checkpoints.length, termHeight - 8));
|
|
125
|
+
const scrollOffset = useMemo(() => {
|
|
126
|
+
if (checkpoints.length <= maxVisible)
|
|
127
|
+
return 0;
|
|
128
|
+
// Keep selected item roughly centered
|
|
129
|
+
const half = Math.floor(maxVisible / 2);
|
|
130
|
+
const start = Math.max(0, Math.min(selectedIndex - half, checkpoints.length - maxVisible));
|
|
131
|
+
return start;
|
|
132
|
+
}, [selectedIndex, maxVisible, checkpoints.length]);
|
|
133
|
+
const visibleCheckpoints = checkpoints.slice(scrollOffset, scrollOffset + maxVisible);
|
|
134
|
+
useInput((input, key) => {
|
|
135
|
+
if (confirmIndex !== null)
|
|
136
|
+
return; // Confirmation handles its own input
|
|
137
|
+
if (key.escape) {
|
|
138
|
+
onCancel();
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
if (key.upArrow) {
|
|
142
|
+
setSelectedIndex(prev => Math.max(0, prev - 1));
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
if (key.downArrow) {
|
|
146
|
+
setSelectedIndex(prev => Math.min(checkpoints.length - 1, prev + 1));
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
if (key.return) {
|
|
150
|
+
// Selecting the last checkpoint (current) = cancel
|
|
151
|
+
if (selectedIndex === checkpoints.length - 1) {
|
|
152
|
+
onCancel();
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
setConfirmIndex(selectedIndex);
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
const termWidth = process.stdout.columns || 80;
|
|
160
|
+
const boxWidth = Math.min(termWidth - 2, 70);
|
|
161
|
+
// Show confirmation dialog
|
|
162
|
+
if (confirmIndex !== null) {
|
|
163
|
+
const checkpoint = checkpoints[confirmIndex];
|
|
164
|
+
const fileChangeCount = checkpoints
|
|
165
|
+
.slice(confirmIndex + 1)
|
|
166
|
+
.reduce((sum, cp) => sum + cp.fileChanges.length, 0);
|
|
167
|
+
return (_jsx(RewindConfirmation, { checkpoint: checkpoint, fileChangeCount: fileChangeCount, onConfirm: (outcome) => {
|
|
168
|
+
if (outcome === RewindOutcome.Cancel) {
|
|
169
|
+
setConfirmIndex(null);
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
onRewind(confirmIndex, outcome);
|
|
173
|
+
}
|
|
174
|
+
}, onCancel: () => setConfirmIndex(null) }));
|
|
175
|
+
}
|
|
176
|
+
if (checkpoints.length === 0) {
|
|
177
|
+
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: "Rewind" }), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: "Nothing to rewind to." }), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: "Press Esc to close." })] }));
|
|
178
|
+
}
|
|
179
|
+
return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: colors.border, paddingX: 1, paddingY: 1, width: boxWidth, children: [_jsx(Box, { marginBottom: 1, children: _jsxs(Text, { bold: true, color: colors.brand, children: [symbols.arrowRight, " Rewind"] }) }), scrollOffset > 0 && (_jsxs(Text, { dimColor: true, children: [" ", symbols.dot, symbols.dot, symbols.dot, " ", scrollOffset, " more above"] })), _jsx(Box, { flexDirection: "column", children: visibleCheckpoints.map((cp, visIdx) => {
|
|
180
|
+
const realIdx = scrollOffset + visIdx;
|
|
181
|
+
const isSelected = realIdx === selectedIndex;
|
|
182
|
+
const isCurrent = realIdx === checkpoints.length - 1;
|
|
183
|
+
return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsxs(Box, { children: [_jsxs(Text, { color: isSelected ? colors.brand : colors.quaternary, children: [isSelected ? symbols.arrowRight : " ", " "] }), _jsx(Text, { color: isSelected ? colors.text : (isCurrent ? colors.secondary : colors.text), children: isCurrent ? symbols.dot : symbols.bullet }), _jsxs(Text, { color: isSelected ? colors.brand : colors.secondary, bold: isSelected, children: [" ", "Turn ", cp.turnIndex + 1] }), _jsxs(Text, { dimColor: true, children: [" ", symbols.divider, " "] }), _jsx(Text, { color: isSelected ? colors.text : colors.secondary, bold: isSelected, children: truncateSummary(cp.summary, 45) }), isCurrent && (_jsx(Text, { dimColor: true, children: " (current)" }))] }), cp.fileChanges.length > 0 && (_jsx(Box, { flexDirection: "column", marginLeft: 4, children: cp.fileChanges.map((fc, fi) => (_jsxs(Text, { dimColor: true, children: [basename(fc.filePath), " (", formatFileOp(fc.operation, fc.isNewFile), ")"] }, fi))) })), isSelected && (_jsx(Box, { marginLeft: 4, children: _jsx(Text, { dimColor: true, children: formatTimeAgo(cp.timestamp) }) }))] }, realIdx));
|
|
184
|
+
}) }), scrollOffset + maxVisible < checkpoints.length && (_jsxs(Text, { dimColor: true, children: [" ", symbols.dot, symbols.dot, symbols.dot, " ", checkpoints.length - scrollOffset - maxVisible, " more below"] })), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { dimColor: true, children: [symbols.arrowRight, " Use arrow keys to select, Enter to rewind, Esc to cancel"] }) })] }));
|
|
185
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* StoreSelector — inline store picker for chat
|
|
3
|
+
*
|
|
4
|
+
* Renders a SelectInput with stores. Esc to cancel.
|
|
5
|
+
*/
|
|
6
|
+
import type { StoreInfo } from "../services/auth-service.js";
|
|
7
|
+
interface StoreSelectorProps {
|
|
8
|
+
stores: StoreInfo[];
|
|
9
|
+
currentStoreId: string;
|
|
10
|
+
onSelect: (store: StoreInfo) => void;
|
|
11
|
+
onCancel: () => void;
|
|
12
|
+
}
|
|
13
|
+
export declare function StoreSelector({ stores, currentStoreId, onSelect, onCancel }: StoreSelectorProps): import("react/jsx-runtime").JSX.Element;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,24 @@
|
|
|
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 StoreSelector({ stores, currentStoreId, onSelect, onCancel }) {
|
|
6
|
+
useInput((_input, key) => {
|
|
7
|
+
if (key.escape)
|
|
8
|
+
onCancel();
|
|
9
|
+
});
|
|
10
|
+
const items = stores.map((s) => ({
|
|
11
|
+
label: s.name + (s.slug ? ` (${s.slug})` : ""),
|
|
12
|
+
value: s.id,
|
|
13
|
+
}));
|
|
14
|
+
const handleSelect = (item) => {
|
|
15
|
+
const store = stores.find((s) => s.id === item.value);
|
|
16
|
+
if (store)
|
|
17
|
+
onSelect(store);
|
|
18
|
+
};
|
|
19
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: colors.secondary, children: " Select store:" }), _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 }) => {
|
|
20
|
+
const store = stores.find((s) => label.startsWith(s.name));
|
|
21
|
+
const isCurrent = store?.id === currentStoreId;
|
|
22
|
+
return (_jsxs(Box, { children: [_jsx(Text, { color: isSelected ? colors.brand : colors.text, bold: isSelected, children: label }), isCurrent && _jsxs(Text, { color: colors.success, children: [" ", symbols.dot, " current"] })] }));
|
|
23
|
+
} }), _jsx(Text, { color: colors.quaternary, children: " esc to cancel" })] }));
|
|
24
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* StreamingText — renders streaming markdown text
|
|
3
|
+
*
|
|
4
|
+
* Wrapped in React.memo to prevent re-renders when parent state
|
|
5
|
+
* (subagent updates, tool updates) changes but text hasn't.
|
|
6
|
+
*/
|
|
7
|
+
import React from "react";
|
|
8
|
+
interface StreamingTextProps {
|
|
9
|
+
text: string;
|
|
10
|
+
}
|
|
11
|
+
export declare const StreamingText: React.NamedExoticComponent<StreamingTextProps>;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* StreamingText — renders streaming markdown text
|
|
4
|
+
*
|
|
5
|
+
* Wrapped in React.memo to prevent re-renders when parent state
|
|
6
|
+
* (subagent updates, tool updates) changes but text hasn't.
|
|
7
|
+
*/
|
|
8
|
+
import React from "react";
|
|
9
|
+
import { MarkdownText } from "./MarkdownText.js";
|
|
10
|
+
export const StreamingText = React.memo(function StreamingText({ text }) {
|
|
11
|
+
return _jsx(MarkdownText, { text: text, streaming: true });
|
|
12
|
+
});
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SubagentPanel — Claude Code-style tree rendering for subagents
|
|
3
|
+
*
|
|
4
|
+
* Running agents show spinner + nested tool tree.
|
|
5
|
+
* Completed agents show green dot + stats summary.
|
|
6
|
+
* Uses ├──, └──, │ tree characters matching Claude Code's style.
|
|
7
|
+
* Text-first rendering — minimal Box usage for reliable terminal output.
|
|
8
|
+
*/
|
|
9
|
+
import React from "react";
|
|
10
|
+
export interface SubagentInnerTool {
|
|
11
|
+
name: string;
|
|
12
|
+
status: "running" | "success" | "error";
|
|
13
|
+
input?: Record<string, unknown>;
|
|
14
|
+
durationMs?: number;
|
|
15
|
+
}
|
|
16
|
+
export interface SubagentActivityState {
|
|
17
|
+
type: string;
|
|
18
|
+
model: string;
|
|
19
|
+
description: string;
|
|
20
|
+
turn: number;
|
|
21
|
+
message: string;
|
|
22
|
+
tools: SubagentInnerTool[];
|
|
23
|
+
startTime: number;
|
|
24
|
+
}
|
|
25
|
+
export interface CompletedSubagentInfo {
|
|
26
|
+
id: string;
|
|
27
|
+
type: string;
|
|
28
|
+
description: string;
|
|
29
|
+
toolCount: number;
|
|
30
|
+
tokens: {
|
|
31
|
+
input: number;
|
|
32
|
+
output: number;
|
|
33
|
+
};
|
|
34
|
+
durationMs: number;
|
|
35
|
+
success: boolean;
|
|
36
|
+
}
|
|
37
|
+
interface SubagentPanelProps {
|
|
38
|
+
running: Map<string, SubagentActivityState>;
|
|
39
|
+
completed: CompletedSubagentInfo[];
|
|
40
|
+
}
|
|
41
|
+
export declare function CompletedSubagentTree({ agents }: {
|
|
42
|
+
agents: CompletedSubagentInfo[];
|
|
43
|
+
}): import("react/jsx-runtime").JSX.Element | null;
|
|
44
|
+
export declare const SubagentPanel: React.NamedExoticComponent<SubagentPanelProps>;
|
|
45
|
+
export {};
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* SubagentPanel — Claude Code-style tree rendering for subagents
|
|
4
|
+
*
|
|
5
|
+
* Running agents show spinner + nested tool tree.
|
|
6
|
+
* Completed agents show green dot + stats summary.
|
|
7
|
+
* Uses ├──, └──, │ tree characters matching Claude Code's style.
|
|
8
|
+
* Text-first rendering — minimal Box usage for reliable terminal output.
|
|
9
|
+
*/
|
|
10
|
+
import React from "react";
|
|
11
|
+
import { Box, Text } from "ink";
|
|
12
|
+
import Spinner from "ink-spinner";
|
|
13
|
+
import { shortenPath, getDisplayName } from "./ToolIndicator.js";
|
|
14
|
+
import { colors } from "../shared/Theme.js";
|
|
15
|
+
// ============================================================================
|
|
16
|
+
// HELPERS
|
|
17
|
+
// ============================================================================
|
|
18
|
+
function formatTokens(n) {
|
|
19
|
+
if (n < 1000)
|
|
20
|
+
return String(n);
|
|
21
|
+
if (n < 10000)
|
|
22
|
+
return `${(n / 1000).toFixed(1)}k`;
|
|
23
|
+
return `${Math.round(n / 1000)}k`;
|
|
24
|
+
}
|
|
25
|
+
function toolContext(name, input) {
|
|
26
|
+
if (!input)
|
|
27
|
+
return "";
|
|
28
|
+
if (name === "read_file" || name === "write_file" || name === "edit_file" || name === "list_directory") {
|
|
29
|
+
return input.path ? shortenPath(String(input.path)) : "";
|
|
30
|
+
}
|
|
31
|
+
if (name === "glob")
|
|
32
|
+
return input.pattern ? String(input.pattern) : "";
|
|
33
|
+
if (name === "grep") {
|
|
34
|
+
const parts = [input.pattern, input.path ? shortenPath(String(input.path)) : null].filter(Boolean);
|
|
35
|
+
return parts.join(" in ");
|
|
36
|
+
}
|
|
37
|
+
if (name === "search_files" || name === "search_content") {
|
|
38
|
+
return [input.pattern || input.query, input.path ? shortenPath(String(input.path)) : null].filter(Boolean).join(" in ");
|
|
39
|
+
}
|
|
40
|
+
if (name === "run_command") {
|
|
41
|
+
const cmd = String(input.command || "");
|
|
42
|
+
return cmd.length > 40 ? cmd.slice(0, 37) + "..." : cmd;
|
|
43
|
+
}
|
|
44
|
+
if (name === "web_fetch")
|
|
45
|
+
return input.url ? String(input.url).slice(0, 40) : "";
|
|
46
|
+
return "";
|
|
47
|
+
}
|
|
48
|
+
const AGENT_TYPE_LABELS = {
|
|
49
|
+
explore: "Explore",
|
|
50
|
+
plan: "Plan",
|
|
51
|
+
"general-purpose": "Agent",
|
|
52
|
+
research: "Research",
|
|
53
|
+
};
|
|
54
|
+
const MAX_VISIBLE_TOOLS = 6;
|
|
55
|
+
// ============================================================================
|
|
56
|
+
// RUNNING AGENT TREE
|
|
57
|
+
// ============================================================================
|
|
58
|
+
function RunningAgentTree({ agent, isLast, isOnly }) {
|
|
59
|
+
const label = AGENT_TYPE_LABELS[agent.type] || agent.type;
|
|
60
|
+
const desc = agent.description || "";
|
|
61
|
+
const running = agent.tools.filter(t => t.status === "running");
|
|
62
|
+
const completed = agent.tools.filter(t => t.status !== "running");
|
|
63
|
+
const maxCompleted = MAX_VISIBLE_TOOLS - running.length;
|
|
64
|
+
const hiddenCount = Math.max(0, completed.length - maxCompleted);
|
|
65
|
+
const visibleCompleted = completed.slice(hiddenCount);
|
|
66
|
+
const visibleTools = [...visibleCompleted, ...running];
|
|
67
|
+
const isThinking = agent.tools.length === 0 || (agent.tools.every(t => t.status !== "running") && agent.message.includes("calling API"));
|
|
68
|
+
const childPrefix = isOnly ? " " : (isLast ? " " : "│ ");
|
|
69
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { children: [!isOnly ? _jsx(Text, { color: colors.tertiary, children: isLast ? "└── " : "├── " }) : null, _jsx(Text, { color: colors.brand, children: _jsx(Spinner, { type: "dots" }) }), _jsxs(Text, { color: colors.text, bold: true, children: [" ", label] }), desc ? _jsxs(Text, { color: colors.tertiary, children: [" ", desc] }) : null, agent.turn > 0 ? _jsxs(Text, { color: colors.tertiary, children: [" \u00B7 Turn ", agent.turn] }) : null] }), hiddenCount > 0 && (_jsxs(Text, { color: colors.tertiary, children: [childPrefix, "\u2502 ... +", hiddenCount, " earlier"] })), visibleTools.map((tool, i) => {
|
|
70
|
+
const isLastTool = i === visibleTools.length - 1 && !isThinking;
|
|
71
|
+
const toolBranch = isLastTool ? "└── " : "├── ";
|
|
72
|
+
const ctx = toolContext(tool.name, tool.input);
|
|
73
|
+
return (_jsxs(Text, { children: [_jsxs(Text, { color: colors.tertiary, children: [childPrefix, toolBranch] }), tool.status === "running" ? (_jsx(Text, { color: colors.brand, children: _jsx(Spinner, { type: "dots" }) })) : tool.status === "success" ? (_jsx(Text, { color: colors.success, children: "\u2713" })) : (_jsx(Text, { color: colors.error, children: "\u2715" })), _jsxs(Text, { bold: true, children: [" ", getDisplayName(tool.name)] }), ctx ? _jsxs(Text, { color: colors.tertiary, children: [" ", ctx] }) : null] }, `${tool.name}-${i}`));
|
|
74
|
+
}), isThinking && (_jsxs(Text, { children: [_jsxs(Text, { color: colors.tertiary, children: [childPrefix, "\u2514\u2500\u2500 "] }), _jsx(Text, { color: colors.brand, children: _jsx(Spinner, { type: "dots" }) }), _jsx(Text, { color: colors.tertiary, children: " thinking..." })] }))] }));
|
|
75
|
+
}
|
|
76
|
+
// ============================================================================
|
|
77
|
+
// COMPLETED SUMMARY TREE
|
|
78
|
+
// ============================================================================
|
|
79
|
+
export function CompletedSubagentTree({ agents }) {
|
|
80
|
+
if (agents.length === 0)
|
|
81
|
+
return null;
|
|
82
|
+
const typeCounts = new Map();
|
|
83
|
+
for (const a of agents) {
|
|
84
|
+
const label = AGENT_TYPE_LABELS[a.type] || a.type;
|
|
85
|
+
typeCounts.set(label, (typeCounts.get(label) || 0) + 1);
|
|
86
|
+
}
|
|
87
|
+
const typeStr = Array.from(typeCounts.entries())
|
|
88
|
+
.map(([label, count]) => `${count} ${label} agent${count > 1 ? "s" : ""}`)
|
|
89
|
+
.join(", ");
|
|
90
|
+
return (_jsxs(Box, { flexDirection: "column", marginLeft: 2, children: [_jsxs(Text, { children: [_jsx(Text, { color: colors.success, children: "\u25CF" }), _jsxs(Text, { color: colors.text, bold: true, children: [" ", typeStr, " finished"] })] }), agents.map((agent, i) => {
|
|
91
|
+
const isLast = i === agents.length - 1;
|
|
92
|
+
const branch = isLast ? "└── " : "├── ";
|
|
93
|
+
const childPrefix = isLast ? " " : "│ ";
|
|
94
|
+
const label = AGENT_TYPE_LABELS[agent.type] || agent.type;
|
|
95
|
+
const totalTokens = agent.tokens.input + agent.tokens.output;
|
|
96
|
+
return (_jsxs(React.Fragment, { children: [_jsxs(Text, { children: [_jsx(Text, { color: colors.tertiary, children: branch }), _jsxs(Text, { color: colors.text, children: [label, " ", agent.description] }), _jsxs(Text, { color: colors.tertiary, children: [" \u00B7 ", agent.toolCount, " tool use", agent.toolCount !== 1 ? "s" : "", " \u00B7 ", formatTokens(totalTokens), " tokens"] })] }), _jsxs(Text, { children: [_jsx(Text, { color: colors.tertiary, children: childPrefix }), agent.success ? (_jsx(Text, { color: colors.success, children: "Done" })) : (_jsx(Text, { color: colors.error, children: "Failed" }))] })] }, agent.id));
|
|
97
|
+
})] }));
|
|
98
|
+
}
|
|
99
|
+
// ============================================================================
|
|
100
|
+
// MAIN PANEL
|
|
101
|
+
// ============================================================================
|
|
102
|
+
export const SubagentPanel = React.memo(function SubagentPanel({ running, completed }) {
|
|
103
|
+
const hasRunning = running.size > 0;
|
|
104
|
+
const hasCompleted = completed.length > 0;
|
|
105
|
+
if (!hasRunning && !hasCompleted)
|
|
106
|
+
return null;
|
|
107
|
+
const runningEntries = Array.from(running.entries());
|
|
108
|
+
const isOnlyOne = runningEntries.length === 1 && !hasCompleted;
|
|
109
|
+
return (_jsxs(Box, { flexDirection: "column", children: [hasRunning && runningEntries.map(([id, agent], i) => (_jsx(RunningAgentTree, { agent: agent, isLast: i === runningEntries.length - 1, isOnly: isOnlyOne }, id))), hasCompleted && _jsx(CompletedSubagentTree, { agents: completed })] }));
|
|
110
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TeamPanel — Claude Code-style tree rendering for team execution
|
|
3
|
+
*
|
|
4
|
+
* Uses ├──, └──, │ tree chars. Text-first rendering.
|
|
5
|
+
* Shows green ● header with task progress and per-teammate status.
|
|
6
|
+
*/
|
|
7
|
+
import React from "react";
|
|
8
|
+
export interface TeamInfo {
|
|
9
|
+
name: string;
|
|
10
|
+
tasksCompleted: number;
|
|
11
|
+
tasksTotal: number;
|
|
12
|
+
teammates: Map<string, {
|
|
13
|
+
name: string;
|
|
14
|
+
status: string;
|
|
15
|
+
}>;
|
|
16
|
+
}
|
|
17
|
+
interface TeamPanelProps {
|
|
18
|
+
team: TeamInfo;
|
|
19
|
+
}
|
|
20
|
+
export declare const TeamPanel: React.NamedExoticComponent<TeamPanelProps>;
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* TeamPanel — Claude Code-style tree rendering for team execution
|
|
4
|
+
*
|
|
5
|
+
* Uses ├──, └──, │ tree chars. Text-first rendering.
|
|
6
|
+
* Shows green ● header with task progress and per-teammate status.
|
|
7
|
+
*/
|
|
8
|
+
import React from "react";
|
|
9
|
+
import { Box, Text } from "ink";
|
|
10
|
+
import Spinner from "ink-spinner";
|
|
11
|
+
import { colors } from "../shared/Theme.js";
|
|
12
|
+
// Custom comparator — prevent re-render when Map contents haven't actually changed
|
|
13
|
+
function teamPropsEqual(prev, next) {
|
|
14
|
+
if (prev.team.name !== next.team.name)
|
|
15
|
+
return false;
|
|
16
|
+
if (prev.team.tasksCompleted !== next.team.tasksCompleted)
|
|
17
|
+
return false;
|
|
18
|
+
if (prev.team.tasksTotal !== next.team.tasksTotal)
|
|
19
|
+
return false;
|
|
20
|
+
if (prev.team.teammates.size !== next.team.teammates.size)
|
|
21
|
+
return false;
|
|
22
|
+
for (const [id, mate] of prev.team.teammates) {
|
|
23
|
+
const nextMate = next.team.teammates.get(id);
|
|
24
|
+
if (!nextMate)
|
|
25
|
+
return false;
|
|
26
|
+
if (mate.name !== nextMate.name || mate.status !== nextMate.status)
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
export const TeamPanel = React.memo(function TeamPanel({ team }) {
|
|
32
|
+
const teammates = Array.from(team.teammates.entries());
|
|
33
|
+
return (_jsxs(Box, { flexDirection: "column", marginLeft: 2, children: [_jsxs(Text, { children: [_jsx(Text, { color: colors.success, children: "\u25CF" }), _jsxs(Text, { color: colors.text, bold: true, children: [" Team: ", team.name] }), _jsxs(Text, { color: colors.tertiary, children: [" \u00B7 ", team.tasksCompleted, "/", team.tasksTotal, " tasks"] })] }), teammates.map(([id, mate], i) => {
|
|
34
|
+
const isLast = i === teammates.length - 1;
|
|
35
|
+
const branch = isLast ? "└── " : "├── ";
|
|
36
|
+
const isDone = mate.status === "done";
|
|
37
|
+
const isFailed = mate.status === "failed";
|
|
38
|
+
const isWorking = !isDone && !isFailed;
|
|
39
|
+
const color = isDone ? colors.success : isFailed ? colors.error : undefined;
|
|
40
|
+
return (_jsxs(Text, { children: [_jsx(Text, { color: colors.tertiary, children: branch }), isWorking ? (_jsx(Text, { color: colors.brand, children: _jsx(Spinner, { type: "dots" }) })) : isDone ? (_jsx(Text, { color: colors.success, children: "\u2713" })) : (_jsx(Text, { color: colors.error, children: "\u2715" })), _jsxs(Text, { color: color || colors.dim, children: [" ", mate.name || mate.status] }), mate.name && mate.status !== mate.name && mate.status !== "done" && mate.status !== "failed" ? (_jsxs(Text, { color: colors.tertiary, children: [" \u2014 ", mate.status] })) : null] }, id));
|
|
41
|
+
})] }));
|
|
42
|
+
}, teamPropsEqual);
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ToolIndicator — polished tool call rendering
|
|
3
|
+
*
|
|
4
|
+
* Every result gets syntax highlighting via MarkdownText.
|
|
5
|
+
* Financial-aware: green for gains, red for deductions/negatives.
|
|
6
|
+
* Params: purple keys, typed values (blue dates, green money, red negatives).
|
|
7
|
+
* Duration badge, tool type glyph.
|
|
8
|
+
*/
|
|
9
|
+
import React from "react";
|
|
10
|
+
interface ToolIndicatorProps {
|
|
11
|
+
id: string;
|
|
12
|
+
name: string;
|
|
13
|
+
status: "running" | "success" | "error";
|
|
14
|
+
result?: string;
|
|
15
|
+
input?: Record<string, unknown>;
|
|
16
|
+
durationMs?: number;
|
|
17
|
+
expanded?: boolean;
|
|
18
|
+
/** When > 1, shows a "× N" badge and hides result (collapsed duplicate group) */
|
|
19
|
+
count?: number;
|
|
20
|
+
}
|
|
21
|
+
/** Shorten an absolute path for display: strip cwd, collapse home, truncate */
|
|
22
|
+
export declare function shortenPath(fullPath: string, maxLen?: number): string;
|
|
23
|
+
export declare function getDisplayName(name: string): string;
|
|
24
|
+
export declare const ToolIndicator: React.NamedExoticComponent<ToolIndicatorProps>;
|
|
25
|
+
export {};
|