@gobi-ai/cli 0.6.14 → 0.6.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -44,7 +44,7 @@ gobi space warp
44
44
  gobi brain search --query "machine learning"
45
45
 
46
46
  # Ask a brain a question
47
- gobi brain ask --vault-slug my-vault --space-slug my-space --question "What is RAG?"
47
+ gobi brain ask --vault-slug my-vault --question "What is RAG?"
48
48
  ```
49
49
 
50
50
  ## Commands
@@ -62,27 +62,35 @@ gobi brain ask --vault-slug my-vault --space-slug my-space --question "What is R
62
62
  | Command | Description |
63
63
  |---------|-------------|
64
64
  | `gobi init` | Log in (if needed) and select or create a vault |
65
- | `gobi space warp` | Select the active space |
65
+ | `gobi space list` | List spaces you are a member of |
66
+ | `gobi space warp [spaceSlug]` | Select the active space (interactive if slug omitted) |
66
67
 
67
68
  ### Brains
68
69
 
69
70
  | Command | Description |
70
71
  |---------|-------------|
71
- | `gobi brain search --query <q>` | Search brains across all your spaces |
72
- | `gobi brain ask --vault-slug <slug> --space-slug <slug> --question <q>` | Ask a brain a question (creates a 1:1 session) |
72
+ | `gobi brain search --query <q>` | Search public brains by text and semantic similarity |
73
+ | `gobi brain ask --vault-slug <slug> --question <q>` | Ask a brain a question (creates a 1:1 session) |
73
74
  | `gobi brain publish` | Upload `BRAIN.md` to your vault |
74
75
  | `gobi brain unpublish` | Remove `BRAIN.md` from your vault |
75
76
 
77
+ Public brains are accessible at `https://gobispace.com/@{vaultSlug}`.
78
+
79
+ `brain ask` also accepts `--rich-text <json>` (mutually exclusive with `--question`) and `--mode <auto|manual>`.
80
+
76
81
  ### Brain Updates
77
82
 
78
83
  | Command | Description |
79
84
  |---------|-------------|
80
- | `gobi brain list-updates` | List brain updates for your vault |
85
+ | `gobi brain list-updates` | List brain updates |
81
86
  | `gobi brain list-updates --mine` | List only your own brain updates |
82
87
  | `gobi brain post-update --title <t> --content <c>` | Post a brain update |
83
- | `gobi brain edit-update <id> --title <t>` | Edit a brain update |
88
+ | `gobi brain edit-update <id> [--title <t>] [--content <c>]` | Edit a brain update (at least one required) |
84
89
  | `gobi brain delete-update <id>` | Delete a brain update |
85
90
 
91
+ `list-updates` also accepts `--space-slug <slug>` to scope to a space, and `--limit`/`--cursor` for pagination.
92
+ `post-update` and `edit-update` accept `--auto-attachments` to upload wiki-linked `[[files]]` before posting.
93
+
86
94
  ### Threads
87
95
 
88
96
  | Command | Description |
@@ -90,7 +98,7 @@ gobi brain ask --vault-slug my-vault --space-slug my-space --question "What is R
90
98
  | `gobi space list-threads` | List threads in the current space |
91
99
  | `gobi space get-thread <id>` | Get a thread and its replies |
92
100
  | `gobi space create-thread --title <t> --content <c>` | Create a thread |
93
- | `gobi space edit-thread <id> --title <t>` | Edit a thread |
101
+ | `gobi space edit-thread <id> [--title <t>] [--content <c>]` | Edit a thread (at least one required) |
94
102
  | `gobi space delete-thread <id>` | Delete a thread |
95
103
 
96
104
  ### Replies
@@ -108,15 +116,44 @@ gobi brain ask --vault-slug my-vault --space-slug my-space --question "What is R
108
116
  | `gobi session list` | List your sessions |
109
117
  | `gobi session get <id>` | Get a session and its messages |
110
118
  | `gobi session reply <id> --content <c>` | Send a message in a session |
111
- | `gobi session update <id> --mode <mode>` | Set session mode (auto/manual) |
112
119
 
