@sechroom/cli 2026.6.25 → 2026.6.26
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 +54 -16
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2681,6 +2681,24 @@ function codeLanePrefix(clients) {
|
|
|
2681
2681
|
for (const c of CLIENT_PRIORITY) if (clients.includes(c)) return CODE_LANE_PREFIX_BY_CLIENT[c];
|
|
2682
2682
|
return "claude-code";
|
|
2683
2683
|
}
|
|
2684
|
+
async function inferLanes(cfg, clients) {
|
|
2685
|
+
let wf;
|
|
2686
|
+
let profile;
|
|
2687
|
+
try {
|
|
2688
|
+
const client = await makeClient(cfg);
|
|
2689
|
+
[wf, profile] = await Promise.all([
|
|
2690
|
+
client.GET("/me/workflow-preferences", {}).then((r) => r.data).catch(() => void 0),
|
|
2691
|
+
client.GET("/me/profile", {}).then((r) => r.data).catch(() => void 0)
|
|
2692
|
+
]);
|
|
2693
|
+
} catch {
|
|
2694
|
+
}
|
|
2695
|
+
const handle = handleFromDisplayName(profile?.effectiveDisplayName);
|
|
2696
|
+
const prefix = codeLanePrefix(clients ?? ["claude-code"]);
|
|
2697
|
+
return {
|
|
2698
|
+
code: process.env.SECHROOM_CODE_LANE ?? wf?.defaultCodeLane ?? (handle ? `${prefix}-${handle}` : void 0),
|
|
2699
|
+
design: process.env.SECHROOM_DESIGN_LANE ?? wf?.defaultDesignLane ?? (handle ? `claude-design-${handle}` : void 0)
|
|
2700
|
+
};
|
|
2701
|
+
}
|
|
2684
2702
|
function writePin(code, design) {
|
|
2685
2703
|
const values = {};
|
|
2686
2704
|
if (code) values["code-lane"] = code;
|
|
@@ -2693,20 +2711,7 @@ function writePin(code, design) {
|
|
|
2693
2711
|
async function ensureLanePin(cfg, opts) {
|
|
2694
2712
|
if (opts.dryRun) return;
|
|
2695
2713
|
if (readSem()) return;
|
|
2696
|
-
|
|
2697
|
-
let profile;
|
|
2698
|
-
try {
|
|
2699
|
-
const client = await makeClient(cfg);
|
|
2700
|
-
[wf, profile] = await Promise.all([
|
|
2701
|
-
client.GET("/me/workflow-preferences", {}).then((r) => r.data).catch(() => void 0),
|
|
2702
|
-
client.GET("/me/profile", {}).then((r) => r.data).catch(() => void 0)
|
|
2703
|
-
]);
|
|
2704
|
-
} catch {
|
|
2705
|
-
}
|
|
2706
|
-
const handle = handleFromDisplayName(profile?.effectiveDisplayName);
|
|
2707
|
-
const prefix = codeLanePrefix(opts.clients ?? ["claude-code"]);
|
|
2708
|
-
const codeGuess = wf?.defaultCodeLane ?? (handle ? `${prefix}-${handle}` : void 0);
|
|
2709
|
-
const designGuess = wf?.defaultDesignLane ?? (handle ? `claude-design-${handle}` : void 0);
|
|
2714
|
+
const { code: codeGuess, design: designGuess } = await inferLanes(cfg, opts.clients);
|
|
2710
2715
|
if (!canPrompt() || opts.yes) {
|
|
2711
2716
|
if (opts.yes && (codeGuess || designGuess)) writePin(codeGuess, designGuess);
|
|
2712
2717
|
return;
|
|
@@ -3342,6 +3347,33 @@ ${style.bold(entry.path)} ${style.dim("is not bound yet.")}
|
|
|
3342
3347
|
reason: `unbound \u2014 bind to ${ws}`
|
|
3343
3348
|
};
|
|
3344
3349
|
}
|
|
3350
|
+
async function resolveFanoutLane(cfg, opts) {
|
|
3351
|
+
let code = opts.lane ?? process.env.SECHROOM_CODE_LANE;
|
|
3352
|
+
let design = opts.designLane ?? process.env.SECHROOM_DESIGN_LANE;
|
|
3353
|
+
if (!code || !design) {
|
|
3354
|
+
const inferred = await inferLanes(cfg, ["claude-code"]);
|
|
3355
|
+
code = code ?? inferred.code;
|
|
3356
|
+
design = design ?? inferred.design;
|
|
3357
|
+
}
|
|
3358
|
+
if (!opts.lane && !opts.yes && !opts.dryRun && canPrompt() && (code || design)) {
|
|
3359
|
+
process.stderr.write(`
|
|
3360
|
+
This fan-out will pin the same lane in every repo:
|
|
3361
|
+
`);
|
|
3362
|
+
if (code) process.stderr.write(` ${style.dim("code-lane")} = ${style.cyan(code)}
|
|
3363
|
+
`);
|
|
3364
|
+
if (design) process.stderr.write(` ${style.dim("design-lane")} = ${style.cyan(design)}
|
|
3365
|
+
`);
|
|
3366
|
+
if (!await promptYesNo("Use this lane for all repos?")) {
|
|
3367
|
+
code = await promptText("Code-lane id (blank = let each repo infer)?", code ?? "") || void 0;
|
|
3368
|
+
design = await promptText("Design-lane id (blank = skip)?", design ?? "") || void 0;
|
|
3369
|
+
}
|
|
3370
|
+
}
|
|
3371
|
+
if (code) process.env.SECHROOM_CODE_LANE = code;
|
|
3372
|
+
else delete process.env.SECHROOM_CODE_LANE;
|
|
3373
|
+
if (design) process.env.SECHROOM_DESIGN_LANE = design;
|
|
3374
|
+
else delete process.env.SECHROOM_DESIGN_LANE;
|
|
3375
|
+
return { code, design };
|
|
3376
|
+
}
|
|
3345
3377
|
async function runRecurse(cfg, g, opts) {
|
|
3346
3378
|
const { yes, dryRun, json } = opts;
|
|
3347
3379
|
const root = process.cwd();
|
|
@@ -3359,6 +3391,9 @@ async function runRecurse(cfg, g, opts) {
|
|
|
3359
3391
|
process.stderr.write(`${style.bold("onboard --recurse")} ${style.dim(`(${entries.length} repo${entries.length === 1 ? "" : "s"} from ${sourceLabel})`)}
|
|
3360
3392
|
`);
|
|
3361
3393
|
}
|
|
3394
|
+
const lane = await resolveFanoutLane(cfg, { lane: opts.lane, designLane: opts.designLane, yes, dryRun });
|
|
3395
|
+
if (!json && lane.code) process.stderr.write(`${ok("\u2713")} lane ${style.cyan(lane.code)}${lane.design ? ` ${style.dim(`/ ${lane.design}`)}` : ""} for every repo
|
|
3396
|
+
`);
|
|
3362
3397
|
const client = await makeClient(cfg);
|
|
3363
3398
|
const plans = [];
|
|
3364
3399
|
for (const entry of entries) plans.push(await planRecurseChild(entry, root, client, { yes, dryRun }));
|
|
@@ -3370,7 +3405,7 @@ async function runRecurse(cfg, g, opts) {
|
|
|
3370
3405
|
summarizeFanout(results, { dryRun });
|
|
3371
3406
|
}
|
|
3372
3407
|
function registerOnboard(program2) {
|
|
3373
|
-
program2.command("onboard").description("Guided first-run setup: configure, sign in, set timezone, detect clients, and wire this project").option("--recurse", "orchestration-root mode: onboard every child repo under this dir (auto-discovered, or from ./.sechroom/repos.json) \u2014 refreshes bound repos, prompts a workspace per new one", false).option("--client <list>", `comma-separated clients (${ALL_CLIENT_KEYS.join(", ")}) or 'all' (default: auto-detected)`).option("--local", "save the binding (tenant + base URL + workspace) to a committed .sechroom.json in this repo instead of the global config", false).option("--workspace <id>", "bind this directory to a workspace (skips the interactive workspace pick)").option("--cli-only", "configure the CLI only \u2014 don't wire any AI client (no MCP config, no agent files)", false).option("--no-mcp", "skip the MCP server config (.mcp.json etc.); still write the agent instruction files").option("--copy", "make a personal copy of the agent instructions you can edit (default: prompt on a TTY, else skip)").option("--dry-run", "walk through without writing files or changing the profile", false).option("--refresh", "re-fetch descriptors and refresh any out-of-date managed blocks (local edits preserved to .proposed)", false).option("--force", "rewrite every managed block, overwriting local edits inside the markers (content outside untouched)", false).option("--check", "report whether anything would change and exit (0 = all current, 1 = stale/drift/absent); writes nothing", false).option("-y, --yes", "non-interactive: accept defaults (system timezone, detected clients, global config, full wire)", false).addHelpText(
|
|
3408
|
+
program2.command("onboard").description("Guided first-run setup: configure, sign in, set timezone, detect clients, and wire this project").option("--recurse", "orchestration-root mode: onboard every child repo under this dir (auto-discovered, or from ./.sechroom/repos.json) \u2014 refreshes bound repos, prompts a workspace per new one", false).option("--lane <id>", "set the code-lane (substrate source identity) explicitly instead of inferring it; with --recurse it's used for every child repo").option("--design-lane <id>", "set the design-lane explicitly (substrate-authoring identity); with --recurse applies to every child").option("--client <list>", `comma-separated clients (${ALL_CLIENT_KEYS.join(", ")}) or 'all' (default: auto-detected)`).option("--local", "save the binding (tenant + base URL + workspace) to a committed .sechroom.json in this repo instead of the global config", false).option("--workspace <id>", "bind this directory to a workspace (skips the interactive workspace pick)").option("--cli-only", "configure the CLI only \u2014 don't wire any AI client (no MCP config, no agent files)", false).option("--no-mcp", "skip the MCP server config (.mcp.json etc.); still write the agent instruction files").option("--copy", "make a personal copy of the agent instructions you can edit (default: prompt on a TTY, else skip)").option("--dry-run", "walk through without writing files or changing the profile", false).option("--refresh", "re-fetch descriptors and refresh any out-of-date managed blocks (local edits preserved to .proposed)", false).option("--force", "rewrite every managed block, overwriting local edits inside the markers (content outside untouched)", false).option("--check", "report whether anything would change and exit (0 = all current, 1 = stale/drift/absent); writes nothing", false).option("-y, --yes", "non-interactive: accept defaults (system timezone, detected clients, global config, full wire)", false).addHelpText(
|
|
3374
3409
|
"after",
|
|
3375
3410
|
`
|
|
3376
3411
|
Examples:
|
|
@@ -3380,6 +3415,7 @@ Examples:
|
|
|
3380
3415
|
$ sechroom onboard --local save tenant + base URL to a committed ./.sechroom.json
|
|
3381
3416
|
$ sechroom onboard --workspace wsp_XX bind this directory to a workspace (no pick prompt)
|
|
3382
3417
|
$ sechroom onboard --recurse orchestration root: onboard every child repo under this dir
|
|
3418
|
+
$ sechroom onboard --recurse --lane claude-code-you pin one lane across every repo in the tree
|
|
3383
3419
|
$ sechroom onboard --refresh refresh out-of-date instruction blocks in place
|
|
3384
3420
|
$ sechroom onboard --check CI/pre-commit: nonzero exit if instructions are out of date
|
|
3385
3421
|
$ sechroom onboard --yes non-interactive: defaults + global config + full wire
|
|
@@ -3391,11 +3427,13 @@ Examples:
|
|
|
3391
3427
|
const mode = opts.check ? "check" : opts.force ? "force" : "apply";
|
|
3392
3428
|
const check = mode === "check";
|
|
3393
3429
|
const yes = Boolean(opts.yes) || check;
|
|
3430
|
+
if (opts.lane) process.env.SECHROOM_CODE_LANE = opts.lane;
|
|
3431
|
+
if (opts.designLane) process.env.SECHROOM_DESIGN_LANE = opts.designLane;
|
|
3394
3432
|
if (opts.recurse) {
|
|
3395
3433
|
const baseUrl2 = resolveBaseUrl(g);
|
|
3396
3434
|
await ensureAuth({ baseUrl: baseUrl2, tenant: "", clientId: readPersisted().clientId }, yes);
|
|
3397
3435
|
const cfg2 = await ensureTenant(baseUrl2, g, { yes: true, json, persist: false });
|
|
3398
|
-
await runRecurse(cfg2, g, { yes, dryRun, json });
|
|
3436
|
+
await runRecurse(cfg2, g, { yes, dryRun, json, lane: opts.lane, designLane: opts.designLane });
|
|
3399
3437
|
return;
|
|
3400
3438
|
}
|
|
3401
3439
|
const baseUrl = resolveBaseUrl(g);
|
package/package.json
CHANGED