@sechroom/cli 2026.6.36 → 2026.6.37

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 +97 -43
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1275,7 +1275,8 @@ Examples:
1275
1275
  $ sechroom workspace get wsp_XXXX --json
1276
1276
  $ sechroom workspace rename wsp_XXXX --name "Renamed"
1277
1277
  $ sechroom workspace move wsp_XXXX --parent wsp_YYYY
1278
- $ sechroom workspace feed wsp_XXXX --limit 20 --cascade`
1278
+ $ sechroom workspace feed wsp_XXXX --limit 20 --cascade
1279
+ $ sechroom workspace feed wsp_XXXX --tag kind:plan --since 2026-06-01T00:00:00Z --order UpdatedDesc`
1279
1280
  );
1280
1281
  workspace.command("create").description("Create a workspace (POST /workspaces)").requiredOption("--name <name>", "Workspace name").option("--description <description>", "Optional description").option("--parent <parentId>", "Parent workspace id (omit for a top-level workspace)").action(async (opts, cmd) => {
1281
1282
  const cfg = resolveConfig(cmd.optsWithGlobals());
@@ -1383,7 +1384,7 @@ Examples:
1383
1384
  });
1384
1385
  emitAction(`restored workspace ${style.bold(workspaceId)}`, data, cmd.optsWithGlobals().json);
1385
1386
  });