113
- ### Global options
120
+ `session reply` also accepts `--rich-text <json>` (mutually exclusive with `--content`).
121
+
122
+ ### Sense
123
+
124
+ | Command | Description |
125
+ |---------|-------------|
126
+ | `gobi sense activities --start-time <iso> --end-time <iso>` | Fetch activity records in a time range |
127
+ | `gobi sense transcriptions --start-time <iso> --end-time <iso>` | Fetch transcription records in a time range |
128
+
129
+ Times are ISO 8601 UTC (e.g. `2026-03-20T00:00:00Z`).
130
+
131
+ ### Sync
132
+
133
+ | Command | Description |
134
+ |---------|-------------|
135
+ | `gobi sync` | Sync local vault files with Gobi Webdrive |
114
136
 
115
137
  | Option | Description |
116
138
  |--------|-------------|
117
- | `--json` | Output results as JSON |
118
- | `--space-slug <slug>` | Override the default space (on `space` commands); required on `brain ask`, optional filter on `session list` |
119
- | `--vault-slug <slug>` | Override the default vault (on `brain list-updates` and `brain post-update`) |
139
+ | `--upload-only` | Only upload local changes to server |
140
+ | `--download-only` | Only download server changes to local |
141
+ | `--conflict <strategy>` | Conflict resolution: `ask` (default), `server`, `client`, `skip` |
142
+ | `--dir <path>` | Local vault directory (default: current directory) |
143
+ | `--dry-run` | Preview changes without making them |
144
+ | `--full` | Full sync: ignore cursor and hash cache, re-check every file |
145
+ | `--path <path>` | Restrict sync to specific file/folder (repeatable) |
146
+ | `--plan-file <path>` | Write dry-run plan to file, or read plan to execute |
147
+ | `--execute` | Execute a previously written plan file (requires `--plan-file`) |
148
+ | `--conflict-choices <json>` | Per-file conflict resolutions as JSON (use with `--execute`) |
149
+
150
+ ### Global options
151
+
152
+ | Option | Scope | Description |
153
+ |--------|-------|-------------|
154
+ | `--json` | All commands | Output results as JSON |
155
+ | `--space-slug <slug>` | `space` commands | Override the default space (from `.gobi/settings.yaml`) |
156
+ | `--vault-slug <slug>` | Per-command | Override the default vault; available on `brain list-updates`, `brain post-update`, `brain edit-update`, `space create-thread`, `space edit-thread`, `space edit-reply` |
120
157
 
121
158
  ## Configuration
122
159
 
@@ -124,7 +161,7 @@ gobi brain ask --vault-slug my-vault --space-slug my-space --question "What is R
124
161
 
125
162
  | Variable | Default | Description |
126
163
  |----------|---------|-------------|
127
- | `GOBI_BASE_URL` | `https://backend.joingobi.com` | API server URL |
164
+ | `GOBI_BASE_URL` | `https://api.joingobi.com` | API server URL |
128
165
  | `GOBI_WEBDRIVE_BASE_URL` | `https://webdrive.joingobi.com` | File storage URL |
129
166
 
130
167
  ### Files
