oh-my-opencode-slim 0.8.4 → 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 +2 -0
- 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/index.js +65 -3
- package/dist/cli/paths.d.ts +10 -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/schema.d.ts +24 -0
- package/dist/council/council-manager.d.ts +40 -0
- package/dist/council/index.d.ts +1 -0
- package/dist/hooks/index.d.ts +2 -2
- 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 +1004 -682
- package/dist/tools/council.d.ts +9 -0
- package/dist/tools/index.d.ts +1 -1
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/session.d.ts +59 -0
- package/oh-my-opencode-slim.schema.json +70 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3410,7 +3410,7 @@ var require_index_min = __commonJS((exports) => {
|
|
|
3410
3410
|
// node_modules/which/lib/index.js
|
|
3411
3411
|
var require_lib = __commonJS((exports, module) => {
|
|
3412
3412
|
var { isexe, sync: isexeSync } = require_index_min();
|
|
3413
|
-
var { join:
|
|
3413
|
+
var { join: join9, delimiter, sep, posix } = __require("path");
|
|
3414
3414
|
var isWindows = process.platform === "win32";
|
|
3415
3415
|
var rSlash = new RegExp(`[${posix.sep}${sep === posix.sep ? "" : sep}]`.replace(/(\\)/g, "\\$1"));
|
|
3416
3416
|
var rRel = new RegExp(`^\\.${rSlash.source}`);
|
|
@@ -3437,7 +3437,7 @@ var require_lib = __commonJS((exports, module) => {
|
|
|
3437
3437
|
var getPathPart = (raw, cmd) => {
|
|
3438
3438
|
const pathPart = /^".*"$/.test(raw) ? raw.slice(1, -1) : raw;
|
|
3439
3439
|
const prefix = !pathPart && rRel.test(cmd) ? cmd.slice(0, 2) : "";
|
|
3440
|
-
return prefix +
|
|
3440
|
+
return prefix + join9(pathPart, cmd);
|
|
3441
3441
|
};
|
|
3442
3442
|
var which = async (cmd, opt = {}) => {
|
|
3443
3443
|
const { pathEnv, pathExt, pathExtExe } = getPathInfo(cmd, opt);
|
|
@@ -3502,12 +3502,11 @@ function getCustomOpenCodeConfigDir() {
|
|
|
3502
3502
|
const configDir = process.env.OPENCODE_CONFIG_DIR?.trim();
|
|
3503
3503
|
return configDir || undefined;
|
|
3504
3504
|
}
|
|
3505
|
-
function
|
|
3506
|
-
const
|
|
3507
|
-
|
|
3508
|
-
return
|
|
3509
|
-
}
|
|
3510
|
-
return getDefaultOpenCodeConfigDir();
|
|
3505
|
+
function getConfigSearchDirs() {
|
|
3506
|
+
const dirs = [getCustomOpenCodeConfigDir(), getDefaultOpenCodeConfigDir()];
|
|
3507
|
+
return dirs.filter((dir, index) => {
|
|
3508
|
+
return Boolean(dir) && dirs.indexOf(dir) === index;
|
|
3509
|
+
});
|
|
3511
3510
|
}
|
|
3512
3511
|
function getOpenCodeConfigPaths() {
|
|
3513
3512
|
const configDir = getDefaultOpenCodeConfigDir();
|
|
@@ -3600,17 +3599,31 @@ var SUBAGENT_NAMES = [
|
|
|
3600
3599
|
"librarian",
|
|
3601
3600
|
"oracle",
|
|
3602
3601
|
"designer",
|
|
3603
|
-
"fixer"
|
|
3602
|
+
"fixer",
|
|
3603
|
+
"council",
|
|
3604
|
+
"councillor",
|
|
3605
|
+
"council-master"
|
|
3604
3606
|
];
|
|
3605
3607
|
var ORCHESTRATOR_NAME = "orchestrator";
|
|
3606
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
|
+
];
|
|
3607
3617
|
var SUBAGENT_DELEGATION_RULES = {
|
|
3608
|
-
orchestrator:
|
|
3618
|
+
orchestrator: ORCHESTRATABLE_AGENTS,
|
|
3609
3619
|
fixer: [],
|
|
3610
3620
|
designer: [],
|
|
3611
3621
|
explorer: [],
|
|
3612
3622
|
librarian: [],
|
|
3613
|
-
oracle: []
|
|
3623
|
+
oracle: [],
|
|
3624
|
+
council: [],
|
|
3625
|
+
councillor: [],
|
|
3626
|
+
"council-master": []
|
|
3614
3627
|
};
|
|
3615
3628
|
var DEFAULT_MODELS = {
|
|
3616
3629
|
orchestrator: undefined,
|
|
@@ -3618,55 +3631,21 @@ var DEFAULT_MODELS = {
|
|
|
3618
3631
|
librarian: "openai/gpt-5.4-mini",
|
|
3619
3632
|
explorer: "openai/gpt-5.4-mini",
|
|
3620
3633
|
designer: "openai/gpt-5.4-mini",
|
|
3621
|
-
fixer: "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"
|
|
3622
3638
|
};
|
|
3623
3639
|
var POLL_INTERVAL_BACKGROUND_MS = 2000;
|
|
3624
3640
|
var DEFAULT_TIMEOUT_MS = 2 * 60 * 1000;
|
|
3625
3641
|
var MAX_POLL_TIME_MS = 5 * 60 * 1000;
|
|
3626
3642
|
var FALLBACK_FAILOVER_TIMEOUT_MS = 15000;
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
|
|
3631
|
-
|
|
3632
|
-
var
|
|
3633
|
-
orchestrator: ["websearch"],
|
|
3634
|
-
designer: [],
|
|
3635
|
-
oracle: [],
|
|
3636
|
-
librarian: ["websearch", "context7", "grep_app"],
|
|
3637
|
-
explorer: [],
|
|
3638
|
-
fixer: []
|
|
3639
|
-
};
|
|
3640
|
-
function parseList(items, allAvailable) {
|
|
3641
|
-
if (!items || items.length === 0) {
|
|
3642
|
-
return [];
|
|
3643
|
-
}
|
|
3644
|
-
const allow = items.filter((i) => !i.startsWith("!"));
|
|
3645
|
-
const deny = items.filter((i) => i.startsWith("!")).map((i) => i.slice(1));
|
|
3646
|
-
if (deny.includes("*")) {
|
|
3647
|
-
return [];
|
|
3648
|
-
}
|
|
3649
|
-
if (allow.includes("*")) {
|
|
3650
|
-
return allAvailable.filter((item) => !deny.includes(item));
|
|
3651
|
-
}
|
|
3652
|
-
return allow.filter((item) => !deny.includes(item));
|
|
3653
|
-
}
|
|
3654
|
-
function getAgentMcpList(agentName, config) {
|
|
3655
|
-
const agentConfig = getAgentOverride(config, agentName);
|
|
3656
|
-
if (agentConfig?.mcps !== undefined) {
|
|
3657
|
-
return agentConfig.mcps;
|
|
3658
|
-
}
|
|
3659
|
-
const defaultMcps = DEFAULT_AGENT_MCPS[agentName];
|
|
3660
|
-
return defaultMcps ?? [];
|
|
3661
|
-
}
|
|
3662
|
-
|
|
3663
|
-
// src/cli/config-io.ts
|
|
3664
|
-
function stripJsonComments(json) {
|
|
3665
|
-
const commentPattern = /\\"|"(?:\\"|[^"])*"|(\/\/.*|\/\*[\s\S]*?\*\/)/g;
|
|
3666
|
-
const trailingCommaPattern = /\\"|"(?:\\"|[^"])*"|(,)(\s*[}\]])/g;
|
|
3667
|
-
return json.replace(commentPattern, (match, commentGroup) => commentGroup ? "" : match).replace(trailingCommaPattern, (match, comma, closing) => comma ? closing : match);
|
|
3668
|
-
}
|
|
3669
|
-
|
|
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;
|
|
3670
3649
|
// node_modules/zod/v4/classic/external.js
|
|
3671
3650
|
var exports_external = {};
|
|
3672
3651
|
__export(exports_external, {
|
|
@@ -17199,6 +17178,107 @@ function date4(params) {
|
|
|
17199
17178
|
|
|
17200
17179
|
// node_modules/zod/v4/classic/external.js
|
|
17201
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
|
+
|
|
17202
17282
|
// src/config/schema.ts
|
|
17203
17283
|
var ProviderModelIdSchema = exports_external.string().regex(/^[^/\s]+\/[^\s]+$/, "Expected provider/model format (provider/.../model)");
|
|
17204
17284
|
var ManualAgentPlanSchema = exports_external.object({
|
|
@@ -17287,7 +17367,8 @@ var PluginConfigSchema = exports_external.object({
|
|
|
17287
17367
|
disabled_mcps: exports_external.array(exports_external.string()).optional(),
|
|
17288
17368
|
tmux: TmuxConfigSchema.optional(),
|
|
17289
17369
|
background: BackgroundTaskConfigSchema.optional(),
|
|
17290
|
-
fallback: FailoverConfigSchema.optional()
|
|
17370
|
+
fallback: FailoverConfigSchema.optional(),
|
|
17371
|
+
council: CouncilConfigSchema.optional()
|
|
17291
17372
|
});
|
|
17292
17373
|
|
|
17293
17374
|
// src/config/loader.ts
|
|
@@ -17321,6 +17402,15 @@ function findConfigPath(basePath) {
|
|
|
17321
17402
|
}
|
|
17322
17403
|
return null;
|
|
17323
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
|
+
}
|
|
17324
17414
|
function deepMerge(base, override) {
|
|
17325
17415
|
if (!base)
|
|
17326
17416
|
return override;
|
|
@@ -17339,9 +17429,8 @@ function deepMerge(base, override) {
|
|
|
17339
17429
|
return result;
|
|
17340
17430
|
}
|
|
17341
17431
|
function loadPluginConfig(directory) {
|
|
17342
|
-
const
|
|
17432
|
+
const userConfigPath = findConfigPathInDirs(getConfigSearchDirs(), "oh-my-opencode-slim");
|
|
17343
17433
|
const projectConfigBasePath = path.join(directory, ".opencode", "oh-my-opencode-slim");
|
|
17344
|
-
const userConfigPath = findConfigPath(userConfigBasePath);
|
|
17345
17434
|
const projectConfigPath = findConfigPath(projectConfigBasePath);
|
|
17346
17435
|
let config2 = userConfigPath ? loadConfigFromPath(userConfigPath) ?? {} : {};
|
|
17347
17436
|
const projectConfig = projectConfigPath ? loadConfigFromPath(projectConfigPath) : null;
|
|
@@ -17372,8 +17461,10 @@ function loadPluginConfig(directory) {
|
|
|
17372
17461
|
}
|
|
17373
17462
|
function loadAgentPrompt(agentName, preset) {
|
|
17374
17463
|
const presetDirName = preset && /^[a-zA-Z0-9_-]+$/.test(preset) ? preset : undefined;
|
|
17375
|
-
const
|
|
17376
|
-
|
|
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
|
+
});
|
|
17377
17468
|
const result = {};
|
|
17378
17469
|
const readFirstPrompt = (fileName, errorPrefix) => {
|
|
17379
17470
|
for (const dir of promptSearchDirs) {
|
|
@@ -17398,6 +17489,418 @@ function getAgentOverride(config2, name) {
|
|
|
17398
17489
|
const overrides = config2?.agents ?? {};
|
|
17399
17490
|
return overrides[name] ?? overrides[Object.keys(AGENT_ALIASES).find((k) => AGENT_ALIASES[k] === name) ?? ""];
|
|
17400
17491
|
}
|
|
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(`
|
|
17546
|
+
|
|
17547
|
+
`);
|
|
17548
|
+
}
|
|
17549
|
+
|
|
17550
|
+
// src/agents/orchestrator.ts
|
|
17551
|
+
function resolvePrompt(base, customPrompt, customAppendPrompt) {
|
|
17552
|
+
if (customPrompt)
|
|
17553
|
+
return customPrompt;
|
|
17554
|
+
if (customAppendPrompt)
|
|
17555
|
+
return `${base}
|
|
17556
|
+
|
|
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>
|
|
17563
|
+
|
|
17564
|
+
<Agents>
|
|
17565
|
+
|
|
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
|
|
17572
|
+
|
|
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.
|
|
17580
|
+
|
|
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.
|
|
17589
|
+
|
|
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
|
+
|
|
17401
17904
|
// src/agents/designer.ts
|
|
17402
17905
|
var DESIGNER_PROMPT = `You are a Designer - a frontend UI/UX specialist who creates intentional, polished experiences.
|
|
17403
17906
|
|
|
@@ -17474,19 +17977,9 @@ var EXPLORER_PROMPT = `You are Explorer - a fast codebase navigation specialist.
|
|
|
17474
17977
|
|
|
17475
17978
|
**Role**: Quick contextual grep for codebases. Answer "Where is X?", "Find Y", "Which file has Z".
|
|
17476
17979
|
|
|
17477
|
-
**
|
|
17478
|
-
- **grep**: Fast regex content search (powered by ripgrep). Use for text patterns, function names, strings.
|
|
17479
|
-
Example: grep(pattern="function handleClick", include="*.ts")
|
|
17480
|
-
- **glob**: File pattern matching. Use to find files by name/extension.
|
|
17481
|
-
- **ast_grep_search**: AST-aware structural search (25 languages). Use for code patterns.
|
|
17482
|
-
- Meta-variables: $VAR (single node), $$$ (multiple nodes)
|
|
17483
|
-
- Patterns must be complete AST nodes
|
|
17484
|
-
- Example: ast_grep_search(pattern="console.log($MSG)", lang="typescript")
|
|
17485
|
-
- Example: ast_grep_search(pattern="async function $NAME($$$) { $$$ }", lang="javascript")
|
|
17486
|
-
|
|
17487
|
-
**When to use which**:
|
|
17980
|
+
**When to use which tools**:
|
|
17488
17981
|
- **Text/regex patterns** (strings, comments, variable names): grep
|
|
17489
|
-
- **Structural patterns** (function shapes, class structures): ast_grep_search
|
|
17982
|
+
- **Structural patterns** (function shapes, class structures): ast_grep_search
|
|
17490
17983
|
- **File discovery** (find by name/extension): glob
|
|
17491
17984
|
|
|
17492
17985
|
**Behavior**:
|
|
@@ -17671,165 +18164,6 @@ ${customAppendPrompt}`;
|
|
|
17671
18164
|
};
|
|
17672
18165
|
}
|
|
17673
18166
|
|
|
17674
|
-
// src/agents/orchestrator.ts
|
|
17675
|
-
var ORCHESTRATOR_PROMPT = `<Role>
|
|
17676
|
-
You are an AI coding orchestrator that optimizes for quality, speed, cost, and reliability by delegating to specialists when it provides net efficiency gains.
|
|
17677
|
-
</Role>
|
|
17678
|
-
|
|
17679
|
-
<Agents>
|
|
17680
|
-
|
|
17681
|
-
@explorer
|
|
17682
|
-
- Role: Parallel search specialist for discovering unknowns across the codebase
|
|
17683
|
-
- Capabilities: Glob, grep, AST queries to locate files, symbols, patterns
|
|
17684
|
-
- **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
|
|
17685
|
-
- **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
|
|
17686
|
-
|
|
17687
|
-
@librarian
|
|
17688
|
-
- Role: Authoritative source for current library docs and API references
|
|
17689
|
-
- Capabilities: Fetches latest official docs, examples, API signatures, version-specific behavior via grep_app MCP
|
|
17690
|
-
- **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
|
|
17691
|
-
- **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
|
|
17692
|
-
- **Rule of thumb:** "How does this library work?" \u2192 @librarian. "How does programming work?" \u2192 yourself.
|
|
17693
|
-
|
|
17694
|
-
@oracle
|
|
17695
|
-
- Role: Strategic advisor for high-stakes decisions and persistent problems
|
|
17696
|
-
- Capabilities: Deep architectural reasoning, system-level trade-offs, complex debugging
|
|
17697
|
-
- Tools/Constraints: Slow, expensive, high-quality\u2014use sparingly when thoroughness beats speed
|
|
17698
|
-
- **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
|
|
17699
|
-
- **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
|
|
17700
|
-
- **Rule of thumb:** Need senior architect review? \u2192 @oracle. Just do it and PR? \u2192 yourself.
|
|
17701
|
-
|
|
17702
|
-
@designer
|
|
17703
|
-
- Role: UI/UX specialist for intentional, polished experiences
|
|
17704
|
-
- Capabilities: Visual direction, interactions, responsive layouts, design systems with aesthetic intent
|
|
17705
|
-
- **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
|
|
17706
|
-
- **Don't delegate when:** Backend/logic with no visual \u2022 Quick prototypes where design doesn't matter yet
|
|
17707
|
-
- **Rule of thumb:** Users see it and polish matters? \u2192 @designer. Headless/functional? \u2192 yourself.
|
|
17708
|
-
|
|
17709
|
-
@fixer
|
|
17710
|
-
- Role: Fast, parallel execution specialist for well-defined tasks
|
|
17711
|
-
- Capabilities: Efficient implementation when spec and context are clear
|
|
17712
|
-
- Tools/Constraints: Execution-focused\u2014no research, no architectural decisions
|
|
17713
|
-
- **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
|
|
17714
|
-
- **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
|
|
17715
|
-
- **Parallelization:** 3+ independent tasks \u2192 spawn multiple @fixers. 1-2 simple tasks \u2192 do yourself.
|
|
17716
|
-
- **Rule of thumb:** Explaining > doing? \u2192 yourself. Can split to parallel streams? \u2192 multiple @fixers.
|
|
17717
|
-
|
|
17718
|
-
</Agents>
|
|
17719
|
-
|
|
17720
|
-
<Workflow>
|
|
17721
|
-
|
|
17722
|
-
## 1. Understand
|
|
17723
|
-
Parse request: explicit requirements + implicit needs.
|
|
17724
|
-
|
|
17725
|
-
## 2. Path Analysis
|
|
17726
|
-
Evaluate approach by: quality, speed, cost, reliability.
|
|
17727
|
-
Choose the path that optimizes all four.
|
|
17728
|
-
|
|
17729
|
-
## 3. Delegation Check
|
|
17730
|
-
**STOP. Review specialists before acting.**
|
|
17731
|
-
|
|
17732
|
-
Each specialist delivers 10x results in their domain:
|
|
17733
|
-
- @explorer \u2192 Parallel discovery when you need to find unknowns, not read knowns
|
|
17734
|
-
- @librarian \u2192 Complex/evolving APIs where docs prevent errors, not basic usage
|
|
17735
|
-
- @oracle \u2192 High-stakes decisions where wrong choice is costly, not routine calls
|
|
17736
|
-
- @designer \u2192 User-facing experiences where polish matters, not internal logic
|
|
17737
|
-
- @fixer \u2192 Parallel execution of clear specs, not explaining trivial changes
|
|
17738
|
-
|
|
17739
|
-
**Delegation efficiency:**
|
|
17740
|
-
- Reference paths/lines, don't paste files (\`src/app.ts:42\` not full contents)
|
|
17741
|
-
- Provide context summaries, let specialists read what they need
|
|
17742
|
-
- Brief user on delegation goal before each call
|
|
17743
|
-
- Skip delegation if overhead \u2265 doing it yourself
|
|
17744
|
-
|
|
17745
|
-
**Fixer parallelization:**
|
|
17746
|
-
- 3+ independent tasks? Spawn multiple @fixers simultaneously
|
|
17747
|
-
- 1-2 simple tasks? Do it yourself
|
|
17748
|
-
- Sequential dependencies? Handle serially or do yourself
|
|
17749
|
-
|
|
17750
|
-
## 4. Parallelize
|
|
17751
|
-
Can tasks run simultaneously?
|
|
17752
|
-
- Multiple @explorer searches across different domains?
|
|
17753
|
-
- @explorer + @librarian research in parallel?
|
|
17754
|
-
- Multiple @fixer instances for independent changes?
|
|
17755
|
-
|
|
17756
|
-
Balance: respect dependencies, avoid parallelizing what must be sequential.
|
|
17757
|
-
|
|
17758
|
-
## 5. Execute
|
|
17759
|
-
1. Break complex tasks into todos if needed
|
|
17760
|
-
2. Fire parallel research/implementation
|
|
17761
|
-
3. Delegate to specialists or do it yourself based on step 3
|
|
17762
|
-
4. Integrate results
|
|
17763
|
-
5. Adjust if needed
|
|
17764
|
-
|
|
17765
|
-
## 6. Verify
|
|
17766
|
-
- Run \`lsp_diagnostics\` for errors
|
|
17767
|
-
- Suggest \`simplify\` skill when applicable
|
|
17768
|
-
- Confirm specialists completed successfully
|
|
17769
|
-
- Verify solution meets requirements
|
|
17770
|
-
|
|
17771
|
-
## Agent Role Mapping
|
|
17772
|
-
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.
|
|
17773
|
-
When a workflow calls for a **reviewer** subagent: dispatch \`@oracle\`. Oracle has the depth for architectural review and access to code review skills.
|
|
17774
|
-
|
|
17775
|
-
</Workflow>
|
|
17776
|
-
|
|
17777
|
-
<Communication>
|
|
17778
|
-
|
|
17779
|
-
## Clarity Over Assumptions
|
|
17780
|
-
- If request is vague or has multiple valid interpretations, ask a targeted question before proceeding
|
|
17781
|
-
- Don't guess at critical details (file paths, API choices, architectural decisions)
|
|
17782
|
-
- Do make reasonable assumptions for minor details and state them briefly
|
|
17783
|
-
|
|
17784
|
-
## Concise Execution
|
|
17785
|
-
- Answer directly, no preamble
|
|
17786
|
-
- Don't summarize what you did unless asked
|
|
17787
|
-
- Don't explain code unless asked
|
|
17788
|
-
- One-word answers are fine when appropriate
|
|
17789
|
-
- Brief delegation notices: "Checking docs via @librarian..." not "I'm going to delegate to @librarian because..."
|
|
17790
|
-
|
|
17791
|
-
## No Flattery
|
|
17792
|
-
Never: "Great question!" "Excellent idea!" "Smart choice!" or any praise of user input.
|
|
17793
|
-
|
|
17794
|
-
## Honest Pushback
|
|
17795
|
-
When user's approach seems problematic:
|
|
17796
|
-
- State concern + alternative concisely
|
|
17797
|
-
- Ask if they want to proceed anyway
|
|
17798
|
-
- Don't lecture, don't blindly implement
|
|
17799
|
-
|
|
17800
|
-
## Example
|
|
17801
|
-
**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."
|
|
17802
|
-
|
|
17803
|
-
**Good:** "Checking Next.js App Router docs via @librarian..."
|
|
17804
|
-
[proceeds with implementation]
|
|
17805
|
-
|
|
17806
|
-
</Communication>
|
|
17807
|
-
`;
|
|
17808
|
-
function createOrchestratorAgent(model, customPrompt, customAppendPrompt) {
|
|
17809
|
-
let prompt = ORCHESTRATOR_PROMPT;
|
|
17810
|
-
if (customPrompt) {
|
|
17811
|
-
prompt = customPrompt;
|
|
17812
|
-
} else if (customAppendPrompt) {
|
|
17813
|
-
prompt = `${ORCHESTRATOR_PROMPT}
|
|
17814
|
-
|
|
17815
|
-
${customAppendPrompt}`;
|
|
17816
|
-
}
|
|
17817
|
-
const definition = {
|
|
17818
|
-
name: "orchestrator",
|
|
17819
|
-
description: "AI coding orchestrator that delegates tasks to specialist agents for optimal quality, speed, and cost",
|
|
17820
|
-
config: {
|
|
17821
|
-
temperature: 0.1,
|
|
17822
|
-
prompt
|
|
17823
|
-
}
|
|
17824
|
-
};
|
|
17825
|
-
if (Array.isArray(model)) {
|
|
17826
|
-
definition._modelArray = model.map((m) => typeof m === "string" ? { id: m } : m);
|
|
17827
|
-
} else if (typeof model === "string" && model) {
|
|
17828
|
-
definition.config.model = model;
|
|
17829
|
-
}
|
|
17830
|
-
return definition;
|
|
17831
|
-
}
|
|
17832
|
-
|
|
17833
18167
|
// src/agents/index.ts
|
|
17834
18168
|
function applyOverrides(agent, override) {
|
|
17835
18169
|
if (override.model) {
|
|
@@ -17848,9 +18182,10 @@ function applyOverrides(agent, override) {
|
|
|
17848
18182
|
function applyDefaultPermissions(agent, configuredSkills) {
|
|
17849
18183
|
const existing = agent.config.permission ?? {};
|
|
17850
18184
|
const skillPermissions = getSkillPermissionsForAgent(agent.name, configuredSkills);
|
|
18185
|
+
const questionPerm = existing.question === "deny" ? "deny" : "allow";
|
|
17851
18186
|
agent.config.permission = {
|
|
17852
18187
|
...existing,
|
|
17853
|
-
question:
|
|
18188
|
+
question: questionPerm,
|
|
17854
18189
|
skill: {
|
|
17855
18190
|
...typeof existing.skill === "object" ? existing.skill : {},
|
|
17856
18191
|
...skillPermissions
|
|
@@ -17865,7 +18200,10 @@ var SUBAGENT_FACTORIES = {
|
|
|
17865
18200
|
librarian: createLibrarianAgent,
|
|
17866
18201
|
oracle: createOracleAgent,
|
|
17867
18202
|
designer: createDesignerAgent,
|
|
17868
|
-
fixer: createFixerAgent
|
|
18203
|
+
fixer: createFixerAgent,
|
|
18204
|
+
council: createCouncilAgent,
|
|
18205
|
+
councillor: createCouncillorAgent,
|
|
18206
|
+
"council-master": createCouncilMasterAgent
|
|
17869
18207
|
};
|
|
17870
18208
|
function createAgents(config2) {
|
|
17871
18209
|
const getModelForAgent = (name) => {
|
|
@@ -17912,7 +18250,12 @@ function getAgentConfigs(config2) {
|
|
|
17912
18250
|
description: a.description,
|
|
17913
18251
|
mcps: getAgentMcpList(a.name, config2)
|
|
17914
18252
|
};
|
|
17915
|
-
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)) {
|
|
17916
18259
|
sdkConfig.mode = "subagent";
|
|
17917
18260
|
} else if (a.name === "orchestrator") {
|
|
17918
18261
|
sdkConfig.mode = "primary";
|
|
@@ -18300,17 +18643,48 @@ async function extractZip(archivePath, destDir) {
|
|
|
18300
18643
|
throw new Error(`zip extraction failed (exit ${exitCode}): ${stderr}`);
|
|
18301
18644
|
}
|
|
18302
18645
|
}
|
|
18303
|
-
// src/background/
|
|
18304
|
-
|
|
18305
|
-
|
|
18306
|
-
|
|
18307
|
-
|
|
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();
|
|
18308
18684
|
}
|
|
18309
|
-
return {
|
|
18310
|
-
providerID: model.slice(0, slashIndex),
|
|
18311
|
-
modelID: model.slice(slashIndex + 1)
|
|
18312
|
-
};
|
|
18313
18685
|
}
|
|
18686
|
+
|
|
18687
|
+
// src/background/background-manager.ts
|
|
18314
18688
|
function generateTaskId() {
|
|
18315
18689
|
return `bg_${Math.random().toString(36).substring(2, 10)}`;
|
|
18316
18690
|
}
|
|
@@ -18319,6 +18693,7 @@ class BackgroundTaskManager {
|
|
|
18319
18693
|
tasks = new Map;
|
|
18320
18694
|
tasksBySessionId = new Map;
|
|
18321
18695
|
agentBySessionId = new Map;
|
|
18696
|
+
depthTracker;
|
|
18322
18697
|
client;
|
|
18323
18698
|
directory;
|
|
18324
18699
|
tmuxEnabled;
|
|
@@ -18337,6 +18712,7 @@ class BackgroundTaskManager {
|
|
|
18337
18712
|
maxConcurrentStarts: 10
|
|
18338
18713
|
};
|
|
18339
18714
|
this.maxConcurrentStarts = this.backgroundConfig.maxConcurrentStarts;
|
|
18715
|
+
this.depthTracker = new SubagentDepthTracker;
|
|
18340
18716
|
}
|
|
18341
18717
|
getSubagentRules(agentName) {
|
|
18342
18718
|
return SUBAGENT_DELEGATION_RULES[agentName] ?? ["explorer"];
|
|
@@ -18407,30 +18783,7 @@ class BackgroundTaskManager {
|
|
|
18407
18783
|
seen.add(model);
|
|
18408
18784
|
chain.push(model);
|
|
18409
18785
|
}
|
|
18410
|
-
return chain;
|
|
18411
|
-
}
|
|
18412
|
-
async promptWithTimeout(args, timeoutMs) {
|
|
18413
|
-
if (timeoutMs <= 0) {
|
|
18414
|
-
await this.client.session.prompt(args);
|
|
18415
|
-
return;
|
|
18416
|
-
}
|
|
18417
|
-
const sessionId = args.path.id;
|
|
18418
|
-
let timer;
|
|
18419
|
-
try {
|
|
18420
|
-
const promptPromise = this.client.session.prompt(args);
|
|
18421
|
-
promptPromise.catch(() => {});
|
|
18422
|
-
await Promise.race([
|
|
18423
|
-
promptPromise,
|
|
18424
|
-
new Promise((_, reject) => {
|
|
18425
|
-
timer = setTimeout(() => {
|
|
18426
|
-
this.client.session.abort({ path: { id: sessionId } }).catch(() => {});
|
|
18427
|
-
reject(new Error(`Prompt timed out after ${timeoutMs}ms`));
|
|
18428
|
-
}, timeoutMs);
|
|
18429
|
-
})
|
|
18430
|
-
]);
|
|
18431
|
-
} finally {
|
|
18432
|
-
clearTimeout(timer);
|
|
18433
|
-
}
|
|
18786
|
+
return chain;
|
|
18434
18787
|
}
|
|
18435
18788
|
calculateToolPermissions(agentName) {
|
|
18436
18789
|
const allowedSubagents = this.getSubagentRules(agentName);
|
|
@@ -18447,20 +18800,31 @@ class BackgroundTaskManager {
|
|
|
18447
18800
|
return;
|
|
18448
18801
|
}
|
|
18449
18802
|
try {
|
|
18450
|
-
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({
|
|
18451
18814
|
body: {
|
|
18452
18815
|
parentID: task.parentSessionId,
|
|
18453
18816
|
title: `Background: ${task.description}`
|
|
18454
18817
|
},
|
|
18455
18818
|
query: { directory: this.directory }
|
|
18456
18819
|
});
|
|
18457
|
-
if (!
|
|
18820
|
+
if (!session2.data?.id) {
|
|
18458
18821
|
throw new Error("Failed to create background session");
|
|
18459
18822
|
}
|
|
18460
|
-
task.sessionId =
|
|
18461
|
-
this.tasksBySessionId.set(
|
|
18462
|
-
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);
|
|
18463
18826
|
task.status = "running";
|
|
18827
|
+
this.depthTracker.registerChild(task.parentSessionId, session2.data.id);
|
|
18464
18828
|
if (this.tmuxEnabled) {
|
|
18465
18829
|
await new Promise((r) => setTimeout(r, 500));
|
|
18466
18830
|
}
|
|
@@ -18479,7 +18843,7 @@ class BackgroundTaskManager {
|
|
|
18479
18843
|
const attemptModels = chain.length > 0 ? chain : [undefined];
|
|
18480
18844
|
const errors3 = [];
|
|
18481
18845
|
let succeeded = false;
|
|
18482
|
-
const sessionId =
|
|
18846
|
+
const sessionId = session2.data.id;
|
|
18483
18847
|
for (let i = 0;i < attemptModels.length; i++) {
|
|
18484
18848
|
const model = attemptModels[i];
|
|
18485
18849
|
const modelLabel = model ?? "default-model";
|
|
@@ -18498,7 +18862,7 @@ class BackgroundTaskManager {
|
|
|
18498
18862
|
if (i > 0) {
|
|
18499
18863
|
log(`[background-manager] fallback attempt ${i + 1}/${attemptModels.length}: ${modelLabel}`, { taskId: task.id });
|
|
18500
18864
|
}
|
|
18501
|
-
await this.
|
|
18865
|
+
await promptWithTimeout(this.client, {
|
|
18502
18866
|
path: { id: sessionId },
|
|
18503
18867
|
body,
|
|
18504
18868
|
query: promptQuery
|
|
@@ -18525,7 +18889,7 @@ class BackgroundTaskManager {
|
|
|
18525
18889
|
throw new Error(`All fallback models failed. ${errors3.join(" | ")}`);
|
|
18526
18890
|
}
|
|
18527
18891
|
log(`[background-manager] task started: ${task.id}`, {
|
|
18528
|
-
sessionId:
|
|
18892
|
+
sessionId: session2.data.id
|
|
18529
18893
|
});
|
|
18530
18894
|
} catch (error48) {
|
|
18531
18895
|
const errorMessage = error48 instanceof Error ? error48.message : String(error48);
|
|
@@ -18570,6 +18934,7 @@ class BackgroundTaskManager {
|
|
|
18570
18934
|
task.error = "Session deleted";
|
|
18571
18935
|
this.tasksBySessionId.delete(sessionId);
|
|
18572
18936
|
this.agentBySessionId.delete(sessionId);
|
|
18937
|
+
this.depthTracker.cleanup(sessionId);
|
|
18573
18938
|
const resolver = this.completionResolvers.get(taskId);
|
|
18574
18939
|
if (resolver) {
|
|
18575
18940
|
resolver(task);
|
|
@@ -18582,22 +18947,7 @@ class BackgroundTaskManager {
|
|
|
18582
18947
|
if (!task.sessionId)
|
|
18583
18948
|
return;
|
|
18584
18949
|
try {
|
|
18585
|
-
const
|
|
18586
|
-
path: { id: task.sessionId }
|
|
18587
|
-
});
|
|
18588
|
-
const messages = messagesResult.data ?? [];
|
|
18589
|
-
const assistantMessages = messages.filter((m) => m.info?.role === "assistant");
|
|
18590
|
-
const extractedContent = [];
|
|
18591
|
-
for (const message of assistantMessages) {
|
|
18592
|
-
for (const part of message.parts ?? []) {
|
|
18593
|
-
if ((part.type === "text" || part.type === "reasoning") && part.text) {
|
|
18594
|
-
extractedContent.push(part.text);
|
|
18595
|
-
}
|
|
18596
|
-
}
|
|
18597
|
-
}
|
|
18598
|
-
const responseText = extractedContent.filter((t) => t.length > 0).join(`
|
|
18599
|
-
|
|
18600
|
-
`);
|
|
18950
|
+
const responseText = await extractSessionResult(this.client, task.sessionId);
|
|
18601
18951
|
if (responseText) {
|
|
18602
18952
|
this.completeTask(task, "completed", responseText);
|
|
18603
18953
|
} else {
|
|
@@ -18713,6 +19063,10 @@ class BackgroundTaskManager {
|
|
|
18713
19063
|
this.tasks.clear();
|
|
18714
19064
|
this.tasksBySessionId.clear();
|
|
18715
19065
|
this.agentBySessionId.clear();
|
|
19066
|
+
this.depthTracker.cleanupAll();
|
|
19067
|
+
}
|
|
19068
|
+
getDepthTracker() {
|
|
19069
|
+
return this.depthTracker;
|
|
18716
19070
|
}
|
|
18717
19071
|
}
|
|
18718
19072
|
// src/background/tmux-session-manager.ts
|
|
@@ -18882,6 +19236,267 @@ class TmuxSessionManager {
|
|
|
18882
19236
|
log("[tmux-session-manager] cleanup complete");
|
|
18883
19237
|
}
|
|
18884
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
|
+
};
|
|
19460
|
+
});
|
|
19461
|
+
}
|
|
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
|
+
}
|
|
19493
|
+
}
|
|
19494
|
+
return {
|
|
19495
|
+
success: false,
|
|
19496
|
+
error: `All master models failed. ${errors3.join(" | ")}`
|
|
19497
|
+
};
|
|
19498
|
+
}
|
|
19499
|
+
}
|
|
18885
19500
|
// src/hooks/auto-update-checker/cache.ts
|
|
18886
19501
|
import * as fs3 from "fs";
|
|
18887
19502
|
import * as path4 from "path";
|
|
@@ -19558,7 +20173,10 @@ class ForegroundFallbackManager {
|
|
|
19558
20173
|
const agentName = this.sessionAgent.get(sessionID);
|
|
19559
20174
|
const chain = this.resolveChain(agentName, currentModel);
|
|
19560
20175
|
if (!chain.length) {
|
|
19561
|
-
log("[foreground-fallback] no chain configured", {
|
|
20176
|
+
log("[foreground-fallback] no chain configured", {
|
|
20177
|
+
sessionID,
|
|
20178
|
+
agentName
|
|
20179
|
+
});
|
|
19562
20180
|
return;
|
|
19563
20181
|
}
|
|
19564
20182
|
if (!this.sessionTried.has(sessionID)) {
|
|
@@ -19647,7 +20265,6 @@ var JSON_ERROR_TOOL_EXCLUDE_LIST = [
|
|
|
19647
20265
|
"bash",
|
|
19648
20266
|
"read",
|
|
19649
20267
|
"glob",
|
|
19650
|
-
"grep",
|
|
19651
20268
|
"webfetch",
|
|
19652
20269
|
"grep_app_searchgithub",
|
|
19653
20270
|
"websearch_web_search_exa"
|
|
@@ -19695,9 +20312,7 @@ ${JSON_ERROR_REMINDER}`;
|
|
|
19695
20312
|
};
|
|
19696
20313
|
}
|
|
19697
20314
|
// src/hooks/phase-reminder/index.ts
|
|
19698
|
-
var PHASE_REMINDER = `<reminder
|
|
19699
|
-
Understand \u2192 find the best path (delegate based on rules and parallelize independent work) \u2192 execute \u2192 verify.
|
|
19700
|
-
If delegating, launch the specialist in the same turn you mention it.</reminder>`;
|
|
20315
|
+
var PHASE_REMINDER = `<reminder>${PHASE_REMINDER_TEXT}</reminder>`;
|
|
19701
20316
|
function createPhaseReminderHook() {
|
|
19702
20317
|
return {
|
|
19703
20318
|
"experimental.chat.messages.transform": async (_input, output) => {
|
|
@@ -19736,15 +20351,15 @@ ${originalText}`;
|
|
|
19736
20351
|
}
|
|
19737
20352
|
};
|
|
19738
20353
|
}
|
|
19739
|
-
// src/hooks/post-
|
|
20354
|
+
// src/hooks/post-file-tool-nudge/index.ts
|
|
19740
20355
|
var NUDGE = `
|
|
19741
20356
|
|
|
19742
20357
|
---
|
|
19743
|
-
|
|
19744
|
-
function
|
|
20358
|
+
${PHASE_REMINDER_TEXT}`;
|
|
20359
|
+
function createPostFileToolNudgeHook() {
|
|
19745
20360
|
return {
|
|
19746
20361
|
"tool.execute.after": async (input, output) => {
|
|
19747
|
-
if (input.tool !== "Read" && input.tool !== "read") {
|
|
20362
|
+
if (input.tool !== "Read" && input.tool !== "read" && input.tool !== "Write" && input.tool !== "write") {
|
|
19748
20363
|
return;
|
|
19749
20364
|
}
|
|
19750
20365
|
output.output = output.output + NUDGE;
|
|
@@ -32764,340 +33379,76 @@ Only cancels pending/starting/running tasks.`,
|
|
|
32764
33379
|
});
|
|
32765
33380
|
return { background_task, background_output, background_cancel };
|
|
32766
33381
|
}
|
|
32767
|
-
// src/tools/
|
|
32768
|
-
var
|
|
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.
|
|
32769
33393
|
|
|
32770
|
-
|
|
32771
|
-
import { spawnSync as spawnSync2 } from "child_process";
|
|
32772
|
-
import { existsSync as existsSync8 } from "fs";
|
|
32773
|
-
import { dirname as dirname4, join as join10 } from "path";
|
|
33394
|
+
Sends the prompt to multiple models (councillors) in parallel, then a council master synthesizes the best response.
|
|
32774
33395
|
|
|
32775
|
-
|
|
32776
|
-
|
|
32777
|
-
|
|
32778
|
-
|
|
32779
|
-
|
|
32780
|
-
|
|
32781
|
-
|
|
32782
|
-
|
|
32783
|
-
|
|
32784
|
-
|
|
32785
|
-
|
|
32786
|
-
|
|
32787
|
-
}
|
|
32788
|
-
|
|
32789
|
-
|
|
32790
|
-
|
|
32791
|
-
|
|
32792
|
-
|
|
32793
|
-
|
|
32794
|
-
|
|
32795
|
-
|
|
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}
|
|
32796
33420
|
|
|
32797
|
-
|
|
32798
|
-
|
|
32799
|
-
|
|
32800
|
-
|
|
32801
|
-
|
|
32802
|
-
|
|
32803
|
-
|
|
32804
|
-
|
|
32805
|
-
|
|
32806
|
-
|
|
32807
|
-
} catch {}
|
|
32808
|
-
return null;
|
|
32809
|
-
}
|
|
32810
|
-
function getDataDir() {
|
|
32811
|
-
if (process.platform === "win32") {
|
|
32812
|
-
return process.env.LOCALAPPDATA || process.env.APPDATA || join10(process.env.USERPROFILE || ".", "AppData", "Local");
|
|
32813
|
-
}
|
|
32814
|
-
return process.env.XDG_DATA_HOME || join10(process.env.HOME || ".", ".local", "share");
|
|
32815
|
-
}
|
|
32816
|
-
function getOpenCodeBundledRg() {
|
|
32817
|
-
const execPath = process.execPath;
|
|
32818
|
-
const execDir = dirname4(execPath);
|
|
32819
|
-
const isWindows = process.platform === "win32";
|
|
32820
|
-
const rgName = isWindows ? "rg.exe" : "rg";
|
|
32821
|
-
const candidates = [
|
|
32822
|
-
join10(getDataDir(), "opencode", "bin", rgName),
|
|
32823
|
-
join10(execDir, rgName),
|
|
32824
|
-
join10(execDir, "bin", rgName),
|
|
32825
|
-
join10(execDir, "..", "bin", rgName),
|
|
32826
|
-
join10(execDir, "..", "libexec", rgName)
|
|
32827
|
-
];
|
|
32828
|
-
for (const candidate of candidates) {
|
|
32829
|
-
if (existsSync8(candidate)) {
|
|
32830
|
-
return candidate;
|
|
32831
|
-
}
|
|
32832
|
-
}
|
|
32833
|
-
return null;
|
|
32834
|
-
}
|
|
32835
|
-
function resolveGrepCli() {
|
|
32836
|
-
if (cachedCli)
|
|
32837
|
-
return cachedCli;
|
|
32838
|
-
const bundledRg = getOpenCodeBundledRg();
|
|
32839
|
-
if (bundledRg) {
|
|
32840
|
-
cachedCli = { path: bundledRg, backend: "rg" };
|
|
32841
|
-
return cachedCli;
|
|
32842
|
-
}
|
|
32843
|
-
const systemRg = findExecutable("rg");
|
|
32844
|
-
if (systemRg) {
|
|
32845
|
-
cachedCli = { path: systemRg, backend: "rg" };
|
|
32846
|
-
return cachedCli;
|
|
32847
|
-
}
|
|
32848
|
-
const installedRg = getInstalledRipgrepPath();
|
|
32849
|
-
if (installedRg) {
|
|
32850
|
-
cachedCli = { path: installedRg, backend: "rg" };
|
|
32851
|
-
return cachedCli;
|
|
32852
|
-
}
|
|
32853
|
-
const grep = findExecutable("grep");
|
|
32854
|
-
if (grep) {
|
|
32855
|
-
cachedCli = { path: grep, backend: "grep" };
|
|
32856
|
-
return cachedCli;
|
|
32857
|
-
}
|
|
32858
|
-
cachedCli = { path: "rg", backend: "rg" };
|
|
32859
|
-
return cachedCli;
|
|
32860
|
-
}
|
|
32861
|
-
var DEFAULT_MAX_DEPTH = 20;
|
|
32862
|
-
var DEFAULT_MAX_FILESIZE = "10M";
|
|
32863
|
-
var DEFAULT_MAX_COUNT = 500;
|
|
32864
|
-
var DEFAULT_MAX_COLUMNS = 1000;
|
|
32865
|
-
var DEFAULT_TIMEOUT_MS3 = 300000;
|
|
32866
|
-
var DEFAULT_MAX_OUTPUT_BYTES2 = 10 * 1024 * 1024;
|
|
32867
|
-
var RG_SAFETY_FLAGS = [
|
|
32868
|
-
"--no-follow",
|
|
32869
|
-
"--color=never",
|
|
32870
|
-
"--no-heading",
|
|
32871
|
-
"--line-number",
|
|
32872
|
-
"--with-filename"
|
|
32873
|
-
];
|
|
32874
|
-
var GREP_SAFETY_FLAGS = ["-n", "-H", "--color=never"];
|
|
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 += `
|
|
32875
33431
|
|
|
32876
|
-
|
|
32877
|
-
|
|
32878
|
-
|
|
32879
|
-
...RG_SAFETY_FLAGS,
|
|
32880
|
-
`--max-depth=${Math.min(options.maxDepth ?? DEFAULT_MAX_DEPTH, DEFAULT_MAX_DEPTH)}`,
|
|
32881
|
-
`--max-filesize=${options.maxFilesize ?? DEFAULT_MAX_FILESIZE}`,
|
|
32882
|
-
`--max-count=${Math.min(options.maxCount ?? DEFAULT_MAX_COUNT, DEFAULT_MAX_COUNT)}`,
|
|
32883
|
-
`--max-columns=${Math.min(options.maxColumns ?? DEFAULT_MAX_COLUMNS, DEFAULT_MAX_COLUMNS)}`
|
|
32884
|
-
];
|
|
32885
|
-
if (options.context !== undefined && options.context > 0) {
|
|
32886
|
-
args.push(`-C${Math.min(options.context, 10)}`);
|
|
32887
|
-
}
|
|
32888
|
-
if (options.caseSensitive) {
|
|
32889
|
-
args.push("--case-sensitive");
|
|
32890
|
-
} else {
|
|
32891
|
-
args.push("-i");
|
|
32892
|
-
}
|
|
32893
|
-
if (options.wholeWord)
|
|
32894
|
-
args.push("-w");
|
|
32895
|
-
if (options.fixedStrings)
|
|
32896
|
-
args.push("-F");
|
|
32897
|
-
if (options.multiline)
|
|
32898
|
-
args.push("-U");
|
|
32899
|
-
if (options.hidden)
|
|
32900
|
-
args.push("--hidden");
|
|
32901
|
-
if (options.noIgnore)
|
|
32902
|
-
args.push("--no-ignore");
|
|
32903
|
-
if (options.fileType?.length) {
|
|
32904
|
-
for (const type of options.fileType) {
|
|
32905
|
-
args.push(`--type=${type}`);
|
|
32906
|
-
}
|
|
32907
|
-
}
|
|
32908
|
-
if (options.globs) {
|
|
32909
|
-
for (const glob of options.globs) {
|
|
32910
|
-
args.push(`--glob=${glob}`);
|
|
32911
|
-
}
|
|
32912
|
-
}
|
|
32913
|
-
if (options.excludeGlobs) {
|
|
32914
|
-
for (const glob of options.excludeGlobs) {
|
|
32915
|
-
args.push(`--glob=!${glob}`);
|
|
32916
|
-
}
|
|
32917
|
-
}
|
|
32918
|
-
return args;
|
|
32919
|
-
}
|
|
32920
|
-
function buildGrepArgs(options) {
|
|
32921
|
-
const args = [...GREP_SAFETY_FLAGS, "-r"];
|
|
32922
|
-
if (options.context !== undefined && options.context > 0) {
|
|
32923
|
-
args.push(`-C${Math.min(options.context, 10)}`);
|
|
32924
|
-
}
|
|
32925
|
-
if (!options.caseSensitive)
|
|
32926
|
-
args.push("-i");
|
|
32927
|
-
if (options.wholeWord)
|
|
32928
|
-
args.push("-w");
|
|
32929
|
-
if (options.fixedStrings)
|
|
32930
|
-
args.push("-F");
|
|
32931
|
-
if (options.globs?.length) {
|
|
32932
|
-
for (const glob of options.globs) {
|
|
32933
|
-
args.push(`--include=${glob}`);
|
|
32934
|
-
}
|
|
32935
|
-
}
|
|
32936
|
-
if (options.excludeGlobs?.length) {
|
|
32937
|
-
for (const glob of options.excludeGlobs) {
|
|
32938
|
-
args.push(`--exclude=${glob}`);
|
|
32939
|
-
}
|
|
32940
|
-
}
|
|
32941
|
-
args.push("--exclude-dir=.git", "--exclude-dir=node_modules");
|
|
32942
|
-
return args;
|
|
32943
|
-
}
|
|
32944
|
-
function buildArgs(options, backend) {
|
|
32945
|
-
return backend === "rg" ? buildRgArgs(options) : buildGrepArgs(options);
|
|
32946
|
-
}
|
|
32947
|
-
function parseOutput(output) {
|
|
32948
|
-
if (!output.trim())
|
|
32949
|
-
return [];
|
|
32950
|
-
const matches = [];
|
|
32951
|
-
const lines = output.trim().split(/\r?\n/);
|
|
32952
|
-
for (const line of lines) {
|
|
32953
|
-
if (!line.trim())
|
|
32954
|
-
continue;
|
|
32955
|
-
const match = line.match(/^(.+?):(\d+):(.*)$/);
|
|
32956
|
-
if (match) {
|
|
32957
|
-
matches.push({
|
|
32958
|
-
file: match[1],
|
|
32959
|
-
line: parseInt(match[2], 10),
|
|
32960
|
-
text: match[3]
|
|
32961
|
-
});
|
|
33432
|
+
---
|
|
33433
|
+
*Council: ${completed}/${total} councillors responded (${composition})*`;
|
|
33434
|
+
return output;
|
|
32962
33435
|
}
|
|
32963
|
-
}
|
|
32964
|
-
return matches;
|
|
32965
|
-
}
|
|
32966
|
-
async function runRg(options) {
|
|
32967
|
-
const cli = resolveGrepCli();
|
|
32968
|
-
const args = buildArgs(options, cli.backend);
|
|
32969
|
-
const timeout = Math.min(options.timeout ?? DEFAULT_TIMEOUT_MS3, DEFAULT_TIMEOUT_MS3);
|
|
32970
|
-
if (cli.backend === "rg") {
|
|
32971
|
-
args.push("--", options.pattern);
|
|
32972
|
-
} else {
|
|
32973
|
-
args.push("-e", options.pattern);
|
|
32974
|
-
}
|
|
32975
|
-
const paths2 = options.paths?.length ? options.paths : ["."];
|
|
32976
|
-
args.push(...paths2);
|
|
32977
|
-
const proc = spawn4([cli.path, ...args], {
|
|
32978
|
-
stdout: "pipe",
|
|
32979
|
-
stderr: "pipe"
|
|
32980
33436
|
});
|
|
32981
|
-
|
|
32982
|
-
const id = setTimeout(() => {
|
|
32983
|
-
proc.kill();
|
|
32984
|
-
reject(new Error(`Search timeout after ${timeout}ms`));
|
|
32985
|
-
}, timeout);
|
|
32986
|
-
proc.exited.then(() => clearTimeout(id));
|
|
32987
|
-
});
|
|
32988
|
-
try {
|
|
32989
|
-
const stdout = await Promise.race([
|
|
32990
|
-
new Response(proc.stdout).text(),
|
|
32991
|
-
timeoutPromise
|
|
32992
|
-
]);
|
|
32993
|
-
const stderr = await new Response(proc.stderr).text();
|
|
32994
|
-
const exitCode = await proc.exited;
|
|
32995
|
-
const truncated = stdout.length >= DEFAULT_MAX_OUTPUT_BYTES2;
|
|
32996
|
-
const outputToProcess = truncated ? stdout.substring(0, DEFAULT_MAX_OUTPUT_BYTES2) : stdout;
|
|
32997
|
-
if (exitCode > 1 && stderr.trim()) {
|
|
32998
|
-
return {
|
|
32999
|
-
matches: [],
|
|
33000
|
-
totalMatches: 0,
|
|
33001
|
-
filesSearched: 0,
|
|
33002
|
-
truncated: false,
|
|
33003
|
-
error: stderr.trim()
|
|
33004
|
-
};
|
|
33005
|
-
}
|
|
33006
|
-
const matches = parseOutput(outputToProcess);
|
|
33007
|
-
const filesSearched = new Set(matches.map((m) => m.file)).size;
|
|
33008
|
-
return {
|
|
33009
|
-
matches,
|
|
33010
|
-
totalMatches: matches.length,
|
|
33011
|
-
filesSearched,
|
|
33012
|
-
truncated
|
|
33013
|
-
};
|
|
33014
|
-
} catch (e) {
|
|
33015
|
-
return {
|
|
33016
|
-
matches: [],
|
|
33017
|
-
totalMatches: 0,
|
|
33018
|
-
filesSearched: 0,
|
|
33019
|
-
truncated: false,
|
|
33020
|
-
error: e instanceof Error ? e.message : String(e)
|
|
33021
|
-
};
|
|
33022
|
-
}
|
|
33023
|
-
}
|
|
33024
|
-
// src/tools/grep/utils.ts
|
|
33025
|
-
function formatGrepResult(result) {
|
|
33026
|
-
if (result.error) {
|
|
33027
|
-
return `Error: ${result.error}`;
|
|
33028
|
-
}
|
|
33029
|
-
if (result.matches.length === 0) {
|
|
33030
|
-
return "No matches found.";
|
|
33031
|
-
}
|
|
33032
|
-
const lines = [];
|
|
33033
|
-
const byFile = new Map;
|
|
33034
|
-
for (const match of result.matches) {
|
|
33035
|
-
const existing = byFile.get(match.file) || [];
|
|
33036
|
-
existing.push({ line: match.line, text: match.text });
|
|
33037
|
-
byFile.set(match.file, existing);
|
|
33038
|
-
}
|
|
33039
|
-
for (const [file3, matches] of byFile) {
|
|
33040
|
-
lines.push(`
|
|
33041
|
-
${file3}:`);
|
|
33042
|
-
for (const match of matches) {
|
|
33043
|
-
lines.push(` ${match.line}: ${match.text}`);
|
|
33044
|
-
}
|
|
33045
|
-
}
|
|
33046
|
-
const summary = `Found ${result.totalMatches} matches in ${result.filesSearched} files`;
|
|
33047
|
-
if (result.truncated) {
|
|
33048
|
-
lines.push(`
|
|
33049
|
-
${summary} (output truncated)`);
|
|
33050
|
-
} else {
|
|
33051
|
-
lines.push(`
|
|
33052
|
-
${summary}`);
|
|
33053
|
-
}
|
|
33054
|
-
return lines.join(`
|
|
33055
|
-
`);
|
|
33437
|
+
return { council_session };
|
|
33056
33438
|
}
|
|
33057
|
-
|
|
33058
|
-
// src/tools/grep/tools.ts
|
|
33059
|
-
var grep = tool({
|
|
33060
|
-
description: "Fast content search tool with safety limits (60s timeout, 10MB output). " + "Searches file contents using regular expressions. " + 'Supports full regex syntax (eg. "log.*Error", "function\\s+\\w+", etc.). ' + 'Filter files by pattern with the include parameter (e.g. "*.js", "*.{ts,tsx}"). ' + "Returns file paths with matches sorted by modification time.",
|
|
33061
|
-
args: {
|
|
33062
|
-
pattern: tool.schema.string().describe("The regex pattern to search for in file contents"),
|
|
33063
|
-
include: tool.schema.string().optional().describe('File pattern to include in the search (e.g. "*.js", "*.{ts,tsx}")'),
|
|
33064
|
-
path: tool.schema.string().optional().describe("The directory to search in. Defaults to the current working directory."),
|
|
33065
|
-
caseSensitive: tool.schema.boolean().optional().default(false).describe("Perform case-sensitive search (default: false)"),
|
|
33066
|
-
wholeWord: tool.schema.boolean().optional().default(false).describe("Match whole words only (default: false)"),
|
|
33067
|
-
fixedStrings: tool.schema.boolean().optional().default(false).describe("Treat pattern as literal string (default: false)")
|
|
33068
|
-
},
|
|
33069
|
-
execute: async (args) => {
|
|
33070
|
-
try {
|
|
33071
|
-
const globs = args.include ? [args.include] : undefined;
|
|
33072
|
-
const paths2 = args.path ? [args.path] : undefined;
|
|
33073
|
-
const result = await runRg({
|
|
33074
|
-
pattern: args.pattern,
|
|
33075
|
-
paths: paths2,
|
|
33076
|
-
globs,
|
|
33077
|
-
context: 0,
|
|
33078
|
-
caseSensitive: args.caseSensitive ?? false,
|
|
33079
|
-
wholeWord: args.wholeWord ?? false,
|
|
33080
|
-
fixedStrings: args.fixedStrings ?? false
|
|
33081
|
-
});
|
|
33082
|
-
return formatGrepResult(result);
|
|
33083
|
-
} catch (e) {
|
|
33084
|
-
return `Error: ${e instanceof Error ? e.message : String(e)}`;
|
|
33085
|
-
}
|
|
33086
|
-
}
|
|
33087
|
-
});
|
|
33088
33439
|
// src/tools/lsp/client.ts
|
|
33089
33440
|
var import_node = __toESM(require_main(), 1);
|
|
33090
33441
|
import { readFileSync as readFileSync4 } from "fs";
|
|
33091
33442
|
import { extname, resolve as resolve2 } from "path";
|
|
33092
33443
|
import { Readable, Writable } from "stream";
|
|
33093
33444
|
import { pathToFileURL } from "url";
|
|
33094
|
-
var {spawn:
|
|
33445
|
+
var {spawn: spawn4 } = globalThis.Bun;
|
|
33095
33446
|
|
|
33096
33447
|
// src/tools/lsp/config.ts
|
|
33097
33448
|
var import_which = __toESM(require_lib(), 1);
|
|
33098
|
-
import { existsSync as
|
|
33449
|
+
import { existsSync as existsSync8 } from "fs";
|
|
33099
33450
|
import { homedir as homedir4 } from "os";
|
|
33100
|
-
import { join as
|
|
33451
|
+
import { join as join9 } from "path";
|
|
33101
33452
|
|
|
33102
33453
|
// src/tools/lsp/config-store.ts
|
|
33103
33454
|
var userConfig = new Map;
|
|
@@ -33127,8 +33478,8 @@ function hasUserLspConfig() {
|
|
|
33127
33478
|
}
|
|
33128
33479
|
|
|
33129
33480
|
// src/tools/lsp/constants.ts
|
|
33130
|
-
import { existsSync as
|
|
33131
|
-
import { dirname as
|
|
33481
|
+
import { existsSync as existsSync7, readdirSync, statSync as statSync3 } from "fs";
|
|
33482
|
+
import { dirname as dirname4, resolve } from "path";
|
|
33132
33483
|
var SEVERITY_MAP = {
|
|
33133
33484
|
1: "error",
|
|
33134
33485
|
2: "warning",
|
|
@@ -33148,10 +33499,10 @@ function* walkUpDirectories(start, stop) {
|
|
|
33148
33499
|
let dir = resolve(start);
|
|
33149
33500
|
try {
|
|
33150
33501
|
if (!statSync3(dir).isDirectory()) {
|
|
33151
|
-
dir =
|
|
33502
|
+
dir = dirname4(dir);
|
|
33152
33503
|
}
|
|
33153
33504
|
} catch {
|
|
33154
|
-
dir =
|
|
33505
|
+
dir = dirname4(dir);
|
|
33155
33506
|
}
|
|
33156
33507
|
let prevDir = "";
|
|
33157
33508
|
while (dir !== prevDir && dir !== "/") {
|
|
@@ -33159,7 +33510,7 @@ function* walkUpDirectories(start, stop) {
|
|
|
33159
33510
|
prevDir = dir;
|
|
33160
33511
|
if (dir === stop)
|
|
33161
33512
|
break;
|
|
33162
|
-
dir =
|
|
33513
|
+
dir = dirname4(dir);
|
|
33163
33514
|
}
|
|
33164
33515
|
}
|
|
33165
33516
|
function NearestRoot(includePatterns, excludePatterns) {
|
|
@@ -33168,7 +33519,7 @@ function NearestRoot(includePatterns, excludePatterns) {
|
|
|
33168
33519
|
if (excludePatterns) {
|
|
33169
33520
|
for (const dir of walkUpDirectories(file3, cwd)) {
|
|
33170
33521
|
for (const pattern of excludePatterns) {
|
|
33171
|
-
if (
|
|
33522
|
+
if (existsSync7(`${dir}/${pattern}`)) {
|
|
33172
33523
|
return;
|
|
33173
33524
|
}
|
|
33174
33525
|
}
|
|
@@ -33178,13 +33529,13 @@ function NearestRoot(includePatterns, excludePatterns) {
|
|
|
33178
33529
|
for (const pattern of includePatterns) {
|
|
33179
33530
|
if (pattern.includes("*")) {
|
|
33180
33531
|
try {
|
|
33181
|
-
const entries =
|
|
33532
|
+
const entries = readdirSync(dir);
|
|
33182
33533
|
const regex = new RegExp(`^${pattern.replace(/\./g, "\\.").replace(/\*/g, ".*")}$`);
|
|
33183
33534
|
if (entries.some((entry) => regex.test(entry))) {
|
|
33184
33535
|
return dir;
|
|
33185
33536
|
}
|
|
33186
33537
|
} catch {}
|
|
33187
|
-
} else if (
|
|
33538
|
+
} else if (existsSync7(`${dir}/${pattern}`)) {
|
|
33188
33539
|
return dir;
|
|
33189
33540
|
}
|
|
33190
33541
|
}
|
|
@@ -33732,11 +34083,11 @@ function isServerInstalled(command) {
|
|
|
33732
34083
|
return false;
|
|
33733
34084
|
const cmd = command[0];
|
|
33734
34085
|
if (cmd.includes("/") || cmd.includes("\\")) {
|
|
33735
|
-
return
|
|
34086
|
+
return existsSync8(cmd);
|
|
33736
34087
|
}
|
|
33737
34088
|
const isWindows = process.platform === "win32";
|
|
33738
34089
|
const ext = isWindows ? ".exe" : "";
|
|
33739
|
-
const opencodeBin =
|
|
34090
|
+
const opencodeBin = join9(homedir4(), ".config", "opencode", "bin");
|
|
33740
34091
|
const searchPath = (process.env.PATH ?? "") + (isWindows ? ";" : ":") + opencodeBin;
|
|
33741
34092
|
const result = import_which.default.sync(cmd, {
|
|
33742
34093
|
path: searchPath,
|
|
@@ -33747,8 +34098,8 @@ function isServerInstalled(command) {
|
|
|
33747
34098
|
return true;
|
|
33748
34099
|
}
|
|
33749
34100
|
const cwd = process.cwd();
|
|
33750
|
-
const localBin =
|
|
33751
|
-
if (
|
|
34101
|
+
const localBin = join9(cwd, "node_modules", ".bin", cmd);
|
|
34102
|
+
if (existsSync8(localBin) || existsSync8(localBin + ext)) {
|
|
33752
34103
|
return true;
|
|
33753
34104
|
}
|
|
33754
34105
|
return false;
|
|
@@ -33929,7 +34280,7 @@ class LSPClient {
|
|
|
33929
34280
|
command: this.server.command.join(" "),
|
|
33930
34281
|
root: this.root
|
|
33931
34282
|
});
|
|
33932
|
-
this.proc =
|
|
34283
|
+
this.proc = spawn4(this.server.command, {
|
|
33933
34284
|
stdin: "pipe",
|
|
33934
34285
|
stdout: "pipe",
|
|
33935
34286
|
stderr: "pipe",
|
|
@@ -34161,19 +34512,19 @@ stderr: ${stderr}` : ""));
|
|
|
34161
34512
|
}
|
|
34162
34513
|
// src/tools/lsp/utils.ts
|
|
34163
34514
|
import {
|
|
34164
|
-
existsSync as
|
|
34515
|
+
existsSync as existsSync9,
|
|
34165
34516
|
readFileSync as readFileSync5,
|
|
34166
34517
|
statSync as statSync4,
|
|
34167
|
-
unlinkSync as
|
|
34518
|
+
unlinkSync as unlinkSync2,
|
|
34168
34519
|
writeFileSync as writeFileSync3
|
|
34169
34520
|
} from "fs";
|
|
34170
|
-
import { dirname as
|
|
34521
|
+
import { dirname as dirname5, extname as extname2, join as join10, resolve as resolve3 } from "path";
|
|
34171
34522
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
34172
34523
|
function findServerProjectRoot(filePath, server) {
|
|
34173
34524
|
if (server.root) {
|
|
34174
|
-
return server.root(filePath) ??
|
|
34525
|
+
return server.root(filePath) ?? dirname5(resolve3(filePath));
|
|
34175
34526
|
}
|
|
34176
|
-
return
|
|
34527
|
+
return dirname5(resolve3(filePath));
|
|
34177
34528
|
}
|
|
34178
34529
|
function uriToPath(uri) {
|
|
34179
34530
|
return fileURLToPath2(uri);
|
|
@@ -34203,7 +34554,7 @@ async function withLspClient(filePath, fn) {
|
|
|
34203
34554
|
throw new Error(formatServerLookupError(result));
|
|
34204
34555
|
}
|
|
34205
34556
|
const server = result.server;
|
|
34206
|
-
const root = findServerProjectRoot(absPath, server) ??
|
|
34557
|
+
const root = findServerProjectRoot(absPath, server) ?? dirname5(absPath);
|
|
34207
34558
|
log("[lsp] withLspClient: acquiring client", {
|
|
34208
34559
|
filePath: absPath,
|
|
34209
34560
|
server: server.id,
|
|
@@ -34358,7 +34709,7 @@ function applyWorkspaceEdit(edit) {
|
|
|
34358
34709
|
const newPath = uriToPath(change.newUri);
|
|
34359
34710
|
const content = readFileSync5(oldPath, "utf-8");
|
|
34360
34711
|
writeFileSync3(newPath, content, "utf-8");
|
|
34361
|
-
|
|
34712
|
+
unlinkSync2(oldPath);
|
|
34362
34713
|
result.filesModified.push(newPath);
|
|
34363
34714
|
} catch (err) {
|
|
34364
34715
|
result.success = false;
|
|
@@ -34367,7 +34718,7 @@ function applyWorkspaceEdit(edit) {
|
|
|
34367
34718
|
} else if (change.kind === "delete") {
|
|
34368
34719
|
try {
|
|
34369
34720
|
const filePath = uriToPath(change.uri);
|
|
34370
|
-
|
|
34721
|
+
unlinkSync2(filePath);
|
|
34371
34722
|
result.filesModified.push(filePath);
|
|
34372
34723
|
} catch (err) {
|
|
34373
34724
|
result.success = false;
|
|
@@ -34578,6 +34929,7 @@ var OhMyOpenCodeLite = async (ctx) => {
|
|
|
34578
34929
|
}
|
|
34579
34930
|
const backgroundManager = new BackgroundTaskManager(ctx, tmuxConfig, config3);
|
|
34580
34931
|
const backgroundTools = createBackgroundTools(ctx, backgroundManager, tmuxConfig, config3);
|
|
34932
|
+
const councilTools = config3.council ? createCouncilTool(ctx, new CouncilManager(ctx, config3, backgroundManager.getDepthTracker(), tmuxConfig.enabled)) : {};
|
|
34581
34933
|
const mcps = createBuiltinMcps(config3.disabled_mcps);
|
|
34582
34934
|
const tmuxSessionManager = new TmuxSessionManager(ctx, tmuxConfig);
|
|
34583
34935
|
const autoUpdateChecker = createAutoUpdateCheckerHook(ctx, {
|
|
@@ -34585,7 +34937,7 @@ var OhMyOpenCodeLite = async (ctx) => {
|
|
|
34585
34937
|
autoUpdate: true
|
|
34586
34938
|
});
|
|
34587
34939
|
const phaseReminderHook = createPhaseReminderHook();
|
|
34588
|
-
const
|
|
34940
|
+
const postFileToolNudgeHook = createPostFileToolNudgeHook();
|
|
34589
34941
|
const chatHeadersHook = createChatHeadersHook(ctx);
|
|
34590
34942
|
const delegateTaskRetryHook = createDelegateTaskRetryHook(ctx);
|
|
34591
34943
|
const jsonErrorRecoveryHook = createJsonErrorRecoveryHook(ctx);
|
|
@@ -34595,11 +34947,11 @@ var OhMyOpenCodeLite = async (ctx) => {
|
|
|
34595
34947
|
agent: agents,
|
|
34596
34948
|
tool: {
|
|
34597
34949
|
...backgroundTools,
|
|
34950
|
+
...councilTools,
|
|
34598
34951
|
lsp_goto_definition,
|
|
34599
34952
|
lsp_find_references,
|
|
34600
34953
|
lsp_diagnostics,
|
|
34601
34954
|
lsp_rename,
|
|
34602
|
-
grep,
|
|
34603
34955
|
ast_grep_search,
|
|
34604
34956
|
ast_grep_replace
|
|
34605
34957
|
},
|
|
@@ -34651,52 +35003,22 @@ var OhMyOpenCodeLite = async (ctx) => {
|
|
|
34651
35003
|
}
|
|
34652
35004
|
}
|
|
34653
35005
|
if (Object.keys(effectiveArrays).length > 0) {
|
|
34654
|
-
const providerConfig = opencodeConfig.provider ?? {};
|
|
34655
|
-
const hasProviderConfig = Object.keys(providerConfig).length > 0;
|
|
34656
35006
|
for (const [agentName, modelArray] of Object.entries(effectiveArrays)) {
|
|
34657
35007
|
if (modelArray.length === 0)
|
|
34658
35008
|
continue;
|
|
34659
|
-
|
|
34660
|
-
|
|
34661
|
-
|
|
34662
|
-
|
|
34663
|
-
|
|
34664
|
-
|
|
34665
|
-
continue;
|
|
34666
|
-
const providerID = modelEntry.id.slice(0, slashIdx);
|
|
34667
|
-
if (configuredProviders.includes(providerID)) {
|
|
34668
|
-
const entry = configAgent[agentName];
|
|
34669
|
-
if (entry) {
|
|
34670
|
-
entry.model = modelEntry.id;
|
|
34671
|
-
if (modelEntry.variant) {
|
|
34672
|
-
entry.variant = modelEntry.variant;
|
|
34673
|
-
}
|
|
34674
|
-
}
|
|
34675
|
-
log("[plugin] resolved model fallback", {
|
|
34676
|
-
agent: agentName,
|
|
34677
|
-
model: modelEntry.id,
|
|
34678
|
-
variant: modelEntry.variant
|
|
34679
|
-
});
|
|
34680
|
-
resolved = true;
|
|
34681
|
-
break;
|
|
34682
|
-
}
|
|
34683
|
-
}
|
|
34684
|
-
}
|
|
34685
|
-
if (!resolved) {
|
|
34686
|
-
const firstModel = modelArray[0];
|
|
34687
|
-
const entry = configAgent[agentName];
|
|
34688
|
-
if (entry) {
|
|
34689
|
-
entry.model = firstModel.id;
|
|
34690
|
-
if (firstModel.variant) {
|
|
34691
|
-
entry.variant = firstModel.variant;
|
|
34692
|
-
}
|
|
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;
|
|
34693
35015
|
}
|
|
34694
|
-
log("[plugin] resolved model from array (no provider config)", {
|
|
34695
|
-
agent: agentName,
|
|
34696
|
-
model: firstModel.id,
|
|
34697
|
-
variant: firstModel.variant
|
|
34698
|
-
});
|
|
34699
35016
|
}
|
|
35017
|
+
log("[plugin] resolved model from array", {
|
|
35018
|
+
agent: agentName,
|
|
35019
|
+
model: chosen.id,
|
|
35020
|
+
variant: chosen.variant
|
|
35021
|
+
});
|
|
34700
35022
|
}
|
|
34701
35023
|
}
|
|
34702
35024
|
const configMcp = opencodeConfig.mcp;
|
|
@@ -34741,7 +35063,7 @@ var OhMyOpenCodeLite = async (ctx) => {
|
|
|
34741
35063
|
"tool.execute.after": async (input, output) => {
|
|
34742
35064
|
await delegateTaskRetryHook["tool.execute.after"](input, output);
|
|
34743
35065
|
await jsonErrorRecoveryHook["tool.execute.after"](input, output);
|
|
34744
|
-
await
|
|
35066
|
+
await postFileToolNudgeHook["tool.execute.after"](input, output);
|
|
34745
35067
|
}
|
|
34746
35068
|
};
|
|
34747
35069
|
};
|