@sechroom/cli 2026.6.192-rc.f5192d37 → 2026.6.194-rc.5f6d8f6f

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.
Files changed (2) hide show
  1. package/dist/index.js +110 -28
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1780,7 +1780,7 @@ import { delimiter, dirname as dirname4, join as join4 } from "path";
1780
1780
 
1781
1781
  // src/sem.ts
1782
1782
  import { basename as basename2, dirname as dirname2, join as join2 } from "path";
1783
- import { appendFileSync, existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
1783
+ import { appendFileSync, existsSync as existsSync2, mkdirSync as mkdirSync2, readdirSync, readFileSync as readFileSync2, statSync, writeFileSync as writeFileSync2 } from "fs";
1784
1784
  var SEM_FILE = join2(".sechroom", "lane.json");
1785
1785
  var LEGACY_SEM_FILE = ".sem";
1786
1786
  var STATE_DIR_NAME2 = ".sechroom";
@@ -1799,6 +1799,43 @@ function resolveSemPathForRead(start = process.cwd()) {
1799
1799
  dir = parent;
1800
1800
  }
1801
1801
  }
1802
+ function applyWorktreeLaneSuffix(lane, start = process.cwd()) {
1803
+ try {
1804
+ let dir = start;
1805
+ let gitPath;
1806
+ for (; ; ) {
1807
+ const candidate = join2(dir, ".git");
1808
+ if (existsSync2(candidate)) {
1809
+ gitPath = candidate;
1810
+ break;
1811
+ }
1812
+ const parent = dirname2(dir);
1813
+ if (parent === dir) break;
1814
+ dir = parent;
1815
+ }
1816
+ if (!gitPath || statSync(gitPath).isDirectory()) return lane;
1817
+ const gitFile = readFileSync2(gitPath, "utf8");
1818
+ const common = gitFile.trim().match(/^gitdir:\s*(.+)\/worktrees\/[^/\s]+\s*$/);
1819
+ if (!common) return lane;
1820
+ const worktreesDir = join2(common[1], "worktrees");
1821
+ const siblings = readdirSync(worktreesDir).filter((n) => {
1822
+ try {
1823
+ return statSync(join2(worktreesDir, n)).isDirectory();
1824
+ } catch {
1825
+ return false;
1826
+ }
1827
+ });
1828
+ return laneWithWorktreeSuffix(lane, gitFile, siblings);
1829
+ } catch {
1830
+ return lane;
1831
+ }
1832
+ }
1833
+ function laneWithWorktreeSuffix(lane, gitFile, siblings) {
1834
+ const m = gitFile.trim().match(/\/worktrees\/([^/\s]+)\s*$/);
1835
+ if (!m) return lane;
1836
+ const idx = [...siblings].sort().indexOf(m[1]);
1837
+ return idx < 0 ? lane : `${lane}-${idx + 2}`;
1838
+ }
1802
1839
  function parseSem(text) {
1803
1840
  const out = {};
1804
1841
  for (const raw of text.split("\n")) {
@@ -2090,8 +2127,9 @@ function resolveLane(flagLane, cwd) {
2090
2127
  const env = process.env.SECHROOM_LANE;
2091
2128
  if (env) return env;
2092
2129
  const start = cwd ?? process.cwd();
2093
- const sem = readSem(resolveSemPathForRead(start));
2094
- return sem?.values["code-lane"];
2130
+ const base = readSem(resolveSemPathForRead(start))?.values["code-lane"];
2131
+ if (!base) return void 0;
2132
+ return applyWorktreeLaneSuffix(base, start);
2095
2133
  }
2096
2134
  var INTENT_FILE = join4(".sechroom", "continuity.json");
2097
2135
  function resolveIntentPath(start) {
@@ -3248,7 +3286,7 @@ import { basename as basename3, join as join8 } from "path";
3248
3286
 
3249
3287
  // src/commands/fanout.ts
3250
3288
  import { spawnSync } from "child_process";
3251
- import { existsSync as existsSync7, readFileSync as readFileSync6, readdirSync, statSync } from "fs";
3289
+ import { existsSync as existsSync7, readFileSync as readFileSync6, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
3252
3290
  import { isAbsolute, join as join7, resolve } from "path";
3253
3291
  var ICON = {
3254
3292
  refresh: "\u21BB",
@@ -3262,7 +3300,7 @@ function resolveChildDir(path, root) {
3262
3300
  function discoverChildren(root) {
3263
3301
  let names;
3264
3302
  try {
3265
- names = readdirSync(root);
3303
+ names = readdirSync2(root);
3266
3304
  } catch {
3267
3305
  return [];
3268
3306
  }
@@ -3271,7 +3309,7 @@ function discoverChildren(root) {
3271
3309
  if (name.startsWith(".") || name === "node_modules") continue;
3272
3310
  const dir = join7(root, name);
3273
3311
  try {
3274
- if (!statSync(dir).isDirectory()) continue;
3312
+ if (!statSync2(dir).isDirectory()) continue;
3275
3313
  } catch {
3276
3314
  continue;
3277
3315
  }
@@ -4039,6 +4077,63 @@ Examples:
4039
4077
  // src/commands/skills.ts
4040
4078
  import { join as join10 } from "path";
4041
4079
  import { existsSync as existsSync10, rmSync as rmSync2 } from "fs";
4080
+
4081
+ // src/commands/lane.ts
4082
+ var LANE_KEYS = ["code-lane", "design-lane"];
4083
+ function showLane(json) {
4084
+ const found = readSem();
4085
+ if (!found) {
4086
+ if (json) return emit({ path: null, values: {} }, true);
4087
+ return console.log(
4088
+ style.dim(`No ./.sechroom/lane.json pin in this checkout. Run 'sechroom lane set'.`)
4089
+ );
4090
+ }
4091
+ const resolved = { ...found.values };
4092
+ let suffixed = false;
4093
+ for (const k of LANE_KEYS) {
4094
+ const v = found.values[k];
4095
+ if (!v) continue;
4096
+ resolved[k] = applyWorktreeLaneSuffix(v);
4097
+ if (resolved[k] !== v) suffixed = true;
4098
+ }
4099
+ if (json) return emit({ path: found.path, values: resolved, worktreeSuffixApplied: suffixed }, true);
4100
+ console.log(style.dim(`from ${found.path}`));
4101
+ Object.entries(resolved).forEach(([k, v]) => console.log(" " + style.bold(k) + " = " + v));
4102
+ if (suffixed) console.log(style.dim(" (worktree -N suffix applied \u2014 non-primary git worktree)"));
4103
+ }
4104
+ function setLane(opts) {
4105
+ if (!opts.codeLane && !opts.designLane) fail("Provide --code-lane and/or --design-lane.");
4106
+ const target = localSemPath();
4107
+ const values = readLocalSemValues();
4108
+ if (opts.codeLane) values["code-lane"] = opts.codeLane;
4109
+ if (opts.designLane) values["design-lane"] = opts.designLane;
4110
+ writeSem(values, target);
4111
+ if (opts.json) return emit({ path: target, values }, true);
4112
+ console.log(style.green(`Wrote lane pin \u2192 ${target} ${style.dim("(git-ignored)")}`));
4113
+ Object.entries(values).forEach(([k, v]) => console.log(" " + style.dim(k) + " = " + v));
4114
+ }
4115
+ function registerLane(program2) {
4116
+ 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)));
4117
+ 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(
4118
+ (opts, cmd) => setLane({
4119
+ codeLane: opts.codeLane,
4120
+ designLane: opts.designLane,
4121
+ json: Boolean(opts.json) || Boolean(cmd.optsWithGlobals().json)
4122
+ })
4123
+ );
4124
+ lane.addHelpText(
4125
+ "after",
4126
+ `
4127
+ Examples:
4128
+ $ sechroom lane show the resolved lane(s)
4129
+ $ sechroom lane set --code-lane claude-code-chris --design-lane claude-design-chris
4130
+
4131
+ In a non-primary git worktree the ratified concurrent-session -N suffix is auto-applied (SBC-1094).
4132
+ (Aliases: 'sechroom skills lane' / 'skills set-lane' \u2014 kept for back-compat.)`
4133
+ );
4134
+ }
4135
+
4136
+ // src/commands/skills.ts
4042
4137
  function registerSkills(program2) {
4043
4138
  const skills = program2.command("skills").description("Manage operator skills (materialised by `onboard`)");
4044
4139
  skills.addHelpText(
@@ -4083,28 +4178,14 @@ To install/refresh skills, run 'sechroom onboard' (it offers to materialise them
4083
4178
  if (opts.json) return emit({ slug, removed, dir }, true);
4084
4179
  console.log(style.green(`Removed ${removed.length} skill(s) for ${slug} from ${dir}`));
4085
4180
  });
4086
- skills.command("set-lane").description("Write this checkout's lane pin to a local ./.sechroom/lane.json file (read at runtime by skills)").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) => {
4087
- if (!opts.codeLane && !opts.designLane) fail("Provide --code-lane and/or --design-lane.");
4088
- const target = localSemPath();
4089
- const values = readLocalSemValues();
4090
- if (opts.codeLane) values["code-lane"] = opts.codeLane;
4091
- if (opts.designLane) values["design-lane"] = opts.designLane;
4092
- writeSem(values, target);
4093
- if (cmd.optsWithGlobals().json) return emit({ path: target, values }, true);
4094
- console.log(style.green(`Wrote lane pin \u2192 ${target} ${style.dim("(git-ignored)")}`));
4095
- Object.entries(values).forEach(([k, v]) => console.log(" " + style.dim(k) + " = " + v));
4096
- });
4097
- skills.command("lane").description("Show the lane pin resolved from ./.sechroom/lane.json (nearest in this checkout; legacy ./.sem honoured)").option("--json", "machine output").action((opts, cmd) => {
4098
- const json = cmd.optsWithGlobals().json;
4099
- const found = readSem();
4100
- if (!found) {
4101
- if (json) return emit({ path: null, values: {} }, true);
4102
- return console.log(style.dim(`No ./.sechroom/lane.json pin in this checkout. Run 'sechroom skills set-lane'.`));
4103
- }
4104
- if (json) return emit(found, true);
4105
- console.log(style.dim(`from ${found.path}`));
4106
- Object.entries(found.values).forEach(([k, v]) => console.log(" " + style.bold(k) + " = " + v));
4107
- });
4181
+ 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(
4182
+ (opts, cmd) => setLane({
4183
+ codeLane: opts.codeLane,
4184
+ designLane: opts.designLane,
4185
+ json: Boolean(opts.json) || Boolean(cmd.optsWithGlobals().json)
4186
+ })
4187
+ );
4188
+ 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)));
4108
4189
  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) => {
4109
4190
  if (!opts.defaultCodeLane && !opts.defaultDesignLane && !opts.handoverRecipient)
4110
4191
  fail("Provide at least one of --default-code-lane / --default-design-lane / --handover-recipient.");
@@ -4391,6 +4472,7 @@ registerSetup(program);
4391
4472
  registerOnboard(program);
4392
4473
  registerSweep(program);
4393
4474
  registerSkills(program);
4475
+ registerLane(program);
4394
4476
  registerReset(program);
4395
4477
  program.parseAsync().catch((err2) => {
4396
4478
  process.stderr.write(`error: ${err2 instanceof Error ? err2.message : String(err2)}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sechroom/cli",
3
- "version": "2026.6.192-rc.f5192d37",
3
+ "version": "2026.6.194-rc.5f6d8f6f",
4
4
  "description": "Sechroom CLI — a thin, generated client over the Sechroom HTTP API. An agent/human surface alongside MCP.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",