1386
- workspace.command("feed <workspaceId>").description("List a workspace's memory feed (GET /workspaces/{workspaceId}/memories/feed)").option("--limit <n>", "Max results", "20").option("--cursor <cursor>", "Paging cursor from a prior page").option("--cascade", "Cascade into descendant workspaces", false).option("--include-projects", "Include the workspace's projects", false).option("--include-archived", "Include archived memories", false).option("--query <query>", "Filter the feed by text").option("--tag <tag>", "Filter tags (comma-separated)").action(async (workspaceId, opts, cmd) => {
1387
+ workspace.command("feed <workspaceId>").description("List a workspace's memory feed (GET /workspaces/{workspaceId}/memories/feed)").option("--limit <n>", "Max results", "20").option("--cursor <cursor>", "Paging cursor from a prior page").option("--cascade", "Cascade into descendant workspaces", false).option("--include-projects", "Include the workspace's projects", false).option("--include-archived", "Include archived memories", false).option("--query <query>", "Filter the feed by text").option("--tag <tag>", "Filter tags (comma-separated)").option("--since <iso>", "Only memories updated since this ISO-8601 timestamp (updatedSince)").option("--order <order>", "Order: UpdatedDesc | UpdatedAsc | CreatedDesc | CreatedAsc").action(async (workspaceId, opts, cmd) => {
1387
1388
  const cfg = resolveConfig(cmd.optsWithGlobals());
1388
1389
  const data = await runApi("Fetching feed", async () => {
1389
1390
  const client = await makeClient(cfg);
@@ -1397,7 +1398,12 @@ Examples:
1397
1398
  includeArchived: Boolean(opts.includeArchived),
1398
1399
  ...opts.cursor ? { cursor: opts.cursor } : {},
1399
1400
  ...opts.query ? { query: opts.query } : {},
1400
- ...opts.tag ? { filterTags: opts.tag } : {}
1401
+ ...opts.tag ? { filterTags: opts.tag } : {},
1402
+ ...opts.since ? { updatedSince: opts.since } : {},
1403
+ // orderBy is WorkspaceFeedOrder (Updated*/Created*) post-FR-feed-005
1404
+ // (the FeedOrder schema-name collision is fixed). opts.order is a raw
1405
+ // commander string; cast to a valid member to satisfy the union.
1406
+ ...opts.order ? { orderBy: opts.order } : {}
1401
1407
  }
1402
1408
  }
1403
1409
  });
@@ -1827,10 +1833,9 @@ import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as rea
1827
1833
  import { delimiter, dirname as dirname4, join as join5 } from "path";
1828
1834
 
1829
1835
  // src/sem.ts
1830
- import { basename as basename2, dirname as dirname2, join as join2 } from "path";
1836
+ import { dirname as dirname2, join as join2 } from "path";
1831
1837
  import { appendFileSync, existsSync as existsSync2, mkdirSync as mkdirSync2, readdirSync, readFileSync as readFileSync2, statSync, writeFileSync as writeFileSync2 } from "fs";
1832
1838
  var SEM_FILE = join2(".sechroom", "lane.json");
1833
- var LEGACY_SEM_FILE = ".sem";
1834
1839
  var STATE_DIR_NAME2 = ".sechroom";
1835
1840
  function localSemPath(cwd = process.cwd()) {
1836
1841
  return join2(cwd, SEM_FILE);
@@ -1840,8 +1845,6 @@ function resolveSemPathForRead(start = process.cwd()) {
1840
1845
  while (true) {
1841
1846
  const candidate = join2(dir, SEM_FILE);
1842
1847
  if (existsSync2(candidate)) return candidate;
1843
- const legacy = join2(dir, LEGACY_SEM_FILE);
1844
- if (existsSync2(legacy)) return legacy;
1845
1848
  const parent = dirname2(dir);
1846
1849
  if (parent === dir) return void 0;
1847
1850
  dir = parent;
@@ -1884,34 +1887,17 @@ function laneWithWorktreeSuffix(lane, gitFile, siblings) {
1884
1887
  const idx = [...siblings].sort().indexOf(m[1]);
1885
1888
  return idx < 0 ? lane : `${lane}-${idx + 2}`;
1886
1889
  }
1887
- function parseSem(text) {
1888
- const out = {};
1889
- for (const raw of text.split("\n")) {
1890
- const line = raw.trim();
1891
- if (!line || line.startsWith("#")) continue;
1892
- const eq = line.indexOf("=");
1893
- if (eq === -1) continue;
1894
- const key = line.slice(0, eq).trim();
1895
- const value = line.slice(eq + 1).trim();
1896
- if (key) out[key] = value;
1897
- }
1898
- return out;
1899
- }
1900
1890
  function serializeSem(values) {
1901
1891
  return JSON.stringify(values, null, 2) + "\n";
1902
1892
  }
1903
1893
  function readSem(path) {
1904
1894
  const p = path ?? resolveSemPathForRead();
1905
1895
  if (!p || !existsSync2(p)) return void 0;
1906
- const text = readFileSync2(p, "utf8");
1907
- const values = basename2(p) === LEGACY_SEM_FILE ? parseSem(text) : parseLaneJson(text);
1908
- return { path: p, values };
1896
+ return { path: p, values: parseLaneJson(readFileSync2(p, "utf8")) };
1909
1897
  }
1910
1898
  function readLocalSemValues(cwd = process.cwd()) {
1911
1899
  const next = join2(cwd, SEM_FILE);
1912
1900
  if (existsSync2(next)) return readSem(next)?.values ?? {};
1913
- const legacy = join2(cwd, LEGACY_SEM_FILE);
1914
- if (existsSync2(legacy)) return readSem(legacy)?.values ?? {};
1915
1901
  return {};
1916
1902
  }
1917
1903
  function parseLaneJson(text) {
@@ -1931,8 +1917,34 @@ function writeSem(values, path = localSemPath()) {
1931
1917
  mkdirSync2(dirname2(path), { recursive: true });
1932
1918
  writeFileSync2(path, serializeSem(values));
1933
1919
  ensureSemIgnored(path);
1920
+ ensureContinuityScaffold(path);
1934
1921
  return path;
1935
1922
  }
1923
+ var CONTINUITY_FILE_NAME = "continuity.json";
1924
+ var CONTINUITY_SCAFFOLD = JSON.stringify(
1925
+ {
1926
+ _readme: "Agent-maintained continuity intent. Keep these current during the session; `sechroom checkpoint` and the PreCompact hook snapshot from here. The five required fields (objective, state, lastAction, nextAction, resumeInstruction) must all be non-empty for a snapshot to be created.",
1927
+ objective: "",
1928
+ state: "",
1929
+ lastAction: "",
1930
+ nextAction: "",
1931
+ resumeInstruction: "",
1932
+ constraints: [],
1933
+ questions: [],
1934
+ artifacts: [],
1935
+ confidence: null
1936
+ },
1937
+ null,
1938
+ 2
1939
+ ) + "\n";
1940
+ function ensureContinuityScaffold(semPath) {
1941
+ try {
1942
+ const target = join2(dirname2(semPath), CONTINUITY_FILE_NAME);
1943
+ if (existsSync2(target)) return;
1944
+ writeFileSync2(target, CONTINUITY_SCAFFOLD);
1945
+ } catch {
1946
+ }
1947
+ }
1936
1948
  function ignoresSem(content) {
1937
1949
  return content.split("\n").some((line) => {
1938
1950
  const t = line.trim();
@@ -2201,10 +2213,22 @@ function clientTargets(cwd, opts = {}) {
2201
2213
  label: "Cursor",
2202
2214
  mcp: { surfaceKey: "claude-code", sectionType: SectionType.McpConfig, path: join4(cwd, ".cursor", "mcp.json"), format: "json" },
2203
2215
  instruction: { surfaceKey: "chatgpt", path: join4(cwd, "AGENTS.md") }
2216
+ },
2217
+ antigravity: {
2218
+ key: "antigravity",
2219
+ label: "Google Antigravity",
2220
+ // FR-sechroom-247 — Antigravity reads MCP from a GLOBAL, home-relative
2221
+ // `~/.gemini/config/mcp_config.json` (not cwd; not affected by
2222
+ // CLAUDE_CONFIG_DIR / CODEX_HOME). The snippet — `serverUrl`-shaped, no
2223
+ // `type` — comes from the `antigravity` server surface, so we don't
2224
+ // hardcode it here. Instructions go in the project `AGENTS.md`
2225
+ // (cross-tool, shared with Codex/Cursor).
2226
+ mcp: { surfaceKey: "antigravity", sectionType: SectionType.McpConfig, path: join4(home, ".gemini", "config", "mcp_config.json"), format: "json" },
2227
+ instruction: { surfaceKey: "antigravity", path: join4(cwd, "AGENTS.md") }
2204
2228
  }
2205
2229
  };
2206
2230
  }
2207
- var ALL_CLIENT_KEYS = ["claude-code", "claude-desktop", "codex", "cursor"];
2231
+ var ALL_CLIENT_KEYS = ["claude-code", "claude-desktop", "codex", "cursor", "antigravity"];
2208
2232
  var DEFAULT_CLIENT_KEY = "claude-code";
2209
2233
  function detectInstalledClients(cwd) {
2210
2234
  const home = homedir3();
@@ -2213,6 +2237,7 @@ function detectInstalledClients(cwd) {
2213
2237
  if (existsSync3(dirname3(claudeDesktopConfigPath(home)))) detected.push("claude-desktop");
2214
2238
  if (resolveCodexHomes({}).some((d) => existsSync3(d))) detected.push("codex");
2215
2239
  if (existsSync3(join4(home, ".cursor")) || existsSync3(join4(cwd, ".cursor"))) detected.push("cursor");
2240
+ if (existsSync3(join4(home, ".gemini"))) detected.push("antigravity");
2216
2241
  return detected;
2217
2242
  }
2218
2243
 
@@ -2541,15 +2566,17 @@ Examples:
2541
2566
  $ sechroom hook install --surface codex Codex only
2542
2567
  $ sechroom hook install --local --dry-run preview the project .claude/settings.json
2543
2568
 
2544
- Lane source (high -> low): --lane > SECHROOM_LANE > ./.sem code-lane (D-binding-5).
2569
+ Lane source (high -> low): --lane > SECHROOM_LANE > ./.sechroom/lane.json code-lane (D-binding-5).
2545
2570
  Fail-soft: no lane / no auth / no-or-partial intent file / API error -> exit 0, never blocks.`
2546
2571
  );
2547
- hook.command("session-start").description("Resume the checkout's lane and emit continuity context for a SessionStart hook").option("--lane <laneId>", "Override the resolved lane (else SECHROOM_LANE, else ./.sem code-lane)").option("--surface <surface>", "Target surface: claude | codex (output is identical for session-start)", "claude").option("--max-artifacts <n>", "Cap artifacts in the resume bundle").action(async (opts, cmd) => {
2572
+ hook.command("session-start").description("Resume the checkout's lane and emit continuity context for a SessionStart hook").option("--lane <laneId>", "Override the resolved lane (else SECHROOM_LANE, else ./.sechroom/lane.json code-lane)").option("--surface <surface>", "Target surface: claude | codex (output is identical for session-start)", "claude").option("--max-artifacts <n>", "Cap artifacts in the resume bundle").action(async (opts, cmd) => {
2548
2573
  try {
2549
2574
  const raw = await readStdin();
2550
2575
  const input = parseHookInput(raw);
2551
2576
  const lane = resolveLane(opts.lane, input.cwd);
2552
2577
  if (!lane) return process.exit(0);
2578
+ const semPath = resolveSemPathForRead(input.cwd ?? process.cwd());
2579
+ if (semPath) ensureContinuityScaffold(semPath);
2553
2580
  const cfg = resolveConfig(cmd.optsWithGlobals());
2554
2581
  const client = await makeClient(cfg);
2555
2582
  const { data } = await client.POST("/continuity/resume/lane", {
@@ -2568,7 +2595,7 @@ Fail-soft: no lane / no auth / no-or-partial intent file / API error -> exit 0,
2568
2595
  return process.exit(0);
2569
2596
  }
2570
2597
  });
2571
- hook.command("pre-compact").description("Save a continuity snapshot from the agent-maintained intent file on a PreCompact hook").option("--lane <laneId>", "Override the resolved lane (else SECHROOM_LANE, else ./.sem code-lane)").option("--scope <scope>", "Snapshot scope (else the intent file's `scope`, else 'compaction')").option("--surface <surface>", "Target surface: claude | codex (lifecycle-only on both)", "claude").action(async (opts, cmd) => {
2598
+ hook.command("pre-compact").description("Save a continuity snapshot from the agent-maintained intent file on a PreCompact hook").option("--lane <laneId>", "Override the resolved lane (else SECHROOM_LANE, else ./.sechroom/lane.json code-lane)").option("--scope <scope>", "Snapshot scope (else the intent file's `scope`, else 'compaction')").option("--surface <surface>", "Target surface: claude | codex (lifecycle-only on both)", "claude").action(async (opts, cmd) => {
2572
2599
  try {
2573
2600
  const raw = await readStdin();
2574
2601
  const input = parseHookInput(raw);
@@ -2579,7 +2606,7 @@ Fail-soft: no lane / no auth / no-or-partial intent file / API error -> exit 0,
2579
2606
  return process.exit(0);
2580
2607
  }
2581
2608
  });
2582
- hook.command("session-end").description("Save a continuity snapshot from the intent file on a SessionEnd (Claude) / Stop (Codex) hook").option("--lane <laneId>", "Override the resolved lane (else SECHROOM_LANE, else ./.sem code-lane)").option("--scope <scope>", "Snapshot scope (else the intent file's `scope`, else 'session-end')").option("--surface <surface>", "Target surface: claude | codex (lifecycle-only on both)", "claude").option(
2609
+ hook.command("session-end").description("Save a continuity snapshot from the intent file on a SessionEnd (Claude) / Stop (Codex) hook").option("--lane <laneId>", "Override the resolved lane (else SECHROOM_LANE, else ./.sechroom/lane.json code-lane)").option("--scope <scope>", "Snapshot scope (else the intent file's `scope`, else 'session-end')").option("--surface <surface>", "Target surface: claude | codex (lifecycle-only on both)", "claude").option(
2583
2610
  "--debounce-minutes <n>",
2584
2611
  "skip if a hook checkpoint ran within this many minutes \u2014 for high-frequency triggers like Codex Stop (Claude SessionEnd passes none)"
2585
2612
  ).action(async (opts, cmd) => {
@@ -2656,12 +2683,12 @@ Fail-soft: no lane / no auth / no-or-partial intent file / API error -> exit 0,
2656
2683
  function registerCheckpoint(program2) {
2657
2684
  program2.command("checkpoint").description(
2658
2685
  "Checkpoint working state: create a continuity snapshot (server-validated) AND sync ./.sechroom/continuity.json in one step"
2659
- ).option("--lane <laneId>", "Lane id (else SECHROOM_LANE, else ./.sem code-lane)").option("--scope <scope>", "Snapshot scope (else the file's scope, else 'session')").option("--objective <text>", "Current objective").option("--state <text>", "Current state").option("--last-action <text>", "Last meaningful action").option("--next-action <text>", "Next intended action").option("--resume-instruction <text>", "Resume instruction").option("--constraint <text...>", "Active constraints (repeatable)").option("--question <text...>", "Open questions (repeatable)").option("--surface-marker <text...>", "Surface markers (repeatable)").option("--artifact <id...>", "Relevant artifact ids (repeatable)").option("--confidence <n>", "Confidence 0..1").option("--dry-run", "validate + print the snapshot payload without creating it or writing the file", false).addHelpText(
2686
+ ).option("--lane <laneId>", "Lane id (else SECHROOM_LANE, else ./.sechroom/lane.json code-lane)").option("--scope <scope>", "Snapshot scope (else the file's scope, else 'session')").option("--objective <text>", "Current objective").option("--state <text>", "Current state").option("--last-action <text>", "Last meaningful action").option("--next-action <text>", "Next intended action").option("--resume-instruction <text>", "Resume instruction").option("--constraint <text...>", "Active constraints (repeatable)").option("--question <text...>", "Open questions (repeatable)").option("--surface-marker <text...>", "Surface markers (repeatable)").option("--artifact <id...>", "Relevant artifact ids (repeatable)").option("--confidence <n>", "Confidence 0..1").option("--dry-run", "validate + print the snapshot payload without creating it or writing the file", false).addHelpText(
2660
2687
  "after",
2661
2688
  `
2662
2689
  File-first: reads ./.sechroom/continuity.json (kept current as you work) as the base; any flag
2663
2690
  overrides that field. The snapshot is created FIRST (server-validated), then the local file is
2664
- written/normalized with the returned snapshotId. Lane: --lane > SECHROOM_LANE > ./.sem code-lane.
2691
+ written/normalized with the returned snapshotId. Lane: --lane > SECHROOM_LANE > ./.sechroom/lane.json code-lane.
2665
2692
 
2666
2693
  Examples:
2667
2694
  $ sechroom checkpoint snapshot from ./.sechroom/continuity.json, then sync it
@@ -2689,7 +2716,7 @@ Examples:
2689
2716
  const lane = resolveLane(opts.lane, cwd);
2690
2717
  if (!lane) {
2691
2718
  fail(
2692
- "no lane resolved \u2014 pass --lane, set SECHROOM_LANE, or pin one in ./.sem (code-lane). See `sechroom lane`."
2719
+ "no lane resolved \u2014 pass --lane, set SECHROOM_LANE, or pin one in ./.sechroom/lane.json (code-lane). See `sechroom lane`."
2693
2720
  );
2694
2721
  }
2695
2722
  const required = [
@@ -2815,7 +2842,7 @@ Examples:
2815
2842
  });
2816
2843
  emitAction("updated profile", data, cmd.optsWithGlobals().json);
2817
2844
  });
2818
- account.command("feed").description("Your recent memory feed (GET /me/memories/feed)").option("--limit <n>", "Max results", "20").option("--cursor <cursor>", "Opaque paging cursor").option("--query <query>", "Free-text filter").option("--filter-tags <tags>", "Comma-separated tag filter").option("--include-archived", "Include archived memories", false).option("--include-text", "Include memory body text", false).action(async (opts, cmd) => {
2845
+ account.command("feed").description("Your recent memory feed (GET /me/memories/feed)").option("--limit <n>", "Max results", "20").option("--cursor <cursor>", "Opaque paging cursor").option("--query <query>", "Free-text filter").option("--filter-tags <tags>", "Comma-separated tag filter").option("--include-archived", "Include archived memories", false).option("--include-text", "Include memory body text", false).option("--since <iso>", "Only contributions updated since this ISO-8601 timestamp (updatedSince)").option("--order <order>", "Order: LastTouchedDesc | LastTouchedAsc | FirstTouchedDesc | FirstTouchedAsc").action(async (opts, cmd) => {
2819
2846
  const cfg = resolveConfig(cmd.optsWithGlobals());
2820
2847
  const data = await runApi("Fetching feed", async () => {
2821
2848
  const client = await makeClient(cfg);
@@ -2827,7 +2854,9 @@ Examples:
2827
2854
  includeText: Boolean(opts.includeText),
2828
2855
  ...opts.cursor ? { cursor: opts.cursor } : {},
2829
2856
  ...opts.query ? { query: opts.query } : {},
2830
- ...opts.filterTags ? { filterTags: opts.filterTags } : {}
2857
+ ...opts.filterTags ? { filterTags: opts.filterTags } : {},
2858
+ ...opts.since ? { updatedSince: opts.since } : {},
2859
+ ...opts.order ? { orderBy: opts.order } : {}
2831
2860
  }
2832
2861
  }
2833
2862
  });
@@ -3314,6 +3343,8 @@ var SKILL_ROLE_TAG = "sechroom:role:skill-template";
3314
3343
  var SKILL_NAME_PREFIX = "skill:";
3315
3344
  var AGENT_ROLE_TAG = "sechroom:role:agent-template";
3316
3345
  var AGENT_NAME_PREFIX = "agent:";
3346
+ var REFERENCE_ROLE_TAG = "sechroom:role:skill-reference";
3347
+ var REFERENCE_NAME_PREFIX = "component:";
3317
3348
  function tagsOf(row) {
3318
3349
  const m = row?.item ?? row;
3319
3350
  return m?.tags ?? m?.Tags ?? [];
@@ -3350,6 +3381,9 @@ function resolveSkills(systemRows, personalRows, surface) {
3350
3381
  function resolveAgents(systemRows, personalRows, surface) {
3351
3382
  return resolveByRole(systemRows, personalRows, surface, AGENT_ROLE_TAG, AGENT_NAME_PREFIX);
3352
3383
  }
3384
+ function resolveReferences(systemRows, personalRows, surface) {
3385
+ return resolveByRole(systemRows, personalRows, surface, REFERENCE_ROLE_TAG, REFERENCE_NAME_PREFIX);
3386
+ }
3353
3387
 
3354
3388
  // src/setup/skill-resolution-io.ts
3355
3389
  var AGENT_TARGET = { "claude-code": "claude-agent" };
@@ -3385,6 +3419,9 @@ function resolveSkillSet(rows, surface) {
3385
3419
  function resolveAgentSet(rows, surface) {
3386
3420
  return resolveAgents(rows.systemRows, rows.personalRows, agentTargetFor(surface));
3387
3421
  }
3422
+ function resolveReferenceSet(rows, surface) {
3423
+ return resolveReferences(rows.systemRows, rows.personalRows, surface);
3424
+ }
3388
3425
 
3389
3426
  // src/setup/skills-lock.ts
3390
3427
  import { existsSync as existsSync6, mkdirSync as mkdirSync6, readFileSync as readFileSync5, writeFileSync as writeFileSync6 } from "fs";
@@ -3594,7 +3631,7 @@ function registerInit(program2) {
3594
3631
  `
3595
3632
  Examples:
3596
3633
  $ sechroom init Claude Code (default): ./.mcp.json + ./CLAUDE.md
3597
- $ sechroom init --client all claude-code, claude-desktop, codex, cursor
3634
+ $ sechroom init --client all claude-code, claude-desktop, codex, cursor, antigravity
3598
3635
  $ sechroom init --client codex cursor space-separated (comma also works)
3599
3636
  $ sechroom init --mcp-only just the MCP config (skip agent files)
3600
3637
  $ sechroom init --dry-run --json preview the writes, change nothing`
@@ -3847,7 +3884,7 @@ Wired to namespace '${slug}'. Restart your AI client (or reload MCP) to pick it
3847
3884
 
3848
3885
  // src/commands/onboard.ts
3849
3886
  import { existsSync as existsSync8 } from "fs";
3850
- import { basename as basename3, join as join10 } from "path";
3887
+ import { basename as basename2, join as join10 } from "path";
3851
3888
 
3852
3889
  // src/commands/fanout.ts
3853
3890
  import { spawnSync } from "child_process";
@@ -4056,7 +4093,7 @@ function personalSubtreeIds(personalId, all) {
4056
4093
  }
4057
4094
  async function pickWorkspace(client, opts = {}) {
4058
4095
  const promptLabel = opts.promptLabel ?? "Bind this directory to a workspace:";
4059
- const dirName = opts.dirName ?? basename3(process.cwd());
4096
+ const dirName = opts.dirName ?? basename2(process.cwd());
4060
4097
  const all = await withSpinner("Listing your workspaces", () => fetchWorkspaces(client));
4061
4098
  if (all.length === 0) {
4062
4099
  process.stderr.write(`no workspaces found \u2014 skipping workspace binding (you can set it later with \`sechroom config set --local workspaceId <id>\`)
@@ -4118,7 +4155,7 @@ async function resolveWorkspaceBinding(client, existing, opts) {
4118
4155
  }
4119
4156
  if (existing) return existing;
4120
4157
  if (!canPrompt() || opts.yes) return void 0;
4121
- return pickWorkspace(client, { dirName: basename3(process.cwd()) });
4158
+ return pickWorkspace(client, { dirName: basename2(process.cwd()) });
4122
4159
  }
4123
4160
  async function ensureTenant(baseUrl, g, opts) {
4124
4161
  const persisted = readPersisted();
@@ -4295,7 +4332,7 @@ ${style.bold(entry.path)} ${style.dim("is not bound yet.")}
4295
4332
  `);
4296
4333
  const ws = await pickWorkspace(client, {
4297
4334
  promptLabel: `Bind ${style.cyan(entry.path)} to a workspace:`,
4298
- dirName: basename3(entry.path)
4335
+ dirName: basename2(entry.path)
4299
4336
  });
4300
4337
  if (!ws) {
4301
4338
  return { label: entry.path, dir, disposition: "skip-unbound", argv: [], reason: "unbound \u2014 no workspace chosen (skipped)" };
@@ -4740,6 +4777,17 @@ function writeAgents(dir, agents, surface) {
4740
4777
  if (written.length) recordMaterialisedSkills(dir, DEFAULT_SKILLS_SLUG, written, { surface });
4741
4778
  return written;
4742
4779
  }
4780
+ function writeReferencesIntoSkillDirs(dir, skills, refs) {
4781
+ if (!refs.length || !skills.length) return [];
4782
+ for (const s of skills) {
4783
+ const refDir = join12(dir, s.name, "references");
4784
+ mkdirSync8(refDir, { recursive: true });
4785
+ for (const r of refs) {
4786
+ writeFileSync8(join12(refDir, `${r.name}.md`), r.body.endsWith("\n") ? r.body : r.body + "\n");
4787
+ }
4788
+ }
4789
+ return refs.map((r) => r.name);
4790
+ }
4743
4791
  var SKILL_SPEC = { kind: "skill", dir: skillsDir, resolve: resolveSkillSet, write: writeSkills };
4744
4792
  var AGENT_SPEC = { kind: "agent", dir: agentsDir, resolve: resolveAgentSet, write: writeAgents };
4745
4793
  function scopeOf(opts) {
@@ -4760,12 +4808,14 @@ async function runInstall(spec, cmd, opts) {
4760
4808
  const personalWorkspaceId = await getPersonalWorkspaceId(cfg);
4761
4809
  const rows = await fetchTemplateRows(cfg, personalWorkspaceId);
4762
4810
  const items = spec.resolve(rows, CLIENT_SURFACE);
4811
+ const refs = spec.kind === "skill" ? resolveReferenceSet(rows, CLIENT_SURFACE) : [];
4763
4812
  const results = targets.map((t) => {
4764
4813
  const dir = spec.dir(t.dir);
4765
4814
  const written = dryRun ? items.map((i) => i.name) : spec.write(dir, items, CLIENT_SURFACE);
4766
- return { dir, label: t.label, written };
4815
+ const refsWritten = dryRun ? refs.map((r) => r.name) : writeReferencesIntoSkillDirs(dir, items, refs);
4816
+ return { dir, label: t.label, written, refsWritten };
4767
4817
  });
4768
- if (json) return emit({ kind: spec.kind, dryRun, available: items.length, targets: results }, true);
4818
+ if (json) return emit({ kind: spec.kind, dryRun, available: items.length, references: refs.length, targets: results }, true);
4769
4819
  if (items.length === 0) {
4770
4820
  console.log(style.dim(`No ${spec.kind}s available to install \u2014 is the bundle installed for your account?`));
4771
4821
  return;
@@ -4774,6 +4824,10 @@ async function runInstall(spec, cmd, opts) {
4774
4824
  console.log(
4775
4825
  `${dryRun ? "" : style.green("\u2713 ")}${dryRun ? "would write" : "wrote"} ${r.written.length} ${spec.kind}(s) ${style.dim("\u2192")} ${r.dir}`
4776
4826
  );
4827
+ if (r.refsWritten.length)
4828
+ console.log(
4829
+ `${dryRun ? "" : style.green("\u2713 ")}${dryRun ? "would write" : "wrote"} ${r.refsWritten.length} reference(s) into each skill ${style.dim("\u2192")} ${r.dir}/<skill>/references`
4830
+ );
4777
4831
  if (dryRun) for (const i of items) console.log(` ${i.name} ${style.dim(`[${i.source}]`)}`);
4778
4832
  }
4779
4833
  }
@@ -4910,7 +4964,7 @@ Examples:
4910
4964
  console.log(" " + style.bold("default-design-lane") + " = " + (data?.defaultDesignLane ?? style.dim("(unset)")));
4911
4965
  console.log(" " + style.bold("handover-recipient") + " = " + (data?.handoverRecipient ?? style.dim("(unset)")));
4912
4966
  });
4913
- skills.command("resolve").description("Resolve the effective ${identity.*} slot values (per-location .sem + per-operator workflow prefs)").option("--json", "machine output (a flat slot->value map + per-slot source)").action(async (opts, cmd) => {
4967
+ skills.command("resolve").description("Resolve the effective ${identity.*} slot values (per-location .sechroom/lane.json + per-operator workflow prefs)").option("--json", "machine output (a flat slot->value map + per-slot source)").action(async (opts, cmd) => {
4914
4968
  const local = readSem()?.values ?? {};
4915
4969
  let operator = {};
4916
4970
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sechroom/cli",
3
- "version": "2026.6.36",
3
+ "version": "2026.6.37",
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",