@sechroom/cli 2026.6.221-rc.1f0a622c → 2026.6.223-rc.d98a230b

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 +74 -37
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -844,6 +844,50 @@ Examples:
844
844
  cmd.optsWithGlobals().json
845
845
  );
846
846
  });
847
+ memory.command("update <memoryId>").description("Update metadata only \u2014 title/tags/type/confidence (PATCH /memories/{memoryId}/metadata; omitted = unchanged)").option("--title <text>", "Set the title").option("--tag <tag...>", "Set the full tag list (replaces existing); repeatable").option("--add-tag <tag...>", "Add tag(s) to the existing set (repeatable)").option("--remove-tag <tag...>", "Remove tag(s) from the existing set (repeatable)").option("--type <type>", "Set the memory type (e.g. reference, note, document)").option("--confidence <n>", "Set confidence (0..1)", (v) => Number(v)).option("--memory-source <src>", "Set the memory's own Source field").option("--bump-version", "Bump the version chain (use for a content reinterpretation, e.g. a type promotion)", false).option("--source <source>", "Contributing lane stamp (attribution)", "cli").action(async (memoryId, opts, cmd) => {
848
+ const cfg = resolveConfig(cmd.optsWithGlobals());
849
+ const json = cmd.optsWithGlobals().json;
850
+ const hasTagOps = Boolean(opts.tag || opts.addTag || opts.removeTag);
851
+ const hasAny = opts.title !== void 0 || hasTagOps || opts.type !== void 0 || opts.confidence !== void 0 || opts.memorySource !== void 0 || Boolean(opts.bumpVersion);
852
+ if (!hasAny)
853
+ fail("nothing to update \u2014 pass at least one of --title / --tag / --add-tag / --remove-tag / --type / --confidence / --memory-source.");
854
+ let tags;
855
+ if (hasTagOps) {
856
+ let base;
857
+ if (opts.tag) base = opts.tag;
858
+ else {
859
+ const current = await runApi("Reading current tags", async () => {
860
+ const client = await makeClient(cfg);
861
+ return client.GET("/memories/{memoryId}", { params: { path: { memoryId } } });
862
+ });
863
+ base = current?.item?.tags ?? current?.tags ?? [];
864
+ }
865
+ const set = new Set(base);
866
+ for (const t of opts.addTag ?? []) set.add(t);
867
+ for (const t of opts.removeTag ?? []) set.delete(t);
868
+ tags = [...set];
869
+ }
870
+ const body = {
871
+ memoryId,
872
+ source: opts.source,
873
+ bumpVersion: Boolean(opts.bumpVersion)
874
+ };
875
+ if (opts.title !== void 0) body.title = opts.title;
876
+ if (tags !== void 0) body.tags = tags;
877
+ if (opts.type !== void 0) body.type = opts.type;
878
+ if (opts.confidence !== void 0) body.confidence = opts.confidence;
879
+ if (opts.memorySource !== void 0) body.memorySource = opts.memorySource;
880
+ const data = await runApi("Updating metadata", async () => {
881
+ const client = await makeClient(cfg);
882
+ return client.PATCH("/memories/{memoryId}/metadata", {
883
+ params: { path: { memoryId } },
884
+ body
885
+ });
886
+ });
887
+ const changed = data?.changed ?? [];
888
+ const summary = changed.length > 0 ? `updated ${changed.join(", ")}` : "no changes";
889
+ emitAction(`${summary} on ${style.bold(memoryId)} \u2192 v${style.bold(String(data?.version ?? "?"))}`, data, json);
890
+ });
847
891
  memory.command("archive <memoryId>").description("Archive a memory (POST /memories/{memoryId}/archive)").option("--source <source>", "Source / lane stamp", "cli").action(async (memoryId, opts, cmd) => {
848
892
  const cfg = resolveConfig(cmd.optsWithGlobals());
849
893
  const data = await runApi("Archiving memory", async () => {
@@ -1783,10 +1827,9 @@ import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as rea
1783
1827
  import { delimiter, dirname as dirname4, join as join5 } from "path";
1784
1828
 
1785
1829
  // src/sem.ts
1786
- import { basename as basename2, dirname as dirname2, join as join2 } from "path";
1830
+ import { dirname as dirname2, join as join2 } from "path";
1787
1831
  import { appendFileSync, existsSync as existsSync2, mkdirSync as mkdirSync2, readdirSync, readFileSync as readFileSync2, statSync, writeFileSync as writeFileSync2 } from "fs";
1788
1832
  var SEM_FILE = join2(".sechroom", "lane.json");
1789
- var LEGACY_SEM_FILE = ".sem";
1790
1833
  var STATE_DIR_NAME2 = ".sechroom";
1791
1834
  function localSemPath(cwd = process.cwd()) {
1792
1835
  return join2(cwd, SEM_FILE);
@@ -1796,8 +1839,6 @@ function resolveSemPathForRead(start = process.cwd()) {
1796
1839
  while (true) {
1797
1840
  const candidate = join2(dir, SEM_FILE);
1798
1841
  if (existsSync2(candidate)) return candidate;
1799
- const legacy = join2(dir, LEGACY_SEM_FILE);
1800
- if (existsSync2(legacy)) return legacy;
1801
1842
  const parent = dirname2(dir);
1802
1843
  if (parent === dir) return void 0;
1803
1844
  dir = parent;
@@ -1840,34 +1881,17 @@ function laneWithWorktreeSuffix(lane, gitFile, siblings) {
1840
1881
  const idx = [...siblings].sort().indexOf(m[1]);
1841
1882
  return idx < 0 ? lane : `${lane}-${idx + 2}`;
1842
1883
  }
1843
- function parseSem(text) {
1844
- const out = {};
1845
- for (const raw of text.split("\n")) {
1846
- const line = raw.trim();
1847
- if (!line || line.startsWith("#")) continue;
1848
- const eq = line.indexOf("=");
1849
- if (eq === -1) continue;
1850
- const key = line.slice(0, eq).trim();
1851
- const value = line.slice(eq + 1).trim();
1852
- if (key) out[key] = value;
1853
- }
1854
- return out;
1855
- }
1856
1884
  function serializeSem(values) {
1857
1885
  return JSON.stringify(values, null, 2) + "\n";
1858
1886
  }
1859
1887
  function readSem(path) {
1860
1888
  const p = path ?? resolveSemPathForRead();
1861
1889
  if (!p || !existsSync2(p)) return void 0;
1862
- const text = readFileSync2(p, "utf8");
1863
- const values = basename2(p) === LEGACY_SEM_FILE ? parseSem(text) : parseLaneJson(text);
1864
- return { path: p, values };
1890
+ return { path: p, values: parseLaneJson(readFileSync2(p, "utf8")) };
1865
1891
  }
1866
1892
  function readLocalSemValues(cwd = process.cwd()) {
1867
1893
  const next = join2(cwd, SEM_FILE);
1868
1894
  if (existsSync2(next)) return readSem(next)?.values ?? {};
1869
- const legacy = join2(cwd, LEGACY_SEM_FILE);
1870
- if (existsSync2(legacy)) return readSem(legacy)?.values ?? {};
1871
1895
  return {};
1872
1896
  }
1873
1897
  function parseLaneJson(text) {
@@ -2157,10 +2181,22 @@ function clientTargets(cwd, opts = {}) {
2157
2181
  label: "Cursor",
2158
2182
  mcp: { surfaceKey: "claude-code", sectionType: SectionType.McpConfig, path: join4(cwd, ".cursor", "mcp.json"), format: "json" },
2159
2183
  instruction: { surfaceKey: "chatgpt", path: join4(cwd, "AGENTS.md") }
2184
+ },
2185
+ antigravity: {
2186
+ key: "antigravity",
2187
+ label: "Google Antigravity",
2188
+ // FR-sechroom-247 — Antigravity reads MCP from a GLOBAL, home-relative
2189
+ // `~/.gemini/config/mcp_config.json` (not cwd; not affected by
2190
+ // CLAUDE_CONFIG_DIR / CODEX_HOME). The snippet — `serverUrl`-shaped, no
2191
+ // `type` — comes from the `antigravity` server surface, so we don't
2192
+ // hardcode it here. Instructions go in the project `AGENTS.md`
2193
+ // (cross-tool, shared with Codex/Cursor).
2194
+ mcp: { surfaceKey: "antigravity", sectionType: SectionType.McpConfig, path: join4(home, ".gemini", "config", "mcp_config.json"), format: "json" },
2195
+ instruction: { surfaceKey: "antigravity", path: join4(cwd, "AGENTS.md") }
2160
2196
  }
2161
2197
  };
2162
2198
  }
2163
- var ALL_CLIENT_KEYS = ["claude-code", "claude-desktop", "codex", "cursor"];
2199
+ var ALL_CLIENT_KEYS = ["claude-code", "claude-desktop", "codex", "cursor", "antigravity"];
2164
2200
  var DEFAULT_CLIENT_KEY = "claude-code";
2165
2201
  function detectInstalledClients(cwd) {
2166
2202
  const home = homedir3();
@@ -2169,6 +2205,7 @@ function detectInstalledClients(cwd) {
2169
2205
  if (existsSync3(dirname3(claudeDesktopConfigPath(home)))) detected.push("claude-desktop");
2170
2206
  if (resolveCodexHomes({}).some((d) => existsSync3(d))) detected.push("codex");
2171
2207
  if (existsSync3(join4(home, ".cursor")) || existsSync3(join4(cwd, ".cursor"))) detected.push("cursor");
2208
+ if (existsSync3(join4(home, ".gemini"))) detected.push("antigravity");
2172
2209
  return detected;
2173
2210
  }
2174
2211
 
@@ -2497,10 +2534,10 @@ Examples:
2497
2534
  $ sechroom hook install --surface codex Codex only
2498
2535
  $ sechroom hook install --local --dry-run preview the project .claude/settings.json
2499
2536
 
2500
- Lane source (high -> low): --lane > SECHROOM_LANE > ./.sem code-lane (D-binding-5).
2537
+ Lane source (high -> low): --lane > SECHROOM_LANE > ./.sechroom/lane.json code-lane (D-binding-5).
2501
2538
  Fail-soft: no lane / no auth / no-or-partial intent file / API error -> exit 0, never blocks.`
2502
2539
  );
2503
- 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) => {
2540
+ 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) => {
2504
2541
  try {
2505
2542
  const raw = await readStdin();
2506
2543
  const input = parseHookInput(raw);
@@ -2524,7 +2561,7 @@ Fail-soft: no lane / no auth / no-or-partial intent file / API error -> exit 0,
2524
2561
  return process.exit(0);
2525
2562
  }
2526
2563
  });
2527
- 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) => {
2564
+ 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) => {
2528
2565
  try {
2529
2566
  const raw = await readStdin();
2530
2567
  const input = parseHookInput(raw);
@@ -2535,7 +2572,7 @@ Fail-soft: no lane / no auth / no-or-partial intent file / API error -> exit 0,
2535
2572
  return process.exit(0);
2536
2573
  }
2537
2574
  });
2538
- 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(
2575
+ 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(
2539
2576
  "--debounce-minutes <n>",
2540
2577
  "skip if a hook checkpoint ran within this many minutes \u2014 for high-frequency triggers like Codex Stop (Claude SessionEnd passes none)"
2541
2578
  ).action(async (opts, cmd) => {
@@ -2612,12 +2649,12 @@ Fail-soft: no lane / no auth / no-or-partial intent file / API error -> exit 0,
2612
2649
  function registerCheckpoint(program2) {
2613
2650
  program2.command("checkpoint").description(
2614
2651
  "Checkpoint working state: create a continuity snapshot (server-validated) AND sync ./.sechroom/continuity.json in one step"
2615
- ).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(
2652
+ ).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(
2616
2653
  "after",
2617
2654
  `
2618
2655
  File-first: reads ./.sechroom/continuity.json (kept current as you work) as the base; any flag
2619
2656
  overrides that field. The snapshot is created FIRST (server-validated), then the local file is
2620
- written/normalized with the returned snapshotId. Lane: --lane > SECHROOM_LANE > ./.sem code-lane.
2657
+ written/normalized with the returned snapshotId. Lane: --lane > SECHROOM_LANE > ./.sechroom/lane.json code-lane.
2621
2658
 
2622
2659
  Examples:
2623
2660
  $ sechroom checkpoint snapshot from ./.sechroom/continuity.json, then sync it
@@ -2645,7 +2682,7 @@ Examples:
2645
2682
  const lane = resolveLane(opts.lane, cwd);
2646
2683
  if (!lane) {
2647
2684
  fail(
2648
- "no lane resolved \u2014 pass --lane, set SECHROOM_LANE, or pin one in ./.sem (code-lane). See `sechroom lane`."
2685
+ "no lane resolved \u2014 pass --lane, set SECHROOM_LANE, or pin one in ./.sechroom/lane.json (code-lane). See `sechroom lane`."
2649
2686
  );
2650
2687
  }
2651
2688
  const required = [
@@ -3550,7 +3587,7 @@ function registerInit(program2) {
3550
3587
  `
3551
3588
  Examples:
3552
3589
  $ sechroom init Claude Code (default): ./.mcp.json + ./CLAUDE.md
3553
- $ sechroom init --client all claude-code, claude-desktop, codex, cursor
3590
+ $ sechroom init --client all claude-code, claude-desktop, codex, cursor, antigravity
3554
3591
  $ sechroom init --client codex cursor space-separated (comma also works)
3555
3592
  $ sechroom init --mcp-only just the MCP config (skip agent files)
3556
3593
  $ sechroom init --dry-run --json preview the writes, change nothing`
@@ -3803,7 +3840,7 @@ Wired to namespace '${slug}'. Restart your AI client (or reload MCP) to pick it
3803
3840
 
3804
3841
  // src/commands/onboard.ts
3805
3842
  import { existsSync as existsSync8 } from "fs";
3806
- import { basename as basename3, join as join10 } from "path";
3843
+ import { basename as basename2, join as join10 } from "path";
3807
3844
 
3808
3845
  // src/commands/fanout.ts
3809
3846
  import { spawnSync } from "child_process";
@@ -4012,7 +4049,7 @@ function personalSubtreeIds(personalId, all) {
4012
4049
  }
4013
4050
  async function pickWorkspace(client, opts = {}) {
4014
4051
  const promptLabel = opts.promptLabel ?? "Bind this directory to a workspace:";
4015
- const dirName = opts.dirName ?? basename3(process.cwd());
4052
+ const dirName = opts.dirName ?? basename2(process.cwd());
4016
4053
  const all = await withSpinner("Listing your workspaces", () => fetchWorkspaces(client));
4017
4054
  if (all.length === 0) {
4018
4055
  process.stderr.write(`no workspaces found \u2014 skipping workspace binding (you can set it later with \`sechroom config set --local workspaceId <id>\`)
@@ -4074,7 +4111,7 @@ async function resolveWorkspaceBinding(client, existing, opts) {
4074
4111
  }
4075
4112
  if (existing) return existing;
4076
4113
  if (!canPrompt() || opts.yes) return void 0;
4077
- return pickWorkspace(client, { dirName: basename3(process.cwd()) });
4114
+ return pickWorkspace(client, { dirName: basename2(process.cwd()) });
4078
4115
  }
4079
4116
  async function ensureTenant(baseUrl, g, opts) {
4080
4117
  const persisted = readPersisted();
@@ -4251,7 +4288,7 @@ ${style.bold(entry.path)} ${style.dim("is not bound yet.")}
4251
4288
  `);
4252
4289
  const ws = await pickWorkspace(client, {
4253
4290
  promptLabel: `Bind ${style.cyan(entry.path)} to a workspace:`,
4254
- dirName: basename3(entry.path)
4291
+ dirName: basename2(entry.path)
4255
4292
  });
4256
4293
  if (!ws) {
4257
4294
  return { label: entry.path, dir, disposition: "skip-unbound", argv: [], reason: "unbound \u2014 no workspace chosen (skipped)" };
@@ -4866,7 +4903,7 @@ Examples:
4866
4903
  console.log(" " + style.bold("default-design-lane") + " = " + (data?.defaultDesignLane ?? style.dim("(unset)")));
4867
4904
  console.log(" " + style.bold("handover-recipient") + " = " + (data?.handoverRecipient ?? style.dim("(unset)")));
4868
4905
  });
4869
- 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) => {
4906
+ 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) => {
4870
4907
  const local = readSem()?.values ?? {};
4871
4908
  let operator = {};
4872
4909
  try {
@@ -4903,7 +4940,7 @@ function registerAgents(program2) {
4903
4940
  Examples:
4904
4941
  $ sechroom agents install materialise your installed agents to ~/.claude/agents
4905
4942
  $ sechroom agents install --scope project write them to ./.claude/agents instead
4906
- $ sechroom agents install --claude-config-dir ~/.claude-abcs target another instance
4943
+ $ sechroom agents install --claude-config-dir ~/.claude-work target another instance
4907
4944
  $ sechroom agents list what's materialised on disk
4908
4945
  $ sechroom agents clean remove the materialised agent files
4909
4946
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sechroom/cli",
3
- "version": "2026.6.221-rc.1f0a622c",
3
+ "version": "2026.6.223-rc.d98a230b",
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",