oh-my-opencode-slim 0.8.3 → 0.8.5
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/README.md +35 -7
- package/dist/agents/council-master.d.ts +2 -0
- package/dist/agents/council.d.ts +28 -0
- package/dist/agents/councillor.d.ts +2 -0
- package/dist/agents/orchestrator.d.ts +6 -0
- package/dist/background/background-manager.d.ts +6 -1
- package/dist/background/index.d.ts +1 -0
- package/dist/background/subagent-depth.d.ts +35 -0
- package/dist/cli/config-io.d.ts +1 -1
- package/dist/cli/custom-skills.d.ts +2 -2
- package/dist/cli/index.js +173 -56
- package/dist/cli/paths.d.ts +22 -0
- package/dist/cli/providers.d.ts +4 -4
- package/dist/cli/types.d.ts +2 -0
- package/dist/config/constants.d.ts +7 -2
- package/dist/config/council-schema.d.ts +134 -0
- package/dist/config/index.d.ts +1 -0
- package/dist/config/loader.d.ts +2 -1
- package/dist/config/schema.d.ts +27 -0
- package/dist/council/council-manager.d.ts +40 -0
- package/dist/council/index.d.ts +1 -0
- package/dist/hooks/foreground-fallback/index.d.ts +72 -0
- package/dist/hooks/index.d.ts +2 -1
- package/dist/hooks/json-error-recovery/hook.d.ts +1 -1
- package/dist/hooks/phase-reminder/index.d.ts +1 -1
- package/dist/hooks/post-file-tool-nudge/index.d.ts +18 -0
- package/dist/index.js +2273 -879
- package/dist/tools/council.d.ts +9 -0
- package/dist/tools/index.d.ts +2 -2
- package/dist/tools/lsp/config-store.d.ts +29 -0
- package/dist/tools/lsp/constants.d.ts +18 -2
- package/dist/tools/lsp/index.d.ts +1 -0
- package/dist/tools/lsp/types.d.ts +7 -0
- package/dist/tools/lsp/utils.d.ts +14 -1
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/session.d.ts +59 -0
- package/oh-my-opencode-slim.schema.json +78 -0
- package/package.json +3 -1
package/dist/index.js
CHANGED
|
@@ -3037,7 +3037,7 @@ var require_main = __commonJS((exports) => {
|
|
|
3037
3037
|
var ril_1 = require_ril();
|
|
3038
3038
|
ril_1.default.install();
|
|
3039
3039
|
var path6 = __require("path");
|
|
3040
|
-
var
|
|
3040
|
+
var os3 = __require("os");
|
|
3041
3041
|
var crypto_1 = __require("crypto");
|
|
3042
3042
|
var net_1 = __require("net");
|
|
3043
3043
|
var api_1 = require_api();
|
|
@@ -3180,7 +3180,7 @@ var require_main = __commonJS((exports) => {
|
|
|
3180
3180
|
if (XDG_RUNTIME_DIR) {
|
|
3181
3181
|
result = path6.join(XDG_RUNTIME_DIR, `vscode-ipc-${randomSuffix}.sock`);
|
|
3182
3182
|
} else {
|
|
3183
|
-
result = path6.join(
|
|
3183
|
+
result = path6.join(os3.tmpdir(), `vscode-${randomSuffix}.sock`);
|
|
3184
3184
|
}
|
|
3185
3185
|
const limit = safeIpcPathLengths.get(process.platform);
|
|
3186
3186
|
if (limit !== undefined && result.length > limit) {
|
|
@@ -3277,6 +3277,242 @@ var require_main = __commonJS((exports) => {
|
|
|
3277
3277
|
exports.createMessageConnection = createMessageConnection;
|
|
3278
3278
|
});
|
|
3279
3279
|
|
|
3280
|
+
// node_modules/isexe/dist/commonjs/index.min.js
|
|
3281
|
+
var require_index_min = __commonJS((exports) => {
|
|
3282
|
+
var a = (t, e) => () => (e || t((e = { exports: {} }).exports, e), e.exports);
|
|
3283
|
+
var _ = a((i) => {
|
|
3284
|
+
Object.defineProperty(i, "__esModule", { value: true });
|
|
3285
|
+
i.sync = i.isexe = undefined;
|
|
3286
|
+
var M = __require("fs"), x = __require("fs/promises"), q = async (t, e = {}) => {
|
|
3287
|
+
let { ignoreErrors: r = false } = e;
|
|
3288
|
+
try {
|
|
3289
|
+
return d(await (0, x.stat)(t), e);
|
|
3290
|
+
} catch (s) {
|
|
3291
|
+
let n = s;
|
|
3292
|
+
if (r || n.code === "EACCES")
|
|
3293
|
+
return false;
|
|
3294
|
+
throw n;
|
|
3295
|
+
}
|
|
3296
|
+
};
|
|
3297
|
+
i.isexe = q;
|
|
3298
|
+
var m = (t, e = {}) => {
|
|
3299
|
+
let { ignoreErrors: r = false } = e;
|
|
3300
|
+
try {
|
|
3301
|
+
return d((0, M.statSync)(t), e);
|
|
3302
|
+
} catch (s) {
|
|
3303
|
+
let n = s;
|
|
3304
|
+
if (r || n.code === "EACCES")
|
|
3305
|
+
return false;
|
|
3306
|
+
throw n;
|
|
3307
|
+
}
|
|
3308
|
+
};
|
|
3309
|
+
i.sync = m;
|
|
3310
|
+
var d = (t, e) => t.isFile() && A(t, e), A = (t, e) => {
|
|
3311
|
+
let r = e.uid ?? process.getuid?.(), s = e.groups ?? process.getgroups?.() ?? [], n = e.gid ?? process.getgid?.() ?? s[0];
|
|
3312
|
+
if (r === undefined || n === undefined)
|
|
3313
|
+
throw new Error("cannot get uid or gid");
|
|
3314
|
+
let u = new Set([n, ...s]), c = t.mode, S = t.uid, P = t.gid, f = parseInt("100", 8), l = parseInt("010", 8), j = parseInt("001", 8), C = f | l;
|
|
3315
|
+
return !!(c & j || c & l && u.has(P) || c & f && S === r || c & C && r === 0);
|
|
3316
|
+
};
|
|
3317
|
+
});
|
|
3318
|
+
var g = a((o) => {
|
|
3319
|
+
Object.defineProperty(o, "__esModule", { value: true });
|
|
3320
|
+
o.sync = o.isexe = undefined;
|
|
3321
|
+
var T = __require("fs"), I = __require("fs/promises"), D = __require("path"), F = async (t, e = {}) => {
|
|
3322
|
+
let { ignoreErrors: r = false } = e;
|
|
3323
|
+
try {
|
|
3324
|
+
return y(await (0, I.stat)(t), t, e);
|
|
3325
|
+
} catch (s) {
|
|
3326
|
+
let n = s;
|
|
3327
|
+
if (r || n.code === "EACCES")
|
|
3328
|
+
return false;
|
|
3329
|
+
throw n;
|
|
3330
|
+
}
|
|
3331
|
+
};
|
|
3332
|
+
o.isexe = F;
|
|
3333
|
+
var L = (t, e = {}) => {
|
|
3334
|
+
let { ignoreErrors: r = false } = e;
|
|
3335
|
+
try {
|
|
3336
|
+
return y((0, T.statSync)(t), t, e);
|
|
3337
|
+
} catch (s) {
|
|
3338
|
+
let n = s;
|
|
3339
|
+
if (r || n.code === "EACCES")
|
|
3340
|
+
return false;
|
|
3341
|
+
throw n;
|
|
3342
|
+
}
|
|
3343
|
+
};
|
|
3344
|
+
o.sync = L;
|
|
3345
|
+
var B = (t, e) => {
|
|
3346
|
+
let { pathExt: r = process.env.PATHEXT || "" } = e, s = r.split(D.delimiter);
|
|
3347
|
+
if (s.indexOf("") !== -1)
|
|
3348
|
+
return true;
|
|
3349
|
+
for (let n of s) {
|
|
3350
|
+
let u = n.toLowerCase(), c = t.substring(t.length - u.length).toLowerCase();
|
|
3351
|
+
if (u && c === u)
|
|
3352
|
+
return true;
|
|
3353
|
+
}
|
|
3354
|
+
return false;
|
|
3355
|
+
}, y = (t, e, r) => t.isFile() && B(e, r);
|
|
3356
|
+
});
|
|
3357
|
+
var p = a((h) => {
|
|
3358
|
+
Object.defineProperty(h, "__esModule", { value: true });
|
|
3359
|
+
});
|
|
3360
|
+
var v = exports && exports.__createBinding || (Object.create ? function(t, e, r, s) {
|
|
3361
|
+
s === undefined && (s = r);
|
|
3362
|
+
var n = Object.getOwnPropertyDescriptor(e, r);
|
|
3363
|
+
(!n || ("get" in n ? !e.__esModule : n.writable || n.configurable)) && (n = { enumerable: true, get: function() {
|
|
3364
|
+
return e[r];
|
|
3365
|
+
} }), Object.defineProperty(t, s, n);
|
|
3366
|
+
} : function(t, e, r, s) {
|
|
3367
|
+
s === undefined && (s = r), t[s] = e[r];
|
|
3368
|
+
});
|
|
3369
|
+
var G = exports && exports.__setModuleDefault || (Object.create ? function(t, e) {
|
|
3370
|
+
Object.defineProperty(t, "default", { enumerable: true, value: e });
|
|
3371
|
+
} : function(t, e) {
|
|
3372
|
+
t.default = e;
|
|
3373
|
+
});
|
|
3374
|
+
var w = exports && exports.__importStar || function() {
|
|
3375
|
+
var t = function(e) {
|
|
3376
|
+
return t = Object.getOwnPropertyNames || function(r) {
|
|
3377
|
+
var s = [];
|
|
3378
|
+
for (var n in r)
|
|
3379
|
+
Object.prototype.hasOwnProperty.call(r, n) && (s[s.length] = n);
|
|
3380
|
+
return s;
|
|
3381
|
+
}, t(e);
|
|
3382
|
+
};
|
|
3383
|
+
return function(e) {
|
|
3384
|
+
if (e && e.__esModule)
|
|
3385
|
+
return e;
|
|
3386
|
+
var r = {};
|
|
3387
|
+
if (e != null)
|
|
3388
|
+
for (var s = t(e), n = 0;n < s.length; n++)
|
|
3389
|
+
s[n] !== "default" && v(r, e, s[n]);
|
|
3390
|
+
return G(r, e), r;
|
|
3391
|
+
};
|
|
3392
|
+
}();
|
|
3393
|
+
var X = exports && exports.__exportStar || function(t, e) {
|
|
3394
|
+
for (var r in t)
|
|
3395
|
+
r !== "default" && !Object.prototype.hasOwnProperty.call(e, r) && v(e, t, r);
|
|
3396
|
+
};
|
|
3397
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3398
|
+
exports.sync = exports.isexe = exports.posix = exports.win32 = undefined;
|
|
3399
|
+
var E = w(_());
|
|
3400
|
+
exports.posix = E;
|
|
3401
|
+
var O = w(g());
|
|
3402
|
+
exports.win32 = O;
|
|
3403
|
+
X(p(), exports);
|
|
3404
|
+
var H = process.env._ISEXE_TEST_PLATFORM_ || process.platform;
|
|
3405
|
+
var b = H === "win32" ? O : E;
|
|
3406
|
+
exports.isexe = b.isexe;
|
|
3407
|
+
exports.sync = b.sync;
|
|
3408
|
+
});
|
|
3409
|
+
|
|
3410
|
+
// node_modules/which/lib/index.js
|
|
3411
|
+
var require_lib = __commonJS((exports, module) => {
|
|
3412
|
+
var { isexe, sync: isexeSync } = require_index_min();
|
|
3413
|
+
var { join: join9, delimiter, sep, posix } = __require("path");
|
|
3414
|
+
var isWindows = process.platform === "win32";
|
|
3415
|
+
var rSlash = new RegExp(`[${posix.sep}${sep === posix.sep ? "" : sep}]`.replace(/(\\)/g, "\\$1"));
|
|
3416
|
+
var rRel = new RegExp(`^\\.${rSlash.source}`);
|
|
3417
|
+
var getNotFoundError = (cmd) => Object.assign(new Error(`not found: ${cmd}`), { code: "ENOENT" });
|
|
3418
|
+
var getPathInfo = (cmd, {
|
|
3419
|
+
path: optPath = process.env.PATH,
|
|
3420
|
+
pathExt: optPathExt = process.env.PATHEXT,
|
|
3421
|
+
delimiter: optDelimiter = delimiter
|
|
3422
|
+
}) => {
|
|
3423
|
+
const pathEnv = cmd.match(rSlash) ? [""] : [
|
|
3424
|
+
...isWindows ? [process.cwd()] : [],
|
|
3425
|
+
...(optPath || "").split(optDelimiter)
|
|
3426
|
+
];
|
|
3427
|
+
if (isWindows) {
|
|
3428
|
+
const pathExtExe = optPathExt || [".EXE", ".CMD", ".BAT", ".COM"].join(optDelimiter);
|
|
3429
|
+
const pathExt = pathExtExe.split(optDelimiter).flatMap((item) => [item, item.toLowerCase()]);
|
|
3430
|
+
if (cmd.includes(".") && pathExt[0] !== "") {
|
|
3431
|
+
pathExt.unshift("");
|
|
3432
|
+
}
|
|
3433
|
+
return { pathEnv, pathExt, pathExtExe };
|
|
3434
|
+
}
|
|
3435
|
+
return { pathEnv, pathExt: [""] };
|
|
3436
|
+
};
|
|
3437
|
+
var getPathPart = (raw, cmd) => {
|
|
3438
|
+
const pathPart = /^".*"$/.test(raw) ? raw.slice(1, -1) : raw;
|
|
3439
|
+
const prefix = !pathPart && rRel.test(cmd) ? cmd.slice(0, 2) : "";
|
|
3440
|
+
return prefix + join9(pathPart, cmd);
|
|
3441
|
+
};
|
|
3442
|
+
var which = async (cmd, opt = {}) => {
|
|
3443
|
+
const { pathEnv, pathExt, pathExtExe } = getPathInfo(cmd, opt);
|
|
3444
|
+
const found = [];
|
|
3445
|
+
for (const envPart of pathEnv) {
|
|
3446
|
+
const p = getPathPart(envPart, cmd);
|
|
3447
|
+
for (const ext of pathExt) {
|
|
3448
|
+
const withExt = p + ext;
|
|
3449
|
+
const is = await isexe(withExt, { pathExt: pathExtExe, ignoreErrors: true });
|
|
3450
|
+
if (is) {
|
|
3451
|
+
if (!opt.all) {
|
|
3452
|
+
return withExt;
|
|
3453
|
+
}
|
|
3454
|
+
found.push(withExt);
|
|
3455
|
+
}
|
|
3456
|
+
}
|
|
3457
|
+
}
|
|
3458
|
+
if (opt.all && found.length) {
|
|
3459
|
+
return found;
|
|
3460
|
+
}
|
|
3461
|
+
if (opt.nothrow) {
|
|
3462
|
+
return null;
|
|
3463
|
+
}
|
|
3464
|
+
throw getNotFoundError(cmd);
|
|
3465
|
+
};
|
|
3466
|
+
var whichSync = (cmd, opt = {}) => {
|
|
3467
|
+
const { pathEnv, pathExt, pathExtExe } = getPathInfo(cmd, opt);
|
|
3468
|
+
const found = [];
|
|
3469
|
+
for (const pathEnvPart of pathEnv) {
|
|
3470
|
+
const p = getPathPart(pathEnvPart, cmd);
|
|
3471
|
+
for (const ext of pathExt) {
|
|
3472
|
+
const withExt = p + ext;
|
|
3473
|
+
const is = isexeSync(withExt, { pathExt: pathExtExe, ignoreErrors: true });
|
|
3474
|
+
if (is) {
|
|
3475
|
+
if (!opt.all) {
|
|
3476
|
+
return withExt;
|
|
3477
|
+
}
|
|
3478
|
+
found.push(withExt);
|
|
3479
|
+
}
|
|
3480
|
+
}
|
|
3481
|
+
}
|
|
3482
|
+
if (opt.all && found.length) {
|
|
3483
|
+
return found;
|
|
3484
|
+
}
|
|
3485
|
+
if (opt.nothrow) {
|
|
3486
|
+
return null;
|
|
3487
|
+
}
|
|
3488
|
+
throw getNotFoundError(cmd);
|
|
3489
|
+
};
|
|
3490
|
+
module.exports = which;
|
|
3491
|
+
which.sync = whichSync;
|
|
3492
|
+
});
|
|
3493
|
+
|
|
3494
|
+
// src/cli/paths.ts
|
|
3495
|
+
import { homedir } from "os";
|
|
3496
|
+
import { dirname, join } from "path";
|
|
3497
|
+
function getDefaultOpenCodeConfigDir() {
|
|
3498
|
+
const userConfigDir = process.env.XDG_CONFIG_HOME ? process.env.XDG_CONFIG_HOME : join(homedir(), ".config");
|
|
3499
|
+
return join(userConfigDir, "opencode");
|
|
3500
|
+
}
|
|
3501
|
+
function getCustomOpenCodeConfigDir() {
|
|
3502
|
+
const configDir = process.env.OPENCODE_CONFIG_DIR?.trim();
|
|
3503
|
+
return configDir || undefined;
|
|
3504
|
+
}
|
|
3505
|
+
function getConfigSearchDirs() {
|
|
3506
|
+
const dirs = [getCustomOpenCodeConfigDir(), getDefaultOpenCodeConfigDir()];
|
|
3507
|
+
return dirs.filter((dir, index) => {
|
|
3508
|
+
return Boolean(dir) && dirs.indexOf(dir) === index;
|
|
3509
|
+
});
|
|
3510
|
+
}
|
|
3511
|
+
function getOpenCodeConfigPaths() {
|
|
3512
|
+
const configDir = getDefaultOpenCodeConfigDir();
|
|
3513
|
+
return [join(configDir, "opencode.json"), join(configDir, "opencode.jsonc")];
|
|
3514
|
+
}
|
|
3515
|
+
|
|
3280
3516
|
// src/cli/custom-skills.ts
|
|
3281
3517
|
var CUSTOM_SKILLS = [
|
|
3282
3518
|
{
|
|
@@ -3363,86 +3599,53 @@ var SUBAGENT_NAMES = [
|
|
|
3363
3599
|
"librarian",
|
|
3364
3600
|
"oracle",
|
|
3365
3601
|
"designer",
|
|
3366
|
-
"fixer"
|
|
3602
|
+
"fixer",
|
|
3603
|
+
"council",
|
|
3604
|
+
"councillor",
|
|
3605
|
+
"council-master"
|
|
3367
3606
|
];
|
|
3368
3607
|
var ORCHESTRATOR_NAME = "orchestrator";
|
|
3369
3608
|
var ALL_AGENT_NAMES = [ORCHESTRATOR_NAME, ...SUBAGENT_NAMES];
|
|
3609
|
+
var ORCHESTRATABLE_AGENTS = [
|
|
3610
|
+
"explorer",
|
|
3611
|
+
"librarian",
|
|
3612
|
+
"oracle",
|
|
3613
|
+
"designer",
|
|
3614
|
+
"fixer",
|
|
3615
|
+
"council"
|
|
3616
|
+
];
|
|
3370
3617
|
var SUBAGENT_DELEGATION_RULES = {
|
|
3371
|
-
orchestrator:
|
|
3618
|
+
orchestrator: ORCHESTRATABLE_AGENTS,
|
|
3372
3619
|
fixer: [],
|
|
3373
3620
|
designer: [],
|
|
3374
3621
|
explorer: [],
|
|
3375
3622
|
librarian: [],
|
|
3376
|
-
oracle: []
|
|
3623
|
+
oracle: [],
|
|
3624
|
+
council: [],
|
|
3625
|
+
councillor: [],
|
|
3626
|
+
"council-master": []
|
|
3377
3627
|
};
|
|
3378
3628
|
var DEFAULT_MODELS = {
|
|
3379
3629
|
orchestrator: undefined,
|
|
3380
3630
|
oracle: "openai/gpt-5.4",
|
|
3381
|
-
librarian: "openai/gpt-5-
|
|
3382
|
-
explorer: "openai/gpt-5-
|
|
3383
|
-
designer: "
|
|
3384
|
-
fixer: "openai/gpt-5-
|
|
3631
|
+
librarian: "openai/gpt-5.4-mini",
|
|
3632
|
+
explorer: "openai/gpt-5.4-mini",
|
|
3633
|
+
designer: "openai/gpt-5.4-mini",
|
|
3634
|
+
fixer: "openai/gpt-5.4-mini",
|
|
3635
|
+
council: "openai/gpt-5.4-mini",
|
|
3636
|
+
councillor: "openai/gpt-5.4-mini",
|
|
3637
|
+
"council-master": "openai/gpt-5.4-mini"
|
|
3385
3638
|
};
|
|
3386
3639
|
var POLL_INTERVAL_BACKGROUND_MS = 2000;
|
|
3387
3640
|
var DEFAULT_TIMEOUT_MS = 2 * 60 * 1000;
|
|
3388
3641
|
var MAX_POLL_TIME_MS = 5 * 60 * 1000;
|
|
3389
3642
|
var FALLBACK_FAILOVER_TIMEOUT_MS = 15000;
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
import { homedir } from "os";
|
|
3397
|
-
import { join } from "path";
|
|
3398
|
-
function getConfigDir() {
|
|
3399
|
-
const userConfigDir = process.env.XDG_CONFIG_HOME ? process.env.XDG_CONFIG_HOME : join(homedir(), ".config");
|
|
3400
|
-
return join(userConfigDir, "opencode");
|
|
3401
|
-
}
|
|
3402
|
-
function getOpenCodeConfigPaths() {
|
|
3403
|
-
const configDir = getConfigDir();
|
|
3404
|
-
return [join(configDir, "opencode.json"), join(configDir, "opencode.jsonc")];
|
|
3405
|
-
}
|
|
3406
|
-
|
|
3407
|
-
// src/config/agent-mcps.ts
|
|
3408
|
-
var DEFAULT_AGENT_MCPS = {
|
|
3409
|
-
orchestrator: ["websearch"],
|
|
3410
|
-
designer: [],
|
|
3411
|
-
oracle: [],
|
|
3412
|
-
librarian: ["websearch", "context7", "grep_app"],
|
|
3413
|
-
explorer: [],
|
|
3414
|
-
fixer: []
|
|
3415
|
-
};
|
|
3416
|
-
function parseList(items, allAvailable) {
|
|
3417
|
-
if (!items || items.length === 0) {
|
|
3418
|
-
return [];
|
|
3419
|
-
}
|
|
3420
|
-
const allow = items.filter((i) => !i.startsWith("!"));
|
|
3421
|
-
const deny = items.filter((i) => i.startsWith("!")).map((i) => i.slice(1));
|
|
3422
|
-
if (deny.includes("*")) {
|
|
3423
|
-
return [];
|
|
3424
|
-
}
|
|
3425
|
-
if (allow.includes("*")) {
|
|
3426
|
-
return allAvailable.filter((item) => !deny.includes(item));
|
|
3427
|
-
}
|
|
3428
|
-
return allow.filter((item) => !deny.includes(item));
|
|
3429
|
-
}
|
|
3430
|
-
function getAgentMcpList(agentName, config) {
|
|
3431
|
-
const agentConfig = getAgentOverride(config, agentName);
|
|
3432
|
-
if (agentConfig?.mcps !== undefined) {
|
|
3433
|
-
return agentConfig.mcps;
|
|
3434
|
-
}
|
|
3435
|
-
const defaultMcps = DEFAULT_AGENT_MCPS[agentName];
|
|
3436
|
-
return defaultMcps ?? [];
|
|
3437
|
-
}
|
|
3438
|
-
|
|
3439
|
-
// src/cli/config-io.ts
|
|
3440
|
-
function stripJsonComments(json) {
|
|
3441
|
-
const commentPattern = /\\"|"(?:\\"|[^"])*"|(\/\/.*|\/\*[\s\S]*?\*\/)/g;
|
|
3442
|
-
const trailingCommaPattern = /\\"|"(?:\\"|[^"])*"|(,)(\s*[}\]])/g;
|
|
3443
|
-
return json.replace(commentPattern, (match, commentGroup) => commentGroup ? "" : match).replace(trailingCommaPattern, (match, comma, closing) => comma ? closing : match);
|
|
3444
|
-
}
|
|
3445
|
-
|
|
3643
|
+
var DEFAULT_MAX_SUBAGENT_DEPTH = 3;
|
|
3644
|
+
var PHASE_REMINDER_TEXT = `Recall Workflow Rules:
|
|
3645
|
+
Understand \u2192 build the best path (delegated based on Agent rules, split and parallelized as much as possible) \u2192 execute \u2192 verify.
|
|
3646
|
+
If delegating, launch the specialist in the same turn you mention it.`;
|
|
3647
|
+
var TMUX_SPAWN_DELAY_MS = 500;
|
|
3648
|
+
var COUNCILLOR_STAGGER_MS = 250;
|
|
3446
3649
|
// node_modules/zod/v4/classic/external.js
|
|
3447
3650
|
var exports_external = {};
|
|
3448
3651
|
__export(exports_external, {
|
|
@@ -5682,7 +5885,7 @@ class Doc {
|
|
|
5682
5885
|
var version = {
|
|
5683
5886
|
major: 4,
|
|
5684
5887
|
minor: 3,
|
|
5685
|
-
patch:
|
|
5888
|
+
patch: 6
|
|
5686
5889
|
};
|
|
5687
5890
|
|
|
5688
5891
|
// node_modules/zod/v4/core/schemas.js
|
|
@@ -6968,7 +7171,7 @@ var $ZodRecord = /* @__PURE__ */ $constructor("$ZodRecord", (inst, def) => {
|
|
|
6968
7171
|
if (keyResult instanceof Promise) {
|
|
6969
7172
|
throw new Error("Async schemas not supported in object keys currently");
|
|
6970
7173
|
}
|
|
6971
|
-
const checkNumericKey = typeof key === "string" && number.test(key) && keyResult.issues.length
|
|
7174
|
+
const checkNumericKey = typeof key === "string" && number.test(key) && keyResult.issues.length;
|
|
6972
7175
|
if (checkNumericKey) {
|
|
6973
7176
|
const retryResult = def.keyType._zod.run({ value: Number(key), issues: [] }, ctx);
|
|
6974
7177
|
if (retryResult instanceof Promise) {
|
|
@@ -14339,7 +14542,7 @@ function finalize(ctx, schema) {
|
|
|
14339
14542
|
}
|
|
14340
14543
|
}
|
|
14341
14544
|
}
|
|
14342
|
-
if (refSchema.$ref) {
|
|
14545
|
+
if (refSchema.$ref && refSeen.def) {
|
|
14343
14546
|
for (const key in schema2) {
|
|
14344
14547
|
if (key === "$ref" || key === "allOf")
|
|
14345
14548
|
continue;
|
|
@@ -16975,6 +17178,107 @@ function date4(params) {
|
|
|
16975
17178
|
|
|
16976
17179
|
// node_modules/zod/v4/classic/external.js
|
|
16977
17180
|
config(en_default());
|
|
17181
|
+
// src/config/council-schema.ts
|
|
17182
|
+
var ModelIdSchema = exports_external.string().regex(/^[^/\s]+\/[^\s]+$/, 'Expected provider/model format (e.g. "openai/gpt-5.4-mini")');
|
|
17183
|
+
var CouncillorConfigSchema = exports_external.object({
|
|
17184
|
+
model: ModelIdSchema.describe('Model ID in provider/model format (e.g. "openai/gpt-5.4-mini")'),
|
|
17185
|
+
variant: exports_external.string().optional(),
|
|
17186
|
+
prompt: exports_external.string().optional().describe("Optional role/guidance injected into the councillor user prompt")
|
|
17187
|
+
});
|
|
17188
|
+
var PresetMasterOverrideSchema = exports_external.object({
|
|
17189
|
+
model: ModelIdSchema.optional().describe("Override the master model for this preset"),
|
|
17190
|
+
variant: exports_external.string().optional().describe("Override the master variant for this preset"),
|
|
17191
|
+
prompt: exports_external.string().optional().describe("Override the master synthesis guidance for this preset")
|
|
17192
|
+
});
|
|
17193
|
+
var CouncilPresetSchema = exports_external.record(exports_external.string(), exports_external.record(exports_external.string(), exports_external.unknown())).transform((entries, ctx) => {
|
|
17194
|
+
const councillors = {};
|
|
17195
|
+
let masterOverride;
|
|
17196
|
+
for (const [key, raw] of Object.entries(entries)) {
|
|
17197
|
+
if (key === "master") {
|
|
17198
|
+
const parsed = PresetMasterOverrideSchema.safeParse(raw);
|
|
17199
|
+
if (!parsed.success) {
|
|
17200
|
+
ctx.addIssue(`Invalid master override in preset: ${parsed.error.issues.map((i) => i.message).join(", ")}`);
|
|
17201
|
+
return exports_external.NEVER;
|
|
17202
|
+
}
|
|
17203
|
+
masterOverride = parsed.data;
|
|
17204
|
+
} else {
|
|
17205
|
+
const parsed = CouncillorConfigSchema.safeParse(raw);
|
|
17206
|
+
if (!parsed.success) {
|
|
17207
|
+
ctx.addIssue(`Invalid councillor "${key}": ${parsed.error.issues.map((i) => i.message).join(", ")}`);
|
|
17208
|
+
return exports_external.NEVER;
|
|
17209
|
+
}
|
|
17210
|
+
councillors[key] = parsed.data;
|
|
17211
|
+
}
|
|
17212
|
+
}
|
|
17213
|
+
return { councillors, master: masterOverride };
|
|
17214
|
+
});
|
|
17215
|
+
var CouncilMasterConfigSchema = exports_external.object({
|
|
17216
|
+
model: ModelIdSchema.describe('Model ID for the council master (e.g. "anthropic/claude-opus-4-6")'),
|
|
17217
|
+
variant: exports_external.string().optional(),
|
|
17218
|
+
prompt: exports_external.string().optional().describe("Optional role/guidance injected into the master synthesis prompt")
|
|
17219
|
+
});
|
|
17220
|
+
var CouncilConfigSchema = exports_external.object({
|
|
17221
|
+
master: CouncilMasterConfigSchema,
|
|
17222
|
+
presets: exports_external.record(exports_external.string(), CouncilPresetSchema),
|
|
17223
|
+
master_timeout: exports_external.number().min(0).default(300000),
|
|
17224
|
+
councillors_timeout: exports_external.number().min(0).default(180000),
|
|
17225
|
+
default_preset: exports_external.string().default("default"),
|
|
17226
|
+
master_fallback: exports_external.array(ModelIdSchema).optional().transform((val) => {
|
|
17227
|
+
if (!val)
|
|
17228
|
+
return val;
|
|
17229
|
+
const unique = [...new Set(val)];
|
|
17230
|
+
if (unique.length !== val.length) {
|
|
17231
|
+
return unique;
|
|
17232
|
+
}
|
|
17233
|
+
return val;
|
|
17234
|
+
}).describe("Fallback models for the council master. Tried in order if the primary model fails. " + 'Example: ["anthropic/claude-sonnet-4-6", "openai/gpt-5.4"]')
|
|
17235
|
+
});
|
|
17236
|
+
// src/config/loader.ts
|
|
17237
|
+
import * as fs from "fs";
|
|
17238
|
+
import * as path from "path";
|
|
17239
|
+
|
|
17240
|
+
// src/config/agent-mcps.ts
|
|
17241
|
+
var DEFAULT_AGENT_MCPS = {
|
|
17242
|
+
orchestrator: ["websearch"],
|
|
17243
|
+
designer: [],
|
|
17244
|
+
oracle: [],
|
|
17245
|
+
librarian: ["websearch", "context7", "grep_app"],
|
|
17246
|
+
explorer: [],
|
|
17247
|
+
fixer: [],
|
|
17248
|
+
council: [],
|
|
17249
|
+
councillor: [],
|
|
17250
|
+
"council-master": []
|
|
17251
|
+
};
|
|
17252
|
+
function parseList(items, allAvailable) {
|
|
17253
|
+
if (!items || items.length === 0) {
|
|
17254
|
+
return [];
|
|
17255
|
+
}
|
|
17256
|
+
const allow = items.filter((i) => !i.startsWith("!"));
|
|
17257
|
+
const deny = items.filter((i) => i.startsWith("!")).map((i) => i.slice(1));
|
|
17258
|
+
if (deny.includes("*")) {
|
|
17259
|
+
return [];
|
|
17260
|
+
}
|
|
17261
|
+
if (allow.includes("*")) {
|
|
17262
|
+
return allAvailable.filter((item) => !deny.includes(item));
|
|
17263
|
+
}
|
|
17264
|
+
return allow.filter((item) => !deny.includes(item));
|
|
17265
|
+
}
|
|
17266
|
+
function getAgentMcpList(agentName, config2) {
|
|
17267
|
+
const agentConfig = getAgentOverride(config2, agentName);
|
|
17268
|
+
if (agentConfig?.mcps !== undefined) {
|
|
17269
|
+
return agentConfig.mcps;
|
|
17270
|
+
}
|
|
17271
|
+
const defaultMcps = DEFAULT_AGENT_MCPS[agentName];
|
|
17272
|
+
return defaultMcps ?? [];
|
|
17273
|
+
}
|
|
17274
|
+
|
|
17275
|
+
// src/cli/config-io.ts
|
|
17276
|
+
function stripJsonComments(json2) {
|
|
17277
|
+
const commentPattern = /\\"|"(?:\\"|[^"])*"|(\/\/.*|\/\*[\s\S]*?\*\/)/g;
|
|
17278
|
+
const trailingCommaPattern = /\\"|"(?:\\"|[^"])*"|(,)(\s*[}\]])/g;
|
|
17279
|
+
return json2.replace(commentPattern, (match, commentGroup) => commentGroup ? "" : match).replace(trailingCommaPattern, (match, comma, closing) => comma ? closing : match);
|
|
17280
|
+
}
|
|
17281
|
+
|
|
16978
17282
|
// src/config/schema.ts
|
|
16979
17283
|
var ProviderModelIdSchema = exports_external.string().regex(/^[^/\s]+\/[^\s]+$/, "Expected provider/model format (provider/.../model)");
|
|
16980
17284
|
var ManualAgentPlanSchema = exports_external.object({
|
|
@@ -17049,10 +17353,12 @@ var BackgroundTaskConfigSchema = exports_external.object({
|
|
|
17049
17353
|
var FailoverConfigSchema = exports_external.object({
|
|
17050
17354
|
enabled: exports_external.boolean().default(true),
|
|
17051
17355
|
timeoutMs: exports_external.number().min(0).default(15000),
|
|
17356
|
+
retryDelayMs: exports_external.number().min(0).default(500),
|
|
17052
17357
|
chains: FallbackChainsSchema.default({})
|
|
17053
17358
|
});
|
|
17054
17359
|
var PluginConfigSchema = exports_external.object({
|
|
17055
17360
|
preset: exports_external.string().optional(),
|
|
17361
|
+
setDefaultAgent: exports_external.boolean().optional(),
|
|
17056
17362
|
scoringEngineVersion: exports_external.enum(["v1", "v2-shadow", "v2"]).optional(),
|
|
17057
17363
|
balanceProviderUsage: exports_external.boolean().optional(),
|
|
17058
17364
|
manualPlan: ManualPlanSchema.optional(),
|
|
@@ -17061,14 +17367,12 @@ var PluginConfigSchema = exports_external.object({
|
|
|
17061
17367
|
disabled_mcps: exports_external.array(exports_external.string()).optional(),
|
|
17062
17368
|
tmux: TmuxConfigSchema.optional(),
|
|
17063
17369
|
background: BackgroundTaskConfigSchema.optional(),
|
|
17064
|
-
fallback: FailoverConfigSchema.optional()
|
|
17370
|
+
fallback: FailoverConfigSchema.optional(),
|
|
17371
|
+
council: CouncilConfigSchema.optional()
|
|
17065
17372
|
});
|
|
17066
17373
|
|
|
17067
17374
|
// src/config/loader.ts
|
|
17068
17375
|
var PROMPTS_DIR_NAME = "oh-my-opencode-slim";
|
|
17069
|
-
function getUserConfigDir() {
|
|
17070
|
-
return process.env.XDG_CONFIG_HOME || path.join(os.homedir(), ".config");
|
|
17071
|
-
}
|
|
17072
17376
|
function loadConfigFromPath(configPath) {
|
|
17073
17377
|
try {
|
|
17074
17378
|
const content = fs.readFileSync(configPath, "utf-8");
|
|
@@ -17098,6 +17402,15 @@ function findConfigPath(basePath) {
|
|
|
17098
17402
|
}
|
|
17099
17403
|
return null;
|
|
17100
17404
|
}
|
|
17405
|
+
function findConfigPathInDirs(configDirs, baseName) {
|
|
17406
|
+
for (const configDir of configDirs) {
|
|
17407
|
+
const configPath = findConfigPath(path.join(configDir, baseName));
|
|
17408
|
+
if (configPath) {
|
|
17409
|
+
return configPath;
|
|
17410
|
+
}
|
|
17411
|
+
}
|
|
17412
|
+
return null;
|
|
17413
|
+
}
|
|
17101
17414
|
function deepMerge(base, override) {
|
|
17102
17415
|
if (!base)
|
|
17103
17416
|
return override;
|
|
@@ -17116,9 +17429,8 @@ function deepMerge(base, override) {
|
|
|
17116
17429
|
return result;
|
|
17117
17430
|
}
|
|
17118
17431
|
function loadPluginConfig(directory) {
|
|
17119
|
-
const
|
|
17432
|
+
const userConfigPath = findConfigPathInDirs(getConfigSearchDirs(), "oh-my-opencode-slim");
|
|
17120
17433
|
const projectConfigBasePath = path.join(directory, ".opencode", "oh-my-opencode-slim");
|
|
17121
|
-
const userConfigPath = findConfigPath(userConfigBasePath);
|
|
17122
17434
|
const projectConfigPath = findConfigPath(projectConfigBasePath);
|
|
17123
17435
|
let config2 = userConfigPath ? loadConfigFromPath(userConfigPath) ?? {} : {};
|
|
17124
17436
|
const projectConfig = projectConfigPath ? loadConfigFromPath(projectConfigPath) : null;
|
|
@@ -17149,8 +17461,10 @@ function loadPluginConfig(directory) {
|
|
|
17149
17461
|
}
|
|
17150
17462
|
function loadAgentPrompt(agentName, preset) {
|
|
17151
17463
|
const presetDirName = preset && /^[a-zA-Z0-9_-]+$/.test(preset) ? preset : undefined;
|
|
17152
|
-
const
|
|
17153
|
-
|
|
17464
|
+
const promptSearchDirs = getConfigSearchDirs().flatMap((configDir) => {
|
|
17465
|
+
const promptsDir = path.join(configDir, PROMPTS_DIR_NAME);
|
|
17466
|
+
return presetDirName ? [path.join(promptsDir, presetDirName), promptsDir] : [promptsDir];
|
|
17467
|
+
});
|
|
17154
17468
|
const result = {};
|
|
17155
17469
|
const readFirstPrompt = (fileName, errorPrefix) => {
|
|
17156
17470
|
for (const dir of promptSearchDirs) {
|
|
@@ -17175,49 +17489,461 @@ function getAgentOverride(config2, name) {
|
|
|
17175
17489
|
const overrides = config2?.agents ?? {};
|
|
17176
17490
|
return overrides[name] ?? overrides[Object.keys(AGENT_ALIASES).find((k) => AGENT_ALIASES[k] === name) ?? ""];
|
|
17177
17491
|
}
|
|
17178
|
-
// src/
|
|
17179
|
-
|
|
17180
|
-
|
|
17181
|
-
|
|
17492
|
+
// src/utils/session.ts
|
|
17493
|
+
function shortModelLabel(model) {
|
|
17494
|
+
return model.split("/").pop() ?? model;
|
|
17495
|
+
}
|
|
17496
|
+
function parseModelReference(model) {
|
|
17497
|
+
const slashIndex = model.indexOf("/");
|
|
17498
|
+
if (slashIndex <= 0 || slashIndex >= model.length - 1) {
|
|
17499
|
+
return null;
|
|
17500
|
+
}
|
|
17501
|
+
return {
|
|
17502
|
+
providerID: model.slice(0, slashIndex),
|
|
17503
|
+
modelID: model.slice(slashIndex + 1)
|
|
17504
|
+
};
|
|
17505
|
+
}
|
|
17506
|
+
async function promptWithTimeout(client, args, timeoutMs) {
|
|
17507
|
+
if (timeoutMs <= 0) {
|
|
17508
|
+
await client.session.prompt(args);
|
|
17509
|
+
return;
|
|
17510
|
+
}
|
|
17511
|
+
const sessionId = args.path.id;
|
|
17512
|
+
let timer;
|
|
17513
|
+
try {
|
|
17514
|
+
const promptPromise = client.session.prompt(args);
|
|
17515
|
+
promptPromise.catch(() => {});
|
|
17516
|
+
await Promise.race([
|
|
17517
|
+
promptPromise,
|
|
17518
|
+
new Promise((_, reject) => {
|
|
17519
|
+
timer = setTimeout(() => {
|
|
17520
|
+
client.session.abort({ path: { id: sessionId } }).catch(() => {});
|
|
17521
|
+
reject(new Error(`Prompt timed out after ${timeoutMs}ms`));
|
|
17522
|
+
}, timeoutMs);
|
|
17523
|
+
})
|
|
17524
|
+
]);
|
|
17525
|
+
} finally {
|
|
17526
|
+
clearTimeout(timer);
|
|
17527
|
+
}
|
|
17528
|
+
}
|
|
17529
|
+
async function extractSessionResult(client, sessionId, options) {
|
|
17530
|
+
const includeReasoning = options?.includeReasoning ?? true;
|
|
17531
|
+
const messagesResult = await client.session.messages({
|
|
17532
|
+
path: { id: sessionId }
|
|
17533
|
+
});
|
|
17534
|
+
const messages = messagesResult.data ?? [];
|
|
17535
|
+
const assistantMessages = messages.filter((m) => m.info?.role === "assistant");
|
|
17536
|
+
const extractedContent = [];
|
|
17537
|
+
for (const message of assistantMessages) {
|
|
17538
|
+
for (const part of message.parts ?? []) {
|
|
17539
|
+
const allowed = includeReasoning ? part.type === "text" || part.type === "reasoning" : part.type === "text";
|
|
17540
|
+
if (allowed && part.text) {
|
|
17541
|
+
extractedContent.push(part.text);
|
|
17542
|
+
}
|
|
17543
|
+
}
|
|
17544
|
+
}
|
|
17545
|
+
return extractedContent.filter((t) => t.length > 0).join(`
|
|
17182
17546
|
|
|
17183
|
-
|
|
17547
|
+
`);
|
|
17548
|
+
}
|
|
17184
17549
|
|
|
17185
|
-
|
|
17186
|
-
|
|
17187
|
-
|
|
17188
|
-
|
|
17550
|
+
// src/agents/orchestrator.ts
|
|
17551
|
+
function resolvePrompt(base, customPrompt, customAppendPrompt) {
|
|
17552
|
+
if (customPrompt)
|
|
17553
|
+
return customPrompt;
|
|
17554
|
+
if (customAppendPrompt)
|
|
17555
|
+
return `${base}
|
|
17189
17556
|
|
|
17190
|
-
|
|
17191
|
-
|
|
17192
|
-
|
|
17193
|
-
|
|
17557
|
+
${customAppendPrompt}`;
|
|
17558
|
+
return base;
|
|
17559
|
+
}
|
|
17560
|
+
var ORCHESTRATOR_PROMPT = `<Role>
|
|
17561
|
+
You are an AI coding orchestrator that optimizes for quality, speed, cost, and reliability by delegating to specialists when it provides net efficiency gains.
|
|
17562
|
+
</Role>
|
|
17194
17563
|
|
|
17195
|
-
|
|
17196
|
-
- Leverage framework animation utilities when available (Tailwind's transition/animation classes)
|
|
17197
|
-
- Focus on high-impact moments: orchestrated page loads with staggered reveals
|
|
17198
|
-
- Use scroll-triggers and hover states that surprise and delight
|
|
17199
|
-
- One well-timed animation > scattered micro-interactions
|
|
17200
|
-
- Drop to custom CSS/JS only when utilities can't achieve the vision
|
|
17564
|
+
<Agents>
|
|
17201
17565
|
|
|
17202
|
-
|
|
17203
|
-
-
|
|
17204
|
-
-
|
|
17205
|
-
-
|
|
17566
|
+
@explorer
|
|
17567
|
+
- Role: Parallel search specialist for discovering unknowns across the codebase
|
|
17568
|
+
- Stats: 3x faster codebase search than orchestrator, 1/2 cost of orchestrator
|
|
17569
|
+
- Capabilities: Glob, grep, AST queries to locate files, symbols, patterns
|
|
17570
|
+
- **Delegate when:** Need to discover what exists before planning \u2022 Parallel searches speed discovery \u2022 Need summarized map vs full contents \u2022 Broad/uncertain scope
|
|
17571
|
+
- **Don't delegate when:** Know the path and need actual content \u2022 Need full file anyway \u2022 Single specific lookup \u2022 About to edit the file
|
|
17206
17572
|
|
|
17207
|
-
|
|
17208
|
-
-
|
|
17209
|
-
-
|
|
17210
|
-
-
|
|
17573
|
+
@librarian
|
|
17574
|
+
- Role: Authoritative source for current library docs and API references
|
|
17575
|
+
- Stats: 10x better finding up-to-date library docs than orchestrator, 1/2 cost of orchestrator
|
|
17576
|
+
- Capabilities: Fetches latest official docs, examples, API signatures, version-specific behavior via grep_app MCP
|
|
17577
|
+
- **Delegate when:** Libraries with frequent API changes (React, Next.js, AI SDKs) \u2022 Complex APIs needing official examples (ORMs, auth) \u2022 Version-specific behavior matters \u2022 Unfamiliar library \u2022 Edge cases or advanced features \u2022 Nuanced best practices
|
|
17578
|
+
- **Don't delegate when:** Standard usage you're confident about (\`Array.map()\`, \`fetch()\`) \u2022 Simple stable APIs \u2022 General programming knowledge \u2022 Info already in conversation \u2022 Built-in language features
|
|
17579
|
+
- **Rule of thumb:** "How does this library work?" \u2192 @librarian. "How does programming work?" \u2192 yourself.
|
|
17211
17580
|
|
|
17212
|
-
|
|
17213
|
-
-
|
|
17214
|
-
-
|
|
17215
|
-
-
|
|
17581
|
+
@oracle
|
|
17582
|
+
- Role: Strategic advisor for high-stakes decisions and persistent problems
|
|
17583
|
+
- Stats: 10x better decisions maker, problem solver, investigator than orchestrator, 0.8x speed of orchestrator, same cost.
|
|
17584
|
+
- Capabilities: Deep architectural reasoning, system-level trade-offs, complex debugging
|
|
17585
|
+
- Tools/Constraints: Slow, expensive, high-quality\u2014use sparingly when thoroughness beats speed
|
|
17586
|
+
- **Delegate when:** Major architectural decisions with long-term impact \u2022 Problems persisting after 2+ fix attempts \u2022 High-risk multi-system refactors \u2022 Costly trade-offs (performance vs maintainability) \u2022 Complex debugging with unclear root cause \u2022 Security/scalability/data integrity decisions \u2022 Genuinely uncertain and cost of wrong choice is high
|
|
17587
|
+
- **Don't delegate when:** Routine decisions you're confident about \u2022 First bug fix attempt \u2022 Straightforward trade-offs \u2022 Tactical "how" vs strategic "should" \u2022 Time-sensitive good-enough decisions \u2022 Quick research/testing can answer
|
|
17588
|
+
- **Rule of thumb:** Need senior architect review? \u2192 @oracle. Just do it and PR? \u2192 yourself.
|
|
17216
17589
|
|
|
17217
|
-
|
|
17218
|
-
-
|
|
17219
|
-
-
|
|
17220
|
-
-
|
|
17590
|
+
@designer
|
|
17591
|
+
- Role: UI/UX specialist for intentional, polished experiences
|
|
17592
|
+
- Stats: 10x better UI/UX than orchestrator
|
|
17593
|
+
- Capabilities: Visual direction, interactions, responsive layouts, design systems with aesthetic intent
|
|
17594
|
+
- **Delegate when:** User-facing interfaces needing polish \u2022 Responsive layouts \u2022 UX-critical components (forms, nav, dashboards) \u2022 Visual consistency systems \u2022 Animations/micro-interactions \u2022 Landing/marketing pages \u2022 Refining functional\u2192delightful
|
|
17595
|
+
- **Don't delegate when:** Backend/logic with no visual \u2022 Quick prototypes where design doesn't matter yet
|
|
17596
|
+
- **Rule of thumb:** Users see it and polish matters? \u2192 @designer. Headless/functional? \u2192 yourself.
|
|
17597
|
+
|
|
17598
|
+
@fixer
|
|
17599
|
+
- Role: Fast execution specialist for well-defined tasks, which empowers orchestrator with parallel, speedy executions
|
|
17600
|
+
- Stats: 2x faster code edits, 1/2 cost of orchestrator, 0.8x quality of orchestrator
|
|
17601
|
+
- Tools/Constraints: Execution-focused\u2014no research, no architectural decisions
|
|
17602
|
+
- **Delegate when:** For implementation work, think and triage first. If the change is non-trivial or multi-file, hand bounded execution to @fixer
|
|
17603
|
+
- **Don't delegate when:** Needs discovery/research/decisions \u2022 Single small change (<20 lines, one file) \u2022 Unclear requirements needing iteration \u2022 Explaining to fixer > doing \u2022 Tight integration with your current work \u2022 Sequential dependencies
|
|
17604
|
+
- **Rule of thumb:** Explaining > doing? \u2192 yourself. Orchestrator paths selection is vastly improved by Fixer. eg it can reduce overall speed if Orchestrator splits what's usually a single task into multiple subtasks and parallelize it with fixer.
|
|
17605
|
+
|
|
17606
|
+
@council
|
|
17607
|
+
- Role: Multi-LLM consensus engine for high-confidence answers
|
|
17608
|
+
- Stats: 3x slower than orchestrator, 3x or more cost of orchestrator
|
|
17609
|
+
- Capabilities: Runs multiple models in parallel, synthesizes their responses via a council master
|
|
17610
|
+
- **Delegate when:** Critical decisions needing diverse model perspectives \u2022 High-stakes architectural choices where consensus reduces risk \u2022 Ambiguous problems where multi-model disagreement is informative \u2022 Security-sensitive design reviews
|
|
17611
|
+
- **Don't delegate when:** Straightforward tasks you're confident about \u2022 Speed matters more than confidence \u2022 Single-model answer is sufficient \u2022 Routine implementation work
|
|
17612
|
+
- **Result handling:** Present the council's synthesized response verbatim. Do not re-summarize \u2014 the council master has already produced the final answer.
|
|
17613
|
+
- **Rule of thumb:** Need second/third opinions from different models? \u2192 @council. One good answer enough? \u2192 yourself.
|
|
17614
|
+
|
|
17615
|
+
</Agents>
|
|
17616
|
+
|
|
17617
|
+
<Workflow>
|
|
17618
|
+
|
|
17619
|
+
## 1. Understand
|
|
17620
|
+
Parse request: explicit requirements + implicit needs.
|
|
17621
|
+
|
|
17622
|
+
## 2. Path Selection
|
|
17623
|
+
Evaluate approach by: quality, speed, cost, reliability.
|
|
17624
|
+
Choose the path that optimizes all four.
|
|
17625
|
+
|
|
17626
|
+
## 3. Delegation Check
|
|
17627
|
+
**STOP. Review specialists before acting.**
|
|
17628
|
+
|
|
17629
|
+
!!! Review available agents and delegation rules. Decide whether to delegate or do it yourself. !!!
|
|
17630
|
+
|
|
17631
|
+
**Delegation efficiency:**
|
|
17632
|
+
- Reference paths/lines, don't paste files (\`src/app.ts:42\` not full contents)
|
|
17633
|
+
- Provide context summaries, let specialists read what they need
|
|
17634
|
+
- Brief user on delegation goal before each call
|
|
17635
|
+
- Skip delegation if overhead \u2265 doing it yourself
|
|
17636
|
+
|
|
17637
|
+
## 4. Split and Parallelize
|
|
17638
|
+
Can tasks be split into subtasks and run in parallel?
|
|
17639
|
+
- Multiple @explorer searches across different domains?
|
|
17640
|
+
- @explorer + @librarian research in parallel?
|
|
17641
|
+
- Multiple @fixer instances for faster, scoped implementation?
|
|
17642
|
+
|
|
17643
|
+
Balance: respect dependencies, avoid parallelizing what must be sequential.
|
|
17644
|
+
|
|
17645
|
+
## 5. Execute
|
|
17646
|
+
1. Break complex tasks into todos
|
|
17647
|
+
2. Fire parallel research/implementation
|
|
17648
|
+
3. Delegate to specialists or do it yourself based on step 3
|
|
17649
|
+
4. Integrate results
|
|
17650
|
+
5. Adjust if needed
|
|
17651
|
+
|
|
17652
|
+
## 6. Verify
|
|
17653
|
+
- Run \`lsp_diagnostics\` for errors
|
|
17654
|
+
- Suggest \`simplify\` skill when applicable
|
|
17655
|
+
- Confirm specialists completed successfully
|
|
17656
|
+
- Verify solution meets requirements
|
|
17657
|
+
|
|
17658
|
+
</Workflow>
|
|
17659
|
+
|
|
17660
|
+
<Communication>
|
|
17661
|
+
|
|
17662
|
+
## Clarity Over Assumptions
|
|
17663
|
+
- If request is vague or has multiple valid interpretations, ask a targeted question before proceeding
|
|
17664
|
+
- Don't guess at critical details (file paths, API choices, architectural decisions)
|
|
17665
|
+
- Do make reasonable assumptions for minor details and state them briefly
|
|
17666
|
+
|
|
17667
|
+
## Concise Execution
|
|
17668
|
+
- Answer directly, no preamble
|
|
17669
|
+
- Don't summarize what you did unless asked
|
|
17670
|
+
- Don't explain code unless asked
|
|
17671
|
+
- One-word answers are fine when appropriate
|
|
17672
|
+
- Brief delegation notices: "Checking docs via @librarian..." not "I'm going to delegate to @librarian because..."
|
|
17673
|
+
|
|
17674
|
+
## No Flattery
|
|
17675
|
+
Never: "Great question!" "Excellent idea!" "Smart choice!" or any praise of user input.
|
|
17676
|
+
|
|
17677
|
+
## Honest Pushback
|
|
17678
|
+
When user's approach seems problematic:
|
|
17679
|
+
- State concern + alternative concisely
|
|
17680
|
+
- Ask if they want to proceed anyway
|
|
17681
|
+
- Don't lecture, don't blindly implement
|
|
17682
|
+
|
|
17683
|
+
## Example
|
|
17684
|
+
**Bad:** "Great question! Let me think about the best approach here. I'm going to delegate to @librarian to check the latest Next.js documentation for the App Router, and then I'll implement the solution for you."
|
|
17685
|
+
|
|
17686
|
+
**Good:** "Checking Next.js App Router docs via @librarian..."
|
|
17687
|
+
[proceeds with implementation]
|
|
17688
|
+
|
|
17689
|
+
</Communication>
|
|
17690
|
+
`;
|
|
17691
|
+
function createOrchestratorAgent(model, customPrompt, customAppendPrompt) {
|
|
17692
|
+
const prompt = resolvePrompt(ORCHESTRATOR_PROMPT, customPrompt, customAppendPrompt);
|
|
17693
|
+
const definition = {
|
|
17694
|
+
name: "orchestrator",
|
|
17695
|
+
description: "AI coding orchestrator that delegates tasks to specialist agents for optimal quality, speed, and cost",
|
|
17696
|
+
config: {
|
|
17697
|
+
temperature: 0.1,
|
|
17698
|
+
prompt
|
|
17699
|
+
}
|
|
17700
|
+
};
|
|
17701
|
+
if (Array.isArray(model)) {
|
|
17702
|
+
definition._modelArray = model.map((m) => typeof m === "string" ? { id: m } : m);
|
|
17703
|
+
} else if (typeof model === "string" && model) {
|
|
17704
|
+
definition.config.model = model;
|
|
17705
|
+
}
|
|
17706
|
+
return definition;
|
|
17707
|
+
}
|
|
17708
|
+
|
|
17709
|
+
// src/agents/council.ts
|
|
17710
|
+
var COUNCIL_AGENT_PROMPT = `You are the Council agent \u2014 a multi-LLM orchestration system that runs consensus across multiple models.
|
|
17711
|
+
|
|
17712
|
+
**Tool**: You have access to the \`council_session\` tool.
|
|
17713
|
+
|
|
17714
|
+
**When to use**:
|
|
17715
|
+
- When invoked by a user with a request
|
|
17716
|
+
- When you want multiple expert opinions on a complex problem
|
|
17717
|
+
- When higher confidence is needed through model consensus
|
|
17718
|
+
|
|
17719
|
+
**Usage**:
|
|
17720
|
+
1. Call the \`council_session\` tool with the user's prompt
|
|
17721
|
+
2. Optionally specify a preset (default: "default")
|
|
17722
|
+
3. Receive the synthesized response from the council master
|
|
17723
|
+
4. Present the result to the user
|
|
17724
|
+
|
|
17725
|
+
**Behavior**:
|
|
17726
|
+
- Delegate requests directly to council_session
|
|
17727
|
+
- Don't pre-analyze or filter the prompt
|
|
17728
|
+
- Present the synthesized result verbatim \u2014 do not re-summarize or condense
|
|
17729
|
+
- Briefly explain the consensus if requested`;
|
|
17730
|
+
function createCouncilAgent(model, customPrompt, customAppendPrompt) {
|
|
17731
|
+
const prompt = resolvePrompt(COUNCIL_AGENT_PROMPT, customPrompt, customAppendPrompt);
|
|
17732
|
+
const definition = {
|
|
17733
|
+
name: "council",
|
|
17734
|
+
description: "Multi-LLM council agent that synthesizes responses from multiple models for higher-quality outputs",
|
|
17735
|
+
config: {
|
|
17736
|
+
temperature: 0.1,
|
|
17737
|
+
prompt
|
|
17738
|
+
}
|
|
17739
|
+
};
|
|
17740
|
+
if (model) {
|
|
17741
|
+
definition.config.model = model;
|
|
17742
|
+
}
|
|
17743
|
+
return definition;
|
|
17744
|
+
}
|
|
17745
|
+
function formatCouncillorPrompt(userPrompt, councillorPrompt) {
|
|
17746
|
+
if (!councillorPrompt)
|
|
17747
|
+
return userPrompt;
|
|
17748
|
+
return `${councillorPrompt}
|
|
17749
|
+
|
|
17750
|
+
---
|
|
17751
|
+
|
|
17752
|
+
${userPrompt}`;
|
|
17753
|
+
}
|
|
17754
|
+
function formatMasterSynthesisPrompt(originalPrompt, councillorResults, masterPrompt) {
|
|
17755
|
+
const completedWithResults = councillorResults.filter((cr) => cr.status === "completed" && cr.result);
|
|
17756
|
+
const councillorSection = completedWithResults.map((cr) => {
|
|
17757
|
+
const shortModel = shortModelLabel(cr.model);
|
|
17758
|
+
return `**${cr.name}** (${shortModel}):
|
|
17759
|
+
${cr.result}`;
|
|
17760
|
+
}).join(`
|
|
17761
|
+
|
|
17762
|
+
`);
|
|
17763
|
+
const failedSection = councillorResults.filter((cr) => cr.status !== "completed").map((cr) => `**${cr.name}**: ${cr.status} \u2014 ${cr.error ?? "Unknown"}`).join(`
|
|
17764
|
+
`);
|
|
17765
|
+
if (completedWithResults.length === 0) {
|
|
17766
|
+
return `---
|
|
17767
|
+
|
|
17768
|
+
**Original Prompt**:
|
|
17769
|
+
${originalPrompt}
|
|
17770
|
+
|
|
17771
|
+
---
|
|
17772
|
+
|
|
17773
|
+
**Councillor Responses**:
|
|
17774
|
+
All councillors failed to produce output. Please generate a response based on the original prompt alone.`;
|
|
17775
|
+
}
|
|
17776
|
+
let prompt = `---
|
|
17777
|
+
|
|
17778
|
+
**Original Prompt**:
|
|
17779
|
+
${originalPrompt}
|
|
17780
|
+
|
|
17781
|
+
---
|
|
17782
|
+
|
|
17783
|
+
**Councillor Responses**:
|
|
17784
|
+
${councillorSection}`;
|
|
17785
|
+
if (failedSection) {
|
|
17786
|
+
prompt += `
|
|
17787
|
+
|
|
17788
|
+
---
|
|
17789
|
+
|
|
17790
|
+
**Failed/Timed-out Councillors**:
|
|
17791
|
+
${failedSection}`;
|
|
17792
|
+
}
|
|
17793
|
+
prompt += `
|
|
17794
|
+
|
|
17795
|
+
---
|
|
17796
|
+
|
|
17797
|
+
Synthesize the optimal response based on the above.`;
|
|
17798
|
+
if (masterPrompt) {
|
|
17799
|
+
prompt += `
|
|
17800
|
+
|
|
17801
|
+
---
|
|
17802
|
+
|
|
17803
|
+
**Master Guidance**:
|
|
17804
|
+
${masterPrompt}`;
|
|
17805
|
+
}
|
|
17806
|
+
return prompt;
|
|
17807
|
+
}
|
|
17808
|
+
|
|
17809
|
+
// src/agents/council-master.ts
|
|
17810
|
+
var COUNCIL_MASTER_PROMPT = `You are the council master responsible for synthesizing responses from multiple AI models.
|
|
17811
|
+
|
|
17812
|
+
**Role**: Review all councillor responses and create the optimal final answer.
|
|
17813
|
+
|
|
17814
|
+
**Process**:
|
|
17815
|
+
1. Read the original user prompt
|
|
17816
|
+
2. Review each councillor's response carefully
|
|
17817
|
+
3. Identify the best elements from each response
|
|
17818
|
+
4. Resolve contradictions between councillors
|
|
17819
|
+
5. Synthesize a final, optimal response
|
|
17820
|
+
|
|
17821
|
+
**Behavior**:
|
|
17822
|
+
- Each councillor had read-only access to the codebase \u2014 their responses may reference specific files, functions, and line numbers
|
|
17823
|
+
- Clearly explain your reasoning for the chosen approach
|
|
17824
|
+
- Be transparent about trade-offs
|
|
17825
|
+
- Credit specific insights from individual councillors by name
|
|
17826
|
+
- If councillors disagree, explain your resolution
|
|
17827
|
+
- Don't just average responses \u2014 choose and improve
|
|
17828
|
+
|
|
17829
|
+
**Output**:
|
|
17830
|
+
- Present the synthesized solution
|
|
17831
|
+
- Review, retain, and include relevant code examples, diagrams, and concrete details from councillor responses
|
|
17832
|
+
- Explain your synthesis reasoning
|
|
17833
|
+
- Note any remaining uncertainties
|
|
17834
|
+
- Acknowledge if consensus was impossible`;
|
|
17835
|
+
function createCouncilMasterAgent(model, customPrompt, customAppendPrompt) {
|
|
17836
|
+
const prompt = resolvePrompt(COUNCIL_MASTER_PROMPT, customPrompt, customAppendPrompt);
|
|
17837
|
+
return {
|
|
17838
|
+
name: "council-master",
|
|
17839
|
+
description: "Council synthesis engine. Receives councillor responses and produces the final answer. No tools, pure text synthesis.",
|
|
17840
|
+
config: {
|
|
17841
|
+
model,
|
|
17842
|
+
temperature: 0.1,
|
|
17843
|
+
prompt,
|
|
17844
|
+
permission: {
|
|
17845
|
+
"*": "deny",
|
|
17846
|
+
question: "deny"
|
|
17847
|
+
}
|
|
17848
|
+
}
|
|
17849
|
+
};
|
|
17850
|
+
}
|
|
17851
|
+
|
|
17852
|
+
// src/agents/councillor.ts
|
|
17853
|
+
var COUNCILLOR_PROMPT = `You are a councillor in a multi-model council.
|
|
17854
|
+
|
|
17855
|
+
**Role**: Provide your best independent analysis and solution to the given problem.
|
|
17856
|
+
|
|
17857
|
+
**Capabilities**: You have read-only access to the codebase. You can:
|
|
17858
|
+
- Read files (read)
|
|
17859
|
+
- Search by name patterns (glob)
|
|
17860
|
+
- Search by content (grep)
|
|
17861
|
+
- Query language server (lsp_diagnostics, lsp_goto_definition, lsp_find_references)
|
|
17862
|
+
- Search code patterns (ast_grep_search)
|
|
17863
|
+
- Search external docs (if MCPs are configured for this agent)
|
|
17864
|
+
|
|
17865
|
+
You CANNOT edit files, write files, run shell commands, or delegate to other agents. You are an advisor, not an implementer.
|
|
17866
|
+
|
|
17867
|
+
**Behavior**:
|
|
17868
|
+
- **Examine the codebase** before answering \u2014 your read access is what makes council valuable. Don't guess at code you can see.
|
|
17869
|
+
- Analyze the problem thoroughly
|
|
17870
|
+
- Provide a complete, well-reasoned response
|
|
17871
|
+
- Focus on the quality and correctness of your solution
|
|
17872
|
+
- Be direct and concise
|
|
17873
|
+
- Don't be influenced by what other councillors might say \u2014 you won't see their responses
|
|
17874
|
+
|
|
17875
|
+
**Output**:
|
|
17876
|
+
- Give your honest assessment
|
|
17877
|
+
- Reference specific files and line numbers when relevant
|
|
17878
|
+
- Include relevant reasoning
|
|
17879
|
+
- State any assumptions clearly
|
|
17880
|
+
- Note any uncertainties`;
|
|
17881
|
+
function createCouncillorAgent(model, customPrompt, customAppendPrompt) {
|
|
17882
|
+
const prompt = resolvePrompt(COUNCILLOR_PROMPT, customPrompt, customAppendPrompt);
|
|
17883
|
+
return {
|
|
17884
|
+
name: "councillor",
|
|
17885
|
+
description: "Read-only council advisor. Examines codebase and provides independent analysis. Spawned internally by the council system.",
|
|
17886
|
+
config: {
|
|
17887
|
+
model,
|
|
17888
|
+
temperature: 0.2,
|
|
17889
|
+
prompt,
|
|
17890
|
+
permission: {
|
|
17891
|
+
"*": "deny",
|
|
17892
|
+
question: "deny",
|
|
17893
|
+
read: "allow",
|
|
17894
|
+
glob: "allow",
|
|
17895
|
+
grep: "allow",
|
|
17896
|
+
lsp: "allow",
|
|
17897
|
+
list: "allow",
|
|
17898
|
+
codesearch: "allow"
|
|
17899
|
+
}
|
|
17900
|
+
}
|
|
17901
|
+
};
|
|
17902
|
+
}
|
|
17903
|
+
|
|
17904
|
+
// src/agents/designer.ts
|
|
17905
|
+
var DESIGNER_PROMPT = `You are a Designer - a frontend UI/UX specialist who creates intentional, polished experiences.
|
|
17906
|
+
|
|
17907
|
+
**Role**: Craft cohesive UI/UX that balances visual impact with usability.
|
|
17908
|
+
|
|
17909
|
+
## Design Principles
|
|
17910
|
+
|
|
17911
|
+
**Typography**
|
|
17912
|
+
- Choose distinctive, characterful fonts that elevate aesthetics
|
|
17913
|
+
- Avoid generic defaults (Arial, Inter)\u2014opt for unexpected, beautiful choices
|
|
17914
|
+
- Pair display fonts with refined body fonts for hierarchy
|
|
17915
|
+
|
|
17916
|
+
**Color & Theme**
|
|
17917
|
+
- Commit to a cohesive aesthetic with clear color variables
|
|
17918
|
+
- Dominant colors with sharp accents > timid, evenly-distributed palettes
|
|
17919
|
+
- Create atmosphere through intentional color relationships
|
|
17920
|
+
|
|
17921
|
+
**Motion & Interaction**
|
|
17922
|
+
- Leverage framework animation utilities when available (Tailwind's transition/animation classes)
|
|
17923
|
+
- Focus on high-impact moments: orchestrated page loads with staggered reveals
|
|
17924
|
+
- Use scroll-triggers and hover states that surprise and delight
|
|
17925
|
+
- One well-timed animation > scattered micro-interactions
|
|
17926
|
+
- Drop to custom CSS/JS only when utilities can't achieve the vision
|
|
17927
|
+
|
|
17928
|
+
**Spatial Composition**
|
|
17929
|
+
- Break conventions: asymmetry, overlap, diagonal flow, grid-breaking
|
|
17930
|
+
- Generous negative space OR controlled density\u2014commit to the choice
|
|
17931
|
+
- Unexpected layouts that guide the eye
|
|
17932
|
+
|
|
17933
|
+
**Visual Depth**
|
|
17934
|
+
- Create atmosphere beyond solid colors: gradient meshes, noise textures, geometric patterns
|
|
17935
|
+
- Layer transparencies, dramatic shadows, decorative borders
|
|
17936
|
+
- Contextual effects that match the aesthetic (grain overlays, custom cursors)
|
|
17937
|
+
|
|
17938
|
+
**Styling Approach**
|
|
17939
|
+
- Default to Tailwind CSS utility classes when available\u2014fast, maintainable, consistent
|
|
17940
|
+
- Use custom CSS when the vision requires it: complex animations, unique effects, advanced compositions
|
|
17941
|
+
- Balance utility-first speed with creative freedom where it matters
|
|
17942
|
+
|
|
17943
|
+
**Match Vision to Execution**
|
|
17944
|
+
- Maximalist designs \u2192 elaborate implementation, extensive animations, rich effects
|
|
17945
|
+
- Minimalist designs \u2192 restraint, precision, careful spacing and typography
|
|
17946
|
+
- Elegance comes from executing the chosen vision fully, not halfway
|
|
17221
17947
|
|
|
17222
17948
|
## Constraints
|
|
17223
17949
|
- Respect existing design systems when present
|
|
@@ -17251,19 +17977,9 @@ var EXPLORER_PROMPT = `You are Explorer - a fast codebase navigation specialist.
|
|
|
17251
17977
|
|
|
17252
17978
|
**Role**: Quick contextual grep for codebases. Answer "Where is X?", "Find Y", "Which file has Z".
|
|
17253
17979
|
|
|
17254
|
-
**
|
|
17255
|
-
- **grep**: Fast regex content search (powered by ripgrep). Use for text patterns, function names, strings.
|
|
17256
|
-
Example: grep(pattern="function handleClick", include="*.ts")
|
|
17257
|
-
- **glob**: File pattern matching. Use to find files by name/extension.
|
|
17258
|
-
- **ast_grep_search**: AST-aware structural search (25 languages). Use for code patterns.
|
|
17259
|
-
- Meta-variables: $VAR (single node), $$$ (multiple nodes)
|
|
17260
|
-
- Patterns must be complete AST nodes
|
|
17261
|
-
- Example: ast_grep_search(pattern="console.log($MSG)", lang="typescript")
|
|
17262
|
-
- Example: ast_grep_search(pattern="async function $NAME($$$) { $$$ }", lang="javascript")
|
|
17263
|
-
|
|
17264
|
-
**When to use which**:
|
|
17980
|
+
**When to use which tools**:
|
|
17265
17981
|
- **Text/regex patterns** (strings, comments, variable names): grep
|
|
17266
|
-
- **Structural patterns** (function shapes, class structures): ast_grep_search
|
|
17982
|
+
- **Structural patterns** (function shapes, class structures): ast_grep_search
|
|
17267
17983
|
- **File discovery** (find by name/extension): glob
|
|
17268
17984
|
|
|
17269
17985
|
**Behavior**:
|
|
@@ -17448,165 +18164,6 @@ ${customAppendPrompt}`;
|
|
|
17448
18164
|
};
|
|
17449
18165
|
}
|
|
17450
18166
|
|
|
17451
|
-
// src/agents/orchestrator.ts
|
|
17452
|
-
var ORCHESTRATOR_PROMPT = `<Role>
|
|
17453
|
-
You are an AI coding orchestrator that optimizes for quality, speed, cost, and reliability by delegating to specialists when it provides net efficiency gains.
|
|
17454
|
-
</Role>
|
|
17455
|
-
|
|
17456
|
-
<Agents>
|
|
17457
|
-
|
|
17458
|
-
@explorer
|
|
17459
|
-
- Role: Parallel search specialist for discovering unknowns across the codebase
|
|
17460
|
-
- Capabilities: Glob, grep, AST queries to locate files, symbols, patterns
|
|
17461
|
-
- **Delegate when:** Need to discover what exists before planning \u2022 Parallel searches speed discovery \u2022 Need summarized map vs full contents \u2022 Broad/uncertain scope
|
|
17462
|
-
- **Don't delegate when:** Know the path and need actual content \u2022 Need full file anyway \u2022 Single specific lookup \u2022 About to edit the file
|
|
17463
|
-
|
|
17464
|
-
@librarian
|
|
17465
|
-
- Role: Authoritative source for current library docs and API references
|
|
17466
|
-
- Capabilities: Fetches latest official docs, examples, API signatures, version-specific behavior via grep_app MCP
|
|
17467
|
-
- **Delegate when:** Libraries with frequent API changes (React, Next.js, AI SDKs) \u2022 Complex APIs needing official examples (ORMs, auth) \u2022 Version-specific behavior matters \u2022 Unfamiliar library \u2022 Edge cases or advanced features \u2022 Nuanced best practices
|
|
17468
|
-
- **Don't delegate when:** Standard usage you're confident about (\`Array.map()\`, \`fetch()\`) \u2022 Simple stable APIs \u2022 General programming knowledge \u2022 Info already in conversation \u2022 Built-in language features
|
|
17469
|
-
- **Rule of thumb:** "How does this library work?" \u2192 @librarian. "How does programming work?" \u2192 yourself.
|
|
17470
|
-
|
|
17471
|
-
@oracle
|
|
17472
|
-
- Role: Strategic advisor for high-stakes decisions and persistent problems
|
|
17473
|
-
- Capabilities: Deep architectural reasoning, system-level trade-offs, complex debugging
|
|
17474
|
-
- Tools/Constraints: Slow, expensive, high-quality\u2014use sparingly when thoroughness beats speed
|
|
17475
|
-
- **Delegate when:** Major architectural decisions with long-term impact \u2022 Problems persisting after 2+ fix attempts \u2022 High-risk multi-system refactors \u2022 Costly trade-offs (performance vs maintainability) \u2022 Complex debugging with unclear root cause \u2022 Security/scalability/data integrity decisions \u2022 Genuinely uncertain and cost of wrong choice is high
|
|
17476
|
-
- **Don't delegate when:** Routine decisions you're confident about \u2022 First bug fix attempt \u2022 Straightforward trade-offs \u2022 Tactical "how" vs strategic "should" \u2022 Time-sensitive good-enough decisions \u2022 Quick research/testing can answer
|
|
17477
|
-
- **Rule of thumb:** Need senior architect review? \u2192 @oracle. Just do it and PR? \u2192 yourself.
|
|
17478
|
-
|
|
17479
|
-
@designer
|
|
17480
|
-
- Role: UI/UX specialist for intentional, polished experiences
|
|
17481
|
-
- Capabilities: Visual direction, interactions, responsive layouts, design systems with aesthetic intent
|
|
17482
|
-
- **Delegate when:** User-facing interfaces needing polish \u2022 Responsive layouts \u2022 UX-critical components (forms, nav, dashboards) \u2022 Visual consistency systems \u2022 Animations/micro-interactions \u2022 Landing/marketing pages \u2022 Refining functional\u2192delightful
|
|
17483
|
-
- **Don't delegate when:** Backend/logic with no visual \u2022 Quick prototypes where design doesn't matter yet
|
|
17484
|
-
- **Rule of thumb:** Users see it and polish matters? \u2192 @designer. Headless/functional? \u2192 yourself.
|
|
17485
|
-
|
|
17486
|
-
@fixer
|
|
17487
|
-
- Role: Fast, parallel execution specialist for well-defined tasks
|
|
17488
|
-
- Capabilities: Efficient implementation when spec and context are clear
|
|
17489
|
-
- Tools/Constraints: Execution-focused\u2014no research, no architectural decisions
|
|
17490
|
-
- **Delegate when:** Clearly specified with known approach \u2022 3+ independent parallel tasks \u2022 Straightforward but time-consuming \u2022 Solid plan needing execution \u2022 Repetitive multi-location changes \u2022 Overhead < time saved by parallelization
|
|
17491
|
-
- **Don't delegate when:** Needs discovery/research/decisions \u2022 Single small change (<20 lines, one file) \u2022 Unclear requirements needing iteration \u2022 Explaining > doing \u2022 Tight integration with your current work \u2022 Sequential dependencies
|
|
17492
|
-
- **Parallelization:** 3+ independent tasks \u2192 spawn multiple @fixers. 1-2 simple tasks \u2192 do yourself.
|
|
17493
|
-
- **Rule of thumb:** Explaining > doing? \u2192 yourself. Can split to parallel streams? \u2192 multiple @fixers.
|
|
17494
|
-
|
|
17495
|
-
</Agents>
|
|
17496
|
-
|
|
17497
|
-
<Workflow>
|
|
17498
|
-
|
|
17499
|
-
## 1. Understand
|
|
17500
|
-
Parse request: explicit requirements + implicit needs.
|
|
17501
|
-
|
|
17502
|
-
## 2. Path Analysis
|
|
17503
|
-
Evaluate approach by: quality, speed, cost, reliability.
|
|
17504
|
-
Choose the path that optimizes all four.
|
|
17505
|
-
|
|
17506
|
-
## 3. Delegation Check
|
|
17507
|
-
**STOP. Review specialists before acting.**
|
|
17508
|
-
|
|
17509
|
-
Each specialist delivers 10x results in their domain:
|
|
17510
|
-
- @explorer \u2192 Parallel discovery when you need to find unknowns, not read knowns
|
|
17511
|
-
- @librarian \u2192 Complex/evolving APIs where docs prevent errors, not basic usage
|
|
17512
|
-
- @oracle \u2192 High-stakes decisions where wrong choice is costly, not routine calls
|
|
17513
|
-
- @designer \u2192 User-facing experiences where polish matters, not internal logic
|
|
17514
|
-
- @fixer \u2192 Parallel execution of clear specs, not explaining trivial changes
|
|
17515
|
-
|
|
17516
|
-
**Delegation efficiency:**
|
|
17517
|
-
- Reference paths/lines, don't paste files (\`src/app.ts:42\` not full contents)
|
|
17518
|
-
- Provide context summaries, let specialists read what they need
|
|
17519
|
-
- Brief user on delegation goal before each call
|
|
17520
|
-
- Skip delegation if overhead \u2265 doing it yourself
|
|
17521
|
-
|
|
17522
|
-
**Fixer parallelization:**
|
|
17523
|
-
- 3+ independent tasks? Spawn multiple @fixers simultaneously
|
|
17524
|
-
- 1-2 simple tasks? Do it yourself
|
|
17525
|
-
- Sequential dependencies? Handle serially or do yourself
|
|
17526
|
-
|
|
17527
|
-
## 4. Parallelize
|
|
17528
|
-
Can tasks run simultaneously?
|
|
17529
|
-
- Multiple @explorer searches across different domains?
|
|
17530
|
-
- @explorer + @librarian research in parallel?
|
|
17531
|
-
- Multiple @fixer instances for independent changes?
|
|
17532
|
-
|
|
17533
|
-
Balance: respect dependencies, avoid parallelizing what must be sequential.
|
|
17534
|
-
|
|
17535
|
-
## 5. Execute
|
|
17536
|
-
1. Break complex tasks into todos if needed
|
|
17537
|
-
2. Fire parallel research/implementation
|
|
17538
|
-
3. Delegate to specialists or do it yourself based on step 3
|
|
17539
|
-
4. Integrate results
|
|
17540
|
-
5. Adjust if needed
|
|
17541
|
-
|
|
17542
|
-
## 6. Verify
|
|
17543
|
-
- Run \`lsp_diagnostics\` for errors
|
|
17544
|
-
- Suggest \`simplify\` skill when applicable
|
|
17545
|
-
- Confirm specialists completed successfully
|
|
17546
|
-
- Verify solution meets requirements
|
|
17547
|
-
|
|
17548
|
-
## Agent Role Mapping
|
|
17549
|
-
When a workflow calls for an **implementer** subagent: dispatch \`@fixer\`. Fixer has enforced constraints (no research, no delegation, structured output) that match the implementer role exactly.
|
|
17550
|
-
When a workflow calls for a **reviewer** subagent: dispatch \`@oracle\`. Oracle has the depth for architectural review and access to code review skills.
|
|
17551
|
-
|
|
17552
|
-
</Workflow>
|
|
17553
|
-
|
|
17554
|
-
<Communication>
|
|
17555
|
-
|
|
17556
|
-
## Clarity Over Assumptions
|
|
17557
|
-
- If request is vague or has multiple valid interpretations, ask a targeted question before proceeding
|
|
17558
|
-
- Don't guess at critical details (file paths, API choices, architectural decisions)
|
|
17559
|
-
- Do make reasonable assumptions for minor details and state them briefly
|
|
17560
|
-
|
|
17561
|
-
## Concise Execution
|
|
17562
|
-
- Answer directly, no preamble
|
|
17563
|
-
- Don't summarize what you did unless asked
|
|
17564
|
-
- Don't explain code unless asked
|
|
17565
|
-
- One-word answers are fine when appropriate
|
|
17566
|
-
- Brief delegation notices: "Checking docs via @librarian..." not "I'm going to delegate to @librarian because..."
|
|
17567
|
-
|
|
17568
|
-
## No Flattery
|
|
17569
|
-
Never: "Great question!" "Excellent idea!" "Smart choice!" or any praise of user input.
|
|
17570
|
-
|
|
17571
|
-
## Honest Pushback
|
|
17572
|
-
When user's approach seems problematic:
|
|
17573
|
-
- State concern + alternative concisely
|
|
17574
|
-
- Ask if they want to proceed anyway
|
|
17575
|
-
- Don't lecture, don't blindly implement
|
|
17576
|
-
|
|
17577
|
-
## Example
|
|
17578
|
-
**Bad:** "Great question! Let me think about the best approach here. I'm going to delegate to @librarian to check the latest Next.js documentation for the App Router, and then I'll implement the solution for you."
|
|
17579
|
-
|
|
17580
|
-
**Good:** "Checking Next.js App Router docs via @librarian..."
|
|
17581
|
-
[proceeds with implementation]
|
|
17582
|
-
|
|
17583
|
-
</Communication>
|
|
17584
|
-
`;
|
|
17585
|
-
function createOrchestratorAgent(model, customPrompt, customAppendPrompt) {
|
|
17586
|
-
let prompt = ORCHESTRATOR_PROMPT;
|
|
17587
|
-
if (customPrompt) {
|
|
17588
|
-
prompt = customPrompt;
|
|
17589
|
-
} else if (customAppendPrompt) {
|
|
17590
|
-
prompt = `${ORCHESTRATOR_PROMPT}
|
|
17591
|
-
|
|
17592
|
-
${customAppendPrompt}`;
|
|
17593
|
-
}
|
|
17594
|
-
const definition = {
|
|
17595
|
-
name: "orchestrator",
|
|
17596
|
-
description: "AI coding orchestrator that delegates tasks to specialist agents for optimal quality, speed, and cost",
|
|
17597
|
-
config: {
|
|
17598
|
-
temperature: 0.1,
|
|
17599
|
-
prompt
|
|
17600
|
-
}
|
|
17601
|
-
};
|
|
17602
|
-
if (Array.isArray(model)) {
|
|
17603
|
-
definition._modelArray = model.map((m) => typeof m === "string" ? { id: m } : m);
|
|
17604
|
-
} else if (typeof model === "string" && model) {
|
|
17605
|
-
definition.config.model = model;
|
|
17606
|
-
}
|
|
17607
|
-
return definition;
|
|
17608
|
-
}
|
|
17609
|
-
|
|
17610
18167
|
// src/agents/index.ts
|
|
17611
18168
|
function applyOverrides(agent, override) {
|
|
17612
18169
|
if (override.model) {
|
|
@@ -17625,9 +18182,10 @@ function applyOverrides(agent, override) {
|
|
|
17625
18182
|
function applyDefaultPermissions(agent, configuredSkills) {
|
|
17626
18183
|
const existing = agent.config.permission ?? {};
|
|
17627
18184
|
const skillPermissions = getSkillPermissionsForAgent(agent.name, configuredSkills);
|
|
18185
|
+
const questionPerm = existing.question === "deny" ? "deny" : "allow";
|
|
17628
18186
|
agent.config.permission = {
|
|
17629
18187
|
...existing,
|
|
17630
|
-
question:
|
|
18188
|
+
question: questionPerm,
|
|
17631
18189
|
skill: {
|
|
17632
18190
|
...typeof existing.skill === "object" ? existing.skill : {},
|
|
17633
18191
|
...skillPermissions
|
|
@@ -17642,7 +18200,10 @@ var SUBAGENT_FACTORIES = {
|
|
|
17642
18200
|
librarian: createLibrarianAgent,
|
|
17643
18201
|
oracle: createOracleAgent,
|
|
17644
18202
|
designer: createDesignerAgent,
|
|
17645
|
-
fixer: createFixerAgent
|
|
18203
|
+
fixer: createFixerAgent,
|
|
18204
|
+
council: createCouncilAgent,
|
|
18205
|
+
councillor: createCouncillorAgent,
|
|
18206
|
+
"council-master": createCouncilMasterAgent
|
|
17646
18207
|
};
|
|
17647
18208
|
function createAgents(config2) {
|
|
17648
18209
|
const getModelForAgent = (name) => {
|
|
@@ -17689,7 +18250,12 @@ function getAgentConfigs(config2) {
|
|
|
17689
18250
|
description: a.description,
|
|
17690
18251
|
mcps: getAgentMcpList(a.name, config2)
|
|
17691
18252
|
};
|
|
17692
|
-
if (
|
|
18253
|
+
if (a.name === "council") {
|
|
18254
|
+
sdkConfig.mode = "all";
|
|
18255
|
+
} else if (a.name === "councillor" || a.name === "council-master") {
|
|
18256
|
+
sdkConfig.mode = "subagent";
|
|
18257
|
+
sdkConfig.hidden = true;
|
|
18258
|
+
} else if (isSubagent(a.name)) {
|
|
17693
18259
|
sdkConfig.mode = "subagent";
|
|
17694
18260
|
} else if (a.name === "orchestrator") {
|
|
17695
18261
|
sdkConfig.mode = "primary";
|
|
@@ -17700,9 +18266,9 @@ function getAgentConfigs(config2) {
|
|
|
17700
18266
|
|
|
17701
18267
|
// src/utils/logger.ts
|
|
17702
18268
|
import * as fs2 from "fs";
|
|
17703
|
-
import * as
|
|
18269
|
+
import * as os from "os";
|
|
17704
18270
|
import * as path2 from "path";
|
|
17705
|
-
var logFile = path2.join(
|
|
18271
|
+
var logFile = path2.join(os.tmpdir(), "oh-my-opencode-slim.log");
|
|
17706
18272
|
function log(message, data) {
|
|
17707
18273
|
try {
|
|
17708
18274
|
const timestamp = new Date().toISOString();
|
|
@@ -18077,17 +18643,48 @@ async function extractZip(archivePath, destDir) {
|
|
|
18077
18643
|
throw new Error(`zip extraction failed (exit ${exitCode}): ${stderr}`);
|
|
18078
18644
|
}
|
|
18079
18645
|
}
|
|
18080
|
-
// src/background/
|
|
18081
|
-
|
|
18082
|
-
|
|
18083
|
-
|
|
18084
|
-
|
|
18646
|
+
// src/background/subagent-depth.ts
|
|
18647
|
+
class SubagentDepthTracker {
|
|
18648
|
+
depthBySession = new Map;
|
|
18649
|
+
_maxDepth;
|
|
18650
|
+
constructor(maxDepth = DEFAULT_MAX_SUBAGENT_DEPTH) {
|
|
18651
|
+
this._maxDepth = maxDepth;
|
|
18652
|
+
}
|
|
18653
|
+
get maxDepth() {
|
|
18654
|
+
return this._maxDepth;
|
|
18655
|
+
}
|
|
18656
|
+
getDepth(sessionId) {
|
|
18657
|
+
return this.depthBySession.get(sessionId) ?? 0;
|
|
18658
|
+
}
|
|
18659
|
+
registerChild(parentSessionId, childSessionId) {
|
|
18660
|
+
const parentDepth = this.getDepth(parentSessionId);
|
|
18661
|
+
const childDepth = parentDepth + 1;
|
|
18662
|
+
if (childDepth > this.maxDepth) {
|
|
18663
|
+
log("[subagent-depth] spawn blocked: max depth exceeded", {
|
|
18664
|
+
parentSessionId,
|
|
18665
|
+
parentDepth,
|
|
18666
|
+
childDepth,
|
|
18667
|
+
maxDepth: this.maxDepth
|
|
18668
|
+
});
|
|
18669
|
+
return false;
|
|
18670
|
+
}
|
|
18671
|
+
this.depthBySession.set(childSessionId, childDepth);
|
|
18672
|
+
log("[subagent-depth] child registered", {
|
|
18673
|
+
parentSessionId,
|
|
18674
|
+
childSessionId,
|
|
18675
|
+
childDepth
|
|
18676
|
+
});
|
|
18677
|
+
return true;
|
|
18678
|
+
}
|
|
18679
|
+
cleanup(sessionId) {
|
|
18680
|
+
this.depthBySession.delete(sessionId);
|
|
18681
|
+
}
|
|
18682
|
+
cleanupAll() {
|
|
18683
|
+
this.depthBySession.clear();
|
|
18085
18684
|
}
|
|
18086
|
-
return {
|
|
18087
|
-
providerID: model.slice(0, slashIndex),
|
|
18088
|
-
modelID: model.slice(slashIndex + 1)
|
|
18089
|
-
};
|
|
18090
18685
|
}
|
|
18686
|
+
|
|
18687
|
+
// src/background/background-manager.ts
|
|
18091
18688
|
function generateTaskId() {
|
|
18092
18689
|
return `bg_${Math.random().toString(36).substring(2, 10)}`;
|
|
18093
18690
|
}
|
|
@@ -18096,6 +18693,7 @@ class BackgroundTaskManager {
|
|
|
18096
18693
|
tasks = new Map;
|
|
18097
18694
|
tasksBySessionId = new Map;
|
|
18098
18695
|
agentBySessionId = new Map;
|
|
18696
|
+
depthTracker;
|
|
18099
18697
|
client;
|
|
18100
18698
|
directory;
|
|
18101
18699
|
tmuxEnabled;
|
|
@@ -18114,6 +18712,7 @@ class BackgroundTaskManager {
|
|
|
18114
18712
|
maxConcurrentStarts: 10
|
|
18115
18713
|
};
|
|
18116
18714
|
this.maxConcurrentStarts = this.backgroundConfig.maxConcurrentStarts;
|
|
18715
|
+
this.depthTracker = new SubagentDepthTracker;
|
|
18117
18716
|
}
|
|
18118
18717
|
getSubagentRules(agentName) {
|
|
18119
18718
|
return SUBAGENT_DELEGATION_RULES[agentName] ?? ["explorer"];
|
|
@@ -18186,20 +18785,6 @@ class BackgroundTaskManager {
|
|
|
18186
18785
|
}
|
|
18187
18786
|
return chain;
|
|
18188
18787
|
}
|
|
18189
|
-
async promptWithTimeout(args, timeoutMs) {
|
|
18190
|
-
if (timeoutMs <= 0) {
|
|
18191
|
-
await this.client.session.prompt(args);
|
|
18192
|
-
return;
|
|
18193
|
-
}
|
|
18194
|
-
await Promise.race([
|
|
18195
|
-
this.client.session.prompt(args),
|
|
18196
|
-
new Promise((_, reject) => {
|
|
18197
|
-
setTimeout(() => {
|
|
18198
|
-
reject(new Error(`Prompt timed out after ${timeoutMs}ms`));
|
|
18199
|
-
}, timeoutMs);
|
|
18200
|
-
})
|
|
18201
|
-
]);
|
|
18202
|
-
}
|
|
18203
18788
|
calculateToolPermissions(agentName) {
|
|
18204
18789
|
const allowedSubagents = this.getSubagentRules(agentName);
|
|
18205
18790
|
if (allowedSubagents.length === 0) {
|
|
@@ -18215,20 +18800,31 @@ class BackgroundTaskManager {
|
|
|
18215
18800
|
return;
|
|
18216
18801
|
}
|
|
18217
18802
|
try {
|
|
18218
|
-
const
|
|
18803
|
+
const parentDepth = this.depthTracker.getDepth(task.parentSessionId);
|
|
18804
|
+
if (parentDepth + 1 > this.depthTracker.maxDepth) {
|
|
18805
|
+
log("[background-manager] spawn blocked: max depth exceeded", {
|
|
18806
|
+
parentSessionId: task.parentSessionId,
|
|
18807
|
+
parentDepth,
|
|
18808
|
+
maxDepth: this.depthTracker.maxDepth
|
|
18809
|
+
});
|
|
18810
|
+
this.completeTask(task, "failed", "Subagent depth exceeded");
|
|
18811
|
+
return;
|
|
18812
|
+
}
|
|
18813
|
+
const session2 = await this.client.session.create({
|
|
18219
18814
|
body: {
|
|
18220
18815
|
parentID: task.parentSessionId,
|
|
18221
18816
|
title: `Background: ${task.description}`
|
|
18222
18817
|
},
|
|
18223
18818
|
query: { directory: this.directory }
|
|
18224
18819
|
});
|
|
18225
|
-
if (!
|
|
18820
|
+
if (!session2.data?.id) {
|
|
18226
18821
|
throw new Error("Failed to create background session");
|
|
18227
18822
|
}
|
|
18228
|
-
task.sessionId =
|
|
18229
|
-
this.tasksBySessionId.set(
|
|
18230
|
-
this.agentBySessionId.set(
|
|
18823
|
+
task.sessionId = session2.data.id;
|
|
18824
|
+
this.tasksBySessionId.set(session2.data.id, task.id);
|
|
18825
|
+
this.agentBySessionId.set(session2.data.id, task.agent);
|
|
18231
18826
|
task.status = "running";
|
|
18827
|
+
this.depthTracker.registerChild(task.parentSessionId, session2.data.id);
|
|
18232
18828
|
if (this.tmuxEnabled) {
|
|
18233
18829
|
await new Promise((r) => setTimeout(r, 500));
|
|
18234
18830
|
}
|
|
@@ -18242,11 +18838,15 @@ class BackgroundTaskManager {
|
|
|
18242
18838
|
});
|
|
18243
18839
|
const fallbackEnabled = this.config?.fallback?.enabled ?? true;
|
|
18244
18840
|
const timeoutMs = fallbackEnabled ? this.config?.fallback?.timeoutMs ?? FALLBACK_FAILOVER_TIMEOUT_MS : 0;
|
|
18841
|
+
const retryDelayMs = this.config?.fallback?.retryDelayMs ?? 500;
|
|
18245
18842
|
const chain = fallbackEnabled ? this.resolveFallbackChain(task.agent) : [];
|
|
18246
18843
|
const attemptModels = chain.length > 0 ? chain : [undefined];
|
|
18247
18844
|
const errors3 = [];
|
|
18248
18845
|
let succeeded = false;
|
|
18249
|
-
|
|
18846
|
+
const sessionId = session2.data.id;
|
|
18847
|
+
for (let i = 0;i < attemptModels.length; i++) {
|
|
18848
|
+
const model = attemptModels[i];
|
|
18849
|
+
const modelLabel = model ?? "default-model";
|
|
18250
18850
|
try {
|
|
18251
18851
|
const body = {
|
|
18252
18852
|
...basePromptBody,
|
|
@@ -18259,8 +18859,11 @@ class BackgroundTaskManager {
|
|
|
18259
18859
|
}
|
|
18260
18860
|
body.model = ref;
|
|
18261
18861
|
}
|
|
18262
|
-
|
|
18263
|
-
|
|
18862
|
+
if (i > 0) {
|
|
18863
|
+
log(`[background-manager] fallback attempt ${i + 1}/${attemptModels.length}: ${modelLabel}`, { taskId: task.id });
|
|
18864
|
+
}
|
|
18865
|
+
await promptWithTimeout(this.client, {
|
|
18866
|
+
path: { id: sessionId },
|
|
18264
18867
|
body,
|
|
18265
18868
|
query: promptQuery
|
|
18266
18869
|
}, timeoutMs);
|
|
@@ -18268,10 +18871,17 @@ class BackgroundTaskManager {
|
|
|
18268
18871
|
break;
|
|
18269
18872
|
} catch (error48) {
|
|
18270
18873
|
const msg = error48 instanceof Error ? error48.message : String(error48);
|
|
18271
|
-
|
|
18272
|
-
|
|
18273
|
-
|
|
18274
|
-
|
|
18874
|
+
errors3.push(`${modelLabel}: ${msg}`);
|
|
18875
|
+
log(`[background-manager] model failed: ${modelLabel} \u2014 ${msg}`, {
|
|
18876
|
+
taskId: task.id
|
|
18877
|
+
});
|
|
18878
|
+
if (i < attemptModels.length - 1) {
|
|
18879
|
+
try {
|
|
18880
|
+
await this.client.session.abort({
|
|
18881
|
+
path: { id: sessionId }
|
|
18882
|
+
});
|
|
18883
|
+
await new Promise((r) => setTimeout(r, retryDelayMs));
|
|
18884
|
+
} catch {}
|
|
18275
18885
|
}
|
|
18276
18886
|
}
|
|
18277
18887
|
}
|
|
@@ -18279,7 +18889,7 @@ class BackgroundTaskManager {
|
|
|
18279
18889
|
throw new Error(`All fallback models failed. ${errors3.join(" | ")}`);
|
|
18280
18890
|
}
|
|
18281
18891
|
log(`[background-manager] task started: ${task.id}`, {
|
|
18282
|
-
sessionId:
|
|
18892
|
+
sessionId: session2.data.id
|
|
18283
18893
|
});
|
|
18284
18894
|
} catch (error48) {
|
|
18285
18895
|
const errorMessage = error48 instanceof Error ? error48.message : String(error48);
|
|
@@ -18324,6 +18934,7 @@ class BackgroundTaskManager {
|
|
|
18324
18934
|
task.error = "Session deleted";
|
|
18325
18935
|
this.tasksBySessionId.delete(sessionId);
|
|
18326
18936
|
this.agentBySessionId.delete(sessionId);
|
|
18937
|
+
this.depthTracker.cleanup(sessionId);
|
|
18327
18938
|
const resolver = this.completionResolvers.get(taskId);
|
|
18328
18939
|
if (resolver) {
|
|
18329
18940
|
resolver(task);
|
|
@@ -18336,22 +18947,7 @@ class BackgroundTaskManager {
|
|
|
18336
18947
|
if (!task.sessionId)
|
|
18337
18948
|
return;
|
|
18338
18949
|
try {
|
|
18339
|
-
const
|
|
18340
|
-
path: { id: task.sessionId }
|
|
18341
|
-
});
|
|
18342
|
-
const messages = messagesResult.data ?? [];
|
|
18343
|
-
const assistantMessages = messages.filter((m) => m.info?.role === "assistant");
|
|
18344
|
-
const extractedContent = [];
|
|
18345
|
-
for (const message of assistantMessages) {
|
|
18346
|
-
for (const part of message.parts ?? []) {
|
|
18347
|
-
if ((part.type === "text" || part.type === "reasoning") && part.text) {
|
|
18348
|
-
extractedContent.push(part.text);
|
|
18349
|
-
}
|
|
18350
|
-
}
|
|
18351
|
-
}
|
|
18352
|
-
const responseText = extractedContent.filter((t) => t.length > 0).join(`
|
|
18353
|
-
|
|
18354
|
-
`);
|
|
18950
|
+
const responseText = await extractSessionResult(this.client, task.sessionId);
|
|
18355
18951
|
if (responseText) {
|
|
18356
18952
|
this.completeTask(task, "completed", responseText);
|
|
18357
18953
|
} else {
|
|
@@ -18467,6 +19063,10 @@ class BackgroundTaskManager {
|
|
|
18467
19063
|
this.tasks.clear();
|
|
18468
19064
|
this.tasksBySessionId.clear();
|
|
18469
19065
|
this.agentBySessionId.clear();
|
|
19066
|
+
this.depthTracker.cleanupAll();
|
|
19067
|
+
}
|
|
19068
|
+
getDepthTracker() {
|
|
19069
|
+
return this.depthTracker;
|
|
18470
19070
|
}
|
|
18471
19071
|
}
|
|
18472
19072
|
// src/background/tmux-session-manager.ts
|
|
@@ -18602,54 +19202,315 @@ class TmuxSessionManager {
|
|
|
18602
19202
|
for (const sessionId of sessionsToClose) {
|
|
18603
19203
|
await this.closeSession(sessionId);
|
|
18604
19204
|
}
|
|
18605
|
-
} catch (err) {
|
|
18606
|
-
log("[tmux-session-manager] poll error", { error: String(err) });
|
|
18607
|
-
}
|
|
18608
|
-
}
|
|
18609
|
-
async closeSession(sessionId) {
|
|
18610
|
-
const tracked = this.sessions.get(sessionId);
|
|
18611
|
-
if (!tracked)
|
|
18612
|
-
return;
|
|
18613
|
-
log("[tmux-session-manager] closing session pane", {
|
|
18614
|
-
sessionId,
|
|
18615
|
-
paneId: tracked.paneId
|
|
19205
|
+
} catch (err) {
|
|
19206
|
+
log("[tmux-session-manager] poll error", { error: String(err) });
|
|
19207
|
+
}
|
|
19208
|
+
}
|
|
19209
|
+
async closeSession(sessionId) {
|
|
19210
|
+
const tracked = this.sessions.get(sessionId);
|
|
19211
|
+
if (!tracked)
|
|
19212
|
+
return;
|
|
19213
|
+
log("[tmux-session-manager] closing session pane", {
|
|
19214
|
+
sessionId,
|
|
19215
|
+
paneId: tracked.paneId
|
|
19216
|
+
});
|
|
19217
|
+
await closeTmuxPane(tracked.paneId);
|
|
19218
|
+
this.sessions.delete(sessionId);
|
|
19219
|
+
if (this.sessions.size === 0) {
|
|
19220
|
+
this.stopPolling();
|
|
19221
|
+
}
|
|
19222
|
+
}
|
|
19223
|
+
async cleanup() {
|
|
19224
|
+
this.stopPolling();
|
|
19225
|
+
if (this.sessions.size > 0) {
|
|
19226
|
+
log("[tmux-session-manager] closing all panes", {
|
|
19227
|
+
count: this.sessions.size
|
|
19228
|
+
});
|
|
19229
|
+
const closePromises = Array.from(this.sessions.values()).map((s) => closeTmuxPane(s.paneId).catch((err) => log("[tmux-session-manager] cleanup error for pane", {
|
|
19230
|
+
paneId: s.paneId,
|
|
19231
|
+
error: String(err)
|
|
19232
|
+
})));
|
|
19233
|
+
await Promise.all(closePromises);
|
|
19234
|
+
this.sessions.clear();
|
|
19235
|
+
}
|
|
19236
|
+
log("[tmux-session-manager] cleanup complete");
|
|
19237
|
+
}
|
|
19238
|
+
}
|
|
19239
|
+
// src/council/council-manager.ts
|
|
19240
|
+
class CouncilManager {
|
|
19241
|
+
client;
|
|
19242
|
+
directory;
|
|
19243
|
+
config;
|
|
19244
|
+
depthTracker;
|
|
19245
|
+
tmuxEnabled;
|
|
19246
|
+
constructor(ctx, config2, depthTracker, tmuxEnabled = false) {
|
|
19247
|
+
this.client = ctx.client;
|
|
19248
|
+
this.directory = ctx.directory;
|
|
19249
|
+
this.config = config2;
|
|
19250
|
+
this.depthTracker = depthTracker;
|
|
19251
|
+
this.tmuxEnabled = tmuxEnabled;
|
|
19252
|
+
}
|
|
19253
|
+
async runCouncil(prompt, presetName, parentSessionId) {
|
|
19254
|
+
if (this.depthTracker) {
|
|
19255
|
+
const parentDepth = this.depthTracker.getDepth(parentSessionId);
|
|
19256
|
+
if (parentDepth + 1 > this.depthTracker.maxDepth) {
|
|
19257
|
+
log("[council-manager] spawn blocked: max depth exceeded", {
|
|
19258
|
+
parentSessionId,
|
|
19259
|
+
parentDepth,
|
|
19260
|
+
maxDepth: this.depthTracker.maxDepth
|
|
19261
|
+
});
|
|
19262
|
+
return {
|
|
19263
|
+
success: false,
|
|
19264
|
+
error: "Subagent depth exceeded",
|
|
19265
|
+
councillorResults: []
|
|
19266
|
+
};
|
|
19267
|
+
}
|
|
19268
|
+
}
|
|
19269
|
+
const councilConfig = this.config?.council;
|
|
19270
|
+
if (!councilConfig) {
|
|
19271
|
+
log("[council-manager] Council configuration not found");
|
|
19272
|
+
return {
|
|
19273
|
+
success: false,
|
|
19274
|
+
error: "Council not configured",
|
|
19275
|
+
councillorResults: []
|
|
19276
|
+
};
|
|
19277
|
+
}
|
|
19278
|
+
const resolvedPreset = presetName ?? councilConfig.default_preset ?? "default";
|
|
19279
|
+
const preset = councilConfig.presets[resolvedPreset];
|
|
19280
|
+
if (!preset) {
|
|
19281
|
+
log(`[council-manager] Preset "${resolvedPreset}" not found`);
|
|
19282
|
+
return {
|
|
19283
|
+
success: false,
|
|
19284
|
+
error: `Preset "${resolvedPreset}" not found`,
|
|
19285
|
+
councillorResults: []
|
|
19286
|
+
};
|
|
19287
|
+
}
|
|
19288
|
+
if (Object.keys(preset.councillors).length === 0) {
|
|
19289
|
+
log(`[council-manager] Preset "${resolvedPreset}" has no councillors`);
|
|
19290
|
+
return {
|
|
19291
|
+
success: false,
|
|
19292
|
+
error: `Preset "${resolvedPreset}" has no councillors configured`,
|
|
19293
|
+
councillorResults: []
|
|
19294
|
+
};
|
|
19295
|
+
}
|
|
19296
|
+
const councillorsTimeout = councilConfig.councillors_timeout ?? 180000;
|
|
19297
|
+
const masterTimeout = councilConfig.master_timeout ?? 300000;
|
|
19298
|
+
const councillorCount = Object.keys(preset.councillors).length;
|
|
19299
|
+
log(`[council-manager] Starting council with preset "${resolvedPreset}"`, {
|
|
19300
|
+
councillors: Object.keys(preset.councillors)
|
|
19301
|
+
});
|
|
19302
|
+
this.sendStartNotification(parentSessionId, councillorCount).catch((err) => {
|
|
19303
|
+
log("[council-manager] Failed to send start notification", {
|
|
19304
|
+
error: err instanceof Error ? err.message : String(err)
|
|
19305
|
+
});
|
|
19306
|
+
});
|
|
19307
|
+
const councillorResults = await this.runCouncillors(prompt, preset.councillors, parentSessionId, councillorsTimeout);
|
|
19308
|
+
const completedCount = councillorResults.filter((r) => r.status === "completed").length;
|
|
19309
|
+
log(`[council-manager] Councillors completed: ${completedCount}/${councillorResults.length}`);
|
|
19310
|
+
if (completedCount === 0) {
|
|
19311
|
+
return {
|
|
19312
|
+
success: false,
|
|
19313
|
+
error: "All councillors failed or timed out",
|
|
19314
|
+
councillorResults
|
|
19315
|
+
};
|
|
19316
|
+
}
|
|
19317
|
+
const masterResult = await this.runMaster(prompt, councillorResults, councilConfig, parentSessionId, masterTimeout, preset.master);
|
|
19318
|
+
if (!masterResult.success) {
|
|
19319
|
+
log("[council-manager] Master failed", {
|
|
19320
|
+
error: masterResult.error
|
|
19321
|
+
});
|
|
19322
|
+
const bestResult = councillorResults.find((r) => r.status === "completed" && r.result);
|
|
19323
|
+
return {
|
|
19324
|
+
success: false,
|
|
19325
|
+
error: masterResult.error ?? "Council master failed",
|
|
19326
|
+
result: bestResult?.result ? `(Degraded \u2014 master failed, using ${bestResult.name}'s response)
|
|
19327
|
+
|
|
19328
|
+
${bestResult.result}` : undefined,
|
|
19329
|
+
councillorResults
|
|
19330
|
+
};
|
|
19331
|
+
}
|
|
19332
|
+
log("[council-manager] Council completed successfully");
|
|
19333
|
+
return {
|
|
19334
|
+
success: true,
|
|
19335
|
+
result: masterResult.result,
|
|
19336
|
+
councillorResults
|
|
19337
|
+
};
|
|
19338
|
+
}
|
|
19339
|
+
async sendStartNotification(parentSessionId, councillorCount) {
|
|
19340
|
+
const message = [
|
|
19341
|
+
`\u2394 Council starting \u2014 ${councillorCount} councillors launching \u2014 ctrl+x \u2193 to watch`,
|
|
19342
|
+
"",
|
|
19343
|
+
"[system status: continue without acknowledging this notification]"
|
|
19344
|
+
].join(`
|
|
19345
|
+
`);
|
|
19346
|
+
await this.client.session.prompt({
|
|
19347
|
+
path: { id: parentSessionId },
|
|
19348
|
+
body: {
|
|
19349
|
+
noReply: true,
|
|
19350
|
+
parts: [{ type: "text", text: message }]
|
|
19351
|
+
}
|
|
19352
|
+
});
|
|
19353
|
+
}
|
|
19354
|
+
async runAgentSession(options) {
|
|
19355
|
+
const modelRef = parseModelReference(options.model);
|
|
19356
|
+
if (!modelRef) {
|
|
19357
|
+
throw new Error(`Invalid model format: ${options.model}`);
|
|
19358
|
+
}
|
|
19359
|
+
let sessionId;
|
|
19360
|
+
try {
|
|
19361
|
+
const session2 = await this.client.session.create({
|
|
19362
|
+
body: {
|
|
19363
|
+
parentID: options.parentSessionId,
|
|
19364
|
+
title: options.title
|
|
19365
|
+
},
|
|
19366
|
+
query: { directory: this.directory }
|
|
19367
|
+
});
|
|
19368
|
+
if (!session2.data?.id) {
|
|
19369
|
+
throw new Error("Failed to create session");
|
|
19370
|
+
}
|
|
19371
|
+
sessionId = session2.data.id;
|
|
19372
|
+
if (this.depthTracker) {
|
|
19373
|
+
const registered = this.depthTracker.registerChild(options.parentSessionId, sessionId);
|
|
19374
|
+
if (!registered) {
|
|
19375
|
+
throw new Error("Subagent depth exceeded");
|
|
19376
|
+
}
|
|
19377
|
+
}
|
|
19378
|
+
if (this.tmuxEnabled) {
|
|
19379
|
+
await new Promise((r) => setTimeout(r, TMUX_SPAWN_DELAY_MS));
|
|
19380
|
+
}
|
|
19381
|
+
const body = {
|
|
19382
|
+
agent: options.agent,
|
|
19383
|
+
model: modelRef,
|
|
19384
|
+
tools: { background_task: false, task: false },
|
|
19385
|
+
parts: [{ type: "text", text: options.promptText }]
|
|
19386
|
+
};
|
|
19387
|
+
if (options.variant) {
|
|
19388
|
+
body.variant = options.variant;
|
|
19389
|
+
}
|
|
19390
|
+
await promptWithTimeout(this.client, {
|
|
19391
|
+
path: { id: sessionId },
|
|
19392
|
+
body,
|
|
19393
|
+
query: { directory: this.directory }
|
|
19394
|
+
}, options.timeout);
|
|
19395
|
+
const result = await extractSessionResult(this.client, sessionId, {
|
|
19396
|
+
includeReasoning: options.includeReasoning
|
|
19397
|
+
});
|
|
19398
|
+
return result || "(No output)";
|
|
19399
|
+
} finally {
|
|
19400
|
+
if (sessionId) {
|
|
19401
|
+
this.client.session.abort({ path: { id: sessionId } }).catch(() => {});
|
|
19402
|
+
if (this.depthTracker) {
|
|
19403
|
+
this.depthTracker.cleanup(sessionId);
|
|
19404
|
+
}
|
|
19405
|
+
}
|
|
19406
|
+
}
|
|
19407
|
+
}
|
|
19408
|
+
async runCouncillors(prompt, councillors, parentSessionId, timeout) {
|
|
19409
|
+
const entries = Object.entries(councillors);
|
|
19410
|
+
const promises = entries.map(([name, config2], index) => (async () => {
|
|
19411
|
+
if (index > 0) {
|
|
19412
|
+
await new Promise((r) => setTimeout(r, index * COUNCILLOR_STAGGER_MS));
|
|
19413
|
+
}
|
|
19414
|
+
const modelLabel = shortModelLabel(config2.model);
|
|
19415
|
+
try {
|
|
19416
|
+
const result = await this.runAgentSession({
|
|
19417
|
+
parentSessionId,
|
|
19418
|
+
title: `Council ${name} (${modelLabel})`,
|
|
19419
|
+
agent: "councillor",
|
|
19420
|
+
model: config2.model,
|
|
19421
|
+
promptText: formatCouncillorPrompt(prompt, config2.prompt),
|
|
19422
|
+
variant: config2.variant,
|
|
19423
|
+
timeout,
|
|
19424
|
+
includeReasoning: false
|
|
19425
|
+
});
|
|
19426
|
+
return {
|
|
19427
|
+
name,
|
|
19428
|
+
model: config2.model,
|
|
19429
|
+
status: "completed",
|
|
19430
|
+
result
|
|
19431
|
+
};
|
|
19432
|
+
} catch (error48) {
|
|
19433
|
+
const msg = error48 instanceof Error ? error48.message : String(error48);
|
|
19434
|
+
return {
|
|
19435
|
+
name,
|
|
19436
|
+
model: config2.model,
|
|
19437
|
+
status: msg.includes("timed out") ? "timed_out" : "failed",
|
|
19438
|
+
error: `Councillor "${name}": ${msg}`
|
|
19439
|
+
};
|
|
19440
|
+
}
|
|
19441
|
+
})());
|
|
19442
|
+
const settled = await Promise.allSettled(promises);
|
|
19443
|
+
return settled.map((result, index) => {
|
|
19444
|
+
const [name, cfg] = entries[index];
|
|
19445
|
+
if (result.status === "fulfilled") {
|
|
19446
|
+
return {
|
|
19447
|
+
name,
|
|
19448
|
+
model: cfg.model,
|
|
19449
|
+
status: result.value.status,
|
|
19450
|
+
result: result.value.result,
|
|
19451
|
+
error: result.value.error
|
|
19452
|
+
};
|
|
19453
|
+
}
|
|
19454
|
+
return {
|
|
19455
|
+
name,
|
|
19456
|
+
model: cfg.model,
|
|
19457
|
+
status: "failed",
|
|
19458
|
+
error: result.reason instanceof Error ? result.reason.message : String(result.reason)
|
|
19459
|
+
};
|
|
18616
19460
|
});
|
|
18617
|
-
await closeTmuxPane(tracked.paneId);
|
|
18618
|
-
this.sessions.delete(sessionId);
|
|
18619
|
-
if (this.sessions.size === 0) {
|
|
18620
|
-
this.stopPolling();
|
|
18621
|
-
}
|
|
18622
19461
|
}
|
|
18623
|
-
async
|
|
18624
|
-
|
|
18625
|
-
|
|
18626
|
-
|
|
18627
|
-
|
|
18628
|
-
|
|
18629
|
-
|
|
18630
|
-
|
|
18631
|
-
|
|
18632
|
-
|
|
18633
|
-
|
|
18634
|
-
|
|
19462
|
+
async runMaster(prompt, councillorResults, councilConfig, parentSessionId, timeout, presetMasterOverride) {
|
|
19463
|
+
const masterConfig = councilConfig.master;
|
|
19464
|
+
const fallbackModels = councilConfig.master_fallback ?? [];
|
|
19465
|
+
const effectiveModel = presetMasterOverride?.model ?? masterConfig.model;
|
|
19466
|
+
const effectiveVariant = presetMasterOverride?.variant ?? masterConfig.variant;
|
|
19467
|
+
const effectivePrompt = presetMasterOverride?.prompt ?? masterConfig.prompt;
|
|
19468
|
+
const attemptModels = [effectiveModel, ...fallbackModels];
|
|
19469
|
+
const synthesisPrompt = formatMasterSynthesisPrompt(prompt, councillorResults, effectivePrompt);
|
|
19470
|
+
const errors3 = [];
|
|
19471
|
+
for (let i = 0;i < attemptModels.length; i++) {
|
|
19472
|
+
const model = attemptModels[i];
|
|
19473
|
+
const currentLabel = shortModelLabel(model);
|
|
19474
|
+
try {
|
|
19475
|
+
if (i > 0) {
|
|
19476
|
+
log(`[council-manager] master fallback ${i}/${attemptModels.length - 1}: ${currentLabel}`);
|
|
19477
|
+
}
|
|
19478
|
+
const result = await this.runAgentSession({
|
|
19479
|
+
parentSessionId,
|
|
19480
|
+
title: `Council Master (${currentLabel})`,
|
|
19481
|
+
agent: "council-master",
|
|
19482
|
+
model,
|
|
19483
|
+
promptText: synthesisPrompt,
|
|
19484
|
+
variant: effectiveVariant,
|
|
19485
|
+
timeout
|
|
19486
|
+
});
|
|
19487
|
+
return { success: true, result };
|
|
19488
|
+
} catch (error48) {
|
|
19489
|
+
const msg = error48 instanceof Error ? error48.message : String(error48);
|
|
19490
|
+
errors3.push(`${currentLabel}: ${msg}`);
|
|
19491
|
+
log(`[council-manager] master model failed: ${currentLabel} \u2014 ${msg}`);
|
|
19492
|
+
}
|
|
18635
19493
|
}
|
|
18636
|
-
|
|
19494
|
+
return {
|
|
19495
|
+
success: false,
|
|
19496
|
+
error: `All master models failed. ${errors3.join(" | ")}`
|
|
19497
|
+
};
|
|
18637
19498
|
}
|
|
18638
19499
|
}
|
|
18639
19500
|
// src/hooks/auto-update-checker/cache.ts
|
|
18640
19501
|
import * as fs3 from "fs";
|
|
18641
19502
|
import * as path4 from "path";
|
|
18642
19503
|
// src/hooks/auto-update-checker/constants.ts
|
|
18643
|
-
import * as
|
|
19504
|
+
import * as os2 from "os";
|
|
18644
19505
|
import * as path3 from "path";
|
|
18645
19506
|
var PACKAGE_NAME = "oh-my-opencode-slim";
|
|
18646
19507
|
var NPM_REGISTRY_URL = `https://registry.npmjs.org/-/package/${PACKAGE_NAME}/dist-tags`;
|
|
18647
19508
|
var NPM_FETCH_TIMEOUT = 5000;
|
|
18648
19509
|
function getCacheDir() {
|
|
18649
19510
|
if (process.platform === "win32") {
|
|
18650
|
-
return path3.join(process.env.LOCALAPPDATA ??
|
|
19511
|
+
return path3.join(process.env.LOCALAPPDATA ?? os2.homedir(), "opencode");
|
|
18651
19512
|
}
|
|
18652
|
-
return path3.join(
|
|
19513
|
+
return path3.join(os2.homedir(), ".cache", "opencode");
|
|
18653
19514
|
}
|
|
18654
19515
|
var CACHE_DIR = getCacheDir();
|
|
18655
19516
|
var INSTALLED_PACKAGE_JSON = path3.join(CACHE_DIR, "node_modules", PACKAGE_NAME, "package.json");
|
|
@@ -19186,12 +20047,224 @@ ${buildRetryGuidance(detected)}`;
|
|
|
19186
20047
|
}
|
|
19187
20048
|
};
|
|
19188
20049
|
}
|
|
20050
|
+
// src/hooks/foreground-fallback/index.ts
|
|
20051
|
+
var RATE_LIMIT_PATTERNS = [
|
|
20052
|
+
/\b429\b/,
|
|
20053
|
+
/rate.?limit/i,
|
|
20054
|
+
/too many requests/i,
|
|
20055
|
+
/quota.?exceeded/i,
|
|
20056
|
+
/usage.?exceeded/i,
|
|
20057
|
+
/usage limit/i,
|
|
20058
|
+
/overloaded/i,
|
|
20059
|
+
/resource.?exhausted/i,
|
|
20060
|
+
/insufficient.?quota/i,
|
|
20061
|
+
/high concurrency/i,
|
|
20062
|
+
/reduce concurrency/i
|
|
20063
|
+
];
|
|
20064
|
+
function isRateLimitError(error48) {
|
|
20065
|
+
if (!error48 || typeof error48 !== "object")
|
|
20066
|
+
return false;
|
|
20067
|
+
const err = error48;
|
|
20068
|
+
const text = [
|
|
20069
|
+
err.message ?? "",
|
|
20070
|
+
String(err.data?.statusCode ?? ""),
|
|
20071
|
+
err.data?.message ?? "",
|
|
20072
|
+
err.data?.responseBody ?? ""
|
|
20073
|
+
].join(" ");
|
|
20074
|
+
return RATE_LIMIT_PATTERNS.some((p) => p.test(text));
|
|
20075
|
+
}
|
|
20076
|
+
function parseModel(model) {
|
|
20077
|
+
const slash = model.indexOf("/");
|
|
20078
|
+
if (slash <= 0 || slash >= model.length - 1)
|
|
20079
|
+
return null;
|
|
20080
|
+
return { providerID: model.slice(0, slash), modelID: model.slice(slash + 1) };
|
|
20081
|
+
}
|
|
20082
|
+
var DEDUP_WINDOW_MS = 5000;
|
|
20083
|
+
|
|
20084
|
+
class ForegroundFallbackManager {
|
|
20085
|
+
client;
|
|
20086
|
+
chains;
|
|
20087
|
+
enabled;
|
|
20088
|
+
sessionModel = new Map;
|
|
20089
|
+
sessionAgent = new Map;
|
|
20090
|
+
sessionTried = new Map;
|
|
20091
|
+
inProgress = new Set;
|
|
20092
|
+
lastTrigger = new Map;
|
|
20093
|
+
constructor(client, chains, enabled) {
|
|
20094
|
+
this.client = client;
|
|
20095
|
+
this.chains = chains;
|
|
20096
|
+
this.enabled = enabled;
|
|
20097
|
+
}
|
|
20098
|
+
async handleEvent(rawEvent) {
|
|
20099
|
+
if (!this.enabled)
|
|
20100
|
+
return;
|
|
20101
|
+
const event = rawEvent;
|
|
20102
|
+
if (!event?.type)
|
|
20103
|
+
return;
|
|
20104
|
+
switch (event.type) {
|
|
20105
|
+
case "message.updated": {
|
|
20106
|
+
const info = event.properties?.info;
|
|
20107
|
+
if (!info)
|
|
20108
|
+
break;
|
|
20109
|
+
const sessionID = info.sessionID;
|
|
20110
|
+
if (!sessionID)
|
|
20111
|
+
break;
|
|
20112
|
+
if (typeof info.agent === "string") {
|
|
20113
|
+
this.sessionAgent.set(sessionID, info.agent);
|
|
20114
|
+
}
|
|
20115
|
+
if (typeof info.providerID === "string" && typeof info.modelID === "string") {
|
|
20116
|
+
this.sessionModel.set(sessionID, `${info.providerID}/${info.modelID}`);
|
|
20117
|
+
}
|
|
20118
|
+
if (info.error && isRateLimitError(info.error)) {
|
|
20119
|
+
await this.tryFallback(sessionID);
|
|
20120
|
+
}
|
|
20121
|
+
break;
|
|
20122
|
+
}
|
|
20123
|
+
case "session.error": {
|
|
20124
|
+
const props = event.properties;
|
|
20125
|
+
if (props?.sessionID && props.error && isRateLimitError(props.error)) {
|
|
20126
|
+
await this.tryFallback(props.sessionID);
|
|
20127
|
+
}
|
|
20128
|
+
break;
|
|
20129
|
+
}
|
|
20130
|
+
case "session.status": {
|
|
20131
|
+
const props = event.properties;
|
|
20132
|
+
if (!props?.sessionID || props.status?.type !== "retry")
|
|
20133
|
+
break;
|
|
20134
|
+
const msg = props.status.message?.toLowerCase() ?? "";
|
|
20135
|
+
if (msg.includes("rate limit") || msg.includes("usage limit") || msg.includes("usage exceeded") || msg.includes("quota exceeded") || msg.includes("high concurrency") || msg.includes("reduce concurrency")) {
|
|
20136
|
+
await this.tryFallback(props.sessionID);
|
|
20137
|
+
}
|
|
20138
|
+
break;
|
|
20139
|
+
}
|
|
20140
|
+
case "subagent.session.created": {
|
|
20141
|
+
const props = event.properties;
|
|
20142
|
+
if (props?.sessionID && typeof props.agentName === "string") {
|
|
20143
|
+
this.sessionAgent.set(props.sessionID, props.agentName);
|
|
20144
|
+
}
|
|
20145
|
+
break;
|
|
20146
|
+
}
|
|
20147
|
+
case "session.deleted": {
|
|
20148
|
+
const props = event.properties;
|
|
20149
|
+
const id = props?.info?.id ?? props?.sessionID;
|
|
20150
|
+
if (id) {
|
|
20151
|
+
this.sessionModel.delete(id);
|
|
20152
|
+
this.sessionAgent.delete(id);
|
|
20153
|
+
this.sessionTried.delete(id);
|
|
20154
|
+
this.inProgress.delete(id);
|
|
20155
|
+
this.lastTrigger.delete(id);
|
|
20156
|
+
}
|
|
20157
|
+
break;
|
|
20158
|
+
}
|
|
20159
|
+
}
|
|
20160
|
+
}
|
|
20161
|
+
async tryFallback(sessionID) {
|
|
20162
|
+
if (!sessionID)
|
|
20163
|
+
return;
|
|
20164
|
+
if (this.inProgress.has(sessionID))
|
|
20165
|
+
return;
|
|
20166
|
+
const now = Date.now();
|
|
20167
|
+
if (now - (this.lastTrigger.get(sessionID) ?? 0) < DEDUP_WINDOW_MS)
|
|
20168
|
+
return;
|
|
20169
|
+
this.lastTrigger.set(sessionID, now);
|
|
20170
|
+
this.inProgress.add(sessionID);
|
|
20171
|
+
try {
|
|
20172
|
+
const currentModel = this.sessionModel.get(sessionID);
|
|
20173
|
+
const agentName = this.sessionAgent.get(sessionID);
|
|
20174
|
+
const chain = this.resolveChain(agentName, currentModel);
|
|
20175
|
+
if (!chain.length) {
|
|
20176
|
+
log("[foreground-fallback] no chain configured", {
|
|
20177
|
+
sessionID,
|
|
20178
|
+
agentName
|
|
20179
|
+
});
|
|
20180
|
+
return;
|
|
20181
|
+
}
|
|
20182
|
+
if (!this.sessionTried.has(sessionID)) {
|
|
20183
|
+
this.sessionTried.set(sessionID, new Set);
|
|
20184
|
+
}
|
|
20185
|
+
const tried = this.sessionTried.get(sessionID);
|
|
20186
|
+
if (currentModel)
|
|
20187
|
+
tried.add(currentModel);
|
|
20188
|
+
const nextModel = chain.find((m) => !tried.has(m));
|
|
20189
|
+
if (!nextModel) {
|
|
20190
|
+
log("[foreground-fallback] fallback chain exhausted", {
|
|
20191
|
+
sessionID,
|
|
20192
|
+
agentName,
|
|
20193
|
+
tried: [...tried]
|
|
20194
|
+
});
|
|
20195
|
+
return;
|
|
20196
|
+
}
|
|
20197
|
+
tried.add(nextModel);
|
|
20198
|
+
const ref = parseModel(nextModel);
|
|
20199
|
+
if (!ref) {
|
|
20200
|
+
log("[foreground-fallback] invalid model format", {
|
|
20201
|
+
sessionID,
|
|
20202
|
+
nextModel
|
|
20203
|
+
});
|
|
20204
|
+
return;
|
|
20205
|
+
}
|
|
20206
|
+
const result = await this.client.session.messages({
|
|
20207
|
+
path: { id: sessionID }
|
|
20208
|
+
});
|
|
20209
|
+
const messages = result.data ?? [];
|
|
20210
|
+
const lastUser = [...messages].reverse().find((m) => m.info.role === "user");
|
|
20211
|
+
if (!lastUser) {
|
|
20212
|
+
log("[foreground-fallback] no user message found", { sessionID });
|
|
20213
|
+
return;
|
|
20214
|
+
}
|
|
20215
|
+
try {
|
|
20216
|
+
await this.client.session.abort({ path: { id: sessionID } });
|
|
20217
|
+
} catch {}
|
|
20218
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
20219
|
+
const sessionClient = this.client.session;
|
|
20220
|
+
await sessionClient.promptAsync({
|
|
20221
|
+
path: { id: sessionID },
|
|
20222
|
+
body: { parts: lastUser.parts, model: ref }
|
|
20223
|
+
});
|
|
20224
|
+
this.sessionModel.set(sessionID, nextModel);
|
|
20225
|
+
log("[foreground-fallback] switched to fallback model", {
|
|
20226
|
+
sessionID,
|
|
20227
|
+
agentName,
|
|
20228
|
+
from: currentModel,
|
|
20229
|
+
to: nextModel
|
|
20230
|
+
});
|
|
20231
|
+
} catch (err) {
|
|
20232
|
+
log("[foreground-fallback] fallback attempt failed", {
|
|
20233
|
+
sessionID,
|
|
20234
|
+
error: err instanceof Error ? err.message : String(err)
|
|
20235
|
+
});
|
|
20236
|
+
} finally {
|
|
20237
|
+
this.inProgress.delete(sessionID);
|
|
20238
|
+
}
|
|
20239
|
+
}
|
|
20240
|
+
resolveChain(agentName, currentModel) {
|
|
20241
|
+
if (agentName) {
|
|
20242
|
+
return this.chains[agentName] ?? [];
|
|
20243
|
+
}
|
|
20244
|
+
if (currentModel) {
|
|
20245
|
+
for (const chain of Object.values(this.chains)) {
|
|
20246
|
+
if (chain.includes(currentModel))
|
|
20247
|
+
return chain;
|
|
20248
|
+
}
|
|
20249
|
+
}
|
|
20250
|
+
const all = [];
|
|
20251
|
+
const seen = new Set;
|
|
20252
|
+
for (const chain of Object.values(this.chains)) {
|
|
20253
|
+
for (const m of chain) {
|
|
20254
|
+
if (!seen.has(m)) {
|
|
20255
|
+
seen.add(m);
|
|
20256
|
+
all.push(m);
|
|
20257
|
+
}
|
|
20258
|
+
}
|
|
20259
|
+
}
|
|
20260
|
+
return all;
|
|
20261
|
+
}
|
|
20262
|
+
}
|
|
19189
20263
|
// src/hooks/json-error-recovery/hook.ts
|
|
19190
20264
|
var JSON_ERROR_TOOL_EXCLUDE_LIST = [
|
|
19191
20265
|
"bash",
|
|
19192
20266
|
"read",
|
|
19193
20267
|
"glob",
|
|
19194
|
-
"grep",
|
|
19195
20268
|
"webfetch",
|
|
19196
20269
|
"grep_app_searchgithub",
|
|
19197
20270
|
"websearch_web_search_exa"
|
|
@@ -19239,9 +20312,7 @@ ${JSON_ERROR_REMINDER}`;
|
|
|
19239
20312
|
};
|
|
19240
20313
|
}
|
|
19241
20314
|
// src/hooks/phase-reminder/index.ts
|
|
19242
|
-
var PHASE_REMINDER = `<reminder
|
|
19243
|
-
Understand \u2192 find the best path (delegate based on rules and parallelize independent work) \u2192 execute \u2192 verify.
|
|
19244
|
-
If delegating, launch the specialist in the same turn you mention it.</reminder>`;
|
|
20315
|
+
var PHASE_REMINDER = `<reminder>${PHASE_REMINDER_TEXT}</reminder>`;
|
|
19245
20316
|
function createPhaseReminderHook() {
|
|
19246
20317
|
return {
|
|
19247
20318
|
"experimental.chat.messages.transform": async (_input, output) => {
|
|
@@ -19280,15 +20351,15 @@ ${originalText}`;
|
|
|
19280
20351
|
}
|
|
19281
20352
|
};
|
|
19282
20353
|
}
|
|
19283
|
-
// src/hooks/post-
|
|
20354
|
+
// src/hooks/post-file-tool-nudge/index.ts
|
|
19284
20355
|
var NUDGE = `
|
|
19285
20356
|
|
|
19286
20357
|
---
|
|
19287
|
-
|
|
19288
|
-
function
|
|
20358
|
+
${PHASE_REMINDER_TEXT}`;
|
|
20359
|
+
function createPostFileToolNudgeHook() {
|
|
19289
20360
|
return {
|
|
19290
20361
|
"tool.execute.after": async (input, output) => {
|
|
19291
|
-
if (input.tool !== "Read" && input.tool !== "read") {
|
|
20362
|
+
if (input.tool !== "Read" && input.tool !== "read" && input.tool !== "Write" && input.tool !== "write") {
|
|
19292
20363
|
return;
|
|
19293
20364
|
}
|
|
19294
20365
|
output.output = output.output + NUDGE;
|
|
@@ -31656,12 +32727,12 @@ var {spawn: spawn3 } = globalThis.Bun;
|
|
|
31656
32727
|
// src/tools/ast-grep/constants.ts
|
|
31657
32728
|
import { existsSync as existsSync5, statSync as statSync2 } from "fs";
|
|
31658
32729
|
import { createRequire as createRequire2 } from "module";
|
|
31659
|
-
import { dirname as
|
|
32730
|
+
import { dirname as dirname3, join as join8 } from "path";
|
|
31660
32731
|
|
|
31661
32732
|
// src/tools/ast-grep/downloader.ts
|
|
31662
32733
|
import { chmodSync, existsSync as existsSync4, mkdirSync, unlinkSync } from "fs";
|
|
31663
32734
|
import { createRequire } from "module";
|
|
31664
|
-
import { homedir as
|
|
32735
|
+
import { homedir as homedir3 } from "os";
|
|
31665
32736
|
import { join as join7 } from "path";
|
|
31666
32737
|
var REPO = "ast-grep/ast-grep";
|
|
31667
32738
|
var DEFAULT_VERSION = "0.40.0";
|
|
@@ -31686,11 +32757,11 @@ var PLATFORM_MAP = {
|
|
|
31686
32757
|
function getCacheDir2() {
|
|
31687
32758
|
if (process.platform === "win32") {
|
|
31688
32759
|
const localAppData = process.env.LOCALAPPDATA || process.env.APPDATA;
|
|
31689
|
-
const base2 = localAppData || join7(
|
|
32760
|
+
const base2 = localAppData || join7(homedir3(), "AppData", "Local");
|
|
31690
32761
|
return join7(base2, "oh-my-opencode-slim", "bin");
|
|
31691
32762
|
}
|
|
31692
32763
|
const xdgCache = process.env.XDG_CACHE_HOME;
|
|
31693
|
-
const base = xdgCache || join7(
|
|
32764
|
+
const base = xdgCache || join7(homedir3(), ".cache");
|
|
31694
32765
|
return join7(base, "oh-my-opencode-slim", "bin");
|
|
31695
32766
|
}
|
|
31696
32767
|
function getBinaryName() {
|
|
@@ -31713,8 +32784,8 @@ async function downloadAstGrep(version3 = DEFAULT_VERSION) {
|
|
|
31713
32784
|
if (existsSync4(binaryPath)) {
|
|
31714
32785
|
return binaryPath;
|
|
31715
32786
|
}
|
|
31716
|
-
const { arch, os:
|
|
31717
|
-
const assetName = `app-${arch}-${
|
|
32787
|
+
const { arch, os: os3 } = platformInfo;
|
|
32788
|
+
const assetName = `app-${arch}-${os3}.zip`;
|
|
31718
32789
|
const downloadUrl = `https://github.com/${REPO}/releases/download/${version3}/${assetName}`;
|
|
31719
32790
|
console.log(`[oh-my-opencode-slim] Downloading ast-grep binary...`);
|
|
31720
32791
|
try {
|
|
@@ -31813,7 +32884,7 @@ function findSgCliPathSync() {
|
|
|
31813
32884
|
try {
|
|
31814
32885
|
const require2 = createRequire2(import.meta.url);
|
|
31815
32886
|
const cliPkgPath = require2.resolve("@ast-grep/cli/package.json");
|
|
31816
|
-
const cliDir =
|
|
32887
|
+
const cliDir = dirname3(cliPkgPath);
|
|
31817
32888
|
const sgPath = join8(cliDir, binaryName);
|
|
31818
32889
|
if (existsSync5(sgPath) && isValidBinary(sgPath)) {
|
|
31819
32890
|
return sgPath;
|
|
@@ -31824,7 +32895,7 @@ function findSgCliPathSync() {
|
|
|
31824
32895
|
try {
|
|
31825
32896
|
const require2 = createRequire2(import.meta.url);
|
|
31826
32897
|
const pkgPath = require2.resolve(`${platformPkg}/package.json`);
|
|
31827
|
-
const pkgDir =
|
|
32898
|
+
const pkgDir = dirname3(pkgPath);
|
|
31828
32899
|
const astGrepName = process.platform === "win32" ? "ast-grep.exe" : "ast-grep";
|
|
31829
32900
|
const binaryPath = join8(pkgDir, astGrepName);
|
|
31830
32901
|
if (existsSync5(binaryPath) && isValidBinary(binaryPath)) {
|
|
@@ -32269,380 +33340,146 @@ Returns: results if completed, error if failed, status if running.`,
|
|
|
32269
33340
|
Duration: ${duration5}
|
|
32270
33341
|
|
|
32271
33342
|
---
|
|
32272
|
-
|
|
32273
|
-
`;
|
|
32274
|
-
if (task.status === "completed" && task.result != null) {
|
|
32275
|
-
output += task.result;
|
|
32276
|
-
} else if (task.status === "failed") {
|
|
32277
|
-
output += `Error: ${task.error}`;
|
|
32278
|
-
} else if (task.status === "cancelled") {
|
|
32279
|
-
output += "(Task cancelled)";
|
|
32280
|
-
} else {
|
|
32281
|
-
output += "(Task still running)";
|
|
32282
|
-
}
|
|
32283
|
-
return output;
|
|
32284
|
-
}
|
|
32285
|
-
});
|
|
32286
|
-
const background_cancel = tool({
|
|
32287
|
-
description: `Cancel background task(s).
|
|
32288
|
-
|
|
32289
|
-
task_id: cancel specific task
|
|
32290
|
-
all=true: cancel all running tasks
|
|
32291
|
-
|
|
32292
|
-
Only cancels pending/starting/running tasks.`,
|
|
32293
|
-
args: {
|
|
32294
|
-
task_id: z2.string().optional().describe("Specific task to cancel"),
|
|
32295
|
-
all: z2.boolean().optional().describe("Cancel all running tasks")
|
|
32296
|
-
},
|
|
32297
|
-
async execute(args) {
|
|
32298
|
-
if (args.all === true) {
|
|
32299
|
-
const count = manager.cancel();
|
|
32300
|
-
return `Cancelled ${count} task(s).`;
|
|
32301
|
-
}
|
|
32302
|
-
if (typeof args.task_id === "string") {
|
|
32303
|
-
const count = manager.cancel(args.task_id);
|
|
32304
|
-
return count > 0 ? `Cancelled task ${args.task_id}.` : `Task ${args.task_id} not found or not running.`;
|
|
32305
|
-
}
|
|
32306
|
-
return "Specify task_id or use all=true.";
|
|
32307
|
-
}
|
|
32308
|
-
});
|
|
32309
|
-
return { background_task, background_output, background_cancel };
|
|
32310
|
-
}
|
|
32311
|
-
// src/tools/grep/cli.ts
|
|
32312
|
-
var {spawn: spawn4 } = globalThis.Bun;
|
|
32313
|
-
|
|
32314
|
-
// src/tools/grep/constants.ts
|
|
32315
|
-
import { spawnSync as spawnSync2 } from "child_process";
|
|
32316
|
-
import { existsSync as existsSync8 } from "fs";
|
|
32317
|
-
import { dirname as dirname3, join as join10 } from "path";
|
|
32318
|
-
|
|
32319
|
-
// src/tools/grep/downloader.ts
|
|
32320
|
-
import {
|
|
32321
|
-
chmodSync as chmodSync2,
|
|
32322
|
-
existsSync as existsSync7,
|
|
32323
|
-
mkdirSync as mkdirSync2,
|
|
32324
|
-
readdirSync,
|
|
32325
|
-
unlinkSync as unlinkSync2
|
|
32326
|
-
} from "fs";
|
|
32327
|
-
import { join as join9 } from "path";
|
|
32328
|
-
function getInstallDir() {
|
|
32329
|
-
const homeDir = process.env.HOME || process.env.USERPROFILE || ".";
|
|
32330
|
-
return join9(homeDir, ".cache", "oh-my-opencode-slim", "bin");
|
|
32331
|
-
}
|
|
32332
|
-
function getRgPath() {
|
|
32333
|
-
const isWindows = process.platform === "win32";
|
|
32334
|
-
return join9(getInstallDir(), isWindows ? "rg.exe" : "rg");
|
|
32335
|
-
}
|
|
32336
|
-
function getInstalledRipgrepPath() {
|
|
32337
|
-
const rgPath = getRgPath();
|
|
32338
|
-
return existsSync7(rgPath) ? rgPath : null;
|
|
32339
|
-
}
|
|
32340
|
-
|
|
32341
|
-
// src/tools/grep/constants.ts
|
|
32342
|
-
var cachedCli = null;
|
|
32343
|
-
function findExecutable(name) {
|
|
32344
|
-
const isWindows = process.platform === "win32";
|
|
32345
|
-
const cmd = isWindows ? "where" : "which";
|
|
32346
|
-
try {
|
|
32347
|
-
const result = spawnSync2(cmd, [name], { encoding: "utf-8", timeout: 5000 });
|
|
32348
|
-
if (result.status === 0 && result.stdout.trim()) {
|
|
32349
|
-
return result.stdout.trim().split(/\r?\n/)[0];
|
|
32350
|
-
}
|
|
32351
|
-
} catch {}
|
|
32352
|
-
return null;
|
|
32353
|
-
}
|
|
32354
|
-
function getDataDir() {
|
|
32355
|
-
if (process.platform === "win32") {
|
|
32356
|
-
return process.env.LOCALAPPDATA || process.env.APPDATA || join10(process.env.USERPROFILE || ".", "AppData", "Local");
|
|
32357
|
-
}
|
|
32358
|
-
return process.env.XDG_DATA_HOME || join10(process.env.HOME || ".", ".local", "share");
|
|
32359
|
-
}
|
|
32360
|
-
function getOpenCodeBundledRg() {
|
|
32361
|
-
const execPath = process.execPath;
|
|
32362
|
-
const execDir = dirname3(execPath);
|
|
32363
|
-
const isWindows = process.platform === "win32";
|
|
32364
|
-
const rgName = isWindows ? "rg.exe" : "rg";
|
|
32365
|
-
const candidates = [
|
|
32366
|
-
join10(getDataDir(), "opencode", "bin", rgName),
|
|
32367
|
-
join10(execDir, rgName),
|
|
32368
|
-
join10(execDir, "bin", rgName),
|
|
32369
|
-
join10(execDir, "..", "bin", rgName),
|
|
32370
|
-
join10(execDir, "..", "libexec", rgName)
|
|
32371
|
-
];
|
|
32372
|
-
for (const candidate of candidates) {
|
|
32373
|
-
if (existsSync8(candidate)) {
|
|
32374
|
-
return candidate;
|
|
32375
|
-
}
|
|
32376
|
-
}
|
|
32377
|
-
return null;
|
|
32378
|
-
}
|
|
32379
|
-
function resolveGrepCli() {
|
|
32380
|
-
if (cachedCli)
|
|
32381
|
-
return cachedCli;
|
|
32382
|
-
const bundledRg = getOpenCodeBundledRg();
|
|
32383
|
-
if (bundledRg) {
|
|
32384
|
-
cachedCli = { path: bundledRg, backend: "rg" };
|
|
32385
|
-
return cachedCli;
|
|
32386
|
-
}
|
|
32387
|
-
const systemRg = findExecutable("rg");
|
|
32388
|
-
if (systemRg) {
|
|
32389
|
-
cachedCli = { path: systemRg, backend: "rg" };
|
|
32390
|
-
return cachedCli;
|
|
32391
|
-
}
|
|
32392
|
-
const installedRg = getInstalledRipgrepPath();
|
|
32393
|
-
if (installedRg) {
|
|
32394
|
-
cachedCli = { path: installedRg, backend: "rg" };
|
|
32395
|
-
return cachedCli;
|
|
32396
|
-
}
|
|
32397
|
-
const grep = findExecutable("grep");
|
|
32398
|
-
if (grep) {
|
|
32399
|
-
cachedCli = { path: grep, backend: "grep" };
|
|
32400
|
-
return cachedCli;
|
|
32401
|
-
}
|
|
32402
|
-
cachedCli = { path: "rg", backend: "rg" };
|
|
32403
|
-
return cachedCli;
|
|
32404
|
-
}
|
|
32405
|
-
var DEFAULT_MAX_DEPTH = 20;
|
|
32406
|
-
var DEFAULT_MAX_FILESIZE = "10M";
|
|
32407
|
-
var DEFAULT_MAX_COUNT = 500;
|
|
32408
|
-
var DEFAULT_MAX_COLUMNS = 1000;
|
|
32409
|
-
var DEFAULT_TIMEOUT_MS3 = 300000;
|
|
32410
|
-
var DEFAULT_MAX_OUTPUT_BYTES2 = 10 * 1024 * 1024;
|
|
32411
|
-
var RG_SAFETY_FLAGS = [
|
|
32412
|
-
"--no-follow",
|
|
32413
|
-
"--color=never",
|
|
32414
|
-
"--no-heading",
|
|
32415
|
-
"--line-number",
|
|
32416
|
-
"--with-filename"
|
|
32417
|
-
];
|
|
32418
|
-
var GREP_SAFETY_FLAGS = ["-n", "-H", "--color=never"];
|
|
32419
|
-
|
|
32420
|
-
// src/tools/grep/cli.ts
|
|
32421
|
-
function buildRgArgs(options) {
|
|
32422
|
-
const args = [
|
|
32423
|
-
...RG_SAFETY_FLAGS,
|
|
32424
|
-
`--max-depth=${Math.min(options.maxDepth ?? DEFAULT_MAX_DEPTH, DEFAULT_MAX_DEPTH)}`,
|
|
32425
|
-
`--max-filesize=${options.maxFilesize ?? DEFAULT_MAX_FILESIZE}`,
|
|
32426
|
-
`--max-count=${Math.min(options.maxCount ?? DEFAULT_MAX_COUNT, DEFAULT_MAX_COUNT)}`,
|
|
32427
|
-
`--max-columns=${Math.min(options.maxColumns ?? DEFAULT_MAX_COLUMNS, DEFAULT_MAX_COLUMNS)}`
|
|
32428
|
-
];
|
|
32429
|
-
if (options.context !== undefined && options.context > 0) {
|
|
32430
|
-
args.push(`-C${Math.min(options.context, 10)}`);
|
|
32431
|
-
}
|
|
32432
|
-
if (options.caseSensitive) {
|
|
32433
|
-
args.push("--case-sensitive");
|
|
32434
|
-
} else {
|
|
32435
|
-
args.push("-i");
|
|
32436
|
-
}
|
|
32437
|
-
if (options.wholeWord)
|
|
32438
|
-
args.push("-w");
|
|
32439
|
-
if (options.fixedStrings)
|
|
32440
|
-
args.push("-F");
|
|
32441
|
-
if (options.multiline)
|
|
32442
|
-
args.push("-U");
|
|
32443
|
-
if (options.hidden)
|
|
32444
|
-
args.push("--hidden");
|
|
32445
|
-
if (options.noIgnore)
|
|
32446
|
-
args.push("--no-ignore");
|
|
32447
|
-
if (options.fileType?.length) {
|
|
32448
|
-
for (const type of options.fileType) {
|
|
32449
|
-
args.push(`--type=${type}`);
|
|
32450
|
-
}
|
|
32451
|
-
}
|
|
32452
|
-
if (options.globs) {
|
|
32453
|
-
for (const glob of options.globs) {
|
|
32454
|
-
args.push(`--glob=${glob}`);
|
|
32455
|
-
}
|
|
32456
|
-
}
|
|
32457
|
-
if (options.excludeGlobs) {
|
|
32458
|
-
for (const glob of options.excludeGlobs) {
|
|
32459
|
-
args.push(`--glob=!${glob}`);
|
|
32460
|
-
}
|
|
32461
|
-
}
|
|
32462
|
-
return args;
|
|
32463
|
-
}
|
|
32464
|
-
function buildGrepArgs(options) {
|
|
32465
|
-
const args = [...GREP_SAFETY_FLAGS, "-r"];
|
|
32466
|
-
if (options.context !== undefined && options.context > 0) {
|
|
32467
|
-
args.push(`-C${Math.min(options.context, 10)}`);
|
|
32468
|
-
}
|
|
32469
|
-
if (!options.caseSensitive)
|
|
32470
|
-
args.push("-i");
|
|
32471
|
-
if (options.wholeWord)
|
|
32472
|
-
args.push("-w");
|
|
32473
|
-
if (options.fixedStrings)
|
|
32474
|
-
args.push("-F");
|
|
32475
|
-
if (options.globs?.length) {
|
|
32476
|
-
for (const glob of options.globs) {
|
|
32477
|
-
args.push(`--include=${glob}`);
|
|
32478
|
-
}
|
|
32479
|
-
}
|
|
32480
|
-
if (options.excludeGlobs?.length) {
|
|
32481
|
-
for (const glob of options.excludeGlobs) {
|
|
32482
|
-
args.push(`--exclude=${glob}`);
|
|
32483
|
-
}
|
|
32484
|
-
}
|
|
32485
|
-
args.push("--exclude-dir=.git", "--exclude-dir=node_modules");
|
|
32486
|
-
return args;
|
|
32487
|
-
}
|
|
32488
|
-
function buildArgs(options, backend) {
|
|
32489
|
-
return backend === "rg" ? buildRgArgs(options) : buildGrepArgs(options);
|
|
32490
|
-
}
|
|
32491
|
-
function parseOutput(output) {
|
|
32492
|
-
if (!output.trim())
|
|
32493
|
-
return [];
|
|
32494
|
-
const matches = [];
|
|
32495
|
-
const lines = output.trim().split(/\r?\n/);
|
|
32496
|
-
for (const line of lines) {
|
|
32497
|
-
if (!line.trim())
|
|
32498
|
-
continue;
|
|
32499
|
-
const match = line.match(/^(.+?):(\d+):(.*)$/);
|
|
32500
|
-
if (match) {
|
|
32501
|
-
matches.push({
|
|
32502
|
-
file: match[1],
|
|
32503
|
-
line: parseInt(match[2], 10),
|
|
32504
|
-
text: match[3]
|
|
32505
|
-
});
|
|
32506
|
-
}
|
|
32507
|
-
}
|
|
32508
|
-
return matches;
|
|
32509
|
-
}
|
|
32510
|
-
async function runRg(options) {
|
|
32511
|
-
const cli = resolveGrepCli();
|
|
32512
|
-
const args = buildArgs(options, cli.backend);
|
|
32513
|
-
const timeout = Math.min(options.timeout ?? DEFAULT_TIMEOUT_MS3, DEFAULT_TIMEOUT_MS3);
|
|
32514
|
-
if (cli.backend === "rg") {
|
|
32515
|
-
args.push("--", options.pattern);
|
|
32516
|
-
} else {
|
|
32517
|
-
args.push("-e", options.pattern);
|
|
32518
|
-
}
|
|
32519
|
-
const paths2 = options.paths?.length ? options.paths : ["."];
|
|
32520
|
-
args.push(...paths2);
|
|
32521
|
-
const proc = spawn4([cli.path, ...args], {
|
|
32522
|
-
stdout: "pipe",
|
|
32523
|
-
stderr: "pipe"
|
|
32524
|
-
});
|
|
32525
|
-
const timeoutPromise = new Promise((_, reject) => {
|
|
32526
|
-
const id = setTimeout(() => {
|
|
32527
|
-
proc.kill();
|
|
32528
|
-
reject(new Error(`Search timeout after ${timeout}ms`));
|
|
32529
|
-
}, timeout);
|
|
32530
|
-
proc.exited.then(() => clearTimeout(id));
|
|
32531
|
-
});
|
|
32532
|
-
try {
|
|
32533
|
-
const stdout = await Promise.race([
|
|
32534
|
-
new Response(proc.stdout).text(),
|
|
32535
|
-
timeoutPromise
|
|
32536
|
-
]);
|
|
32537
|
-
const stderr = await new Response(proc.stderr).text();
|
|
32538
|
-
const exitCode = await proc.exited;
|
|
32539
|
-
const truncated = stdout.length >= DEFAULT_MAX_OUTPUT_BYTES2;
|
|
32540
|
-
const outputToProcess = truncated ? stdout.substring(0, DEFAULT_MAX_OUTPUT_BYTES2) : stdout;
|
|
32541
|
-
if (exitCode > 1 && stderr.trim()) {
|
|
32542
|
-
return {
|
|
32543
|
-
matches: [],
|
|
32544
|
-
totalMatches: 0,
|
|
32545
|
-
filesSearched: 0,
|
|
32546
|
-
truncated: false,
|
|
32547
|
-
error: stderr.trim()
|
|
32548
|
-
};
|
|
32549
|
-
}
|
|
32550
|
-
const matches = parseOutput(outputToProcess);
|
|
32551
|
-
const filesSearched = new Set(matches.map((m) => m.file)).size;
|
|
32552
|
-
return {
|
|
32553
|
-
matches,
|
|
32554
|
-
totalMatches: matches.length,
|
|
32555
|
-
filesSearched,
|
|
32556
|
-
truncated
|
|
32557
|
-
};
|
|
32558
|
-
} catch (e) {
|
|
32559
|
-
return {
|
|
32560
|
-
matches: [],
|
|
32561
|
-
totalMatches: 0,
|
|
32562
|
-
filesSearched: 0,
|
|
32563
|
-
truncated: false,
|
|
32564
|
-
error: e instanceof Error ? e.message : String(e)
|
|
32565
|
-
};
|
|
32566
|
-
}
|
|
32567
|
-
}
|
|
32568
|
-
// src/tools/grep/utils.ts
|
|
32569
|
-
function formatGrepResult(result) {
|
|
32570
|
-
if (result.error) {
|
|
32571
|
-
return `Error: ${result.error}`;
|
|
32572
|
-
}
|
|
32573
|
-
if (result.matches.length === 0) {
|
|
32574
|
-
return "No matches found.";
|
|
32575
|
-
}
|
|
32576
|
-
const lines = [];
|
|
32577
|
-
const byFile = new Map;
|
|
32578
|
-
for (const match of result.matches) {
|
|
32579
|
-
const existing = byFile.get(match.file) || [];
|
|
32580
|
-
existing.push({ line: match.line, text: match.text });
|
|
32581
|
-
byFile.set(match.file, existing);
|
|
32582
|
-
}
|
|
32583
|
-
for (const [file3, matches] of byFile) {
|
|
32584
|
-
lines.push(`
|
|
32585
|
-
${file3}:`);
|
|
32586
|
-
for (const match of matches) {
|
|
32587
|
-
lines.push(` ${match.line}: ${match.text}`);
|
|
33343
|
+
|
|
33344
|
+
`;
|
|
33345
|
+
if (task.status === "completed" && task.result != null) {
|
|
33346
|
+
output += task.result;
|
|
33347
|
+
} else if (task.status === "failed") {
|
|
33348
|
+
output += `Error: ${task.error}`;
|
|
33349
|
+
} else if (task.status === "cancelled") {
|
|
33350
|
+
output += "(Task cancelled)";
|
|
33351
|
+
} else {
|
|
33352
|
+
output += "(Task still running)";
|
|
33353
|
+
}
|
|
33354
|
+
return output;
|
|
32588
33355
|
}
|
|
32589
|
-
}
|
|
32590
|
-
const
|
|
32591
|
-
|
|
32592
|
-
|
|
32593
|
-
|
|
32594
|
-
|
|
32595
|
-
|
|
32596
|
-
|
|
32597
|
-
|
|
32598
|
-
|
|
32599
|
-
|
|
33356
|
+
});
|
|
33357
|
+
const background_cancel = tool({
|
|
33358
|
+
description: `Cancel background task(s).
|
|
33359
|
+
|
|
33360
|
+
task_id: cancel specific task
|
|
33361
|
+
all=true: cancel all running tasks
|
|
33362
|
+
|
|
33363
|
+
Only cancels pending/starting/running tasks.`,
|
|
33364
|
+
args: {
|
|
33365
|
+
task_id: z2.string().optional().describe("Specific task to cancel"),
|
|
33366
|
+
all: z2.boolean().optional().describe("Cancel all running tasks")
|
|
33367
|
+
},
|
|
33368
|
+
async execute(args) {
|
|
33369
|
+
if (args.all === true) {
|
|
33370
|
+
const count = manager.cancel();
|
|
33371
|
+
return `Cancelled ${count} task(s).`;
|
|
33372
|
+
}
|
|
33373
|
+
if (typeof args.task_id === "string") {
|
|
33374
|
+
const count = manager.cancel(args.task_id);
|
|
33375
|
+
return count > 0 ? `Cancelled task ${args.task_id}.` : `Task ${args.task_id} not found or not running.`;
|
|
33376
|
+
}
|
|
33377
|
+
return "Specify task_id or use all=true.";
|
|
33378
|
+
}
|
|
33379
|
+
});
|
|
33380
|
+
return { background_task, background_output, background_cancel };
|
|
32600
33381
|
}
|
|
33382
|
+
// src/tools/council.ts
|
|
33383
|
+
var z3 = tool.schema;
|
|
33384
|
+
function formatModelComposition(councillorResults) {
|
|
33385
|
+
return councillorResults.map((cr) => {
|
|
33386
|
+
const shortModel = shortModelLabel(cr.model ?? "");
|
|
33387
|
+
return `${cr.name}: ${shortModel}`;
|
|
33388
|
+
}).join(", ");
|
|
33389
|
+
}
|
|
33390
|
+
function createCouncilTool(_ctx, councilManager) {
|
|
33391
|
+
const council_session = tool({
|
|
33392
|
+
description: `Launch a multi-LLM council session for consensus-based analysis.
|
|
32601
33393
|
|
|
32602
|
-
|
|
32603
|
-
|
|
32604
|
-
|
|
32605
|
-
|
|
32606
|
-
|
|
32607
|
-
|
|
32608
|
-
|
|
32609
|
-
|
|
32610
|
-
|
|
32611
|
-
|
|
32612
|
-
|
|
32613
|
-
|
|
32614
|
-
|
|
32615
|
-
|
|
32616
|
-
|
|
32617
|
-
|
|
32618
|
-
|
|
32619
|
-
|
|
32620
|
-
|
|
32621
|
-
|
|
32622
|
-
|
|
32623
|
-
|
|
32624
|
-
|
|
32625
|
-
|
|
32626
|
-
|
|
32627
|
-
|
|
32628
|
-
|
|
33394
|
+
Sends the prompt to multiple models (councillors) in parallel, then a council master synthesizes the best response.
|
|
33395
|
+
|
|
33396
|
+
Returns the synthesized result with councillor summary.`,
|
|
33397
|
+
args: {
|
|
33398
|
+
prompt: z3.string().describe("The prompt to send to all councillors"),
|
|
33399
|
+
preset: z3.string().optional().describe('Council preset to use (default: "default"). Must match a preset in the council config.')
|
|
33400
|
+
},
|
|
33401
|
+
async execute(args, toolContext) {
|
|
33402
|
+
if (!toolContext || typeof toolContext !== "object" || !("sessionID" in toolContext)) {
|
|
33403
|
+
throw new Error("Invalid toolContext: missing sessionID");
|
|
33404
|
+
}
|
|
33405
|
+
const allowedAgents = ["council", "orchestrator"];
|
|
33406
|
+
const callingAgent = toolContext.agent;
|
|
33407
|
+
if (callingAgent && !allowedAgents.includes(callingAgent)) {
|
|
33408
|
+
throw new Error(`Council sessions can only be invoked by council or orchestrator agents. Current agent: ${callingAgent}`);
|
|
33409
|
+
}
|
|
33410
|
+
const prompt = String(args.prompt);
|
|
33411
|
+
const preset = typeof args.preset === "string" ? args.preset : undefined;
|
|
33412
|
+
const parentSessionId = toolContext.sessionID;
|
|
33413
|
+
const result = await councilManager.runCouncil(prompt, preset, parentSessionId);
|
|
33414
|
+
if (!result.success) {
|
|
33415
|
+
if (result.result) {
|
|
33416
|
+
const completed2 = result.councillorResults.filter((cr) => cr.status === "completed").length;
|
|
33417
|
+
const total2 = result.councillorResults.length;
|
|
33418
|
+
const composition2 = formatModelComposition(result.councillorResults);
|
|
33419
|
+
return `${result.result}
|
|
33420
|
+
|
|
33421
|
+
---
|
|
33422
|
+
*Council: ${completed2}/${total2} councillors responded (${composition2}) \u2014 degraded*`;
|
|
33423
|
+
}
|
|
33424
|
+
return `Council session failed: ${result.error}`;
|
|
33425
|
+
}
|
|
33426
|
+
let output = result.result ?? "(No output)";
|
|
33427
|
+
const completed = result.councillorResults.filter((cr) => cr.status === "completed").length;
|
|
33428
|
+
const total = result.councillorResults.length;
|
|
33429
|
+
const composition = formatModelComposition(result.councillorResults);
|
|
33430
|
+
output += `
|
|
33431
|
+
|
|
33432
|
+
---
|
|
33433
|
+
*Council: ${completed}/${total} councillors responded (${composition})*`;
|
|
33434
|
+
return output;
|
|
32629
33435
|
}
|
|
32630
|
-
}
|
|
32631
|
-
}
|
|
33436
|
+
});
|
|
33437
|
+
return { council_session };
|
|
33438
|
+
}
|
|
32632
33439
|
// src/tools/lsp/client.ts
|
|
32633
33440
|
var import_node = __toESM(require_main(), 1);
|
|
32634
33441
|
import { readFileSync as readFileSync4 } from "fs";
|
|
32635
|
-
import { extname, resolve } from "path";
|
|
33442
|
+
import { extname, resolve as resolve2 } from "path";
|
|
32636
33443
|
import { Readable, Writable } from "stream";
|
|
32637
33444
|
import { pathToFileURL } from "url";
|
|
32638
|
-
var {spawn:
|
|
33445
|
+
var {spawn: spawn4 } = globalThis.Bun;
|
|
32639
33446
|
|
|
32640
33447
|
// src/tools/lsp/config.ts
|
|
32641
|
-
|
|
32642
|
-
import {
|
|
32643
|
-
import {
|
|
33448
|
+
var import_which = __toESM(require_lib(), 1);
|
|
33449
|
+
import { existsSync as existsSync8 } from "fs";
|
|
33450
|
+
import { homedir as homedir4 } from "os";
|
|
33451
|
+
import { join as join9 } from "path";
|
|
33452
|
+
|
|
33453
|
+
// src/tools/lsp/config-store.ts
|
|
33454
|
+
var userConfig = new Map;
|
|
33455
|
+
function setUserLspConfig(config3) {
|
|
33456
|
+
userConfig.clear();
|
|
33457
|
+
if (config3) {
|
|
33458
|
+
for (const [id, server] of Object.entries(config3)) {
|
|
33459
|
+
if (server && typeof server === "object") {
|
|
33460
|
+
const s = server;
|
|
33461
|
+
userConfig.set(id, {
|
|
33462
|
+
id,
|
|
33463
|
+
command: s.command,
|
|
33464
|
+
extensions: s.extensions,
|
|
33465
|
+
disabled: s.disabled,
|
|
33466
|
+
env: s.env,
|
|
33467
|
+
initialization: s.initialization
|
|
33468
|
+
});
|
|
33469
|
+
}
|
|
33470
|
+
}
|
|
33471
|
+
}
|
|
33472
|
+
}
|
|
33473
|
+
function getAllUserLspConfigs() {
|
|
33474
|
+
return new Map(userConfig);
|
|
33475
|
+
}
|
|
33476
|
+
function hasUserLspConfig() {
|
|
33477
|
+
return userConfig.size > 0;
|
|
33478
|
+
}
|
|
32644
33479
|
|
|
32645
33480
|
// src/tools/lsp/constants.ts
|
|
33481
|
+
import { existsSync as existsSync7, readdirSync, statSync as statSync3 } from "fs";
|
|
33482
|
+
import { dirname as dirname4, resolve } from "path";
|
|
32646
33483
|
var SEVERITY_MAP = {
|
|
32647
33484
|
1: "error",
|
|
32648
33485
|
2: "warning",
|
|
@@ -32651,22 +33488,76 @@ var SEVERITY_MAP = {
|
|
|
32651
33488
|
};
|
|
32652
33489
|
var DEFAULT_MAX_REFERENCES = 200;
|
|
32653
33490
|
var DEFAULT_MAX_DIAGNOSTICS = 200;
|
|
33491
|
+
var LOCK_FILE_PATTERNS = [
|
|
33492
|
+
"package-lock.json",
|
|
33493
|
+
"bun.lockb",
|
|
33494
|
+
"bun.lock",
|
|
33495
|
+
"pnpm-lock.yaml",
|
|
33496
|
+
"yarn.lock"
|
|
33497
|
+
];
|
|
33498
|
+
function* walkUpDirectories(start, stop) {
|
|
33499
|
+
let dir = resolve(start);
|
|
33500
|
+
try {
|
|
33501
|
+
if (!statSync3(dir).isDirectory()) {
|
|
33502
|
+
dir = dirname4(dir);
|
|
33503
|
+
}
|
|
33504
|
+
} catch {
|
|
33505
|
+
dir = dirname4(dir);
|
|
33506
|
+
}
|
|
33507
|
+
let prevDir = "";
|
|
33508
|
+
while (dir !== prevDir && dir !== "/") {
|
|
33509
|
+
yield dir;
|
|
33510
|
+
prevDir = dir;
|
|
33511
|
+
if (dir === stop)
|
|
33512
|
+
break;
|
|
33513
|
+
dir = dirname4(dir);
|
|
33514
|
+
}
|
|
33515
|
+
}
|
|
33516
|
+
function NearestRoot(includePatterns, excludePatterns) {
|
|
33517
|
+
return (file3) => {
|
|
33518
|
+
const cwd = process.cwd();
|
|
33519
|
+
if (excludePatterns) {
|
|
33520
|
+
for (const dir of walkUpDirectories(file3, cwd)) {
|
|
33521
|
+
for (const pattern of excludePatterns) {
|
|
33522
|
+
if (existsSync7(`${dir}/${pattern}`)) {
|
|
33523
|
+
return;
|
|
33524
|
+
}
|
|
33525
|
+
}
|
|
33526
|
+
}
|
|
33527
|
+
}
|
|
33528
|
+
for (const dir of walkUpDirectories(file3, cwd)) {
|
|
33529
|
+
for (const pattern of includePatterns) {
|
|
33530
|
+
if (pattern.includes("*")) {
|
|
33531
|
+
try {
|
|
33532
|
+
const entries = readdirSync(dir);
|
|
33533
|
+
const regex = new RegExp(`^${pattern.replace(/\./g, "\\.").replace(/\*/g, ".*")}$`);
|
|
33534
|
+
if (entries.some((entry) => regex.test(entry))) {
|
|
33535
|
+
return dir;
|
|
33536
|
+
}
|
|
33537
|
+
} catch {}
|
|
33538
|
+
} else if (existsSync7(`${dir}/${pattern}`)) {
|
|
33539
|
+
return dir;
|
|
33540
|
+
}
|
|
33541
|
+
}
|
|
33542
|
+
}
|
|
33543
|
+
return;
|
|
33544
|
+
};
|
|
33545
|
+
}
|
|
32654
33546
|
var BUILTIN_SERVERS = {
|
|
33547
|
+
deno: {
|
|
33548
|
+
command: ["deno", "lsp"],
|
|
33549
|
+
extensions: [".ts", ".tsx", ".js", ".jsx", ".mjs"],
|
|
33550
|
+
root: NearestRoot(["deno.json", "deno.jsonc"])
|
|
33551
|
+
},
|
|
32655
33552
|
typescript: {
|
|
32656
33553
|
command: ["typescript-language-server", "--stdio"],
|
|
32657
|
-
extensions: [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".mts", ".cts"]
|
|
33554
|
+
extensions: [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".mts", ".cts"],
|
|
33555
|
+
root: NearestRoot(LOCK_FILE_PATTERNS, ["deno.json", "deno.jsonc"])
|
|
32658
33556
|
},
|
|
32659
33557
|
vue: {
|
|
32660
33558
|
command: ["vue-language-server", "--stdio"],
|
|
32661
|
-
extensions: [".vue"]
|
|
32662
|
-
|
|
32663
|
-
svelte: {
|
|
32664
|
-
command: ["svelteserver", "--stdio"],
|
|
32665
|
-
extensions: [".svelte"]
|
|
32666
|
-
},
|
|
32667
|
-
astro: {
|
|
32668
|
-
command: ["astro-ls", "--stdio"],
|
|
32669
|
-
extensions: [".astro"]
|
|
33559
|
+
extensions: [".vue"],
|
|
33560
|
+
root: NearestRoot(LOCK_FILE_PATTERNS)
|
|
32670
33561
|
},
|
|
32671
33562
|
eslint: {
|
|
32672
33563
|
command: ["vscode-eslint-language-server", "--stdio"],
|
|
@@ -32677,54 +33568,316 @@ var BUILTIN_SERVERS = {
|
|
|
32677
33568
|
".jsx",
|
|
32678
33569
|
".mjs",
|
|
32679
33570
|
".cjs",
|
|
33571
|
+
".mts",
|
|
33572
|
+
".cts",
|
|
33573
|
+
".vue"
|
|
33574
|
+
],
|
|
33575
|
+
root: NearestRoot(LOCK_FILE_PATTERNS)
|
|
33576
|
+
},
|
|
33577
|
+
oxlint: {
|
|
33578
|
+
command: ["oxlint", "--lsp"],
|
|
33579
|
+
extensions: [
|
|
33580
|
+
".ts",
|
|
33581
|
+
".tsx",
|
|
33582
|
+
".js",
|
|
33583
|
+
".jsx",
|
|
33584
|
+
".mjs",
|
|
33585
|
+
".cjs",
|
|
33586
|
+
".mts",
|
|
33587
|
+
".cts",
|
|
32680
33588
|
".vue",
|
|
33589
|
+
".astro",
|
|
32681
33590
|
".svelte"
|
|
32682
|
-
]
|
|
33591
|
+
],
|
|
33592
|
+
root: NearestRoot([
|
|
33593
|
+
".oxlintrc.json",
|
|
33594
|
+
...LOCK_FILE_PATTERNS,
|
|
33595
|
+
"package.json"
|
|
33596
|
+
])
|
|
32683
33597
|
},
|
|
32684
|
-
|
|
32685
|
-
command: ["
|
|
32686
|
-
extensions: [
|
|
33598
|
+
biome: {
|
|
33599
|
+
command: ["biome", "lsp-proxy", "--stdio"],
|
|
33600
|
+
extensions: [
|
|
33601
|
+
".ts",
|
|
33602
|
+
".tsx",
|
|
33603
|
+
".js",
|
|
33604
|
+
".jsx",
|
|
33605
|
+
".mjs",
|
|
33606
|
+
".cjs",
|
|
33607
|
+
".mts",
|
|
33608
|
+
".cts",
|
|
33609
|
+
".json",
|
|
33610
|
+
".jsonc",
|
|
33611
|
+
".vue",
|
|
33612
|
+
".astro",
|
|
33613
|
+
".svelte",
|
|
33614
|
+
".css",
|
|
33615
|
+
".graphql",
|
|
33616
|
+
".gql",
|
|
33617
|
+
".html"
|
|
33618
|
+
],
|
|
33619
|
+
root: NearestRoot(["biome.json", "biome.jsonc", ...LOCK_FILE_PATTERNS])
|
|
32687
33620
|
},
|
|
32688
33621
|
gopls: {
|
|
32689
33622
|
command: ["gopls"],
|
|
32690
|
-
extensions: [".go"]
|
|
33623
|
+
extensions: [".go"],
|
|
33624
|
+
root: NearestRoot(["go.work", "go.mod", "go.sum"])
|
|
32691
33625
|
},
|
|
32692
|
-
|
|
32693
|
-
command: ["
|
|
32694
|
-
extensions: [".
|
|
33626
|
+
ruby_lsp: {
|
|
33627
|
+
command: ["rubocop", "--lsp"],
|
|
33628
|
+
extensions: [".rb", ".rake", ".gemspec", ".ru"],
|
|
33629
|
+
root: NearestRoot(["Gemfile"])
|
|
32695
33630
|
},
|
|
32696
|
-
|
|
32697
|
-
command: ["
|
|
32698
|
-
extensions: [".py", ".pyi"]
|
|
33631
|
+
ty: {
|
|
33632
|
+
command: ["ty", "server"],
|
|
33633
|
+
extensions: [".py", ".pyi"],
|
|
33634
|
+
root: NearestRoot([
|
|
33635
|
+
"pyproject.toml",
|
|
33636
|
+
"ty.toml",
|
|
33637
|
+
"setup.py",
|
|
33638
|
+
"setup.cfg",
|
|
33639
|
+
"requirements.txt",
|
|
33640
|
+
"Pipfile",
|
|
33641
|
+
"pyrightconfig.json"
|
|
33642
|
+
])
|
|
32699
33643
|
},
|
|
32700
33644
|
pyright: {
|
|
32701
33645
|
command: ["pyright-langserver", "--stdio"],
|
|
32702
|
-
extensions: [".py", ".pyi"]
|
|
33646
|
+
extensions: [".py", ".pyi"],
|
|
33647
|
+
root: NearestRoot([
|
|
33648
|
+
"pyproject.toml",
|
|
33649
|
+
"setup.py",
|
|
33650
|
+
"setup.cfg",
|
|
33651
|
+
"requirements.txt",
|
|
33652
|
+
"Pipfile",
|
|
33653
|
+
"pyrightconfig.json"
|
|
33654
|
+
])
|
|
32703
33655
|
},
|
|
32704
|
-
|
|
32705
|
-
command: ["
|
|
32706
|
-
extensions: [".
|
|
33656
|
+
elixir_ls: {
|
|
33657
|
+
command: ["elixir-ls"],
|
|
33658
|
+
extensions: [".ex", ".exs"],
|
|
33659
|
+
root: NearestRoot(["mix.exs", "mix.lock"])
|
|
32707
33660
|
},
|
|
32708
33661
|
zls: {
|
|
32709
33662
|
command: ["zls"],
|
|
32710
|
-
extensions: [".zig"]
|
|
33663
|
+
extensions: [".zig", ".zon"],
|
|
33664
|
+
root: NearestRoot(["build.zig"])
|
|
33665
|
+
},
|
|
33666
|
+
csharp: {
|
|
33667
|
+
command: ["csharp-ls"],
|
|
33668
|
+
extensions: [".cs"],
|
|
33669
|
+
root: NearestRoot([".slnx", ".sln", ".csproj", "global.json"])
|
|
33670
|
+
},
|
|
33671
|
+
fsharp: {
|
|
33672
|
+
command: ["fsautocomplete"],
|
|
33673
|
+
extensions: [".fs", ".fsi", ".fsx", ".fsscript"],
|
|
33674
|
+
root: NearestRoot([".slnx", ".sln", ".fsproj", "global.json"])
|
|
33675
|
+
},
|
|
33676
|
+
sourcekit_lsp: {
|
|
33677
|
+
command: ["sourcekit-lsp"],
|
|
33678
|
+
extensions: [".swift", ".objc", ".objcpp"],
|
|
33679
|
+
root: NearestRoot(["Package.swift", "*.xcodeproj", "*.xcworkspace"])
|
|
33680
|
+
},
|
|
33681
|
+
rust: {
|
|
33682
|
+
command: ["rust-analyzer"],
|
|
33683
|
+
extensions: [".rs"],
|
|
33684
|
+
root: NearestRoot(["Cargo.toml", "Cargo.lock"])
|
|
33685
|
+
},
|
|
33686
|
+
clangd: {
|
|
33687
|
+
command: ["clangd", "--background-index", "--clang-tidy"],
|
|
33688
|
+
extensions: [
|
|
33689
|
+
".c",
|
|
33690
|
+
".cpp",
|
|
33691
|
+
".cc",
|
|
33692
|
+
".cxx",
|
|
33693
|
+
".c++",
|
|
33694
|
+
".h",
|
|
33695
|
+
".hpp",
|
|
33696
|
+
".hh",
|
|
33697
|
+
".hxx",
|
|
33698
|
+
".h++"
|
|
33699
|
+
],
|
|
33700
|
+
root: NearestRoot([
|
|
33701
|
+
"compile_commands.json",
|
|
33702
|
+
"compile_flags.txt",
|
|
33703
|
+
".clangd",
|
|
33704
|
+
"CMakeLists.txt",
|
|
33705
|
+
"Makefile"
|
|
33706
|
+
])
|
|
33707
|
+
},
|
|
33708
|
+
svelte: {
|
|
33709
|
+
command: ["svelteserver", "--stdio"],
|
|
33710
|
+
extensions: [".svelte"],
|
|
33711
|
+
root: NearestRoot(LOCK_FILE_PATTERNS)
|
|
33712
|
+
},
|
|
33713
|
+
astro: {
|
|
33714
|
+
command: ["astro-ls", "--stdio"],
|
|
33715
|
+
extensions: [".astro"],
|
|
33716
|
+
root: NearestRoot(LOCK_FILE_PATTERNS)
|
|
33717
|
+
},
|
|
33718
|
+
jdtls: {
|
|
33719
|
+
command: ["jdtls"],
|
|
33720
|
+
extensions: [".java"],
|
|
33721
|
+
root: NearestRoot([
|
|
33722
|
+
"pom.xml",
|
|
33723
|
+
"build.gradle",
|
|
33724
|
+
"build.gradle.kts",
|
|
33725
|
+
".project",
|
|
33726
|
+
".classpath"
|
|
33727
|
+
])
|
|
33728
|
+
},
|
|
33729
|
+
kotlin_ls: {
|
|
33730
|
+
command: ["kotlin-lsp", "--stdio"],
|
|
33731
|
+
extensions: [".kt", ".kts"],
|
|
33732
|
+
root: NearestRoot([
|
|
33733
|
+
"settings.gradle.kts",
|
|
33734
|
+
"settings.gradle",
|
|
33735
|
+
"gradlew",
|
|
33736
|
+
"build.gradle.kts",
|
|
33737
|
+
"build.gradle",
|
|
33738
|
+
"pom.xml"
|
|
33739
|
+
])
|
|
33740
|
+
},
|
|
33741
|
+
yaml_ls: {
|
|
33742
|
+
command: ["yaml-language-server", "--stdio"],
|
|
33743
|
+
extensions: [".yaml", ".yml"],
|
|
33744
|
+
root: NearestRoot(LOCK_FILE_PATTERNS)
|
|
33745
|
+
},
|
|
33746
|
+
lua_ls: {
|
|
33747
|
+
command: ["lua-language-server"],
|
|
33748
|
+
extensions: [".lua"],
|
|
33749
|
+
root: NearestRoot([
|
|
33750
|
+
".luarc.json",
|
|
33751
|
+
".luarc.jsonc",
|
|
33752
|
+
".luacheckrc",
|
|
33753
|
+
"stylua.toml",
|
|
33754
|
+
"selene.toml",
|
|
33755
|
+
"selene.yml"
|
|
33756
|
+
])
|
|
33757
|
+
},
|
|
33758
|
+
php_intelephense: {
|
|
33759
|
+
command: ["intelephense", "--stdio"],
|
|
33760
|
+
extensions: [".php"],
|
|
33761
|
+
root: NearestRoot(["composer.json", "composer.lock", ".php-version"])
|
|
33762
|
+
},
|
|
33763
|
+
prisma: {
|
|
33764
|
+
command: ["prisma", "language-server"],
|
|
33765
|
+
extensions: [".prisma"],
|
|
33766
|
+
root: NearestRoot(["schema.prisma", "prisma/schema.prisma", "prisma"])
|
|
33767
|
+
},
|
|
33768
|
+
dart: {
|
|
33769
|
+
command: ["dart", "language-server", "--lsp"],
|
|
33770
|
+
extensions: [".dart"],
|
|
33771
|
+
root: NearestRoot(["pubspec.yaml", "analysis_options.yaml"])
|
|
33772
|
+
},
|
|
33773
|
+
ocaml_lsp: {
|
|
33774
|
+
command: ["ocamllsp"],
|
|
33775
|
+
extensions: [".ml", ".mli"],
|
|
33776
|
+
root: NearestRoot(["dune-project", "dune-workspace", ".merlin", "opam"])
|
|
33777
|
+
},
|
|
33778
|
+
bash: {
|
|
33779
|
+
command: ["bash-language-server", "start"],
|
|
33780
|
+
extensions: [".sh", ".bash", ".zsh", ".ksh"],
|
|
33781
|
+
root: undefined
|
|
33782
|
+
},
|
|
33783
|
+
terraform_ls: {
|
|
33784
|
+
command: ["terraform-ls", "serve"],
|
|
33785
|
+
extensions: [".tf", ".tfvars"],
|
|
33786
|
+
root: NearestRoot([".terraform.lock.hcl", "terraform.tfstate", "*.tf"])
|
|
33787
|
+
},
|
|
33788
|
+
texlab: {
|
|
33789
|
+
command: ["texlab"],
|
|
33790
|
+
extensions: [".tex", ".bib"],
|
|
33791
|
+
root: NearestRoot([".latexmkrc", "latexmkrc", ".texlabroot", "texlabroot"])
|
|
33792
|
+
},
|
|
33793
|
+
dockerfile: {
|
|
33794
|
+
command: ["docker-langserver", "--stdio"],
|
|
33795
|
+
extensions: [".dockerfile", "Dockerfile"],
|
|
33796
|
+
root: undefined
|
|
33797
|
+
},
|
|
33798
|
+
gleam: {
|
|
33799
|
+
command: ["gleam", "lsp"],
|
|
33800
|
+
extensions: [".gleam"],
|
|
33801
|
+
root: NearestRoot(["gleam.toml"])
|
|
33802
|
+
},
|
|
33803
|
+
clojure_lsp: {
|
|
33804
|
+
command: ["clojure-lsp", "listen"],
|
|
33805
|
+
extensions: [".clj", ".cljs", ".cljc", ".edn"],
|
|
33806
|
+
root: NearestRoot([
|
|
33807
|
+
"deps.edn",
|
|
33808
|
+
"project.clj",
|
|
33809
|
+
"shadow-cljs.edn",
|
|
33810
|
+
"bb.edn",
|
|
33811
|
+
"build.boot"
|
|
33812
|
+
])
|
|
33813
|
+
},
|
|
33814
|
+
nixd: {
|
|
33815
|
+
command: ["nixd"],
|
|
33816
|
+
extensions: [".nix"],
|
|
33817
|
+
root: NearestRoot(["flake.nix"])
|
|
33818
|
+
},
|
|
33819
|
+
tinymist: {
|
|
33820
|
+
command: ["tinymist"],
|
|
33821
|
+
extensions: [".typ", ".typc"],
|
|
33822
|
+
root: NearestRoot(["typst.toml"])
|
|
33823
|
+
},
|
|
33824
|
+
haskell_language_server: {
|
|
33825
|
+
command: ["haskell-language-server-wrapper", "--lsp"],
|
|
33826
|
+
extensions: [".hs", ".lhs"],
|
|
33827
|
+
root: NearestRoot(["stack.yaml", "cabal.project", "hie.yaml", "*.cabal"])
|
|
33828
|
+
},
|
|
33829
|
+
julials: {
|
|
33830
|
+
command: [
|
|
33831
|
+
"julia",
|
|
33832
|
+
"--startup-file=no",
|
|
33833
|
+
"--history-file=no",
|
|
33834
|
+
"-e",
|
|
33835
|
+
"using LanguageServer; runserver()"
|
|
33836
|
+
],
|
|
33837
|
+
extensions: [".jl"],
|
|
33838
|
+
root: NearestRoot(["Project.toml", "Manifest.toml", "*.jl"])
|
|
32711
33839
|
}
|
|
32712
33840
|
};
|
|
32713
33841
|
var LSP_INSTALL_HINTS = {
|
|
33842
|
+
deno: "Install Deno: https://deno.land/#installation",
|
|
32714
33843
|
typescript: "npm install -g typescript-language-server typescript",
|
|
32715
33844
|
vue: "npm install -g @vue/language-server",
|
|
32716
|
-
svelte: "npm install -g svelte-language-server",
|
|
32717
|
-
astro: "npm install -g @astrojs/language-server",
|
|
32718
33845
|
eslint: "npm install -g vscode-langservers-extracted",
|
|
32719
|
-
|
|
33846
|
+
oxlint: "npm install -g oxlint or install via package manager",
|
|
33847
|
+
biome: "npm install -g @biomejs/biome",
|
|
32720
33848
|
gopls: "go install golang.org/x/tools/gopls@latest",
|
|
32721
|
-
|
|
32722
|
-
|
|
33849
|
+
ruby_lsp: "gem install rubocop (Ruby LSP runs via rubocop --lsp)",
|
|
33850
|
+
ty: "pip install ty or see https://github.com/astral-sh/ty",
|
|
32723
33851
|
pyright: "pip install pyright",
|
|
32724
|
-
|
|
32725
|
-
zls: "
|
|
33852
|
+
elixir_ls: "Download from https://github.com/elixir-lsp/elixir-ls/releases or build from source",
|
|
33853
|
+
zls: "Install via your package manager or build from source: https://github.com/zigtools/zls",
|
|
33854
|
+
csharp: "dotnet tool install --global csharp-ls",
|
|
33855
|
+
fsharp: "dotnet tool install --global fsautocomplete",
|
|
33856
|
+
sourcekit_lsp: "Install via Xcode or Swift toolchain (included with Xcode)",
|
|
33857
|
+
rust: "rustup component add rust-analyzer",
|
|
33858
|
+
clangd: "Install clangd via your system package manager or LLVM",
|
|
33859
|
+
svelte: "npm install -g svelte-language-server",
|
|
33860
|
+
astro: "npm install -g @astrojs/language-server",
|
|
33861
|
+
jdtls: "See https://github.com/eclipse-jdtls/eclipse.jdt.ls for installation",
|
|
33862
|
+
kotlin_ls: "Download from https://github.com/Kotlin/kotlin-lsp/releases",
|
|
33863
|
+
yaml_ls: "npm install -g yaml-language-server",
|
|
33864
|
+
lua_ls: "Download from https://github.com/LuaLS/lua-language-server/releases",
|
|
33865
|
+
php_intelephense: "npm install -g intelephense",
|
|
33866
|
+
prisma: "npm install -g @prisma/language-server or use npx",
|
|
33867
|
+
dart: "dart pub global activate language_server",
|
|
33868
|
+
ocaml_lsp: "opam install ocaml-lsp-server",
|
|
33869
|
+
bash: "npm install -g bash-language-server",
|
|
33870
|
+
terraform_ls: "Download from https://github.com/hashicorp/terraform-ls/releases or install via tfenv",
|
|
33871
|
+
texlab: "Download from https://github.com/latex-lsp/texlab/releases",
|
|
33872
|
+
dockerfile: "npm install -g dockerfile-language-server-nodejs",
|
|
33873
|
+
gleam: "Install Gleam: https://gleam.run/getting-started/",
|
|
33874
|
+
clojure_lsp: "Install via deps.edn, project.clj, or: clj -M -m clojure-lsp.main",
|
|
33875
|
+
nixd: "Install via nix-env or your system package manager",
|
|
33876
|
+
tinymist: "cargo install tinymist or download from releases",
|
|
33877
|
+
haskell_language_server: "Install Haskell Tool Stack or Cabal, then language-server",
|
|
33878
|
+
julials: "Install Julia: https://julialang.org/downloads/"
|
|
32726
33879
|
};
|
|
32727
|
-
var
|
|
33880
|
+
var LANGUAGE_EXTENSIONS = {
|
|
32728
33881
|
".ts": "typescript",
|
|
32729
33882
|
".tsx": "typescriptreact",
|
|
32730
33883
|
".mts": "typescript",
|
|
@@ -32733,35 +33886,180 @@ var EXT_TO_LANG = {
|
|
|
32733
33886
|
".jsx": "javascriptreact",
|
|
32734
33887
|
".mjs": "javascript",
|
|
32735
33888
|
".cjs": "javascript",
|
|
33889
|
+
".ets": "typescript",
|
|
32736
33890
|
".vue": "vue",
|
|
32737
33891
|
".svelte": "svelte",
|
|
32738
33892
|
".astro": "astro",
|
|
32739
33893
|
".html": "html",
|
|
33894
|
+
".htm": "html",
|
|
33895
|
+
".xml": "xml",
|
|
33896
|
+
".xsl": "xsl",
|
|
32740
33897
|
".css": "css",
|
|
32741
33898
|
".scss": "scss",
|
|
33899
|
+
".sass": "sass",
|
|
32742
33900
|
".less": "less",
|
|
32743
33901
|
".json": "json",
|
|
33902
|
+
".jsonc": "json",
|
|
33903
|
+
".graphql": "graphql",
|
|
33904
|
+
".gql": "graphql",
|
|
33905
|
+
".dockerfile": "dockerfile",
|
|
33906
|
+
".sh": "shellscript",
|
|
33907
|
+
".bash": "shellscript",
|
|
33908
|
+
".zsh": "shellscript",
|
|
33909
|
+
".ksh": "shellscript",
|
|
32744
33910
|
".go": "go",
|
|
32745
33911
|
".rs": "rust",
|
|
32746
33912
|
".py": "python",
|
|
32747
33913
|
".pyi": "python",
|
|
33914
|
+
".rb": "ruby",
|
|
33915
|
+
".rake": "ruby",
|
|
33916
|
+
".gemspec": "ruby",
|
|
33917
|
+
".ru": "ruby",
|
|
32748
33918
|
".c": "c",
|
|
32749
33919
|
".cpp": "cpp",
|
|
32750
33920
|
".cc": "cpp",
|
|
32751
33921
|
".cxx": "cpp",
|
|
33922
|
+
".c++": "cpp",
|
|
32752
33923
|
".h": "c",
|
|
32753
33924
|
".hpp": "cpp",
|
|
32754
|
-
".
|
|
33925
|
+
".hh": "cpp",
|
|
33926
|
+
".hxx": "cpp",
|
|
33927
|
+
".h++": "cpp",
|
|
33928
|
+
".java": "java",
|
|
33929
|
+
".kt": "kotlin",
|
|
33930
|
+
".kts": "kotlin",
|
|
33931
|
+
".cs": "csharp",
|
|
33932
|
+
".fs": "fsharp",
|
|
33933
|
+
".fsi": "fsharp",
|
|
33934
|
+
".fsx": "fsharp",
|
|
33935
|
+
".fsscript": "fsharp",
|
|
33936
|
+
".swift": "swift",
|
|
33937
|
+
".m": "objective-c",
|
|
33938
|
+
".mm": "objective-cpp",
|
|
33939
|
+
".zig": "zig",
|
|
33940
|
+
".zon": "zig",
|
|
33941
|
+
".ex": "elixir",
|
|
33942
|
+
".exs": "elixir",
|
|
33943
|
+
".clj": "clojure",
|
|
33944
|
+
".cljs": "clojure",
|
|
33945
|
+
".cljc": "clojure",
|
|
33946
|
+
".edn": "clojure",
|
|
33947
|
+
".hs": "haskell",
|
|
33948
|
+
".lhs": "haskell",
|
|
33949
|
+
".ml": "ocaml",
|
|
33950
|
+
".mli": "ocaml",
|
|
33951
|
+
".scala": "scala",
|
|
33952
|
+
".php": "php",
|
|
33953
|
+
".lua": "lua",
|
|
33954
|
+
".dart": "dart",
|
|
33955
|
+
".yaml": "yaml",
|
|
33956
|
+
".yml": "yaml",
|
|
33957
|
+
".tf": "terraform",
|
|
33958
|
+
".tfvars": "terraform-vars",
|
|
33959
|
+
".hcl": "hcl",
|
|
33960
|
+
".nix": "nix",
|
|
33961
|
+
".typ": "typst",
|
|
33962
|
+
".typc": "typst",
|
|
33963
|
+
".tex": "latex",
|
|
33964
|
+
".latex": "latex",
|
|
33965
|
+
".bib": "bibtex",
|
|
33966
|
+
".bibtex": "bibtex",
|
|
33967
|
+
".prisma": "prisma",
|
|
33968
|
+
".jl": "julia",
|
|
33969
|
+
".gleam": "gleam",
|
|
33970
|
+
".md": "markdown",
|
|
33971
|
+
".markdown": "markdown",
|
|
33972
|
+
".d": "d",
|
|
33973
|
+
".pas": "pascal",
|
|
33974
|
+
".pascal": "pascal",
|
|
33975
|
+
".diff": "diff",
|
|
33976
|
+
".patch": "diff",
|
|
33977
|
+
".erl": "erlang",
|
|
33978
|
+
".hrl": "erlang",
|
|
33979
|
+
".groovy": "groovy",
|
|
33980
|
+
".handlebars": "handlebars",
|
|
33981
|
+
".hbs": "handlebars",
|
|
33982
|
+
".ini": "ini",
|
|
33983
|
+
".makefile": "makefile",
|
|
33984
|
+
makefile: "makefile",
|
|
33985
|
+
".pug": "jade",
|
|
33986
|
+
".jade": "jade",
|
|
33987
|
+
".r": "r",
|
|
33988
|
+
".cshtml": "razor",
|
|
33989
|
+
".razor": "razor",
|
|
33990
|
+
".erb": "erb",
|
|
33991
|
+
".html.erb": "erb",
|
|
33992
|
+
".js.erb": "erb",
|
|
33993
|
+
".css.erb": "erb",
|
|
33994
|
+
".json.erb": "erb",
|
|
33995
|
+
".shader": "shaderlab",
|
|
33996
|
+
".sql": "sql",
|
|
33997
|
+
".perl": "perl",
|
|
33998
|
+
".pl": "perl",
|
|
33999
|
+
".pm": "perl",
|
|
34000
|
+
".pm6": "perl6",
|
|
34001
|
+
".ps1": "powershell",
|
|
34002
|
+
".psm1": "powershell",
|
|
34003
|
+
".coffee": "coffeescript",
|
|
34004
|
+
".bat": "bat",
|
|
34005
|
+
".abap": "abap",
|
|
34006
|
+
".gitcommit": "git-commit",
|
|
34007
|
+
".gitrebase": "git-rebase"
|
|
32755
34008
|
};
|
|
32756
34009
|
|
|
32757
34010
|
// src/tools/lsp/config.ts
|
|
32758
|
-
function
|
|
34011
|
+
function buildMergedServers() {
|
|
34012
|
+
const servers = new Map;
|
|
32759
34013
|
for (const [id, config3] of Object.entries(BUILTIN_SERVERS)) {
|
|
34014
|
+
servers.set(id, {
|
|
34015
|
+
id,
|
|
34016
|
+
command: config3.command,
|
|
34017
|
+
extensions: config3.extensions,
|
|
34018
|
+
root: config3.root,
|
|
34019
|
+
env: config3.env,
|
|
34020
|
+
initialization: config3.initialization
|
|
34021
|
+
});
|
|
34022
|
+
}
|
|
34023
|
+
if (hasUserLspConfig()) {
|
|
34024
|
+
for (const [id, userConfig2] of getAllUserLspConfigs()) {
|
|
34025
|
+
if (userConfig2.disabled === true) {
|
|
34026
|
+
servers.delete(id);
|
|
34027
|
+
continue;
|
|
34028
|
+
}
|
|
34029
|
+
const existing = servers.get(id);
|
|
34030
|
+
if (existing) {
|
|
34031
|
+
servers.set(id, {
|
|
34032
|
+
...existing,
|
|
34033
|
+
id,
|
|
34034
|
+
command: userConfig2.command ?? existing.command,
|
|
34035
|
+
extensions: userConfig2.extensions ?? existing.extensions,
|
|
34036
|
+
root: existing.root,
|
|
34037
|
+
env: userConfig2.env ?? existing.env,
|
|
34038
|
+
initialization: userConfig2.initialization ?? existing.initialization
|
|
34039
|
+
});
|
|
34040
|
+
} else {
|
|
34041
|
+
servers.set(id, {
|
|
34042
|
+
id,
|
|
34043
|
+
command: userConfig2.command ?? [],
|
|
34044
|
+
extensions: userConfig2.extensions ?? [],
|
|
34045
|
+
root: undefined,
|
|
34046
|
+
env: userConfig2.env,
|
|
34047
|
+
initialization: userConfig2.initialization
|
|
34048
|
+
});
|
|
34049
|
+
}
|
|
34050
|
+
}
|
|
34051
|
+
}
|
|
34052
|
+
return servers;
|
|
34053
|
+
}
|
|
34054
|
+
function findServerForExtension(ext) {
|
|
34055
|
+
const servers = buildMergedServers();
|
|
34056
|
+
for (const [, config3] of servers) {
|
|
32760
34057
|
if (config3.extensions.includes(ext)) {
|
|
32761
34058
|
const server = {
|
|
32762
|
-
id,
|
|
34059
|
+
id: config3.id,
|
|
32763
34060
|
command: config3.command,
|
|
32764
34061
|
extensions: config3.extensions,
|
|
34062
|
+
root: config3.root,
|
|
32765
34063
|
env: config3.env,
|
|
32766
34064
|
initialization: config3.initialization
|
|
32767
34065
|
};
|
|
@@ -32771,39 +34069,37 @@ function findServerForExtension(ext) {
|
|
|
32771
34069
|
return {
|
|
32772
34070
|
status: "not_installed",
|
|
32773
34071
|
server,
|
|
32774
|
-
installHint: LSP_INSTALL_HINTS[id] || `Install '${config3.command[0]}' and add to PATH`
|
|
34072
|
+
installHint: LSP_INSTALL_HINTS[config3.id] || `Install '${config3.command[0]}' and add to PATH`
|
|
32775
34073
|
};
|
|
32776
34074
|
}
|
|
32777
34075
|
}
|
|
32778
34076
|
return { status: "not_configured", extension: ext };
|
|
32779
34077
|
}
|
|
32780
34078
|
function getLanguageId(ext) {
|
|
32781
|
-
return
|
|
34079
|
+
return LANGUAGE_EXTENSIONS[ext] || "plaintext";
|
|
32782
34080
|
}
|
|
32783
34081
|
function isServerInstalled(command) {
|
|
32784
34082
|
if (command.length === 0)
|
|
32785
34083
|
return false;
|
|
32786
34084
|
const cmd = command[0];
|
|
32787
34085
|
if (cmd.includes("/") || cmd.includes("\\")) {
|
|
32788
|
-
return
|
|
34086
|
+
return existsSync8(cmd);
|
|
32789
34087
|
}
|
|
32790
34088
|
const isWindows = process.platform === "win32";
|
|
32791
34089
|
const ext = isWindows ? ".exe" : "";
|
|
32792
|
-
const
|
|
32793
|
-
const
|
|
32794
|
-
const
|
|
32795
|
-
|
|
32796
|
-
|
|
32797
|
-
|
|
32798
|
-
|
|
32799
|
-
|
|
32800
|
-
const cwd = process.cwd();
|
|
32801
|
-
const localBin = join11(cwd, "node_modules", ".bin", cmd);
|
|
32802
|
-
if (existsSync9(localBin) || existsSync9(localBin + ext)) {
|
|
34090
|
+
const opencodeBin = join9(homedir4(), ".config", "opencode", "bin");
|
|
34091
|
+
const searchPath = (process.env.PATH ?? "") + (isWindows ? ";" : ":") + opencodeBin;
|
|
34092
|
+
const result = import_which.default.sync(cmd, {
|
|
34093
|
+
path: searchPath,
|
|
34094
|
+
pathExt: isWindows ? process.env.PATHEXT : undefined,
|
|
34095
|
+
nothrow: true
|
|
34096
|
+
});
|
|
34097
|
+
if (result !== null) {
|
|
32803
34098
|
return true;
|
|
32804
34099
|
}
|
|
32805
|
-
const
|
|
32806
|
-
|
|
34100
|
+
const cwd = process.cwd();
|
|
34101
|
+
const localBin = join9(cwd, "node_modules", ".bin", cmd);
|
|
34102
|
+
if (existsSync8(localBin) || existsSync8(localBin + ext)) {
|
|
32807
34103
|
return true;
|
|
32808
34104
|
}
|
|
32809
34105
|
return false;
|
|
@@ -32816,6 +34112,7 @@ class LSPServerManager {
|
|
|
32816
34112
|
cleanupInterval = null;
|
|
32817
34113
|
IDLE_TIMEOUT = 5 * 60 * 1000;
|
|
32818
34114
|
constructor() {
|
|
34115
|
+
log("[lsp] manager initialized");
|
|
32819
34116
|
this.startCleanupTimer();
|
|
32820
34117
|
this.registerProcessCleanup();
|
|
32821
34118
|
}
|
|
@@ -32872,16 +34169,31 @@ class LSPServerManager {
|
|
|
32872
34169
|
const managed = this.clients.get(key);
|
|
32873
34170
|
if (managed) {
|
|
32874
34171
|
if (managed.initPromise) {
|
|
34172
|
+
log("[lsp] getClient: waiting for init", { key, server: server.id });
|
|
32875
34173
|
await managed.initPromise;
|
|
32876
34174
|
}
|
|
32877
34175
|
if (managed.client.isAlive()) {
|
|
32878
34176
|
managed.refCount++;
|
|
32879
34177
|
managed.lastUsedAt = Date.now();
|
|
34178
|
+
log("[lsp] getClient: reuse pooled client", {
|
|
34179
|
+
key,
|
|
34180
|
+
server: server.id,
|
|
34181
|
+
refCount: managed.refCount
|
|
34182
|
+
});
|
|
32880
34183
|
return managed.client;
|
|
32881
34184
|
}
|
|
34185
|
+
log("[lsp] getClient: client dead, recreating", {
|
|
34186
|
+
key,
|
|
34187
|
+
server: server.id
|
|
34188
|
+
});
|
|
32882
34189
|
await managed.client.stop();
|
|
32883
34190
|
this.clients.delete(key);
|
|
32884
34191
|
}
|
|
34192
|
+
log("[lsp] getClient: creating new client", {
|
|
34193
|
+
key,
|
|
34194
|
+
server: server.id,
|
|
34195
|
+
root
|
|
34196
|
+
});
|
|
32885
34197
|
const client = new LSPClient(root, server);
|
|
32886
34198
|
const initPromise2 = (async () => {
|
|
32887
34199
|
await client.start();
|
|
@@ -32901,7 +34213,13 @@ class LSPServerManager {
|
|
|
32901
34213
|
m.initPromise = undefined;
|
|
32902
34214
|
m.isInitializing = false;
|
|
32903
34215
|
}
|
|
34216
|
+
log("[lsp] getClient: client ready", { key, server: server.id });
|
|
32904
34217
|
} catch (err) {
|
|
34218
|
+
log("[lsp] getClient: init failed", {
|
|
34219
|
+
key,
|
|
34220
|
+
server: server.id,
|
|
34221
|
+
error: String(err)
|
|
34222
|
+
});
|
|
32905
34223
|
this.clients.delete(key);
|
|
32906
34224
|
throw err;
|
|
32907
34225
|
}
|
|
@@ -32913,6 +34231,11 @@ class LSPServerManager {
|
|
|
32913
34231
|
if (managed && managed.refCount > 0) {
|
|
32914
34232
|
managed.refCount--;
|
|
32915
34233
|
managed.lastUsedAt = Date.now();
|
|
34234
|
+
log("[lsp] releaseClient", {
|
|
34235
|
+
key,
|
|
34236
|
+
server: serverId,
|
|
34237
|
+
refCount: managed.refCount
|
|
34238
|
+
});
|
|
32916
34239
|
}
|
|
32917
34240
|
}
|
|
32918
34241
|
isServerInitializing(root, serverId) {
|
|
@@ -32921,14 +34244,19 @@ class LSPServerManager {
|
|
|
32921
34244
|
return managed?.isInitializing ?? false;
|
|
32922
34245
|
}
|
|
32923
34246
|
async stopAll() {
|
|
32924
|
-
|
|
34247
|
+
log("[lsp] stopAll: shutting down all clients", {
|
|
34248
|
+
count: this.clients.size
|
|
34249
|
+
});
|
|
34250
|
+
for (const [key, managed] of this.clients) {
|
|
32925
34251
|
await managed.client.stop();
|
|
34252
|
+
log("[lsp] stopAll: client stopped", { key });
|
|
32926
34253
|
}
|
|
32927
34254
|
this.clients.clear();
|
|
32928
34255
|
if (this.cleanupInterval) {
|
|
32929
34256
|
clearInterval(this.cleanupInterval);
|
|
32930
34257
|
this.cleanupInterval = null;
|
|
32931
34258
|
}
|
|
34259
|
+
log("[lsp] stopAll: complete");
|
|
32932
34260
|
}
|
|
32933
34261
|
}
|
|
32934
34262
|
var lspManager = LSPServerManager.getInstance();
|
|
@@ -32947,7 +34275,12 @@ class LSPClient {
|
|
|
32947
34275
|
this.server = server;
|
|
32948
34276
|
}
|
|
32949
34277
|
async start() {
|
|
32950
|
-
|
|
34278
|
+
log("[lsp] LSPClient.start: spawning server", {
|
|
34279
|
+
server: this.server.id,
|
|
34280
|
+
command: this.server.command.join(" "),
|
|
34281
|
+
root: this.root
|
|
34282
|
+
});
|
|
34283
|
+
this.proc = spawn4(this.server.command, {
|
|
32951
34284
|
stdin: "pipe",
|
|
32952
34285
|
stdout: "pipe",
|
|
32953
34286
|
stderr: "pipe",
|
|
@@ -33016,13 +34349,19 @@ class LSPClient {
|
|
|
33016
34349
|
this.processExited = true;
|
|
33017
34350
|
});
|
|
33018
34351
|
this.connection.listen();
|
|
33019
|
-
await new Promise((
|
|
34352
|
+
await new Promise((resolve3) => setTimeout(resolve3, 100));
|
|
33020
34353
|
if (this.proc.exitCode !== null) {
|
|
33021
34354
|
const stderr = this.stderrBuffer.join(`
|
|
33022
34355
|
`);
|
|
34356
|
+
log("[lsp] LSPClient.start: server exited immediately", {
|
|
34357
|
+
server: this.server.id,
|
|
34358
|
+
exitCode: this.proc.exitCode,
|
|
34359
|
+
stderr: stderr.slice(0, 500)
|
|
34360
|
+
});
|
|
33023
34361
|
throw new Error(`LSP server exited immediately with code ${this.proc.exitCode}` + (stderr ? `
|
|
33024
34362
|
stderr: ${stderr}` : ""));
|
|
33025
34363
|
}
|
|
34364
|
+
log("[lsp] LSPClient.start: server spawned", { server: this.server.id });
|
|
33026
34365
|
}
|
|
33027
34366
|
startStderrReading() {
|
|
33028
34367
|
if (!this.proc)
|
|
@@ -33048,6 +34387,10 @@ stderr: ${stderr}` : ""));
|
|
|
33048
34387
|
async initialize() {
|
|
33049
34388
|
if (!this.connection)
|
|
33050
34389
|
throw new Error("LSP connection not established");
|
|
34390
|
+
log("[lsp] LSPClient.initialize: sending initialize request", {
|
|
34391
|
+
server: this.server.id,
|
|
34392
|
+
root: this.root
|
|
34393
|
+
});
|
|
33051
34394
|
const rootUri = pathToFileURL(this.root).href;
|
|
33052
34395
|
await this.connection.sendRequest("initialize", {
|
|
33053
34396
|
processId: process.pid,
|
|
@@ -33079,14 +34422,22 @@ stderr: ${stderr}` : ""));
|
|
|
33079
34422
|
});
|
|
33080
34423
|
this.connection.sendNotification("initialized");
|
|
33081
34424
|
await new Promise((r) => setTimeout(r, 300));
|
|
34425
|
+
log("[lsp] LSPClient.initialize: complete", { server: this.server.id });
|
|
33082
34426
|
}
|
|
33083
34427
|
async openFile(filePath) {
|
|
33084
|
-
const absPath =
|
|
33085
|
-
if (this.openedFiles.has(absPath))
|
|
34428
|
+
const absPath = resolve2(filePath);
|
|
34429
|
+
if (this.openedFiles.has(absPath)) {
|
|
34430
|
+
log("[lsp] openFile: already open, skipping", { filePath: absPath });
|
|
33086
34431
|
return;
|
|
34432
|
+
}
|
|
33087
34433
|
const text = readFileSync4(absPath, "utf-8");
|
|
33088
34434
|
const ext = extname(absPath);
|
|
33089
34435
|
const languageId = getLanguageId(ext);
|
|
34436
|
+
log("[lsp] openFile: opening document", {
|
|
34437
|
+
filePath: absPath,
|
|
34438
|
+
languageId,
|
|
34439
|
+
size: text.length
|
|
34440
|
+
});
|
|
33090
34441
|
this.connection?.sendNotification("textDocument/didOpen", {
|
|
33091
34442
|
textDocument: {
|
|
33092
34443
|
uri: pathToFileURL(absPath).href,
|
|
@@ -33099,7 +34450,7 @@ stderr: ${stderr}` : ""));
|
|
|
33099
34450
|
await new Promise((r) => setTimeout(r, 1000));
|
|
33100
34451
|
}
|
|
33101
34452
|
async definition(filePath, line, character) {
|
|
33102
|
-
const absPath =
|
|
34453
|
+
const absPath = resolve2(filePath);
|
|
33103
34454
|
await this.openFile(absPath);
|
|
33104
34455
|
return this.connection?.sendRequest("textDocument/definition", {
|
|
33105
34456
|
textDocument: { uri: pathToFileURL(absPath).href },
|
|
@@ -33107,7 +34458,7 @@ stderr: ${stderr}` : ""));
|
|
|
33107
34458
|
});
|
|
33108
34459
|
}
|
|
33109
34460
|
async references(filePath, line, character, includeDeclaration = true) {
|
|
33110
|
-
const absPath =
|
|
34461
|
+
const absPath = resolve2(filePath);
|
|
33111
34462
|
await this.openFile(absPath);
|
|
33112
34463
|
return this.connection?.sendRequest("textDocument/references", {
|
|
33113
34464
|
textDocument: { uri: pathToFileURL(absPath).href },
|
|
@@ -33116,7 +34467,7 @@ stderr: ${stderr}` : ""));
|
|
|
33116
34467
|
});
|
|
33117
34468
|
}
|
|
33118
34469
|
async diagnostics(filePath) {
|
|
33119
|
-
const absPath =
|
|
34470
|
+
const absPath = resolve2(filePath);
|
|
33120
34471
|
const uri = pathToFileURL(absPath).href;
|
|
33121
34472
|
await this.openFile(absPath);
|
|
33122
34473
|
await new Promise((r) => setTimeout(r, 500));
|
|
@@ -33131,7 +34482,7 @@ stderr: ${stderr}` : ""));
|
|
|
33131
34482
|
return { items: this.diagnosticsStore.get(uri) ?? [] };
|
|
33132
34483
|
}
|
|
33133
34484
|
async rename(filePath, line, character, newName) {
|
|
33134
|
-
const absPath =
|
|
34485
|
+
const absPath = resolve2(filePath);
|
|
33135
34486
|
await this.openFile(absPath);
|
|
33136
34487
|
return this.connection?.sendRequest("textDocument/rename", {
|
|
33137
34488
|
textDocument: { uri: pathToFileURL(absPath).href },
|
|
@@ -33143,6 +34494,7 @@ stderr: ${stderr}` : ""));
|
|
|
33143
34494
|
return this.proc !== null && !this.processExited && this.proc.exitCode === null;
|
|
33144
34495
|
}
|
|
33145
34496
|
async stop() {
|
|
34497
|
+
log("[lsp] LSPClient.stop: stopping", { server: this.server.id });
|
|
33146
34498
|
try {
|
|
33147
34499
|
if (this.connection) {
|
|
33148
34500
|
await this.connection.sendRequest("shutdown");
|
|
@@ -33155,45 +34507,24 @@ stderr: ${stderr}` : ""));
|
|
|
33155
34507
|
this.connection = null;
|
|
33156
34508
|
this.processExited = true;
|
|
33157
34509
|
this.diagnosticsStore.clear();
|
|
34510
|
+
log("[lsp] LSPClient.stop: complete", { server: this.server.id });
|
|
33158
34511
|
}
|
|
33159
34512
|
}
|
|
33160
34513
|
// src/tools/lsp/utils.ts
|
|
33161
34514
|
import {
|
|
33162
|
-
existsSync as
|
|
34515
|
+
existsSync as existsSync9,
|
|
33163
34516
|
readFileSync as readFileSync5,
|
|
33164
|
-
statSync as
|
|
33165
|
-
unlinkSync as
|
|
34517
|
+
statSync as statSync4,
|
|
34518
|
+
unlinkSync as unlinkSync2,
|
|
33166
34519
|
writeFileSync as writeFileSync3
|
|
33167
34520
|
} from "fs";
|
|
33168
|
-
import { dirname as
|
|
34521
|
+
import { dirname as dirname5, extname as extname2, join as join10, resolve as resolve3 } from "path";
|
|
33169
34522
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
33170
|
-
function
|
|
33171
|
-
|
|
33172
|
-
|
|
33173
|
-
if (!statSync3(dir).isDirectory()) {
|
|
33174
|
-
dir = dirname4(dir);
|
|
33175
|
-
}
|
|
33176
|
-
} catch {
|
|
33177
|
-
dir = dirname4(dir);
|
|
33178
|
-
}
|
|
33179
|
-
const markers = [
|
|
33180
|
-
".git",
|
|
33181
|
-
"package.json",
|
|
33182
|
-
"pyproject.toml",
|
|
33183
|
-
"Cargo.toml",
|
|
33184
|
-
"go.mod"
|
|
33185
|
-
];
|
|
33186
|
-
let prevDir = "";
|
|
33187
|
-
while (dir !== prevDir) {
|
|
33188
|
-
for (const marker of markers) {
|
|
33189
|
-
if (existsSync10(join12(dir, marker))) {
|
|
33190
|
-
return dir;
|
|
33191
|
-
}
|
|
33192
|
-
}
|
|
33193
|
-
prevDir = dir;
|
|
33194
|
-
dir = dirname4(dir);
|
|
34523
|
+
function findServerProjectRoot(filePath, server) {
|
|
34524
|
+
if (server.root) {
|
|
34525
|
+
return server.root(filePath) ?? dirname5(resolve3(filePath));
|
|
33195
34526
|
}
|
|
33196
|
-
return
|
|
34527
|
+
return dirname5(resolve3(filePath));
|
|
33197
34528
|
}
|
|
33198
34529
|
function uriToPath(uri) {
|
|
33199
34530
|
return fileURLToPath2(uri);
|
|
@@ -33212,24 +34543,42 @@ function formatServerLookupError(result) {
|
|
|
33212
34543
|
return `No LSP server configured for extension: ${result.extension}`;
|
|
33213
34544
|
}
|
|
33214
34545
|
async function withLspClient(filePath, fn) {
|
|
33215
|
-
const absPath =
|
|
34546
|
+
const absPath = resolve3(filePath);
|
|
33216
34547
|
const ext = extname2(absPath);
|
|
33217
34548
|
const result = findServerForExtension(ext);
|
|
33218
34549
|
if (result.status !== "found") {
|
|
34550
|
+
log("[lsp] withLspClient: server not found", {
|
|
34551
|
+
filePath: absPath,
|
|
34552
|
+
extension: ext
|
|
34553
|
+
});
|
|
33219
34554
|
throw new Error(formatServerLookupError(result));
|
|
33220
34555
|
}
|
|
33221
34556
|
const server = result.server;
|
|
33222
|
-
const root =
|
|
34557
|
+
const root = findServerProjectRoot(absPath, server) ?? dirname5(absPath);
|
|
34558
|
+
log("[lsp] withLspClient: acquiring client", {
|
|
34559
|
+
filePath: absPath,
|
|
34560
|
+
server: server.id,
|
|
34561
|
+
root
|
|
34562
|
+
});
|
|
33223
34563
|
const client = await lspManager.getClient(root, server);
|
|
33224
34564
|
try {
|
|
33225
|
-
|
|
34565
|
+
const result2 = await fn(client);
|
|
34566
|
+
log("[lsp] withLspClient: operation complete", { server: server.id });
|
|
34567
|
+
return result2;
|
|
33226
34568
|
} catch (e) {
|
|
33227
34569
|
if (e instanceof Error && e.message.includes("timeout")) {
|
|
33228
34570
|
const isInitializing = lspManager.isServerInitializing(root, server.id);
|
|
33229
34571
|
if (isInitializing) {
|
|
34572
|
+
log("[lsp] withLspClient: timeout during init", {
|
|
34573
|
+
server: server.id
|
|
34574
|
+
});
|
|
33230
34575
|
throw new Error(`LSP server is still initializing. Please retry in a few seconds.`);
|
|
33231
34576
|
}
|
|
33232
34577
|
}
|
|
34578
|
+
log("[lsp] withLspClient: operation failed", {
|
|
34579
|
+
server: server.id,
|
|
34580
|
+
error: String(e)
|
|
34581
|
+
});
|
|
33233
34582
|
throw e;
|
|
33234
34583
|
} finally {
|
|
33235
34584
|
lspManager.releaseClient(root, server.id);
|
|
@@ -33313,6 +34662,7 @@ function applyTextEditsToFile(filePath, edits) {
|
|
|
33313
34662
|
}
|
|
33314
34663
|
function applyWorkspaceEdit(edit) {
|
|
33315
34664
|
if (!edit) {
|
|
34665
|
+
log("[lsp] applyWorkspaceEdit: no edit provided");
|
|
33316
34666
|
return {
|
|
33317
34667
|
success: false,
|
|
33318
34668
|
filesModified: [],
|
|
@@ -33320,6 +34670,8 @@ function applyWorkspaceEdit(edit) {
|
|
|
33320
34670
|
errors: ["No edit provided"]
|
|
33321
34671
|
};
|
|
33322
34672
|
}
|
|
34673
|
+
const changeCount = (edit.changes ? Object.keys(edit.changes).length : 0) + (edit.documentChanges ? edit.documentChanges.length : 0);
|
|
34674
|
+
log("[lsp] applyWorkspaceEdit: applying", { changeCount });
|
|
33323
34675
|
const result = {
|
|
33324
34676
|
success: true,
|
|
33325
34677
|
filesModified: [],
|
|
@@ -33357,7 +34709,7 @@ function applyWorkspaceEdit(edit) {
|
|
|
33357
34709
|
const newPath = uriToPath(change.newUri);
|
|
33358
34710
|
const content = readFileSync5(oldPath, "utf-8");
|
|
33359
34711
|
writeFileSync3(newPath, content, "utf-8");
|
|
33360
|
-
|
|
34712
|
+
unlinkSync2(oldPath);
|
|
33361
34713
|
result.filesModified.push(newPath);
|
|
33362
34714
|
} catch (err) {
|
|
33363
34715
|
result.success = false;
|
|
@@ -33366,7 +34718,7 @@ function applyWorkspaceEdit(edit) {
|
|
|
33366
34718
|
} else if (change.kind === "delete") {
|
|
33367
34719
|
try {
|
|
33368
34720
|
const filePath = uriToPath(change.uri);
|
|
33369
|
-
|
|
34721
|
+
unlinkSync2(filePath);
|
|
33370
34722
|
result.filesModified.push(filePath);
|
|
33371
34723
|
} catch (err) {
|
|
33372
34724
|
result.success = false;
|
|
@@ -33386,6 +34738,12 @@ function applyWorkspaceEdit(edit) {
|
|
|
33386
34738
|
}
|
|
33387
34739
|
}
|
|
33388
34740
|
}
|
|
34741
|
+
log("[lsp] applyWorkspaceEdit: complete", {
|
|
34742
|
+
success: result.success,
|
|
34743
|
+
filesModified: result.filesModified.length,
|
|
34744
|
+
totalEdits: result.totalEdits,
|
|
34745
|
+
errors: result.errors.length
|
|
34746
|
+
});
|
|
33389
34747
|
return result;
|
|
33390
34748
|
}
|
|
33391
34749
|
function formatApplyResult(result) {
|
|
@@ -33534,6 +34892,28 @@ var OhMyOpenCodeLite = async (ctx) => {
|
|
|
33534
34892
|
modelArrayMap[agentDef.name] = agentDef._modelArray;
|
|
33535
34893
|
}
|
|
33536
34894
|
}
|
|
34895
|
+
const runtimeChains = {};
|
|
34896
|
+
for (const agentDef of agentDefs) {
|
|
34897
|
+
if (agentDef._modelArray?.length) {
|
|
34898
|
+
runtimeChains[agentDef.name] = agentDef._modelArray.map((m) => m.id);
|
|
34899
|
+
}
|
|
34900
|
+
}
|
|
34901
|
+
if (config3.fallback?.enabled !== false) {
|
|
34902
|
+
const chains = config3.fallback?.chains ?? {};
|
|
34903
|
+
for (const [agentName, chainModels] of Object.entries(chains)) {
|
|
34904
|
+
if (!chainModels?.length)
|
|
34905
|
+
continue;
|
|
34906
|
+
const existing = runtimeChains[agentName] ?? [];
|
|
34907
|
+
const seen = new Set(existing);
|
|
34908
|
+
for (const m of chainModels) {
|
|
34909
|
+
if (!seen.has(m)) {
|
|
34910
|
+
seen.add(m);
|
|
34911
|
+
existing.push(m);
|
|
34912
|
+
}
|
|
34913
|
+
}
|
|
34914
|
+
runtimeChains[agentName] = existing;
|
|
34915
|
+
}
|
|
34916
|
+
}
|
|
33537
34917
|
const tmuxConfig = {
|
|
33538
34918
|
enabled: config3.tmux?.enabled ?? false,
|
|
33539
34919
|
layout: config3.tmux?.layout ?? "main-vertical",
|
|
@@ -33549,6 +34929,7 @@ var OhMyOpenCodeLite = async (ctx) => {
|
|
|
33549
34929
|
}
|
|
33550
34930
|
const backgroundManager = new BackgroundTaskManager(ctx, tmuxConfig, config3);
|
|
33551
34931
|
const backgroundTools = createBackgroundTools(ctx, backgroundManager, tmuxConfig, config3);
|
|
34932
|
+
const councilTools = config3.council ? createCouncilTool(ctx, new CouncilManager(ctx, config3, backgroundManager.getDepthTracker(), tmuxConfig.enabled)) : {};
|
|
33552
34933
|
const mcps = createBuiltinMcps(config3.disabled_mcps);
|
|
33553
34934
|
const tmuxSessionManager = new TmuxSessionManager(ctx, tmuxConfig);
|
|
33554
34935
|
const autoUpdateChecker = createAutoUpdateCheckerHook(ctx, {
|
|
@@ -33556,26 +34937,31 @@ var OhMyOpenCodeLite = async (ctx) => {
|
|
|
33556
34937
|
autoUpdate: true
|
|
33557
34938
|
});
|
|
33558
34939
|
const phaseReminderHook = createPhaseReminderHook();
|
|
33559
|
-
const
|
|
34940
|
+
const postFileToolNudgeHook = createPostFileToolNudgeHook();
|
|
33560
34941
|
const chatHeadersHook = createChatHeadersHook(ctx);
|
|
33561
34942
|
const delegateTaskRetryHook = createDelegateTaskRetryHook(ctx);
|
|
33562
34943
|
const jsonErrorRecoveryHook = createJsonErrorRecoveryHook(ctx);
|
|
34944
|
+
const foregroundFallback = new ForegroundFallbackManager(ctx.client, runtimeChains, config3.fallback?.enabled !== false && Object.keys(runtimeChains).length > 0);
|
|
33563
34945
|
return {
|
|
33564
34946
|
name: "oh-my-opencode-slim",
|
|
33565
34947
|
agent: agents,
|
|
33566
34948
|
tool: {
|
|
33567
34949
|
...backgroundTools,
|
|
34950
|
+
...councilTools,
|
|
33568
34951
|
lsp_goto_definition,
|
|
33569
34952
|
lsp_find_references,
|
|
33570
34953
|
lsp_diagnostics,
|
|
33571
34954
|
lsp_rename,
|
|
33572
|
-
grep,
|
|
33573
34955
|
ast_grep_search,
|
|
33574
34956
|
ast_grep_replace
|
|
33575
34957
|
},
|
|
33576
34958
|
mcp: mcps,
|
|
33577
34959
|
config: async (opencodeConfig) => {
|
|
33578
|
-
|
|
34960
|
+
const lspConfig = opencodeConfig.lsp;
|
|
34961
|
+
setUserLspConfig(lspConfig);
|
|
34962
|
+
if (config3.setDefaultAgent !== false && !opencodeConfig.default_agent) {
|
|
34963
|
+
opencodeConfig.default_agent = "orchestrator";
|
|
34964
|
+
}
|
|
33579
34965
|
if (!opencodeConfig.agent) {
|
|
33580
34966
|
opencodeConfig.agent = { ...agents };
|
|
33581
34967
|
} else {
|
|
@@ -33594,38 +34980,45 @@ var OhMyOpenCodeLite = async (ctx) => {
|
|
|
33594
34980
|
}
|
|
33595
34981
|
}
|
|
33596
34982
|
const configAgent = opencodeConfig.agent;
|
|
33597
|
-
|
|
33598
|
-
|
|
33599
|
-
|
|
33600
|
-
|
|
33601
|
-
|
|
33602
|
-
|
|
33603
|
-
|
|
33604
|
-
|
|
33605
|
-
|
|
33606
|
-
|
|
33607
|
-
|
|
33608
|
-
|
|
33609
|
-
|
|
33610
|
-
|
|
33611
|
-
|
|
33612
|
-
|
|
33613
|
-
|
|
33614
|
-
|
|
33615
|
-
|
|
33616
|
-
agent: agentName,
|
|
33617
|
-
model: modelEntry.id,
|
|
33618
|
-
variant: modelEntry.variant
|
|
33619
|
-
});
|
|
33620
|
-
resolved = true;
|
|
33621
|
-
break;
|
|
33622
|
-
}
|
|
34983
|
+
const fallbackChainsEnabled = config3.fallback?.enabled !== false;
|
|
34984
|
+
const fallbackChains = fallbackChainsEnabled ? config3.fallback?.chains ?? {} : {};
|
|
34985
|
+
const effectiveArrays = {};
|
|
34986
|
+
for (const [agentName, models] of Object.entries(modelArrayMap)) {
|
|
34987
|
+
effectiveArrays[agentName] = [...models];
|
|
34988
|
+
}
|
|
34989
|
+
for (const [agentName, chainModels] of Object.entries(fallbackChains)) {
|
|
34990
|
+
if (!chainModels || chainModels.length === 0)
|
|
34991
|
+
continue;
|
|
34992
|
+
if (!effectiveArrays[agentName]) {
|
|
34993
|
+
const entry = configAgent[agentName];
|
|
34994
|
+
const currentModel = typeof entry?.model === "string" ? entry.model : undefined;
|
|
34995
|
+
effectiveArrays[agentName] = currentModel ? [{ id: currentModel }] : [];
|
|
34996
|
+
}
|
|
34997
|
+
const seen = new Set(effectiveArrays[agentName].map((m) => m.id));
|
|
34998
|
+
for (const chainModel of chainModels) {
|
|
34999
|
+
if (!seen.has(chainModel)) {
|
|
35000
|
+
seen.add(chainModel);
|
|
35001
|
+
effectiveArrays[agentName].push({ id: chainModel });
|
|
33623
35002
|
}
|
|
33624
|
-
|
|
33625
|
-
|
|
33626
|
-
|
|
33627
|
-
|
|
35003
|
+
}
|
|
35004
|
+
}
|
|
35005
|
+
if (Object.keys(effectiveArrays).length > 0) {
|
|
35006
|
+
for (const [agentName, modelArray] of Object.entries(effectiveArrays)) {
|
|
35007
|
+
if (modelArray.length === 0)
|
|
35008
|
+
continue;
|
|
35009
|
+
const chosen = modelArray[0];
|
|
35010
|
+
const entry = configAgent[agentName];
|
|
35011
|
+
if (entry) {
|
|
35012
|
+
entry.model = chosen.id;
|
|
35013
|
+
if (chosen.variant) {
|
|
35014
|
+
entry.variant = chosen.variant;
|
|
35015
|
+
}
|
|
33628
35016
|
}
|
|
35017
|
+
log("[plugin] resolved model from array", {
|
|
35018
|
+
agent: agentName,
|
|
35019
|
+
model: chosen.id,
|
|
35020
|
+
variant: chosen.variant
|
|
35021
|
+
});
|
|
33629
35022
|
}
|
|
33630
35023
|
}
|
|
33631
35024
|
const configMcp = opencodeConfig.mcp;
|
|
@@ -33657,6 +35050,7 @@ var OhMyOpenCodeLite = async (ctx) => {
|
|
|
33657
35050
|
}
|
|
33658
35051
|
},
|
|
33659
35052
|
event: async (input) => {
|
|
35053
|
+
await foregroundFallback.handleEvent(input.event);
|
|
33660
35054
|
await autoUpdateChecker.event(input);
|
|
33661
35055
|
await tmuxSessionManager.onSessionCreated(input.event);
|
|
33662
35056
|
await backgroundManager.handleSessionStatus(input.event);
|
|
@@ -33669,7 +35063,7 @@ var OhMyOpenCodeLite = async (ctx) => {
|
|
|
33669
35063
|
"tool.execute.after": async (input, output) => {
|
|
33670
35064
|
await delegateTaskRetryHook["tool.execute.after"](input, output);
|
|
33671
35065
|
await jsonErrorRecoveryHook["tool.execute.after"](input, output);
|
|
33672
|
-
await
|
|
35066
|
+
await postFileToolNudgeHook["tool.execute.after"](input, output);
|
|
33673
35067
|
}
|
|
33674
35068
|
};
|
|
33675
35069
|
};
|