wave-code 1.1.5 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bundle/wave.mjs +541 -544
- package/package.json +2 -2
- package/src/commands/plugin/install.ts +5 -5
- package/src/components/ChatInterface.tsx +2 -0
- package/src/components/HooksManager.tsx +286 -0
- package/src/components/InputBox.tsx +86 -49
- package/src/components/MessageBlockItem.tsx +0 -5
- package/src/components/MessageList.tsx +0 -1
- package/src/components/PlanView.tsx +141 -0
- package/src/constants/commands.ts +12 -0
- package/src/contexts/useChat.tsx +94 -70
- package/src/daemon/commands.ts +486 -30
- package/src/hooks/useInputManager.ts +14 -0
- package/src/hooks/useLineScroll.ts +58 -0
- package/src/index.ts +94 -8
- package/src/managers/inputHandlers.ts +2 -0
- package/src/managers/inputReducer.ts +10 -0
- package/src/reducers/hooksManagerReducer.ts +92 -0
- package/src/stdio/agentBridge.ts +552 -55
- package/src/stdio/daemonServer.ts +26 -82
- package/src/stdio/protocol.ts +14 -4
- package/src/stdio-cli.ts +54 -0
- package/src/utils/rewindCheckpoints.ts +2 -2
- package/src/utils/worktree.ts +175 -50
- package/src/components/BangDisplay.tsx +0 -41
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wave-code",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "CLI-based code assistant powered by AI, built with React and Ink",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"wrap-ansi": "^10.0.0",
|
|
57
57
|
"yargs": "^17.7.2",
|
|
58
58
|
"zod": "^3.23.8",
|
|
59
|
-
"wave-agent-sdk": "1.
|
|
59
|
+
"wave-agent-sdk": "1.2.0"
|
|
60
60
|
},
|
|
61
61
|
"engines": {
|
|
62
62
|
"node": ">=22"
|
|
@@ -14,11 +14,11 @@ export async function installPluginCommand(argv: {
|
|
|
14
14
|
);
|
|
15
15
|
console.log(`Cache path: ${installed.cachePath}`);
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
17
|
+
// When no scope is given, default to user scope (matches Claude Code).
|
|
18
|
+
const scope = argv.scope ?? "user";
|
|
19
|
+
const pluginId = `${installed.name}@${installed.marketplace}`;
|
|
20
|
+
await pluginCore.enablePlugin(pluginId, scope);
|
|
21
|
+
console.log(`Plugin ${pluginId} enabled in ${scope} scope`);
|
|
22
22
|
|
|
23
23
|
process.exit(0);
|
|
24
24
|
} catch (error) {
|
|
@@ -29,6 +29,7 @@ export const ChatInterface: React.FC = () => {
|
|
|
29
29
|
maxInputTokens,
|
|
30
30
|
slashCommands,
|
|
31
31
|
hasSlashCommand,
|
|
32
|
+
hooks,
|
|
32
33
|
isConfirmationVisible,
|
|
33
34
|
confirmingTool,
|
|
34
35
|
handleConfirmationDecision,
|
|
@@ -109,6 +110,7 @@ export const ChatInterface: React.FC = () => {
|
|
|
109
110
|
disconnectMcpServer={disconnectMcpServer}
|
|
110
111
|
slashCommands={slashCommands}
|
|
111
112
|
hasSlashCommand={hasSlashCommand}
|
|
113
|
+
hooks={hooks}
|
|
112
114
|
latestTotalTokens={latestTotalTokens}
|
|
113
115
|
maxInputTokens={maxInputTokens}
|
|
114
116
|
showLoginHint={showLoginHint}
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
import React, { useEffect, useMemo, useReducer } from "react";
|
|
2
|
+
import { Box, Text, useInput, useStdout } from "ink";
|
|
3
|
+
import {
|
|
4
|
+
HOOK_EVENT_SUMMARIES,
|
|
5
|
+
type HookEvent,
|
|
6
|
+
type HookEventConfig,
|
|
7
|
+
} from "wave-agent-sdk";
|
|
8
|
+
import {
|
|
9
|
+
hooksManagerReducer,
|
|
10
|
+
type HooksManagerState,
|
|
11
|
+
} from "../reducers/hooksManagerReducer.js";
|
|
12
|
+
|
|
13
|
+
export interface HooksManagerProps {
|
|
14
|
+
onCancel: () => void;
|
|
15
|
+
/** Hook configs per scope (user/project/plugin settings.json + plugin set). */
|
|
16
|
+
hooks: Partial<
|
|
17
|
+
Record<"user" | "project" | "plugin", Record<string, HookEventConfig[]>>
|
|
18
|
+
>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface DisplayEntry {
|
|
22
|
+
kind: "header" | "hook" | "empty";
|
|
23
|
+
label: string;
|
|
24
|
+
sub?: string;
|
|
25
|
+
scope?: "user" | "project" | "plugin";
|
|
26
|
+
selectableIndex: number; // -1 for non-selectable rows
|
|
27
|
+
hook?: {
|
|
28
|
+
event: string;
|
|
29
|
+
matcher?: string;
|
|
30
|
+
config: HookEventConfig;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const SCOPE_LABELS: Record<"user" | "project" | "plugin", string> = {
|
|
35
|
+
user: "User hooks",
|
|
36
|
+
project: "Project hooks",
|
|
37
|
+
plugin: "Plugin hooks",
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const SCOPE_ORDER: Array<"user" | "project" | "plugin"> = [
|
|
41
|
+
"user",
|
|
42
|
+
"project",
|
|
43
|
+
"plugin",
|
|
44
|
+
];
|
|
45
|
+
|
|
46
|
+
const initialState: HooksManagerState = {
|
|
47
|
+
selectedIndex: 0,
|
|
48
|
+
viewMode: "list",
|
|
49
|
+
pendingEffect: null,
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/** Hook name for display: `Event:Matcher` when a matcher is present, else the
|
|
53
|
+
* event name alone. */
|
|
54
|
+
function formatHookName(event: string, matcher?: string): string {
|
|
55
|
+
return matcher ? `${event}:${matcher}` : event;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export const HooksManager: React.FC<HooksManagerProps> = ({
|
|
59
|
+
onCancel,
|
|
60
|
+
hooks,
|
|
61
|
+
}) => {
|
|
62
|
+
const [state, dispatch] = useReducer(hooksManagerReducer, initialState);
|
|
63
|
+
const { stdout } = useStdout();
|
|
64
|
+
|
|
65
|
+
// Handle pending effects
|
|
66
|
+
useEffect(() => {
|
|
67
|
+
if (!state.pendingEffect) return;
|
|
68
|
+
const effect = state.pendingEffect;
|
|
69
|
+
dispatch({ type: "CLEAR_PENDING_EFFECT" });
|
|
70
|
+
if (effect.type === "CANCEL") {
|
|
71
|
+
onCancel();
|
|
72
|
+
}
|
|
73
|
+
}, [state.pendingEffect, onCancel]);
|
|
74
|
+
|
|
75
|
+
// Flatten hooks (grouped by scope) into one navigable list. Headers and
|
|
76
|
+
// the empty-state line are non-selectable.
|
|
77
|
+
const entries = useMemo<DisplayEntry[]>(() => {
|
|
78
|
+
const result: DisplayEntry[] = [];
|
|
79
|
+
let selectableCount = 0;
|
|
80
|
+
|
|
81
|
+
result.push({ kind: "header", label: "HOOKS", selectableIndex: -1 });
|
|
82
|
+
let hookCount = 0;
|
|
83
|
+
for (const scope of SCOPE_ORDER) {
|
|
84
|
+
const scoped = hooks[scope] ?? {};
|
|
85
|
+
const rows: DisplayEntry[] = [];
|
|
86
|
+
for (const [event, configs] of Object.entries(scoped)) {
|
|
87
|
+
for (const config of configs ?? []) {
|
|
88
|
+
const matcher = config.matcher;
|
|
89
|
+
const name = formatHookName(event, matcher);
|
|
90
|
+
rows.push({
|
|
91
|
+
kind: "hook",
|
|
92
|
+
label: name,
|
|
93
|
+
sub:
|
|
94
|
+
HOOK_EVENT_SUMMARIES[event as HookEvent] ??
|
|
95
|
+
config.hooks[0]?.command,
|
|
96
|
+
scope,
|
|
97
|
+
selectableIndex: selectableCount++,
|
|
98
|
+
hook: { event, matcher, config },
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
if (rows.length === 0) continue;
|
|
103
|
+
result.push({
|
|
104
|
+
kind: "header",
|
|
105
|
+
label: SCOPE_LABELS[scope],
|
|
106
|
+
scope,
|
|
107
|
+
selectableIndex: -1,
|
|
108
|
+
});
|
|
109
|
+
result.push(...rows);
|
|
110
|
+
hookCount += rows.length;
|
|
111
|
+
}
|
|
112
|
+
if (hookCount === 0) {
|
|
113
|
+
result.push({
|
|
114
|
+
kind: "empty",
|
|
115
|
+
label: "No hooks configured",
|
|
116
|
+
selectableIndex: -1,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return result;
|
|
121
|
+
}, [hooks]);
|
|
122
|
+
|
|
123
|
+
const itemCount = entries.filter((e) => e.selectableIndex >= 0).length;
|
|
124
|
+
|
|
125
|
+
// Window slice: center the selected item within the visible area, clamping
|
|
126
|
+
// to the terminal's available rows (reusable pattern from
|
|
127
|
+
// AgentsManager/SkillsManager).
|
|
128
|
+
const availableRows = stdout?.rows ?? 24;
|
|
129
|
+
const maxVisible = Math.max(3, Math.min(15, availableRows - 12));
|
|
130
|
+
const selectedFlatIndex = entries.findIndex(
|
|
131
|
+
(e) => e.selectableIndex === state.selectedIndex,
|
|
132
|
+
);
|
|
133
|
+
const startIndex = Math.max(
|
|
134
|
+
0,
|
|
135
|
+
Math.min(
|
|
136
|
+
selectedFlatIndex - Math.floor(maxVisible / 2),
|
|
137
|
+
Math.max(0, entries.length - maxVisible),
|
|
138
|
+
),
|
|
139
|
+
);
|
|
140
|
+
const visibleEntries = entries.slice(startIndex, startIndex + maxVisible);
|
|
141
|
+
|
|
142
|
+
useInput((input, key) => {
|
|
143
|
+
dispatch({ type: "HANDLE_KEY", input, key, itemCount });
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
const selectedEntry = entries.find(
|
|
147
|
+
(e) => e.selectableIndex === state.selectedIndex,
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
// Detail view — body renders fully expanded with no height limit, no
|
|
151
|
+
// clipping and no scrolling (aligned with AgentsManager/SkillsManager).
|
|
152
|
+
if (state.viewMode === "detail" && selectedEntry?.hook) {
|
|
153
|
+
const { event, matcher, config } = selectedEntry.hook;
|
|
154
|
+
const summary = HOOK_EVENT_SUMMARIES[event as HookEvent];
|
|
155
|
+
return (
|
|
156
|
+
<Box
|
|
157
|
+
flexDirection="column"
|
|
158
|
+
borderStyle="single"
|
|
159
|
+
borderColor="cyan"
|
|
160
|
+
borderBottom={false}
|
|
161
|
+
borderLeft={false}
|
|
162
|
+
borderRight={false}
|
|
163
|
+
paddingTop={1}
|
|
164
|
+
gap={1}
|
|
165
|
+
>
|
|
166
|
+
<Box>
|
|
167
|
+
<Text color="cyan" bold>
|
|
168
|
+
Hook: {formatHookName(event, matcher)}
|
|
169
|
+
</Text>
|
|
170
|
+
</Box>
|
|
171
|
+
|
|
172
|
+
<Box flexDirection="column" gap={1}>
|
|
173
|
+
<Box>
|
|
174
|
+
<Text wrap="wrap">
|
|
175
|
+
<Text color="blue">Event:</Text> {event}
|
|
176
|
+
{summary ? ` — ${summary}` : ""}
|
|
177
|
+
</Text>
|
|
178
|
+
</Box>
|
|
179
|
+
{matcher && (
|
|
180
|
+
<Box>
|
|
181
|
+
<Text>
|
|
182
|
+
<Text color="blue">Matcher:</Text> {matcher}
|
|
183
|
+
</Text>
|
|
184
|
+
</Box>
|
|
185
|
+
)}
|
|
186
|
+
<Box flexDirection="column" gap={0}>
|
|
187
|
+
<Text color="blue">Commands:</Text>
|
|
188
|
+
{config.hooks.map((hook, index) => (
|
|
189
|
+
<Text key={index} wrap="wrap">
|
|
190
|
+
{index + 1}. {hook.command}
|
|
191
|
+
{hook.async ? " (async)" : ""}
|
|
192
|
+
{hook.timeout ? ` (timeout ${hook.timeout}s)` : ""}
|
|
193
|
+
</Text>
|
|
194
|
+
))}
|
|
195
|
+
</Box>
|
|
196
|
+
</Box>
|
|
197
|
+
|
|
198
|
+
<Box marginTop={1}>
|
|
199
|
+
<Text dimColor>Esc or Enter to go back</Text>
|
|
200
|
+
</Box>
|
|
201
|
+
</Box>
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (itemCount === 0) {
|
|
206
|
+
return (
|
|
207
|
+
<Box
|
|
208
|
+
flexDirection="column"
|
|
209
|
+
borderStyle="single"
|
|
210
|
+
borderColor="cyan"
|
|
211
|
+
borderBottom={false}
|
|
212
|
+
borderLeft={false}
|
|
213
|
+
borderRight={false}
|
|
214
|
+
paddingTop={1}
|
|
215
|
+
>
|
|
216
|
+
<Text color="cyan" bold>
|
|
217
|
+
Hooks
|
|
218
|
+
</Text>
|
|
219
|
+
<Text>No hooks configured</Text>
|
|
220
|
+
<Text dimColor>
|
|
221
|
+
Configure hooks in the hooks field of ~/.wave/settings.json or
|
|
222
|
+
.wave/settings.json, or ask Claude to set one up for you
|
|
223
|
+
</Text>
|
|
224
|
+
<Text dimColor>Press Escape to close</Text>
|
|
225
|
+
</Box>
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return (
|
|
230
|
+
<Box
|
|
231
|
+
flexDirection="column"
|
|
232
|
+
borderStyle="single"
|
|
233
|
+
borderColor="cyan"
|
|
234
|
+
borderBottom={false}
|
|
235
|
+
borderLeft={false}
|
|
236
|
+
borderRight={false}
|
|
237
|
+
paddingTop={1}
|
|
238
|
+
gap={1}
|
|
239
|
+
>
|
|
240
|
+
<Box>
|
|
241
|
+
<Text color="cyan" bold>
|
|
242
|
+
Hooks
|
|
243
|
+
</Text>
|
|
244
|
+
</Box>
|
|
245
|
+
<Text dimColor>Select a hook to view details</Text>
|
|
246
|
+
|
|
247
|
+
<Box flexDirection="column">
|
|
248
|
+
{visibleEntries.map((entry, index) => {
|
|
249
|
+
const isSelected = entry.selectableIndex === state.selectedIndex;
|
|
250
|
+
if (entry.kind === "header") {
|
|
251
|
+
return (
|
|
252
|
+
<Text key={`${entry.kind}-${entry.label}-${index}`} dimColor bold>
|
|
253
|
+
{entry.label}
|
|
254
|
+
</Text>
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
if (entry.kind === "empty") {
|
|
258
|
+
return (
|
|
259
|
+
<Text key={`empty-${index}`} dimColor>
|
|
260
|
+
{entry.label}
|
|
261
|
+
</Text>
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
return (
|
|
265
|
+
<Text
|
|
266
|
+
key={`${entry.kind}-${entry.selectableIndex}`}
|
|
267
|
+
color={isSelected ? "black" : "white"}
|
|
268
|
+
backgroundColor={isSelected ? "cyan" : undefined}
|
|
269
|
+
wrap="truncate-end"
|
|
270
|
+
>
|
|
271
|
+
{isSelected ? "▶ " : " "}
|
|
272
|
+
{entry.selectableIndex + 1}. {entry.label}
|
|
273
|
+
{entry.sub ? ` · ${entry.sub}` : ""}
|
|
274
|
+
</Text>
|
|
275
|
+
);
|
|
276
|
+
})}
|
|
277
|
+
</Box>
|
|
278
|
+
|
|
279
|
+
<Box marginTop={1}>
|
|
280
|
+
<Text dimColor>
|
|
281
|
+
↑/↓ to select · Enter to view details · Esc to close
|
|
282
|
+
</Text>
|
|
283
|
+
</Box>
|
|
284
|
+
</Box>
|
|
285
|
+
);
|
|
286
|
+
};
|
|
@@ -8,6 +8,7 @@ import { BackgroundTaskManager } from "./BackgroundTaskManager.js";
|
|
|
8
8
|
import { McpManager } from "./McpManager.js";
|
|
9
9
|
import { AgentsManager } from "./AgentsManager.js";
|
|
10
10
|
import { SkillsManager } from "./SkillsManager.js";
|
|
11
|
+
import { HooksManager } from "./HooksManager.js";
|
|
11
12
|
import { RewindCommand } from "./RewindCommand.js";
|
|
12
13
|
import { HelpView } from "./HelpView.js";
|
|
13
14
|
import { StatusCommand } from "./StatusCommand.js";
|
|
@@ -18,10 +19,15 @@ import { WorkflowManager } from "./WorkflowManager.js";
|
|
|
18
19
|
import { StatusLine } from "./StatusLine.js";
|
|
19
20
|
import { Notifications } from "./Notifications.js";
|
|
20
21
|
import { BtwDisplay } from "./BtwDisplay.js";
|
|
22
|
+
import { PlanView } from "./PlanView.js";
|
|
21
23
|
import { useInputManager } from "../hooks/useInputManager.js";
|
|
22
24
|
import { useChat } from "../contexts/useChat.js";
|
|
23
25
|
|
|
24
|
-
import type {
|
|
26
|
+
import type {
|
|
27
|
+
McpServerStatus,
|
|
28
|
+
SlashCommand,
|
|
29
|
+
HookEventConfig,
|
|
30
|
+
} from "wave-agent-sdk";
|
|
25
31
|
|
|
26
32
|
export const INPUT_PLACEHOLDER_TEXT =
|
|
27
33
|
"Type your message (use /help for more info)...";
|
|
@@ -49,6 +55,10 @@ export interface InputBoxProps {
|
|
|
49
55
|
// Slash Command related properties
|
|
50
56
|
slashCommands?: SlashCommand[];
|
|
51
57
|
hasSlashCommand?: (commandId: string) => boolean;
|
|
58
|
+
// Hooks related properties (for /hooks overlay)
|
|
59
|
+
hooks?: Partial<
|
|
60
|
+
Record<"user" | "project" | "plugin", Record<string, HookEventConfig[]>>
|
|
61
|
+
>;
|
|
52
62
|
// Token usage
|
|
53
63
|
latestTotalTokens?: number;
|
|
54
64
|
maxInputTokens?: number;
|
|
@@ -67,6 +77,7 @@ export const InputBox: React.FC<InputBoxProps> = ({
|
|
|
67
77
|
disconnectMcpServer = async () => false,
|
|
68
78
|
slashCommands = [],
|
|
69
79
|
hasSlashCommand = () => false,
|
|
80
|
+
hooks = {},
|
|
70
81
|
latestTotalTokens = 0,
|
|
71
82
|
maxInputTokens = 200000,
|
|
72
83
|
showLoginHint = false,
|
|
@@ -93,6 +104,9 @@ export const InputBox: React.FC<InputBoxProps> = ({
|
|
|
93
104
|
setIsBtwActive,
|
|
94
105
|
agentDefinitions,
|
|
95
106
|
skills,
|
|
107
|
+
planView,
|
|
108
|
+
setPlanView,
|
|
109
|
+
handlePlanCommand,
|
|
96
110
|
} = useChat();
|
|
97
111
|
|
|
98
112
|
// Ref to hold setInputText so queue callbacks can access it before useInputManager returns
|
|
@@ -146,6 +160,7 @@ export const InputBox: React.FC<InputBoxProps> = ({
|
|
|
146
160
|
showModelSelector,
|
|
147
161
|
showWorkflowManager,
|
|
148
162
|
showSkillsManager,
|
|
163
|
+
showHooksManager,
|
|
149
164
|
setShowBackgroundTaskManager,
|
|
150
165
|
setShowMcpManager,
|
|
151
166
|
setShowAgentsManager,
|
|
@@ -157,6 +172,7 @@ export const InputBox: React.FC<InputBoxProps> = ({
|
|
|
157
172
|
setShowModelSelector,
|
|
158
173
|
setShowWorkflowManager,
|
|
159
174
|
setShowSkillsManager,
|
|
175
|
+
setShowHooksManager,
|
|
160
176
|
// Permission mode
|
|
161
177
|
permissionMode,
|
|
162
178
|
setPermissionMode,
|
|
@@ -180,6 +196,7 @@ export const InputBox: React.FC<InputBoxProps> = ({
|
|
|
180
196
|
onAbortMessage: abortMessage,
|
|
181
197
|
onBackgroundCurrentTask: backgroundCurrentTask,
|
|
182
198
|
onPermissionModeChange: setChatPermissionMode,
|
|
199
|
+
onPlanCommand: handlePlanCommand,
|
|
183
200
|
sessionId,
|
|
184
201
|
workdir: workingDirectory,
|
|
185
202
|
getFullMessageThread,
|
|
@@ -211,6 +228,7 @@ export const InputBox: React.FC<InputBoxProps> = ({
|
|
|
211
228
|
// If they are active, we should skip InputBox's global input handling to avoid
|
|
212
229
|
// duplicate dispatches or state update conflicts.
|
|
213
230
|
if (
|
|
231
|
+
planView ||
|
|
214
232
|
showRewindManager ||
|
|
215
233
|
showHelp ||
|
|
216
234
|
showStatusCommand ||
|
|
@@ -221,7 +239,8 @@ export const InputBox: React.FC<InputBoxProps> = ({
|
|
|
221
239
|
showMcpManager ||
|
|
222
240
|
showAgentsManager ||
|
|
223
241
|
showWorkflowManager ||
|
|
224
|
-
showSkillsManager
|
|
242
|
+
showSkillsManager ||
|
|
243
|
+
showHooksManager
|
|
225
244
|
) {
|
|
226
245
|
return;
|
|
227
246
|
}
|
|
@@ -324,6 +343,13 @@ export const InputBox: React.FC<InputBoxProps> = ({
|
|
|
324
343
|
/>
|
|
325
344
|
)}
|
|
326
345
|
|
|
346
|
+
{showHooksManager && (
|
|
347
|
+
<HooksManager
|
|
348
|
+
onCancel={() => setShowHooksManager(false)}
|
|
349
|
+
hooks={hooks}
|
|
350
|
+
/>
|
|
351
|
+
)}
|
|
352
|
+
|
|
327
353
|
{showWorkflowManager && (
|
|
328
354
|
<WorkflowManager onCancel={() => setShowWorkflowManager(false)} />
|
|
329
355
|
)}
|
|
@@ -368,56 +394,67 @@ export const InputBox: React.FC<InputBoxProps> = ({
|
|
|
368
394
|
/>
|
|
369
395
|
)}
|
|
370
396
|
|
|
397
|
+
{planView && (
|
|
398
|
+
<PlanView
|
|
399
|
+
path={planView.path}
|
|
400
|
+
content={planView.content}
|
|
401
|
+
message={planView.message}
|
|
402
|
+
onCancel={() => setPlanView(null)}
|
|
403
|
+
/>
|
|
404
|
+
)}
|
|
405
|
+
|
|
371
406
|
{btwState.question || btwState.answer
|
|
372
407
|
? null
|
|
373
|
-
:
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
{
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
408
|
+
: planView
|
|
409
|
+
? null
|
|
410
|
+
: showBackgroundTaskManager ||
|
|
411
|
+
showMcpManager ||
|
|
412
|
+
showAgentsManager ||
|
|
413
|
+
showSkillsManager ||
|
|
414
|
+
showRewindManager ||
|
|
415
|
+
showHelp ||
|
|
416
|
+
showStatusCommand ||
|
|
417
|
+
showLoginCommand ||
|
|
418
|
+
showPluginManager ||
|
|
419
|
+
showModelSelector ||
|
|
420
|
+
showWorkflowManager || (
|
|
421
|
+
<Box flexDirection="column">
|
|
422
|
+
{escClearPending && (
|
|
423
|
+
<Text color="gray">Press Esc again to clear input</Text>
|
|
424
|
+
)}
|
|
425
|
+
<Box
|
|
426
|
+
borderStyle="single"
|
|
427
|
+
borderColor="gray"
|
|
428
|
+
borderLeft={false}
|
|
429
|
+
borderRight={false}
|
|
430
|
+
>
|
|
431
|
+
<Text color={isPlaceholder ? "gray" : "white"}>
|
|
432
|
+
{shouldShowCursor ? (
|
|
433
|
+
<>
|
|
434
|
+
{beforeCursor}
|
|
435
|
+
<Text backgroundColor="white" color="black">
|
|
436
|
+
{atCursor}
|
|
437
|
+
</Text>
|
|
438
|
+
{afterCursor}
|
|
439
|
+
</>
|
|
440
|
+
) : (
|
|
441
|
+
displayText
|
|
442
|
+
)}
|
|
443
|
+
</Text>
|
|
444
|
+
</Box>
|
|
445
|
+
<Box justifyContent="space-between">
|
|
446
|
+
<StatusLine
|
|
447
|
+
permissionMode={permissionMode}
|
|
448
|
+
isShellCommand={isShellCommand}
|
|
449
|
+
/>
|
|
450
|
+
<Notifications
|
|
451
|
+
latestTotalTokens={latestTotalTokens}
|
|
452
|
+
maxInputTokens={maxInputTokens}
|
|
453
|
+
showLoginHint={showLoginHint}
|
|
454
|
+
/>
|
|
455
|
+
</Box>
|
|
418
456
|
</Box>
|
|
419
|
-
|
|
420
|
-
)}
|
|
457
|
+
)}
|
|
421
458
|
</Box>
|
|
422
459
|
);
|
|
423
460
|
};
|
|
@@ -2,7 +2,6 @@ import React from "react";
|
|
|
2
2
|
import { Box, Text } from "ink";
|
|
3
3
|
import type { Message, MessageBlock } from "wave-agent-sdk";
|
|
4
4
|
import { MessageSource } from "wave-agent-sdk";
|
|
5
|
-
import { BangDisplay } from "./BangDisplay.js";
|
|
6
5
|
import { ToolDisplay } from "./ToolDisplay.js";
|
|
7
6
|
import { CompactDisplay } from "./CompactDisplay.js";
|
|
8
7
|
import { ReasoningDisplay } from "./ReasoningDisplay.js";
|
|
@@ -54,10 +53,6 @@ export const MessageBlockItem = ({
|
|
|
54
53
|
</Box>
|
|
55
54
|
)}
|
|
56
55
|
|
|
57
|
-
{block.type === "bang" && (
|
|
58
|
-
<BangDisplay block={block} isExpanded={isExpanded} />
|
|
59
|
-
)}
|
|
60
|
-
|
|
61
56
|
{block.type === "tool" && (
|
|
62
57
|
<ToolDisplay block={block} isExpanded={isExpanded} />
|
|
63
58
|
)}
|
|
@@ -47,7 +47,6 @@ export const MessageList = React.memo(
|
|
|
47
47
|
(b.stage === "running" ||
|
|
48
48
|
b.stage === "streaming" ||
|
|
49
49
|
b.stage === "start")) ||
|
|
50
|
-
(b.type === "bang" && b.stage === "running") ||
|
|
51
50
|
(b.type === "reasoning" && b.stage === "streaming") ||
|
|
52
51
|
(b.type === "text" && b.stage === "streaming");
|
|
53
52
|
|