wave-code 0.10.1 → 0.10.3
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/acp/agent.d.ts.map +1 -1
- package/dist/acp/agent.js +125 -82
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +2 -2
- package/dist/components/App.d.ts.map +1 -1
- package/dist/components/App.js +4 -4
- package/dist/components/DiffDisplay.d.ts.map +1 -1
- package/dist/components/DiffDisplay.js +23 -2
- package/dist/contexts/useChat.d.ts.map +1 -1
- package/dist/contexts/useChat.js +21 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +15 -0
- package/dist/print-cli.d.ts.map +1 -1
- package/dist/print-cli.js +2 -2
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/highlightUtils.d.ts.map +1 -1
- package/dist/utils/highlightUtils.js +8 -0
- package/dist/utils/toolParameterTransforms.d.ts +1 -5
- package/dist/utils/toolParameterTransforms.d.ts.map +1 -1
- package/dist/utils/toolParameterTransforms.js +0 -14
- package/dist/utils/worktree.js +2 -2
- package/package.json +2 -2
- package/src/acp/agent.ts +170 -81
- package/src/cli.tsx +2 -0
- package/src/components/App.tsx +4 -0
- package/src/components/DiffDisplay.tsx +27 -2
- package/src/contexts/useChat.tsx +29 -4
- package/src/index.ts +17 -2
- package/src/print-cli.ts +3 -1
- package/src/types.ts +2 -0
- package/src/utils/highlightUtils.ts +8 -0
- package/src/utils/toolParameterTransforms.ts +1 -23
- package/src/utils/worktree.ts +2 -2
package/dist/print-cli.js
CHANGED
|
@@ -14,7 +14,7 @@ function displayTimingInfo(startTime, showStats) {
|
|
|
14
14
|
}
|
|
15
15
|
export async function startPrintCli(options) {
|
|
16
16
|
const startTime = new Date();
|
|
17
|
-
const { restoreSessionId, continueLastSession, message, showStats = false, bypassPermissions, pluginDirs, tools, worktreeSession, workdir, model, } = options;
|
|
17
|
+
const { restoreSessionId, continueLastSession, message, showStats = false, bypassPermissions, permissionMode, pluginDirs, tools, worktreeSession, workdir, model, } = options;
|
|
18
18
|
if ((!message || message.trim() === "") &&
|
|
19
19
|
!continueLastSession &&
|
|
20
20
|
!restoreSessionId) {
|
|
@@ -96,7 +96,7 @@ export async function startPrintCli(options) {
|
|
|
96
96
|
callbacks,
|
|
97
97
|
restoreSessionId,
|
|
98
98
|
continueLastSession,
|
|
99
|
-
permissionMode: bypassPermissions ? "bypassPermissions" : undefined,
|
|
99
|
+
permissionMode: permissionMode || (bypassPermissions ? "bypassPermissions" : undefined),
|
|
100
100
|
plugins: pluginDirs?.map((path) => ({ type: "local", path })),
|
|
101
101
|
tools,
|
|
102
102
|
workdir,
|
package/dist/types.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { WorktreeSession } from "./utils/worktree.js";
|
|
2
|
+
import { PermissionMode } from "wave-agent-sdk";
|
|
2
3
|
export interface BaseAppProps {
|
|
3
4
|
bypassPermissions?: boolean;
|
|
5
|
+
permissionMode?: PermissionMode;
|
|
4
6
|
pluginDirs?: string[];
|
|
5
7
|
tools?: string[];
|
|
6
8
|
worktreeSession?: WorktreeSession;
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAEhD,MAAM,WAAW,YAAY;IAC3B,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"highlightUtils.d.ts","sourceRoot":"","sources":["../../src/utils/highlightUtils.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"highlightUtils.d.ts","sourceRoot":"","sources":["../../src/utils/highlightUtils.ts"],"names":[],"mappings":"AAqEA,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAsCvE"}
|
|
@@ -45,6 +45,14 @@ function nodeToAnsi(node) {
|
|
|
45
45
|
const classes = node.getAttribute("class")?.split(/\s+/) || [];
|
|
46
46
|
for (const className of classes) {
|
|
47
47
|
if (theme[className]) {
|
|
48
|
+
// If content has newlines, split it and apply style to each line
|
|
49
|
+
// to ensure ANSI codes are correctly applied when splitting the final string by lines.
|
|
50
|
+
if (content.includes("\n")) {
|
|
51
|
+
return content
|
|
52
|
+
.split("\n")
|
|
53
|
+
.map((line) => theme[className](line))
|
|
54
|
+
.join("\n");
|
|
55
|
+
}
|
|
48
56
|
return theme[className](content);
|
|
49
57
|
}
|
|
50
58
|
}
|
|
@@ -2,11 +2,7 @@
|
|
|
2
2
|
* Tool parameter transformation utilities for UI rendering
|
|
3
3
|
* Forces type judgment based on tool name using type assertions
|
|
4
4
|
*/
|
|
5
|
-
import { type Change, type
|
|
6
|
-
/**
|
|
7
|
-
* Transform Write tool parameters to changes
|
|
8
|
-
*/
|
|
9
|
-
export declare function transformWriteParameters(parameters: WriteToolParameters): Change[];
|
|
5
|
+
import { type Change, type EditToolParameters } from "wave-agent-sdk";
|
|
10
6
|
/**
|
|
11
7
|
* Transform Edit tool parameters to changes
|
|
12
8
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toolParameterTransforms.d.ts","sourceRoot":"","sources":["../../src/utils/toolParameterTransforms.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,
|
|
1
|
+
{"version":3,"file":"toolParameterTransforms.d.ts","sourceRoot":"","sources":["../../src/utils/toolParameterTransforms.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,KAAK,MAAM,EAAE,KAAK,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAmBtE;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,UAAU,EAAE,kBAAkB,GAC7B,MAAM,EAAE,CAOV;AAED;;;GAGG;AACH,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,eAAe,CAAC,EAAE,MAAM,GACvB,MAAM,EAAE,CA0BV"}
|
|
@@ -18,17 +18,6 @@ function parseToolParameters(parameters) {
|
|
|
18
18
|
return {};
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
|
-
/**
|
|
22
|
-
* Transform Write tool parameters to changes
|
|
23
|
-
*/
|
|
24
|
-
export function transformWriteParameters(parameters) {
|
|
25
|
-
return [
|
|
26
|
-
{
|
|
27
|
-
oldContent: "", // No previous content for write operations
|
|
28
|
-
newContent: parameters.content,
|
|
29
|
-
},
|
|
30
|
-
];
|
|
31
|
-
}
|
|
32
21
|
/**
|
|
33
22
|
* Transform Edit tool parameters to changes
|
|
34
23
|
*/
|
|
@@ -52,9 +41,6 @@ export function transformToolBlockToChanges(toolName, parameters, startLineNumbe
|
|
|
52
41
|
const parsedParams = parseToolParameters(parameters);
|
|
53
42
|
let changes = [];
|
|
54
43
|
switch (toolName) {
|
|
55
|
-
case "Write":
|
|
56
|
-
changes = transformWriteParameters(parsedParams);
|
|
57
|
-
break;
|
|
58
44
|
case "Edit":
|
|
59
45
|
changes = transformEditParameters(parsedParams);
|
|
60
46
|
break;
|
package/dist/utils/worktree.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { execSync } from "node:child_process";
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import * as fs from "node:fs";
|
|
4
|
-
import {
|
|
4
|
+
import { getDefaultRemoteBranch, getGitMainRepoRoot } from "wave-agent-sdk";
|
|
5
5
|
export const WORKTREE_DIR = ".wave/worktrees";
|
|
6
6
|
/**
|
|
7
7
|
* Create a new git worktree
|
|
@@ -10,7 +10,7 @@ export const WORKTREE_DIR = ".wave/worktrees";
|
|
|
10
10
|
* @returns Worktree session details
|
|
11
11
|
*/
|
|
12
12
|
export function createWorktree(name, cwd) {
|
|
13
|
-
const repoRoot =
|
|
13
|
+
const repoRoot = getGitMainRepoRoot(cwd);
|
|
14
14
|
const worktreePath = path.join(repoRoot, WORKTREE_DIR, name);
|
|
15
15
|
const branchName = `worktree-${name}`;
|
|
16
16
|
const baseBranch = getDefaultRemoteBranch(cwd);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wave-code",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.3",
|
|
4
4
|
"description": "CLI-based code assistant powered by AI, built with React and Ink",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"yargs": "^17.7.2",
|
|
42
42
|
"@agentclientprotocol/sdk": "0.16.1",
|
|
43
43
|
"zod": "^3.23.8",
|
|
44
|
-
"wave-agent-sdk": "0.10.
|
|
44
|
+
"wave-agent-sdk": "0.10.3"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"@types/react": "^19.1.8",
|
package/src/acp/agent.ts
CHANGED
|
@@ -6,7 +6,9 @@ import {
|
|
|
6
6
|
AgentToolBlockUpdateParams,
|
|
7
7
|
Task,
|
|
8
8
|
listSessions as listWaveSessions,
|
|
9
|
+
listAllSessions as listAllWaveSessions,
|
|
9
10
|
deleteSession as deleteWaveSession,
|
|
11
|
+
truncateContent,
|
|
10
12
|
} from "wave-agent-sdk";
|
|
11
13
|
import * as fs from "node:fs/promises";
|
|
12
14
|
import * as path from "node:path";
|
|
@@ -137,6 +139,7 @@ export class WaveAcpAgent implements AcpAgent {
|
|
|
137
139
|
const agent = await WaveAgent.create({
|
|
138
140
|
workdir: cwd,
|
|
139
141
|
restoreSessionId: sessionId,
|
|
142
|
+
stream: false,
|
|
140
143
|
canUseTool: (context) => {
|
|
141
144
|
if (!agentRef.instance) {
|
|
142
145
|
throw new Error("Agent instance not yet initialized");
|
|
@@ -170,19 +173,11 @@ export class WaveAcpAgent implements AcpAgent {
|
|
|
170
173
|
// Update the callbacks object with the correct sessionId
|
|
171
174
|
Object.assign(callbacks, this.createCallbacks(actualSessionId));
|
|
172
175
|
|
|
173
|
-
return agent;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
async newSession(params: NewSessionRequest): Promise<NewSessionResponse> {
|
|
177
|
-
const { cwd } = params;
|
|
178
|
-
logger.info(`Creating new session in ${cwd}`);
|
|
179
|
-
const agent = await this.createAgent(undefined, cwd);
|
|
180
|
-
logger.info(`New session created with ID: ${agent.sessionId}`);
|
|
181
|
-
|
|
182
176
|
// Send initial available commands after agent creation
|
|
177
|
+
// Use setImmediate to ensure the client receives the session response before the update
|
|
183
178
|
setImmediate(() => {
|
|
184
179
|
this.connection.sessionUpdate({
|
|
185
|
-
sessionId:
|
|
180
|
+
sessionId: actualSessionId as AcpSessionId,
|
|
186
181
|
update: {
|
|
187
182
|
sessionUpdate: "available_commands_update",
|
|
188
183
|
availableCommands: agent.getSlashCommands().map((cmd) => ({
|
|
@@ -196,6 +191,15 @@ export class WaveAcpAgent implements AcpAgent {
|
|
|
196
191
|
});
|
|
197
192
|
});
|
|
198
193
|
|
|
194
|
+
return agent;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
async newSession(params: NewSessionRequest): Promise<NewSessionResponse> {
|
|
198
|
+
const { cwd } = params;
|
|
199
|
+
logger.info(`Creating new session in ${cwd}`);
|
|
200
|
+
const agent = await this.createAgent(undefined, cwd);
|
|
201
|
+
logger.info(`New session created with ID: ${agent.sessionId}`);
|
|
202
|
+
|
|
199
203
|
return {
|
|
200
204
|
sessionId: agent.sessionId as AcpSessionId,
|
|
201
205
|
modes: this.getSessionModeState(agent),
|
|
@@ -208,23 +212,6 @@ export class WaveAcpAgent implements AcpAgent {
|
|
|
208
212
|
logger.info(`Loading session: ${sessionId} in ${cwd}`);
|
|
209
213
|
const agent = await this.createAgent(sessionId, cwd);
|
|
210
214
|
|
|
211
|
-
// Send initial available commands after agent creation
|
|
212
|
-
setImmediate(() => {
|
|
213
|
-
this.connection.sessionUpdate({
|
|
214
|
-
sessionId: agent.sessionId as AcpSessionId,
|
|
215
|
-
update: {
|
|
216
|
-
sessionUpdate: "available_commands_update",
|
|
217
|
-
availableCommands: agent.getSlashCommands().map((cmd) => ({
|
|
218
|
-
name: cmd.name,
|
|
219
|
-
description: cmd.description,
|
|
220
|
-
input: {
|
|
221
|
-
hint: "Enter arguments...",
|
|
222
|
-
},
|
|
223
|
-
})),
|
|
224
|
-
},
|
|
225
|
-
});
|
|
226
|
-
});
|
|
227
|
-
|
|
228
215
|
return {
|
|
229
216
|
modes: this.getSessionModeState(agent),
|
|
230
217
|
configOptions: this.getSessionConfigOptions(agent),
|
|
@@ -236,17 +223,21 @@ export class WaveAcpAgent implements AcpAgent {
|
|
|
236
223
|
): Promise<ListSessionsResponse> {
|
|
237
224
|
const { cwd } = params;
|
|
238
225
|
logger.info(`listSessions called with params: ${JSON.stringify(params)}`);
|
|
226
|
+
|
|
227
|
+
let waveSessions;
|
|
239
228
|
if (!cwd) {
|
|
240
|
-
logger.
|
|
241
|
-
|
|
229
|
+
logger.info("listSessions called without cwd, listing all sessions");
|
|
230
|
+
waveSessions = await listAllWaveSessions();
|
|
231
|
+
} else {
|
|
232
|
+
logger.info(`Listing sessions for ${cwd}`);
|
|
233
|
+
waveSessions = await listWaveSessions(cwd);
|
|
242
234
|
}
|
|
243
235
|
|
|
244
|
-
logger.info(`
|
|
245
|
-
const waveSessions = await listWaveSessions(cwd);
|
|
246
|
-
logger.info(`Found ${waveSessions.length} sessions for ${cwd}`);
|
|
236
|
+
logger.info(`Found ${waveSessions.length} sessions`);
|
|
247
237
|
const sessions: SessionInfo[] = waveSessions.map((meta) => ({
|
|
248
238
|
sessionId: meta.id as AcpSessionId,
|
|
249
239
|
cwd: meta.workdir,
|
|
240
|
+
title: meta.firstMessage ? truncateContent(meta.firstMessage) : undefined,
|
|
250
241
|
updatedAt: meta.lastActiveAt.toISOString(),
|
|
251
242
|
}));
|
|
252
243
|
return { sessions };
|
|
@@ -378,6 +369,27 @@ export class WaveAcpAgent implements AcpAgent {
|
|
|
378
369
|
context.toolCallId ||
|
|
379
370
|
"perm-" + Math.random().toString(36).substring(2, 9);
|
|
380
371
|
|
|
372
|
+
let effectiveName = context.toolName;
|
|
373
|
+
let effectiveCompactParams: string | undefined = undefined;
|
|
374
|
+
|
|
375
|
+
if (agent?.messages && context.toolCallId) {
|
|
376
|
+
const toolBlock = agent.messages
|
|
377
|
+
.flatMap((m) => m.blocks)
|
|
378
|
+
.find((b) => b.type === "tool" && b.id === context.toolCallId) as
|
|
379
|
+
| import("wave-agent-sdk").ToolBlock
|
|
380
|
+
| undefined;
|
|
381
|
+
if (toolBlock) {
|
|
382
|
+
effectiveName = toolBlock.name || effectiveName;
|
|
383
|
+
effectiveCompactParams =
|
|
384
|
+
toolBlock.compactParams || effectiveCompactParams;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
const displayTitle =
|
|
389
|
+
effectiveName && effectiveCompactParams
|
|
390
|
+
? `${effectiveName}: ${effectiveCompactParams}`
|
|
391
|
+
: effectiveName || "Tool Call";
|
|
392
|
+
|
|
381
393
|
const options: PermissionOption[] = [
|
|
382
394
|
{
|
|
383
395
|
optionId: "allow_once",
|
|
@@ -394,11 +406,6 @@ export class WaveAcpAgent implements AcpAgent {
|
|
|
394
406
|
name: "Reject Once",
|
|
395
407
|
kind: "reject_once",
|
|
396
408
|
},
|
|
397
|
-
{
|
|
398
|
-
optionId: "reject_always",
|
|
399
|
-
name: "Reject Always",
|
|
400
|
-
kind: "reject_always",
|
|
401
|
-
},
|
|
402
409
|
];
|
|
403
410
|
|
|
404
411
|
const content = context.toolName
|
|
@@ -420,7 +427,7 @@ export class WaveAcpAgent implements AcpAgent {
|
|
|
420
427
|
sessionId: sessionId as AcpSessionId,
|
|
421
428
|
toolCall: {
|
|
422
429
|
toolCallId,
|
|
423
|
-
title:
|
|
430
|
+
title: displayTitle,
|
|
424
431
|
status: "pending",
|
|
425
432
|
rawInput: context.toolInput,
|
|
426
433
|
content,
|
|
@@ -438,21 +445,15 @@ export class WaveAcpAgent implements AcpAgent {
|
|
|
438
445
|
logger.info(`User selected permission option: ${selectedOptionId}`);
|
|
439
446
|
|
|
440
447
|
switch (selectedOptionId) {
|
|
441
|
-
case "allow_once":
|
|
442
|
-
return { behavior: "allow" };
|
|
443
448
|
case "allow_always":
|
|
444
449
|
return {
|
|
445
450
|
behavior: "allow",
|
|
446
451
|
newPermissionRule: `${context.toolName}(*)`,
|
|
447
452
|
};
|
|
453
|
+
case "allow_once":
|
|
454
|
+
return { behavior: "allow" };
|
|
448
455
|
case "reject_once":
|
|
449
456
|
return { behavior: "deny", message: "Rejected by user" };
|
|
450
|
-
case "reject_always":
|
|
451
|
-
return {
|
|
452
|
-
behavior: "deny",
|
|
453
|
-
message: "Rejected by user",
|
|
454
|
-
newPermissionRule: `!${context.toolName}(*)`,
|
|
455
|
-
};
|
|
456
457
|
default:
|
|
457
458
|
return { behavior: "deny", message: "Unknown option selected" };
|
|
458
459
|
}
|
|
@@ -474,7 +475,8 @@ export class WaveAcpAgent implements AcpAgent {
|
|
|
474
475
|
if (name === "Write") {
|
|
475
476
|
let oldText: string | null = null;
|
|
476
477
|
try {
|
|
477
|
-
const filePath = parameters.file_path
|
|
478
|
+
const filePath = (parameters.file_path ||
|
|
479
|
+
parameters.filePath) as string;
|
|
478
480
|
const fullPath = path.isAbsolute(filePath)
|
|
479
481
|
? filePath
|
|
480
482
|
: path.join(workdir, filePath);
|
|
@@ -485,7 +487,7 @@ export class WaveAcpAgent implements AcpAgent {
|
|
|
485
487
|
return [
|
|
486
488
|
{
|
|
487
489
|
type: "diff",
|
|
488
|
-
path: parameters.file_path as string,
|
|
490
|
+
path: (parameters.file_path || parameters.filePath) as string,
|
|
489
491
|
oldText,
|
|
490
492
|
newText: parameters.content as string,
|
|
491
493
|
},
|
|
@@ -495,7 +497,8 @@ export class WaveAcpAgent implements AcpAgent {
|
|
|
495
497
|
let oldText: string | null = null;
|
|
496
498
|
let newText: string | null = null;
|
|
497
499
|
try {
|
|
498
|
-
const filePath = parameters.file_path
|
|
500
|
+
const filePath = (parameters.file_path ||
|
|
501
|
+
parameters.filePath) as string;
|
|
499
502
|
const fullPath = path.isAbsolute(filePath)
|
|
500
503
|
? filePath
|
|
501
504
|
: path.join(workdir, filePath);
|
|
@@ -520,7 +523,7 @@ export class WaveAcpAgent implements AcpAgent {
|
|
|
520
523
|
return [
|
|
521
524
|
{
|
|
522
525
|
type: "diff",
|
|
523
|
-
path: parameters.file_path as string,
|
|
526
|
+
path: (parameters.file_path || parameters.filePath) as string,
|
|
524
527
|
oldText,
|
|
525
528
|
newText,
|
|
526
529
|
},
|
|
@@ -531,55 +534,83 @@ export class WaveAcpAgent implements AcpAgent {
|
|
|
531
534
|
return [
|
|
532
535
|
{
|
|
533
536
|
type: "diff",
|
|
534
|
-
path: parameters.file_path as string,
|
|
537
|
+
path: (parameters.file_path || parameters.filePath) as string,
|
|
535
538
|
oldText: parameters.old_string as string,
|
|
536
539
|
newText: parameters.new_string as string,
|
|
537
540
|
},
|
|
538
541
|
];
|
|
539
542
|
}
|
|
540
|
-
return this.getToolContent(name, parameters);
|
|
543
|
+
return this.getToolContent(name, parameters, undefined);
|
|
541
544
|
}
|
|
542
545
|
|
|
543
546
|
private getToolContent(
|
|
544
547
|
name: string,
|
|
545
548
|
parameters: Record<string, unknown> | undefined,
|
|
549
|
+
shortResult: string | undefined,
|
|
546
550
|
): ToolCallContent[] | undefined {
|
|
547
|
-
|
|
548
|
-
if (
|
|
549
|
-
|
|
550
|
-
{
|
|
551
|
+
const contents: ToolCallContent[] = [];
|
|
552
|
+
if (parameters) {
|
|
553
|
+
if (name === "Write") {
|
|
554
|
+
contents.push({
|
|
551
555
|
type: "diff",
|
|
552
|
-
path: parameters.file_path as string,
|
|
556
|
+
path: (parameters.file_path || parameters.filePath) as string,
|
|
553
557
|
oldText: null,
|
|
554
558
|
newText: parameters.content as string,
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
if (name === "Edit") {
|
|
559
|
-
return [
|
|
560
|
-
{
|
|
559
|
+
});
|
|
560
|
+
} else if (name === "Edit") {
|
|
561
|
+
contents.push({
|
|
561
562
|
type: "diff",
|
|
562
|
-
path: parameters.file_path as string,
|
|
563
|
+
path: (parameters.file_path || parameters.filePath) as string,
|
|
563
564
|
oldText: parameters.old_string as string,
|
|
564
565
|
newText: parameters.new_string as string,
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
if (shortResult) {
|
|
571
|
+
contents.push({
|
|
572
|
+
type: "content",
|
|
573
|
+
content: {
|
|
574
|
+
type: "text",
|
|
575
|
+
text: shortResult,
|
|
565
576
|
},
|
|
566
|
-
|
|
577
|
+
});
|
|
567
578
|
}
|
|
568
|
-
|
|
579
|
+
|
|
580
|
+
return contents.length > 0 ? contents : undefined;
|
|
569
581
|
}
|
|
570
582
|
|
|
571
583
|
private getToolLocations(
|
|
572
584
|
name: string,
|
|
573
585
|
parameters: Record<string, unknown> | undefined,
|
|
586
|
+
extraStartLineNumber?: number,
|
|
574
587
|
): ToolCallLocation[] | undefined {
|
|
575
588
|
if (!parameters) return undefined;
|
|
576
|
-
if (
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
589
|
+
if (
|
|
590
|
+
name === "Write" ||
|
|
591
|
+
name === "Edit" ||
|
|
592
|
+
name === "Read" ||
|
|
593
|
+
name === "LSP"
|
|
594
|
+
) {
|
|
595
|
+
const filePath = (parameters.file_path || parameters.filePath) as string;
|
|
596
|
+
let line =
|
|
597
|
+
extraStartLineNumber ??
|
|
598
|
+
(parameters.startLineNumber as number) ??
|
|
599
|
+
(parameters.line as number) ??
|
|
600
|
+
(parameters.offset as number);
|
|
601
|
+
|
|
602
|
+
if (name === "Write" && line === undefined) {
|
|
603
|
+
line = 1;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
if (filePath) {
|
|
607
|
+
return [
|
|
608
|
+
{
|
|
609
|
+
path: filePath,
|
|
610
|
+
line: line,
|
|
611
|
+
},
|
|
612
|
+
];
|
|
613
|
+
}
|
|
583
614
|
}
|
|
584
615
|
return undefined;
|
|
585
616
|
}
|
|
@@ -605,6 +636,15 @@ export class WaveAcpAgent implements AcpAgent {
|
|
|
605
636
|
|
|
606
637
|
private createCallbacks(sessionId: string): AgentOptions["callbacks"] {
|
|
607
638
|
const getAgent = () => this.agents.get(sessionId);
|
|
639
|
+
const toolStates = new Map<
|
|
640
|
+
string,
|
|
641
|
+
{
|
|
642
|
+
name?: string;
|
|
643
|
+
compactParams?: string;
|
|
644
|
+
shortResult?: string;
|
|
645
|
+
startLineNumber?: number;
|
|
646
|
+
}
|
|
647
|
+
>();
|
|
608
648
|
return {
|
|
609
649
|
onAssistantContentUpdated: (chunk: string) => {
|
|
610
650
|
this.connection.sessionUpdate({
|
|
@@ -631,7 +671,42 @@ export class WaveAcpAgent implements AcpAgent {
|
|
|
631
671
|
});
|
|
632
672
|
},
|
|
633
673
|
onToolBlockUpdated: (params: AgentToolBlockUpdateParams) => {
|
|
634
|
-
const {
|
|
674
|
+
const {
|
|
675
|
+
id,
|
|
676
|
+
name,
|
|
677
|
+
stage,
|
|
678
|
+
success,
|
|
679
|
+
error,
|
|
680
|
+
result,
|
|
681
|
+
parameters,
|
|
682
|
+
compactParams,
|
|
683
|
+
shortResult,
|
|
684
|
+
startLineNumber,
|
|
685
|
+
} = params;
|
|
686
|
+
|
|
687
|
+
let state = toolStates.get(id);
|
|
688
|
+
if (!state) {
|
|
689
|
+
state = {};
|
|
690
|
+
toolStates.set(id, state);
|
|
691
|
+
}
|
|
692
|
+
if (name) state.name = name;
|
|
693
|
+
if (compactParams) state.compactParams = compactParams;
|
|
694
|
+
if (shortResult) state.shortResult = shortResult;
|
|
695
|
+
if (startLineNumber !== undefined)
|
|
696
|
+
state.startLineNumber = startLineNumber;
|
|
697
|
+
|
|
698
|
+
const effectiveName = state.name || name;
|
|
699
|
+
const effectiveCompactParams = state.compactParams || compactParams;
|
|
700
|
+
const effectiveShortResult = state.shortResult || shortResult;
|
|
701
|
+
const effectiveStartLineNumber =
|
|
702
|
+
state.startLineNumber !== undefined
|
|
703
|
+
? state.startLineNumber
|
|
704
|
+
: startLineNumber;
|
|
705
|
+
|
|
706
|
+
const displayTitle =
|
|
707
|
+
effectiveName && effectiveCompactParams
|
|
708
|
+
? `${effectiveName}: ${effectiveCompactParams}`
|
|
709
|
+
: effectiveName || "Tool Call";
|
|
635
710
|
|
|
636
711
|
let parsedParameters: Record<string, unknown> | undefined = undefined;
|
|
637
712
|
if (parameters) {
|
|
@@ -643,14 +718,24 @@ export class WaveAcpAgent implements AcpAgent {
|
|
|
643
718
|
}
|
|
644
719
|
|
|
645
720
|
const content =
|
|
646
|
-
|
|
647
|
-
? this.getToolContent(
|
|
721
|
+
effectiveName && (parsedParameters || effectiveShortResult)
|
|
722
|
+
? this.getToolContent(
|
|
723
|
+
effectiveName,
|
|
724
|
+
parsedParameters,
|
|
725
|
+
effectiveShortResult,
|
|
726
|
+
)
|
|
648
727
|
: undefined;
|
|
649
728
|
const locations =
|
|
650
|
-
|
|
651
|
-
? this.getToolLocations(
|
|
729
|
+
effectiveName && parsedParameters
|
|
730
|
+
? this.getToolLocations(
|
|
731
|
+
effectiveName,
|
|
732
|
+
parsedParameters,
|
|
733
|
+
effectiveStartLineNumber,
|
|
734
|
+
)
|
|
652
735
|
: undefined;
|
|
653
|
-
const kind =
|
|
736
|
+
const kind = effectiveName
|
|
737
|
+
? this.getToolKind(effectiveName)
|
|
738
|
+
: undefined;
|
|
654
739
|
|
|
655
740
|
if (stage === "start") {
|
|
656
741
|
this.connection.sessionUpdate({
|
|
@@ -658,7 +743,7 @@ export class WaveAcpAgent implements AcpAgent {
|
|
|
658
743
|
update: {
|
|
659
744
|
sessionUpdate: "tool_call",
|
|
660
745
|
toolCallId: id,
|
|
661
|
-
title:
|
|
746
|
+
title: displayTitle,
|
|
662
747
|
status: "pending",
|
|
663
748
|
content,
|
|
664
749
|
locations,
|
|
@@ -689,7 +774,7 @@ export class WaveAcpAgent implements AcpAgent {
|
|
|
689
774
|
sessionUpdate: "tool_call_update",
|
|
690
775
|
toolCallId: id,
|
|
691
776
|
status,
|
|
692
|
-
title:
|
|
777
|
+
title: displayTitle,
|
|
693
778
|
rawOutput: result || error,
|
|
694
779
|
content,
|
|
695
780
|
locations,
|
|
@@ -697,6 +782,10 @@ export class WaveAcpAgent implements AcpAgent {
|
|
|
697
782
|
rawInput: parsedParameters,
|
|
698
783
|
},
|
|
699
784
|
});
|
|
785
|
+
|
|
786
|
+
if (stage === "end") {
|
|
787
|
+
toolStates.delete(id);
|
|
788
|
+
}
|
|
700
789
|
},
|
|
701
790
|
onTasksChange: (tasks) => {
|
|
702
791
|
this.connection.sessionUpdate({
|
package/src/cli.tsx
CHANGED
|
@@ -15,6 +15,7 @@ export async function startCli(options: CliOptions): Promise<void> {
|
|
|
15
15
|
restoreSessionId,
|
|
16
16
|
continueLastSession,
|
|
17
17
|
bypassPermissions,
|
|
18
|
+
permissionMode,
|
|
18
19
|
pluginDirs,
|
|
19
20
|
tools,
|
|
20
21
|
worktreeSession,
|
|
@@ -37,6 +38,7 @@ export async function startCli(options: CliOptions): Promise<void> {
|
|
|
37
38
|
restoreSessionId={restoreSessionId}
|
|
38
39
|
continueLastSession={continueLastSession}
|
|
39
40
|
bypassPermissions={bypassPermissions}
|
|
41
|
+
permissionMode={permissionMode}
|
|
40
42
|
pluginDirs={pluginDirs}
|
|
41
43
|
tools={tools}
|
|
42
44
|
worktreeSession={worktreeSession}
|
package/src/components/App.tsx
CHANGED
|
@@ -23,6 +23,7 @@ interface AppWithProvidersProps extends BaseAppProps {
|
|
|
23
23
|
|
|
24
24
|
const AppWithProviders: React.FC<AppWithProvidersProps> = ({
|
|
25
25
|
bypassPermissions,
|
|
26
|
+
permissionMode,
|
|
26
27
|
pluginDirs,
|
|
27
28
|
tools,
|
|
28
29
|
worktreeSession,
|
|
@@ -94,6 +95,7 @@ const AppWithProviders: React.FC<AppWithProvidersProps> = ({
|
|
|
94
95
|
return (
|
|
95
96
|
<ChatProvider
|
|
96
97
|
bypassPermissions={bypassPermissions}
|
|
98
|
+
permissionMode={permissionMode}
|
|
97
99
|
pluginDirs={pluginDirs}
|
|
98
100
|
tools={tools}
|
|
99
101
|
workdir={workdir}
|
|
@@ -157,6 +159,7 @@ export const App: React.FC<AppProps> = ({
|
|
|
157
159
|
restoreSessionId,
|
|
158
160
|
continueLastSession,
|
|
159
161
|
bypassPermissions,
|
|
162
|
+
permissionMode,
|
|
160
163
|
pluginDirs,
|
|
161
164
|
tools,
|
|
162
165
|
worktreeSession,
|
|
@@ -172,6 +175,7 @@ export const App: React.FC<AppProps> = ({
|
|
|
172
175
|
>
|
|
173
176
|
<AppWithProviders
|
|
174
177
|
bypassPermissions={bypassPermissions}
|
|
178
|
+
permissionMode={permissionMode}
|
|
175
179
|
pluginDirs={pluginDirs}
|
|
176
180
|
tools={tools}
|
|
177
181
|
worktreeSession={worktreeSession}
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import React, { useMemo } from "react";
|
|
2
2
|
import { Box, Text } from "ink";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
WRITE_TOOL_NAME,
|
|
5
|
+
EDIT_TOOL_NAME,
|
|
6
|
+
type WriteToolParameters,
|
|
7
|
+
} from "wave-agent-sdk";
|
|
4
8
|
import { transformToolBlockToChanges } from "../utils/toolParameterTransforms.js";
|
|
5
9
|
import { diffLines, diffWords } from "diff";
|
|
10
|
+
import path from "path";
|
|
11
|
+
import { Markdown } from "./Markdown.js";
|
|
6
12
|
|
|
7
13
|
interface DiffDisplayProps {
|
|
8
14
|
toolName?: string;
|
|
@@ -97,6 +103,23 @@ export const DiffDisplay: React.FC<DiffDisplayProps> = ({
|
|
|
97
103
|
}
|
|
98
104
|
};
|
|
99
105
|
|
|
106
|
+
// Render highlighted code for Write tool
|
|
107
|
+
const renderWriteContent = () => {
|
|
108
|
+
if (!parameters) return null;
|
|
109
|
+
try {
|
|
110
|
+
const parsed = JSON.parse(parameters) as WriteToolParameters;
|
|
111
|
+
const content = parsed.content || "";
|
|
112
|
+
const filePath = parsed.file_path || "";
|
|
113
|
+
const ext = path.extname(filePath).slice(1);
|
|
114
|
+
|
|
115
|
+
const markdown = `\`\`\`${ext}\n${content}\n\`\`\``;
|
|
116
|
+
return <Markdown>{markdown}</Markdown>;
|
|
117
|
+
} catch (error) {
|
|
118
|
+
console.warn("Error rendering write content:", error);
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
|
|
100
123
|
// Render expanded diff display
|
|
101
124
|
const renderExpandedDiff = () => {
|
|
102
125
|
try {
|
|
@@ -352,7 +375,9 @@ export const DiffDisplay: React.FC<DiffDisplayProps> = ({
|
|
|
352
375
|
return (
|
|
353
376
|
<Box flexDirection="column">
|
|
354
377
|
<Box paddingLeft={2} borderLeft borderColor="cyan" flexDirection="column">
|
|
355
|
-
{
|
|
378
|
+
{toolName === WRITE_TOOL_NAME
|
|
379
|
+
? renderWriteContent()
|
|
380
|
+
: renderExpandedDiff()}
|
|
356
381
|
</Box>
|
|
357
382
|
</Box>
|
|
358
383
|
);
|