@@ -343,8 +343,12 @@ function matchesPaths(filePath, paths) {
343
343
  async function performSync(baseUrl, vaultSlug, state, syncfilesChanges, privatefilesChanges, localFiles, opts, token) {
344
344
  const body = {
345
345
  cursor: state.cursor,
346
- // dryRun: don't mutate server-side syncfiles/privatefiles
347
- syncfilesChanges: opts.dryRun ? { added: [], removed: [] } : syncfilesChanges,
346
+ // dryRun: suppress removes (destructive) but send adds so the server can
347
+ // compute the correct file scope for the preview. Privatefiles are fully
348
+ // suppressed — they don't affect file actions and should never mutate in dry-run.
349
+ syncfilesChanges: opts.dryRun
350
+ ? { added: syncfilesChanges.added, removed: [] }
351
+ : syncfilesChanges,
348
352
  privatefilesChanges: opts.dryRun ? { added: [], removed: [] } : privatefilesChanges,
349
353
  clientFiles: localFiles,
350
354
  uploadOnly: opts.uploadOnly,
@@ -357,6 +361,10 @@ export async function runSync(opts) {
357
361
  const baseUrl = opts.webdriveUrl ?? WEBDRIVE_BASE_URL;
358
362
  const gobiDir = join(vaultDir, ".gobi");
359
363
  mkdirSync(gobiDir, { recursive: true });
364
+ const token = opts.authToken ?? (await getValidToken());
365
+ if (opts.execute) {
366
+ return executeSyncPlan(opts, baseUrl, token, gobiDir);
367
+ }
360
368
  const state = loadSyncState(gobiDir);
361
369
  // --full: treat this run as a first-time sync (re-check every file against the server)
362
370
  if (opts.full) {
@@ -365,7 +373,6 @@ export async function runSync(opts) {
365
373
  if (!jsonMode)
366
374
  console.log("Full sync: ignoring cursor and hash cache.");
367
375
  }
368
- const token = opts.authToken ?? (await getValidToken());
369
376
  // Read syncfiles whitelist
370
377
  const syncfilesExistsLocally = existsSync(join(gobiDir, "syncfiles"));
371
378
  const { patterns: currPatterns, contentHash: currSyncfilesHash } = readSyncfiles(gobiDir);
@@ -474,6 +481,26 @@ export async function runSync(opts) {
474
481
  }
475
482
  if (!jsonMode)
476
483
  console.log(` ${syncResp.files.length} action(s).`);
484
+ // Write plan file if requested (dry-run + --plan-file)
485
+ if (opts.dryRun && opts.planFile) {
486
+ const offlineDeletions = Object.keys(state.hashCache).filter((p) => !localPathSet.has(p) && !existsSync(join(vaultDir, p)));
487
+ const plan = {
488
+ vaultSlug,
489
+ syncCursor: syncResp.cursor,
490
+ syncfilesHash: syncResp.syncfilesHash || null,
491
+ privatefilesHash: syncResp.privatefilesHash || null,
492
+ downloadSyncfiles: !!(syncResp.syncfilesHash &&
493
+ (syncResp.syncfilesHash !== state.syncfilesHash || !syncfilesExistsLocally)),
494
+ downloadPrivatefiles: !!(syncResp.privatefilesHash &&
495
+ (syncResp.privatefilesHash !== state.privatefilesHash || !privatefilesExistsLocally)),
496
+ actions: syncResp.files.filter((f) => matchesPaths(f.path, opts.paths ?? [])),
497
+ offlineDeletions,
498
+ createdAt: new Date().toISOString(),
499
+ };
500
+ await writeFile(opts.planFile, JSON.stringify(plan, null, 2));
501
+ if (!jsonMode)
502
+ console.log(`Plan written to ${opts.planFile}`);
503
+ }
477
504
  // Process actions
478
505
  let uploaded = 0, downloaded = 0, deletedLocally = 0, conflicts = 0, skipped = 0, errors = 0;
479
506
  const errorDetails = [];
@@ -633,15 +660,17 @@ export async function runSync(opts) {
633
660
  }
634
661
  // Persist state (always, even on partial failures)
635
662
  const finalCursor = Math.max(syncResp.cursor, maxMutationCursor !== null ? maxMutationCursor : 0);
636
- state.cursor = finalCursor;
637
- state.syncfilesHash = syncResp.syncfilesHash || currSyncfilesHash;
638
- // If the server returned an empty syncfilesHash the vault was deleted server-side
639
- // (empty patterns path). Reset patterns so the next sync re-registers them as "added",
640
- // which lets the 409 retry resurrect the vault.
641
- state.patterns = syncResp.syncfilesHash === "" ? [] : effectivePatterns;
642
- state.privatePatterns = effectivePrivatePatterns;
643
- state.privatefilesHash = syncResp.privatefilesHash || state.privatefilesHash;
644
- saveSyncState(gobiDir, state);
663
+ if (!opts.dryRun) {
664
+ state.cursor = finalCursor;
665
+ state.syncfilesHash = syncResp.syncfilesHash || currSyncfilesHash;
666
+ // If the server returned an empty syncfilesHash the vault was deleted server-side
667
+ // (empty patterns path). Reset patterns so the next sync re-registers them as "added",
668
+ // which lets the 409 retry resurrect the vault.
669
+ state.patterns = syncResp.syncfilesHash === "" ? [] : effectivePatterns;
670
+ state.privatePatterns = effectivePrivatePatterns;
671
+ state.privatefilesHash = syncResp.privatefilesHash || state.privatefilesHash;
672
+ saveSyncState(gobiDir, state);
673
+ }
645
674
  // Output summary
646
675
  const result = {
647
676
  uploaded,
@@ -672,6 +701,184 @@ export async function runSync(opts) {
672
701
  process.exitCode = 1;
673
702
  }
674
703
  }
704
+ return result;
705
+ }
706
+ // ─── Execute Plan ─────────────────────────────────────────────────────────────
707
+ async function executeSyncPlan(opts, baseUrl, token, gobiDir) {
708
+ const { vaultSlug, dir: vaultDir, jsonMode } = opts;
709
+ if (!opts.planFile)
710
+ throw new GobiError("--execute requires --plan-file", "INVALID_OPTION");
711
+ if (!existsSync(opts.planFile))
712
+ throw new GobiError(`Plan file not found: ${opts.planFile}`, "PLAN_NOT_FOUND");
713
+ const plan = JSON.parse(readFileSync(opts.planFile, "utf-8"));
714
+ if (plan.vaultSlug !== vaultSlug)
715
+ throw new GobiError(`Plan is for vault "${plan.vaultSlug}", not "${vaultSlug}"`, "PLAN_MISMATCH");
716
+ // Validate conflict coverage upfront
717
+ const conflictActions = plan.actions.filter((a) => a.action === "conflict");
718
+ const canFallback = opts.conflict && opts.conflict !== "ask";
719
+ const unresolvedConflicts = conflictActions
720
+ .filter((a) => !opts.conflictChoices?.[a.path])
721
+ .map((a) => a.path);
722
+ if (unresolvedConflicts.length > 0 && !canFallback) {
723
+ throw new GobiError(`Unresolved conflicts — pass --conflict or add to --conflict-choices:\n${unresolvedConflicts.map((p) => ` ${p}`).join("\n")}`, "UNRESOLVED_CONFLICTS");
724
+ }
725
+ const state = loadSyncState(gobiDir);
726
+ // Execute offline deletions
727
+ let maxMutationCursor = null;
728
+ for (const path of plan.offlineDeletions) {
729
+ if (opts.downloadOnly)
730
+ continue;
731
+ if (!jsonMode)
732
+ console.log(` Deleting remote (offline deletion): ${path}`);
733
+ try {
734
+ const cursor = await webdriveDelete(baseUrl, vaultSlug, path, token);
735
+ if (cursor !== null && (maxMutationCursor === null || cursor > maxMutationCursor))
736
+ maxMutationCursor = cursor;
737
+ }
738
+ catch (err) {
739
+ if (!jsonMode)
740
+ console.error(` Error deleting remote ${path}: ${err.message}`);
741
+ }
742
+ delete state.hashCache[path];
743
+ }
744
+ // Execute actions from plan
745
+ let uploaded = 0, downloaded = 0, deletedLocally = 0, conflicts = 0, skipped = 0, errors = 0;
746
+ const errorDetails = [];
747
+ for (const entry of plan.actions) {
748
+ try {
749
+ const absPath = join(vaultDir, entry.path);
750
+ if (entry.action === "upload") {
751
+ if (opts.downloadOnly)
752
+ continue;
753
+ const content = readFileSync(absPath);
754
+ const hash = md5Hex(content);
755
+ const cursor = await webdrivePut(baseUrl, vaultSlug, entry.path, content, hash, token);
756
+ if (cursor !== null && (maxMutationCursor === null || cursor > maxMutationCursor))
757
+ maxMutationCursor = cursor;
758
+ state.hashCache[entry.path] = { hash, mtime: statSync(absPath).mtimeMs, size: content.length };
759
+ if (!jsonMode)
760
+ console.log(` Uploaded: ${entry.path}`);
761
+ uploaded++;
762
+ }
763
+ else if (entry.action === "download") {
764
+ if (opts.uploadOnly)
765
+ continue;
766
+ const content = await webdriveGet(baseUrl, vaultSlug, entry.path, token);
767
+ mkdirSync(dirname(absPath), { recursive: true });
768
+ await writeFile(absPath, content);
769
+ const hash = md5Hex(content);
770
+ state.hashCache[entry.path] = { hash, mtime: statSync(absPath).mtimeMs, size: content.length };
771
+ if (!jsonMode)
772
+ console.log(` Downloaded: ${entry.path}`);
773
+ downloaded++;
774
+ }
775
+ else if (entry.action === "delete_local") {
776
+ if (opts.uploadOnly)
777
+ continue;
778
+ if (!existsSync(absPath))
779
+ continue;
780
+ await trash(absPath);
781
+ delete state.hashCache[entry.path];
782
+ if (!jsonMode)
783
+ console.log(` Deleted local (moved to trash): ${entry.path}`);
784
+ deletedLocally++;
785
+ }
786
+ else if (entry.action === "conflict") {
787
+ conflicts++;
788
+ const choice = opts.conflictChoices?.[entry.path] ?? opts.conflict ?? "skip";
789
+ if (choice === "server") {
790
+ const content = await webdriveGet(baseUrl, vaultSlug, entry.path, token);
791
+ mkdirSync(dirname(absPath), { recursive: true });
792
+ await writeFile(absPath, content);
793
+ const hash = md5Hex(content);
794
+ state.hashCache[entry.path] = { hash, mtime: statSync(absPath).mtimeMs, size: content.length };
795
+ if (!jsonMode)
796
+ console.log(` Conflict resolved (server): ${entry.path}`);
797
+ downloaded++;
798
+ }
799
+ else if (choice === "client") {
800
+ if (!jsonMode)
801
+ console.log(` Conflict resolved (local kept): ${entry.path}`);
802
+ }
803
+ else {
804
+ skipped++;
805
+ if (!jsonMode)
806
+ console.log(` Conflict skipped: ${entry.path}`);
807
+ }
808
+ }
809
+ }
810
+ catch (err) {
811
+ errors++;
812
+ const msg = err.message;
813
+ errorDetails.push({ path: entry.path, action: entry.action, error: msg });
814
+ if (!jsonMode)
815
+ console.error(` Error [${entry.action}] ${entry.path}: ${msg}`);
816
+ }
817
+ }
818
+ // Download syncfiles/privatefiles as indicated by plan
819
+ const { patterns: currPatterns } = readSyncfiles(gobiDir);
820
+ let effectivePatterns = currPatterns;
821
+ if (plan.downloadSyncfiles) {
822
+ try {
823
+ const content = await webdriveGet(baseUrl, vaultSlug, ".gobi/syncfiles", token);
824
+ await writeFile(join(gobiDir, "syncfiles"), content);
825
+ effectivePatterns = readSyncfiles(gobiDir).patterns;
826
+ if (!jsonMode)
827
+ console.log(" Updated local syncfiles from server.");
828
+ }
829
+ catch (err) {
830
+ if (!jsonMode)
831
+ console.error(`Warning: Failed to download syncfiles: ${err.message}`);
832
+ }
833
+ }
834
+ let effectivePrivatePatterns = readPrivatefiles(gobiDir);
835
+ if (plan.downloadPrivatefiles) {
836
+ try {
837
+ const content = await webdriveGet(baseUrl, vaultSlug, ".gobi/privatefiles", token);
838
+ await writeFile(join(gobiDir, "privatefiles"), content);
839
+ effectivePrivatePatterns = readPrivatefiles(gobiDir);
840
+ if (!jsonMode)
841
+ console.log(" Updated local privatefiles from server.");
842
+ }
843
+ catch (err) {
844
+ if (!jsonMode)
845
+ console.error(`Warning: Failed to download privatefiles: ${err.message}`);
846
+ }
847
+ }
848
+ // Save state
849
+ const finalCursor = Math.max(plan.syncCursor, maxMutationCursor ?? 0);
850
+ state.cursor = finalCursor;
851
+ state.syncfilesHash = plan.syncfilesHash ?? state.syncfilesHash;
852
+ state.patterns = effectivePatterns;
853
+ state.privatePatterns = effectivePrivatePatterns;
854
+ state.privatefilesHash = plan.privatefilesHash ?? state.privatefilesHash;
855
+ saveSyncState(gobiDir, state);
856
+ // Delete plan file after successful execution
857
+ rmSync(opts.planFile, { force: true });
858
+ const result = {
859
+ uploaded,
860
+ downloaded,
861
+ deletedLocally,
862
+ conflicts,
863
+ skipped,
864
+ errors,
865
+ cursor: finalCursor,
866
+ errorDetails,
867
+ };
868
+ if (jsonMode) {
869
+ jsonOut(result);
870
+ }
871
+ else {
872
+ console.log("\nSync complete.");
873
+ console.log(` Uploaded: ${uploaded}`);
874
+ console.log(` Downloaded: ${downloaded}`);
875
+ console.log(` Deleted local: ${deletedLocally}`);
876
+ console.log(` Conflicts: ${conflicts}`);
877
+ console.log(` Errors: ${errors}`);
878
+ if (errors > 0)
879
+ process.exitCode = 1;
880
+ }
881
+ return result;
675
882
  }
676
883
  // ─── Commander Registration ───────────────────────────────────────────────────
677
884
  export function registerSyncCommand(program) {
@@ -685,14 +892,29 @@ export function registerSyncCommand(program) {
685
892
  .option("--dry-run", "Preview changes without making them")
686
893
  .option("--full", "Full sync: ignore cursor and hash cache, re-check every file")
687
894
  .option("--path <path>", "Restrict sync to a specific file or folder (repeatable)", (v, prev) => prev.concat(v), [])
895
+ .option("--plan-file <path>", "Write dry-run plan to file (use with --dry-run) or read plan to execute (use with --execute)")
896
+ .option("--execute", "Execute a previously written plan file (requires --plan-file)")
897
+ .option("--conflict-choices <json>", "Per-file conflict resolutions as JSON object, e.g. '{\"file.md\":\"server\"}' (use with --execute)")
688
898
  .action(async function (opts) {
689
899
  if (opts.uploadOnly && opts.downloadOnly) {
690
900
  throw new GobiError("--upload-only and --download-only are mutually exclusive.", "INVALID_OPTION");
691
901
  }
902
+ if (opts.execute && !opts.planFile) {
903
+ throw new GobiError("--execute requires --plan-file", "INVALID_OPTION");
904
+ }
692
905
  const validStrategies = ["ask", "server", "client", "skip"];
693
906
  if (!validStrategies.includes(opts.conflict)) {
694
907
  throw new GobiError(`Invalid --conflict value "${opts.conflict}". Use: ask|server|client|skip`, "INVALID_OPTION");
695
908
  }
909
+ let conflictChoices;
910
+ if (opts.conflictChoices) {
911
+ try {
912
+ conflictChoices = JSON.parse(opts.conflictChoices);
913
+ }
914
+ catch {
915
+ throw new GobiError("--conflict-choices must be valid JSON", "INVALID_OPTION");
916
+ }
917
+ }
696
918
  const vaultSlug = getVaultSlug();
697
919
  const dir = opts.dir ? pathResolve(opts.dir) : process.cwd();
698
920
  await runSync({
@@ -704,6 +926,9 @@ export function registerSyncCommand(program) {
704
926
  dryRun: !!opts.dryRun,
705
927
  full: !!opts.full,
706
928
  paths: opts.path ?? [],
929
+ planFile: opts.planFile,
930
+ execute: !!opts.execute,
931
+ conflictChoices,
707
932
  jsonMode: isJsonMode(this),
708
933
  });
709
934
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gobi-ai/cli",
3
- "version": "0.6.14",
3
+ "version": "0.6.16",
4
4
  "description": "CLI client for the Gobi collaborative knowledge platform",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -10,12 +10,12 @@ description: >-
10
10
  allowed-tools: Bash(gobi:*)
11
11
  metadata:
12
12
  author: gobi-ai
13
- version: "0.6.13"
13
+ version: "0.6.16"
14
14
  ---
15
15
 
16
16
  # gobi-cli
17
17
 
18
- A CLI client for the Gobi collaborative knowledge platform (v0.6.13).
18
+ A CLI client for the Gobi collaborative knowledge platform (v0.6.16).
19
19
 
20
20
  ## Prerequisites
21
21
 
@@ -98,11 +98,11 @@ gobi auth status
98
98
 
99
99
  ## Gobi Brain — Knowledge Management
100
100
 
101
- `gobi brain` commands manage your vault's brain: search across all spaces, ask brains questions, and publish/unpublish your BRAIN.md.
101
+ `gobi brain` commands manage your vault's brain: search across all spaces, ask brains questions, and publish/unpublish your BRAIN.md. Public brains are accessible at `https://gobispace.com/@{vaultSlug}`.
102
102
 
103
103
  ## Gobi Session — Conversations
104
104
 
105
- `gobi session` commands manage your conversations: list, read, reply to, and update sessions.
105
+ `gobi session` commands manage your conversations: list, read, and reply to sessions.
106
106
 
107
107
  ## Important: JSON Mode
108
108
 
@@ -192,6 +192,8 @@ gobi auth --help
192
192
  gobi space --help
193
193
  gobi brain --help
194
194
  gobi session --help
195
+ gobi sense --help
196
+ gobi sync --help
195
197
  ```
196
198
 
197
199
  ## Configuration Files
@@ -206,5 +208,5 @@ gobi session --help
206
208
 
207
209
  | Variable | Default | Description |
208
210
  |----------|---------|-------------|
209
- | `GOBI_BASE_URL` | `https://backend.joingobi.com` | API server URL |
211
+ | `GOBI_BASE_URL` | `https://api.joingobi.com` | API server URL |
210
212
  | `GOBI_WEBDRIVE_BASE_URL` | `https://webdrive.joingobi.com` | File storage URL |
@@ -98,11 +98,11 @@ gobi auth status
98
98
 
99
99
  ## Gobi Brain — Knowledge Management
100
100
 
101
- `gobi brain` commands manage your vault's brain: search across all spaces, ask brains questions, and publish/unpublish your BRAIN.md.
101
+ `gobi brain` commands manage your vault's brain: search across all spaces, ask brains questions, and publish/unpublish your BRAIN.md. Public brains are accessible at `https://gobispace.com/@{vaultSlug}`.
102
102
 
103
103
  ## Gobi Session — Conversations
104
104
 
105
- `gobi session` commands manage your conversations: list, read, reply to, and update sessions.
105
+ `gobi session` commands manage your conversations: list, read, and reply to sessions.
106
106
 
107
107
  ## Important: JSON Mode
108
108
 
@@ -154,6 +154,8 @@ gobi auth --help
154
154
  gobi space --help
155
155
  gobi brain --help
156
156
  gobi session --help
157
+ gobi sense --help
158
+ gobi sync --help
157
159
  ```
158
160
 
159
161
  ## Configuration Files
@@ -168,5 +170,5 @@ gobi session --help
168
170
 
169
171
  | Variable | Default | Description |
170
172
  |----------|---------|-------------|
171
- | `GOBI_BASE_URL` | `https://backend.joingobi.com` | API server URL |
173
+ | `GOBI_BASE_URL` | `https://api.joingobi.com` | API server URL |
172
174
  | `GOBI_WEBDRIVE_BASE_URL` | `https://webdrive.joingobi.com` | File storage URL |
@@ -6,12 +6,15 @@ Usage: gobi sync [options]
6
6
  Sync local vault files with Gobi Webdrive.
7
7
 
8
8
  Options:
9
- --upload-only Only upload local changes to server
10
- --download-only Only download server changes to local
11
- --conflict <strategy> Conflict resolution strategy: ask|server|client|skip (default: "ask")
12
- --dir <path> Local vault directory (default: current directory)
13
- --dry-run Preview changes without making them
14
- --full Full sync: ignore cursor and hash cache, re-check every file
15
- --path <path> Restrict sync to a specific file or folder (repeatable) (default: [])
16
- -h, --help display help for command
9
+ --upload-only Only upload local changes to server
10
+ --download-only Only download server changes to local
11
+ --conflict <strategy> Conflict resolution strategy: ask|server|client|skip (default: "ask")
12
+ --dir <path> Local vault directory (default: current directory)
13
+ --dry-run Preview changes without making them
14
+ --full Full sync: ignore cursor and hash cache, re-check every file
15
+ --path <path> Restrict sync to a specific file or folder (repeatable) (default: [])
16
+ --plan-file <path> Write dry-run plan to file (use with --dry-run) or read plan to execute (use with --execute)
17
+ --execute Execute a previously written plan file (requires --plan-file)
18
+ --conflict-choices <json> Per-file conflict resolutions as JSON object, e.g. '{"file.md":"server"}' (use with --execute)
19
+ -h, --help display help for command
17
20
  ```