abelworkflow 0.1.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/abelworkflow.mjs +6 -1
- package/lib/cli.mjs +614 -90
- package/lib/templates/codex/agents/default.toml +56 -0
- package/lib/templates/codex/agents/explorer.toml +63 -0
- package/lib/templates/codex/agents/planner.toml +89 -0
- package/lib/templates/codex/agents/reviewer.toml +71 -0
- package/lib/templates/codex/agents/worker.toml +67 -0
- package/lib/templates/codex/config-base.toml +95 -0
- package/package.json +1 -1
- package/skills/context7-auto-research/.env +4 -0
- package/skills/prompt-enhancer/.env +2 -0
- package/skills/prompt-enhancer/scripts/__pycache__/_dotenv.cpython-314.pyc +0 -0
- package/skills/prompt-enhancer/scripts/__pycache__/enhance.cpython-314.pyc +0 -0
- package/skills/prompt-enhancer/scripts/__pycache__/prompt_enhancer_entry.cpython-314.pyc +0 -0
package/bin/abelworkflow.mjs
CHANGED
package/lib/cli.mjs
CHANGED
|
@@ -12,10 +12,62 @@ const home = homedir();
|
|
|
12
12
|
const defaultAgentsDir = join(home, ".agents");
|
|
13
13
|
const installMetadataName = ".abelworkflow-install.json";
|
|
14
14
|
const claudeSettingsPath = join(home, ".claude", "settings.json");
|
|
15
|
-
const claudeVscodeConfigPath = join(home, ".claude", "config.json");
|
|
16
15
|
const claudeMetaConfigPath = join(home, ".claude.json");
|
|
17
16
|
const codexConfigPath = join(home, ".codex", "config.toml");
|
|
18
17
|
const codexAuthPath = join(home, ".codex", "auth.json");
|
|
18
|
+
const codexTemplateRoot = join(packageRoot, "lib", "templates", "codex");
|
|
19
|
+
const codexTemplateConfigPath = join(codexTemplateRoot, "config-base.toml");
|
|
20
|
+
const codexTemplateAgentsPath = join(codexTemplateRoot, "agents");
|
|
21
|
+
const installBackupStamp = Date.now();
|
|
22
|
+
const createdBackupPaths = new Set();
|
|
23
|
+
const claudeModelEnvKeys = [
|
|
24
|
+
"ANTHROPIC_MODEL",
|
|
25
|
+
"ANTHROPIC_DEFAULT_OPUS_MODEL",
|
|
26
|
+
"ANTHROPIC_DEFAULT_SONNET_MODEL",
|
|
27
|
+
"ANTHROPIC_DEFAULT_HAIKU_MODEL",
|
|
28
|
+
"CLAUDE_CODE_SUBAGENT_MODEL"
|
|
29
|
+
];
|
|
30
|
+
const defaultClaudeSettings = {
|
|
31
|
+
$schema: "https://json.schemastore.org/claude-code-settings.json",
|
|
32
|
+
env: {
|
|
33
|
+
DISABLE_TELEMETRY: "1",
|
|
34
|
+
DISABLE_ERROR_REPORTING: "1",
|
|
35
|
+
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: "1",
|
|
36
|
+
ANTHROPIC_BASE_URL: "",
|
|
37
|
+
ANTHROPIC_API_KEY: "",
|
|
38
|
+
ANTHROPIC_MODEL: "",
|
|
39
|
+
ANTHROPIC_DEFAULT_OPUS_MODEL: "",
|
|
40
|
+
ANTHROPIC_DEFAULT_SONNET_MODEL: "",
|
|
41
|
+
ANTHROPIC_DEFAULT_HAIKU_MODEL: "",
|
|
42
|
+
CLAUDE_CODE_SUBAGENT_MODEL: "",
|
|
43
|
+
API_TIMEOUT_MS: "1000000"
|
|
44
|
+
},
|
|
45
|
+
includeCoAuthoredBy: false,
|
|
46
|
+
permissions: {
|
|
47
|
+
allow: [
|
|
48
|
+
"Bash",
|
|
49
|
+
"Skill",
|
|
50
|
+
"LS",
|
|
51
|
+
"Read",
|
|
52
|
+
"Agent",
|
|
53
|
+
"Write",
|
|
54
|
+
"Edit",
|
|
55
|
+
"MultiEdit",
|
|
56
|
+
"Glob",
|
|
57
|
+
"Grep",
|
|
58
|
+
"WebFetch",
|
|
59
|
+
"WebSearch",
|
|
60
|
+
"TodoWrite",
|
|
61
|
+
"NotebookRead",
|
|
62
|
+
"NotebookEdit",
|
|
63
|
+
"mcp__augment-context-engine"
|
|
64
|
+
],
|
|
65
|
+
deny: []
|
|
66
|
+
},
|
|
67
|
+
hooks: {},
|
|
68
|
+
alwaysThinkingEnabled: true,
|
|
69
|
+
language: "Chinese"
|
|
70
|
+
};
|
|
19
71
|
const managedEntries = [
|
|
20
72
|
{ target: "AGENTS.md" },
|
|
21
73
|
{ target: "README.md" },
|
|
@@ -174,17 +226,38 @@ async function pathTargetExists(path) {
|
|
|
174
226
|
}
|
|
175
227
|
}
|
|
176
228
|
|
|
229
|
+
async function createBackupPath(targetPath) {
|
|
230
|
+
let index = 0;
|
|
231
|
+
while (true) {
|
|
232
|
+
const backupPath = `${targetPath}.bak.${installBackupStamp}${index ? `-${index}` : ""}`;
|
|
233
|
+
if (!(await pathExists(backupPath))) {
|
|
234
|
+
return backupPath;
|
|
235
|
+
}
|
|
236
|
+
index += 1;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
async function backupExistingPath(targetPath) {
|
|
241
|
+
if (createdBackupPaths.has(targetPath) || !(await pathExists(targetPath))) {
|
|
242
|
+
return null;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const backupPath = await createBackupPath(targetPath);
|
|
246
|
+
await cp(targetPath, backupPath, { recursive: true, force: false });
|
|
247
|
+
createdBackupPaths.add(targetPath);
|
|
248
|
+
console.log(`已备份已有配置: ${pathToLabel(targetPath)} -> ${pathToLabel(backupPath)}`);
|
|
249
|
+
return backupPath;
|
|
250
|
+
}
|
|
251
|
+
|
|
177
252
|
async function backupIfNeeded(targetPath, force) {
|
|
178
253
|
if (!(await pathExists(targetPath))) {
|
|
179
254
|
return null;
|
|
180
255
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
await rm(targetPath, { recursive: true, force: true });
|
|
187
|
-
return null;
|
|
256
|
+
|
|
257
|
+
const backupPath = await createBackupPath(targetPath);
|
|
258
|
+
await rename(targetPath, backupPath);
|
|
259
|
+
console.log(`已备份已有配置: ${pathToLabel(targetPath)} -> ${pathToLabel(backupPath)}`);
|
|
260
|
+
return backupPath;
|
|
188
261
|
}
|
|
189
262
|
|
|
190
263
|
async function syncManagedFiles(agentsDir) {
|
|
@@ -250,8 +323,10 @@ async function replaceManagedEntry(source, target, entry) {
|
|
|
250
323
|
|
|
251
324
|
const sourceStat = await lstat(source);
|
|
252
325
|
if (sourceStat.isDirectory()) {
|
|
326
|
+
await backupExistingPath(target);
|
|
253
327
|
await rm(target, { recursive: true, force: true });
|
|
254
328
|
} else if (await pathExists(target)) {
|
|
329
|
+
await backupExistingPath(target);
|
|
255
330
|
const targetStat = await lstat(target);
|
|
256
331
|
if (targetStat.isDirectory()) {
|
|
257
332
|
await rm(target, { recursive: true, force: true });
|
|
@@ -802,6 +877,11 @@ async function writeJsonFileSafe(path, data) {
|
|
|
802
877
|
await writeFile(path, `${JSON.stringify(data, null, 2)}\n`, "utf8");
|
|
803
878
|
}
|
|
804
879
|
|
|
880
|
+
async function writeJsonFileWithBackup(path, data) {
|
|
881
|
+
await backupExistingPath(path);
|
|
882
|
+
await writeJsonFileSafe(path, data);
|
|
883
|
+
}
|
|
884
|
+
|
|
805
885
|
function parseDotenv(content) {
|
|
806
886
|
const values = {};
|
|
807
887
|
for (const rawLine of content.split(/\r?\n/u)) {
|
|
@@ -864,6 +944,7 @@ async function updateDotenvFile(path, updates) {
|
|
|
864
944
|
current[key] = String(value);
|
|
865
945
|
}
|
|
866
946
|
}
|
|
947
|
+
await backupExistingPath(path);
|
|
867
948
|
await mkdir(dirname(path), { recursive: true });
|
|
868
949
|
await writeFile(path, renderDotenv(current), "utf8");
|
|
869
950
|
}
|
|
@@ -1143,12 +1224,33 @@ async function configurePromptEnhancerEnv(agentsDir) {
|
|
|
1143
1224
|
console.log(`已写入 ${pathToLabel(envPath)}`);
|
|
1144
1225
|
}
|
|
1145
1226
|
|
|
1227
|
+
function mergeClaudeSettingsWithDefaults(settings) {
|
|
1228
|
+
const env = settings?.env && typeof settings.env === "object" ? settings.env : {};
|
|
1229
|
+
const permissions = settings?.permissions && typeof settings.permissions === "object" ? settings.permissions : {};
|
|
1230
|
+
return {
|
|
1231
|
+
...defaultClaudeSettings,
|
|
1232
|
+
...settings,
|
|
1233
|
+
env: {
|
|
1234
|
+
...defaultClaudeSettings.env,
|
|
1235
|
+
...env
|
|
1236
|
+
},
|
|
1237
|
+
permissions: {
|
|
1238
|
+
...defaultClaudeSettings.permissions,
|
|
1239
|
+
...permissions,
|
|
1240
|
+
allow: Array.isArray(permissions.allow) ? permissions.allow : defaultClaudeSettings.permissions.allow,
|
|
1241
|
+
deny: Array.isArray(permissions.deny) ? permissions.deny : defaultClaudeSettings.permissions.deny
|
|
1242
|
+
},
|
|
1243
|
+
hooks: settings?.hooks && typeof settings.hooks === "object" ? settings.hooks : defaultClaudeSettings.hooks
|
|
1244
|
+
};
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1146
1247
|
function getExistingClaudeApiConfig(settings) {
|
|
1147
|
-
const env = settings
|
|
1248
|
+
const env = mergeClaudeSettingsWithDefaults(settings).env;
|
|
1148
1249
|
return {
|
|
1149
1250
|
baseUrl: env.ANTHROPIC_BASE_URL || "https://api.anthropic.com",
|
|
1150
1251
|
authType: env.ANTHROPIC_AUTH_TOKEN ? "auth_token" : "api_key",
|
|
1151
|
-
key: env.ANTHROPIC_AUTH_TOKEN || env.ANTHROPIC_API_KEY || ""
|
|
1252
|
+
key: env.ANTHROPIC_AUTH_TOKEN || env.ANTHROPIC_API_KEY || "",
|
|
1253
|
+
model: claudeModelEnvKeys.map((field) => env[field]).find(Boolean) || ""
|
|
1152
1254
|
};
|
|
1153
1255
|
}
|
|
1154
1256
|
|
|
@@ -1189,14 +1291,12 @@ async function configureClaudeApi() {
|
|
|
1189
1291
|
const key = await promptSecret(authType === "auth_token" ? "Claude Code Auth Token" : "Claude Code API Key", {
|
|
1190
1292
|
defaultValue: existing.key || undefined
|
|
1191
1293
|
});
|
|
1294
|
+
const model = await promptText("Claude Code 模型", {
|
|
1295
|
+
defaultValue: existing.model || undefined
|
|
1296
|
+
});
|
|
1192
1297
|
|
|
1193
|
-
const nextSettings =
|
|
1194
|
-
|
|
1195
|
-
env: {
|
|
1196
|
-
...(settings.env || {}),
|
|
1197
|
-
ANTHROPIC_BASE_URL: baseUrl
|
|
1198
|
-
}
|
|
1199
|
-
};
|
|
1298
|
+
const nextSettings = mergeClaudeSettingsWithDefaults(settings);
|
|
1299
|
+
nextSettings.env.ANTHROPIC_BASE_URL = baseUrl;
|
|
1200
1300
|
|
|
1201
1301
|
if (authType === "auth_token") {
|
|
1202
1302
|
nextSettings.env.ANTHROPIC_AUTH_TOKEN = key;
|
|
@@ -1205,55 +1305,94 @@ async function configureClaudeApi() {
|
|
|
1205
1305
|
nextSettings.env.ANTHROPIC_API_KEY = key;
|
|
1206
1306
|
delete nextSettings.env.ANTHROPIC_AUTH_TOKEN;
|
|
1207
1307
|
}
|
|
1308
|
+
for (const field of claudeModelEnvKeys) {
|
|
1309
|
+
nextSettings.env[field] = model;
|
|
1310
|
+
}
|
|
1208
1311
|
|
|
1209
|
-
await
|
|
1210
|
-
|
|
1211
|
-
const vscodeConfig = await readJsonFileSafe(claudeVscodeConfigPath, {});
|
|
1212
|
-
vscodeConfig.primaryApiKey = "abelworkflow";
|
|
1213
|
-
await writeJsonFileSafe(claudeVscodeConfigPath, vscodeConfig);
|
|
1312
|
+
await writeJsonFileWithBackup(claudeSettingsPath, nextSettings);
|
|
1214
1313
|
|
|
1215
1314
|
const metaConfig = await readJsonFileSafe(claudeMetaConfigPath, {});
|
|
1216
1315
|
metaConfig.hasCompletedOnboarding = true;
|
|
1217
1316
|
ensureApprovedClaudeApiKey(metaConfig, key);
|
|
1218
|
-
await
|
|
1317
|
+
await writeJsonFileWithBackup(claudeMetaConfigPath, metaConfig);
|
|
1219
1318
|
|
|
1220
1319
|
console.log(`已更新 ${pathToLabel(claudeSettingsPath)} (${authType}, ${baseUrl}, ${maskSecret(key)})`);
|
|
1221
1320
|
}
|
|
1222
1321
|
|
|
1223
1322
|
function updateTopLevelTomlField(content, field, value) {
|
|
1224
1323
|
const lineEnding = detectLineEnding(content);
|
|
1225
|
-
const firstSectionMatch = content.match(/^\[/mu);
|
|
1226
|
-
const topLevelEnd = firstSectionMatch?.index ?? content.length;
|
|
1227
|
-
let topLevel = content.slice(0, topLevelEnd);
|
|
1228
|
-
const rest = content.slice(topLevelEnd);
|
|
1229
|
-
const fieldRegex = new RegExp(`^(#\\s*)?${escapeRegExp(field)}\\s*=\\s*["'][^"']*["'][ \\t]*(?:#.*)?\\r?$`, "mu");
|
|
1230
|
-
|
|
1231
1324
|
if (value === null) {
|
|
1232
|
-
|
|
1233
|
-
}
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1325
|
+
return removeTopLevelTomlField(content, field);
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1328
|
+
const { topLevel, rest } = splitTopLevelTomlContent(content);
|
|
1329
|
+
const entry = extractTopLevelTomlEntries(content).find((item) => item.field === field);
|
|
1330
|
+
const nextLine = `${field} = ${JSON.stringify(value)}`;
|
|
1331
|
+
let nextTopLevel;
|
|
1332
|
+
|
|
1333
|
+
if (entry) {
|
|
1334
|
+
const start = topLevel.indexOf(entry.raw);
|
|
1335
|
+
if (start === -1) {
|
|
1336
|
+
return content;
|
|
1241
1337
|
}
|
|
1338
|
+
nextTopLevel = `${topLevel.slice(0, start)}${nextLine}${topLevel.slice(start + entry.raw.length)}`;
|
|
1339
|
+
} else {
|
|
1340
|
+
nextTopLevel = topLevel.trimEnd()
|
|
1341
|
+
? `${topLevel.trimEnd()}${lineEnding}${nextLine}${lineEnding}`
|
|
1342
|
+
: `${nextLine}${lineEnding}`;
|
|
1242
1343
|
}
|
|
1243
1344
|
|
|
1244
|
-
|
|
1345
|
+
nextTopLevel = nextTopLevel.trimEnd();
|
|
1245
1346
|
|
|
1246
|
-
if (rest &&
|
|
1247
|
-
|
|
1347
|
+
if (rest && nextTopLevel) {
|
|
1348
|
+
nextTopLevel = `${nextTopLevel}${lineEnding}${lineEnding}`;
|
|
1248
1349
|
}
|
|
1249
1350
|
|
|
1250
|
-
return `${
|
|
1351
|
+
return `${nextTopLevel}${rest}`;
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1354
|
+
function removeTopLevelTomlField(content, field) {
|
|
1355
|
+
const lineEnding = detectLineEnding(content);
|
|
1356
|
+
const { topLevel, rest } = splitTopLevelTomlContent(content);
|
|
1357
|
+
const entry = extractTopLevelTomlEntries(content).find((item) => item.field === field);
|
|
1358
|
+
if (!entry) {
|
|
1359
|
+
return content;
|
|
1360
|
+
}
|
|
1361
|
+
|
|
1362
|
+
const start = topLevel.indexOf(entry.raw);
|
|
1363
|
+
if (start === -1) {
|
|
1364
|
+
return content;
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
let nextTopLevel = `${topLevel.slice(0, start)}${topLevel.slice(start + entry.raw.length)}`;
|
|
1368
|
+
nextTopLevel = collapseBlankLines(nextTopLevel, lineEnding).trimEnd();
|
|
1369
|
+
|
|
1370
|
+
if (rest && nextTopLevel) {
|
|
1371
|
+
nextTopLevel = `${nextTopLevel}${lineEnding}${lineEnding}`;
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1374
|
+
return `${nextTopLevel}${rest}`;
|
|
1251
1375
|
}
|
|
1252
1376
|
|
|
1253
1377
|
function removeTomlSection(content, sectionName) {
|
|
1254
1378
|
const lineEnding = detectLineEnding(content);
|
|
1255
|
-
const
|
|
1256
|
-
|
|
1379
|
+
const sectionHeaderRegex = new RegExp(
|
|
1380
|
+
`(?:^|\\r?\\n)\\[${escapeRegExp(sectionName)}\\](?:[ \\t]+#.*)?[ \\t]*\\r?$`,
|
|
1381
|
+
"mu"
|
|
1382
|
+
);
|
|
1383
|
+
const headerMatch = sectionHeaderRegex.exec(content);
|
|
1384
|
+
if (!headerMatch) {
|
|
1385
|
+
return content;
|
|
1386
|
+
}
|
|
1387
|
+
|
|
1388
|
+
const sectionStart = headerMatch[0].startsWith("\n") || headerMatch[0].startsWith("\r\n")
|
|
1389
|
+
? headerMatch.index + headerMatch[0].indexOf("[")
|
|
1390
|
+
: headerMatch.index;
|
|
1391
|
+
const headerLineEnd = content.indexOf(lineEnding, sectionStart);
|
|
1392
|
+
const bodyStart = headerLineEnd === -1 ? content.length : headerLineEnd + lineEnding.length;
|
|
1393
|
+
const nextSectionStart = findTomlSectionStart(content, bodyStart);
|
|
1394
|
+
const sectionEnd = nextSectionStart === -1 ? content.length : nextSectionStart;
|
|
1395
|
+
return collapseBlankLines(`${content.slice(0, sectionStart)}${content.slice(sectionEnd)}`, lineEnding).trimEnd();
|
|
1257
1396
|
}
|
|
1258
1397
|
|
|
1259
1398
|
function buildTomlSection(sectionName, values, lineEnding = "\n") {
|
|
@@ -1273,18 +1412,235 @@ function buildTomlSection(sectionName, values, lineEnding = "\n") {
|
|
|
1273
1412
|
return `${lines.join(lineEnding)}${lineEnding}`;
|
|
1274
1413
|
}
|
|
1275
1414
|
|
|
1276
|
-
function
|
|
1277
|
-
|
|
1278
|
-
|
|
1415
|
+
function formatTomlValue(value) {
|
|
1416
|
+
if (typeof value === "string") {
|
|
1417
|
+
return JSON.stringify(value);
|
|
1418
|
+
}
|
|
1419
|
+
if (typeof value === "boolean") {
|
|
1420
|
+
return value ? "true" : "false";
|
|
1421
|
+
}
|
|
1422
|
+
return String(value);
|
|
1423
|
+
}
|
|
1424
|
+
|
|
1425
|
+
function getTomlLines(content) {
|
|
1426
|
+
if (!content) {
|
|
1427
|
+
return [];
|
|
1428
|
+
}
|
|
1429
|
+
|
|
1430
|
+
const chunks = content.match(/[^\r\n]*(?:\r?\n|$)/gu) || [];
|
|
1431
|
+
const lines = [];
|
|
1432
|
+
let offset = 0;
|
|
1433
|
+
|
|
1434
|
+
for (const chunk of chunks) {
|
|
1435
|
+
if (!chunk && offset >= content.length) {
|
|
1436
|
+
break;
|
|
1437
|
+
}
|
|
1438
|
+
|
|
1439
|
+
const lineEndingMatch = chunk.match(/\r?\n$/u);
|
|
1440
|
+
const lineEnding = lineEndingMatch ? lineEndingMatch[0] : "";
|
|
1441
|
+
lines.push({
|
|
1442
|
+
line: lineEnding ? chunk.slice(0, -lineEnding.length) : chunk,
|
|
1443
|
+
start: offset
|
|
1444
|
+
});
|
|
1445
|
+
offset += chunk.length;
|
|
1446
|
+
|
|
1447
|
+
if (!lineEnding && offset >= content.length) {
|
|
1448
|
+
break;
|
|
1449
|
+
}
|
|
1450
|
+
}
|
|
1451
|
+
|
|
1452
|
+
return lines;
|
|
1453
|
+
}
|
|
1454
|
+
|
|
1455
|
+
function findTomlSectionStart(content, fromIndex = 0) {
|
|
1456
|
+
const scopedContent = content.slice(fromIndex);
|
|
1457
|
+
let multilineDelimiter = "";
|
|
1458
|
+
|
|
1459
|
+
for (const { line, start } of getTomlLines(scopedContent)) {
|
|
1460
|
+
const trimmed = line.trim();
|
|
1461
|
+
|
|
1462
|
+
if (multilineDelimiter) {
|
|
1463
|
+
if (line.includes(multilineDelimiter)) {
|
|
1464
|
+
multilineDelimiter = "";
|
|
1465
|
+
}
|
|
1466
|
+
continue;
|
|
1467
|
+
}
|
|
1468
|
+
|
|
1469
|
+
if (!trimmed || trimmed.startsWith("#")) {
|
|
1470
|
+
continue;
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1473
|
+
if (/^\[[^\]]+\]\s*(?:#.*)?$/u.test(trimmed)) {
|
|
1474
|
+
return fromIndex + start;
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1477
|
+
const match = line.match(/^\s*([A-Za-z0-9_]+)\s*=\s*(.*)$/u);
|
|
1478
|
+
if (!match) {
|
|
1479
|
+
continue;
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1482
|
+
const delimiter = getTomlMultilineStringDelimiter(match[2].trimStart());
|
|
1483
|
+
if (delimiter && match[2].indexOf(delimiter, delimiter.length) === -1) {
|
|
1484
|
+
multilineDelimiter = delimiter;
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1488
|
+
return -1;
|
|
1489
|
+
}
|
|
1490
|
+
|
|
1491
|
+
function splitTopLevelTomlContent(content) {
|
|
1492
|
+
const topLevelEnd = findTomlSectionStart(content);
|
|
1493
|
+
return {
|
|
1494
|
+
topLevel: topLevelEnd === -1 ? content : content.slice(0, topLevelEnd),
|
|
1495
|
+
rest: topLevelEnd === -1 ? "" : content.slice(topLevelEnd)
|
|
1496
|
+
};
|
|
1497
|
+
}
|
|
1498
|
+
|
|
1499
|
+
function getTomlMultilineStringDelimiter(value) {
|
|
1500
|
+
if (value.startsWith(`"""`)) {
|
|
1501
|
+
return `"""`;
|
|
1502
|
+
}
|
|
1503
|
+
if (value.startsWith(`'''`)) {
|
|
1504
|
+
return `'''`;
|
|
1505
|
+
}
|
|
1506
|
+
return "";
|
|
1507
|
+
}
|
|
1508
|
+
|
|
1509
|
+
function extractTopLevelTomlEntries(content) {
|
|
1510
|
+
const { topLevel } = splitTopLevelTomlContent(content);
|
|
1511
|
+
const lineEnding = detectLineEnding(topLevel);
|
|
1512
|
+
const lines = topLevel.split(/\r?\n/u);
|
|
1513
|
+
const entries = [];
|
|
1514
|
+
|
|
1515
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
1516
|
+
const line = lines[index];
|
|
1279
1517
|
const trimmed = line.trim();
|
|
1280
1518
|
if (!trimmed || trimmed.startsWith("#")) {
|
|
1281
1519
|
continue;
|
|
1282
1520
|
}
|
|
1283
|
-
|
|
1284
|
-
|
|
1521
|
+
|
|
1522
|
+
const match = line.match(/^\s*([A-Za-z0-9_]+)\s*=\s*(.*)$/u);
|
|
1523
|
+
if (!match) {
|
|
1524
|
+
continue;
|
|
1525
|
+
}
|
|
1526
|
+
|
|
1527
|
+
const rawLines = [line];
|
|
1528
|
+
const delimiter = getTomlMultilineStringDelimiter(match[2].trimStart());
|
|
1529
|
+
if (delimiter && match[2].indexOf(delimiter, delimiter.length) === -1) {
|
|
1530
|
+
for (index += 1; index < lines.length; index += 1) {
|
|
1531
|
+
rawLines.push(lines[index]);
|
|
1532
|
+
if (lines[index].includes(delimiter)) {
|
|
1533
|
+
break;
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1536
|
+
}
|
|
1537
|
+
|
|
1538
|
+
entries.push({
|
|
1539
|
+
field: match[1],
|
|
1540
|
+
raw: rawLines.join(lineEnding)
|
|
1541
|
+
});
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1544
|
+
return entries;
|
|
1545
|
+
}
|
|
1546
|
+
|
|
1547
|
+
function mergeMissingTopLevelTomlEntries(content, entries) {
|
|
1548
|
+
if (!entries.length) {
|
|
1549
|
+
return content;
|
|
1550
|
+
}
|
|
1551
|
+
|
|
1552
|
+
const lineEnding = detectLineEnding(content);
|
|
1553
|
+
const { topLevel, rest } = splitTopLevelTomlContent(content);
|
|
1554
|
+
const existingFields = new Set(extractTopLevelTomlEntries(content).map(({ field }) => field));
|
|
1555
|
+
const missingEntries = entries.filter(({ field }) => !existingFields.has(field));
|
|
1556
|
+
if (!missingEntries.length) {
|
|
1557
|
+
return content;
|
|
1558
|
+
}
|
|
1559
|
+
|
|
1560
|
+
let nextTopLevel = topLevel.trimEnd();
|
|
1561
|
+
for (const entry of missingEntries) {
|
|
1562
|
+
nextTopLevel = nextTopLevel
|
|
1563
|
+
? `${nextTopLevel}${lineEnding}${entry.raw}`
|
|
1564
|
+
: entry.raw;
|
|
1565
|
+
}
|
|
1566
|
+
|
|
1567
|
+
nextTopLevel = nextTopLevel.trimEnd();
|
|
1568
|
+
if (rest && nextTopLevel) {
|
|
1569
|
+
nextTopLevel = `${nextTopLevel}${lineEnding}${lineEnding}`;
|
|
1570
|
+
}
|
|
1571
|
+
return `${nextTopLevel}${rest}`;
|
|
1572
|
+
}
|
|
1573
|
+
|
|
1574
|
+
function updateTomlBodyFields(body, values, lineEnding = "\n") {
|
|
1575
|
+
const normalizedBody = body.replace(/\r?\n$/u, "");
|
|
1576
|
+
const lines = normalizedBody ? normalizedBody.split(/\r?\n/u) : [];
|
|
1577
|
+
const remaining = new Map(
|
|
1578
|
+
Object.entries(values).filter(([, value]) => value !== undefined && value !== null && value !== "")
|
|
1579
|
+
);
|
|
1580
|
+
const managedFields = new Set(remaining.keys());
|
|
1581
|
+
const seenFields = new Set();
|
|
1582
|
+
const nextLines = [];
|
|
1583
|
+
|
|
1584
|
+
for (const rawLine of lines) {
|
|
1585
|
+
const match = rawLine.match(/^(\s*)([A-Za-z0-9_]+)\s*=\s*.*$/u);
|
|
1586
|
+
if (!match || !managedFields.has(match[2])) {
|
|
1587
|
+
nextLines.push(rawLine);
|
|
1588
|
+
continue;
|
|
1589
|
+
}
|
|
1590
|
+
|
|
1591
|
+
if (seenFields.has(match[2])) {
|
|
1285
1592
|
continue;
|
|
1286
1593
|
}
|
|
1287
|
-
|
|
1594
|
+
|
|
1595
|
+
nextLines.push(`${match[1]}${match[2]} = ${formatTomlValue(remaining.get(match[2]))}`);
|
|
1596
|
+
seenFields.add(match[2]);
|
|
1597
|
+
remaining.delete(match[2]);
|
|
1598
|
+
}
|
|
1599
|
+
|
|
1600
|
+
for (const [field, value] of remaining) {
|
|
1601
|
+
nextLines.push(`${field} = ${formatTomlValue(value)}`);
|
|
1602
|
+
}
|
|
1603
|
+
|
|
1604
|
+
return nextLines.join(lineEnding);
|
|
1605
|
+
}
|
|
1606
|
+
|
|
1607
|
+
function updateTomlSectionFields(content, sectionName, values) {
|
|
1608
|
+
const lineEnding = detectLineEnding(content);
|
|
1609
|
+
const sectionHeaderRegex = new RegExp(
|
|
1610
|
+
`^\\[${escapeRegExp(sectionName)}\\](?:[ \\t]+#.*)?[ \\t]*\\r?$`,
|
|
1611
|
+
"mu"
|
|
1612
|
+
);
|
|
1613
|
+
const headerMatch = sectionHeaderRegex.exec(content);
|
|
1614
|
+
|
|
1615
|
+
if (!headerMatch) {
|
|
1616
|
+
const nextSection = buildTomlSection(sectionName, values, lineEnding).trimEnd();
|
|
1617
|
+
return content.trimEnd()
|
|
1618
|
+
? `${content.trimEnd()}${lineEnding}${lineEnding}${nextSection}${lineEnding}`
|
|
1619
|
+
: `${nextSection}${lineEnding}`;
|
|
1620
|
+
}
|
|
1621
|
+
|
|
1622
|
+
const sectionStart = headerMatch.index;
|
|
1623
|
+
const headerLineEnd = content.indexOf(lineEnding, sectionStart);
|
|
1624
|
+
const bodyStart = headerLineEnd === -1 ? content.length : headerLineEnd + lineEnding.length;
|
|
1625
|
+
const headerLine = content.slice(sectionStart, headerLineEnd === -1 ? content.length : headerLineEnd);
|
|
1626
|
+
const nextSectionStart = findTomlSectionStart(content, bodyStart);
|
|
1627
|
+
const sectionEnd = nextSectionStart === -1 ? content.length : nextSectionStart;
|
|
1628
|
+
const prefix = content.slice(0, sectionStart);
|
|
1629
|
+
const body = content.slice(bodyStart, sectionEnd);
|
|
1630
|
+
const suffix = content.slice(sectionEnd);
|
|
1631
|
+
const nextBody = updateTomlBodyFields(body, values, lineEnding);
|
|
1632
|
+
const renderedSection = nextBody
|
|
1633
|
+
? `${headerLine}${lineEnding}${nextBody}${lineEnding}`
|
|
1634
|
+
: `${headerLine}${lineEnding}`;
|
|
1635
|
+
|
|
1636
|
+
return `${prefix}${renderedSection}${suffix.replace(/^\r?\n/u, "")}`;
|
|
1637
|
+
}
|
|
1638
|
+
|
|
1639
|
+
function readTopLevelTomlString(content, field) {
|
|
1640
|
+
const { topLevel } = splitTopLevelTomlContent(content);
|
|
1641
|
+
for (const line of topLevel.split(/\r?\n/u)) {
|
|
1642
|
+
const trimmed = line.trim();
|
|
1643
|
+
if (!trimmed || trimmed.startsWith("#")) {
|
|
1288
1644
|
continue;
|
|
1289
1645
|
}
|
|
1290
1646
|
const match = trimmed.match(new RegExp(`^${escapeRegExp(field)}\\s*=\\s*"([^"]+)"$`, "u"));
|
|
@@ -1296,87 +1652,242 @@ function readTopLevelTomlString(content, field) {
|
|
|
1296
1652
|
}
|
|
1297
1653
|
|
|
1298
1654
|
function parseTomlSection(content, sectionName) {
|
|
1299
|
-
const
|
|
1300
|
-
|
|
1655
|
+
const lineEnding = detectLineEnding(content);
|
|
1656
|
+
const sectionHeaderRegex = new RegExp(
|
|
1657
|
+
`^\\[${escapeRegExp(sectionName)}\\](?:[ \\t]+#.*)?[ \\t]*\\r?$`,
|
|
1658
|
+
"mu"
|
|
1659
|
+
);
|
|
1660
|
+
const headerMatch = sectionHeaderRegex.exec(content);
|
|
1661
|
+
if (!headerMatch) {
|
|
1301
1662
|
return {};
|
|
1302
1663
|
}
|
|
1303
1664
|
|
|
1665
|
+
const sectionStart = headerMatch.index;
|
|
1666
|
+
const headerLineEnd = content.indexOf(lineEnding, sectionStart);
|
|
1667
|
+
const bodyStart = headerLineEnd === -1 ? content.length : headerLineEnd + lineEnding.length;
|
|
1668
|
+
const nextSectionStart = findTomlSectionStart(content, bodyStart);
|
|
1669
|
+
const body = content.slice(bodyStart, nextSectionStart === -1 ? content.length : nextSectionStart);
|
|
1670
|
+
|
|
1304
1671
|
const values = {};
|
|
1305
|
-
for (const rawLine of
|
|
1672
|
+
for (const rawLine of body.split(/\r?\n/u)) {
|
|
1306
1673
|
const line = rawLine.trim();
|
|
1307
1674
|
if (!line || line.startsWith("#")) {
|
|
1308
1675
|
continue;
|
|
1309
1676
|
}
|
|
1310
|
-
const stringMatch = line.match(/^([A-Za-z0-9_]+)\s*=\s*"([^"]*)"
|
|
1677
|
+
const stringMatch = line.match(/^([A-Za-z0-9_]+)\s*=\s*"([^"]*)"\s*(?:#.*)?$/u);
|
|
1311
1678
|
if (stringMatch) {
|
|
1312
1679
|
values[stringMatch[1]] = stringMatch[2];
|
|
1313
1680
|
continue;
|
|
1314
1681
|
}
|
|
1315
|
-
const boolMatch = line.match(/^([A-Za-z0-9_]+)\s*=\s*(true|false)
|
|
1682
|
+
const boolMatch = line.match(/^([A-Za-z0-9_]+)\s*=\s*(true|false)\s*(?:#.*)?$/u);
|
|
1316
1683
|
if (boolMatch) {
|
|
1317
1684
|
values[boolMatch[1]] = boolMatch[2] === "true";
|
|
1685
|
+
continue;
|
|
1686
|
+
}
|
|
1687
|
+
const numberMatch = line.match(/^([A-Za-z0-9_]+)\s*=\s*(-?\d+(?:\.\d+)?)\s*(?:#.*)?$/u);
|
|
1688
|
+
if (numberMatch) {
|
|
1689
|
+
values[numberMatch[1]] = Number(numberMatch[2]);
|
|
1318
1690
|
}
|
|
1319
1691
|
}
|
|
1320
1692
|
return values;
|
|
1321
1693
|
}
|
|
1322
1694
|
|
|
1323
|
-
|
|
1324
|
-
const
|
|
1325
|
-
|
|
1695
|
+
function stripCodexSubagentDefaults(content) {
|
|
1696
|
+
const lineEnding = detectLineEnding(content);
|
|
1697
|
+
let nextContent = removeTopLevelTomlField(content, "approvals_reviewer");
|
|
1698
|
+
nextContent = removeTopLevelTomlField(nextContent, "developer_instructions");
|
|
1699
|
+
nextContent = removeTomlSection(nextContent, "agents");
|
|
1700
|
+
|
|
1701
|
+
const featureValues = parseTomlSection(nextContent, "features");
|
|
1702
|
+
delete featureValues.multi_agent;
|
|
1703
|
+
delete featureValues.guardian_approval;
|
|
1704
|
+
nextContent = removeTomlSection(nextContent, "features");
|
|
1705
|
+
|
|
1706
|
+
if (Object.keys(featureValues).length) {
|
|
1707
|
+
const featuresSection = buildTomlSection("features", featureValues, lineEnding).trimEnd();
|
|
1708
|
+
nextContent = nextContent.trimEnd()
|
|
1709
|
+
? `${nextContent.trimEnd()}${lineEnding}${lineEnding}${featuresSection}${lineEnding}`
|
|
1710
|
+
: `${featuresSection}${lineEnding}`;
|
|
1711
|
+
}
|
|
1712
|
+
|
|
1713
|
+
return nextContent;
|
|
1714
|
+
}
|
|
1715
|
+
|
|
1716
|
+
function mergeCodexTemplateDefaults(content, templateContent) {
|
|
1717
|
+
const defaultTopLevelFields = new Set([
|
|
1718
|
+
"personality",
|
|
1719
|
+
"disable_response_storage",
|
|
1720
|
+
"approvals_reviewer",
|
|
1721
|
+
"approval_policy",
|
|
1722
|
+
"sandbox_mode",
|
|
1723
|
+
"service_tier",
|
|
1724
|
+
"model",
|
|
1725
|
+
"model_reasoning_effort",
|
|
1726
|
+
"developer_instructions"
|
|
1727
|
+
]);
|
|
1728
|
+
const templateEntries = extractTopLevelTomlEntries(templateContent)
|
|
1729
|
+
.filter(({ field }) => defaultTopLevelFields.has(field));
|
|
1730
|
+
let nextContent = mergeMissingTopLevelTomlEntries(content, templateEntries);
|
|
1731
|
+
|
|
1732
|
+
for (const sectionName of ["agents", "features"]) {
|
|
1733
|
+
const templateValues = parseTomlSection(templateContent, sectionName);
|
|
1734
|
+
const currentValues = parseTomlSection(nextContent, sectionName);
|
|
1735
|
+
const missingValues = Object.fromEntries(
|
|
1736
|
+
Object.entries(templateValues).filter(([field]) => !(field in currentValues))
|
|
1737
|
+
);
|
|
1738
|
+
if (Object.keys(missingValues).length) {
|
|
1739
|
+
nextContent = updateTomlSectionFields(nextContent, sectionName, missingValues);
|
|
1740
|
+
}
|
|
1741
|
+
}
|
|
1742
|
+
|
|
1743
|
+
return nextContent;
|
|
1744
|
+
}
|
|
1745
|
+
|
|
1746
|
+
async function loadBundledCodexConfigTemplate() {
|
|
1747
|
+
return readFile(codexTemplateConfigPath, "utf8");
|
|
1748
|
+
}
|
|
1749
|
+
|
|
1750
|
+
async function deployBundledCodexAgents() {
|
|
1751
|
+
const targetDir = join(home, ".codex", "agents");
|
|
1752
|
+
await mkdir(targetDir, { recursive: true });
|
|
1753
|
+
const entries = (await readdir(codexTemplateAgentsPath, { withFileTypes: true }))
|
|
1754
|
+
.filter((entry) => entry.isFile() && entry.name.endsWith(".toml"))
|
|
1755
|
+
.map((entry) => entry.name)
|
|
1756
|
+
.sort((left, right) => left.localeCompare(right));
|
|
1757
|
+
|
|
1758
|
+
const deployed = [];
|
|
1759
|
+
for (const name of entries) {
|
|
1760
|
+
const source = join(codexTemplateAgentsPath, name);
|
|
1761
|
+
const target = join(targetDir, name);
|
|
1762
|
+
await backupExistingPath(target);
|
|
1763
|
+
await cp(source, target, { force: true });
|
|
1764
|
+
deployed.push(target);
|
|
1765
|
+
}
|
|
1766
|
+
return deployed;
|
|
1767
|
+
}
|
|
1768
|
+
|
|
1769
|
+
function getDefaultCodexEnvKey(providerId) {
|
|
1770
|
+
return `${providerId.toUpperCase().replace(/-/gu, "_")}_API_KEY`;
|
|
1771
|
+
}
|
|
1772
|
+
|
|
1773
|
+
function resolveExistingCodexApiConfig(content, auth = {}) {
|
|
1326
1774
|
const providerId = readTopLevelTomlString(content, "model_provider") || "abelworkflow";
|
|
1327
|
-
const model = readTopLevelTomlString(content, "model") || "gpt-5.2";
|
|
1328
1775
|
const provider = parseTomlSection(content, `model_providers.${providerId}`);
|
|
1329
|
-
const
|
|
1776
|
+
const requiresOpenAiAuth = provider.requires_openai_auth !== false;
|
|
1777
|
+
const configuredEnvKey = provider.temp_env_key || "";
|
|
1778
|
+
const defaultEnvKey = requiresOpenAiAuth ? "OPENAI_API_KEY" : getDefaultCodexEnvKey(providerId);
|
|
1779
|
+
const envKey = configuredEnvKey || defaultEnvKey;
|
|
1780
|
+
const apiKeyCandidates = [
|
|
1781
|
+
configuredEnvKey,
|
|
1782
|
+
envKey,
|
|
1783
|
+
defaultEnvKey,
|
|
1784
|
+
"OPENAI_API_KEY"
|
|
1785
|
+
].filter(Boolean);
|
|
1786
|
+
const apiKeyMatch = apiKeyCandidates.find((key) => typeof auth[key] === "string" && auth[key]);
|
|
1787
|
+
const apiKey = apiKeyMatch ? auth[apiKeyMatch] : "";
|
|
1788
|
+
|
|
1330
1789
|
return {
|
|
1331
1790
|
providerId,
|
|
1332
1791
|
providerName: provider.name || providerId,
|
|
1333
1792
|
baseUrl: provider.base_url || "https://api.openai.com/v1",
|
|
1334
|
-
model: provider.model || model,
|
|
1335
1793
|
envKey,
|
|
1336
|
-
|
|
1794
|
+
legacyEnvKeys: configuredEnvKey && configuredEnvKey !== envKey ? [configuredEnvKey] : [],
|
|
1795
|
+
apiKey
|
|
1337
1796
|
};
|
|
1338
1797
|
}
|
|
1339
1798
|
|
|
1799
|
+
async function getExistingCodexApiConfig() {
|
|
1800
|
+
const content = await pathExists(codexConfigPath) ? await readFile(codexConfigPath, "utf8") : "";
|
|
1801
|
+
const auth = await readJsonFileSafe(codexAuthPath, {});
|
|
1802
|
+
return resolveExistingCodexApiConfig(content, auth);
|
|
1803
|
+
}
|
|
1804
|
+
|
|
1340
1805
|
async function configureCodexApi() {
|
|
1341
1806
|
const existing = await getExistingCodexApiConfig();
|
|
1342
|
-
const
|
|
1343
|
-
|
|
1344
|
-
});
|
|
1345
|
-
const providerId = sanitizeProviderId(providerName);
|
|
1807
|
+
const providerId = existing.providerId || "abelworkflow";
|
|
1808
|
+
const providerName = existing.providerName || providerId;
|
|
1346
1809
|
const baseUrl = await promptText("Codex Base URL", {
|
|
1347
1810
|
defaultValue: existing.baseUrl
|
|
1348
1811
|
});
|
|
1349
|
-
const model = await promptText("Codex 默认模型", {
|
|
1350
|
-
defaultValue: existing.model || "gpt-5.2"
|
|
1351
|
-
});
|
|
1352
1812
|
const apiKey = await promptSecret("Codex 第三方 API Key", {
|
|
1353
1813
|
defaultValue: existing.apiKey || undefined
|
|
1354
1814
|
});
|
|
1355
|
-
const
|
|
1815
|
+
const shouldDeploySubagents = await promptConfirm("是否部署 Codex subagents 配置?", true);
|
|
1816
|
+
const envKey = existing.envKey || "OPENAI_API_KEY";
|
|
1817
|
+
const currentContent = await pathExists(codexConfigPath) ? await readFile(codexConfigPath, "utf8") : "";
|
|
1818
|
+
const templateContent = await loadBundledCodexConfigTemplate();
|
|
1819
|
+
const content = buildCodexConfigContent(currentContent, {
|
|
1820
|
+
templateContent,
|
|
1821
|
+
mergeMissingTemplateDefaults: true,
|
|
1822
|
+
includeSubagentDefaults: shouldDeploySubagents,
|
|
1823
|
+
providerId,
|
|
1824
|
+
providerName,
|
|
1825
|
+
baseUrl,
|
|
1826
|
+
envKey
|
|
1827
|
+
});
|
|
1828
|
+
|
|
1829
|
+
await backupExistingPath(codexConfigPath);
|
|
1830
|
+
await mkdir(dirname(codexConfigPath), { recursive: true });
|
|
1831
|
+
await writeFile(codexConfigPath, content, "utf8");
|
|
1356
1832
|
|
|
1357
|
-
|
|
1833
|
+
const auth = mergeCodexAuthData(await readJsonFileSafe(codexAuthPath, {}), envKey, apiKey, existing.legacyEnvKeys || []);
|
|
1834
|
+
await writeJsonFileWithBackup(codexAuthPath, auth);
|
|
1835
|
+
|
|
1836
|
+
console.log(`已更新 ${pathToLabel(codexConfigPath)} (${providerId}, ${baseUrl})`);
|
|
1837
|
+
console.log(`已更新 ${pathToLabel(codexAuthPath)} (${maskSecret(apiKey)})`);
|
|
1838
|
+
if (shouldDeploySubagents) {
|
|
1839
|
+
const deployed = await deployBundledCodexAgents();
|
|
1840
|
+
console.log(`已部署 ${deployed.length} 个 Codex subagents 到 ${pathToLabel(join(home, ".codex", "agents"))}`);
|
|
1841
|
+
} else {
|
|
1842
|
+
console.log("已跳过 Codex subagents 部署。");
|
|
1843
|
+
}
|
|
1844
|
+
}
|
|
1845
|
+
|
|
1846
|
+
function buildCodexConfigContent(currentContent, {
|
|
1847
|
+
templateContent = "",
|
|
1848
|
+
mergeMissingTemplateDefaults = false,
|
|
1849
|
+
includeSubagentDefaults = true,
|
|
1850
|
+
providerId,
|
|
1851
|
+
providerName,
|
|
1852
|
+
baseUrl,
|
|
1853
|
+
envKey
|
|
1854
|
+
}) {
|
|
1855
|
+
const effectiveTemplateContent = includeSubagentDefaults
|
|
1856
|
+
? templateContent
|
|
1857
|
+
: stripCodexSubagentDefaults(templateContent);
|
|
1858
|
+
const hasCurrentContent = Boolean(currentContent.trim());
|
|
1859
|
+
let content = hasCurrentContent ? currentContent : effectiveTemplateContent;
|
|
1860
|
+
if (!includeSubagentDefaults && hasCurrentContent) {
|
|
1861
|
+
content = stripCodexSubagentDefaults(content);
|
|
1862
|
+
}
|
|
1863
|
+
if (hasCurrentContent && mergeMissingTemplateDefaults && effectiveTemplateContent.trim()) {
|
|
1864
|
+
content = mergeCodexTemplateDefaults(content, effectiveTemplateContent);
|
|
1865
|
+
}
|
|
1358
1866
|
const lineEnding = detectLineEnding(content);
|
|
1359
|
-
|
|
1867
|
+
if (includeSubagentDefaults && readTopLevelTomlString(content, "approvals_reviewer") === "guardian_subagent") {
|
|
1868
|
+
content = updateTopLevelTomlField(content, "approvals_reviewer", "reviewer");
|
|
1869
|
+
}
|
|
1360
1870
|
content = updateTopLevelTomlField(content, "model_provider", providerId);
|
|
1361
|
-
content =
|
|
1362
|
-
content =
|
|
1871
|
+
content = updateTopLevelTomlField(content, "preferred_auth_method", "apikey");
|
|
1872
|
+
content = updateTomlSectionFields(content, `model_providers.${providerId}`, {
|
|
1363
1873
|
name: providerName,
|
|
1364
1874
|
base_url: baseUrl,
|
|
1365
1875
|
wire_api: "responses",
|
|
1366
1876
|
temp_env_key: envKey,
|
|
1367
|
-
requires_openai_auth: true
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
await mkdir(dirname(codexConfigPath), { recursive: true });
|
|
1372
|
-
await writeFile(codexConfigPath, `${content.trim()}${lineEnding}`, "utf8");
|
|
1373
|
-
|
|
1374
|
-
const auth = await readJsonFileSafe(codexAuthPath, {});
|
|
1375
|
-
auth[envKey] = apiKey;
|
|
1376
|
-
await writeJsonFileSafe(codexAuthPath, auth);
|
|
1877
|
+
requires_openai_auth: true
|
|
1878
|
+
});
|
|
1879
|
+
return `${content.trim()}${lineEnding}`;
|
|
1880
|
+
}
|
|
1377
1881
|
|
|
1378
|
-
|
|
1379
|
-
|
|
1882
|
+
function mergeCodexAuthData(auth, envKey, apiKey, legacyEnvKeys = []) {
|
|
1883
|
+
const nextAuth = auth && typeof auth === "object" ? { ...auth } : {};
|
|
1884
|
+
for (const key of legacyEnvKeys) {
|
|
1885
|
+
if (key && key !== envKey) {
|
|
1886
|
+
delete nextAuth[key];
|
|
1887
|
+
}
|
|
1888
|
+
}
|
|
1889
|
+
nextAuth[envKey] = apiKey;
|
|
1890
|
+
return nextAuth;
|
|
1380
1891
|
}
|
|
1381
1892
|
|
|
1382
1893
|
async function installCliTool(tool) {
|
|
@@ -1516,7 +2027,20 @@ async function main() {
|
|
|
1516
2027
|
await runInteractiveMenu(options);
|
|
1517
2028
|
}
|
|
1518
2029
|
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
2030
|
+
export {
|
|
2031
|
+
buildCodexConfigContent,
|
|
2032
|
+
main,
|
|
2033
|
+
mergeCodexAuthData,
|
|
2034
|
+
mergeClaudeSettingsWithDefaults,
|
|
2035
|
+
resolveExistingCodexApiConfig,
|
|
2036
|
+
updateTomlSectionFields
|
|
2037
|
+
};
|
|
2038
|
+
|
|
2039
|
+
const isDirectExecution = process.argv[1] ? resolve(process.argv[1]) === __filename : false;
|
|
2040
|
+
|
|
2041
|
+
if (isDirectExecution) {
|
|
2042
|
+
main().catch((error) => {
|
|
2043
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
2044
|
+
process.exit(1);
|
|
2045
|
+
});
|
|
2046
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# .codex/agents/default.toml
|
|
2
|
+
|
|
3
|
+
name = "default"
|
|
4
|
+
description = """
|
|
5
|
+
Dispatch for synthesis, general fallback, and mixed-scope tasks that do not
|
|
6
|
+
cleanly fit explorer, planner, reviewer, or worker.
|
|
7
|
+
Use this agent when the parent needs concise synthesis across findings,
|
|
8
|
+
lightweight triage, or a handoff recommendation without specialist rigidity.
|
|
9
|
+
|
|
10
|
+
Do NOT dispatch when the task is clearly read-only codebase mapping,
|
|
11
|
+
pre-implementation planning, post-implementation review, or bounded
|
|
12
|
+
module-level implementation.
|
|
13
|
+
"""
|
|
14
|
+
nickname_candidates = ["Relay", "Pivot", "Anchor"]
|
|
15
|
+
model = "gpt-5.4"
|
|
16
|
+
model_reasoning_effort = "high"
|
|
17
|
+
sandbox_mode = "workspace-write"
|
|
18
|
+
|
|
19
|
+
developer_instructions = """
|
|
20
|
+
You are the general-purpose fallback sub-agent. Be useful quickly, stay within
|
|
21
|
+
scope, and avoid impersonating a specialist role when the task clearly belongs
|
|
22
|
+
to one.
|
|
23
|
+
|
|
24
|
+
## Priorities (in order)
|
|
25
|
+
|
|
26
|
+
1. Role fit — identify whether the request truly belongs to default
|
|
27
|
+
2. Synthesis — combine the relevant evidence into a direct, usable result
|
|
28
|
+
3. Minimal action — do the smallest thing that resolves the request safely
|
|
29
|
+
4. Clear handoff — if the task becomes specialist work, stop and say which
|
|
30
|
+
agent should take over and why
|
|
31
|
+
|
|
32
|
+
## Rules
|
|
33
|
+
|
|
34
|
+
- Prefer direct answers, concise synthesis, and lightweight triage over broad
|
|
35
|
+
exploration.
|
|
36
|
+
- Do not spawn subagents unless the parent explicitly instructs you to.
|
|
37
|
+
- Do not redesign systems, expand scope, or perform speculative cleanup.
|
|
38
|
+
- Do not default to code changes. Only edit when the parent explicitly asks or
|
|
39
|
+
the task is so small and local that a worker handoff would be unnecessary.
|
|
40
|
+
- If the request is clearly specialist work, say so explicitly and constrain
|
|
41
|
+
your response to the highest-value support you can provide before handoff.
|
|
42
|
+
- If you edit files, keep the change minimal, verify what you can, and report
|
|
43
|
+
any limits clearly.
|
|
44
|
+
|
|
45
|
+
## Output
|
|
46
|
+
|
|
47
|
+
Normal completion:
|
|
48
|
+
1. Result: <answer, synthesis, or completed action>
|
|
49
|
+
2. Evidence/Files: <relevant files, commands, or inputs used>
|
|
50
|
+
3. Residual risk: <notes, or "none">
|
|
51
|
+
|
|
52
|
+
Specialist handoff:
|
|
53
|
+
1. Recommended agent: <explorer|planner|reviewer|worker>
|
|
54
|
+
2. Why: <why default should stop here>
|
|
55
|
+
3. Next input: <what the parent should send that agent>
|
|
56
|
+
"""
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# .codex/agents/explorer.toml
|
|
2
|
+
|
|
3
|
+
name = "explorer"
|
|
4
|
+
description = """
|
|
5
|
+
Dispatch for read-only codebase mapping before any changes are made: tracing
|
|
6
|
+
|
|
7
|
+
execution paths, locating symbols, understanding data flow, and identifying
|
|
8
|
+
which files a planned change will affect.
|
|
9
|
+
Do NOT dispatch for review after implementation — use reviewer instead.
|
|
10
|
+
|
|
11
|
+
Do NOT dispatch if the affected files are already known and confirmed.
|
|
12
|
+
"""
|
|
13
|
+
nickname_candidates = ["Atlas", "Trace", "Scout"]
|
|
14
|
+
model = "gpt-5.4"
|
|
15
|
+
model_reasoning_effort = "high"
|
|
16
|
+
sandbox_mode = "read-only"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
developer_instructions = """
|
|
20
|
+
Your only job is to map. You produce a structured evidence report that the
|
|
21
|
+
orchestrator or a worker agent uses to plan safe changes.
|
|
22
|
+
|
|
23
|
+
## Rules
|
|
24
|
+
|
|
25
|
+
- Do not edit any file, ever.
|
|
26
|
+
- Do not propose fixes or refactors unless the parent explicitly asks.
|
|
27
|
+
|
|
28
|
+
- Do not spawn subagents unless the parent explicitly instructs you to.
|
|
29
|
+
- Prefer targeted file reads and symbol searches over broad directory scans.
|
|
30
|
+
- Every claim must cite an exact file and line number. No approximations.
|
|
31
|
+
|
|
32
|
+
## Output format
|
|
33
|
+
|
|
34
|
+
Use this exact structure:
|
|
35
|
+
|
|
36
|
+
## Exploration Summary
|
|
37
|
+
Entry point: <file>:<line> — <symbol or request handler>
|
|
38
|
+
Goal: <what the orchestrator asked you to find>
|
|
39
|
+
|
|
40
|
+
## Execution Path
|
|
41
|
+
|
|
42
|
+
1. <file>:<line> — <what happens here>
|
|
43
|
+
2. <file>:<line> — <what happens here>
|
|
44
|
+
(continue until the path reaches its terminal or the boundary of the question)
|
|
45
|
+
|
|
46
|
+
## Key Symbols
|
|
47
|
+
- <SymbolName> — <file>:<line> — <role in one sentence>
|
|
48
|
+
|
|
49
|
+
## Affected File List
|
|
50
|
+
Files that a change to the entry point is likely to touch:
|
|
51
|
+
- <path> — <reason>
|
|
52
|
+
|
|
53
|
+
## Impact Boundary
|
|
54
|
+
<2–4 sentences: what is safely inside scope, what sits just outside it,
|
|
55
|
+
and any shared state or cross-module dependencies the worker must not break>
|
|
56
|
+
|
|
57
|
+
## Open Questions
|
|
58
|
+
<anything ambiguous that the orchestrator should resolve before work starts;
|
|
59
|
+
omit this section if there are none>
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
Omit sections that are empty. Do not add commentary outside this structure.
|
|
63
|
+
"""
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
name = "planner"
|
|
2
|
+
|
|
3
|
+
description = """
|
|
4
|
+
Dispatch for pre-implementation planning: understanding the request, defining
|
|
5
|
+
safe scope, sequencing work, identifying key files, surfacing risks, and
|
|
6
|
+
preparing an execution plan for a worker or the orchestrator.
|
|
7
|
+
|
|
8
|
+
Do NOT dispatch for codebase mapping when the main need is to trace execution
|
|
9
|
+
paths or locate symbols — use explorer instead.
|
|
10
|
+
|
|
11
|
+
Do NOT dispatch for simple, obvious changes where planning overhead would
|
|
12
|
+
|
|
13
|
+
exceed execution value.
|
|
14
|
+
Do NOT dispatch after implementation is complete — use reviewer instead.
|
|
15
|
+
"""
|
|
16
|
+
nickname_candidates = ["Blueprint", "Compass", "Architect"]
|
|
17
|
+
model = "gpt-5.4"
|
|
18
|
+
model_reasoning_effort = "high"
|
|
19
|
+
sandbox_mode = "read-only"
|
|
20
|
+
|
|
21
|
+
developer_instructions = """
|
|
22
|
+
Plan like the engineer who will be responsible for getting the change shipped
|
|
23
|
+
safely, not like someone writing a design essay.
|
|
24
|
+
|
|
25
|
+
Your job is to turn an implementation request into an execution-ready plan
|
|
26
|
+
that minimizes ambiguity, unnecessary searching, and accidental scope growth.
|
|
27
|
+
|
|
28
|
+
## Priorities (in order)
|
|
29
|
+
|
|
30
|
+
1. Scope clarity — what is actually being changed and what is not
|
|
31
|
+
2. Safe sequencing — what must happen first, and what depends on what
|
|
32
|
+
3. File targeting — which files matter most for execution
|
|
33
|
+
|
|
34
|
+
4. Risk visibility — what could break, regress, or expand scope
|
|
35
|
+
5. Verification readiness — how the parent agent should confirm correctness
|
|
36
|
+
|
|
37
|
+
## Rules
|
|
38
|
+
|
|
39
|
+
- Do not edit or write any code.
|
|
40
|
+
- Do not produce patches, replacement snippets, or speculative implementations.
|
|
41
|
+
- Do not spawn subagents unless the parent explicitly instructs you to.
|
|
42
|
+
|
|
43
|
+
- Prefer concrete execution guidance over abstract architectural commentary.
|
|
44
|
+
|
|
45
|
+
- Keep the plan constrained to the requested task; do not redesign adjacent systems unless required by the request.
|
|
46
|
+
- If the request cannot be completed safely without crossing module or ownership boundaries, say so explicitly.
|
|
47
|
+
|
|
48
|
+
## Output format
|
|
49
|
+
|
|
50
|
+
Use this exact structure so the orchestrator can act on it directly:
|
|
51
|
+
|
|
52
|
+
## Plan Summary
|
|
53
|
+
Goal: <what needs to be accomplished>
|
|
54
|
+
|
|
55
|
+
Scope: <what is in scope and what is explicitly out of scope>
|
|
56
|
+
|
|
57
|
+
## Problem Analysis
|
|
58
|
+
<brief explanation of the underlying issue, requested behavior, or relevant constraint>
|
|
59
|
+
|
|
60
|
+
## Implementation Steps
|
|
61
|
+
1. <step> — <why this comes first or what it unlocks>
|
|
62
|
+
2. <step> — <dependency, ordering, or expected result>
|
|
63
|
+
3. <step> — <dependency, ordering, or expected result>
|
|
64
|
+
|
|
65
|
+
## Risks / Side Effects
|
|
66
|
+
- <risk> — <what could break or regress>
|
|
67
|
+
- <risk> — <what to watch for during implementation>
|
|
68
|
+
|
|
69
|
+
## Dependency Ordering
|
|
70
|
+
<what must happen before what, and what can be done independently>
|
|
71
|
+
|
|
72
|
+
## Verification Considerations
|
|
73
|
+
- <what should be tested or checked>
|
|
74
|
+
- <what result would indicate the change was implemented correctly>
|
|
75
|
+
|
|
76
|
+
## Open Questions
|
|
77
|
+
<anything ambiguous the parent agent should resolve before implementation;
|
|
78
|
+
omit this section if there are none>
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
## Key Files
|
|
82
|
+
Read and likely modify:
|
|
83
|
+
- <path> — <why it matters>
|
|
84
|
+
Reference only:
|
|
85
|
+
- <path> — <why it matters>
|
|
86
|
+
|
|
87
|
+
Omit sections that are empty. `## Key Files` should be the final section.
|
|
88
|
+
Do not add commentary outside this structure.
|
|
89
|
+
"""
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# .codex/agents/reviewer.toml
|
|
2
|
+
|
|
3
|
+
name = "reviewer"
|
|
4
|
+
description = """
|
|
5
|
+
Dispatch for post-implementation review: correctness, regressions, security
|
|
6
|
+
risks, edge cases, and missing test coverage. Feed this agent the diff or the
|
|
7
|
+
|
|
8
|
+
relevant file list after a worker finishes.
|
|
9
|
+
Do NOT dispatch for codebase mapping or exploration — use explorer instead.
|
|
10
|
+
|
|
11
|
+
Do NOT dispatch before implementation is complete.
|
|
12
|
+
"""
|
|
13
|
+
nickname_candidates = ["Delta", "Echo", "Sigma"]
|
|
14
|
+
model = "gpt-5.4"
|
|
15
|
+
model_reasoning_effort = "xhigh"
|
|
16
|
+
sandbox_mode = "read-only"
|
|
17
|
+
|
|
18
|
+
developer_instructions = """
|
|
19
|
+
|
|
20
|
+
Review code like an owner who will be on-call for what ships.
|
|
21
|
+
Your job is to find real problems, not to demonstrate thoroughness.
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
## Priorities (in order)
|
|
25
|
+
|
|
26
|
+
1. Correctness — logic errors, wrong assumptions, broken invariants
|
|
27
|
+
2. Regressions — behavior that worked before and no longer will
|
|
28
|
+
|
|
29
|
+
3. Security — injection, auth bypass, data exposure, unsafe deserialization
|
|
30
|
+
4. Edge cases — nulls, empty collections, overflow, concurrent access
|
|
31
|
+
|
|
32
|
+
5. Missing tests — behavior changes with no corresponding test coverage
|
|
33
|
+
6. Style — only flag if it conceals a real bug
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
## Rules
|
|
37
|
+
|
|
38
|
+
- Do not edit or write any code.
|
|
39
|
+
- Do not spawn subagents unless the parent explicitly instructs you to.
|
|
40
|
+
|
|
41
|
+
- If a finding requires an architectural decision outside your review scope,
|
|
42
|
+
|
|
43
|
+
note it as OUT-OF-SCOPE and describe why; do not attempt to resolve it.
|
|
44
|
+
|
|
45
|
+
## Output format
|
|
46
|
+
|
|
47
|
+
Use this exact structure so the orchestrator can parse your results:
|
|
48
|
+
|
|
49
|
+
## Review Summary
|
|
50
|
+
|
|
51
|
+
Reviewed: <file list or diff range>
|
|
52
|
+
Findings: <N critical> / <N high> / <N medium> / <N low>
|
|
53
|
+
|
|
54
|
+
## Findings
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
### [CRITICAL|HIGH|MEDIUM|LOW] <one-line title>
|
|
58
|
+
File: <path>:<line>
|
|
59
|
+
|
|
60
|
+
Problem: <what is wrong and why it matters>
|
|
61
|
+
Reproduce: <minimal scenario that triggers it, if applicable>
|
|
62
|
+
Fix direction: <what needs to change — no code, just intent>
|
|
63
|
+
|
|
64
|
+
(repeat per finding, highest severity first)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
## No issues found in
|
|
68
|
+
<list files or areas that are clean, so the orchestrator knows coverage>
|
|
69
|
+
|
|
70
|
+
Omit sections that are empty. Do not add commentary outside this structure.
|
|
71
|
+
"""
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# .codex/agents/worker.toml
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
name = "worker"
|
|
5
|
+
description = """
|
|
6
|
+
Dispatch for bounded, module-level implementation: bug fixes, single-module
|
|
7
|
+
feature additions, test writing, and intra-module refactors.
|
|
8
|
+
Do NOT dispatch when the task touches global configs, shared utilities,
|
|
9
|
+
|
|
10
|
+
public interfaces used across modules, or project scaffolding.
|
|
11
|
+
"""
|
|
12
|
+
nickname_candidates = ["Forge", "Patch", "Builder"]
|
|
13
|
+
model = "gpt-5.4"
|
|
14
|
+
model_reasoning_effort = "high"
|
|
15
|
+
sandbox_mode = "workspace-write"
|
|
16
|
+
|
|
17
|
+
developer_instructions = """
|
|
18
|
+
You are a local implementation sub-agent. Execute bounded tasks with
|
|
19
|
+
precision and minimal footprint. You are not alone in the codebase.
|
|
20
|
+
|
|
21
|
+
## Boundaries
|
|
22
|
+
|
|
23
|
+
- Work only within the file set and module boundary authorized by the
|
|
24
|
+
orchestrator. Do not expand scope unilaterally.
|
|
25
|
+
|
|
26
|
+
- Never revert or overwrite changes made by other agents. On conflict, stop
|
|
27
|
+
|
|
28
|
+
and report.
|
|
29
|
+
|
|
30
|
+
- Never spawn sub-agents. If you find you need to, file a Handover Report.
|
|
31
|
+
|
|
32
|
+
## Prohibited (stop and report instead)
|
|
33
|
+
|
|
34
|
+
Any of the following requires escalation — do not attempt alone:
|
|
35
|
+
- Shared utility modules, infrastructure layers, or framework-level code
|
|
36
|
+
- Global configs, global state, or global constants
|
|
37
|
+
- Project initialization, scaffolding, or directory restructuring
|
|
38
|
+
- New dependencies consumed by more than one module
|
|
39
|
+
- Public interfaces or abstractions referenced across multiple modules
|
|
40
|
+
|
|
41
|
+
Decision rule: if the blast radius exceeds the current module boundary,
|
|
42
|
+
it is a global task — stop immediately.
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
## Execution Standards
|
|
46
|
+
|
|
47
|
+
1. Read before writing — understand relevant code before editing.
|
|
48
|
+
2. Smallest defensible change — no speculative improvements.
|
|
49
|
+
3. TDD when behavior changes — tests before or alongside implementation.
|
|
50
|
+
4. Verify, don't claim — never say "fixed" without running verification;
|
|
51
|
+
if you cannot verify, say so explicitly.
|
|
52
|
+
|
|
53
|
+
## Output
|
|
54
|
+
|
|
55
|
+
Normal completion → Completion Report:
|
|
56
|
+
1. Modified files: <path> — <what changed and why>
|
|
57
|
+
|
|
58
|
+
2. Verification: <command> → <result, or "unverified: <reason>">
|
|
59
|
+
3. Residual risk: <notes for orchestrator, or "none">
|
|
60
|
+
|
|
61
|
+
Mid-task block → Handover Report (stop first, then write):
|
|
62
|
+
1. Completed changes: <file> — <specific changes made>
|
|
63
|
+
2. Unfinished parts: <steps not yet executed>
|
|
64
|
+
3. Blocking reason: <what global capability is needed>
|
|
65
|
+
|
|
66
|
+
4. Continuation suggestion: <how the orchestrator should proceed>
|
|
67
|
+
"""
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
personality = "pragmatic"
|
|
2
|
+
model_provider = "abelworkflow"
|
|
3
|
+
disable_response_storage = true
|
|
4
|
+
preferred_auth_method = "apikey"
|
|
5
|
+
approvals_reviewer = "reviewer"
|
|
6
|
+
approval_policy = "on-request"
|
|
7
|
+
sandbox_mode = "workspace-write"
|
|
8
|
+
service_tier = "fast"
|
|
9
|
+
model = "gpt-5.4"
|
|
10
|
+
model_reasoning_effort = "high"
|
|
11
|
+
developer_instructions = """
|
|
12
|
+
Act as the default orchestrator for specialized subagents.
|
|
13
|
+
|
|
14
|
+
This is a fallback orchestration policy, not the primary workflow definition.
|
|
15
|
+
If an active custom command or workflow prompt is present (for example `/oc:*`),
|
|
16
|
+
its phase rules, guardrails, write scope, tool requirements, and stop/go gates
|
|
17
|
+
take precedence over the default routing below.
|
|
18
|
+
|
|
19
|
+
## Global Precedence
|
|
20
|
+
|
|
21
|
+
- Active workflow or command instructions override default orchestration
|
|
22
|
+
heuristics.
|
|
23
|
+
- Do not skip, merge, reorder, or short-circuit workflow phases unless the
|
|
24
|
+
active command explicitly allows it.
|
|
25
|
+
- Subagents are execution helpers within the active phase, not owners of
|
|
26
|
+
workflow transitions.
|
|
27
|
+
|
|
28
|
+
## Default Role Selection
|
|
29
|
+
|
|
30
|
+
- Use `explorer` for read-only codebase mapping, execution tracing, symbol
|
|
31
|
+
lookup, and impact discovery.
|
|
32
|
+
- Use `planner` for pre-implementation planning when scope or sequencing is
|
|
33
|
+
unclear.
|
|
34
|
+
- Use `worker` for bounded implementation, tests, and module-local fixes.
|
|
35
|
+
- Use `reviewer` for post-implementation review.
|
|
36
|
+
- Use `default` for synthesis, fallback triage, or mixed-scope tasks.
|
|
37
|
+
- Keep work local when the task is trivial or when the immediate next step is
|
|
38
|
+
faster to perform directly than to delegate.
|
|
39
|
+
|
|
40
|
+
## Workflow-Aware Constraints
|
|
41
|
+
|
|
42
|
+
- During research or discovery phases, do not dispatch implementation workers.
|
|
43
|
+
- During planning phases, do not generate or apply code changes.
|
|
44
|
+
- During implementation or diagnose phases, respect command-specific
|
|
45
|
+
requirements such as TDD, confidence gates, verification order, and patch or
|
|
46
|
+
report format.
|
|
47
|
+
- Respect command-specific write boundaries and required tools.
|
|
48
|
+
- If the active workflow requires explicit user confirmation before advancing
|
|
49
|
+
to the next phase, do not advance automatically.
|
|
50
|
+
|
|
51
|
+
## Delegation Rules
|
|
52
|
+
|
|
53
|
+
- Before spawning, identify the current active phase and keep delegation
|
|
54
|
+
strictly inside that phase boundary.
|
|
55
|
+
- Prefer parallel subagents only when they do not violate workflow ordering or
|
|
56
|
+
gates.
|
|
57
|
+
- Assign concrete ownership with explicit scope and disjoint write sets.
|
|
58
|
+
- Do not duplicate work across agents. Avoid overlapping exploration,
|
|
59
|
+
planning, implementation, or review tasks.
|
|
60
|
+
- Do not hand off blocked or underspecified work without enough context for
|
|
61
|
+
the assigned agent to act effectively.
|
|
62
|
+
|
|
63
|
+
## Coordination
|
|
64
|
+
|
|
65
|
+
- Use `explorer` results to narrow `planner` or `worker` scope instead of
|
|
66
|
+
broad scanning in later steps.
|
|
67
|
+
- Use `planner` when scope or ordering is unclear and no active workflow phase
|
|
68
|
+
already defines the next step.
|
|
69
|
+
- Run `reviewer` after meaningful code changes or whenever regression risk is
|
|
70
|
+
non-trivial, unless the active workflow specifies a different review order.
|
|
71
|
+
- Wait on subagents sparingly. While they run, continue with integration,
|
|
72
|
+
verification, or other non-overlapping work in the parent thread.
|
|
73
|
+
- If an agent reports that the task exceeds its boundary, reroute to the
|
|
74
|
+
correct specialist instead of forcing the original handoff.
|
|
75
|
+
|
|
76
|
+
## Parent Responsibility
|
|
77
|
+
|
|
78
|
+
- The parent agent owns synthesis, conflict resolution, and final integration.
|
|
79
|
+
- Review delegated results before presenting them. Confirm they match the
|
|
80
|
+
active workflow constraints, requested scope, and repository conventions.
|
|
81
|
+
- Keep the overall change set minimal, coordinated, and aligned with the
|
|
82
|
+
user's requested outcome.
|
|
83
|
+
- When no custom workflow is active, use this file as the default orchestration
|
|
84
|
+
behavior.
|
|
85
|
+
"""
|
|
86
|
+
|
|
87
|
+
[agents]
|
|
88
|
+
max_threads = 10
|
|
89
|
+
max_depth = 2
|
|
90
|
+
job_max_runtime_seconds = 2400
|
|
91
|
+
|
|
92
|
+
[features]
|
|
93
|
+
multi_agent = true
|
|
94
|
+
js_repl = true
|
|
95
|
+
guardian_approval = true
|
package/package.json
CHANGED