@sechroom/cli 2026.9.2 → 2026.9.3
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 +118 -23
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -2260,6 +2260,7 @@ function resolveSemPathForRead(start = process.cwd()) {
|
|
|
2260
2260
|
while (true) {
|
|
2261
2261
|
const candidate = join6(dir, SEM_FILE);
|
|
2262
2262
|
if (existsSync4(candidate)) return candidate;
|
|
2263
|
+
if (existsSync4(join6(dir, ".git"))) return void 0;
|
|
2263
2264
|
const parent = dirname2(dir);
|
|
2264
2265
|
if (parent === dir) return void 0;
|
|
2265
2266
|
dir = parent;
|
|
@@ -8130,7 +8131,8 @@ function resolveLane(flagLane, cwd) {
|
|
|
8130
8131
|
const env = process.env.SECHROOM_LANE;
|
|
8131
8132
|
if (env) return env;
|
|
8132
8133
|
const start = cwd ?? process.cwd();
|
|
8133
|
-
const
|
|
8134
|
+
const semPath = resolveSemPathForRead(start);
|
|
8135
|
+
const base = semPath ? readSem(semPath)?.values["code-lane"] : void 0;
|
|
8134
8136
|
if (!base) return void 0;
|
|
8135
8137
|
return applyWorktreeLaneSuffix(base, start);
|
|
8136
8138
|
}
|
|
@@ -10725,6 +10727,64 @@ AGPL \u2014 no Herdr code is vendored here.`
|
|
|
10725
10727
|
|
|
10726
10728
|
// src/commands/lane.ts
|
|
10727
10729
|
var LANE_KEYS = ["code-lane", "design-lane"];
|
|
10730
|
+
function apiFlagsFrom(options) {
|
|
10731
|
+
return {
|
|
10732
|
+
baseUrl: typeof options.baseUrl === "string" ? options.baseUrl : void 0,
|
|
10733
|
+
tenant: typeof options.tenant === "string" ? options.tenant : void 0,
|
|
10734
|
+
account: typeof options.account === "string" ? options.account : void 0,
|
|
10735
|
+
binding: typeof options.binding === "string" ? options.binding : void 0
|
|
10736
|
+
};
|
|
10737
|
+
}
|
|
10738
|
+
function resolvedPinnedLanes(values) {
|
|
10739
|
+
const lanes = LANE_KEYS.flatMap((key) => {
|
|
10740
|
+
const lane = values[key];
|
|
10741
|
+
return lane ? [applyWorktreeLaneSuffix(lane)] : [];
|
|
10742
|
+
});
|
|
10743
|
+
return [...new Set(lanes)];
|
|
10744
|
+
}
|
|
10745
|
+
function reportRegistrationWarning(laneId, error) {
|
|
10746
|
+
const detail = formatFailureMessage(error);
|
|
10747
|
+
const message = `could not register source lane '${laneId}': ${detail}`;
|
|
10748
|
+
process.stderr.write(`${warn(`warning: ${message}`)}
|
|
10749
|
+
`);
|
|
10750
|
+
return message;
|
|
10751
|
+
}
|
|
10752
|
+
async function registerPinnedLanes(laneIds, flags) {
|
|
10753
|
+
if (laneIds.length === 0) return [];
|
|
10754
|
+
let client;
|
|
10755
|
+
try {
|
|
10756
|
+
client = await makeClient(resolveConfig(flags));
|
|
10757
|
+
} catch (error) {
|
|
10758
|
+
return laneIds.map((laneId) => ({
|
|
10759
|
+
laneId,
|
|
10760
|
+
registered: false,
|
|
10761
|
+
warning: reportRegistrationWarning(laneId, error)
|
|
10762
|
+
}));
|
|
10763
|
+
}
|
|
10764
|
+
const results = [];
|
|
10765
|
+
for (const laneId of laneIds) {
|
|
10766
|
+
try {
|
|
10767
|
+
const response = await client.POST("/identity/source-lanes", {
|
|
10768
|
+
body: { laneId }
|
|
10769
|
+
});
|
|
10770
|
+
const failure = response.error ?? (response.response && !response.response.ok ? `HTTP ${response.response.status} ${response.response.statusText}`.trim() : void 0);
|
|
10771
|
+
if (failure !== void 0) throw new Error(formatFailureMessage(failure));
|
|
10772
|
+
results.push({ laneId, registered: true });
|
|
10773
|
+
} catch (error) {
|
|
10774
|
+
results.push({
|
|
10775
|
+
laneId,
|
|
10776
|
+
registered: false,
|
|
10777
|
+
warning: reportRegistrationWarning(laneId, error)
|
|
10778
|
+
});
|
|
10779
|
+
}
|
|
10780
|
+
}
|
|
10781
|
+
return results;
|
|
10782
|
+
}
|
|
10783
|
+
function printRegistrationSummary(registrations) {
|
|
10784
|
+
const registered = registrations.filter((result) => result.registered).map((result) => result.laneId);
|
|
10785
|
+
if (registered.length > 0)
|
|
10786
|
+
console.log(style.dim(`Registered source lane${registered.length === 1 ? "" : "s"}: ${registered.join(", ")}`));
|
|
10787
|
+
}
|
|
10728
10788
|
function showLane(json) {
|
|
10729
10789
|
const found = readSem();
|
|
10730
10790
|
if (!found) {
|
|
@@ -10746,35 +10806,57 @@ function showLane(json) {
|
|
|
10746
10806
|
Object.entries(resolved).forEach(([k, v]) => console.log(" " + style.bold(k) + " = " + v));
|
|
10747
10807
|
if (suffixed) console.log(style.dim(" (worktree -N suffix applied \u2014 non-primary git worktree)"));
|
|
10748
10808
|
}
|
|
10749
|
-
function setLane(opts) {
|
|
10809
|
+
async function setLane(opts) {
|
|
10750
10810
|
if (!opts.codeLane && !opts.designLane) fail("Provide --code-lane and/or --design-lane.");
|
|
10751
10811
|
const target = localSemPath();
|
|
10752
10812
|
const values = readLocalSemValues();
|
|
10753
10813
|
if (opts.codeLane) values["code-lane"] = opts.codeLane;
|
|
10754
10814
|
if (opts.designLane) values["design-lane"] = opts.designLane;
|
|
10755
10815
|
writeSem(values, target);
|
|
10756
|
-
|
|
10816
|
+
const registrations = await registerPinnedLanes(
|
|
10817
|
+
resolvedPinnedLanes(values),
|
|
10818
|
+
opts.apiFlags ?? {}
|
|
10819
|
+
);
|
|
10820
|
+
if (opts.json) return emit({ path: target, values, registrations }, true);
|
|
10757
10821
|
console.log(style.green(`Wrote lane pin \u2192 ${target} ${style.dim("(git-ignored)")}`));
|
|
10758
10822
|
Object.entries(values).forEach(([k, v]) => console.log(" " + style.dim(k) + " = " + v));
|
|
10823
|
+
printRegistrationSummary(registrations);
|
|
10759
10824
|
}
|
|
10760
10825
|
function registerLane(program2) {
|
|
10761
10826
|
const lane = program2.command("lane").description("Show this checkout's continuity lane pin (worktree-aware -N suffix applied)").option("--json", "machine output").action((opts, cmd) => showLane(Boolean(opts.json) || Boolean(cmd.optsWithGlobals().json)));
|
|
10762
|
-
lane.command("set").description("Write this checkout's lane pin to ./.sechroom/lane.json").option("--code-lane <id>", "code-surface lane id (e.g. claude-code-chris)").option("--design-lane <id>", "design / substrate-authoring lane id (e.g. claude-design-chris)").option("--json", "machine output").action(
|
|
10763
|
-
|
|
10827
|
+
lane.command("set").description("Write this checkout's lane pin to ./.sechroom/lane.json").option("--code-lane <id>", "code-surface lane id (e.g. claude-code-chris)").option("--design-lane <id>", "design / substrate-authoring lane id (e.g. claude-design-chris)").option("--json", "machine output").action((opts, cmd) => {
|
|
10828
|
+
const globals = cmd.optsWithGlobals();
|
|
10829
|
+
return setLane({
|
|
10764
10830
|
codeLane: opts.codeLane,
|
|
10765
10831
|
designLane: opts.designLane,
|
|
10766
|
-
json: Boolean(opts.json) || Boolean(cmd.optsWithGlobals().json)
|
|
10767
|
-
|
|
10768
|
-
|
|
10832
|
+
json: Boolean(opts.json) || Boolean(cmd.optsWithGlobals().json),
|
|
10833
|
+
apiFlags: apiFlagsFrom(globals)
|
|
10834
|
+
});
|
|
10835
|
+
});
|
|
10836
|
+
lane.command("register").description("Register the source lanes already pinned in this checkout").option("--json", "machine output").action(async (opts, cmd) => {
|
|
10837
|
+
const found = readSem();
|
|
10838
|
+
if (!found)
|
|
10839
|
+
fail("No ./.sechroom/lane.json pin in this checkout. Run 'sechroom lane set'.");
|
|
10840
|
+
const globals = cmd.optsWithGlobals();
|
|
10841
|
+
const registrations = await registerPinnedLanes(
|
|
10842
|
+
resolvedPinnedLanes(found.values),
|
|
10843
|
+
apiFlagsFrom(globals)
|
|
10844
|
+
);
|
|
10845
|
+
const json = Boolean(opts.json) || Boolean(globals.json);
|
|
10846
|
+
if (json) return emit({ path: found.path, registrations }, true);
|
|
10847
|
+
printRegistrationSummary(registrations);
|
|
10848
|
+
});
|
|
10769
10849
|
lane.addHelpText(
|
|
10770
10850
|
"after",
|
|
10771
10851
|
`
|
|
10772
10852
|
Examples:
|
|
10773
10853
|
$ sechroom lane show the resolved lane(s)
|
|
10774
10854
|
$ sechroom lane set --code-lane claude-code-chris --design-lane claude-design-chris
|
|
10855
|
+
$ sechroom lane register register existing pinned lane(s)
|
|
10775
10856
|
|
|
10776
10857
|
In a non-primary git worktree the ratified concurrent-session -N suffix is auto-applied (SBC-1094).
|
|
10777
|
-
|
|
10858
|
+
Lane resolution stops at the nearest git worktree/repository root; a parent checkout's .sechroom/lane.json is never inherited.
|
|
10859
|
+
(Aliases: 'sechroom skills lane' / 'skills set-lane' \u2014 kept for back-compat; set-lane also registers.)`
|
|
10778
10860
|
);
|
|
10779
10861
|
}
|
|
10780
10862
|
|
|
@@ -12596,6 +12678,7 @@ function copyChoice(opts) {
|
|
|
12596
12678
|
}
|
|
12597
12679
|
async function maybeOfferCopies(cfg, setup, targets, keys, personalWorkspaceId, choice) {
|
|
12598
12680
|
if (!personalWorkspaceId || choice === "no") return;
|
|
12681
|
+
let source;
|
|
12599
12682
|
const seen = /* @__PURE__ */ new Set();
|
|
12600
12683
|
for (const key of keys) {
|
|
12601
12684
|
const instr = targets[key]?.instruction;
|
|
@@ -12624,8 +12707,8 @@ version, the shared template stays clean, and you can discard back anytime.
|
|
|
12624
12707
|
make = await promptYesNo("Make a personal copy to customise?");
|
|
12625
12708
|
}
|
|
12626
12709
|
if (make) {
|
|
12627
|
-
const
|
|
12628
|
-
await createOverride(cfg, resolved, personalWorkspaceId,
|
|
12710
|
+
const copySource = source ??= resolveSourceLane(void 0);
|
|
12711
|
+
await createOverride(cfg, resolved, personalWorkspaceId, copySource);
|
|
12629
12712
|
process.stderr.write(
|
|
12630
12713
|
`\u2713 personal copy created for ${instr.surfaceKey} \u2014 edit it on the Agent setup page or via the API.
|
|
12631
12714
|
`
|
|
@@ -13880,6 +13963,10 @@ async function runRecurse(cfg, g, opts) {
|
|
|
13880
13963
|
}
|
|
13881
13964
|
summarizeFanout(results, { dryRun });
|
|
13882
13965
|
}
|
|
13966
|
+
async function runOnboardCopyPhase(opts) {
|
|
13967
|
+
if (!opts.json && !opts.dryRun && !opts.check) await opts.ensureLanePin();
|
|
13968
|
+
if (!opts.dryRun && !opts.check) await opts.maybeOfferCopies();
|
|
13969
|
+
}
|
|
13883
13970
|
function registerOnboard(program2) {
|
|
13884
13971
|
program2.command("onboard").description(
|
|
13885
13972
|
"Guided first-run setup: configure, sign in, set timezone, detect clients, and wire this project"
|
|
@@ -14097,16 +14184,20 @@ Try: ${style.cyan('sechroom memory search "..."')} or ${style.cyan("sechroom -
|
|
|
14097
14184
|
codexScope: scope
|
|
14098
14185
|
});
|
|
14099
14186
|
const personalWorkspaceId = await getPersonalWorkspaceId(cfg);
|
|
14100
|
-
|
|
14101
|
-
|
|
14187
|
+
await runOnboardCopyPhase({
|
|
14188
|
+
json,
|
|
14189
|
+
dryRun,
|
|
14190
|
+
check,
|
|
14191
|
+
ensureLanePin: () => ensureLanePin(cfg, { yes, dryRun, clients: keys }),
|
|
14192
|
+
maybeOfferCopies: () => maybeOfferCopies(
|
|
14102
14193
|
cfg,
|
|
14103
14194
|
setup,
|
|
14104
14195
|
targets,
|
|
14105
14196
|
keys,
|
|
14106
14197
|
personalWorkspaceId,
|
|
14107
14198
|
copyChoice(opts)
|
|
14108
|
-
)
|
|
14109
|
-
}
|
|
14199
|
+
)
|
|
14200
|
+
});
|
|
14110
14201
|
const writeMcp = wire === "full";
|
|
14111
14202
|
const result = [];
|
|
14112
14203
|
for (const key of keys) {
|
|
@@ -14129,9 +14220,6 @@ Try: ${style.cyan('sechroom memory search "..."')} or ${style.cyan("sechroom -
|
|
|
14129
14220
|
});
|
|
14130
14221
|
}
|
|
14131
14222
|
const evalCounts = buildCheckReport(result).eval;
|
|
14132
|
-
if (!json && !dryRun) {
|
|
14133
|
-
await ensureLanePin(cfg, { yes, dryRun, clients: keys });
|
|
14134
|
-
}
|
|
14135
14223
|
if (!json && !dryRun) {
|
|
14136
14224
|
for (const t of claudeTargets) {
|
|
14137
14225
|
await maybeOfferSkills(cfg, personalWorkspaceId, {
|
|
@@ -14870,13 +14958,20 @@ clients select all; an unconfigured machine preserves the legacy Claude default.
|
|
|
14870
14958
|
if (json) return emit(result, true);
|
|
14871
14959
|
console.log(style.green("\u2713") + ` wrote ${style.bold(result.path)} ${style.dim(`(${result.bytes} bytes)`)}`);
|
|
14872
14960
|
});
|
|
14873
|
-
skills.command("set-lane").description("Alias of `sechroom lane set` (kept for back-compat) \u2014 write this checkout's lane pin").option("--code-lane <id>", "code-surface lane id (e.g. claude-code-chris)").option("--design-lane <id>", "design / substrate-authoring lane id (e.g. claude-design-chris)").option("--json", "machine output").action(
|
|
14874
|
-
|
|
14961
|
+
skills.command("set-lane").description("Alias of `sechroom lane set` (kept for back-compat) \u2014 write this checkout's lane pin").option("--code-lane <id>", "code-surface lane id (e.g. claude-code-chris)").option("--design-lane <id>", "design / substrate-authoring lane id (e.g. claude-design-chris)").option("--json", "machine output").action((opts, cmd) => {
|
|
14962
|
+
const globals = cmd.optsWithGlobals();
|
|
14963
|
+
return setLane({
|
|
14875
14964
|
codeLane: opts.codeLane,
|
|
14876
14965
|
designLane: opts.designLane,
|
|
14877
|
-
json: Boolean(opts.json) || Boolean(cmd.optsWithGlobals().json)
|
|
14878
|
-
|
|
14879
|
-
|
|
14966
|
+
json: Boolean(opts.json) || Boolean(cmd.optsWithGlobals().json),
|
|
14967
|
+
apiFlags: {
|
|
14968
|
+
baseUrl: globals.baseUrl,
|
|
14969
|
+
tenant: globals.tenant,
|
|
14970
|
+
account: globals.account,
|
|
14971
|
+
binding: globals.binding
|
|
14972
|
+
}
|
|
14973
|
+
});
|
|
14974
|
+
});
|
|
14880
14975
|
skills.command("lane").description("Alias of `sechroom lane` (kept for back-compat) \u2014 show this checkout's lane pin").option("--json", "machine output").action((opts, cmd) => showLane(Boolean(opts.json) || Boolean(cmd.optsWithGlobals().json)));
|
|
14881
14976
|
skills.command("set-workflow").description("Set your per-operator workflow defaults (server-side; follows you across tenants)").option("--default-code-lane <id>", "personal default code lane (e.g. claude-code-chris)").option("--default-design-lane <id>", "personal default design lane (e.g. claude-design-chris)").option("--handover-recipient <id>", "your daily-handover counterparty (e.g. andy)").option("--json", "machine output").action(async (opts, cmd) => {
|
|
14882
14977
|
if (!opts.defaultCodeLane && !opts.defaultDesignLane && !opts.handoverRecipient)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sechroom/cli",
|
|
3
|
-
"version": "2026.9.
|
|
3
|
+
"version": "2026.9.3",
|
|
4
4
|
"description": "Command-line interface for Sechroom — sign in, wire your AI tools, and work with your sechroom from the terminal.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"gen": "openapi-typescript \"${SECHROOM_OPENAPI_URL:-https://app.sechroom.ai/api/openapi/v1.json}\" -o src/generated/api.d.ts",
|
|
44
44
|
"build": "tsup src/index.ts --format esm --target node20 --clean",
|
|
45
45
|
"dev": "tsx src/index.ts",
|
|
46
|
-
"test": "node
|
|
46
|
+
"test": "node scripts/run-tests.mjs",
|
|
47
47
|
"check-types": "tsc --noEmit"
|
|
48
48
|
}
|
|
49
49
|
}
|