@sechroom/cli 2026.6.25 → 2026.6.26-rc.9005394b
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 +55 -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,34 @@ ${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 clients = detectInstalledClients(process.cwd());
|
|
3355
|
+
const inferred = await inferLanes(cfg, clients.length ? clients : void 0);
|
|
3356
|
+
code = code ?? inferred.code;
|
|
3357
|
+
design = design ?? inferred.design;
|
|
3358
|
+
}
|
|
3359
|
+
if (!opts.lane && !opts.yes && !opts.dryRun && canPrompt() && (code || design)) {
|
|
3360
|
+
process.stderr.write(`
|
|
3361
|
+
This fan-out will pin the same lane in every repo:
|
|
3362
|
+
`);
|
|
3363
|
+
if (code) process.stderr.write(` ${style.dim("code-lane")} = ${style.cyan(code)}
|
|
3364
|
+
`);
|
|
3365
|
+
if (design) process.stderr.write(` ${style.dim("design-lane")} = ${style.cyan(design)}
|
|
3366
|
+
`);
|
|
3367
|
+
if (!await promptYesNo("Use this lane for all repos?")) {
|
|
3368
|
+
code = await promptText("Code-lane id (blank = let each repo infer)?", code ?? "") || void 0;
|
|
3369
|
+
design = await promptText("Design-lane id (blank = skip)?", design ?? "") || void 0;
|
|
3370
|
+
}
|
|
3371
|
+
}
|
|
3372
|
+
if (code) process.env.SECHROOM_CODE_LANE = code;
|
|
3373
|
+
else delete process.env.SECHROOM_CODE_LANE;
|
|
3374
|
+
if (design) process.env.SECHROOM_DESIGN_LANE = design;
|
|
3375
|
+
else delete process.env.SECHROOM_DESIGN_LANE;
|
|
3376
|
+
return { code, design };
|
|
3377
|
+
}
|
|
3345
3378
|
async function runRecurse(cfg, g, opts) {
|
|
3346
3379
|
const { yes, dryRun, json } = opts;
|
|
3347
3380
|
const root = process.cwd();
|
|
@@ -3359,6 +3392,9 @@ async function runRecurse(cfg, g, opts) {
|
|
|
3359
3392
|
process.stderr.write(`${style.bold("onboard --recurse")} ${style.dim(`(${entries.length} repo${entries.length === 1 ? "" : "s"} from ${sourceLabel})`)}
|
|
3360
3393
|
`);
|
|
3361
3394
|
}
|
|
3395
|
+
const lane = await resolveFanoutLane(cfg, { lane: opts.lane, designLane: opts.designLane, yes, dryRun });
|
|
3396
|
+
if (!json && lane.code) process.stderr.write(`${ok("\u2713")} lane ${style.cyan(lane.code)}${lane.design ? ` ${style.dim(`/ ${lane.design}`)}` : ""} for every repo
|
|
3397
|
+
`);
|
|
3362
3398
|
const client = await makeClient(cfg);
|
|
3363
3399
|
const plans = [];
|
|
3364
3400
|
for (const entry of entries) plans.push(await planRecurseChild(entry, root, client, { yes, dryRun }));
|
|
@@ -3370,7 +3406,7 @@ async function runRecurse(cfg, g, opts) {
|
|
|
3370
3406
|
summarizeFanout(results, { dryRun });
|
|
3371
3407
|
}
|
|
3372
3408
|
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(
|
|
3409
|
+
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
3410
|
"after",
|
|
3375
3411
|
`
|
|
3376
3412
|
Examples:
|
|
@@ -3380,6 +3416,7 @@ Examples:
|
|
|
3380
3416
|
$ sechroom onboard --local save tenant + base URL to a committed ./.sechroom.json
|
|
3381
3417
|
$ sechroom onboard --workspace wsp_XX bind this directory to a workspace (no pick prompt)
|
|
3382
3418
|
$ sechroom onboard --recurse orchestration root: onboard every child repo under this dir
|
|
3419
|
+
$ sechroom onboard --recurse --lane claude-code-you pin one lane across every repo in the tree
|
|
3383
3420
|
$ sechroom onboard --refresh refresh out-of-date instruction blocks in place
|
|
3384
3421
|
$ sechroom onboard --check CI/pre-commit: nonzero exit if instructions are out of date
|
|
3385
3422
|
$ sechroom onboard --yes non-interactive: defaults + global config + full wire
|
|
@@ -3391,11 +3428,13 @@ Examples:
|
|
|
3391
3428
|
const mode = opts.check ? "check" : opts.force ? "force" : "apply";
|
|
3392
3429
|
const check = mode === "check";
|
|
3393
3430
|
const yes = Boolean(opts.yes) || check;
|
|
3431
|
+
if (opts.lane) process.env.SECHROOM_CODE_LANE = opts.lane;
|
|
3432
|
+
if (opts.designLane) process.env.SECHROOM_DESIGN_LANE = opts.designLane;
|
|
3394
3433
|
if (opts.recurse) {
|
|
3395
3434
|
const baseUrl2 = resolveBaseUrl(g);
|
|
3396
3435
|
await ensureAuth({ baseUrl: baseUrl2, tenant: "", clientId: readPersisted().clientId }, yes);
|
|
3397
3436
|
const cfg2 = await ensureTenant(baseUrl2, g, { yes: true, json, persist: false });
|
|
3398
|
-
await runRecurse(cfg2, g, { yes, dryRun, json });
|
|
3437
|
+
await runRecurse(cfg2, g, { yes, dryRun, json, lane: opts.lane, designLane: opts.designLane });
|
|
3399
3438
|
return;
|
|
3400
3439
|
}
|
|
3401
3440
|
const baseUrl = resolveBaseUrl(g);
|
package/package.json
CHANGED