@sechroom/cli 2026.6.8 → 2026.6.10

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 (3) hide show
  1. package/README.md +29 -1
  2. package/dist/index.js +1139 -2
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -132,6 +132,27 @@ export SECHROOM_TENANT=ocd
132
132
  sechroom --json memory search "rate limiting"
133
133
  ```
134
134
 
135
+ ## Command surface (MCP parity)
136
+
137
+ The CLI mirrors the sechroom MCP tool surface — every command is a thin wrapper over the same HTTP endpoint the matching MCP tool shims, so enforcement (`[TenantPermission]`) is identical. Run `sechroom <group> --help` for the subcommands + examples.
138
+
139
+ | Group | Covers |
140
+ |---|---|
141
+ | `memory` | create / get / search · edit-text(+batch) · archive / restore / move · versions / revert · list-archived · owners / tags / types · sum-tokens · similar · by-url |
142
+ | `relationship` | create / list / delete · suggest · `suggestion` get / accept / reject / defer |
143
+ | `workspace` | create / list / get · rename / describe / move · archive / restore · feed |
144
+ | `project` | create / list / get · rename / describe / move · status · victory-conditions · archive / restore |
145
+ | `filing` | suggestions / get · preview · accept / reject / defer / edit-and-accept |
146
+ | `continuity` | snapshot-create / -get · snapshots · resume-me / resume-lane · changed-since · load-set · grant / revoke-grant |
147
+ | `id` | next / peek (FR-_/D-_ sequence allocation) |
148
+ | `account` | profile / set-profile · feed · reviews / review-get / review-accept · lookup-batch |
149
+ | `chat` | send · messages · replies · stop-tracking (Slack / Discord, via `--surface`) |
150
+ | `worklog` · `lookup` | append · resolve any id |
151
+
152
+ Notes on deliberate gaps (API-rooted, not CLI):
153
+ - **No `memory delete`** — the API exposes no hard DELETE; `memory archive` is the soft-delete path.
154
+ - **`memory revert`** needs `--text` + `--content` — the revert endpoint doesn't reconstruct a version's body from its number; pull them from `memory versions` / `memory get` first.
155
+
135
156
  ## Onboarding (`init` / `setup`)
136
157
 
137
158
  `sechroom init` wires a project for sechroom by rendering the server's
@@ -196,7 +217,14 @@ src/
196
217
  config.ts base-url / tenant / token resolution + persistence
197
218
  generated/api.d.ts typed client — `pnpm run gen`; real types committed (hermetic)
198
219
  commands/
199
- memory.ts create / get / search
220
+ memory.ts create / get / search / edit / archive / move / versions / …
221
+ relationships.ts relationships + relationship-suggestions
222
+ workspace.ts workspace CRUD + feed
223
+ project.ts project CRUD + status / victory-conditions
224
+ filing.ts filing-suggestion review (accept / reject / defer / edit-and-accept)
225
+ continuity.ts snapshots + resume / grant
226
+ account.ts id next/peek + profile / feed / reviews / lookup-batch
227
+ chat.ts read Slack / Discord messages + replies
200
228
  worklog.ts append
201
229
  lookup.ts resolve any id (mem_…/unprefixed/sechroom:<id>) -> kind/title/url
202
230
  setup.ts init + setup mcp/agent-files
package/dist/index.js CHANGED
@@ -468,6 +468,12 @@ function emit(data, json) {
468
468
  function publicUrl(url) {
469
469
  return url.replace(/^https?:\/\/localhost:5012/, "https://sechroom.yi.ocd.codes");
470
470
  }
471
+ function resolveViewUrl(baseUrl, url) {
472
+ if (!url) return void 0;
473
+ if (/^https?:\/\//i.test(url)) return publicUrl(url);
474
+ const origin = baseUrl.replace(/\/api\/?$/i, "").replace(/\/+$/, "");
475
+ return publicUrl(`${origin}${url.startsWith("/") ? url : `/${url}`}`);
476
+ }
471
477
  function emitAction(summary, data, json) {
472
478
  if (json) {
473
479
  process.stdout.write(JSON.stringify(data) + "\n");
@@ -510,7 +516,13 @@ Examples:
510
516
  $ sechroom memory create --text "filed note" --owner-type Workspace --owner-id wsp_XXXX
511
517
  $ sechroom memory search "rate limiting" --limit 5 --tag kind:decision
512
518
  $ sechroom memory search "auth flow" --workspace wsp_XXXX --json
513
- $ sechroom memory get mem_XXXX --json`
519
+ $ sechroom memory get mem_XXXX --json
520
+ $ sechroom memory edit-text mem_XXXX --old "teh" --new "the" --replace-all
521
+ $ sechroom memory edit-text-batch mem_XXXX --edit "foo=>bar" --edit "baz=>qux"
522
+ $ sechroom memory move mem_XXXX --owner-type Workspace --owner-id wsp_XXXX
523
+ $ sechroom memory archive mem_XXXX
524
+ $ sechroom memory list-archived --workspace wsp_XXXX --json
525
+ $ sechroom memory tags --json`
514
526
  );
515
527
  memory.command("create").description("Create a memory (POST /memories)").requiredOption("--text <text>", "Memory body text").option("--type <type>", "Memory type", "reference").option("--title <title>", "Optional title").option("--tag <tag...>", "Tags (repeatable)").option("--owner-type <ownerType>", "Workspace | Project | Unfiled", "Unfiled").option("--owner-id <ownerId>", "Owner id (required for Workspace/Project)").option("--source <source>", "Source / lane stamp", "cli").option("--confidence <n>", "Confidence 0..1", "1.0").action(async (opts, cmd) => {
516
528
  const cfg = resolveConfig(cmd.optsWithGlobals());
@@ -532,7 +544,8 @@ Examples:
532
544
  });
533
545
  });
534
546
  const titlePart = opts.title ? ` ${style.dim(`"${opts.title}"`)}` : "";
535
- const urlPart = data.url ? ` ${style.dim("\u2192")} ${publicUrl(data.url)}` : "";
547
+ const view = resolveViewUrl(cfg.baseUrl, data.url);
548
+ const urlPart = view ? ` ${style.dim("\u2192")} ${view}` : "";
536
549
  emitAction(`created memory ${style.bold(data.id)}${titlePart}${urlPart}`, data, cmd.optsWithGlobals().json);
537
550
  });
538
551
  memory.command("get <memoryId>").description("Fetch a memory by id (GET /memories/{memoryId})").action(async (memoryId, _opts, cmd) => {
@@ -563,6 +576,201 @@ Examples:
563
576
  });
564
577
  emit(data, cmd.optsWithGlobals().json);
565
578
  });
579
+ memory.command("edit-text <memoryId>").description("Find/replace one substring (POST /memories/{memoryId}/edit-text)").requiredOption("--old <text>", "Text to find").requiredOption("--new <text>", "Replacement text").option("--replace-all", "Replace every occurrence (default: first only)", false).option("--regenerate-filing", "Re-run filing after the edit", false).option("--source <source>", "Source / lane stamp", "cli").action(async (memoryId, opts, cmd) => {
580
+ const cfg = resolveConfig(cmd.optsWithGlobals());
581
+ const data = await runApi("Editing memory text", async () => {
582
+ const client = await makeClient(cfg);
583
+ return client.POST("/memories/{memoryId}/edit-text", {
584
+ params: { path: { memoryId } },
585
+ body: {
586
+ memoryId,
587
+ oldText: opts.old,
588
+ newText: opts.new,
589
+ replaceAll: Boolean(opts.replaceAll),
590
+ regenerateFiling: Boolean(opts.regenerateFiling),
591
+ source: opts.source
592
+ }
593
+ });
594
+ });
595
+ emitAction(`edited ${style.bold(memoryId)} \u2192 v${style.bold(String(data.version))}`, data, cmd.optsWithGlobals().json);
596
+ });
597
+ memory.command("edit-text-batch <memoryId>").description("Apply many find/replace edits (POST /memories/{memoryId}/edit-text-batch)").requiredOption("--edit <old=>new...>", "Edit as 'old=>new' (repeatable)").option("--replace-all", "Apply replaceAll to every edit", false).option("--regenerate-filing", "Re-run filing after the edits", false).option("--source <source>", "Source / lane stamp", "cli").action(async (memoryId, opts, cmd) => {
598
+ const cfg = resolveConfig(cmd.optsWithGlobals());
599
+ const replaceAll = Boolean(opts.replaceAll);
600
+ const edits = opts.edit.map((spec) => {
601
+ const idx = spec.indexOf("=>");
602
+ if (idx < 0) {
603
+ process.stderr.write(`error: --edit must be 'old=>new', got: ${spec}
604
+ `);
605
+ process.exit(1);
606
+ }
607
+ return { oldText: spec.slice(0, idx), newText: spec.slice(idx + 2), replaceAll };
608
+ });
609
+ const data = await runApi("Applying batch edits", async () => {
610
+ const client = await makeClient(cfg);
611
+ return client.POST("/memories/{memoryId}/edit-text-batch", {
612
+ params: { path: { memoryId } },
613
+ body: {
614
+ memoryId,
615
+ edits,
616
+ regenerateFiling: Boolean(opts.regenerateFiling),
617
+ source: opts.source
618
+ }
619
+ });
620
+ });
621
+ emitAction(
622
+ `applied ${style.bold(String(data.editsApplied))} edit(s) \u2192 v${style.bold(String(data.version))}`,
623
+ data,
624
+ cmd.optsWithGlobals().json
625
+ );
626
+ });
627
+ memory.command("archive <memoryId>").description("Archive a memory (POST /memories/{memoryId}/archive)").option("--source <source>", "Source / lane stamp", "cli").action(async (memoryId, opts, cmd) => {
628
+ const cfg = resolveConfig(cmd.optsWithGlobals());
629
+ const data = await runApi("Archiving memory", async () => {
630
+ const client = await makeClient(cfg);
631
+ return client.POST("/memories/{memoryId}/archive", {
632
+ params: { path: { memoryId } },
633
+ body: { source: opts.source }
634
+ });
635
+ });
636
+ emitAction(`archived ${style.bold(memoryId)}`, data, cmd.optsWithGlobals().json);
637
+ });
638
+ memory.command("restore <memoryId>").description("Restore an archived memory (POST /memories/{memoryId}/restore)").option("--source <source>", "Source / lane stamp", "cli").action(async (memoryId, opts, cmd) => {
639
+ const cfg = resolveConfig(cmd.optsWithGlobals());
640
+ const data = await runApi("Restoring memory", async () => {
641
+ const client = await makeClient(cfg);
642
+ return client.POST("/memories/{memoryId}/restore", {
643
+ params: { path: { memoryId } },
644
+ body: { source: opts.source }
645
+ });
646
+ });
647
+ emitAction(`restored ${style.bold(memoryId)}`, data, cmd.optsWithGlobals().json);
648
+ });
649
+ memory.command("move <memoryId>").description("Move a memory to a new owner (POST /memories/{memoryId}/move)").requiredOption("--owner-type <ownerType>", "Unfiled | Workspace | Project | Candidate").option("--owner-id <ownerId>", "Owner id (required unless Unfiled)").option("--source <source>", "Source / lane stamp", "cli").action(async (memoryId, opts, cmd) => {
650
+ const cfg = resolveConfig(cmd.optsWithGlobals());
651
+ const data = await runApi("Moving memory", async () => {
652
+ const client = await makeClient(cfg);
653
+ return client.POST("/memories/{memoryId}/move", {
654
+ params: { path: { memoryId } },
655
+ body: {
656
+ to: {
657
+ type: opts.ownerType,
658
+ id: String(opts.ownerId ?? "")
659
+ },
660
+ source: opts.source
661
+ }
662
+ });
663
+ });
664
+ emitAction(`moved ${style.bold(memoryId)} \u2192 ${opts.ownerType}`, data, cmd.optsWithGlobals().json);
665
+ });
666
+ memory.command("list-archived").description("List archived memories (GET /memories/archived)").option("--workspace <workspaceId>", "Scope to a workspace").option("--project <projectId>", "Scope to a project").option("--page <n>", "Page number").option("--page-size <n>", "Page size").action(async (opts, cmd) => {
667
+ const cfg = resolveConfig(cmd.optsWithGlobals());
668
+ const data = await runApi("Listing archived memories", async () => {
669
+ const client = await makeClient(cfg);
670
+ return client.GET("/memories/archived", {
671
+ params: {
672
+ query: {
673
+ ...opts.workspace ? { workspaceId: opts.workspace } : {},
674
+ ...opts.project ? { projectId: opts.project } : {},
675
+ ...opts.page ? { page: Number(opts.page) } : {},
676
+ ...opts.pageSize ? { pageSize: Number(opts.pageSize) } : {}
677
+ }
678
+ }
679
+ });
680
+ });
681
+ emit(data, cmd.optsWithGlobals().json);
682
+ });
683
+ memory.command("versions <memoryId>").description("List a memory's versions (GET /memories/{memoryId}/versions)").action(async (memoryId, _opts, cmd) => {
684
+ const cfg = resolveConfig(cmd.optsWithGlobals());
685
+ const data = await runApi("Fetching versions", async () => {
686
+ const client = await makeClient(cfg);
687
+ return client.GET("/memories/{memoryId}/versions", { params: { path: { memoryId } } });
688
+ });
689
+ emit(data, cmd.optsWithGlobals().json);
690
+ });
691
+ memory.command("revert <memoryId>").description("Revert a memory to an earlier version (POST /memories/{memoryId}/revert)").requiredOption("--from-version <n>", "Version to revert from").requiredOption("--text <text>", "Reverted text (the target version's text)").requiredOption("--content <content>", "Reverted content JSON (the target version's content)").option("--source <source>", "Source / lane stamp", "cli").action(async (memoryId, opts, cmd) => {
692
+ const cfg = resolveConfig(cmd.optsWithGlobals());
693
+ const data = await runApi("Reverting memory", async () => {
694
+ const client = await makeClient(cfg);
695
+ return client.POST("/memories/{memoryId}/revert", {
696
+ params: { path: { memoryId } },
697
+ body: {
698
+ fromVersion: Number(opts.fromVersion),
699
+ revertedContent: opts.content,
700
+ revertedText: opts.text,
701
+ source: opts.source
702
+ }
703
+ });
704
+ });
705
+ emitAction(`reverted ${style.bold(memoryId)} from v${opts.fromVersion}`, data, cmd.optsWithGlobals().json);
706
+ });
707
+ memory.command("owners").description("List owners with memory counts (GET /memories/owners)").option("--include-archived", "Include archived memories in counts", false).action(async (opts, cmd) => {
708
+ const cfg = resolveConfig(cmd.optsWithGlobals());
709
+ const data = await runApi("Fetching owners", async () => {
710
+ const client = await makeClient(cfg);
711
+ return client.GET("/memories/owners", {
712
+ params: { query: { includeArchived: Boolean(opts.includeArchived) } }
713
+ });
714
+ });
715
+ emit(data, cmd.optsWithGlobals().json);
716
+ });
717
+ memory.command("tags").description("List tags with memory counts (GET /memories/tags)").option("--include-archived", "Include archived memories in counts", false).action(async (opts, cmd) => {
718
+ const cfg = resolveConfig(cmd.optsWithGlobals());
719
+ const data = await runApi("Fetching tags", async () => {
720
+ const client = await makeClient(cfg);
721
+ return client.GET("/memories/tags", {
722
+ params: { query: { includeArchived: Boolean(opts.includeArchived) } }
723
+ });
724
+ });
725
+ emit(data, cmd.optsWithGlobals().json);
726
+ });
727
+ memory.command("types").description("List types with memory counts (GET /memories/types)").option("--include-archived", "Include archived memories in counts", false).action(async (opts, cmd) => {
728
+ const cfg = resolveConfig(cmd.optsWithGlobals());
729
+ const data = await runApi("Fetching types", async () => {
730
+ const client = await makeClient(cfg);
731
+ return client.GET("/memories/types", {
732
+ params: { query: { includeArchived: Boolean(opts.includeArchived) } }
733
+ });
734
+ });
735
+ emit(data, cmd.optsWithGlobals().json);
736
+ });
737
+ memory.command("sum-tokens").description("Sum token counts across memories (POST /memories/sum-tokens)").option("--id <memoryId...>", "Memory id(s) to sum (repeatable)").option("--snapshot <snapshotId>", "Sum a continuity snapshot's load set").action(async (opts, cmd) => {
738
+ const cfg = resolveConfig(cmd.optsWithGlobals());
739
+ const data = await runApi("Summing tokens", async () => {
740
+ const client = await makeClient(cfg);
741
+ return client.POST("/memories/sum-tokens", {
742
+ body: {
743
+ ids: opts.id ?? null,
744
+ snapshotId: opts.snapshot ?? null
745
+ }
746
+ });
747
+ });
748
+ emit(data, cmd.optsWithGlobals().json);
749
+ });
750
+ memory.command("similar <memoryId>").description("Find similar memories (GET /memories/{memoryId}/similar)").option("--limit <n>", "Max results").option("--threshold <n>", "Minimum similarity threshold").action(async (memoryId, opts, cmd) => {
751
+ const cfg = resolveConfig(cmd.optsWithGlobals());
752
+ const data = await runApi("Finding similar memories", async () => {
753
+ const client = await makeClient(cfg);
754
+ return client.GET("/memories/{memoryId}/similar", {
755
+ params: {
756
+ path: { memoryId },
757
+ query: {
758
+ ...opts.limit ? { limit: Number(opts.limit) } : {},
759
+ ...opts.threshold ? { threshold: Number(opts.threshold) } : {}
760
+ }
761
+ }
762
+ });
763
+ });
764
+ emit(data, cmd.optsWithGlobals().json);
765
+ });
766
+ memory.command("by-url <url>").description("Resolve a memory by its canonical URL (GET /memories/by-url)").action(async (url, _opts, cmd) => {
767
+ const cfg = resolveConfig(cmd.optsWithGlobals());
768
+ const data = await runApi("Resolving memory by URL", async () => {
769
+ const client = await makeClient(cfg);
770
+ return client.GET("/memories/by-url", { params: { query: { url } } });
771
+ });
772
+ emit(data, cmd.optsWithGlobals().json);
773
+ });
566
774
  }
567
775
 
568
776
  // src/commands/worklog.ts
@@ -619,6 +827,927 @@ Examples:
619
827
  });
620
828
  }
621
829
 
830
+ // src/commands/relationships.ts
831
+ function registerRelationships(program2) {
832
+ const relationship = program2.command("relationship").description("Create, list, and manage memory relationships + suggestions");
833
+ relationship.addHelpText(
834
+ "after",
835
+ `
836
+ Examples:
837
+ $ sechroom relationship create mem_FROM mem_TO --type Reference
838
+ $ sechroom relationship list mem_XXXX --direction Outbound --json
839
+ $ sechroom relationship delete rel_XXXX
840
+ $ sechroom relationship suggest mem_XXXX --limit 5
841
+ $ sechroom relationship suggestions --status Pending --memory mem_XXXX
842
+ $ sechroom relationship suggestion accept rsg_XXXX`
843
+ );
844
+ relationship.command("create <fromMemoryId> <toMemoryId>").description("Create a relationship (POST /memories/{memoryId}/relationships)").option("--type <type>", "Relationship type (Reference, Related, Parent, Child, Follows, \u2026)", "Reference").action(async (fromMemoryId, toMemoryId, opts, cmd) => {
845
+ const cfg = resolveConfig(cmd.optsWithGlobals());
846
+ const data = await runApi("Creating relationship", async () => {
847
+ const client = await makeClient(cfg);
848
+ return client.POST("/memories/{memoryId}/relationships", {
849
+ params: { path: { memoryId: fromMemoryId } },
850
+ body: {
851
+ toMemoryId,
852
+ type: opts.type
853
+ }
854
+ });
855
+ });
856
+ const inversePart = data.inverseId ? ` ${style.dim(`(inverse ${data.inverseId})`)}` : "";
857
+ const view = resolveViewUrl(cfg.baseUrl, data.url);
858
+ const urlPart = view ? ` ${style.dim("\u2192")} ${view}` : "";
859
+ emitAction(
860
+ `created relationship ${style.bold(data.id)} ${style.dim(`${fromMemoryId} \u2192 ${toMemoryId}`)}${inversePart}${urlPart}`,
861
+ data,
862
+ cmd.optsWithGlobals().json
863
+ );
864
+ });
865
+ relationship.command("list <memoryId>").description("List a memory's relationships (GET /memories/{memoryId}/relationships)").option("--direction <direction>", "Both | Outbound | Inbound").option("--include-deleted", "Include deleted relationships", false).option("--page <n>", "Page number").option("--page-size <n>", "Page size").action(async (memoryId, opts, cmd) => {
866
+ const cfg = resolveConfig(cmd.optsWithGlobals());
867
+ const data = await runApi("Listing relationships", async () => {
868
+ const client = await makeClient(cfg);
869
+ return client.GET("/memories/{memoryId}/relationships", {
870
+ params: {
871
+ path: { memoryId },
872
+ query: {
873
+ ...opts.direction ? { direction: opts.direction } : {},
874
+ ...opts.includeDeleted ? { includeDeleted: true } : {},
875
+ ...opts.page ? { page: Number(opts.page) } : {},
876
+ ...opts.pageSize ? { pageSize: Number(opts.pageSize) } : {}
877
+ }
878
+ }
879
+ });
880
+ });
881
+ emit(data, cmd.optsWithGlobals().json);
882
+ });
883
+ relationship.command("delete <id>").description("Delete a relationship (DELETE /relationships/{id})").action(async (id, _opts, cmd) => {
884
+ const cfg = resolveConfig(cmd.optsWithGlobals());
885
+ const data = await runApi("Deleting relationship", async () => {
886
+ const client = await makeClient(cfg);
887
+ return client.DELETE("/relationships/{id}", {
888
+ params: { path: { id } },
889
+ body: {}
890
+ });
891
+ });
892
+ emitAction(`deleted relationship ${style.bold(id)}`, data, cmd.optsWithGlobals().json);
893
+ });
894
+ relationship.command("suggest <memoryId>").description("Generate relationship suggestions for a memory (POST /memories/{memoryId}/suggest-relationships)").option("--limit <n>", "Max suggestions to generate").action(async (memoryId, opts, cmd) => {
895
+ const cfg = resolveConfig(cmd.optsWithGlobals());
896
+ const data = await runApi("Suggesting relationships", async () => {
897
+ const client = await makeClient(cfg);
898
+ return client.POST("/memories/{memoryId}/suggest-relationships", {
899
+ params: { path: { memoryId } },
900
+ body: {
901
+ limit: opts.limit ? Number(opts.limit) : null
902
+ }
903
+ });
904
+ });
905
+ emitAction(
906
+ `suggested ${style.bold(String(data.suggested))} for ${memoryId} ${style.dim(`(considered ${data.considered}, suppressed ${data.suppressed})`)}`,
907
+ data,
908
+ cmd.optsWithGlobals().json
909
+ );
910
+ });
911
+ relationship.command("suggestions").description("List relationship suggestions (GET /relationship-suggestions)").option("--memory <memoryId>", "Filter to a memory").option(
912
+ "--status <status>",
913
+ "Pending | Accepted | EditedAndAccepted | Rejected | Superseded | Deferred | Invalidated"
914
+ ).option("--page <n>", "Page number").option("--page-size <n>", "Page size").action(async (opts, cmd) => {
915
+ const cfg = resolveConfig(cmd.optsWithGlobals());
916
+ const data = await runApi("Listing suggestions", async () => {
917
+ const client = await makeClient(cfg);
918
+ return client.GET("/relationship-suggestions", {
919
+ params: {
920
+ query: {
921
+ ...opts.memory ? { memoryId: opts.memory } : {},
922
+ ...opts.status ? {
923
+ status: opts.status
924
+ } : {},
925
+ ...opts.page ? { page: Number(opts.page) } : {},
926
+ ...opts.pageSize ? { pageSize: Number(opts.pageSize) } : {}
927
+ }
928
+ }
929
+ });
930
+ });
931
+ emit(data, cmd.optsWithGlobals().json);
932
+ });
933
+ const suggestion = relationship.command("suggestion").description("Inspect and decide on a single relationship suggestion");
934
+ suggestion.command("get <id>").description("Fetch a suggestion by id (GET /relationship-suggestions/{id})").action(async (id, _opts, cmd) => {
935
+ const cfg = resolveConfig(cmd.optsWithGlobals());
936
+ const data = await runApi("Fetching suggestion", async () => {
937
+ const client = await makeClient(cfg);
938
+ return client.GET("/relationship-suggestions/{id}", { params: { path: { id } } });
939
+ });
940
+ emit(data, cmd.optsWithGlobals().json);
941
+ });
942
+ suggestion.command("accept <id>").description("Accept a suggestion (POST /relationship-suggestions/{id}/accept)").action(async (id, _opts, cmd) => {
943
+ const cfg = resolveConfig(cmd.optsWithGlobals());
944
+ const data = await runApi("Accepting suggestion", async () => {
945
+ const client = await makeClient(cfg);
946
+ return client.POST("/relationship-suggestions/{id}/accept", {
947
+ params: { path: { id } },
948
+ body: {}
949
+ });
950
+ });
951
+ emitAction(`accepted suggestion ${style.bold(id)}`, data, cmd.optsWithGlobals().json);
952
+ });
953
+ suggestion.command("reject <id>").description("Reject a suggestion (POST /relationship-suggestions/{id}/reject)").option("--reason <reason>", "Why it's being rejected").option("--reason-code <code>", "Structured reason code").action(async (id, opts, cmd) => {
954
+ const cfg = resolveConfig(cmd.optsWithGlobals());
955
+ const data = await runApi("Rejecting suggestion", async () => {
956
+ const client = await makeClient(cfg);
957
+ return client.POST("/relationship-suggestions/{id}/reject", {
958
+ params: { path: { id } },
959
+ body: {
960
+ reason: opts.reason ?? null,
961
+ ...opts.reasonCode ? { reasonCode: opts.reasonCode } : {}
962
+ }
963
+ });
964
+ });
965
+ emitAction(`rejected suggestion ${style.bold(id)}`, data, cmd.optsWithGlobals().json);
966
+ });
967
+ suggestion.command("defer <id>").description("Defer a suggestion (POST /relationship-suggestions/{id}/defer)").option("--until <iso>", "Defer until this ISO date-time (omit to defer indefinitely)").action(async (id, opts, cmd) => {
968
+ const cfg = resolveConfig(cmd.optsWithGlobals());
969
+ const data = await runApi("Deferring suggestion", async () => {
970
+ const client = await makeClient(cfg);
971
+ return client.POST("/relationship-suggestions/{id}/defer", {
972
+ params: { path: { id } },
973
+ body: {
974
+ until: opts.until ?? null
975
+ }
976
+ });
977
+ });
978
+ emitAction(`deferred suggestion ${style.bold(id)}`, data, cmd.optsWithGlobals().json);
979
+ });
980
+ }
981
+
982
+ // src/commands/workspace.ts
983
+ function registerWorkspace(program2) {
984
+ const workspace = program2.command("workspace").description("Create, browse, and manage workspaces");
985
+ workspace.addHelpText(
986
+ "after",
987
+ `
988
+ Examples:
989
+ $ sechroom workspace create --name "My Workspace" --parent wsp_XXXX
990
+ $ sechroom workspace list --include-archived --json
991
+ $ sechroom workspace get wsp_XXXX --json
992
+ $ sechroom workspace rename wsp_XXXX --name "Renamed"
993
+ $ sechroom workspace move wsp_XXXX --parent wsp_YYYY
994
+ $ sechroom workspace feed wsp_XXXX --limit 20 --cascade`
995
+ );
996
+ 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) => {
997
+ const cfg = resolveConfig(cmd.optsWithGlobals());
998
+ const data = await runApi("Creating workspace", async () => {
999
+ const client = await makeClient(cfg);
1000
+ return client.POST("/workspaces", {
1001
+ body: {
1002
+ name: opts.name,
1003
+ description: opts.description ?? null,
1004
+ parentId: opts.parent ?? null
1005
+ }
1006
+ });
1007
+ });
1008
+ const view = resolveViewUrl(cfg.baseUrl, data.url);
1009
+ const urlPart = view ? ` ${style.dim("\u2192")} ${view}` : "";
1010
+ emitAction(
1011
+ `created workspace ${style.bold(data.id)} ${style.dim(`"${opts.name}"`)}${urlPart}`,
1012
+ data,
1013
+ cmd.optsWithGlobals().json
1014
+ );
1015
+ });
1016
+ workspace.command("list").description("List the caller's workspaces (GET /workspaces)").option("--include-archived", "Include archived workspaces", false).action(async (opts, cmd) => {
1017
+ const cfg = resolveConfig(cmd.optsWithGlobals());
1018
+ const data = await runApi("Listing workspaces", async () => {
1019
+ const client = await makeClient(cfg);
1020
+ return client.GET("/workspaces", {
1021
+ params: { query: { includeArchived: Boolean(opts.includeArchived) } }
1022
+ });
1023
+ });
1024
+ emit(data, cmd.optsWithGlobals().json);
1025
+ });
1026
+ workspace.command("get <workspaceId>").description("Fetch a workspace by id (GET /workspaces/{workspaceId})").action(async (workspaceId, _opts, cmd) => {
1027
+ const cfg = resolveConfig(cmd.optsWithGlobals());
1028
+ const data = await runApi("Fetching workspace", async () => {
1029
+ const client = await makeClient(cfg);
1030
+ return client.GET("/workspaces/{workspaceId}", { params: { path: { workspaceId } } });
1031
+ });
1032
+ emit(data, cmd.optsWithGlobals().json);
1033
+ });
1034
+ workspace.command("rename <workspaceId>").description("Rename a workspace (PUT /workspaces/{workspaceId}/rename)").requiredOption("--name <name>", "New workspace name").action(async (workspaceId, opts, cmd) => {
1035
+ const cfg = resolveConfig(cmd.optsWithGlobals());
1036
+ const data = await runApi("Renaming workspace", async () => {
1037
+ const client = await makeClient(cfg);
1038
+ return client.PUT("/workspaces/{workspaceId}/rename", {
1039
+ params: { path: { workspaceId } },
1040
+ body: { newName: opts.name }
1041
+ });
1042
+ });
1043
+ emitAction(
1044
+ `renamed workspace ${style.bold(workspaceId)} ${style.dim(`\u2192 "${opts.name}"`)}`,
1045
+ data,
1046
+ cmd.optsWithGlobals().json
1047
+ );
1048
+ });
1049
+ workspace.command("describe <workspaceId>").description("Set a workspace description (PUT /workspaces/{workspaceId}/description)").requiredOption("--description <description>", "New description").action(async (workspaceId, opts, cmd) => {
1050
+ const cfg = resolveConfig(cmd.optsWithGlobals());
1051
+ const data = await runApi("Updating description", async () => {
1052
+ const client = await makeClient(cfg);
1053
+ return client.PUT("/workspaces/{workspaceId}/description", {
1054
+ params: { path: { workspaceId } },
1055
+ body: { newDescription: opts.description }
1056
+ });
1057
+ });
1058
+ emitAction(
1059
+ `updated description for workspace ${style.bold(workspaceId)}`,
1060
+ data,
1061
+ cmd.optsWithGlobals().json
1062
+ );
1063
+ });
1064
+ workspace.command("move <workspaceId>").description("Move a workspace under a new parent (POST /workspaces/{workspaceId}/move)").option("--parent <parentId>", "New parent workspace id (omit to move to top level)").action(async (workspaceId, opts, cmd) => {
1065
+ const cfg = resolveConfig(cmd.optsWithGlobals());
1066
+ const data = await runApi("Moving workspace", async () => {
1067
+ const client = await makeClient(cfg);
1068
+ return client.POST("/workspaces/{workspaceId}/move", {
1069
+ params: { path: { workspaceId } },
1070
+ body: { newParentId: opts.parent ?? null }
1071
+ });
1072
+ });
1073
+ const target = opts.parent ? `under ${style.bold(opts.parent)}` : "to top level";
1074
+ emitAction(
1075
+ `moved workspace ${style.bold(workspaceId)} ${style.dim(target)}`,
1076
+ data,
1077
+ cmd.optsWithGlobals().json
1078
+ );
1079
+ });
1080
+ workspace.command("archive <workspaceId>").description("Archive a workspace (POST /workspaces/{workspaceId}/archive)").action(async (workspaceId, _opts, cmd) => {
1081
+ const cfg = resolveConfig(cmd.optsWithGlobals());
1082
+ const data = await runApi("Archiving workspace", async () => {
1083
+ const client = await makeClient(cfg);
1084
+ return client.POST("/workspaces/{workspaceId}/archive", {
1085
+ params: { path: { workspaceId } },
1086
+ body: {}
1087
+ });
1088
+ });
1089
+ emitAction(`archived workspace ${style.bold(workspaceId)}`, data, cmd.optsWithGlobals().json);
1090
+ });
1091
+ workspace.command("restore <workspaceId>").description("Restore an archived workspace (POST /workspaces/{workspaceId}/restore)").action(async (workspaceId, _opts, cmd) => {
1092
+ const cfg = resolveConfig(cmd.optsWithGlobals());
1093
+ const data = await runApi("Restoring workspace", async () => {
1094
+ const client = await makeClient(cfg);
1095
+ return client.POST("/workspaces/{workspaceId}/restore", {
1096
+ params: { path: { workspaceId } },
1097
+ body: {}
1098
+ });
1099
+ });
1100
+ emitAction(`restored workspace ${style.bold(workspaceId)}`, data, cmd.optsWithGlobals().json);
1101
+ });
1102
+ 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) => {
1103
+ const cfg = resolveConfig(cmd.optsWithGlobals());
1104
+ const data = await runApi("Fetching feed", async () => {
1105
+ const client = await makeClient(cfg);
1106
+ return client.GET("/workspaces/{workspaceId}/memories/feed", {
1107
+ params: {
1108
+ path: { workspaceId },
1109
+ query: {
1110
+ limit: Number(opts.limit),
1111
+ cascadeWorkspaces: Boolean(opts.cascade),
1112
+ includeProjects: Boolean(opts.includeProjects),
1113
+ includeArchived: Boolean(opts.includeArchived),
1114
+ ...opts.cursor ? { cursor: opts.cursor } : {},
1115
+ ...opts.query ? { query: opts.query } : {},
1116
+ ...opts.tag ? { filterTags: opts.tag } : {}
1117
+ }
1118
+ }
1119
+ });
1120
+ });
1121
+ emit(data, cmd.optsWithGlobals().json);
1122
+ });
1123
+ }
1124
+
1125
+ // src/commands/project.ts
1126
+ function registerProject(program2) {
1127
+ const project = program2.command("project").description("Create, browse, and manage projects");
1128
+ project.addHelpText(
1129
+ "after",
1130
+ `
1131
+ Examples:
1132
+ $ sechroom project create --workspace wsp_XXXX --name "Onboarding" --slug onboarding
1133
+ $ sechroom project list --workspace wsp_XXXX --status Active
1134
+ $ sechroom project get prj_XXXX --json
1135
+ $ sechroom project rename prj_XXXX --name "New Name" --slug new-name
1136
+ $ sechroom project status prj_XXXX --status Completed`
1137
+ );
1138
+ project.command("create").description("Create a project (POST /projects)").requiredOption("--workspace <workspaceId>", "Owning workspace id").requiredOption("--name <name>", "Project name").requiredOption("--slug <slug>", "URL slug").option("--description <description>", "Optional description").option("--parent <parentProjectId>", "Optional parent project id").action(async (opts, cmd) => {
1139
+ const cfg = resolveConfig(cmd.optsWithGlobals());
1140
+ const data = await runApi("Creating project", async () => {
1141
+ const client = await makeClient(cfg);
1142
+ return client.POST("/projects", {
1143
+ body: {
1144
+ workspaceId: opts.workspace,
1145
+ name: opts.name,
1146
+ slug: opts.slug,
1147
+ description: opts.description ?? null,
1148
+ parentProjectId: opts.parent ?? null
1149
+ }
1150
+ });
1151
+ });
1152
+ const view = resolveViewUrl(cfg.baseUrl, data.url);
1153
+ const urlPart = view ? ` ${style.dim("\u2192")} ${view}` : "";
1154
+ emitAction(`created project ${style.bold(data.id)}${urlPart}`, data, cmd.optsWithGlobals().json);
1155
+ });
1156
+ project.command("list").description("List projects (GET /projects)").option("--workspace <workspaceId>", "Scope to a workspace").option("--status <status>", "Draft | Active | OnHold | Completed | Cancelled").option("--include-archived", "Include archived projects", false).action(async (opts, cmd) => {
1157
+ const cfg = resolveConfig(cmd.optsWithGlobals());
1158
+ const data = await runApi("Listing projects", async () => {
1159
+ const client = await makeClient(cfg);
1160
+ return client.GET("/projects", {
1161
+ params: {
1162
+ query: {
1163
+ ...opts.workspace ? { workspaceId: opts.workspace } : {},
1164
+ ...opts.status ? { status: opts.status } : {},
1165
+ ...opts.includeArchived ? { includeArchived: true } : {}
1166
+ }
1167
+ }
1168
+ });
1169
+ });
1170
+ emit(data, cmd.optsWithGlobals().json);
1171
+ });
1172
+ project.command("get <projectId>").description("Fetch a project by id (GET /projects/{projectId})").action(async (projectId, _opts, cmd) => {
1173
+ const cfg = resolveConfig(cmd.optsWithGlobals());
1174
+ const data = await runApi("Fetching project", async () => {
1175
+ const client = await makeClient(cfg);
1176
+ return client.GET("/projects/{projectId}", { params: { path: { projectId } } });
1177
+ });
1178
+ emit(data, cmd.optsWithGlobals().json);
1179
+ });
1180
+ project.command("rename <projectId>").description("Rename a project (PUT /projects/{projectId}/rename)").requiredOption("--name <name>", "New project name").requiredOption("--slug <slug>", "New URL slug").action(async (projectId, opts, cmd) => {
1181
+ const cfg = resolveConfig(cmd.optsWithGlobals());
1182
+ const data = await runApi("Renaming project", async () => {
1183
+ const client = await makeClient(cfg);
1184
+ return client.PUT("/projects/{projectId}/rename", {
1185
+ params: { path: { projectId } },
1186
+ body: { newName: opts.name, newSlug: opts.slug }
1187
+ });
1188
+ });
1189
+ emitAction(`renamed project ${style.bold(projectId)} \u2192 ${style.bold(opts.name)}`, data, cmd.optsWithGlobals().json);
1190
+ });
1191
+ project.command("describe <projectId>").description("Set a project's description (PUT /projects/{projectId}/description)").requiredOption("--description <description>", "New description (empty string to clear)").action(async (projectId, opts, cmd) => {
1192
+ const cfg = resolveConfig(cmd.optsWithGlobals());
1193
+ const data = await runApi("Updating description", async () => {
1194
+ const client = await makeClient(cfg);
1195
+ return client.PUT("/projects/{projectId}/description", {
1196
+ params: { path: { projectId } },
1197
+ body: { newDescription: opts.description }
1198
+ });
1199
+ });
1200
+ emitAction(`updated description on project ${style.bold(projectId)}`, data, cmd.optsWithGlobals().json);
1201
+ });
1202
+ project.command("move <projectId>").description("Move a project to another workspace/parent (POST /projects/{projectId}/move)").requiredOption("--workspace <toWorkspaceId>", "Destination workspace id").option("--parent <toParentId>", "Destination parent project id").action(async (projectId, opts, cmd) => {
1203
+ const cfg = resolveConfig(cmd.optsWithGlobals());
1204
+ const data = await runApi("Moving project", async () => {
1205
+ const client = await makeClient(cfg);
1206
+ return client.POST("/projects/{projectId}/move", {
1207
+ params: { path: { projectId } },
1208
+ body: { toWorkspaceId: opts.workspace, toParentId: opts.parent ?? null }
1209
+ });
1210
+ });
1211
+ emitAction(`moved project ${style.bold(projectId)} \u2192 ${style.bold(opts.workspace)}`, data, cmd.optsWithGlobals().json);
1212
+ });
1213
+ project.command("status <projectId>").description("Set a project's status (POST /projects/{projectId}/status)").requiredOption("--status <status>", "Draft | Active | OnHold | Completed | Cancelled").action(async (projectId, opts, cmd) => {
1214
+ const cfg = resolveConfig(cmd.optsWithGlobals());
1215
+ const data = await runApi("Setting status", async () => {
1216
+ const client = await makeClient(cfg);
1217
+ return client.POST("/projects/{projectId}/status", {
1218
+ params: { path: { projectId } },
1219
+ body: {
1220
+ status: opts.status
1221
+ }
1222
+ });
1223
+ });
1224
+ emitAction(`set project ${style.bold(projectId)} status \u2192 ${style.bold(opts.status)}`, data, cmd.optsWithGlobals().json);
1225
+ });
1226
+ project.command("victory-conditions <projectId>").description("Set a project's victory conditions (PUT /projects/{projectId}/victory-conditions)").requiredOption("--conditions <conditions>", "Victory conditions text (empty string to clear)").action(async (projectId, opts, cmd) => {
1227
+ const cfg = resolveConfig(cmd.optsWithGlobals());
1228
+ const data = await runApi("Updating victory conditions", async () => {
1229
+ const client = await makeClient(cfg);
1230
+ return client.PUT("/projects/{projectId}/victory-conditions", {
1231
+ params: { path: { projectId } },
1232
+ body: { victoryConditions: opts.conditions }
1233
+ });
1234
+ });
1235
+ emitAction(`updated victory conditions on project ${style.bold(projectId)}`, data, cmd.optsWithGlobals().json);
1236
+ });
1237
+ project.command("archive <projectId>").description("Archive a project (POST /projects/{projectId}/archive)").action(async (projectId, _opts, cmd) => {
1238
+ const cfg = resolveConfig(cmd.optsWithGlobals());
1239
+ const data = await runApi("Archiving project", async () => {
1240
+ const client = await makeClient(cfg);
1241
+ return client.POST("/projects/{projectId}/archive", {
1242
+ params: { path: { projectId } },
1243
+ body: {}
1244
+ });
1245
+ });
1246
+ emitAction(`archived project ${style.bold(projectId)}`, data, cmd.optsWithGlobals().json);
1247
+ });
1248
+ project.command("restore <projectId>").description("Restore an archived project (POST /projects/{projectId}/restore)").action(async (projectId, _opts, cmd) => {
1249
+ const cfg = resolveConfig(cmd.optsWithGlobals());
1250
+ const data = await runApi("Restoring project", async () => {
1251
+ const client = await makeClient(cfg);
1252
+ return client.POST("/projects/{projectId}/restore", {
1253
+ params: { path: { projectId } },
1254
+ body: {}
1255
+ });
1256
+ });
1257
+ emitAction(`restored project ${style.bold(projectId)}`, data, cmd.optsWithGlobals().json);
1258
+ });
1259
+ }
1260
+
1261
+ // src/commands/filing.ts
1262
+ function registerFiling(program2) {
1263
+ const filing = program2.command("filing").description("Review and act on filing suggestions");
1264
+ filing.addHelpText(
1265
+ "after",
1266
+ `
1267
+ Examples:
1268
+ $ sechroom filing suggestions --status Pending --page-size 20
1269
+ $ sechroom filing get fsg_XXXX --json
1270
+ $ sechroom filing preview --memory-id mem_XXXX
1271
+ $ sechroom filing accept fsg_XXXX
1272
+ $ sechroom filing reject fsg_XXXX --reason "wrong workspace"
1273
+ $ sechroom filing edit-and-accept fsg_XXXX --target-kind Workspace --existing-target-id wsp_XXXX`
1274
+ );
1275
+ filing.command("suggestions").description("List filing suggestions (GET /filing/suggestions)").option("--memory-id <memoryId>", "Filter to a single memory's suggestions").option(
1276
+ "--status <status>",
1277
+ "Generating | Pending | Accepted | Rejected | EditedAndAccepted | Deferred | Invalidated"
1278
+ ).option("--page <n>", "Page number").option("--page-size <n>", "Page size").action(async (opts, cmd) => {
1279
+ const cfg = resolveConfig(cmd.optsWithGlobals());
1280
+ const data = await runApi("Listing filing suggestions", async () => {
1281
+ const client = await makeClient(cfg);
1282
+ return client.GET("/filing/suggestions", {
1283
+ params: {
1284
+ query: {
1285
+ ...opts.memoryId ? { memoryId: opts.memoryId } : {},
1286
+ ...opts.status ? { status: opts.status } : {},
1287
+ ...opts.page ? { page: Number(opts.page) } : {},
1288
+ ...opts.pageSize ? { pageSize: Number(opts.pageSize) } : {}
1289
+ }
1290
+ }
1291
+ });
1292
+ });
1293
+ emit(data, cmd.optsWithGlobals().json);
1294
+ });
1295
+ filing.command("get <id>").description("Fetch a filing suggestion by id (GET /filing/suggestions/{id})").action(async (id, _opts, cmd) => {
1296
+ const cfg = resolveConfig(cmd.optsWithGlobals());
1297
+ const data = await runApi("Fetching filing suggestion", async () => {
1298
+ const client = await makeClient(cfg);
1299
+ return client.GET("/filing/suggestions/{id}", { params: { path: { id } } });
1300
+ });
1301
+ emit(data, cmd.optsWithGlobals().json);
1302
+ });
1303
+ filing.command("preview").description("Preview a filing suggestion for a memory id or ad-hoc shape (POST /filing/suggestions/preview)").option("--memory-id <memoryId>", "Preview filing for an existing memory").option("--text <text>", "Ad-hoc memory body text (instead of --memory-id)").option("--title <title>", "Ad-hoc memory title").option("--tag <tag...>", "Ad-hoc memory tags (repeatable)").option("--type <type>", "Ad-hoc memory type", "reference").option("--scope-workspace <workspaceId>", "Scope the preview to a workspace").action(async (opts, cmd) => {
1304
+ const cfg = resolveConfig(cmd.optsWithGlobals());
1305
+ const memory = opts.text ? {
1306
+ text: opts.text,
1307
+ title: opts.title ?? null,
1308
+ tags: opts.tag ?? null,
1309
+ type: opts.type
1310
+ } : null;
1311
+ const data = await runApi("Previewing filing suggestion", async () => {
1312
+ const client = await makeClient(cfg);
1313
+ return client.POST("/filing/suggestions/preview", {
1314
+ body: {
1315
+ memoryId: opts.memoryId ?? null,
1316
+ memory,
1317
+ scopeWorkspaceId: opts.scopeWorkspace ?? null
1318
+ }
1319
+ });
1320
+ });
1321
+ emit(data, cmd.optsWithGlobals().json);
1322
+ });
1323
+ filing.command("accept <id>").description("Accept a filing suggestion (POST /filing/suggestions/{id}/accept)").action(async (id, _opts, cmd) => {
1324
+ const cfg = resolveConfig(cmd.optsWithGlobals());
1325
+ const data = await runApi("Accepting filing suggestion", async () => {
1326
+ const client = await makeClient(cfg);
1327
+ return client.POST("/filing/suggestions/{id}/accept", { params: { path: { id } }, body: {} });
1328
+ });
1329
+ emitAction(`accepted filing suggestion ${style.bold(id)}`, data, cmd.optsWithGlobals().json);
1330
+ });
1331
+ filing.command("reject <id>").description("Reject a filing suggestion (POST /filing/suggestions/{id}/reject)").option("--reason <reason>", "Why the suggestion was rejected").option("--reason-code <code>", "Structured reason code").action(async (id, opts, cmd) => {
1332
+ const cfg = resolveConfig(cmd.optsWithGlobals());
1333
+ const data = await runApi("Rejecting filing suggestion", async () => {
1334
+ const client = await makeClient(cfg);
1335
+ return client.POST("/filing/suggestions/{id}/reject", {
1336
+ params: { path: { id } },
1337
+ body: {
1338
+ reason: opts.reason ?? null,
1339
+ ...opts.reasonCode ? { reasonCode: opts.reasonCode } : {}
1340
+ }
1341
+ });
1342
+ });
1343
+ emitAction(`rejected filing suggestion ${style.bold(id)}`, data, cmd.optsWithGlobals().json);
1344
+ });
1345
+ filing.command("defer <id>").description("Defer a filing suggestion (POST /filing/suggestions/{id}/defer)").option("--until <iso>", "Defer until an ISO-8601 timestamp (defaults to indefinite)").action(async (id, opts, cmd) => {
1346
+ const cfg = resolveConfig(cmd.optsWithGlobals());
1347
+ const data = await runApi("Deferring filing suggestion", async () => {
1348
+ const client = await makeClient(cfg);
1349
+ return client.POST("/filing/suggestions/{id}/defer", {
1350
+ params: { path: { id } },
1351
+ body: { until: opts.until ?? null }
1352
+ });
1353
+ });
1354
+ emitAction(`deferred filing suggestion ${style.bold(id)}`, data, cmd.optsWithGlobals().json);
1355
+ });
1356
+ filing.command("edit-and-accept <id>").description("Override the target then accept (POST /filing/suggestions/{id}/edit-and-accept)").option("--target-kind <kind>", "Workspace | Project").option("--existing-target-id <id>", "File into an existing workspace/project").option("--new-name <name>", "Create a new target with this name").option("--new-description <text>", "Description for the new target").option("--new-parent-workspace <workspaceId>", "Parent workspace for a new project").option("--memory-id <memoryId...>", "Override the memory set (repeatable)").action(async (id, opts, cmd) => {
1357
+ const cfg = resolveConfig(cmd.optsWithGlobals());
1358
+ const data = await runApi("Editing and accepting filing suggestion", async () => {
1359
+ const client = await makeClient(cfg);
1360
+ return client.POST("/filing/suggestions/{id}/edit-and-accept", {
1361
+ params: { path: { id } },
1362
+ body: {
1363
+ targetKind: opts.targetKind ?? null,
1364
+ existingTargetId: opts.existingTargetId ?? null,
1365
+ newName: opts.newName ?? null,
1366
+ newDescription: opts.newDescription ?? null,
1367
+ newParentWorkspaceId: opts.newParentWorkspace ?? null,
1368
+ overrideMemoryIds: opts.memoryId ?? null
1369
+ }
1370
+ });
1371
+ });
1372
+ emitAction(`edited & accepted filing suggestion ${style.bold(id)}`, data, cmd.optsWithGlobals().json);
1373
+ });
1374
+ }
1375
+
1376
+ // src/commands/continuity.ts
1377
+ function registerContinuity(program2) {
1378
+ const continuity = program2.command("continuity").description("Continuity snapshots: checkpoint and resume work");
1379
+ continuity.addHelpText(
1380
+ "after",
1381
+ `
1382
+ Examples:
1383
+ $ sechroom continuity snapshot-create --lane claude-code-chris --scope loop-spec \\
1384
+ --objective "ship slice 9" --state "tests green, PR open" \\
1385
+ --last-action "raised PR #1425" --next-action "watch for merge" \\
1386
+ --resume-instruction "load csn id, resume at step 10"
1387
+ $ sechroom continuity snapshots --scope loop-spec
1388
+ $ sechroom continuity snapshot-get csn_XXXX --json
1389
+ $ sechroom continuity resume-me --max-artifacts 20
1390
+ $ sechroom continuity changed-since --since 2026-06-01T00:00:00Z
1391
+ $ sechroom continuity grant csn_XXXX --grantee usr_XXXX`
1392
+ );
1393
+ continuity.command("snapshot-create").description("Create a continuity snapshot (POST /continuity/snapshots)").requiredOption("--lane <laneId>", "Lane id (e.g. claude-code-chris)").requiredOption("--scope <scope>", "Snapshot scope (e.g. loop-spec)").requiredOption("--objective <text>", "Current objective").requiredOption("--state <text>", "Current state").requiredOption("--last-action <text>", "Last meaningful action").requiredOption("--next-action <text>", "Next intended action").requiredOption("--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").action(async (opts, cmd) => {
1394
+ const cfg = resolveConfig(cmd.optsWithGlobals());
1395
+ const data = await runApi("Creating snapshot", async () => {
1396
+ const client = await makeClient(cfg);
1397
+ return client.POST("/continuity/snapshots", {
1398
+ body: {
1399
+ laneId: opts.lane,
1400
+ scope: opts.scope,
1401
+ currentObjective: opts.objective,
1402
+ currentState: opts.state,
1403
+ lastMeaningfulAction: opts.lastAction,
1404
+ nextIntendedAction: opts.nextAction,
1405
+ resumeInstruction: opts.resumeInstruction,
1406
+ activeConstraints: opts.constraint ?? null,
1407
+ openQuestions: opts.question ?? null,
1408
+ surfaceMarkers: opts.surfaceMarker ?? null,
1409
+ relevantArtifactIds: opts.artifact ?? null,
1410
+ confidence: opts.confidence != null ? Number(opts.confidence) : null
1411
+ }
1412
+ });
1413
+ });
1414
+ emitAction(`created snapshot ${style.bold(data.snapshotId)}`, data, cmd.optsWithGlobals().json);
1415
+ });
1416
+ continuity.command("snapshot-get <id>").description("Fetch a snapshot by id (GET /continuity/snapshots/{id})").action(async (id, _opts, cmd) => {
1417
+ const cfg = resolveConfig(cmd.optsWithGlobals());
1418
+ const data = await runApi("Fetching snapshot", async () => {
1419
+ const client = await makeClient(cfg);
1420
+ return client.GET("/continuity/snapshots/{id}", { params: { path: { id } } });
1421
+ });
1422
+ emit(data, cmd.optsWithGlobals().json);
1423
+ });
1424
+ continuity.command("snapshots").description("List the caller's own snapshots (GET /me/continuity/snapshots)").option("--scope <scope>", "Filter by scope").option("--lane <laneId>", "Filter by lane id").action(async (opts, cmd) => {
1425
+ const cfg = resolveConfig(cmd.optsWithGlobals());
1426
+ const data = await runApi("Listing snapshots", async () => {
1427
+ const client = await makeClient(cfg);
1428
+ return client.GET("/me/continuity/snapshots", {
1429
+ params: {
1430
+ query: {
1431
+ ...opts.scope ? { scope: opts.scope } : {},
1432
+ ...opts.lane ? { laneId: opts.lane } : {}
1433
+ }
1434
+ }
1435
+ });
1436
+ });
1437
+ emit(data, cmd.optsWithGlobals().json);
1438
+ });
1439
+ continuity.command("resume-me").description("Resume the caller's own lane (POST /continuity/resume/me)").option("--workspace <workspaceId>", "Scope to a workspace").option("--max-artifacts <n>", "Cap artifacts in the bundle").option("--changed-since <iso>", "Only include changes since this ISO timestamp").option("--looking-at-myself", "Include the caller's own changes", false).action(async (opts, cmd) => {
1440
+ const cfg = resolveConfig(cmd.optsWithGlobals());
1441
+ const data = await runApi("Resuming", async () => {
1442
+ const client = await makeClient(cfg);
1443
+ return client.POST("/continuity/resume/me", {
1444
+ body: {
1445
+ workspaceId: opts.workspace ?? null,
1446
+ maxArtifacts: opts.maxArtifacts != null ? Number(opts.maxArtifacts) : null,
1447
+ includeLookingAtMyself: opts.lookingAtMyself ? true : null,
1448
+ changedSince: opts.changedSince ?? null
1449
+ }
1450
+ });
1451
+ });
1452
+ emit(data, cmd.optsWithGlobals().json);
1453
+ });
1454
+ continuity.command("resume-lane <laneId>").description("Resume a specific lane (POST /continuity/resume/lane)").option("--workspace <workspaceId>", "Scope to a workspace").option("--max-artifacts <n>", "Cap artifacts in the bundle").option("--changed-since <iso>", "Only include changes since this ISO timestamp").option("--looking-at-myself", "Include the caller's own changes", false).action(async (laneId, opts, cmd) => {
1455
+ const cfg = resolveConfig(cmd.optsWithGlobals());
1456
+ const data = await runApi("Resuming lane", async () => {
1457
+ const client = await makeClient(cfg);
1458
+ return client.POST("/continuity/resume/lane", {
1459
+ body: {
1460
+ laneId,
1461
+ workspaceId: opts.workspace ?? null,
1462
+ maxArtifacts: opts.maxArtifacts != null ? Number(opts.maxArtifacts) : null,
1463
+ includeLookingAtMyself: opts.lookingAtMyself ? true : null,
1464
+ changedSince: opts.changedSince ?? null
1465
+ }
1466
+ });
1467
+ });
1468
+ emit(data, cmd.optsWithGlobals().json);
1469
+ });
1470
+ continuity.command("changed-since").description("What changed since a timestamp (POST /continuity/changed-since)").requiredOption("--since <iso>", "ISO-8601 timestamp to compare against").option("--looking-at-myself", "Include the caller's own changes", false).action(async (opts, cmd) => {
1471
+ const cfg = resolveConfig(cmd.optsWithGlobals());
1472
+ const data = await runApi("Computing changes", async () => {
1473
+ const client = await makeClient(cfg);
1474
+ return client.POST("/continuity/changed-since", {
1475
+ body: {
1476
+ since: opts.since,
1477
+ includeLookingAtMyself: opts.lookingAtMyself ? true : null
1478
+ }
1479
+ });
1480
+ });
1481
+ emit(data, cmd.optsWithGlobals().json);
1482
+ });
1483
+ continuity.command("load-set").description("Derive the active load set (POST /continuity/load-set/derive)").option("--workspace <workspaceId>", "Scope to a workspace").option("--max-artifacts <n>", "Cap artifacts in the load set").option("--looking-at-myself", "Include the caller's own changes", false).action(async (opts, cmd) => {
1484
+ const cfg = resolveConfig(cmd.optsWithGlobals());
1485
+ const data = await runApi("Deriving load set", async () => {
1486
+ const client = await makeClient(cfg);
1487
+ return client.POST("/continuity/load-set/derive", {
1488
+ body: {
1489
+ workspaceId: opts.workspace ?? null,
1490
+ maxArtifacts: opts.maxArtifacts != null ? Number(opts.maxArtifacts) : null,
1491
+ includeLookingAtMyself: opts.lookingAtMyself ? true : null
1492
+ }
1493
+ });
1494
+ });
1495
+ emit(data, cmd.optsWithGlobals().json);
1496
+ });
1497
+ continuity.command("grant <snapshotId>").description("Grant another operator read access (POST /continuity/snapshots/{snapshotId}/grants)").requiredOption("--grantee <userId>", "Sechroom user id being granted read access").option("--source <source>", "Permission-set source kind", "TenantRole").option("--source-id <sourceId>", "Permission-set source id (e.g. a tenant role)", "viewer").option("--valid-from <iso>", "Optional ISO-8601 grant start").option("--valid-to <iso>", "Optional ISO-8601 grant expiry").action(async (snapshotId, opts, cmd) => {
1498
+ const cfg = resolveConfig(cmd.optsWithGlobals());
1499
+ const data = await runApi("Minting grant", async () => {
1500
+ const client = await makeClient(cfg);
1501
+ return client.POST("/continuity/snapshots/{snapshotId}/grants", {
1502
+ params: { path: { snapshotId } },
1503
+ body: {
1504
+ userId: opts.grantee,
1505
+ kind: "Allow",
1506
+ source: opts.source,
1507
+ sourceId: opts.sourceId,
1508
+ ...opts.validFrom ? { validFrom: opts.validFrom } : {},
1509
+ ...opts.validTo ? { validTo: opts.validTo } : {}
1510
+ }
1511
+ });
1512
+ });
1513
+ emitAction(
1514
+ `granted ${style.bold(data.userId)} read on ${style.bold(snapshotId)} ${style.dim(`(grant ${data.grantId})`)}`,
1515
+ data,
1516
+ cmd.optsWithGlobals().json
1517
+ );
1518
+ });
1519
+ continuity.command("revoke-grant <snapshotId> <grantId>").description("Revoke a grant (DELETE /continuity/snapshots/{snapshotId}/grants/{grantId})").action(async (snapshotId, grantId, _opts, cmd) => {
1520
+ const cfg = resolveConfig(cmd.optsWithGlobals());
1521
+ const data = await runApi("Revoking grant", async () => {
1522
+ const client = await makeClient(cfg);
1523
+ return client.DELETE("/continuity/snapshots/{snapshotId}/grants/{grantId}", {
1524
+ params: { path: { snapshotId, grantId } },
1525
+ body: {}
1526
+ });
1527
+ });
1528
+ emitAction(
1529
+ `revoked grant ${style.bold(grantId)} on ${style.bold(snapshotId)}`,
1530
+ data,
1531
+ cmd.optsWithGlobals().json
1532
+ );
1533
+ });
1534
+ }
1535
+
1536
+ // src/commands/account.ts
1537
+ function registerId(program2) {
1538
+ const id = program2.command("id").description("Allocate human-authored id sequences (FR-*, D-*)");
1539
+ id.addHelpText(
1540
+ "after",
1541
+ `
1542
+ Examples:
1543
+ $ sechroom id next FR sechroom allocate the next FR-sechroom-NNN id
1544
+ $ sechroom id next D Backend allocate the next D-Backend-NNN id
1545
+ $ sechroom id peek FR sechroom inspect the sequence without consuming
1546
+ $ sechroom id peek FR sechroom --json`
1547
+ );
1548
+ id.command("next <namespaceKind> <scope>").description("Allocate the next id in a sequence (POST /id-registry/allocate)").action(async (namespaceKind, scope, _opts, cmd) => {
1549
+ const cfg = resolveConfig(cmd.optsWithGlobals());
1550
+ const data = await runApi("Allocating id", async () => {
1551
+ const client = await makeClient(cfg);
1552
+ return client.POST("/id-registry/allocate", {
1553
+ body: { namespaceKind, scope, clientNonce: null }
1554
+ });
1555
+ });
1556
+ emitAction(`allocated ${style.bold(data.id)} ${style.dim(`(seq ${data.seq})`)}`, data, cmd.optsWithGlobals().json);
1557
+ });
1558
+ id.command("peek <namespaceKind> <scope>").description("Inspect a sequence without consuming (GET /id-registry/state)").action(async (namespaceKind, scope, _opts, cmd) => {
1559
+ const cfg = resolveConfig(cmd.optsWithGlobals());
1560
+ const data = await runApi("Peeking id sequence", async () => {
1561
+ const client = await makeClient(cfg);
1562
+ return client.GET("/id-registry/state", {
1563
+ params: { query: { namespaceKind, scope } }
1564
+ });
1565
+ });
1566
+ emit(data, cmd.optsWithGlobals().json);
1567
+ });
1568
+ }
1569
+ function registerAccount(program2) {
1570
+ const account = program2.command("account").description("Your profile, feeds, and review queue");
1571
+ account.addHelpText(
1572
+ "after",
1573
+ `
1574
+ Examples:
1575
+ $ sechroom account profile
1576
+ $ sechroom account set-profile --display-name "Chris" --timezone "Europe/London"
1577
+ $ sechroom account feed --limit 20
1578
+ $ sechroom account reviews --status Pending
1579
+ $ sechroom account lookup-batch mem_XXXX wsp_YYYY --json`
1580
+ );
1581
+ account.command("profile").description("Show your resolved profile (GET /me/profile)").action(async (_opts, cmd) => {
1582
+ const cfg = resolveConfig(cmd.optsWithGlobals());
1583
+ const data = await runApi("Fetching profile", async () => {
1584
+ const client = await makeClient(cfg);
1585
+ return client.GET("/me/profile");
1586
+ });
1587
+ emit(data, cmd.optsWithGlobals().json);
1588
+ });
1589
+ account.command("set-profile").description("Update your profile (PUT /me/profile)").option("--display-name <displayName>", "Display name").option("--timezone <timezone>", "IANA timezone (e.g. Europe/London)").option("--bio <bio>", "Short bio").option("--photo-url <photoUrl>", "Avatar / photo URL").action(async (opts, cmd) => {
1590
+ const cfg = resolveConfig(cmd.optsWithGlobals());
1591
+ const data = await runApi("Updating profile", async () => {
1592
+ const client = await makeClient(cfg);
1593
+ return client.PUT("/me/profile", {
1594
+ body: {
1595
+ displayName: opts.displayName ?? null,
1596
+ timezone: opts.timezone ?? null,
1597
+ bio: opts.bio ?? null,
1598
+ photoUrl: opts.photoUrl ?? null
1599
+ }
1600
+ });
1601
+ });
1602
+ emitAction("updated profile", data, cmd.optsWithGlobals().json);
1603
+ });
1604
+ 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) => {
1605
+ const cfg = resolveConfig(cmd.optsWithGlobals());
1606
+ const data = await runApi("Fetching feed", async () => {
1607
+ const client = await makeClient(cfg);
1608
+ return client.GET("/me/memories/feed", {
1609
+ params: {
1610
+ query: {
1611
+ limit: Number(opts.limit),
1612
+ includeArchived: Boolean(opts.includeArchived),
1613
+ includeText: Boolean(opts.includeText),
1614
+ ...opts.cursor ? { cursor: opts.cursor } : {},
1615
+ ...opts.query ? { query: opts.query } : {},
1616
+ ...opts.filterTags ? { filterTags: opts.filterTags } : {}
1617
+ }
1618
+ }
1619
+ });
1620
+ });
1621
+ emit(data, cmd.optsWithGlobals().json);
1622
+ });
1623
+ account.command("reviews").description("Your review queue (GET /reviews)").option("--status <status>", "Pending | Resolved | Empty").option("--scope-kind <scopeKind>", "Memory | Project | Workspace").option("--scope-target-id <id>", "Scope target id").option("--limit <n>", "Max results", "20").action(async (opts, cmd) => {
1624
+ const cfg = resolveConfig(cmd.optsWithGlobals());
1625
+ const data = await runApi("Fetching reviews", async () => {
1626
+ const client = await makeClient(cfg);
1627
+ return client.GET("/reviews", {
1628
+ params: {
1629
+ query: {
1630
+ limit: Number(opts.limit),
1631
+ ...opts.status ? { status: opts.status } : {},
1632
+ ...opts.scopeKind ? { scopeKind: opts.scopeKind } : {},
1633
+ ...opts.scopeTargetId ? { scopeTargetId: opts.scopeTargetId } : {}
1634
+ }
1635
+ }
1636
+ });
1637
+ });
1638
+ emit(data, cmd.optsWithGlobals().json);
1639
+ });
1640
+ account.command("review-get <reviewId>").description("Fetch one review bundle (GET /reviews/{reviewId})").action(async (reviewId, _opts, cmd) => {
1641
+ const cfg = resolveConfig(cmd.optsWithGlobals());
1642
+ const data = await runApi("Fetching review", async () => {
1643
+ const client = await makeClient(cfg);
1644
+ return client.GET("/reviews/{reviewId}", { params: { path: { reviewId } } });
1645
+ });
1646
+ emit(data, cmd.optsWithGlobals().json);
1647
+ });
1648
+ account.command("review-accept <reviewId>").description("Accept a review bundle (POST /reviews/{reviewId}/accept)").action(async (reviewId, _opts, cmd) => {
1649
+ const cfg = resolveConfig(cmd.optsWithGlobals());
1650
+ const data = await runApi("Accepting review", async () => {
1651
+ const client = await makeClient(cfg);
1652
+ return client.POST("/reviews/{reviewId}/accept", {
1653
+ params: { path: { reviewId } },
1654
+ body: { decisions: {} }
1655
+ });
1656
+ });
1657
+ emitAction(`accepted review ${style.bold(reviewId)}`, data, cmd.optsWithGlobals().json);
1658
+ });
1659
+ account.command("lookup-batch <ids...>").description("Resolve many ids at once (POST /lookup/batch)").action(async (ids, _opts, cmd) => {
1660
+ const cfg = resolveConfig(cmd.optsWithGlobals());
1661
+ const data = await runApi(`Resolving ${ids.length} ids`, async () => {
1662
+ const client = await makeClient(cfg);
1663
+ return client.POST("/lookup/batch", { body: { ids } });
1664
+ });
1665
+ emit(data, cmd.optsWithGlobals().json);
1666
+ });
1667
+ }
1668
+
1669
+ // src/commands/chat.ts
1670
+ function registerChat(program2) {
1671
+ const chat = program2.command("chat").description("Send and read Slack / Discord channel messages").option("--surface <surface>", "slack | discord", "slack");
1672
+ chat.addHelpText(
1673
+ "after",
1674
+ `
1675
+ Examples:
1676
+ $ sechroom chat send C0123456789 "deploy is green" --surface slack
1677
+ $ sechroom chat send 987654321098765432 "deploy is green" --surface discord --guild 123456789012345678
1678
+ $ sechroom chat send C0123456789 "lgtm" --surface slack --as user --parent 1718049600.123456
1679
+ $ sechroom chat messages --surface slack
1680
+ $ sechroom chat replies 1718049600.123456 --surface slack
1681
+ $ sechroom chat stop-tracking 1718049600.123456 --surface slack`
1682
+ );
1683
+ chat.command("send <channelId> <text>").description("Send a message to a channel (POST /chat/channel-messages/{surface})").option("--guild <guildId>", "Discord guild snowflake \u2014 required for --surface discord").option("--memory <memoryId>", "Attach a sechroom memory id").option("--no-track", "Don't capture replies to this message").option("--parent <parentMessage>", "Thread under a parent (Slack thread_ts / Discord message id)").option("--source <source>", "Source / lane stamp (renders an attribution footer)", "cli").option("--as <as>", "Slack only: 'bot' (default) or 'user' (your linked Slack identity)", "bot").action(async (channelId, text, opts, cmd) => {
1684
+ const { surface, ...globals } = cmd.optsWithGlobals();
1685
+ const json = Boolean(cmd.optsWithGlobals().json);
1686
+ const cfg = resolveConfig(globals);
1687
+ const data = await runApi("Sending message", async () => {
1688
+ const client = await makeClient(cfg);
1689
+ return client.POST("/chat/channel-messages/{surface}", {
1690
+ params: { path: { surface: String(surface) } },
1691
+ body: {
1692
+ channelId,
1693
+ text,
1694
+ guildId: opts.guild ?? null,
1695
+ attachedMemoryId: opts.memory ?? null,
1696
+ trackReplies: opts.track,
1697
+ parentMessage: opts.parent ?? null,
1698
+ source: opts.source,
1699
+ as: opts.as
1700
+ }
1701
+ });
1702
+ });
1703
+ if (!data.ok) {
1704
+ if (json) {
1705
+ emit(data, true);
1706
+ } else {
1707
+ process.stderr.write(
1708
+ `${err("\u2717")} send failed: ${data.upstreamError ?? "error"}${data.errorDescription ? ` \u2014 ${data.errorDescription}` : ""}
1709
+ `
1710
+ );
1711
+ }
1712
+ process.exit(1);
1713
+ }
1714
+ const idPart = data.persistedId ? ` ${style.dim(`(${data.persistedId})`)}` : "";
1715
+ emitAction(`sent to ${surface} ${style.bold(channelId)}${idPart}`, data, json);
1716
+ });
1717
+ chat.command("messages").description("List recent channel messages (GET /chat/channel-messages/{surface})").action(async (_opts, cmd) => {
1718
+ const { surface, ...globals } = cmd.optsWithGlobals();
1719
+ const cfg = resolveConfig(globals);
1720
+ const data = await runApi("Fetching messages", async () => {
1721
+ const client = await makeClient(cfg);
1722
+ return client.GET("/chat/channel-messages/{surface}", {
1723
+ params: { path: { surface: String(surface) } }
1724
+ });
1725
+ });
1726
+ emit(data, cmd.optsWithGlobals().json);
1727
+ });
1728
+ chat.command("replies <messageId>").description("List thread replies for a message (GET /chat/channel-messages/by-id/{id}/replies)").action(async (messageId, _opts, cmd) => {
1729
+ const cfg = resolveConfig(cmd.optsWithGlobals());
1730
+ const data = await runApi("Fetching replies", async () => {
1731
+ const client = await makeClient(cfg);
1732
+ return client.GET("/chat/channel-messages/by-id/{id}/replies", {
1733
+ params: { path: { id: messageId } }
1734
+ });
1735
+ });
1736
+ emit(data, cmd.optsWithGlobals().json);
1737
+ });
1738
+ chat.command("stop-tracking <messageId>").description("Stop watching a message for replies (POST .../by-id/{id}/stop-tracking-replies)").action(async (messageId, _opts, cmd) => {
1739
+ const cfg = resolveConfig(cmd.optsWithGlobals());
1740
+ const data = await runApi("Stopping reply tracking", async () => {
1741
+ const client = await makeClient(cfg);
1742
+ return client.POST("/chat/channel-messages/by-id/{id}/stop-tracking-replies", {
1743
+ params: { path: { id: messageId } },
1744
+ body: {}
1745
+ });
1746
+ });
1747
+ emitAction(`stopped tracking replies on ${style.bold(messageId)}`, data, cmd.optsWithGlobals().json);
1748
+ });
1749
+ }
1750
+
622
1751
  // src/setup/apply.ts
623
1752
  import { mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2, existsSync as existsSync2 } from "fs";
624
1753
  import { dirname as dirname2 } from "path";
@@ -1303,6 +2432,14 @@ local: ${d.localPath ?? "(none)"} ${JSON.stringify(readLocalConfig())}
1303
2432
  registerMemory(program);
1304
2433
  registerWorklog(program);
1305
2434
  registerLookup(program);
2435
+ registerRelationships(program);
2436
+ registerWorkspace(program);
2437
+ registerProject(program);
2438
+ registerFiling(program);
2439
+ registerContinuity(program);
2440
+ registerId(program);
2441
+ registerAccount(program);
2442
+ registerChat(program);
1306
2443
  registerInit(program);
1307
2444
  registerSetup(program);
1308
2445
  registerOnboard(program);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sechroom/cli",
3
- "version": "2026.6.8",
3
+ "version": "2026.6.10",
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",