wave-agent-sdk 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/agent.d.ts +70 -24
- package/dist/agent.js +110 -30
- package/dist/builtin/plugins.js +11 -20
- package/dist/builtin/skills/settings.js +6 -8
- package/dist/core/plugin.d.ts +4 -0
- package/dist/core/plugin.js +7 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/managers/aiManager.d.ts +0 -24
- package/dist/managers/aiManager.js +14 -181
- package/dist/managers/bashModeManager.d.ts +33 -0
- package/dist/managers/bashModeManager.js +110 -0
- package/dist/managers/hookManager.d.ts +5 -0
- package/dist/managers/hookManager.js +7 -0
- package/dist/managers/liveConfigManager.js +3 -3
- package/dist/managers/mcpManager.d.ts +23 -0
- package/dist/managers/mcpManager.js +162 -14
- package/dist/managers/messageManager.d.ts +12 -13
- package/dist/managers/messageManager.js +63 -60
- package/dist/managers/permissionManager.d.ts +29 -0
- package/dist/managers/permissionManager.js +158 -70
- package/dist/managers/planManager.d.ts +9 -0
- package/dist/managers/planManager.js +19 -1
- package/dist/managers/skillManager.d.ts +31 -0
- package/dist/managers/skillManager.js +122 -12
- package/dist/managers/slashCommandManager.js +9 -28
- package/dist/managers/subagentManager.d.ts +7 -0
- package/dist/managers/subagentManager.js +61 -9
- package/dist/managers/workflowManager.js +6 -0
- package/dist/prompts/index.d.ts +0 -1
- package/dist/prompts/index.js +0 -4
- package/dist/services/MarketplaceService.js +36 -14
- package/dist/services/configurationService.d.ts +34 -2
- package/dist/services/configurationService.js +123 -16
- package/dist/services/initializationService.js +2 -2
- package/dist/services/jsonlHandler.d.ts +14 -0
- package/dist/services/jsonlHandler.js +44 -1
- package/dist/services/memory.d.ts +14 -0
- package/dist/services/memory.js +33 -0
- package/dist/services/officialMarketplaceMirror.d.ts +85 -0
- package/dist/services/officialMarketplaceMirror.js +289 -0
- package/dist/services/remoteSettingsService.js +4 -4
- package/dist/services/session.js +30 -13
- package/dist/services/worktreeHooks.js +6 -1
- package/dist/stdio/index.d.ts +10 -0
- package/dist/stdio/index.js +10 -0
- package/dist/stdio/notificationRouter.d.ts +38 -0
- package/dist/stdio/notificationRouter.js +96 -0
- package/dist/stdio/rpcClient.d.ts +18 -0
- package/dist/stdio/rpcClient.js +10 -0
- package/dist/stdio/stdioAgent.d.ts +222 -0
- package/dist/stdio/stdioAgent.js +341 -0
- package/dist/tools/bashTool.js +2 -0
- package/dist/tools/exitPlanMode.js +10 -2
- package/dist/types/agent.d.ts +8 -0
- package/dist/types/commands.d.ts +7 -0
- package/dist/types/configuration.d.ts +6 -1
- package/dist/types/hooks.d.ts +1 -0
- package/dist/types/hooks.js +19 -0
- package/dist/types/mcp.d.ts +3 -0
- package/dist/types/messaging.d.ts +1 -8
- package/dist/types/skills.d.ts +11 -0
- package/dist/utils/bashParser.d.ts +17 -0
- package/dist/utils/bashParser.js +72 -0
- package/dist/utils/bashStructure/bashLexer.d.ts +96 -0
- package/dist/utils/bashStructure/bashLexer.js +676 -0
- package/dist/utils/bashStructure/bashParser.d.ts +144 -0
- package/dist/utils/bashStructure/bashParser.js +606 -0
- package/dist/utils/bashStructure/bashSemantics.d.ts +70 -0
- package/dist/utils/bashStructure/bashSemantics.js +477 -0
- package/dist/utils/bashStructure/index.d.ts +26 -0
- package/dist/utils/bashStructure/index.js +27 -0
- package/dist/utils/bashStructure/types.d.ts +62 -0
- package/dist/utils/bashStructure/types.js +47 -0
- package/dist/utils/containerSetup.js +5 -6
- package/dist/utils/fileUtils.d.ts +11 -0
- package/dist/utils/fileUtils.js +37 -0
- package/dist/utils/messageOperations.d.ts +0 -18
- package/dist/utils/messageOperations.js +0 -62
- package/dist/utils/subagentParser.js +9 -2
- package/dist/utils/tokenCalculation.js +0 -8
- package/dist/utils/worktreeUtils.d.ts +2 -1
- package/dist/utils/worktreeUtils.js +64 -34
- package/package.json +6 -1
- package/dist/managers/bangManager.d.ts +0 -26
- package/dist/managers/bangManager.js +0 -78
|
@@ -26,6 +26,17 @@ export declare function readFirstNLines(filePath: string, maxLines: number): Pro
|
|
|
26
26
|
* @return {Promise<string>} - The last non-empty line of the file, or an empty string if no non-empty lines found.
|
|
27
27
|
*/
|
|
28
28
|
export declare function getLastLine(filePath: string, minLength?: number): Promise<string>;
|
|
29
|
+
/**
|
|
30
|
+
* Read up to `maxBytes` from the end of a file and return the lines it holds,
|
|
31
|
+
* oldest first. The window may start in the middle of a line, in which case
|
|
32
|
+
* that partial first line is dropped so every returned line is complete —
|
|
33
|
+
* callers parse them as JSON.
|
|
34
|
+
*
|
|
35
|
+
* @param {string} filePath - The path to the file.
|
|
36
|
+
* @param {number} maxBytes - Size of the tail window (default 64KB).
|
|
37
|
+
* @return {Promise<string[]>} - Trailing non-empty lines, or [] if unreadable.
|
|
38
|
+
*/
|
|
39
|
+
export declare function readTailLines(filePath: string, maxBytes?: number): Promise<string[]>;
|
|
29
40
|
/**
|
|
30
41
|
* Suggests similar paths if a file is not found.
|
|
31
42
|
*/
|
package/dist/utils/fileUtils.js
CHANGED
|
@@ -136,6 +136,43 @@ export async function getLastLine(filePath, minLength = 1) {
|
|
|
136
136
|
}
|
|
137
137
|
}
|
|
138
138
|
}
|
|
139
|
+
/**
|
|
140
|
+
* Read up to `maxBytes` from the end of a file and return the lines it holds,
|
|
141
|
+
* oldest first. The window may start in the middle of a line, in which case
|
|
142
|
+
* that partial first line is dropped so every returned line is complete —
|
|
143
|
+
* callers parse them as JSON.
|
|
144
|
+
*
|
|
145
|
+
* @param {string} filePath - The path to the file.
|
|
146
|
+
* @param {number} maxBytes - Size of the tail window (default 64KB).
|
|
147
|
+
* @return {Promise<string[]>} - Trailing non-empty lines, or [] if unreadable.
|
|
148
|
+
*/
|
|
149
|
+
export async function readTailLines(filePath, maxBytes = 64 * 1024) {
|
|
150
|
+
let fileHandle;
|
|
151
|
+
try {
|
|
152
|
+
const stats = await fs.stat(filePath);
|
|
153
|
+
const fileSize = stats.size;
|
|
154
|
+
if (fileSize === 0)
|
|
155
|
+
return [];
|
|
156
|
+
const readSize = Math.min(maxBytes, fileSize);
|
|
157
|
+
const start = fileSize - readSize;
|
|
158
|
+
fileHandle = await fs.open(filePath, "r");
|
|
159
|
+
const buffer = Buffer.alloc(readSize);
|
|
160
|
+
const { bytesRead } = await fileHandle.read(buffer, 0, readSize, start);
|
|
161
|
+
const lines = buffer.subarray(0, bytesRead).toString("utf8").split("\n");
|
|
162
|
+
if (start > 0)
|
|
163
|
+
lines.shift(); // partial line (window began mid-line)
|
|
164
|
+
return lines.map((line) => line.trim()).filter((line) => line !== "");
|
|
165
|
+
}
|
|
166
|
+
catch {
|
|
167
|
+
// Unreadable file (missing, permissions, race with deletion): no tail.
|
|
168
|
+
return [];
|
|
169
|
+
}
|
|
170
|
+
finally {
|
|
171
|
+
if (fileHandle) {
|
|
172
|
+
await fileHandle.close();
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
139
176
|
/**
|
|
140
177
|
* Simple Levenshtein distance implementation
|
|
141
178
|
*/
|
|
@@ -54,21 +54,6 @@ export interface AddErrorBlockParams {
|
|
|
54
54
|
messages: Message[];
|
|
55
55
|
error: string;
|
|
56
56
|
}
|
|
57
|
-
export interface AddBangParams {
|
|
58
|
-
messages: Message[];
|
|
59
|
-
command: string;
|
|
60
|
-
}
|
|
61
|
-
export interface UpdateBangParams {
|
|
62
|
-
messages: Message[];
|
|
63
|
-
command: string;
|
|
64
|
-
output: string;
|
|
65
|
-
}
|
|
66
|
-
export interface CompleteBangParams {
|
|
67
|
-
messages: Message[];
|
|
68
|
-
command: string;
|
|
69
|
-
exitCode: number;
|
|
70
|
-
output?: string;
|
|
71
|
-
}
|
|
72
57
|
/**
|
|
73
58
|
* Convert image file path to base64 format
|
|
74
59
|
* @param imagePath Image file path
|
|
@@ -94,9 +79,6 @@ export declare const updateToolBlockInMessage: ({ messages, id, messageId, param
|
|
|
94
79
|
messageId?: string;
|
|
95
80
|
};
|
|
96
81
|
export declare const addErrorBlockToMessage: ({ messages, error, }: AddErrorBlockParams) => Message[];
|
|
97
|
-
export declare const addBangMessage: ({ messages, command, }: AddBangParams) => Message[];
|
|
98
|
-
export declare const updateBangInMessage: ({ messages, command, output, }: UpdateBangParams) => Message[];
|
|
99
|
-
export declare const completeBangInMessage: ({ messages, command, exitCode, output, }: CompleteBangParams) => Message[];
|
|
100
82
|
/**
|
|
101
83
|
* Helper to count tool blocks in messages
|
|
102
84
|
*/
|
|
@@ -304,64 +304,6 @@ export const addErrorBlockToMessage = ({ messages, error, }) => {
|
|
|
304
304
|
}
|
|
305
305
|
return newMessages;
|
|
306
306
|
};
|
|
307
|
-
// Add bang block to message list
|
|
308
|
-
export const addBangMessage = ({ messages, command, }) => {
|
|
309
|
-
const outputMessage = {
|
|
310
|
-
id: generateMessageId(),
|
|
311
|
-
role: "user",
|
|
312
|
-
blocks: [
|
|
313
|
-
{
|
|
314
|
-
type: "bang",
|
|
315
|
-
command,
|
|
316
|
-
output: "",
|
|
317
|
-
stage: "running",
|
|
318
|
-
exitCode: null,
|
|
319
|
-
},
|
|
320
|
-
],
|
|
321
|
-
timestamp: new Date().toISOString(),
|
|
322
|
-
};
|
|
323
|
-
return [...messages, outputMessage];
|
|
324
|
-
};
|
|
325
|
-
// Update output content of bang block
|
|
326
|
-
export const updateBangInMessage = ({ messages, command, output, }) => {
|
|
327
|
-
const newMessages = [...messages];
|
|
328
|
-
// Find the last user message with a bang block for this command
|
|
329
|
-
for (let i = newMessages.length - 1; i >= 0; i--) {
|
|
330
|
-
const msg = newMessages[i];
|
|
331
|
-
if (msg.role === "user") {
|
|
332
|
-
const commandBlock = msg.blocks.find((block) => block.type === "bang" &&
|
|
333
|
-
block.command === command &&
|
|
334
|
-
block.stage === "running");
|
|
335
|
-
if (commandBlock && commandBlock.type === "bang") {
|
|
336
|
-
commandBlock.output = output.trim();
|
|
337
|
-
break;
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
return newMessages;
|
|
342
|
-
};
|
|
343
|
-
// Complete bang execution, update exit status
|
|
344
|
-
export const completeBangInMessage = ({ messages, command, exitCode, output, }) => {
|
|
345
|
-
const newMessages = [...messages];
|
|
346
|
-
// Find the last user message with a bang block for this command
|
|
347
|
-
for (let i = newMessages.length - 1; i >= 0; i--) {
|
|
348
|
-
const msg = newMessages[i];
|
|
349
|
-
if (msg.role === "user") {
|
|
350
|
-
const commandBlock = msg.blocks.find((block) => block.type === "bang" &&
|
|
351
|
-
block.command === command &&
|
|
352
|
-
block.stage === "running");
|
|
353
|
-
if (commandBlock && commandBlock.type === "bang") {
|
|
354
|
-
commandBlock.stage = "end";
|
|
355
|
-
commandBlock.exitCode = exitCode;
|
|
356
|
-
if (output !== undefined) {
|
|
357
|
-
commandBlock.output = output.trim();
|
|
358
|
-
}
|
|
359
|
-
break;
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
return newMessages;
|
|
364
|
-
};
|
|
365
307
|
/**
|
|
366
308
|
* Helper to count tool blocks in messages
|
|
367
309
|
*/
|
|
@@ -458,10 +400,6 @@ export function getMessageContent(message) {
|
|
|
458
400
|
if (textBlock && "content" in textBlock) {
|
|
459
401
|
return textBlock.content;
|
|
460
402
|
}
|
|
461
|
-
const bangBlock = message.blocks.find((block) => block.type === "bang");
|
|
462
|
-
if (bangBlock && "command" in bangBlock) {
|
|
463
|
-
return `!${bangBlock.command}`;
|
|
464
|
-
}
|
|
465
403
|
const compactBlock = message.blocks.find((block) => block.type === "compact");
|
|
466
404
|
if (compactBlock && "content" in compactBlock) {
|
|
467
405
|
return compactBlock.content;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { readFileSync, readdirSync, statSync } from "fs";
|
|
2
|
+
import { homedir } from "os";
|
|
2
3
|
import { join, extname, basename } from "path";
|
|
3
4
|
import { logger } from "./globalLogger.js";
|
|
4
5
|
import { getBuiltinSubagentsDir } from "./configPaths.js";
|
|
@@ -165,8 +166,14 @@ function scanSubagentDirectory(dirPath, scope) {
|
|
|
165
166
|
export async function loadSubagentConfigurations(workdir, env = process.env) {
|
|
166
167
|
const projectWaveDir = join(workdir, ".wave", "agents");
|
|
167
168
|
const projectClaudeDir = join(workdir, ".claude", "agents");
|
|
168
|
-
|
|
169
|
-
|
|
169
|
+
// Resolve user-level dirs via os.homedir() (NOT process.env.HOME): every
|
|
170
|
+
// other user-path resolver in the SDK (settings, skills, MCP, rules, ~/
|
|
171
|
+
// expansion in the Write tool) uses os.homedir(), which on Windows falls
|
|
172
|
+
// back to USERPROFILE. Windows GUI-launched processes (Electron desktop)
|
|
173
|
+
// typically have no HOME set, so process.env.HOME made user-level subagents
|
|
174
|
+
// silently invisible there (`~/.wave/agents` resolved as a relative path).
|
|
175
|
+
const userWaveDir = join(homedir(), ".wave", "agents");
|
|
176
|
+
const userClaudeDir = join(homedir(), ".claude", "agents");
|
|
170
177
|
const builtinDir = getBuiltinSubagentsDir();
|
|
171
178
|
// Load configurations from all sources
|
|
172
179
|
let builtinConfigs = scanSubagentDirectory(builtinDir, "builtin");
|
|
@@ -62,14 +62,6 @@ export function roughTokenCountForMessages(messages) {
|
|
|
62
62
|
total += estimateTokens(block.result);
|
|
63
63
|
}
|
|
64
64
|
break;
|
|
65
|
-
case "bang":
|
|
66
|
-
if (block.command) {
|
|
67
|
-
total += estimateTokens(block.command);
|
|
68
|
-
}
|
|
69
|
-
if (block.output) {
|
|
70
|
-
total += estimateTokens(block.output);
|
|
71
|
-
}
|
|
72
|
-
break;
|
|
73
65
|
case "task_notification":
|
|
74
66
|
total += estimateTokens(block.summary);
|
|
75
67
|
break;
|
|
@@ -47,7 +47,8 @@ export declare function performPostCreationSetup(worktreePath: string, repoRoot:
|
|
|
47
47
|
* is MAX_PATH-limited — deep paths (e.g. node_modules) can fail with "Filename
|
|
48
48
|
* too long", leaving an orphan directory. When git fails we fall back to
|
|
49
49
|
* fs.rmSync with an extended-length path (bypasses MAX_PATH) and prune stale
|
|
50
|
-
* metadata. Failures are logged
|
|
50
|
+
* metadata. Failures are logged; when the directory survives, its branch is
|
|
51
|
+
* deliberately kept so the leftover checkout stays reachable through git.
|
|
51
52
|
*/
|
|
52
53
|
export declare function removeWorktree(info: WorktreeInfo): void;
|
|
53
54
|
/**
|
|
@@ -517,6 +517,36 @@ function toExtendedLengthPath(worktreePath) {
|
|
|
517
517
|
}
|
|
518
518
|
return `\\\\?\\${absolute}`;
|
|
519
519
|
}
|
|
520
|
+
/**
|
|
521
|
+
* Describe what is still on disk after a failed removal. Without this a log
|
|
522
|
+
* line cannot distinguish "directory already gone" from "the whole checkout is
|
|
523
|
+
* still sitting there".
|
|
524
|
+
*/
|
|
525
|
+
function probeResidue(worktreePath) {
|
|
526
|
+
try {
|
|
527
|
+
if (!fs.existsSync(worktreePath))
|
|
528
|
+
return "none";
|
|
529
|
+
const entries = fs.readdirSync(worktreePath);
|
|
530
|
+
const head = entries.slice(0, 5).join(",");
|
|
531
|
+
return `${entries.length}[${head}${entries.length > 5 ? ",…" : ""}]`;
|
|
532
|
+
}
|
|
533
|
+
catch (error) {
|
|
534
|
+
return `unknown(${error.code ?? "error"})`;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
/** Delete the worktree directory with fs.rmSync; returns the error, if any. */
|
|
538
|
+
function rmWorktreeDirWithFs(worktreePath) {
|
|
539
|
+
try {
|
|
540
|
+
fs.rmSync(toExtendedLengthPath(worktreePath), {
|
|
541
|
+
recursive: true,
|
|
542
|
+
force: true,
|
|
543
|
+
});
|
|
544
|
+
return null;
|
|
545
|
+
}
|
|
546
|
+
catch (error) {
|
|
547
|
+
return error;
|
|
548
|
+
}
|
|
549
|
+
}
|
|
520
550
|
/**
|
|
521
551
|
* Remove a git worktree and its branch.
|
|
522
552
|
*
|
|
@@ -525,28 +555,36 @@ function toExtendedLengthPath(worktreePath) {
|
|
|
525
555
|
* is MAX_PATH-limited — deep paths (e.g. node_modules) can fail with "Filename
|
|
526
556
|
* too long", leaving an orphan directory. When git fails we fall back to
|
|
527
557
|
* fs.rmSync with an extended-length path (bypasses MAX_PATH) and prune stale
|
|
528
|
-
* metadata. Failures are logged
|
|
558
|
+
* metadata. Failures are logged; when the directory survives, its branch is
|
|
559
|
+
* deliberately kept so the leftover checkout stays reachable through git.
|
|
529
560
|
*/
|
|
530
561
|
export function removeWorktree(info) {
|
|
531
562
|
const repoRoot = info.repoRoot;
|
|
532
|
-
// Get current branch in worktree before removing
|
|
533
|
-
let currentBranch;
|
|
534
|
-
try {
|
|
535
|
-
currentBranch = execFileSync("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
|
|
536
|
-
cwd: info.path,
|
|
537
|
-
encoding: "utf8",
|
|
538
|
-
stdio: ["ignore", "pipe", "ignore"],
|
|
539
|
-
}).trim();
|
|
540
|
-
}
|
|
541
|
-
catch {
|
|
542
|
-
// Ignore errors
|
|
543
|
-
}
|
|
544
563
|
// Remove worktree
|
|
545
564
|
try {
|
|
546
565
|
execFileSync("git", ["worktree", "remove", "--force", info.path], {
|
|
547
566
|
cwd: repoRoot,
|
|
548
567
|
stdio: ["ignore", "pipe", "pipe"],
|
|
549
568
|
});
|
|
569
|
+
// git exits 0 even when it did not delete the directory: on Windows it
|
|
570
|
+
// cannot remove the directory symlinks/junctions that pnpm's node_modules
|
|
571
|
+
// is made of, so it deletes the files, leaves the skeleton behind and still
|
|
572
|
+
// reports success. Verify on disk instead of trusting the exit status.
|
|
573
|
+
if (fs.existsSync(info.path)) {
|
|
574
|
+
logger.warn("git worktree remove reported success but the directory survived, falling back to fs.rmSync:", {
|
|
575
|
+
worktreePath: info.path,
|
|
576
|
+
residue: probeResidue(info.path),
|
|
577
|
+
});
|
|
578
|
+
const rmError = rmWorktreeDirWithFs(info.path);
|
|
579
|
+
if (rmError !== null) {
|
|
580
|
+
logger.error("Failed to remove worktree or branch:", {
|
|
581
|
+
worktreePath: info.path,
|
|
582
|
+
stage: "fs(after git success)",
|
|
583
|
+
error: rmError instanceof Error ? rmError.message : String(rmError),
|
|
584
|
+
residue: probeResidue(info.path),
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
}
|
|
550
588
|
}
|
|
551
589
|
catch (error) {
|
|
552
590
|
logger.warn("git worktree remove failed, falling back to fs.rmSync:", {
|
|
@@ -577,7 +615,19 @@ export function removeWorktree(info) {
|
|
|
577
615
|
// Ignore errors
|
|
578
616
|
}
|
|
579
617
|
}
|
|
580
|
-
//
|
|
618
|
+
// The directory, not any exit code, decides whether the removal happened. A
|
|
619
|
+
// surviving directory means its checkout (and any uncommitted work in it) is
|
|
620
|
+
// still on disk, and the branch is the only ref still leading back to it.
|
|
621
|
+
if (fs.existsSync(info.path)) {
|
|
622
|
+
logger.warn(`Worktree directory survived removal — keeping ${info.branch} so the leftover checkout stays reachable:`, {
|
|
623
|
+
worktreePath: info.path,
|
|
624
|
+
residue: probeResidue(info.path),
|
|
625
|
+
});
|
|
626
|
+
return;
|
|
627
|
+
}
|
|
628
|
+
// Delete the worktree's own branch. Branches the user checked out inside the
|
|
629
|
+
// worktree are never touched — they may hold commits that are not reachable
|
|
630
|
+
// from anywhere else (aligned with Claude Code).
|
|
581
631
|
try {
|
|
582
632
|
execFileSync("git", ["branch", "-D", info.branch], {
|
|
583
633
|
cwd: repoRoot,
|
|
@@ -587,26 +637,6 @@ export function removeWorktree(info) {
|
|
|
587
637
|
catch {
|
|
588
638
|
// Ignore errors
|
|
589
639
|
}
|
|
590
|
-
// Delete current branch if different and not protected
|
|
591
|
-
if (currentBranch &&
|
|
592
|
-
currentBranch !== info.branch &&
|
|
593
|
-
currentBranch !== "HEAD") {
|
|
594
|
-
const defaultRemoteBranch = getDefaultRemoteBranch(repoRoot);
|
|
595
|
-
const defaultBranchName = defaultRemoteBranch.split("/").pop();
|
|
596
|
-
if (currentBranch !== defaultBranchName &&
|
|
597
|
-
currentBranch !== "main" &&
|
|
598
|
-
currentBranch !== "master") {
|
|
599
|
-
try {
|
|
600
|
-
execFileSync("git", ["branch", "-D", currentBranch], {
|
|
601
|
-
cwd: repoRoot,
|
|
602
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
603
|
-
});
|
|
604
|
-
}
|
|
605
|
-
catch {
|
|
606
|
-
// Ignore errors
|
|
607
|
-
}
|
|
608
|
-
}
|
|
609
|
-
}
|
|
610
640
|
}
|
|
611
641
|
/**
|
|
612
642
|
* Validate that a worktree path is safe to remove before running git removal.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wave-agent-sdk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "SDK for building AI-powered development tools and agents",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -26,6 +26,10 @@
|
|
|
26
26
|
"types": "./dist/constants/tools.d.ts",
|
|
27
27
|
"default": "./dist/constants/tools.js"
|
|
28
28
|
},
|
|
29
|
+
"./stdio": {
|
|
30
|
+
"types": "./dist/stdio/index.d.ts",
|
|
31
|
+
"default": "./dist/stdio/index.js"
|
|
32
|
+
},
|
|
29
33
|
"./package.json": "./package.json",
|
|
30
34
|
"./*": "./*"
|
|
31
35
|
},
|
|
@@ -50,6 +54,7 @@
|
|
|
50
54
|
"@vscode/ripgrep": "^1.18.0",
|
|
51
55
|
"chokidar": "^4.0.3",
|
|
52
56
|
"cron-parser": "^5.5.0",
|
|
57
|
+
"fflate": "^0.8.3",
|
|
53
58
|
"fuzzysort": "^3.1.0",
|
|
54
59
|
"glob": "^13.0.0",
|
|
55
60
|
"lru-cache": "^11.3.5",
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { Container } from "../utils/container.js";
|
|
2
|
-
export interface BangManagerOptions {
|
|
3
|
-
workdir: string;
|
|
4
|
-
}
|
|
5
|
-
export interface CommandExecutionResult {
|
|
6
|
-
exitCode: number;
|
|
7
|
-
output: string;
|
|
8
|
-
}
|
|
9
|
-
export declare class BangManager {
|
|
10
|
-
private container;
|
|
11
|
-
private workdir;
|
|
12
|
-
isCommandRunning: boolean;
|
|
13
|
-
private currentProcess;
|
|
14
|
-
onCommandRunningChange?: (running: boolean) => void;
|
|
15
|
-
constructor(container: Container, options: BangManagerOptions);
|
|
16
|
-
private get messageManager();
|
|
17
|
-
/**
|
|
18
|
-
* Merged env (OS env overlaid with this session's settings snapshot) for
|
|
19
|
-
* bang-command subprocesses, so settings `env` vars reach them without
|
|
20
|
-
* polluting other sessions in one `wave --stdio` process.
|
|
21
|
-
*/
|
|
22
|
-
private get sessionEnv();
|
|
23
|
-
private setCommandRunning;
|
|
24
|
-
executeCommand(command: string): Promise<number>;
|
|
25
|
-
abortCommand(): void;
|
|
26
|
-
}
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
import { spawn } from "child_process";
|
|
2
|
-
import { resolveShellPath } from "../utils/shellResolver.js";
|
|
3
|
-
export class BangManager {
|
|
4
|
-
constructor(container, options) {
|
|
5
|
-
this.container = container;
|
|
6
|
-
this.isCommandRunning = false;
|
|
7
|
-
this.currentProcess = null;
|
|
8
|
-
this.workdir = options.workdir;
|
|
9
|
-
}
|
|
10
|
-
get messageManager() {
|
|
11
|
-
return this.container.get("MessageManager");
|
|
12
|
-
}
|
|
13
|
-
/**
|
|
14
|
-
* Merged env (OS env overlaid with this session's settings snapshot) for
|
|
15
|
-
* bang-command subprocesses, so settings `env` vars reach them without
|
|
16
|
-
* polluting other sessions in one `wave --stdio` process.
|
|
17
|
-
*/
|
|
18
|
-
get sessionEnv() {
|
|
19
|
-
return (this.container
|
|
20
|
-
.get("ConfigurationService")
|
|
21
|
-
?.getMergedEnv?.() ?? process.env);
|
|
22
|
-
}
|
|
23
|
-
setCommandRunning(isRunning) {
|
|
24
|
-
this.isCommandRunning = isRunning;
|
|
25
|
-
this.onCommandRunningChange?.(isRunning);
|
|
26
|
-
}
|
|
27
|
-
async executeCommand(command) {
|
|
28
|
-
if (this.isCommandRunning) {
|
|
29
|
-
throw new Error("Command already running");
|
|
30
|
-
}
|
|
31
|
-
this.setCommandRunning(true);
|
|
32
|
-
// Add bang placeholder
|
|
33
|
-
this.messageManager.addBangMessage(command);
|
|
34
|
-
return new Promise((resolve) => {
|
|
35
|
-
const child = spawn(command, {
|
|
36
|
-
shell: resolveShellPath() ?? true,
|
|
37
|
-
stdio: "pipe",
|
|
38
|
-
cwd: this.workdir,
|
|
39
|
-
env: {
|
|
40
|
-
...process.env,
|
|
41
|
-
...this.sessionEnv,
|
|
42
|
-
},
|
|
43
|
-
});
|
|
44
|
-
this.currentProcess = child;
|
|
45
|
-
let outputBuffer = "";
|
|
46
|
-
const updateOutput = (newData) => {
|
|
47
|
-
outputBuffer += newData;
|
|
48
|
-
};
|
|
49
|
-
child.stdout?.on("data", (data) => {
|
|
50
|
-
updateOutput(data.toString());
|
|
51
|
-
});
|
|
52
|
-
child.stderr?.on("data", (data) => {
|
|
53
|
-
updateOutput(data.toString());
|
|
54
|
-
});
|
|
55
|
-
child.on("exit", (code, signal) => {
|
|
56
|
-
const exitCode = code === null && signal ? 130 : (code ?? 0);
|
|
57
|
-
this.messageManager.completeBangMessage(command, exitCode, outputBuffer);
|
|
58
|
-
this.setCommandRunning(false);
|
|
59
|
-
this.currentProcess = null;
|
|
60
|
-
resolve(exitCode);
|
|
61
|
-
});
|
|
62
|
-
child.on("error", (error) => {
|
|
63
|
-
updateOutput(`\nError: ${error.message}\n`);
|
|
64
|
-
this.messageManager.completeBangMessage(command, 1, outputBuffer);
|
|
65
|
-
this.setCommandRunning(false);
|
|
66
|
-
this.currentProcess = null;
|
|
67
|
-
resolve(1);
|
|
68
|
-
});
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
|
-
abortCommand() {
|
|
72
|
-
if (this.currentProcess && this.isCommandRunning) {
|
|
73
|
-
this.currentProcess.kill("SIGKILL");
|
|
74
|
-
this.currentProcess = null;
|
|
75
|
-
this.isCommandRunning = false;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
}
|