oh-my-opencode 4.3.0 → 4.3.1
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/cli/index.js +49 -29
- package/dist/features/background-agent/parent-wake-notifier.d.ts +1 -0
- package/dist/index.js +377 -179
- package/dist/shared/bun-spawn-shim.d.ts +3 -0
- package/dist/shared/process-stream-reader.d.ts +3 -0
- package/dist/tools/glob/cli.d.ts +3 -1
- package/dist/tools/grep/cli.d.ts +4 -2
- package/dist/tools/shared/search-process-output.d.ts +7 -0
- package/package.json +12 -12
package/dist/cli/index.js
CHANGED
|
@@ -8093,8 +8093,14 @@ var init_external_plugin_detector = __esm(() => {
|
|
|
8093
8093
|
});
|
|
8094
8094
|
|
|
8095
8095
|
// src/shared/bun-spawn-shim.ts
|
|
8096
|
-
import {
|
|
8096
|
+
import {
|
|
8097
|
+
spawn as nodeSpawn,
|
|
8098
|
+
spawnSync as nodeSpawnSync
|
|
8099
|
+
} from "child_process";
|
|
8097
8100
|
import { Readable, Writable } from "stream";
|
|
8101
|
+
function getBunRuntime() {
|
|
8102
|
+
return typeof Bun === "undefined" ? undefined : runtime.Bun;
|
|
8103
|
+
}
|
|
8098
8104
|
function emptyReadableStream() {
|
|
8099
8105
|
return new ReadableStream({
|
|
8100
8106
|
start(controller) {
|
|
@@ -8127,6 +8133,24 @@ function resolveStdio(options) {
|
|
|
8127
8133
|
return options.stdio;
|
|
8128
8134
|
return [options.stdin ?? "ignore", options.stdout ?? "pipe", options.stderr ?? "inherit"];
|
|
8129
8135
|
}
|
|
8136
|
+
function createNodeSpawnOptions(options, platform = process.platform) {
|
|
8137
|
+
const nodeOptions = {
|
|
8138
|
+
stdio: resolveStdio(options),
|
|
8139
|
+
shell: false
|
|
8140
|
+
};
|
|
8141
|
+
if (options.cwd !== undefined)
|
|
8142
|
+
nodeOptions.cwd = options.cwd;
|
|
8143
|
+
if (options.env !== undefined)
|
|
8144
|
+
nodeOptions.env = options.env;
|
|
8145
|
+
if (options.detached !== undefined)
|
|
8146
|
+
nodeOptions.detached = options.detached;
|
|
8147
|
+
if (options.signal !== undefined)
|
|
8148
|
+
nodeOptions.signal = options.signal;
|
|
8149
|
+
if (platform === "win32") {
|
|
8150
|
+
nodeOptions.windowsHide = true;
|
|
8151
|
+
}
|
|
8152
|
+
return nodeOptions;
|
|
8153
|
+
}
|
|
8130
8154
|
function wrapNodeProcess(proc) {
|
|
8131
8155
|
let exitCode = null;
|
|
8132
8156
|
const exited = new Promise((resolve4, reject) => {
|
|
@@ -8169,28 +8193,24 @@ function wrapNodeProcess(proc) {
|
|
|
8169
8193
|
};
|
|
8170
8194
|
}
|
|
8171
8195
|
function spawn(cmdOrOpts, opts) {
|
|
8172
|
-
if (IS_BUN)
|
|
8173
|
-
return runtime.Bun.spawn(cmdOrOpts, opts);
|
|
8174
8196
|
const { cmd, opts: options } = resolveCommand(cmdOrOpts, opts);
|
|
8197
|
+
const bun = getBunRuntime();
|
|
8198
|
+
if (bun)
|
|
8199
|
+
return bun.spawn(cmd, options);
|
|
8175
8200
|
const [bin, ...args] = cmd;
|
|
8176
|
-
|
|
8177
|
-
|
|
8178
|
-
|
|
8179
|
-
|
|
8180
|
-
detached: options.detached,
|
|
8181
|
-
signal: options.signal
|
|
8182
|
-
});
|
|
8201
|
+
if (bin === undefined) {
|
|
8202
|
+
throw new Error("Cannot spawn an empty command");
|
|
8203
|
+
}
|
|
8204
|
+
const proc = nodeSpawn(bin, args, createNodeSpawnOptions(options));
|
|
8183
8205
|
return wrapNodeProcess(proc);
|
|
8184
8206
|
}
|
|
8185
|
-
var runtime
|
|
8207
|
+
var runtime;
|
|
8186
8208
|
var init_bun_spawn_shim = __esm(() => {
|
|
8187
8209
|
runtime = globalThis;
|
|
8188
|
-
IS_BUN = typeof runtime.Bun !== "undefined";
|
|
8189
8210
|
});
|
|
8190
8211
|
|
|
8191
8212
|
// src/shared/archive-entry-validator.ts
|
|
8192
8213
|
var init_archive_entry_validator = () => {};
|
|
8193
|
-
|
|
8194
8214
|
// src/shared/zip-entry-listing/python-zip-entry-listing.ts
|
|
8195
8215
|
var init_python_zip_entry_listing = __esm(() => {
|
|
8196
8216
|
init_bun_spawn_shim();
|
|
@@ -8234,10 +8254,10 @@ var init_zip_extractor = __esm(() => {
|
|
|
8234
8254
|
});
|
|
8235
8255
|
|
|
8236
8256
|
// src/shared/bun-file-shim.ts
|
|
8237
|
-
var runtime2,
|
|
8257
|
+
var runtime2, IS_BUN;
|
|
8238
8258
|
var init_bun_file_shim = __esm(() => {
|
|
8239
8259
|
runtime2 = globalThis;
|
|
8240
|
-
|
|
8260
|
+
IS_BUN = typeof runtime2.Bun !== "undefined";
|
|
8241
8261
|
});
|
|
8242
8262
|
|
|
8243
8263
|
// src/shared/binary-downloader.ts
|
|
@@ -55420,7 +55440,7 @@ var {
|
|
|
55420
55440
|
// package.json
|
|
55421
55441
|
var package_default = {
|
|
55422
55442
|
name: "oh-my-opencode",
|
|
55423
|
-
version: "4.3.
|
|
55443
|
+
version: "4.3.1",
|
|
55424
55444
|
description: "The Best AI Agent Harness - Batteries-Included OpenCode Plugin with Multi-Model Orchestration, Parallel Background Agents, and Crafted LSP/AST Tools",
|
|
55425
55445
|
main: "./dist/index.js",
|
|
55426
55446
|
types: "dist/index.d.ts",
|
|
@@ -55530,17 +55550,17 @@ var package_default = {
|
|
|
55530
55550
|
zod: "^4.4.3"
|
|
55531
55551
|
},
|
|
55532
55552
|
optionalDependencies: {
|
|
55533
|
-
"oh-my-opencode-darwin-arm64": "4.3.
|
|
55534
|
-
"oh-my-opencode-darwin-x64": "4.3.
|
|
55535
|
-
"oh-my-opencode-darwin-x64-baseline": "4.3.
|
|
55536
|
-
"oh-my-opencode-linux-arm64": "4.3.
|
|
55537
|
-
"oh-my-opencode-linux-arm64-musl": "4.3.
|
|
55538
|
-
"oh-my-opencode-linux-x64": "4.3.
|
|
55539
|
-
"oh-my-opencode-linux-x64-baseline": "4.3.
|
|
55540
|
-
"oh-my-opencode-linux-x64-musl": "4.3.
|
|
55541
|
-
"oh-my-opencode-linux-x64-musl-baseline": "4.3.
|
|
55542
|
-
"oh-my-opencode-windows-x64": "4.3.
|
|
55543
|
-
"oh-my-opencode-windows-x64-baseline": "4.3.
|
|
55553
|
+
"oh-my-opencode-darwin-arm64": "4.3.1",
|
|
55554
|
+
"oh-my-opencode-darwin-x64": "4.3.1",
|
|
55555
|
+
"oh-my-opencode-darwin-x64-baseline": "4.3.1",
|
|
55556
|
+
"oh-my-opencode-linux-arm64": "4.3.1",
|
|
55557
|
+
"oh-my-opencode-linux-arm64-musl": "4.3.1",
|
|
55558
|
+
"oh-my-opencode-linux-x64": "4.3.1",
|
|
55559
|
+
"oh-my-opencode-linux-x64-baseline": "4.3.1",
|
|
55560
|
+
"oh-my-opencode-linux-x64-musl": "4.3.1",
|
|
55561
|
+
"oh-my-opencode-linux-x64-musl-baseline": "4.3.1",
|
|
55562
|
+
"oh-my-opencode-windows-x64": "4.3.1",
|
|
55563
|
+
"oh-my-opencode-windows-x64-baseline": "4.3.1"
|
|
55544
55564
|
},
|
|
55545
55565
|
overrides: {
|
|
55546
55566
|
hono: "^4.12.18",
|
|
@@ -82985,7 +83005,7 @@ import { basename as basename5 } from "path";
|
|
|
82985
83005
|
import { accessSync as accessSync3, constants as constants6 } from "fs";
|
|
82986
83006
|
import { delimiter as delimiter2, join as join39 } from "path";
|
|
82987
83007
|
var runtime3 = globalThis;
|
|
82988
|
-
var
|
|
83008
|
+
var IS_BUN2 = typeof runtime3.Bun !== "undefined";
|
|
82989
83009
|
function isUnsafeCommandName(commandName) {
|
|
82990
83010
|
if (commandName.includes("/") || commandName.includes("\\"))
|
|
82991
83011
|
return true;
|
|
@@ -83020,7 +83040,7 @@ function bunWhich(commandName) {
|
|
|
83020
83040
|
return null;
|
|
83021
83041
|
if (isUnsafeCommandName(commandName))
|
|
83022
83042
|
return null;
|
|
83023
|
-
if (
|
|
83043
|
+
if (IS_BUN2)
|
|
83024
83044
|
return runtime3.Bun?.which(commandName) ?? null;
|
|
83025
83045
|
const pathValue = resolvePathValue();
|
|
83026
83046
|
if (!pathValue)
|
|
@@ -62,6 +62,7 @@ export declare class ParentWakeNotifier {
|
|
|
62
62
|
private resolveParentWakePromptContext;
|
|
63
63
|
private cloneParentWake;
|
|
64
64
|
private trackDispatchedParentWake;
|
|
65
|
+
private isSameParentWake;
|
|
65
66
|
private loadParentWakeSessionMessages;
|
|
66
67
|
private getParentWakeMessageRole;
|
|
67
68
|
private getParentWakeMessageFinish;
|
package/dist/index.js
CHANGED
|
@@ -9157,8 +9157,14 @@ var init_external_plugin_detector = __esm(() => {
|
|
|
9157
9157
|
});
|
|
9158
9158
|
|
|
9159
9159
|
// src/shared/bun-spawn-shim.ts
|
|
9160
|
-
import {
|
|
9160
|
+
import {
|
|
9161
|
+
spawn as nodeSpawn,
|
|
9162
|
+
spawnSync as nodeSpawnSync
|
|
9163
|
+
} from "child_process";
|
|
9161
9164
|
import { Readable, Writable } from "stream";
|
|
9165
|
+
function getBunRuntime() {
|
|
9166
|
+
return typeof Bun === "undefined" ? undefined : runtime.Bun;
|
|
9167
|
+
}
|
|
9162
9168
|
function emptyReadableStream() {
|
|
9163
9169
|
return new ReadableStream({
|
|
9164
9170
|
start(controller) {
|
|
@@ -9191,6 +9197,38 @@ function resolveStdio(options) {
|
|
|
9191
9197
|
return options.stdio;
|
|
9192
9198
|
return [options.stdin ?? "ignore", options.stdout ?? "pipe", options.stderr ?? "inherit"];
|
|
9193
9199
|
}
|
|
9200
|
+
function createNodeSpawnOptions(options, platform = process.platform) {
|
|
9201
|
+
const nodeOptions = {
|
|
9202
|
+
stdio: resolveStdio(options),
|
|
9203
|
+
shell: false
|
|
9204
|
+
};
|
|
9205
|
+
if (options.cwd !== undefined)
|
|
9206
|
+
nodeOptions.cwd = options.cwd;
|
|
9207
|
+
if (options.env !== undefined)
|
|
9208
|
+
nodeOptions.env = options.env;
|
|
9209
|
+
if (options.detached !== undefined)
|
|
9210
|
+
nodeOptions.detached = options.detached;
|
|
9211
|
+
if (options.signal !== undefined)
|
|
9212
|
+
nodeOptions.signal = options.signal;
|
|
9213
|
+
if (platform === "win32") {
|
|
9214
|
+
nodeOptions.windowsHide = true;
|
|
9215
|
+
}
|
|
9216
|
+
return nodeOptions;
|
|
9217
|
+
}
|
|
9218
|
+
function createNodeSpawnSyncOptions(options, platform = process.platform) {
|
|
9219
|
+
const nodeOptions = {
|
|
9220
|
+
stdio: resolveStdio(options),
|
|
9221
|
+
shell: false
|
|
9222
|
+
};
|
|
9223
|
+
if (options.cwd !== undefined)
|
|
9224
|
+
nodeOptions.cwd = options.cwd;
|
|
9225
|
+
if (options.env !== undefined)
|
|
9226
|
+
nodeOptions.env = options.env;
|
|
9227
|
+
if (platform === "win32") {
|
|
9228
|
+
nodeOptions.windowsHide = true;
|
|
9229
|
+
}
|
|
9230
|
+
return nodeOptions;
|
|
9231
|
+
}
|
|
9194
9232
|
function wrapNodeProcess(proc) {
|
|
9195
9233
|
let exitCode = null;
|
|
9196
9234
|
const exited = new Promise((resolve5, reject) => {
|
|
@@ -9232,42 +9270,45 @@ function wrapNodeProcess(proc) {
|
|
|
9232
9270
|
}
|
|
9233
9271
|
};
|
|
9234
9272
|
}
|
|
9273
|
+
function toSpawnSyncBuffer(output) {
|
|
9274
|
+
if (output === null) {
|
|
9275
|
+
return;
|
|
9276
|
+
}
|
|
9277
|
+
return Buffer.isBuffer(output) ? output : Buffer.from(output, "utf8");
|
|
9278
|
+
}
|
|
9235
9279
|
function spawn2(cmdOrOpts, opts) {
|
|
9236
|
-
if (IS_BUN)
|
|
9237
|
-
return runtime.Bun.spawn(cmdOrOpts, opts);
|
|
9238
9280
|
const { cmd, opts: options } = resolveCommand(cmdOrOpts, opts);
|
|
9281
|
+
const bun = getBunRuntime();
|
|
9282
|
+
if (bun)
|
|
9283
|
+
return bun.spawn(cmd, options);
|
|
9239
9284
|
const [bin, ...args] = cmd;
|
|
9240
|
-
|
|
9241
|
-
|
|
9242
|
-
|
|
9243
|
-
|
|
9244
|
-
detached: options.detached,
|
|
9245
|
-
signal: options.signal
|
|
9246
|
-
});
|
|
9285
|
+
if (bin === undefined) {
|
|
9286
|
+
throw new Error("Cannot spawn an empty command");
|
|
9287
|
+
}
|
|
9288
|
+
const proc = nodeSpawn(bin, args, createNodeSpawnOptions(options));
|
|
9247
9289
|
return wrapNodeProcess(proc);
|
|
9248
9290
|
}
|
|
9249
9291
|
function spawnSync(cmdOrOpts, opts) {
|
|
9250
|
-
if (IS_BUN)
|
|
9251
|
-
return runtime.Bun.spawnSync(cmdOrOpts, opts);
|
|
9252
9292
|
const { cmd, opts: options } = resolveCommand(cmdOrOpts, opts);
|
|
9293
|
+
const bun = getBunRuntime();
|
|
9294
|
+
if (bun)
|
|
9295
|
+
return bun.spawnSync(cmd, options);
|
|
9253
9296
|
const [bin, ...args] = cmd;
|
|
9254
|
-
|
|
9255
|
-
|
|
9256
|
-
|
|
9257
|
-
|
|
9258
|
-
});
|
|
9297
|
+
if (bin === undefined) {
|
|
9298
|
+
throw new Error("Cannot spawnSync an empty command");
|
|
9299
|
+
}
|
|
9300
|
+
const result = nodeSpawnSync(bin, args, createNodeSpawnSyncOptions(options));
|
|
9259
9301
|
return {
|
|
9260
9302
|
exitCode: result.status ?? 1,
|
|
9261
|
-
stdout: result.stdout
|
|
9262
|
-
stderr: result.stderr
|
|
9303
|
+
stdout: toSpawnSyncBuffer(result.stdout),
|
|
9304
|
+
stderr: toSpawnSyncBuffer(result.stderr),
|
|
9263
9305
|
success: (result.status ?? 1) === 0,
|
|
9264
9306
|
pid: result.pid ?? -1
|
|
9265
9307
|
};
|
|
9266
9308
|
}
|
|
9267
|
-
var runtime
|
|
9309
|
+
var runtime;
|
|
9268
9310
|
var init_bun_spawn_shim = __esm(() => {
|
|
9269
9311
|
runtime = globalThis;
|
|
9270
|
-
IS_BUN = typeof runtime.Bun !== "undefined";
|
|
9271
9312
|
});
|
|
9272
9313
|
|
|
9273
9314
|
// src/shared/archive-entry-validator.ts
|
|
@@ -9326,6 +9367,52 @@ function validateArchiveEntries(entries, destDir) {
|
|
|
9326
9367
|
}
|
|
9327
9368
|
var init_archive_entry_validator = () => {};
|
|
9328
9369
|
|
|
9370
|
+
// src/shared/process-stream-reader.ts
|
|
9371
|
+
function bufferFromChunk(chunk) {
|
|
9372
|
+
if (Buffer.isBuffer(chunk)) {
|
|
9373
|
+
return chunk;
|
|
9374
|
+
}
|
|
9375
|
+
if (chunk instanceof Uint8Array) {
|
|
9376
|
+
return Buffer.from(chunk);
|
|
9377
|
+
}
|
|
9378
|
+
if (typeof chunk === "string") {
|
|
9379
|
+
return Buffer.from(chunk, "utf8");
|
|
9380
|
+
}
|
|
9381
|
+
throw new TypeError(`Unsupported process stream chunk type: ${typeof chunk}`);
|
|
9382
|
+
}
|
|
9383
|
+
async function readWebStream(stream) {
|
|
9384
|
+
const reader = stream.getReader();
|
|
9385
|
+
const chunks = [];
|
|
9386
|
+
try {
|
|
9387
|
+
while (true) {
|
|
9388
|
+
const result = await reader.read();
|
|
9389
|
+
if (result.done) {
|
|
9390
|
+
return chunks;
|
|
9391
|
+
}
|
|
9392
|
+
chunks.push(Buffer.from(result.value));
|
|
9393
|
+
}
|
|
9394
|
+
} finally {
|
|
9395
|
+
reader.releaseLock();
|
|
9396
|
+
}
|
|
9397
|
+
}
|
|
9398
|
+
async function readNodeStream(stream) {
|
|
9399
|
+
const chunks = [];
|
|
9400
|
+
for await (const chunk of stream) {
|
|
9401
|
+
chunks.push(bufferFromChunk(chunk));
|
|
9402
|
+
}
|
|
9403
|
+
return chunks;
|
|
9404
|
+
}
|
|
9405
|
+
function isWebReadableStream(stream) {
|
|
9406
|
+
return typeof ReadableStream !== "undefined" && stream instanceof ReadableStream;
|
|
9407
|
+
}
|
|
9408
|
+
async function readProcessStream(stream) {
|
|
9409
|
+
if (!stream) {
|
|
9410
|
+
return "";
|
|
9411
|
+
}
|
|
9412
|
+
const chunks = isWebReadableStream(stream) ? await readWebStream(stream) : await readNodeStream(stream);
|
|
9413
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
9414
|
+
}
|
|
9415
|
+
|
|
9329
9416
|
// src/shared/zip-entry-listing/python-zip-entry-listing.ts
|
|
9330
9417
|
function isPythonZipListingAvailable() {
|
|
9331
9418
|
const proc = spawnSync(["python3", "--version"], {
|
|
@@ -9363,8 +9450,8 @@ async function listZipEntriesWithPython(archivePath) {
|
|
|
9363
9450
|
});
|
|
9364
9451
|
const [exitCode, stdout, stderr] = await Promise.all([
|
|
9365
9452
|
proc.exited,
|
|
9366
|
-
|
|
9367
|
-
|
|
9453
|
+
readProcessStream(proc.stdout),
|
|
9454
|
+
readProcessStream(proc.stderr)
|
|
9368
9455
|
]);
|
|
9369
9456
|
if (exitCode !== 0) {
|
|
9370
9457
|
throw new Error(`zip entry listing failed (exit ${exitCode}): ${stderr}`);
|
|
@@ -9431,8 +9518,8 @@ async function listZipEntriesWithPowerShell(archivePath, escapePowerShellPath, e
|
|
|
9431
9518
|
});
|
|
9432
9519
|
const [exitCode, stdout, stderr] = await Promise.all([
|
|
9433
9520
|
proc.exited,
|
|
9434
|
-
|
|
9435
|
-
|
|
9521
|
+
readProcessStream(proc.stdout),
|
|
9522
|
+
readProcessStream(proc.stderr)
|
|
9436
9523
|
]);
|
|
9437
9524
|
if (exitCode !== 0) {
|
|
9438
9525
|
throw new Error(`zip entry listing failed (exit ${exitCode}): ${stderr}`);
|
|
@@ -9495,8 +9582,8 @@ async function listZipEntriesWithTar(archivePath) {
|
|
|
9495
9582
|
});
|
|
9496
9583
|
const [exitCode, stdout, stderr] = await Promise.all([
|
|
9497
9584
|
proc.exited,
|
|
9498
|
-
|
|
9499
|
-
|
|
9585
|
+
readProcessStream(proc.stdout),
|
|
9586
|
+
readProcessStream(proc.stderr)
|
|
9500
9587
|
]);
|
|
9501
9588
|
if (exitCode !== 0) {
|
|
9502
9589
|
throw new Error(`zip entry listing failed (exit ${exitCode}): ${stderr}`);
|
|
@@ -9516,8 +9603,8 @@ async function readZipSymlinkTarget(archivePath, entryPath) {
|
|
|
9516
9603
|
});
|
|
9517
9604
|
const [exitCode, stdout, stderr] = await Promise.all([
|
|
9518
9605
|
proc.exited,
|
|
9519
|
-
|
|
9520
|
-
|
|
9606
|
+
readProcessStream(proc.stdout),
|
|
9607
|
+
readProcessStream(proc.stderr)
|
|
9521
9608
|
]);
|
|
9522
9609
|
if (exitCode !== 0) {
|
|
9523
9610
|
throw new Error(`zip symlink target read failed (exit ${exitCode}): ${stderr}`);
|
|
@@ -9560,8 +9647,8 @@ async function listZipEntriesWithZipInfo(archivePath) {
|
|
|
9560
9647
|
});
|
|
9561
9648
|
const [exitCode, stdout, stderr] = await Promise.all([
|
|
9562
9649
|
proc.exited,
|
|
9563
|
-
|
|
9564
|
-
|
|
9650
|
+
readProcessStream(proc.stdout),
|
|
9651
|
+
readProcessStream(proc.stderr)
|
|
9565
9652
|
]);
|
|
9566
9653
|
if (exitCode !== 0) {
|
|
9567
9654
|
throw new Error(`zip entry listing failed (exit ${exitCode}): ${stderr}`);
|
|
@@ -9657,7 +9744,7 @@ async function extractZip(archivePath, destDir) {
|
|
|
9657
9744
|
}
|
|
9658
9745
|
const exitCode = await proc.exited;
|
|
9659
9746
|
if (exitCode !== 0) {
|
|
9660
|
-
const stderr = await
|
|
9747
|
+
const stderr = await readProcessStream(proc.stderr);
|
|
9661
9748
|
throw new Error(`zip extraction failed (exit ${exitCode}): ${stderr}`);
|
|
9662
9749
|
}
|
|
9663
9750
|
}
|
|
@@ -9716,20 +9803,20 @@ function createNodeFile(path6) {
|
|
|
9716
9803
|
};
|
|
9717
9804
|
}
|
|
9718
9805
|
function bunFile(path6) {
|
|
9719
|
-
if (
|
|
9806
|
+
if (IS_BUN)
|
|
9720
9807
|
return runtime2.Bun.file(path6);
|
|
9721
9808
|
return createNodeFile(path6);
|
|
9722
9809
|
}
|
|
9723
9810
|
async function bunWrite(path6, data) {
|
|
9724
|
-
if (
|
|
9811
|
+
if (IS_BUN)
|
|
9725
9812
|
return runtime2.Bun.write(path6, data);
|
|
9726
9813
|
await writeFile(path6, toWritableData(data));
|
|
9727
9814
|
return byteLength(data);
|
|
9728
9815
|
}
|
|
9729
|
-
var runtime2,
|
|
9816
|
+
var runtime2, IS_BUN;
|
|
9730
9817
|
var init_bun_file_shim = __esm(() => {
|
|
9731
9818
|
runtime2 = globalThis;
|
|
9732
|
-
|
|
9819
|
+
IS_BUN = typeof runtime2.Bun !== "undefined";
|
|
9733
9820
|
});
|
|
9734
9821
|
|
|
9735
9822
|
// src/shared/binary-downloader.ts
|
|
@@ -9766,7 +9853,7 @@ async function extractTarGz(archivePath, destDir, options) {
|
|
|
9766
9853
|
});
|
|
9767
9854
|
const exitCode = await proc.exited;
|
|
9768
9855
|
if (exitCode !== 0) {
|
|
9769
|
-
const stderr = await
|
|
9856
|
+
const stderr = await readProcessStream(proc.stderr);
|
|
9770
9857
|
if (isTarTraversalErrorOutput(stderr)) {
|
|
9771
9858
|
throw new Error(`Unsafe archive entry: path contains path traversal (${archivePath})`);
|
|
9772
9859
|
}
|
|
@@ -9816,8 +9903,8 @@ async function listTarEntries(archivePath, cwd) {
|
|
|
9816
9903
|
});
|
|
9817
9904
|
const [exitCode, stdout, stderr] = await Promise.all([
|
|
9818
9905
|
proc.exited,
|
|
9819
|
-
|
|
9820
|
-
|
|
9906
|
+
readProcessStream(proc.stdout),
|
|
9907
|
+
readProcessStream(proc.stderr)
|
|
9821
9908
|
]);
|
|
9822
9909
|
if (isTarTraversalErrorOutput(stderr)) {
|
|
9823
9910
|
throw new Error(`Unsafe archive entry: path contains path traversal (${archivePath})`);
|
|
@@ -69279,6 +69366,8 @@ async function buildReadyNotificationContent(ctx, input) {
|
|
|
69279
69366
|
|
|
69280
69367
|
// src/hooks/session-notification-sender.ts
|
|
69281
69368
|
init_shared();
|
|
69369
|
+
import { execFile } from "child_process";
|
|
69370
|
+
import { promisify as promisify2 } from "util";
|
|
69282
69371
|
import { platform } from "os";
|
|
69283
69372
|
|
|
69284
69373
|
// src/hooks/session-notification-utils.ts
|
|
@@ -69288,7 +69377,7 @@ init_logger();
|
|
|
69288
69377
|
import { accessSync as accessSync2, constants as constants4 } from "fs";
|
|
69289
69378
|
import { delimiter, join as join29 } from "path";
|
|
69290
69379
|
var runtime3 = globalThis;
|
|
69291
|
-
var
|
|
69380
|
+
var IS_BUN2 = typeof runtime3.Bun !== "undefined";
|
|
69292
69381
|
function isUnsafeCommandName(commandName) {
|
|
69293
69382
|
if (commandName.includes("/") || commandName.includes("\\"))
|
|
69294
69383
|
return true;
|
|
@@ -69323,7 +69412,7 @@ function bunWhich(commandName) {
|
|
|
69323
69412
|
return null;
|
|
69324
69413
|
if (isUnsafeCommandName(commandName))
|
|
69325
69414
|
return null;
|
|
69326
|
-
if (
|
|
69415
|
+
if (IS_BUN2)
|
|
69327
69416
|
return runtime3.Bun?.which(commandName) ?? null;
|
|
69328
69417
|
const pathValue = resolvePathValue();
|
|
69329
69418
|
if (!pathValue)
|
|
@@ -69459,14 +69548,33 @@ function getDefaultSoundPath(platform2) {
|
|
|
69459
69548
|
}
|
|
69460
69549
|
}
|
|
69461
69550
|
var hasLoggedUnavailableShellHelper = false;
|
|
69462
|
-
function
|
|
69463
|
-
if (typeof ctx
|
|
69464
|
-
return
|
|
69551
|
+
function getShellRunner(ctx) {
|
|
69552
|
+
if (typeof ctx.$ === "function")
|
|
69553
|
+
return ctx.$;
|
|
69465
69554
|
if (!hasLoggedUnavailableShellHelper) {
|
|
69466
69555
|
hasLoggedUnavailableShellHelper = true;
|
|
69467
|
-
log("[session-notification] ctx.$ unavailable;
|
|
69556
|
+
log("[session-notification] ctx.$ unavailable; falling back to child_process.execFile");
|
|
69468
69557
|
}
|
|
69469
|
-
return
|
|
69558
|
+
return;
|
|
69559
|
+
}
|
|
69560
|
+
function logCommandFailure(commandName, error) {
|
|
69561
|
+
log("[session-notification] notification command failed", {
|
|
69562
|
+
commandName,
|
|
69563
|
+
error: typeof error === "string" ? error : error.message
|
|
69564
|
+
});
|
|
69565
|
+
}
|
|
69566
|
+
function logOperationFailure(operation, error) {
|
|
69567
|
+
log("[session-notification] notification operation failed", {
|
|
69568
|
+
operation,
|
|
69569
|
+
error: typeof error === "string" ? error : error.message
|
|
69570
|
+
});
|
|
69571
|
+
}
|
|
69572
|
+
async function runQuiet(command) {
|
|
69573
|
+
if (typeof command.quiet === "function") {
|
|
69574
|
+
await command.quiet();
|
|
69575
|
+
return;
|
|
69576
|
+
}
|
|
69577
|
+
await command;
|
|
69470
69578
|
}
|
|
69471
69579
|
async function runQuietNothrow(command) {
|
|
69472
69580
|
const safeCommand = typeof command.nothrow === "function" ? command.nothrow() : command;
|
|
@@ -69476,85 +69584,124 @@ async function runQuietNothrow(command) {
|
|
|
69476
69584
|
}
|
|
69477
69585
|
await safeCommand;
|
|
69478
69586
|
}
|
|
69479
|
-
async function
|
|
69480
|
-
|
|
69587
|
+
async function runExecFile(commandPath, args) {
|
|
69588
|
+
const execFileAsync = promisify2(execFile);
|
|
69589
|
+
await execFileAsync(commandPath, [...args], { windowsHide: true });
|
|
69590
|
+
}
|
|
69591
|
+
async function runNotificationCommand(ctx, commandPath, args, shellCommand, shellFailureMode = "nothrow") {
|
|
69592
|
+
const shell = getShellRunner(ctx);
|
|
69593
|
+
if (shell) {
|
|
69594
|
+
if (shellFailureMode === "throw") {
|
|
69595
|
+
await runQuiet(shellCommand(shell));
|
|
69596
|
+
return;
|
|
69597
|
+
}
|
|
69598
|
+
await runQuietNothrow(shellCommand(shell));
|
|
69481
69599
|
return;
|
|
69482
|
-
|
|
69483
|
-
|
|
69484
|
-
|
|
69485
|
-
|
|
69486
|
-
|
|
69487
|
-
|
|
69488
|
-
|
|
69489
|
-
|
|
69490
|
-
|
|
69491
|
-
|
|
69492
|
-
|
|
69493
|
-
|
|
69494
|
-
|
|
69495
|
-
|
|
69496
|
-
|
|
69497
|
-
|
|
69498
|
-
|
|
69600
|
+
}
|
|
69601
|
+
await runExecFile(commandPath, args);
|
|
69602
|
+
}
|
|
69603
|
+
async function sendSessionNotification(ctx, platform2, title, message) {
|
|
69604
|
+
try {
|
|
69605
|
+
switch (platform2) {
|
|
69606
|
+
case "darwin": {
|
|
69607
|
+
const cmuxPath = await getCmuxPath();
|
|
69608
|
+
if (cmuxPath) {
|
|
69609
|
+
try {
|
|
69610
|
+
await runNotificationCommand(ctx, cmuxPath, ["notify", "--title", title, "--body", message], (shell) => shell`${cmuxPath} notify --title ${title} --body ${message}`, "throw");
|
|
69611
|
+
break;
|
|
69612
|
+
} catch (error) {
|
|
69613
|
+
if (error instanceof Error) {
|
|
69614
|
+
logCommandFailure("cmux", error);
|
|
69615
|
+
} else {
|
|
69616
|
+
logCommandFailure("cmux", String(error));
|
|
69617
|
+
}
|
|
69499
69618
|
}
|
|
69500
|
-
|
|
69501
|
-
|
|
69619
|
+
}
|
|
69620
|
+
const terminalNotifierPath = await getTerminalNotifierPath();
|
|
69621
|
+
if (terminalNotifierPath) {
|
|
69622
|
+
const bundleId = process.env.__CFBundleIdentifier;
|
|
69623
|
+
const args = bundleId ? ["-title", title, "-message", message, "-activate", bundleId] : ["-title", title, "-message", message];
|
|
69624
|
+
try {
|
|
69625
|
+
await runNotificationCommand(ctx, terminalNotifierPath, args, (shell) => bundleId ? shell`${terminalNotifierPath} -title ${title} -message ${message} -activate ${bundleId}` : shell`${terminalNotifierPath} -title ${title} -message ${message}`, "throw");
|
|
69626
|
+
break;
|
|
69627
|
+
} catch (error) {
|
|
69628
|
+
if (error instanceof Error) {
|
|
69629
|
+
logCommandFailure("terminal-notifier", error);
|
|
69630
|
+
} else {
|
|
69631
|
+
logCommandFailure("terminal-notifier", String(error));
|
|
69632
|
+
}
|
|
69633
|
+
}
|
|
69634
|
+
}
|
|
69635
|
+
const osascriptPath = await getOsascriptPath();
|
|
69636
|
+
if (!osascriptPath)
|
|
69637
|
+
return;
|
|
69638
|
+
const escapedTitle = escapeAppleScriptText(title);
|
|
69639
|
+
const escapedMessage = escapeAppleScriptText(message);
|
|
69640
|
+
const appleScript = 'display notification "' + escapedMessage + '" with title "' + escapedTitle + '"';
|
|
69641
|
+
await runNotificationCommand(ctx, osascriptPath, ["-e", appleScript], (shell) => shell`${osascriptPath} -e ${appleScript}`);
|
|
69642
|
+
break;
|
|
69643
|
+
}
|
|
69644
|
+
case "linux": {
|
|
69645
|
+
const notifySendPath = await getNotifySendPath();
|
|
69646
|
+
if (!notifySendPath)
|
|
69647
|
+
return;
|
|
69648
|
+
await runNotificationCommand(ctx, notifySendPath, [title, message], (shell) => shell`${notifySendPath} ${title} ${message} 2>/dev/null`);
|
|
69649
|
+
break;
|
|
69650
|
+
}
|
|
69651
|
+
case "win32": {
|
|
69652
|
+
const powershellPath = await getPowershellPath();
|
|
69653
|
+
if (!powershellPath)
|
|
69654
|
+
return;
|
|
69655
|
+
const toastScript = buildWindowsToastScript(title, message);
|
|
69656
|
+
await runNotificationCommand(ctx, powershellPath, ["-Command", toastScript], (shell) => shell`${powershellPath} -Command ${toastScript}`);
|
|
69657
|
+
break;
|
|
69502
69658
|
}
|
|
69503
|
-
const osascriptPath = await getOsascriptPath();
|
|
69504
|
-
if (!osascriptPath)
|
|
69505
|
-
return;
|
|
69506
|
-
const escapedTitle = escapeAppleScriptText(title);
|
|
69507
|
-
const escapedMessage = escapeAppleScriptText(message);
|
|
69508
|
-
await runQuietNothrow(ctx.$`${osascriptPath} -e ${'display notification "' + escapedMessage + '" with title "' + escapedTitle + '"'}`);
|
|
69509
|
-
break;
|
|
69510
|
-
}
|
|
69511
|
-
case "linux": {
|
|
69512
|
-
const notifySendPath = await getNotifySendPath();
|
|
69513
|
-
if (!notifySendPath)
|
|
69514
|
-
return;
|
|
69515
|
-
await runQuietNothrow(ctx.$`${notifySendPath} ${title} ${message} 2>/dev/null`);
|
|
69516
|
-
break;
|
|
69517
69659
|
}
|
|
69518
|
-
|
|
69519
|
-
|
|
69520
|
-
|
|
69521
|
-
|
|
69522
|
-
|
|
69523
|
-
await runQuietNothrow(ctx.$`${powershellPath} -Command ${toastScript}`);
|
|
69524
|
-
break;
|
|
69660
|
+
} catch (error) {
|
|
69661
|
+
if (error instanceof Error) {
|
|
69662
|
+
logOperationFailure("send", error);
|
|
69663
|
+
} else {
|
|
69664
|
+
logOperationFailure("send", String(error));
|
|
69525
69665
|
}
|
|
69526
69666
|
}
|
|
69527
69667
|
}
|
|
69528
69668
|
async function playSessionNotificationSound(ctx, platform2, soundPath) {
|
|
69529
|
-
|
|
69530
|
-
|
|
69531
|
-
|
|
69532
|
-
|
|
69533
|
-
|
|
69534
|
-
|
|
69535
|
-
|
|
69536
|
-
|
|
69537
|
-
|
|
69538
|
-
|
|
69539
|
-
|
|
69540
|
-
|
|
69541
|
-
|
|
69542
|
-
|
|
69543
|
-
|
|
69544
|
-
|
|
69545
|
-
|
|
69546
|
-
|
|
69669
|
+
try {
|
|
69670
|
+
switch (platform2) {
|
|
69671
|
+
case "darwin": {
|
|
69672
|
+
const afplayPath = await getAfplayPath();
|
|
69673
|
+
if (!afplayPath)
|
|
69674
|
+
return;
|
|
69675
|
+
await runNotificationCommand(ctx, afplayPath, [soundPath], (shell) => shell`${afplayPath} ${soundPath}`);
|
|
69676
|
+
break;
|
|
69677
|
+
}
|
|
69678
|
+
case "linux": {
|
|
69679
|
+
const paplayPath = await getPaplayPath();
|
|
69680
|
+
if (paplayPath) {
|
|
69681
|
+
await runNotificationCommand(ctx, paplayPath, [soundPath], (shell) => shell`${paplayPath} ${soundPath} 2>/dev/null`);
|
|
69682
|
+
} else {
|
|
69683
|
+
const aplayPath = await getAplayPath();
|
|
69684
|
+
if (aplayPath) {
|
|
69685
|
+
await runNotificationCommand(ctx, aplayPath, [soundPath], (shell) => shell`${aplayPath} ${soundPath} 2>/dev/null`);
|
|
69686
|
+
}
|
|
69547
69687
|
}
|
|
69688
|
+
break;
|
|
69689
|
+
}
|
|
69690
|
+
case "win32": {
|
|
69691
|
+
const powershellPath = await getPowershellPath();
|
|
69692
|
+
if (!powershellPath)
|
|
69693
|
+
return;
|
|
69694
|
+
const escaped = escapePowerShellSingleQuotedText(soundPath);
|
|
69695
|
+
const soundScript = "(New-Object Media.SoundPlayer '" + escaped + "').PlaySync()";
|
|
69696
|
+
await runNotificationCommand(ctx, powershellPath, ["-Command", soundScript], (shell) => shell`${powershellPath} -Command ${soundScript}`);
|
|
69697
|
+
break;
|
|
69548
69698
|
}
|
|
69549
|
-
break;
|
|
69550
69699
|
}
|
|
69551
|
-
|
|
69552
|
-
|
|
69553
|
-
|
|
69554
|
-
|
|
69555
|
-
|
|
69556
|
-
await runQuietNothrow(ctx.$`${powershellPath} -Command ${"(New-Object Media.SoundPlayer '" + escaped + "').PlaySync()"}`);
|
|
69557
|
-
break;
|
|
69700
|
+
} catch (error) {
|
|
69701
|
+
if (error instanceof Error) {
|
|
69702
|
+
logOperationFailure("sound", error);
|
|
69703
|
+
} else {
|
|
69704
|
+
logOperationFailure("sound", String(error));
|
|
69558
69705
|
}
|
|
69559
69706
|
}
|
|
69560
69707
|
}
|
|
@@ -71625,6 +71772,27 @@ async function runCommentChecker2(input, cliPath, customPrompt) {
|
|
|
71625
71772
|
// src/hooks/comment-checker/cli-runner.ts
|
|
71626
71773
|
var cliPathPromise = null;
|
|
71627
71774
|
var isRunning = false;
|
|
71775
|
+
var sessionLastWarning = new Map;
|
|
71776
|
+
var DEDUP_WINDOW_MS = 30000;
|
|
71777
|
+
function hasCommentSyntax(text) {
|
|
71778
|
+
if (!text)
|
|
71779
|
+
return false;
|
|
71780
|
+
return /^\s*(\/\/|\/\*|#|--|<!--|:\s*)[\s\S]*$/m.test(text) || /<!--[\s\S]*-->/.test(text);
|
|
71781
|
+
}
|
|
71782
|
+
function hasNewCommentsOnly(oldText, newText) {
|
|
71783
|
+
if (!hasCommentSyntax(newText))
|
|
71784
|
+
return false;
|
|
71785
|
+
if (!hasCommentSyntax(oldText))
|
|
71786
|
+
return true;
|
|
71787
|
+
const oldLines = new Set((oldText ?? "").split(`
|
|
71788
|
+
`).map((l) => l.trim()));
|
|
71789
|
+
const newLines = (newText ?? "").split(`
|
|
71790
|
+
`);
|
|
71791
|
+
return newLines.some((l) => {
|
|
71792
|
+
const trimmed = l.trim();
|
|
71793
|
+
return trimmed && hasCommentSyntax(trimmed) && !oldLines.has(trimmed);
|
|
71794
|
+
});
|
|
71795
|
+
}
|
|
71628
71796
|
async function withCommentCheckerLock(fn, fallback, debugLog3) {
|
|
71629
71797
|
if (isRunning) {
|
|
71630
71798
|
debugLog3("comment-checker already running, skipping");
|
|
@@ -71666,6 +71834,17 @@ async function processWithCli(input, pendingCall, output, cliPath, customPrompt,
|
|
|
71666
71834
|
edits: pendingCall.edits
|
|
71667
71835
|
}
|
|
71668
71836
|
};
|
|
71837
|
+
if (!hasNewCommentsOnly(pendingCall.oldString, pendingCall.newString)) {
|
|
71838
|
+
debugLog3("skipping: no net-new comments in edit (oldString/newString)");
|
|
71839
|
+
return;
|
|
71840
|
+
}
|
|
71841
|
+
const lastWarned = sessionLastWarning.get(pendingCall.sessionID) ?? 0;
|
|
71842
|
+
const now = Date.now();
|
|
71843
|
+
if (now - lastWarned < DEDUP_WINDOW_MS) {
|
|
71844
|
+
debugLog3("dedup: skipping comment warning within dedup window for session", pendingCall.sessionID);
|
|
71845
|
+
return;
|
|
71846
|
+
}
|
|
71847
|
+
sessionLastWarning.set(pendingCall.sessionID, now);
|
|
71669
71848
|
const result = await (deps.runCommentChecker ?? runCommentChecker2)(hookInput, cliPath, customPrompt);
|
|
71670
71849
|
if (result.hasComments && result.message) {
|
|
71671
71850
|
debugLog3("CLI detected comments, appending message");
|
|
@@ -100037,11 +100216,17 @@ var cachedCli = null;
|
|
|
100037
100216
|
var autoInstallAttempted = false;
|
|
100038
100217
|
function findExecutable(name) {
|
|
100039
100218
|
const isWindows2 = process.platform === "win32";
|
|
100040
|
-
const cmd = isWindows2 ? "where" : "which";
|
|
100219
|
+
const cmd = isWindows2 ? "where.exe" : "which";
|
|
100041
100220
|
try {
|
|
100042
|
-
const result = spawnSync2(cmd, [name], {
|
|
100043
|
-
|
|
100044
|
-
|
|
100221
|
+
const result = spawnSync2(cmd, [name], {
|
|
100222
|
+
encoding: "utf-8",
|
|
100223
|
+
timeout: 5000,
|
|
100224
|
+
windowsHide: isWindows2,
|
|
100225
|
+
shell: false
|
|
100226
|
+
});
|
|
100227
|
+
const stdout = result.stdout;
|
|
100228
|
+
if (result.status === 0 && stdout.trim()) {
|
|
100229
|
+
return stdout.trim().split(`
|
|
100045
100230
|
`)[0];
|
|
100046
100231
|
}
|
|
100047
100232
|
} catch {
|
|
@@ -100163,6 +100348,29 @@ class Semaphore {
|
|
|
100163
100348
|
}
|
|
100164
100349
|
var rgSemaphore = new Semaphore(2);
|
|
100165
100350
|
|
|
100351
|
+
// src/tools/shared/search-process-output.ts
|
|
100352
|
+
function getErrorMessage5(error) {
|
|
100353
|
+
return error instanceof Error ? error.message : String(error);
|
|
100354
|
+
}
|
|
100355
|
+
function createProcessTimeout(proc, timeoutMs, timeoutMessage) {
|
|
100356
|
+
return new Promise((_, reject) => {
|
|
100357
|
+
const id = setTimeout(() => {
|
|
100358
|
+
proc.kill();
|
|
100359
|
+
reject(new Error(timeoutMessage));
|
|
100360
|
+
}, timeoutMs);
|
|
100361
|
+
proc.exited.then(() => clearTimeout(id), () => clearTimeout(id));
|
|
100362
|
+
});
|
|
100363
|
+
}
|
|
100364
|
+
async function collectSearchProcessOutput(proc, timeoutMs, timeoutMessage) {
|
|
100365
|
+
const stderrPromise = readProcessStream(proc.stderr).catch(getErrorMessage5);
|
|
100366
|
+
const stdout = await Promise.race([
|
|
100367
|
+
readProcessStream(proc.stdout),
|
|
100368
|
+
createProcessTimeout(proc, timeoutMs, timeoutMessage)
|
|
100369
|
+
]);
|
|
100370
|
+
const [exitCode, stderr] = await Promise.all([proc.exited, stderrPromise]);
|
|
100371
|
+
return { stdout, stderr, exitCode };
|
|
100372
|
+
}
|
|
100373
|
+
|
|
100166
100374
|
// src/tools/grep/cli.ts
|
|
100167
100375
|
function buildRgArgs(options) {
|
|
100168
100376
|
const args = [
|
|
@@ -100286,15 +100494,15 @@ function parseCountOutput(output) {
|
|
|
100286
100494
|
}
|
|
100287
100495
|
return results;
|
|
100288
100496
|
}
|
|
100289
|
-
async function runRg(options, resolvedCli) {
|
|
100497
|
+
async function runRg(options, resolvedCli, processSpawner = spawn2) {
|
|
100290
100498
|
await rgSemaphore.acquire();
|
|
100291
100499
|
try {
|
|
100292
|
-
return await runRgInternal(options, resolvedCli);
|
|
100500
|
+
return await runRgInternal(options, resolvedCli, processSpawner);
|
|
100293
100501
|
} finally {
|
|
100294
100502
|
rgSemaphore.release();
|
|
100295
100503
|
}
|
|
100296
100504
|
}
|
|
100297
|
-
async function runRgInternal(options, resolvedCli) {
|
|
100505
|
+
async function runRgInternal(options, resolvedCli, processSpawner = spawn2) {
|
|
100298
100506
|
const cli = resolvedCli ?? resolveGrepCli();
|
|
100299
100507
|
const args = buildArgs(options, cli.backend);
|
|
100300
100508
|
const timeout = Math.min(options.timeout ?? DEFAULT_TIMEOUT_MS2, DEFAULT_TIMEOUT_MS2);
|
|
@@ -100305,21 +100513,12 @@ async function runRgInternal(options, resolvedCli) {
|
|
|
100305
100513
|
}
|
|
100306
100514
|
const paths = options.paths?.length ? options.paths : ["."];
|
|
100307
100515
|
args.push(...paths);
|
|
100308
|
-
const proc = spawn2([cli.path, ...args], {
|
|
100309
|
-
stdout: "pipe",
|
|
100310
|
-
stderr: "pipe"
|
|
100311
|
-
});
|
|
100312
|
-
const timeoutPromise = new Promise((_, reject) => {
|
|
100313
|
-
const id = setTimeout(() => {
|
|
100314
|
-
proc.kill();
|
|
100315
|
-
reject(new Error(`Search timeout after ${timeout}ms`));
|
|
100316
|
-
}, timeout);
|
|
100317
|
-
proc.exited.then(() => clearTimeout(id));
|
|
100318
|
-
});
|
|
100319
100516
|
try {
|
|
100320
|
-
const
|
|
100321
|
-
|
|
100322
|
-
|
|
100517
|
+
const proc = processSpawner([cli.path, ...args], {
|
|
100518
|
+
stdout: "pipe",
|
|
100519
|
+
stderr: "pipe"
|
|
100520
|
+
});
|
|
100521
|
+
const { stdout, stderr, exitCode } = await collectSearchProcessOutput(proc, timeout, `Search timeout after ${timeout}ms`);
|
|
100323
100522
|
const truncated = stdout.length >= DEFAULT_MAX_OUTPUT_BYTES;
|
|
100324
100523
|
const outputToProcess = truncated ? stdout.substring(0, DEFAULT_MAX_OUTPUT_BYTES) : stdout;
|
|
100325
100524
|
if (exitCode > 1 && stderr.trim()) {
|
|
@@ -100350,15 +100549,15 @@ async function runRgInternal(options, resolvedCli) {
|
|
|
100350
100549
|
};
|
|
100351
100550
|
}
|
|
100352
100551
|
}
|
|
100353
|
-
async function runRgCount(options, resolvedCli) {
|
|
100552
|
+
async function runRgCount(options, resolvedCli, processSpawner = spawn2) {
|
|
100354
100553
|
await rgSemaphore.acquire();
|
|
100355
100554
|
try {
|
|
100356
|
-
return await runRgCountInternal(options, resolvedCli);
|
|
100555
|
+
return await runRgCountInternal(options, resolvedCli, processSpawner);
|
|
100357
100556
|
} finally {
|
|
100358
100557
|
rgSemaphore.release();
|
|
100359
100558
|
}
|
|
100360
100559
|
}
|
|
100361
|
-
async function runRgCountInternal(options, resolvedCli) {
|
|
100560
|
+
async function runRgCountInternal(options, resolvedCli, processSpawner = spawn2) {
|
|
100362
100561
|
const cli = resolvedCli ?? resolveGrepCli();
|
|
100363
100562
|
const args = buildArgs({ ...options, context: 0 }, cli.backend);
|
|
100364
100563
|
if (cli.backend === "rg") {
|
|
@@ -100369,19 +100568,15 @@ async function runRgCountInternal(options, resolvedCli) {
|
|
|
100369
100568
|
const paths = options.paths?.length ? options.paths : ["."];
|
|
100370
100569
|
args.push(...paths);
|
|
100371
100570
|
const timeout = Math.min(options.timeout ?? DEFAULT_TIMEOUT_MS2, DEFAULT_TIMEOUT_MS2);
|
|
100372
|
-
const proc = spawn2([cli.path, ...args], {
|
|
100373
|
-
stdout: "pipe",
|
|
100374
|
-
stderr: "pipe"
|
|
100375
|
-
});
|
|
100376
|
-
const timeoutPromise = new Promise((_, reject) => {
|
|
100377
|
-
const id = setTimeout(() => {
|
|
100378
|
-
proc.kill();
|
|
100379
|
-
reject(new Error(`Search timeout after ${timeout}ms`));
|
|
100380
|
-
}, timeout);
|
|
100381
|
-
proc.exited.then(() => clearTimeout(id));
|
|
100382
|
-
});
|
|
100383
100571
|
try {
|
|
100384
|
-
const
|
|
100572
|
+
const proc = processSpawner([cli.path, ...args], {
|
|
100573
|
+
stdout: "pipe",
|
|
100574
|
+
stderr: "pipe"
|
|
100575
|
+
});
|
|
100576
|
+
const { stdout, stderr, exitCode } = await collectSearchProcessOutput(proc, timeout, `Search timeout after ${timeout}ms`);
|
|
100577
|
+
if (exitCode > 1 && stderr.trim()) {
|
|
100578
|
+
throw new Error(stderr.trim());
|
|
100579
|
+
}
|
|
100385
100580
|
return parseCountOutput(stdout);
|
|
100386
100581
|
} catch (e) {
|
|
100387
100582
|
throw new Error(`Count search failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
@@ -100542,12 +100737,12 @@ function buildPowerShellCommand(options) {
|
|
|
100542
100737
|
const searchPath = paths[0] || ".";
|
|
100543
100738
|
const escapedPath = searchPath.replace(/'/g, "''");
|
|
100544
100739
|
const escapedPattern = options.pattern.replace(/'/g, "''");
|
|
100545
|
-
let psCommand = `Get-ChildItem -
|
|
100740
|
+
let psCommand = `Get-ChildItem -LiteralPath '${escapedPath}' -File -Recurse -Depth ${maxDepth - 1} -Filter '${escapedPattern}'`;
|
|
100546
100741
|
if (options.hidden !== false) {
|
|
100547
100742
|
psCommand += " -Force";
|
|
100548
100743
|
}
|
|
100549
100744
|
psCommand += " -ErrorAction SilentlyContinue | Select-Object -ExpandProperty FullName";
|
|
100550
|
-
return ["powershell", "-NoProfile", "-Command", psCommand];
|
|
100745
|
+
return ["powershell.exe", "-NoProfile", "-Command", psCommand];
|
|
100551
100746
|
}
|
|
100552
100747
|
async function getFileMtime(filePath) {
|
|
100553
100748
|
try {
|
|
@@ -100557,15 +100752,15 @@ async function getFileMtime(filePath) {
|
|
|
100557
100752
|
return 0;
|
|
100558
100753
|
}
|
|
100559
100754
|
}
|
|
100560
|
-
async function runRgFiles(options, resolvedCli) {
|
|
100755
|
+
async function runRgFiles(options, resolvedCli, processSpawner = spawn2) {
|
|
100561
100756
|
await rgSemaphore.acquire();
|
|
100562
100757
|
try {
|
|
100563
|
-
return await runRgFilesInternal(options, resolvedCli);
|
|
100758
|
+
return await runRgFilesInternal(options, resolvedCli, processSpawner);
|
|
100564
100759
|
} finally {
|
|
100565
100760
|
rgSemaphore.release();
|
|
100566
100761
|
}
|
|
100567
100762
|
}
|
|
100568
|
-
async function runRgFilesInternal(options, resolvedCli) {
|
|
100763
|
+
async function runRgFilesInternal(options, resolvedCli, processSpawner = spawn2) {
|
|
100569
100764
|
const cli = resolvedCli ?? resolveGrepCli();
|
|
100570
100765
|
const timeout = Math.min(options.timeout ?? DEFAULT_TIMEOUT_MS3, DEFAULT_TIMEOUT_MS3);
|
|
100571
100766
|
const limit = Math.min(options.limit ?? DEFAULT_LIMIT, DEFAULT_LIMIT);
|
|
@@ -100587,22 +100782,13 @@ async function runRgFilesInternal(options, resolvedCli) {
|
|
|
100587
100782
|
cwd = paths[0] || ".";
|
|
100588
100783
|
command = [cli.path, ...args];
|
|
100589
100784
|
}
|
|
100590
|
-
const proc = spawn2(command, {
|
|
100591
|
-
stdout: "pipe",
|
|
100592
|
-
stderr: "pipe",
|
|
100593
|
-
cwd
|
|
100594
|
-
});
|
|
100595
|
-
const timeoutPromise = new Promise((_, reject) => {
|
|
100596
|
-
const id = setTimeout(() => {
|
|
100597
|
-
proc.kill();
|
|
100598
|
-
reject(new Error(`Glob search timeout after ${timeout}ms`));
|
|
100599
|
-
}, timeout);
|
|
100600
|
-
proc.exited.then(() => clearTimeout(id));
|
|
100601
|
-
});
|
|
100602
100785
|
try {
|
|
100603
|
-
const
|
|
100604
|
-
|
|
100605
|
-
|
|
100786
|
+
const proc = processSpawner(command, {
|
|
100787
|
+
stdout: "pipe",
|
|
100788
|
+
stderr: "pipe",
|
|
100789
|
+
cwd
|
|
100790
|
+
});
|
|
100791
|
+
const { stdout, stderr, exitCode } = await collectSearchProcessOutput(proc, timeout, `Glob search timeout after ${timeout}ms`);
|
|
100606
100792
|
if (exitCode > 1 && stderr.trim()) {
|
|
100607
100793
|
return {
|
|
100608
100794
|
files: [],
|
|
@@ -102373,7 +102559,7 @@ init_opencode_message_dir();
|
|
|
102373
102559
|
init_logger();
|
|
102374
102560
|
|
|
102375
102561
|
// src/tools/background-task/session-messages.ts
|
|
102376
|
-
function
|
|
102562
|
+
function getErrorMessage6(value) {
|
|
102377
102563
|
if (Array.isArray(value))
|
|
102378
102564
|
return null;
|
|
102379
102565
|
if (value.error === undefined || value.error === null)
|
|
@@ -102556,7 +102742,7 @@ async function formatFullSession(task, client, options) {
|
|
|
102556
102742
|
} catch (error) {
|
|
102557
102743
|
return `Error fetching messages: ${error instanceof Error ? error.message : String(error)}`;
|
|
102558
102744
|
}
|
|
102559
|
-
const errorMessage =
|
|
102745
|
+
const errorMessage = getErrorMessage6(messagesResult);
|
|
102560
102746
|
if (errorMessage) {
|
|
102561
102747
|
return `Error fetching messages: ${errorMessage}`;
|
|
102562
102748
|
}
|
|
@@ -102762,7 +102948,7 @@ async function formatTaskResult(task, client) {
|
|
|
102762
102948
|
} catch (error) {
|
|
102763
102949
|
return `Error fetching messages: ${error instanceof Error ? error.message : String(error)}`;
|
|
102764
102950
|
}
|
|
102765
|
-
const errorMessage =
|
|
102951
|
+
const errorMessage = getErrorMessage6(messagesResult);
|
|
102766
102952
|
if (errorMessage) {
|
|
102767
102953
|
return `Error fetching messages: ${errorMessage}`;
|
|
102768
102954
|
}
|
|
@@ -110633,6 +110819,11 @@ class ParentWakeNotifier {
|
|
|
110633
110819
|
throw promptResult.error;
|
|
110634
110820
|
}
|
|
110635
110821
|
if (promptResult.status === "reserved" && promptResult.reservedBy === "background-agent-parent-wake") {
|
|
110822
|
+
const dispatchedWake = this.dispatchedParentWakes.get(sessionID);
|
|
110823
|
+
if (dispatchedWake && this.isSameParentWake(latestWake, dispatchedWake)) {
|
|
110824
|
+
log("[background-agent] Suppressed duplicate parent wake during promptAsync gate hold:", { sessionID });
|
|
110825
|
+
return;
|
|
110826
|
+
}
|
|
110636
110827
|
this.requeueWake(sessionID, latestWake);
|
|
110637
110828
|
this.schedulePendingParentWakeFlush(sessionID, 2000);
|
|
110638
110829
|
log("[background-agent] Requeued parent wake flush reserved by promptAsync gate hold:", { sessionID });
|
|
@@ -110769,6 +110960,9 @@ class ParentWakeNotifier {
|
|
|
110769
110960
|
unrefTimerHandle(timer);
|
|
110770
110961
|
this.dispatchedParentWakeTimers.set(sessionID, timer);
|
|
110771
110962
|
}
|
|
110963
|
+
isSameParentWake(left, right) {
|
|
110964
|
+
return left.shouldReply === right.shouldReply && JSON.stringify(left.notifications) === JSON.stringify(right.notifications) && JSON.stringify(left.promptContext) === JSON.stringify(right.promptContext);
|
|
110965
|
+
}
|
|
110772
110966
|
async loadParentWakeSessionMessages(sessionID) {
|
|
110773
110967
|
try {
|
|
110774
110968
|
const messagesResp = await messagesInDirectory(this.deps.client, {
|
|
@@ -110993,9 +111187,13 @@ function describeProcessCleanupError(error) {
|
|
|
110993
111187
|
return { raw: String(error) };
|
|
110994
111188
|
}
|
|
110995
111189
|
function registerErrorEvent(signal) {
|
|
111190
|
+
let logging = false;
|
|
110996
111191
|
const listener = (error) => {
|
|
110997
|
-
|
|
111192
|
+
if (logging)
|
|
111193
|
+
return;
|
|
111194
|
+
logging = true;
|
|
110998
111195
|
log(`[background-agent] ${signal} observed; keeping host alive and skipping cleanup (signal handlers run on real shutdown)`, describeProcessCleanupError(error));
|
|
111196
|
+
logging = false;
|
|
110999
111197
|
};
|
|
111000
111198
|
process.on(signal, listener);
|
|
111001
111199
|
return listener;
|
|
@@ -139870,7 +140068,7 @@ function createEventHandler2(args) {
|
|
|
139870
140068
|
const recentSyntheticIdles = new Map;
|
|
139871
140069
|
const recentRealIdles = new Map;
|
|
139872
140070
|
const recentAnyIdles = new Map;
|
|
139873
|
-
const
|
|
140071
|
+
const DEDUP_WINDOW_MS2 = 500;
|
|
139874
140072
|
const teamModeConfig = pluginConfig.team_mode?.enabled ? pluginConfig.team_mode : undefined;
|
|
139875
140073
|
const teamLeadOrphanHandler = teamModeConfig ? createTeamLeadOrphanHandler(teamModeConfig, managers.tmuxSessionManager, managers.backgroundManager) : undefined;
|
|
139876
140074
|
const teamMemberErrorHandler = teamModeConfig ? createTeamMemberErrorHandler(teamModeConfig, { client: pluginContext.client }) : undefined;
|
|
@@ -139896,7 +140094,7 @@ function createEventHandler2(args) {
|
|
|
139896
140094
|
};
|
|
139897
140095
|
const shouldDispatchIdleEvent = (sessionID, now) => {
|
|
139898
140096
|
const lastDispatchedAt = recentAnyIdles.get(sessionID);
|
|
139899
|
-
if (lastDispatchedAt !== undefined && now - lastDispatchedAt <
|
|
140097
|
+
if (lastDispatchedAt !== undefined && now - lastDispatchedAt < DEDUP_WINDOW_MS2) {
|
|
139900
140098
|
return false;
|
|
139901
140099
|
}
|
|
139902
140100
|
recentAnyIdles.set(sessionID, now);
|
|
@@ -140059,7 +140257,7 @@ function createEventHandler2(args) {
|
|
|
140059
140257
|
recentRealIdles,
|
|
140060
140258
|
recentAnyIdles,
|
|
140061
140259
|
now: Date.now(),
|
|
140062
|
-
dedupWindowMs:
|
|
140260
|
+
dedupWindowMs: DEDUP_WINDOW_MS2
|
|
140063
140261
|
});
|
|
140064
140262
|
const syntheticIdle = normalizeSessionStatusToIdle(input);
|
|
140065
140263
|
if (input.event.type === "session.idle") {
|
|
@@ -140067,7 +140265,7 @@ function createEventHandler2(args) {
|
|
|
140067
140265
|
if (sessionID) {
|
|
140068
140266
|
const now = Date.now();
|
|
140069
140267
|
const emittedAt = recentSyntheticIdles.get(sessionID);
|
|
140070
|
-
if (emittedAt !== undefined && now - emittedAt <
|
|
140268
|
+
if (emittedAt !== undefined && now - emittedAt < DEDUP_WINDOW_MS2) {
|
|
140071
140269
|
recentSyntheticIdles.delete(sessionID);
|
|
140072
140270
|
const lastAnyIdleAt = recentAnyIdles.get(sessionID);
|
|
140073
140271
|
if (lastAnyIdleAt === emittedAt) {
|
|
@@ -140097,7 +140295,7 @@ function createEventHandler2(args) {
|
|
|
140097
140295
|
const sessionID = syntheticIdle.event.properties?.sessionID;
|
|
140098
140296
|
const now = Date.now();
|
|
140099
140297
|
const emittedAt = recentRealIdles.get(sessionID);
|
|
140100
|
-
if (emittedAt !== undefined && now - emittedAt <
|
|
140298
|
+
if (emittedAt !== undefined && now - emittedAt < DEDUP_WINDOW_MS2) {
|
|
140101
140299
|
recentRealIdles.delete(sessionID);
|
|
140102
140300
|
return;
|
|
140103
140301
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type SpawnOptions as NodeSpawnOptions, type SpawnSyncOptions as NodeSpawnSyncOptions } from "node:child_process";
|
|
1
2
|
type StdioMode = "pipe" | "inherit" | "ignore";
|
|
2
3
|
type StdioTuple = [StdioMode, StdioMode, StdioMode];
|
|
3
4
|
export interface SpawnOptions {
|
|
@@ -29,6 +30,8 @@ export interface SpawnSyncResult {
|
|
|
29
30
|
readonly success: boolean;
|
|
30
31
|
readonly pid: number;
|
|
31
32
|
}
|
|
33
|
+
export declare function createNodeSpawnOptions(options: SpawnOptions, platform?: NodeJS.Platform): NodeSpawnOptions;
|
|
34
|
+
export declare function createNodeSpawnSyncOptions(options: SpawnOptions, platform?: NodeJS.Platform): NodeSpawnSyncOptions;
|
|
32
35
|
export declare function spawn(command: string[], options?: SpawnOptions): SpawnedProcess;
|
|
33
36
|
export declare function spawn(options: SpawnOptions & {
|
|
34
37
|
cmd: string[];
|
package/dist/tools/glob/cli.d.ts
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
|
+
import { type SpawnOptions, type SpawnedProcess } from "../../shared/bun-spawn-shim";
|
|
1
2
|
import { type GrepBackend } from "./constants";
|
|
2
3
|
import type { GlobOptions, GlobResult } from "./types";
|
|
3
4
|
export interface ResolvedCli {
|
|
4
5
|
path: string;
|
|
5
6
|
backend: GrepBackend;
|
|
6
7
|
}
|
|
8
|
+
export type SearchProcessSpawner = (command: string[], options?: SpawnOptions) => SpawnedProcess;
|
|
7
9
|
declare function buildRgArgs(options: GlobOptions): string[];
|
|
8
10
|
declare function buildFindArgs(options: GlobOptions): string[];
|
|
9
11
|
declare function buildPowerShellCommand(options: GlobOptions): string[];
|
|
10
12
|
export { buildRgArgs, buildFindArgs, buildPowerShellCommand };
|
|
11
|
-
export declare function runRgFiles(options: GlobOptions, resolvedCli?: ResolvedCli): Promise<GlobResult>;
|
|
13
|
+
export declare function runRgFiles(options: GlobOptions, resolvedCli?: ResolvedCli, processSpawner?: SearchProcessSpawner): Promise<GlobResult>;
|
package/dist/tools/grep/cli.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { type SpawnOptions, type SpawnedProcess } from "../../shared/bun-spawn-shim";
|
|
1
2
|
import { type ResolvedCli } from "../../shared/ripgrep-cli";
|
|
2
3
|
import type { GrepOptions, GrepResult, CountResult } from "./types";
|
|
3
|
-
export
|
|
4
|
-
export declare function
|
|
4
|
+
export type SearchProcessSpawner = (command: string[], options?: SpawnOptions) => SpawnedProcess;
|
|
5
|
+
export declare function runRg(options: GrepOptions, resolvedCli?: ResolvedCli, processSpawner?: SearchProcessSpawner): Promise<GrepResult>;
|
|
6
|
+
export declare function runRgCount(options: Omit<GrepOptions, "context">, resolvedCli?: ResolvedCli, processSpawner?: SearchProcessSpawner): Promise<CountResult[]>;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { SpawnedProcess } from "../../shared/bun-spawn-shim";
|
|
2
|
+
export interface SearchProcessOutput {
|
|
3
|
+
readonly stdout: string;
|
|
4
|
+
readonly stderr: string;
|
|
5
|
+
readonly exitCode: number;
|
|
6
|
+
}
|
|
7
|
+
export declare function collectSearchProcessOutput(proc: SpawnedProcess, timeoutMs: number, timeoutMessage: string): Promise<SearchProcessOutput>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "oh-my-opencode",
|
|
3
|
-
"version": "4.3.
|
|
3
|
+
"version": "4.3.1",
|
|
4
4
|
"description": "The Best AI Agent Harness - Batteries-Included OpenCode Plugin with Multi-Model Orchestration, Parallel Background Agents, and Crafted LSP/AST Tools",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -110,17 +110,17 @@
|
|
|
110
110
|
"zod": "^4.4.3"
|
|
111
111
|
},
|
|
112
112
|
"optionalDependencies": {
|
|
113
|
-
"oh-my-opencode-darwin-arm64": "4.3.
|
|
114
|
-
"oh-my-opencode-darwin-x64": "4.3.
|
|
115
|
-
"oh-my-opencode-darwin-x64-baseline": "4.3.
|
|
116
|
-
"oh-my-opencode-linux-arm64": "4.3.
|
|
117
|
-
"oh-my-opencode-linux-arm64-musl": "4.3.
|
|
118
|
-
"oh-my-opencode-linux-x64": "4.3.
|
|
119
|
-
"oh-my-opencode-linux-x64-baseline": "4.3.
|
|
120
|
-
"oh-my-opencode-linux-x64-musl": "4.3.
|
|
121
|
-
"oh-my-opencode-linux-x64-musl-baseline": "4.3.
|
|
122
|
-
"oh-my-opencode-windows-x64": "4.3.
|
|
123
|
-
"oh-my-opencode-windows-x64-baseline": "4.3.
|
|
113
|
+
"oh-my-opencode-darwin-arm64": "4.3.1",
|
|
114
|
+
"oh-my-opencode-darwin-x64": "4.3.1",
|
|
115
|
+
"oh-my-opencode-darwin-x64-baseline": "4.3.1",
|
|
116
|
+
"oh-my-opencode-linux-arm64": "4.3.1",
|
|
117
|
+
"oh-my-opencode-linux-arm64-musl": "4.3.1",
|
|
118
|
+
"oh-my-opencode-linux-x64": "4.3.1",
|
|
119
|
+
"oh-my-opencode-linux-x64-baseline": "4.3.1",
|
|
120
|
+
"oh-my-opencode-linux-x64-musl": "4.3.1",
|
|
121
|
+
"oh-my-opencode-linux-x64-musl-baseline": "4.3.1",
|
|
122
|
+
"oh-my-opencode-windows-x64": "4.3.1",
|
|
123
|
+
"oh-my-opencode-windows-x64-baseline": "4.3.1"
|
|
124
124
|
},
|
|
125
125
|
"overrides": {
|
|
126
126
|
"hono": "^4.12.18",
|