wave-code 0.2.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/plugin/disable.d.ts +2 -1
- package/dist/commands/plugin/disable.d.ts.map +1 -1
- package/dist/commands/plugin/disable.js +3 -2
- package/dist/commands/plugin/enable.d.ts +2 -1
- package/dist/commands/plugin/enable.d.ts.map +1 -1
- package/dist/commands/plugin/enable.js +3 -2
- package/dist/commands/plugin/install.d.ts +2 -1
- package/dist/commands/plugin/install.d.ts.map +1 -1
- package/dist/commands/plugin/list.d.ts.map +1 -1
- package/dist/commands/plugin/list.js +15 -3
- package/dist/commands/plugin/marketplace.d.ts +3 -0
- package/dist/commands/plugin/marketplace.d.ts.map +1 -1
- package/dist/commands/plugin/marketplace.js +15 -1
- package/dist/commands/plugin/uninstall.d.ts +4 -0
- package/dist/commands/plugin/uninstall.d.ts.map +1 -0
- package/dist/commands/plugin/uninstall.js +29 -0
- package/dist/commands/plugin/update.d.ts +4 -0
- package/dist/commands/plugin/update.d.ts.map +1 -0
- package/dist/commands/plugin/update.js +15 -0
- package/dist/components/ChatInterface.d.ts.map +1 -1
- package/dist/components/ChatInterface.js +2 -2
- package/dist/components/CommandSelector.d.ts.map +1 -1
- package/dist/components/CommandSelector.js +9 -3
- package/dist/components/Confirmation.d.ts.map +1 -1
- package/dist/components/Confirmation.js +73 -20
- package/dist/components/DiffDisplay.d.ts +0 -1
- package/dist/components/DiffDisplay.d.ts.map +1 -1
- package/dist/components/DiffDisplay.js +38 -59
- package/dist/components/DiscoverView.d.ts +3 -0
- package/dist/components/DiscoverView.d.ts.map +1 -0
- package/dist/components/DiscoverView.js +25 -0
- package/dist/components/FileSelector.js +1 -1
- package/dist/components/HistorySearch.d.ts +8 -0
- package/dist/components/HistorySearch.d.ts.map +1 -0
- package/dist/components/HistorySearch.js +67 -0
- package/dist/components/InputBox.d.ts +1 -1
- package/dist/components/InputBox.d.ts.map +1 -1
- package/dist/components/InputBox.js +29 -19
- package/dist/components/InstalledView.d.ts +3 -0
- package/dist/components/InstalledView.d.ts.map +1 -0
- package/dist/components/InstalledView.js +30 -0
- package/dist/components/Markdown.d.ts.map +1 -1
- package/dist/components/Markdown.js +24 -10
- package/dist/components/MarketplaceAddForm.d.ts +3 -0
- package/dist/components/MarketplaceAddForm.d.ts.map +1 -0
- package/dist/components/MarketplaceAddForm.js +26 -0
- package/dist/components/MarketplaceDetail.d.ts +3 -0
- package/dist/components/MarketplaceDetail.d.ts.map +1 -0
- package/dist/components/MarketplaceDetail.js +38 -0
- package/dist/components/MarketplaceList.d.ts +9 -0
- package/dist/components/MarketplaceList.d.ts.map +1 -0
- package/dist/components/MarketplaceList.js +16 -0
- package/dist/components/MarketplaceView.d.ts +3 -0
- package/dist/components/MarketplaceView.d.ts.map +1 -0
- package/dist/components/MarketplaceView.js +28 -0
- package/dist/components/PlanDisplay.d.ts.map +1 -1
- package/dist/components/PlanDisplay.js +2 -2
- package/dist/components/PluginDetail.d.ts +3 -0
- package/dist/components/PluginDetail.d.ts.map +1 -0
- package/dist/components/PluginDetail.js +63 -0
- package/dist/components/PluginList.d.ts +14 -0
- package/dist/components/PluginList.d.ts.map +1 -0
- package/dist/components/PluginList.js +12 -0
- package/dist/components/PluginManagerShell.d.ts +5 -0
- package/dist/components/PluginManagerShell.d.ts.map +1 -0
- package/dist/components/PluginManagerShell.js +89 -0
- package/dist/components/PluginManagerTypes.d.ts +33 -0
- package/dist/components/PluginManagerTypes.d.ts.map +1 -0
- package/dist/components/PluginManagerTypes.js +1 -0
- package/dist/components/RewindCommand.d.ts +9 -0
- package/dist/components/RewindCommand.d.ts.map +1 -0
- package/dist/components/RewindCommand.js +42 -0
- package/dist/components/SessionSelector.d.ts +11 -0
- package/dist/components/SessionSelector.d.ts.map +1 -0
- package/dist/components/SessionSelector.js +38 -0
- package/dist/components/SubagentBlock.d.ts.map +1 -1
- package/dist/components/SubagentBlock.js +24 -1
- package/dist/components/TaskManager.d.ts +6 -0
- package/dist/components/TaskManager.d.ts.map +1 -0
- package/dist/components/TaskManager.js +114 -0
- package/dist/components/ToolResultDisplay.d.ts.map +1 -1
- package/dist/components/ToolResultDisplay.js +2 -1
- package/dist/contexts/PluginManagerContext.d.ts +4 -0
- package/dist/contexts/PluginManagerContext.d.ts.map +1 -0
- package/dist/contexts/PluginManagerContext.js +9 -0
- package/dist/contexts/useChat.d.ts +7 -4
- package/dist/contexts/useChat.d.ts.map +1 -1
- package/dist/contexts/useChat.js +37 -12
- package/dist/hooks/useInputManager.d.ts +8 -16
- package/dist/hooks/useInputManager.d.ts.map +1 -1
- package/dist/hooks/useInputManager.js +39 -55
- package/dist/hooks/usePluginManager.d.ts +3 -0
- package/dist/hooks/usePluginManager.d.ts.map +1 -0
- package/dist/hooks/usePluginManager.js +227 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +150 -177
- package/dist/managers/InputManager.d.ts +18 -26
- package/dist/managers/InputManager.d.ts.map +1 -1
- package/dist/managers/InputManager.js +93 -119
- package/dist/plugin-manager-cli.d.ts +6 -0
- package/dist/plugin-manager-cli.d.ts.map +1 -0
- package/dist/plugin-manager-cli.js +12 -0
- package/dist/session-selector-cli.d.ts +2 -0
- package/dist/session-selector-cli.d.ts.map +1 -0
- package/dist/session-selector-cli.js +25 -0
- package/package.json +9 -5
- package/src/commands/plugin/disable.ts +7 -3
- package/src/commands/plugin/enable.ts +7 -3
- package/src/commands/plugin/install.ts +2 -1
- package/src/commands/plugin/list.ts +21 -3
- package/src/commands/plugin/marketplace.ts +17 -1
- package/src/commands/plugin/uninstall.ts +39 -0
- package/src/commands/plugin/update.ts +19 -0
- package/src/components/ChatInterface.tsx +2 -1
- package/src/components/CommandSelector.tsx +10 -3
- package/src/components/Confirmation.tsx +115 -25
- package/src/components/DiffDisplay.tsx +60 -106
- package/src/components/DiscoverView.tsx +31 -0
- package/src/components/FileSelector.tsx +1 -1
- package/src/components/HistorySearch.tsx +148 -0
- package/src/components/InputBox.tsx +51 -34
- package/src/components/InstalledView.tsx +61 -0
- package/src/components/Markdown.tsx +44 -28
- package/src/components/MarketplaceAddForm.tsx +39 -0
- package/src/components/MarketplaceDetail.tsx +79 -0
- package/src/components/MarketplaceList.tsx +52 -0
- package/src/components/MarketplaceView.tsx +43 -0
- package/src/components/PlanDisplay.tsx +14 -19
- package/src/components/PluginDetail.tsx +147 -0
- package/src/components/PluginList.tsx +51 -0
- package/src/components/PluginManagerShell.tsx +189 -0
- package/src/components/PluginManagerTypes.ts +47 -0
- package/src/components/RewindCommand.tsx +114 -0
- package/src/components/SessionSelector.tsx +127 -0
- package/src/components/SubagentBlock.tsx +34 -1
- package/src/components/{BashShellManager.tsx → TaskManager.tsx} +79 -75
- package/src/components/ToolResultDisplay.tsx +6 -2
- package/src/contexts/PluginManagerContext.ts +15 -0
- package/src/contexts/useChat.tsx +51 -20
- package/src/hooks/useInputManager.ts +39 -71
- package/src/hooks/usePluginManager.ts +302 -0
- package/src/index.ts +241 -280
- package/src/managers/InputManager.ts +113 -162
- package/src/plugin-manager-cli.tsx +13 -0
- package/src/session-selector-cli.tsx +37 -0
- package/dist/components/BashHistorySelector.d.ts +0 -11
- package/dist/components/BashHistorySelector.d.ts.map +0 -1
- package/dist/components/BashHistorySelector.js +0 -93
- package/dist/components/BashShellManager.d.ts +0 -6
- package/dist/components/BashShellManager.d.ts.map +0 -1
- package/dist/components/BashShellManager.js +0 -116
- package/dist/hooks/usePagination.d.ts +0 -20
- package/dist/hooks/usePagination.d.ts.map +0 -1
- package/dist/hooks/usePagination.js +0 -168
- package/src/components/BashHistorySelector.tsx +0 -181
- package/src/hooks/usePagination.ts +0 -203
|
@@ -3,11 +3,12 @@ import {
|
|
|
3
3
|
ConfigurationService,
|
|
4
4
|
PluginManager,
|
|
5
5
|
PluginScopeManager,
|
|
6
|
+
Scope,
|
|
6
7
|
} from "wave-agent-sdk";
|
|
7
8
|
|
|
8
9
|
export async function installPluginCommand(argv: {
|
|
9
10
|
plugin: string;
|
|
10
|
-
scope?:
|
|
11
|
+
scope?: Scope;
|
|
11
12
|
}) {
|
|
12
13
|
const marketplaceService = new MarketplaceService();
|
|
13
14
|
const workdir = process.cwd();
|
|
@@ -1,9 +1,23 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
MarketplaceService,
|
|
3
|
+
ConfigurationService,
|
|
4
|
+
PluginScopeManager,
|
|
5
|
+
PluginManager,
|
|
6
|
+
} from "wave-agent-sdk";
|
|
2
7
|
|
|
3
8
|
export async function listPluginsCommand() {
|
|
4
|
-
const marketplaceService = new MarketplaceService();
|
|
5
9
|
const configurationService = new ConfigurationService();
|
|
10
|
+
const marketplaceService = new MarketplaceService();
|
|
6
11
|
const workdir = process.cwd();
|
|
12
|
+
const pluginManager = new PluginManager({
|
|
13
|
+
workdir,
|
|
14
|
+
configurationService,
|
|
15
|
+
});
|
|
16
|
+
const pluginScopeManager = new PluginScopeManager({
|
|
17
|
+
workdir,
|
|
18
|
+
configurationService,
|
|
19
|
+
pluginManager,
|
|
20
|
+
});
|
|
7
21
|
|
|
8
22
|
try {
|
|
9
23
|
const installedPlugins = await marketplaceService.getInstalledPlugins();
|
|
@@ -16,6 +30,7 @@ export async function listPluginsCommand() {
|
|
|
16
30
|
marketplace: string;
|
|
17
31
|
installed: boolean;
|
|
18
32
|
version?: string;
|
|
33
|
+
scope?: string;
|
|
19
34
|
}[] = [];
|
|
20
35
|
|
|
21
36
|
for (const m of marketplaces) {
|
|
@@ -24,6 +39,7 @@ export async function listPluginsCommand() {
|
|
|
24
39
|
marketplaceService.getMarketplacePath(m),
|
|
25
40
|
);
|
|
26
41
|
manifest.plugins.forEach((p) => {
|
|
42
|
+
const pluginId = `${p.name}@${m.name}`;
|
|
27
43
|
const installed = installedPlugins.plugins.find(
|
|
28
44
|
(ip) => ip.name === p.name && ip.marketplace === m.name,
|
|
29
45
|
);
|
|
@@ -32,6 +48,7 @@ export async function listPluginsCommand() {
|
|
|
32
48
|
marketplace: m.name,
|
|
33
49
|
installed: !!installed,
|
|
34
50
|
version: installed?.version,
|
|
51
|
+
scope: pluginScopeManager.findPluginScope(pluginId) || undefined,
|
|
35
52
|
});
|
|
36
53
|
});
|
|
37
54
|
} catch {
|
|
@@ -52,7 +69,8 @@ export async function listPluginsCommand() {
|
|
|
52
69
|
: "disabled"
|
|
53
70
|
: "not installed";
|
|
54
71
|
const versionStr = p.version ? ` v${p.version}` : "";
|
|
55
|
-
|
|
72
|
+
const scopeStr = p.scope ? ` (${p.scope})` : "";
|
|
73
|
+
console.log(`- ${pluginId}${versionStr}${scopeStr} [${status}]`);
|
|
56
74
|
});
|
|
57
75
|
}
|
|
58
76
|
process.exit(0);
|
|
@@ -42,7 +42,10 @@ export async function listMarketplacesCommand() {
|
|
|
42
42
|
} else {
|
|
43
43
|
sourceInfo = source.url + (source.ref ? `#${source.ref}` : "");
|
|
44
44
|
}
|
|
45
|
-
|
|
45
|
+
const builtinLabel = m.isBuiltin ? " [builtin]" : "";
|
|
46
|
+
console.log(
|
|
47
|
+
`- ${m.name}${builtinLabel}: ${sourceInfo} (${m.source.source})`,
|
|
48
|
+
);
|
|
46
49
|
});
|
|
47
50
|
}
|
|
48
51
|
process.exit(0);
|
|
@@ -53,6 +56,19 @@ export async function listMarketplacesCommand() {
|
|
|
53
56
|
}
|
|
54
57
|
}
|
|
55
58
|
|
|
59
|
+
export async function removeMarketplaceCommand(argv: { name: string }) {
|
|
60
|
+
const service = new MarketplaceService();
|
|
61
|
+
try {
|
|
62
|
+
await service.removeMarketplace(argv.name);
|
|
63
|
+
console.log(`Successfully removed marketplace: ${argv.name}`);
|
|
64
|
+
process.exit(0);
|
|
65
|
+
} catch (error) {
|
|
66
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
67
|
+
console.error(`Failed to remove marketplace: ${message}`);
|
|
68
|
+
process.exit(1);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
56
72
|
export async function updateMarketplaceCommand(argv: { name?: string }) {
|
|
57
73
|
const service = new MarketplaceService();
|
|
58
74
|
try {
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import {
|
|
2
|
+
MarketplaceService,
|
|
3
|
+
ConfigurationService,
|
|
4
|
+
PluginManager,
|
|
5
|
+
PluginScopeManager,
|
|
6
|
+
} from "wave-agent-sdk";
|
|
7
|
+
|
|
8
|
+
export async function uninstallPluginCommand(argv: { plugin: string }) {
|
|
9
|
+
const marketplaceService = new MarketplaceService();
|
|
10
|
+
const workdir = process.cwd();
|
|
11
|
+
|
|
12
|
+
try {
|
|
13
|
+
await marketplaceService.uninstallPlugin(argv.plugin, workdir);
|
|
14
|
+
console.log(`Successfully uninstalled plugin: ${argv.plugin}`);
|
|
15
|
+
|
|
16
|
+
const configurationService = new ConfigurationService();
|
|
17
|
+
const pluginManager = new PluginManager({ workdir });
|
|
18
|
+
const scopeManager = new PluginScopeManager({
|
|
19
|
+
workdir,
|
|
20
|
+
configurationService,
|
|
21
|
+
pluginManager,
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
await scopeManager.removePluginFromAllScopes(argv.plugin);
|
|
26
|
+
console.log(`Cleaned up plugin configuration from all scopes`);
|
|
27
|
+
} catch (error) {
|
|
28
|
+
console.warn(
|
|
29
|
+
`Warning: Could not clean up all plugin configurations: ${error instanceof Error ? error.message : String(error)}`,
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
process.exit(0);
|
|
34
|
+
} catch (error) {
|
|
35
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
36
|
+
console.error(`Failed to uninstall plugin: ${message}`);
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { MarketplaceService } from "wave-agent-sdk";
|
|
2
|
+
|
|
3
|
+
export async function updatePluginCommand(argv: { plugin: string }) {
|
|
4
|
+
const marketplaceService = new MarketplaceService();
|
|
5
|
+
|
|
6
|
+
try {
|
|
7
|
+
const updated = await marketplaceService.updatePlugin(argv.plugin);
|
|
8
|
+
console.log(
|
|
9
|
+
`Successfully updated plugin: ${updated.name} v${updated.version} from ${updated.marketplace}`,
|
|
10
|
+
);
|
|
11
|
+
console.log(`Cache path: ${updated.cachePath}`);
|
|
12
|
+
|
|
13
|
+
process.exit(0);
|
|
14
|
+
} catch (error) {
|
|
15
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
16
|
+
console.error(`Failed to update plugin: ${message}`);
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -27,6 +27,7 @@ export const ChatInterface: React.FC = () => {
|
|
|
27
27
|
confirmingTool,
|
|
28
28
|
handleConfirmationDecision,
|
|
29
29
|
handleConfirmationCancel,
|
|
30
|
+
rewindId,
|
|
30
31
|
} = useChat();
|
|
31
32
|
|
|
32
33
|
if (!sessionId) return null;
|
|
@@ -40,7 +41,7 @@ export const ChatInterface: React.FC = () => {
|
|
|
40
41
|
isCompressing={isCompressing}
|
|
41
42
|
latestTotalTokens={latestTotalTokens}
|
|
42
43
|
isExpanded={isExpanded}
|
|
43
|
-
key={String(isExpanded) + sessionId}
|
|
44
|
+
key={String(isExpanded) + sessionId + rewindId}
|
|
44
45
|
/>
|
|
45
46
|
|
|
46
47
|
{isConfirmationVisible && (
|
|
@@ -4,9 +4,9 @@ import type { SlashCommand } from "wave-agent-sdk";
|
|
|
4
4
|
|
|
5
5
|
const AVAILABLE_COMMANDS: SlashCommand[] = [
|
|
6
6
|
{
|
|
7
|
-
id: "
|
|
8
|
-
name: "
|
|
9
|
-
description: "View and manage background
|
|
7
|
+
id: "tasks",
|
|
8
|
+
name: "tasks",
|
|
9
|
+
description: "View and manage background tasks (shells and subagents)",
|
|
10
10
|
handler: () => {}, // Handler here won't be used, actual processing is in the hook
|
|
11
11
|
},
|
|
12
12
|
{
|
|
@@ -15,6 +15,13 @@ const AVAILABLE_COMMANDS: SlashCommand[] = [
|
|
|
15
15
|
description: "View and manage MCP servers",
|
|
16
16
|
handler: () => {}, // Handler here won't be used, actual processing is in the hook
|
|
17
17
|
},
|
|
18
|
+
{
|
|
19
|
+
id: "rewind",
|
|
20
|
+
name: "rewind",
|
|
21
|
+
description:
|
|
22
|
+
"Revert conversation and file changes to a previous checkpoint",
|
|
23
|
+
handler: () => {}, // Handler here won't be used, actual processing is in the hook
|
|
24
|
+
},
|
|
18
25
|
];
|
|
19
26
|
|
|
20
27
|
export interface CommandSelectorProps {
|
|
@@ -65,6 +65,7 @@ export interface ConfirmationProps {
|
|
|
65
65
|
interface ConfirmationState {
|
|
66
66
|
selectedOption: "allow" | "auto" | "alternative";
|
|
67
67
|
alternativeText: string;
|
|
68
|
+
alternativeCursorPosition: number;
|
|
68
69
|
hasUserInput: boolean; // to hide placeholder
|
|
69
70
|
}
|
|
70
71
|
|
|
@@ -81,6 +82,7 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
|
|
|
81
82
|
const [state, setState] = useState<ConfirmationState>({
|
|
82
83
|
selectedOption: "allow",
|
|
83
84
|
alternativeText: "",
|
|
85
|
+
alternativeCursorPosition: 0,
|
|
84
86
|
hasUserInput: false,
|
|
85
87
|
});
|
|
86
88
|
|
|
@@ -92,6 +94,7 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
|
|
|
92
94
|
>(new Set());
|
|
93
95
|
const [userAnswers, setUserAnswers] = useState<Record<string, string>>({});
|
|
94
96
|
const [otherText, setOtherText] = useState("");
|
|
97
|
+
const [otherCursorPosition, setOtherCursorPosition] = useState(0);
|
|
95
98
|
|
|
96
99
|
const questions =
|
|
97
100
|
(toolInput as unknown as AskUserQuestionInput)?.questions || [];
|
|
@@ -159,6 +162,7 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
|
|
|
159
162
|
setSelectedOptionIndex(0);
|
|
160
163
|
setSelectedOptionIndices(new Set());
|
|
161
164
|
setOtherText("");
|
|
165
|
+
setOtherCursorPosition(0);
|
|
162
166
|
} else {
|
|
163
167
|
// All questions answered
|
|
164
168
|
onDecision({
|
|
@@ -207,10 +211,38 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
|
|
|
207
211
|
}
|
|
208
212
|
|
|
209
213
|
if (isOtherFocused) {
|
|
214
|
+
if (key.leftArrow) {
|
|
215
|
+
setOtherCursorPosition((prev) => Math.max(0, prev - 1));
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
if (key.rightArrow) {
|
|
219
|
+
setOtherCursorPosition((prev) =>
|
|
220
|
+
Math.min(otherText.length, prev + 1),
|
|
221
|
+
);
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
210
224
|
if (key.backspace || key.delete) {
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
225
|
+
if (otherCursorPosition > 0) {
|
|
226
|
+
setOtherText((prev) => {
|
|
227
|
+
const next =
|
|
228
|
+
prev.slice(0, otherCursorPosition - 1) +
|
|
229
|
+
prev.slice(otherCursorPosition);
|
|
230
|
+
return next;
|
|
231
|
+
});
|
|
232
|
+
setOtherCursorPosition((prev) => prev - 1);
|
|
233
|
+
}
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
if (input && !key.ctrl && !key.meta) {
|
|
237
|
+
setOtherText((prev) => {
|
|
238
|
+
const next =
|
|
239
|
+
prev.slice(0, otherCursorPosition) +
|
|
240
|
+
input +
|
|
241
|
+
prev.slice(otherCursorPosition);
|
|
242
|
+
return next;
|
|
243
|
+
});
|
|
244
|
+
setOtherCursorPosition((prev) => prev + input.length);
|
|
245
|
+
return;
|
|
214
246
|
}
|
|
215
247
|
return;
|
|
216
248
|
}
|
|
@@ -229,7 +261,7 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
|
|
|
229
261
|
} else if (state.selectedOption === "auto") {
|
|
230
262
|
if (toolName === BASH_TOOL_NAME) {
|
|
231
263
|
const rule = suggestedPrefix
|
|
232
|
-
? `Bash(${suggestedPrefix}
|
|
264
|
+
? `Bash(${suggestedPrefix}*)`
|
|
233
265
|
: `Bash(${toolInput?.command})`;
|
|
234
266
|
onDecision({
|
|
235
267
|
behavior: "allow",
|
|
@@ -253,6 +285,29 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
|
|
|
253
285
|
return;
|
|
254
286
|
}
|
|
255
287
|
|
|
288
|
+
if (state.selectedOption === "alternative") {
|
|
289
|
+
if (key.leftArrow) {
|
|
290
|
+
setState((prev) => ({
|
|
291
|
+
...prev,
|
|
292
|
+
alternativeCursorPosition: Math.max(
|
|
293
|
+
0,
|
|
294
|
+
prev.alternativeCursorPosition - 1,
|
|
295
|
+
),
|
|
296
|
+
}));
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
if (key.rightArrow) {
|
|
300
|
+
setState((prev) => ({
|
|
301
|
+
...prev,
|
|
302
|
+
alternativeCursorPosition: Math.min(
|
|
303
|
+
prev.alternativeText.length,
|
|
304
|
+
prev.alternativeCursorPosition + 1,
|
|
305
|
+
),
|
|
306
|
+
}));
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
256
311
|
// Handle arrow keys for navigation
|
|
257
312
|
if (key.upArrow) {
|
|
258
313
|
setState((prev) => {
|
|
@@ -287,27 +342,42 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
|
|
|
287
342
|
// Handle text input for alternative option
|
|
288
343
|
if (input && !key.ctrl && !key.meta && !("alt" in key && key.alt)) {
|
|
289
344
|
// Focus on alternative option when user starts typing
|
|
290
|
-
setState((prev) => ({
|
|
291
|
-
selectedOption: "alternative",
|
|
292
|
-
alternativeText: prev.alternativeText + input,
|
|
293
|
-
hasUserInput: true,
|
|
294
|
-
}));
|
|
295
|
-
return;
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
// Handle backspace and delete (same behavior - delete one character)
|
|
299
|
-
if (key.backspace || key.delete) {
|
|
300
345
|
setState((prev) => {
|
|
301
|
-
const
|
|
346
|
+
const nextText =
|
|
347
|
+
prev.alternativeText.slice(0, prev.alternativeCursorPosition) +
|
|
348
|
+
input +
|
|
349
|
+
prev.alternativeText.slice(prev.alternativeCursorPosition);
|
|
302
350
|
return {
|
|
303
351
|
...prev,
|
|
304
352
|
selectedOption: "alternative",
|
|
305
|
-
alternativeText:
|
|
306
|
-
|
|
353
|
+
alternativeText: nextText,
|
|
354
|
+
alternativeCursorPosition:
|
|
355
|
+
prev.alternativeCursorPosition + input.length,
|
|
356
|
+
hasUserInput: true,
|
|
307
357
|
};
|
|
308
358
|
});
|
|
309
359
|
return;
|
|
310
360
|
}
|
|
361
|
+
|
|
362
|
+
// Handle backspace and delete
|
|
363
|
+
if (key.backspace || key.delete) {
|
|
364
|
+
setState((prev) => {
|
|
365
|
+
if (prev.alternativeCursorPosition > 0) {
|
|
366
|
+
const nextText =
|
|
367
|
+
prev.alternativeText.slice(0, prev.alternativeCursorPosition - 1) +
|
|
368
|
+
prev.alternativeText.slice(prev.alternativeCursorPosition);
|
|
369
|
+
return {
|
|
370
|
+
...prev,
|
|
371
|
+
selectedOption: "alternative",
|
|
372
|
+
alternativeText: nextText,
|
|
373
|
+
alternativeCursorPosition: prev.alternativeCursorPosition - 1,
|
|
374
|
+
hasUserInput: nextText.length > 0,
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
return prev;
|
|
378
|
+
});
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
311
381
|
});
|
|
312
382
|
|
|
313
383
|
const placeholderText = "Type here to tell Wave what to do differently";
|
|
@@ -329,11 +399,7 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
|
|
|
329
399
|
</Text>
|
|
330
400
|
<Text color="yellow">{getActionDescription(toolName, toolInput)}</Text>
|
|
331
401
|
|
|
332
|
-
<DiffDisplay
|
|
333
|
-
toolName={toolName}
|
|
334
|
-
parameters={JSON.stringify(toolInput)}
|
|
335
|
-
isExpanded={isExpanded}
|
|
336
|
-
/>
|
|
402
|
+
<DiffDisplay toolName={toolName} parameters={JSON.stringify(toolInput)} />
|
|
337
403
|
|
|
338
404
|
{toolName === ASK_USER_QUESTION_TOOL_NAME &&
|
|
339
405
|
currentQuestion &&
|
|
@@ -383,7 +449,15 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
|
|
|
383
449
|
{isOther && isSelected && (
|
|
384
450
|
<Text>
|
|
385
451
|
:{" "}
|
|
386
|
-
{otherText
|
|
452
|
+
{otherText ? (
|
|
453
|
+
<>
|
|
454
|
+
{otherText.slice(0, otherCursorPosition)}
|
|
455
|
+
<Text backgroundColor="white" color="black">
|
|
456
|
+
{otherText[otherCursorPosition] || " "}
|
|
457
|
+
</Text>
|
|
458
|
+
{otherText.slice(otherCursorPosition + 1)}
|
|
459
|
+
</>
|
|
460
|
+
) : (
|
|
387
461
|
<Text color="gray" dimColor>
|
|
388
462
|
[Type your answer...]
|
|
389
463
|
</Text>
|
|
@@ -474,8 +548,24 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
|
|
|
474
548
|
</Text>
|
|
475
549
|
) : (
|
|
476
550
|
<Text>
|
|
477
|
-
{state.alternativeText
|
|
478
|
-
|
|
551
|
+
{state.alternativeText ? (
|
|
552
|
+
<>
|
|
553
|
+
{state.alternativeText.slice(
|
|
554
|
+
0,
|
|
555
|
+
state.alternativeCursorPosition,
|
|
556
|
+
)}
|
|
557
|
+
<Text backgroundColor="white" color="black">
|
|
558
|
+
{state.alternativeText[
|
|
559
|
+
state.alternativeCursorPosition
|
|
560
|
+
] || " "}
|
|
561
|
+
</Text>
|
|
562
|
+
{state.alternativeText.slice(
|
|
563
|
+
state.alternativeCursorPosition + 1,
|
|
564
|
+
)}
|
|
565
|
+
</>
|
|
566
|
+
) : (
|
|
567
|
+
"Type here to tell Wave what to do differently"
|
|
568
|
+
)}
|
|
479
569
|
</Text>
|
|
480
570
|
)}
|
|
481
571
|
</Text>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { useMemo } from "react";
|
|
2
|
-
import { Box, Text
|
|
2
|
+
import { Box, Text } from "ink";
|
|
3
3
|
import {
|
|
4
4
|
WRITE_TOOL_NAME,
|
|
5
5
|
EDIT_TOOL_NAME,
|
|
@@ -11,19 +11,12 @@ import { diffLines, diffWords } from "diff";
|
|
|
11
11
|
interface DiffDisplayProps {
|
|
12
12
|
toolName?: string;
|
|
13
13
|
parameters?: string;
|
|
14
|
-
isExpanded?: boolean;
|
|
15
14
|
}
|
|
16
15
|
|
|
17
16
|
export const DiffDisplay: React.FC<DiffDisplayProps> = ({
|
|
18
17
|
toolName,
|
|
19
18
|
parameters,
|
|
20
|
-
isExpanded = false,
|
|
21
19
|
}) => {
|
|
22
|
-
const { stdout } = useStdout();
|
|
23
|
-
const maxHeight = useMemo(() => {
|
|
24
|
-
return Math.max(5, (stdout?.rows || 24) - 20);
|
|
25
|
-
}, [stdout?.rows]);
|
|
26
|
-
|
|
27
20
|
const showDiff =
|
|
28
21
|
toolName &&
|
|
29
22
|
[WRITE_TOOL_NAME, EDIT_TOOL_NAME, MULTI_EDIT_TOOL_NAME].includes(toolName);
|
|
@@ -107,7 +100,7 @@ export const DiffDisplay: React.FC<DiffDisplayProps> = ({
|
|
|
107
100
|
}
|
|
108
101
|
};
|
|
109
102
|
|
|
110
|
-
// Render expanded diff display
|
|
103
|
+
// Render expanded diff display
|
|
111
104
|
const renderExpandedDiff = () => {
|
|
112
105
|
try {
|
|
113
106
|
if (changes.length === 0) return null;
|
|
@@ -122,9 +115,8 @@ export const DiffDisplay: React.FC<DiffDisplayProps> = ({
|
|
|
122
115
|
change.newContent || "",
|
|
123
116
|
);
|
|
124
117
|
|
|
118
|
+
// Process line diffs
|
|
125
119
|
const diffElements: React.ReactNode[] = [];
|
|
126
|
-
|
|
127
|
-
// Process line diffs and apply word-level diff to changed lines
|
|
128
120
|
lineDiffs.forEach((part, partIndex) => {
|
|
129
121
|
if (part.added) {
|
|
130
122
|
const lines = part.value
|
|
@@ -175,70 +167,46 @@ export const DiffDisplay: React.FC<DiffDisplayProps> = ({
|
|
|
175
167
|
}
|
|
176
168
|
});
|
|
177
169
|
|
|
178
|
-
//
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
const
|
|
189
|
-
|
|
190
|
-
const isCurrentRemoved =
|
|
191
|
-
typeof currentKey === "string" && currentKey.includes("remove-");
|
|
192
|
-
const isNextAdded =
|
|
193
|
-
typeof nextKey === "string" && nextKey.includes("add-");
|
|
170
|
+
// If it's a single line change (one removed, one added), use word-level diff
|
|
171
|
+
if (
|
|
172
|
+
diffElements.length === 2 &&
|
|
173
|
+
React.isValidElement(diffElements[0]) &&
|
|
174
|
+
React.isValidElement(diffElements[1]) &&
|
|
175
|
+
typeof diffElements[0].key === "string" &&
|
|
176
|
+
diffElements[0].key.includes("remove-") &&
|
|
177
|
+
typeof diffElements[1].key === "string" &&
|
|
178
|
+
diffElements[1].key.includes("add-")
|
|
179
|
+
) {
|
|
180
|
+
const removedText = extractTextFromElement(diffElements[0]);
|
|
181
|
+
const addedText = extractTextFromElement(diffElements[1]);
|
|
194
182
|
|
|
195
|
-
if (
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
// Extract the text content from the removed and added lines
|
|
202
|
-
const removedText = extractTextFromElement(current);
|
|
203
|
-
const addedText = extractTextFromElement(next);
|
|
204
|
-
|
|
205
|
-
if (removedText && addedText) {
|
|
206
|
-
// Apply word-level diff
|
|
207
|
-
const { removedParts, addedParts } = renderWordLevelDiff(
|
|
208
|
-
removedText,
|
|
209
|
-
addedText,
|
|
210
|
-
`word-${changeIndex}-${i}`,
|
|
211
|
-
);
|
|
212
|
-
|
|
213
|
-
allElements.push(
|
|
214
|
-
<Box
|
|
215
|
-
key={`word-diff-removed-${changeIndex}-${i}`}
|
|
216
|
-
flexDirection="row"
|
|
217
|
-
>
|
|
218
|
-
<Text color="red">-</Text>
|
|
219
|
-
{removedParts}
|
|
220
|
-
</Box>,
|
|
221
|
-
);
|
|
222
|
-
allElements.push(
|
|
223
|
-
<Box
|
|
224
|
-
key={`word-diff-added-${changeIndex}-${i}`}
|
|
225
|
-
flexDirection="row"
|
|
226
|
-
>
|
|
227
|
-
<Text color="green">+</Text>
|
|
228
|
-
{addedParts}
|
|
229
|
-
</Box>,
|
|
230
|
-
);
|
|
183
|
+
if (removedText && addedText) {
|
|
184
|
+
const { removedParts, addedParts } = renderWordLevelDiff(
|
|
185
|
+
removedText,
|
|
186
|
+
addedText,
|
|
187
|
+
`word-${changeIndex}`,
|
|
188
|
+
);
|
|
231
189
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
190
|
+
allElements.push(
|
|
191
|
+
<Box
|
|
192
|
+
key={`word-diff-removed-${changeIndex}`}
|
|
193
|
+
flexDirection="row"
|
|
194
|
+
>
|
|
195
|
+
<Text color="red">-</Text>
|
|
196
|
+
{removedParts}
|
|
197
|
+
</Box>,
|
|
198
|
+
);
|
|
199
|
+
allElements.push(
|
|
200
|
+
<Box key={`word-diff-added-${changeIndex}`} flexDirection="row">
|
|
201
|
+
<Text color="green">+</Text>
|
|
202
|
+
{addedParts}
|
|
203
|
+
</Box>,
|
|
204
|
+
);
|
|
238
205
|
} else {
|
|
239
|
-
allElements.push(
|
|
240
|
-
i += 1;
|
|
206
|
+
allElements.push(...diffElements);
|
|
241
207
|
}
|
|
208
|
+
} else {
|
|
209
|
+
allElements.push(...diffElements);
|
|
242
210
|
}
|
|
243
211
|
} catch (error) {
|
|
244
212
|
console.warn(
|
|
@@ -255,21 +223,7 @@ export const DiffDisplay: React.FC<DiffDisplayProps> = ({
|
|
|
255
223
|
}
|
|
256
224
|
});
|
|
257
225
|
|
|
258
|
-
|
|
259
|
-
const displayElements = isTruncated
|
|
260
|
-
? allElements.slice(0, maxHeight - 1)
|
|
261
|
-
: allElements;
|
|
262
|
-
|
|
263
|
-
return (
|
|
264
|
-
<Box flexDirection="column">
|
|
265
|
-
{displayElements}
|
|
266
|
-
{isTruncated && (
|
|
267
|
-
<Text color="yellow" dimColor>
|
|
268
|
-
... (truncated {allElements.length - (maxHeight - 1)} more lines)
|
|
269
|
-
</Text>
|
|
270
|
-
)}
|
|
271
|
-
</Box>
|
|
272
|
-
);
|
|
226
|
+
return <Box flexDirection="column">{allElements}</Box>;
|
|
273
227
|
} catch (error) {
|
|
274
228
|
console.warn("Error rendering expanded diff:", error);
|
|
275
229
|
return (
|
|
@@ -280,26 +234,6 @@ export const DiffDisplay: React.FC<DiffDisplayProps> = ({
|
|
|
280
234
|
}
|
|
281
235
|
};
|
|
282
236
|
|
|
283
|
-
// Helper function to extract text content from a React element
|
|
284
|
-
const extractTextFromElement = (element: React.ReactNode): string | null => {
|
|
285
|
-
if (!React.isValidElement(element)) return null;
|
|
286
|
-
|
|
287
|
-
// Navigate through Box -> Text structure
|
|
288
|
-
const children = (
|
|
289
|
-
element.props as unknown as { children?: React.ReactNode[] }
|
|
290
|
-
).children;
|
|
291
|
-
if (Array.isArray(children) && children.length >= 2) {
|
|
292
|
-
const textElement = children[1]; // Second child should be the Text with content
|
|
293
|
-
if (
|
|
294
|
-
React.isValidElement(textElement) &&
|
|
295
|
-
(textElement.props as unknown as { children?: string }).children
|
|
296
|
-
) {
|
|
297
|
-
return (textElement.props as unknown as { children: string }).children;
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
return null;
|
|
301
|
-
};
|
|
302
|
-
|
|
303
237
|
// Don't render anything if no diff should be shown
|
|
304
238
|
if (!showDiff) {
|
|
305
239
|
return null;
|
|
@@ -316,3 +250,23 @@ export const DiffDisplay: React.FC<DiffDisplayProps> = ({
|
|
|
316
250
|
</Box>
|
|
317
251
|
);
|
|
318
252
|
};
|
|
253
|
+
|
|
254
|
+
// Helper function to extract text content from a React element
|
|
255
|
+
const extractTextFromElement = (element: React.ReactNode): string | null => {
|
|
256
|
+
if (!React.isValidElement(element)) return null;
|
|
257
|
+
|
|
258
|
+
// Navigate through Box -> Text structure
|
|
259
|
+
const children = (
|
|
260
|
+
element.props as unknown as { children?: React.ReactNode[] }
|
|
261
|
+
).children;
|
|
262
|
+
if (Array.isArray(children) && children.length >= 2) {
|
|
263
|
+
const textElement = children[1]; // Second child should be the Text with content
|
|
264
|
+
if (
|
|
265
|
+
React.isValidElement(textElement) &&
|
|
266
|
+
(textElement.props as unknown as { children?: string }).children
|
|
267
|
+
) {
|
|
268
|
+
return (textElement.props as unknown as { children: string }).children;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
return null;
|
|
272
|
+
};
|