@sechroom/cli 2026.7.17 → 2026.7.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +75 -11
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -657,9 +657,14 @@ async function runApi(label, fn) {
|
|
|
657
657
|
s.succeed();
|
|
658
658
|
return res.data;
|
|
659
659
|
}
|
|
660
|
-
function
|
|
660
|
+
function formatFailureMessage(error) {
|
|
661
661
|
let msg;
|
|
662
|
-
if (
|
|
662
|
+
if (error instanceof Error) {
|
|
663
|
+
msg = error.message || error.name;
|
|
664
|
+
if (error.cause instanceof Error && error.cause.message) {
|
|
665
|
+
msg += `: ${error.cause.message}`;
|
|
666
|
+
}
|
|
667
|
+
} else if (typeof error === "object" && error !== null && "title" in error) {
|
|
663
668
|
const problem = error;
|
|
664
669
|
msg = String(problem.title);
|
|
665
670
|
if (problem.errors && typeof problem.errors === "object") {
|
|
@@ -674,6 +679,10 @@ ${detail.join("\n")}`;
|
|
|
674
679
|
} else {
|
|
675
680
|
msg = String(error);
|
|
676
681
|
}
|
|
682
|
+
return msg;
|
|
683
|
+
}
|
|
684
|
+
function fail(error) {
|
|
685
|
+
const msg = formatFailureMessage(error);
|
|
677
686
|
process.stderr.write(`error: ${msg}
|
|
678
687
|
`);
|
|
679
688
|
process.exit(1);
|
|
@@ -1140,7 +1149,10 @@ function resolveReferences(systemRows, personalRows, surface) {
|
|
|
1140
1149
|
}
|
|
1141
1150
|
|
|
1142
1151
|
// src/setup/skill-resolution-io.ts
|
|
1143
|
-
var AGENT_TARGET = {
|
|
1152
|
+
var AGENT_TARGET = {
|
|
1153
|
+
"claude-code": "claude-agent",
|
|
1154
|
+
"gpt-codex": "gpt-codex-agent"
|
|
1155
|
+
};
|
|
1144
1156
|
function agentTargetFor(surface) {
|
|
1145
1157
|
return AGENT_TARGET[surface] ?? `${surface}-agent`;
|
|
1146
1158
|
}
|
|
@@ -1205,12 +1217,58 @@ function writeSkills(dir, skills, surface) {
|
|
|
1205
1217
|
if (written.length) recordMaterialisedSkills(dir, DEFAULT_SKILLS_SLUG, written, { surface });
|
|
1206
1218
|
return written;
|
|
1207
1219
|
}
|
|
1220
|
+
function splitAgentFrontmatter(body) {
|
|
1221
|
+
body = body.replaceAll("\r\n", "\n");
|
|
1222
|
+
if (!body.startsWith("---\n")) return { instructions: body };
|
|
1223
|
+
const end = body.indexOf("\n---\n", 4);
|
|
1224
|
+
if (end < 0) return { instructions: body };
|
|
1225
|
+
const frontmatter = body.slice(4, end);
|
|
1226
|
+
const field = (key) => {
|
|
1227
|
+
const line = frontmatter.split("\n").find((candidate) => candidate.startsWith(`${key}:`));
|
|
1228
|
+
return line?.slice(key.length + 1).trim().replace(/^(["'])(.*)\1$/, "$2");
|
|
1229
|
+
};
|
|
1230
|
+
return { name: field("name"), description: field("description"), instructions: body.slice(end + 5).trimStart() };
|
|
1231
|
+
}
|
|
1232
|
+
function validateCodexSkills(skills) {
|
|
1233
|
+
for (const skill of skills) {
|
|
1234
|
+
const parsed = splitAgentFrontmatter(skill.body);
|
|
1235
|
+
if (parsed.name !== skill.name || !parsed.description) {
|
|
1236
|
+
throw new Error(
|
|
1237
|
+
`Codex skill '${skill.name}' must begin with YAML frontmatter containing matching name and a description.`
|
|
1238
|
+
);
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
var WORKER_NAMES = ["substrate-drafter", "substrate-miner", "substrate-verifier"];
|
|
1243
|
+
function validateCodexWorkerDependencies(skills, agents) {
|
|
1244
|
+
const available = new Set(agents.map((agent) => agent.name));
|
|
1245
|
+
const missing = WORKER_NAMES.filter(
|
|
1246
|
+
(worker) => skills.some((skill) => skill.body.includes(worker)) && !available.has(worker)
|
|
1247
|
+
);
|
|
1248
|
+
if (missing.length) {
|
|
1249
|
+
throw new Error(`Codex skills reference missing agent template(s): ${missing.join(", ")}.`);
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
function codexAgentToml(agent) {
|
|
1253
|
+
const parsed = splitAgentFrontmatter(agent.body);
|
|
1254
|
+
if (!parsed.description) {
|
|
1255
|
+
throw new Error(`Codex agent '${agent.name}' requires a description in its leading YAML frontmatter.`);
|
|
1256
|
+
}
|
|
1257
|
+
return [
|
|
1258
|
+
`name = ${JSON.stringify(parsed.name || agent.name)}`,
|
|
1259
|
+
`description = ${JSON.stringify(parsed.description)}`,
|
|
1260
|
+
`developer_instructions = ${JSON.stringify(parsed.instructions.trimEnd())}`,
|
|
1261
|
+
""
|
|
1262
|
+
].join("\n");
|
|
1263
|
+
}
|
|
1208
1264
|
function writeAgents(dir, agents, surface) {
|
|
1209
1265
|
if (agents.length) mkdirSync3(dir, { recursive: true });
|
|
1210
1266
|
const written = [];
|
|
1211
1267
|
for (const a of agents) {
|
|
1212
|
-
const
|
|
1213
|
-
|
|
1268
|
+
const codex = surface === CLIENT_SURFACE.codex;
|
|
1269
|
+
const file = `${a.name}.${codex ? "toml" : "md"}`;
|
|
1270
|
+
const body = codex ? codexAgentToml(a) : a.body.endsWith("\n") ? a.body : a.body + "\n";
|
|
1271
|
+
writeFileSync3(join4(dir, file), body);
|
|
1214
1272
|
written.push(file);
|
|
1215
1273
|
}
|
|
1216
1274
|
if (written.length) recordMaterialisedSkills(dir, DEFAULT_SKILLS_SLUG, written, { surface });
|
|
@@ -1239,7 +1297,7 @@ var AGENT_SPEC = {
|
|
|
1239
1297
|
dir: agentsDir,
|
|
1240
1298
|
resolve: resolveAgentSet,
|
|
1241
1299
|
write: writeAgents,
|
|
1242
|
-
supportsCodex:
|
|
1300
|
+
supportsCodex: true
|
|
1243
1301
|
};
|
|
1244
1302
|
function scopeOf(opts) {
|
|
1245
1303
|
return opts.local ? "project" : resolveScope(opts.scope);
|
|
@@ -1315,6 +1373,10 @@ async function runInstall(spec, cmd, opts) {
|
|
|
1315
1373
|
const rows = await fetchTemplateRows(cfg, personalWorkspaceId);
|
|
1316
1374
|
const results = targets.map((t) => {
|
|
1317
1375
|
const items = spec.resolve(rows, t.surface);
|
|
1376
|
+
if (t.client === "codex" && spec.kind === "skill") {
|
|
1377
|
+
validateCodexSkills(items);
|
|
1378
|
+
validateCodexWorkerDependencies(items, resolveAgentSet(rows, t.surface));
|
|
1379
|
+
}
|
|
1318
1380
|
const refs = spec.kind === "skill" ? resolveReferenceSet(rows, t.surface) : [];
|
|
1319
1381
|
const written = dryRun ? items.map((i) => i.name) : spec.write(t.dir, items, t.surface);
|
|
1320
1382
|
const refsWritten = dryRun ? refs.map((r) => r.name) : writeReferencesIntoSkillDirs(t.dir, items, refs);
|
|
@@ -1440,17 +1502,19 @@ function registerAgents(program2) {
|
|
|
1440
1502
|
`
|
|
1441
1503
|
Examples:
|
|
1442
1504
|
$ sechroom agents install materialise your installed agents to ~/.claude/agents
|
|
1505
|
+
$ sechroom agents install --client codex materialise native TOML agents to ~/.codex/agents
|
|
1443
1506
|
$ sechroom agents install --scope project write them to ./.claude/agents instead
|
|
1444
1507
|
$ sechroom agents install --claude-config-dir ~/.claude-work target another instance
|
|
1445
1508
|
$ sechroom agents list what's materialised on disk
|
|
1446
1509
|
$ sechroom agents clean remove the materialised agent files
|
|
1447
1510
|
|
|
1448
|
-
Agents are resolved from the agent target (target:claude-agent
|
|
1449
|
-
workers your loop skills call
|
|
1511
|
+
Agents are resolved from the client's agent target (target:claude-agent or
|
|
1512
|
+
target:gpt-codex-agent), the dispatchable workers your loop skills call
|
|
1513
|
+
(e.g. find-prior-art \u2192 substrate-miner).`
|
|
1450
1514
|
);
|
|
1451
|
-
agents.command("install").description("Materialise your installed subagents to disk (the already-installed bundle \u2014 no server install)").option("--scope <scope>", "global (config dir / CLAUDE_CONFIG_DIR) or project (<cwd>/.claude) \u2014 default global").option("--local", "alias for --scope project").option("--dry-run", "print what would be written; write nothing").option("--json", "machine output").action((opts, cmd) => runInstall(AGENT_SPEC, cmd, opts));
|
|
1452
|
-
agents.command("list").description("List the subagents materialised on disk (per resolved config dir)").option("--scope <scope>", "global (config dir / CLAUDE_CONFIG_DIR) or project (<cwd>/.claude) \u2014 default global").option("--local", "alias for --scope project").option("--json", "machine output").action((opts, cmd) => runList(AGENT_SPEC, cmd, opts));
|
|
1453
|
-
agents.command("clean [slug]").description(`Remove subagent files materialised to disk (default ${DEFAULT_SKILLS_SLUG})`).option("--scope <scope>", "global (config dir / CLAUDE_CONFIG_DIR) or project (<cwd>/.claude) \u2014 default global").option("--local", "alias for --scope project").option("--json", "machine output").action((slugArg, opts, cmd) => runClean(AGENT_SPEC, cmd, opts, slugArg));
|
|
1515
|
+
agents.command("install").description("Materialise your installed subagents to disk (the already-installed bundle \u2014 no server install)").option("--scope <scope>", "global (config dir / CLAUDE_CONFIG_DIR) or project (<cwd>/.claude) \u2014 default global").option("--local", "alias for --scope project").option("--dry-run", "print what would be written; write nothing").option("--client <client>", "claude, codex, or all").option("--json", "machine output").action((opts, cmd) => runInstall(AGENT_SPEC, cmd, opts));
|
|
1516
|
+
agents.command("list").description("List the subagents materialised on disk (per resolved config dir)").option("--scope <scope>", "global (config dir / CLAUDE_CONFIG_DIR) or project (<cwd>/.claude) \u2014 default global").option("--local", "alias for --scope project").option("--json", "machine output").option("--client <client>", "claude, codex, or all").action((opts, cmd) => runList(AGENT_SPEC, cmd, opts));
|
|
1517
|
+
agents.command("clean [slug]").description(`Remove subagent files materialised to disk (default ${DEFAULT_SKILLS_SLUG})`).option("--scope <scope>", "global (config dir / CLAUDE_CONFIG_DIR) or project (<cwd>/.claude) \u2014 default global").option("--local", "alias for --scope project").option("--json", "machine output").option("--client <client>", "claude, codex, or all").action((slugArg, opts, cmd) => runClean(AGENT_SPEC, cmd, opts, slugArg));
|
|
1454
1518
|
}
|
|
1455
1519
|
|
|
1456
1520
|
// src/commands/channel.ts
|
package/package.json
CHANGED