@gobi-ai/cli 0.5.0 → 0.6.1

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.
@@ -0,0 +1,45 @@
1
+ import { existsSync, readFileSync } from "fs";
2
+ import { join, extname } from "path";
3
+ import { WEBDRIVE_BASE_URL } from "./constants.js";
4
+ export function extractWikiLinks(content) {
5
+ const seen = new Set();
6
+ const results = [];
7
+ for (const match of content.matchAll(/\[\[([^\]]+)\]\]/g)) {
8
+ const link = match[1].trim();
9
+ if (!seen.has(link)) {
10
+ seen.add(link);
11
+ results.push(link);
12
+ }
13
+ }
14
+ return results;
15
+ }
16
+ export async function uploadAttachments(vaultSlug, links, token) {
17
+ for (const link of links) {
18
+ let localPath = join(process.cwd(), link);
19
+ if (!existsSync(localPath)) {
20
+ if (!extname(link)) {
21
+ localPath = join(process.cwd(), link + ".md");
22
+ }
23
+ if (!existsSync(localPath)) {
24
+ console.warn(`Warning: Skipping [[${link}]]: not found locally`);
25
+ continue;
26
+ }
27
+ }
28
+ const filePath = extname(link) ? link : link + ".md";
29
+ console.log(`Uploading [[${link}]]...`);
30
+ const content = readFileSync(localPath);
31
+ const url = `${WEBDRIVE_BASE_URL}/api/v1/vaults/${vaultSlug}/files/${filePath}`;
32
+ const res = await fetch(url, {
33
+ method: "PUT",
34
+ headers: {
35
+ Authorization: `Bearer ${token}`,
36
+ "Content-Type": "application/octet-stream",
37
+ },
38
+ body: content,
39
+ });
40
+ if (!res.ok) {
41
+ throw new Error(`Failed to upload [[${link}]]: HTTP ${res.status}: ${(await res.text()) || "(no body)"}`);
42
+ }
43
+ console.log(`Uploaded [[${link}]]`);
44
+ }
45
+ }
@@ -5,6 +5,7 @@ import { WEBDRIVE_BASE_URL } from "../constants.js";
5
5
  import { getValidToken } from "../auth/manager.js";
6
6
  import { getVaultSlug } from "./init.js";
7
7
  import { isJsonMode, jsonOut, resolveVaultSlug, unwrapResp } from "./utils.js";
8
+ import { extractWikiLinks, uploadAttachments } from "../attachments.js";
8
9
  export function registerBrainCommand(program) {
9
10
  const brain = program
10
11
  .command("brain")
@@ -191,8 +192,14 @@ export function registerBrainCommand(program) {
191
192
  .option("--vault-slug <vaultSlug>", "Vault slug (overrides .gobi/settings.yaml)")
192
193
  .requiredOption("--title <title>", "Title of the update")
193
194
  .requiredOption("--content <content>", "Update content (markdown supported)")
195
+ .option("--auto-attachments", "Upload wiki-linked [[files]] to webdrive before posting")
194
196
  .action(async (opts) => {
195
197
  const vaultSlug = resolveVaultSlug(opts);
198
+ if (opts.autoAttachments) {
199
+ const token = await getValidToken();
200
+ const links = extractWikiLinks(opts.content);
201
+ await uploadAttachments(vaultSlug, links, token);
202
+ }
196
203
  const resp = (await apiPost(`/brain-updates/vault/${vaultSlug}`, {
197
204
  title: opts.title,
198
205
  content: opts.content,
@@ -0,0 +1,87 @@
1
+ import { apiGet } from "../client.js";
2
+ import { isJsonMode, jsonOut } from "./utils.js";
3
+ export function registerSenseCommand(program) {
4
+ const sense = program
5
+ .command("sense")
6
+ .description("Sense commands (activities, transcriptions).");
7
+ // ── Activities ──
8
+ sense
9
+ .command("activities")
10
+ .description("Fetch activity records within a time range.")
11
+ .requiredOption("--timezone <tz>", "IANA timezone (e.g. America/New_York)")
12
+ .option("--start-time <iso>", "Start of time range (ISO 8601, inclusive)")
13
+ .option("--end-time <iso>", "End of time range (ISO 8601, inclusive); requires --start-time")
14
+ .action(async (opts) => {
15
+ if (opts.endTime && !opts.startTime) {
16
+ throw new Error("--end-time requires --start-time.");
17
+ }
18
+ const params = { timezone: opts.timezone };
19
+ if (opts.startTime)
20
+ params.startTime = opts.startTime;
21
+ if (opts.endTime)
22
+ params.endTime = opts.endTime;
23
+ const resp = (await apiGet("/app/activities", params));
24
+ const activities = (resp.activities || []);
25
+ const pagination = (resp.pagination || {});
26
+ const latestTimestamp = resp.latestTimestamp;
27
+ if (isJsonMode(sense)) {
28
+ jsonOut({ activities, pagination, latestTimestamp });
29
+ return;
30
+ }
31
+ if (!activities.length) {
32
+ console.log("No activities found.");
33
+ if (latestTimestamp)
34
+ console.log(`Latest data available: ${latestTimestamp}`);
35
+ return;
36
+ }
37
+ const lines = activities.map((a) => {
38
+ const endStr = a.end_time ? ` → ${a.end_time}` : "";
39
+ return `- [${a.device_id}] ${a.category}: ${a.details} (${a.start_time}${endStr})`;
40
+ });
41
+ console.log(`Activities (${activities.length} items):\n` + lines.join("\n"));
42
+ if (latestTimestamp)
43
+ console.log(`Latest data available: ${latestTimestamp}`);
44
+ });
45
+ // ── Transcriptions ──
46
+ sense
47
+ .command("transcriptions")
48
+ .description("Fetch transcription records within a time range.")
49
+ .requiredOption("--timezone <tz>", "IANA timezone (e.g. America/New_York)")
50
+ .option("--start-time <iso>", "Start of time range (ISO 8601, inclusive)")
51
+ .option("--end-time <iso>", "End of time range (ISO 8601, inclusive); requires --start-time")
52
+ .action(async (opts) => {
53
+ if (opts.endTime && !opts.startTime) {
54
+ throw new Error("--end-time requires --start-time.");
55
+ }
56
+ const params = { timezone: opts.timezone };
57
+ if (opts.startTime)
58
+ params.startTime = opts.startTime;
59
+ if (opts.endTime)
60
+ params.endTime = opts.endTime;
61
+ const resp = (await apiGet("/app/transcriptions", params));
62
+ const transcriptions = (resp.transcriptions || []);
63
+ const pagination = (resp.pagination || {});
64
+ const latestTimestamp = resp.latestTimestamp;
65
+ if (isJsonMode(sense)) {
66
+ jsonOut({ transcriptions, pagination, latestTimestamp });
67
+ return;
68
+ }
69
+ if (!transcriptions.length) {
70
+ console.log("No transcriptions found.");
71
+ if (latestTimestamp)
72
+ console.log(`Latest data available: ${latestTimestamp}`);
73
+ return;
74
+ }
75
+ const lines = [];
76
+ for (const t of transcriptions) {
77
+ lines.push(`- [${t.device_id}] ${t.created_at}`);
78
+ const turns = (t.turns || []);
79
+ for (const turn of turns) {
80
+ lines.push(` Speaker ${turn.speaker} (${turn.timestamp}): ${turn.text}`);
81
+ }
82
+ }
83
+ console.log(`Transcriptions (${transcriptions.length} items):\n` + lines.join("\n"));
84
+ if (latestTimestamp)
85
+ console.log(`Latest data available: ${latestTimestamp}`);
86
+ });
87
+ }
@@ -1,7 +1,9 @@
1
1
  import { readFileSync } from "fs";
2
2
  import { apiGet, apiPost, apiPatch, apiDelete } from "../client.js";
3
3
  import { selectSpace, writeSpaceSetting } from "./init.js";
4
- import { isJsonMode, jsonOut, resolveSpaceSlug, unwrapResp } from "./utils.js";
4
+ import { isJsonMode, jsonOut, resolveSpaceSlug, resolveVaultSlug, unwrapResp } from "./utils.js";
5
+ import { extractWikiLinks, uploadAttachments } from "../attachments.js";
6
+ import { getValidToken } from "../auth/manager.js";
5
7
  function readContent(value) {
6
8
  if (value === "-")
7
9
  return readFileSync("/dev/stdin", "utf8");
@@ -144,11 +146,20 @@ export function registerSpaceCommand(program) {
144
146
  .description("Create a thread in a space.")
145
147
  .requiredOption("--title <title>", "Title of the thread")
146
148
  .requiredOption("--content <content>", "Thread content (markdown supported)")
149
+ .option("--auto-attachments", "Upload wiki-linked [[files]] to webdrive before posting")
150
+ .option("--vault-slug <vaultSlug>", "Vault slug for attachment uploads (overrides .gobi/settings.yaml)")
147
151
  .action(async (opts) => {
152
+ const content = readContent(opts.content);
153
+ if (opts.autoAttachments) {
154
+ const vaultSlug = resolveVaultSlug(opts);
155
+ const token = await getValidToken();
156
+ const links = extractWikiLinks(content);
157
+ await uploadAttachments(vaultSlug, links, token);
158
+ }
148
159
  const spaceSlug = resolveSpaceSlug(space);
149
160
  const resp = (await apiPost(`/spaces/${spaceSlug}/threads`, {
150
161
  title: opts.title,
151
- content: readContent(opts.content),
162
+ content,
152
163
  }));
153
164
  const thread = unwrapResp(resp);
154
165
  if (isJsonMode(space)) {
package/dist/main.js CHANGED
@@ -7,6 +7,7 @@ import { registerInitCommand, printContext } from "./commands/init.js";
7
7
  import { registerSpaceCommand } from "./commands/space.js";
8
8
  import { registerBrainCommand } from "./commands/brain.js";
9
9
  import { registerSessionsCommand } from "./commands/sessions.js";
10
+ import { registerSenseCommand } from "./commands/sense.js";
10
11
  const require = createRequire(import.meta.url);
11
12
  const { version } = require("../package.json");
12
13
  const SKIP_BANNER_COMMANDS = new Set(["auth", "init"]);
@@ -30,6 +31,7 @@ export async function cli() {
30
31
  registerSpaceCommand(program);
31
32
  registerBrainCommand(program);
32
33
  registerSessionsCommand(program);
34
+ registerSenseCommand(program);
33
35
  // Propagate helpWidth to all subcommands
34
36
  const helpWidth = process.stdout.columns || 200;
35
37
  for (const cmd of program.commands) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gobi-ai/cli",
3
- "version": "0.5.0",
3
+ "version": "0.6.1",
4
4
  "description": "CLI client for the Gobi collaborative knowledge platform",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -26,7 +26,8 @@
26
26
  },
27
27
  "files": [
28
28
  "dist",
29
- "!dist/**/*.test.js"
29
+ "!dist/**/*.test.js",
30
+ "skills"
30
31
  ],
31
32
  "bin": {
32
33
  "gobi": "./dist/index.js"
@@ -0,0 +1,204 @@
1
+ ---
2
+ name: gobi-cli
3
+ description: >-
4
+ Manage the Gobi collaborative knowledge platform from the command line.
5
+ Gobi space is the user's main channel for social interactions and engaging with
6
+ the outside world — checking what's happening, reading and writing threads,
7
+ and collaborating with others.
8
+ Use when the user wants to interact with Gobi spaces, vaults, brains, threads,
9
+ sessions, or brain updates.
10
+ allowed-tools: Bash(gobi:*)
11
+ metadata:
12
+ author: gobi-ai
13
+ version: "0.6.1"
14
+ ---
15
+
16
+ # gobi-cli
17
+
18
+ A CLI client for the Gobi collaborative knowledge platform (v0.6.1).
19
+
20
+ ## Prerequisites
21
+
22
+ Verify the CLI is installed:
23
+
24
+ ```bash
25
+ gobi --version
26
+ ```
27
+
28
+ If not installed:
29
+
30
+ ```bash
31
+ npm install -g @gobi-ai/cli
32
+ ```
33
+
34
+ Or via Homebrew:
35
+
36
+ ```bash
37
+ brew tap gobi-ai/tap && brew install gobi
38
+ ```
39
+
40
+ ## Key Concepts
41
+
42
+ - **Space**: A shared space for a group or community. A logged-in user can be a member of one or more spaces. A space contains threads, sessions, brain updates, and connected vaults.
43
+ - **Vault**: A filetree storage of information and knowledge. A local directory becomes a vault when it contains `.gobi/settings.yaml` with a vault slug and a space slug. Each vault is identified by a slug (e.g. `brave-path-zr962w`).
44
+ - **Brain**: Another name for a vault when referring to its AI-searchable knowledge. You can search brains, ask them questions, and publish a `BRAIN.md` document to configure your vault's brain.
45
+
46
+ ## First-Time Setup
47
+
48
+ The CLI requires three setup steps: authentication, vault initialization, and space selection.
49
+
50
+ ### Step 1: Initialize (Login + Vault)
51
+
52
+ ```bash
53
+ gobi init
54
+ ```
55
+
56
+ This is an **interactive** command that:
57
+ 1. Logs in automatically if not already authenticated (opens a browser URL for Google OAuth)
58
+ 2. Prompts the user to select an existing vault or create a new one
59
+ 3. Writes `.gobi/settings.yaml` in the current directory with the chosen vault slug
60
+ 4. Creates a `BRAIN.md` file if one doesn't exist
61
+
62
+ ### Step 2: Select a Space
63
+
64
+ ```bash
65
+ gobi space warp
66
+ ```
67
+
68
+ This is an **interactive** command that prompts the user to select a space from their available spaces, then saves it to `.gobi/settings.yaml`.
69
+
70
+ After both steps, `.gobi/settings.yaml` will contain:
71
+ ```yaml
72
+ vaultSlug: brave-path-zr962w
73
+ selectedSpaceSlug: cmds
74
+ ```
75
+
76
+ ### Standalone Login
77
+
78
+ If the user only needs to log in (without vault setup):
79
+
80
+ ```bash
81
+ gobi auth login
82
+ ```
83
+
84
+ Check auth status anytime:
85
+
86
+ ```bash
87
+ gobi auth status
88
+ ```
89
+
90
+ **Important for agents**: Before running any `space` command, check if `.gobi/settings.yaml` exists in the current directory with both `vaultSlug` and `selectedSpaceSlug`. If the vault is missing, guide the user through `gobi init`. If only the space is missing, guide the user through `gobi space warp`. These commands require user input (interactive prompts), so the agent cannot run them silently.
91
+
92
+ ## Gobi Space — Community Channel
93
+
94
+ `gobi space` is the main interface for interacting with the user's Gobi community. When the user asks about what's happening, what others are discussing, or wants to engage with their community — use `gobi space` commands. Think of it as the user's community feed and communication hub.
95
+
96
+ - When the user wants to explore or catch up on what's happening in their space, invoke `/gobi:space-explore`.
97
+ - When the user wants to share or post learnings from the current session, invoke `/gobi:space-share`.
98
+
99
+ ## Gobi Brain — Knowledge Management
100
+
101
+ `gobi brain` commands manage your vault's brain: search across all spaces, ask brains questions, and publish/unpublish your BRAIN.md.
102
+
103
+ ## Gobi Session — Conversations
104
+
105
+ `gobi session` commands manage your conversations: list, read, reply to, and update sessions.
106
+
107
+ ## Important: JSON Mode
108
+
109
+ For programmatic/agent usage, always pass `--json` as a **global** option (before the subcommand) to get structured JSON output:
110
+
111
+ ```bash
112
+ gobi --json space list-threads
113
+ ```
114
+
115
+ or
116
+
117
+ ```bash
118
+ gobi --json session list
119
+ ```
120
+
121
+ JSON responses have the shape `{ "success": true, "data": ... }` on success or `{ "success": false, "error": "..." }` on failure.
122
+
123
+ ## Space Slug Override
124
+
125
+ `gobi space` commands use the space from `.gobi/settings.yaml`. Override it with a parent-level flag:
126
+
127
+ ```bash
128
+ gobi space --space-slug <slug> list-threads
129
+ ```
130
+
131
+ For `gobi brain list-updates`, you can filter by space with a subcommand option:
132
+
133
+ ```bash
134
+ gobi brain list-updates --space-slug <slug>
135
+ ```
136
+
137
+ Note: `--space-slug` is not available on other `brain` subcommands or on `session` commands.
138
+
139
+ ## Available Commands
140
+
141
+ - `gobi auth` — Authentication commands.
142
+ - `gobi auth login` — Log in to Gobi. Opens a browser URL for Google OAuth, then polls until authentication is complete.
143
+ - `gobi auth status` — Check whether you are currently authenticated with Gobi.
144
+ - `gobi auth logout` — Log out of Gobi and remove stored credentials.
145
+ - `gobi init` — Log in (if needed) and select or create the vault for the current directory.
146
+ - `gobi space` — Space commands (threads, replies).
147
+ - `gobi space list` — List spaces you are a member of.
148
+ - `gobi space warp` — Select the active space. Pass a slug to warp directly, or omit for interactive selection.
149
+ - `gobi space get-thread` — Get a thread and its replies (paginated).
150
+ - `gobi space list-threads` — List threads in a space (paginated).
151
+ - `gobi space create-thread` — Create a thread in a space.
152
+ - `gobi space edit-thread` — Edit a thread. You must be the author.
153
+ - `gobi space delete-thread` — Delete a thread. You must be the author.
154
+ - `gobi space create-reply` — Create a reply to a thread in a space.
155
+ - `gobi space edit-reply` — Edit a reply. You must be the author.
156
+ - `gobi space delete-reply` — Delete a reply. You must be the author.
157
+ - `gobi brain` — Brain commands (search, ask, publish, unpublish, updates).
158
+ - `gobi brain search` — Search public brains by text and semantic similarity.
159
+ - `gobi brain ask` — Ask a brain a question. Creates a targeted session (1:1 conversation).
160
+ - `gobi brain publish` — Upload BRAIN.md to the vault root on webdrive. Triggers post-processing (brain sync, metadata update, Discord notification).
161
+ - `gobi brain unpublish` — Delete BRAIN.md from the vault on webdrive.
162
+ - `gobi brain list-updates` — List recent brain updates. Without --space-slug, lists all updates for you. With --space-slug, lists updates for that space. Use --mine to show only updates by you.
163
+ - `gobi brain post-update` — Post a brain update for a vault.
164
+ - `gobi brain edit-update` — Edit a published brain update. You must be the author.
165
+ - `gobi brain delete-update` — Delete a published brain update. You must be the author.
166
+ - `gobi session` — Session commands (get, list, reply).
167
+ - `gobi session get` — Get a session and its messages (paginated).
168
+ - `gobi session list` — List all sessions you are part of, sorted by most recent activity.
169
+ - `gobi session reply` — Send a human reply to a session you are a member of.
170
+
171
+ ## Reference Documentation
172
+
173
+ - [gobi auth](references/auth.md)
174
+ - [gobi init](references/init.md)
175
+ - [gobi space](references/space.md)
176
+ - [gobi brain](references/brain.md)
177
+ - [gobi session](references/session.md)
178
+
179
+ ## Discovering Options
180
+
181
+ Run `--help` on any command for details:
182
+
183
+ ```bash
184
+ gobi --help
185
+ gobi auth --help
186
+ gobi space --help
187
+ gobi brain --help
188
+ gobi session --help
189
+ ```
190
+
191
+ ## Configuration Files
192
+
193
+ | Path | Description |
194
+ |------|-------------|
195
+ | `~/.gobi/credentials.json` | Stored authentication tokens (auto-managed) |
196
+ | `.gobi/settings.yaml` | Per-project vault and space configuration |
197
+ | `BRAIN.md` | Brain document with YAML frontmatter, published via `gobi brain publish` |
198
+
199
+ ## Environment Variables
200
+
201
+ | Variable | Default | Description |
202
+ |----------|---------|-------------|
203
+ | `GOBI_BASE_URL` | `https://backend.joingobi.com` | API server URL |
204
+ | `GOBI_WEBDRIVE_BASE_URL` | `https://webdrive.joingobi.com` | File storage URL |
@@ -0,0 +1,172 @@
1
+ ---
2
+ name: gobi-cli
3
+ description: >-
4
+ Manage the Gobi collaborative knowledge platform from the command line.
5
+ Gobi space is the user's main channel for social interactions and engaging with
6
+ the outside world — checking what's happening, reading and writing threads,
7
+ and collaborating with others.
8
+ Use when the user wants to interact with Gobi spaces, vaults, brains, threads,
9
+ sessions, or brain updates.
10
+ allowed-tools: Bash(gobi:*)
11
+ metadata:
12
+ author: gobi-ai
13
+ version: "{{VERSION}}"
14
+ ---
15
+
16
+ # gobi-cli
17
+
18
+ A CLI client for the Gobi collaborative knowledge platform (v{{VERSION}}).
19
+
20
+ ## Prerequisites
21
+
22
+ Verify the CLI is installed:
23
+
24
+ ```bash
25
+ gobi --version
26
+ ```
27
+
28
+ If not installed:
29
+
30
+ ```bash
31
+ npm install -g @gobi-ai/cli
32
+ ```
33
+
34
+ Or via Homebrew:
35
+
36
+ ```bash
37
+ brew tap gobi-ai/tap && brew install gobi
38
+ ```
39
+
40
+ ## Key Concepts
41
+
42
+ - **Space**: A shared space for a group or community. A logged-in user can be a member of one or more spaces. A space contains threads, sessions, brain updates, and connected vaults.
43
+ - **Vault**: A filetree storage of information and knowledge. A local directory becomes a vault when it contains `.gobi/settings.yaml` with a vault slug and a space slug. Each vault is identified by a slug (e.g. `brave-path-zr962w`).
44
+ - **Brain**: Another name for a vault when referring to its AI-searchable knowledge. You can search brains, ask them questions, and publish a `BRAIN.md` document to configure your vault's brain.
45
+
46
+ ## First-Time Setup
47
+
48
+ The CLI requires three setup steps: authentication, vault initialization, and space selection.
49
+
50
+ ### Step 1: Initialize (Login + Vault)
51
+
52
+ ```bash
53
+ gobi init
54
+ ```
55
+
56
+ This is an **interactive** command that:
57
+ 1. Logs in automatically if not already authenticated (opens a browser URL for Google OAuth)
58
+ 2. Prompts the user to select an existing vault or create a new one
59
+ 3. Writes `.gobi/settings.yaml` in the current directory with the chosen vault slug
60
+ 4. Creates a `BRAIN.md` file if one doesn't exist
61
+
62
+ ### Step 2: Select a Space
63
+
64
+ ```bash
65
+ gobi space warp
66
+ ```
67
+
68
+ This is an **interactive** command that prompts the user to select a space from their available spaces, then saves it to `.gobi/settings.yaml`.
69
+
70
+ After both steps, `.gobi/settings.yaml` will contain:
71
+ ```yaml
72
+ vaultSlug: brave-path-zr962w
73
+ selectedSpaceSlug: cmds
74
+ ```
75
+
76
+ ### Standalone Login
77
+
78
+ If the user only needs to log in (without vault setup):
79
+
80
+ ```bash
81
+ gobi auth login
82
+ ```
83
+
84
+ Check auth status anytime:
85
+
86
+ ```bash
87
+ gobi auth status
88
+ ```
89
+
90
+ **Important for agents**: Before running any `space` command, check if `.gobi/settings.yaml` exists in the current directory with both `vaultSlug` and `selectedSpaceSlug`. If the vault is missing, guide the user through `gobi init`. If only the space is missing, guide the user through `gobi space warp`. These commands require user input (interactive prompts), so the agent cannot run them silently.
91
+
92
+ ## Gobi Space — Community Channel
93
+
94
+ `gobi space` is the main interface for interacting with the user's Gobi community. When the user asks about what's happening, what others are discussing, or wants to engage with their community — use `gobi space` commands. Think of it as the user's community feed and communication hub.
95
+
96
+ - When the user wants to explore or catch up on what's happening in their space, invoke `/gobi:space-explore`.
97
+ - When the user wants to share or post learnings from the current session, invoke `/gobi:space-share`.
98
+
99
+ ## Gobi Brain — Knowledge Management
100
+
101
+ `gobi brain` commands manage your vault's brain: search across all spaces, ask brains questions, and publish/unpublish your BRAIN.md.
102
+
103
+ ## Gobi Session — Conversations
104
+
105
+ `gobi session` commands manage your conversations: list, read, reply to, and update sessions.
106
+
107
+ ## Important: JSON Mode
108
+
109
+ For programmatic/agent usage, always pass `--json` as a **global** option (before the subcommand) to get structured JSON output:
110
+
111
+ ```bash
112
+ gobi --json space list-threads
113
+ ```
114
+
115
+ or
116
+
117
+ ```bash
118
+ gobi --json session list
119
+ ```
120
+
121
+ JSON responses have the shape `{ "success": true, "data": ... }` on success or `{ "success": false, "error": "..." }` on failure.
122
+
123
+ ## Space Slug Override
124
+
125
+ `gobi space` commands use the space from `.gobi/settings.yaml`. Override it with a parent-level flag:
126
+
127
+ ```bash
128
+ gobi space --space-slug <slug> list-threads
129
+ ```
130
+
131
+ For `gobi brain list-updates`, you can filter by space with a subcommand option:
132
+
133
+ ```bash
134
+ gobi brain list-updates --space-slug <slug>
135
+ ```
136
+
137
+ Note: `--space-slug` is not available on other `brain` subcommands or on `session` commands.
138
+
139
+ ## Available Commands
140
+
141
+ {{COMMANDS}}
142
+
143
+ ## Reference Documentation
144
+
145
+ {{REFERENCE_TOC}}
146
+
147
+ ## Discovering Options
148
+
149
+ Run `--help` on any command for details:
150
+
151
+ ```bash
152
+ gobi --help
153
+ gobi auth --help
154
+ gobi space --help
155
+ gobi brain --help
156
+ gobi session --help
157
+ ```
158
+
159
+ ## Configuration Files
160
+
161
+ | Path | Description |
162
+ |------|-------------|
163
+ | `~/.gobi/credentials.json` | Stored authentication tokens (auto-managed) |
164
+ | `.gobi/settings.yaml` | Per-project vault and space configuration |
165
+ | `BRAIN.md` | Brain document with YAML frontmatter, published via `gobi brain publish` |
166
+
167
+ ## Environment Variables
168
+
169
+ | Variable | Default | Description |
170
+ |----------|---------|-------------|
171
+ | `GOBI_BASE_URL` | `https://backend.joingobi.com` | API server URL |
172
+ | `GOBI_WEBDRIVE_BASE_URL` | `https://webdrive.joingobi.com` | File storage URL |
@@ -0,0 +1,49 @@
1
+ # gobi auth
2
+
3
+ ```
4
+ Usage: gobi auth [options] [command]
5
+
6
+ Authentication commands.
7
+
8
+ Options:
9
+ -h, --help display help for command
10
+
11
+ Commands:
12
+ login Log in to Gobi. Opens a browser URL for Google OAuth, then polls until authentication is complete.
13
+ status Check whether you are currently authenticated with Gobi.
14
+ logout Log out of Gobi and remove stored credentials.
15
+ help [command] display help for command
16
+ ```
17
+
18
+ ## login
19
+
20
+ ```
21
+ Usage: gobi auth login [options]
22
+
23
+ Log in to Gobi. Opens a browser URL for Google OAuth, then polls until authentication is complete.
24
+
25
+ Options:
26
+ -h, --help display help for command
27
+ ```
28
+
29
+ ## status
30
+
31
+ ```
32
+ Usage: gobi auth status [options]
33
+
34
+ Check whether you are currently authenticated with Gobi.
35
+
36
+ Options:
37
+ -h, --help display help for command
38
+ ```
39
+
40
+ ## logout
41
+
42
+ ```
43
+ Usage: gobi auth logout [options]
44
+
45
+ Log out of Gobi and remove stored credentials.
46
+
47
+ Options:
48
+ -h, --help display help for command
49
+ ```
@@ -0,0 +1,126 @@
1
+ # gobi brain
2
+
3
+ ```
4
+ Usage: gobi brain [options] [command]
5
+
6
+ Brain commands (search, ask, publish, unpublish, updates).
7
+
8
+ Options:
9
+ -h, --help display help for command
10
+
11
+ Commands:
12
+ search [options] Search public brains by text and semantic similarity.
13
+ ask [options] Ask a brain a question. Creates a targeted session (1:1 conversation).
14
+ publish Upload BRAIN.md to the vault root on webdrive. Triggers post-processing (brain sync, metadata update, Discord notification).
15
+ unpublish Delete BRAIN.md from the vault on webdrive.
16
+ list-updates [options] List recent brain updates. Without --space-slug, lists all updates for you. With --space-slug, lists updates for that space. Use --mine to show only updates by
17
+ you.
18
+ post-update [options] Post a brain update for a vault.
19
+ edit-update [options] <updateId> Edit a published brain update. You must be the author.
20
+ delete-update <updateId> Delete a published brain update. You must be the author.
21
+ help [command] display help for command
22
+ ```
23
+
24
+ ## search
25
+
26
+ ```
27
+ Usage: gobi brain search [options]
28
+
29
+ Search public brains by text and semantic similarity.
30
+
31
+ Options:
32
+ --query <query> Search query
33
+ -h, --help display help for command
34
+ ```
35
+
36
+ ## ask
37
+
38
+ ```
39
+ Usage: gobi brain ask [options]
40
+
41
+ Ask a brain a question. Creates a targeted session (1:1 conversation).
42
+
43
+ Options:
44
+ --vault-slug <vaultSlug> Slug of the brain/vault to ask
45
+ --question <question> The question to ask (markdown supported)
46
+ --rich-text <richText> Rich-text JSON array (e.g. [{"type":"text","text":"hello"}])
47
+ --mode <mode> Session mode: "auto" or "manual"
48
+ -h, --help display help for command
49
+ ```
50
+
51
+ ## publish
52
+
53
+ ```
54
+ Usage: gobi brain publish [options]
55
+
56
+ Upload BRAIN.md to the vault root on webdrive. Triggers post-processing (brain sync, metadata update, Discord notification).
57
+
58
+ Options:
59
+ -h, --help display help for command
60
+ ```
61
+
62
+ ## unpublish
63
+
64
+ ```
65
+ Usage: gobi brain unpublish [options]
66
+
67
+ Delete BRAIN.md from the vault on webdrive.
68
+
69
+ Options:
70
+ -h, --help display help for command
71
+ ```
72
+
73
+ ## list-updates
74
+
75
+ ```
76
+ Usage: gobi brain list-updates [options]
77
+
78
+ List recent brain updates. Without --space-slug, lists all updates for you. With --space-slug, lists updates for that space. Use --mine to show only updates by you.
79
+
80
+ Options:
81
+ --vault-slug <vaultSlug> Vault slug (overrides .gobi/settings.yaml)
82
+ --space-slug <spaceSlug> List updates for a space
83
+ --mine List only my own brain updates
84
+ --limit <number> Items per page (default: "20")
85
+ --cursor <string> Pagination cursor from previous response
86
+ -h, --help display help for command
87
+ ```
88
+
89
+ ## post-update
90
+
91
+ ```
92
+ Usage: gobi brain post-update [options]
93
+
94
+ Post a brain update for a vault.
95
+
96
+ Options:
97
+ --vault-slug <vaultSlug> Vault slug (overrides .gobi/settings.yaml)
98
+ --title <title> Title of the update
99
+ --content <content> Update content (markdown supported)
100
+ --auto-attachments Upload wiki-linked [[files]] to webdrive before posting
101
+ -h, --help display help for command
102
+ ```
103
+
104
+ ## edit-update
105
+
106
+ ```
107
+ Usage: gobi brain edit-update [options] <updateId>
108
+
109
+ Edit a published brain update. You must be the author.
110
+
111
+ Options:
112
+ --title <title> New title for the update
113
+ --content <content> New content for the update (markdown supported)
114
+ -h, --help display help for command
115
+ ```
116
+
117
+ ## delete-update
118
+
119
+ ```
120
+ Usage: gobi brain delete-update [options] <updateId>
121
+
122
+ Delete a published brain update. You must be the author.
123
+
124
+ Options:
125
+ -h, --help display help for command
126
+ ```
@@ -0,0 +1,10 @@
1
+ # gobi init
2
+
3
+ ```
4
+ Usage: gobi init [options]
5
+
6
+ Log in (if needed) and select or create the vault for the current directory.
7
+
8
+ Options:
9
+ -h, --help display help for command
10
+ ```
@@ -0,0 +1,55 @@
1
+ # gobi session
2
+
3
+ ```
4
+ Usage: gobi session [options] [command]
5
+
6
+ Session commands (get, list, reply).
7
+
8
+ Options:
9
+ -h, --help display help for command
10
+
11
+ Commands:
12
+ get [options] <sessionId> Get a session and its messages (paginated).
13
+ list [options] List all sessions you are part of, sorted by most recent activity.
14
+ reply [options] <sessionId> Send a human reply to a session you are a member of.
15
+ help [command] display help for command
16
+ ```
17
+
18
+ ## get
19
+
20
+ ```
21
+ Usage: gobi session get [options] <sessionId>
22
+
23
+ Get a session and its messages (paginated).
24
+
25
+ Options:
26
+ --limit <number> Messages per page (default: "20")
27
+ --cursor <string> Pagination cursor from previous response
28
+ -h, --help display help for command
29
+ ```
30
+
31
+ ## list
32
+
33
+ ```
34
+ Usage: gobi session list [options]
35
+
36
+ List all sessions you are part of, sorted by most recent activity.
37
+
38
+ Options:
39
+ --limit <number> Items per page (default: "20")
40
+ --cursor <string> Pagination cursor from previous response
41
+ -h, --help display help for command
42
+ ```
43
+
44
+ ## reply
45
+
46
+ ```
47
+ Usage: gobi session reply [options] <sessionId>
48
+
49
+ Send a human reply to a session you are a member of.
50
+
51
+ Options:
52
+ --content <content> Reply content (markdown supported)
53
+ --rich-text <richText> Rich-text JSON array (e.g. [{"type":"text","text":"hello"}])
54
+ -h, --help display help for command
55
+ ```
@@ -0,0 +1,146 @@
1
+ # gobi space
2
+
3
+ ```
4
+ Usage: gobi space [options] [command]
5
+
6
+ Space commands (threads, replies).
7
+
8
+ Options:
9
+ --space-slug <slug> Space slug (overrides .gobi/settings.yaml)
10
+ -h, --help display help for command
11
+
12
+ Commands:
13
+ list List spaces you are a member of.
14
+ warp [spaceSlug] Select the active space. Pass a slug to warp directly, or omit for interactive selection.
15
+ get-thread [options] <threadId> Get a thread and its replies (paginated).
16
+ list-threads [options] List threads in a space (paginated).
17
+ create-thread [options] Create a thread in a space.
18
+ edit-thread [options] <threadId> Edit a thread. You must be the author.
19
+ delete-thread <threadId> Delete a thread. You must be the author.
20
+ create-reply [options] <threadId> Create a reply to a thread in a space.
21
+ edit-reply [options] <replyId> Edit a reply. You must be the author.
22
+ delete-reply <replyId> Delete a reply. You must be the author.
23
+ help [command] display help for command
24
+ ```
25
+
26
+ ## list
27
+
28
+ ```
29
+ Usage: gobi space list [options]
30
+
31
+ List spaces you are a member of.
32
+
33
+ Options:
34
+ -h, --help display help for command
35
+ ```
36
+
37
+ ## warp
38
+
39
+ ```
40
+ Usage: gobi space warp [options] [spaceSlug]
41
+
42
+ Select the active space. Pass a slug to warp directly, or omit for interactive selection.
43
+
44
+ Options:
45
+ -h, --help display help for command
46
+ ```
47
+
48
+ ## get-thread
49
+
50
+ ```
51
+ Usage: gobi space get-thread [options] <threadId>
52
+
53
+ Get a thread and its replies (paginated).
54
+
55
+ Options:
56
+ --limit <number> Replies per page (default: "20")
57
+ --cursor <string> Pagination cursor from previous response
58
+ -h, --help display help for command
59
+ ```
60
+
61
+ ## list-threads
62
+
63
+ ```
64
+ Usage: gobi space list-threads [options]
65
+
66
+ List threads in a space (paginated).
67
+
68
+ Options:
69
+ --limit <number> Items per page (default: "20")
70
+ --cursor <string> Pagination cursor from previous response
71
+ -h, --help display help for command
72
+ ```
73
+
74
+ ## create-thread
75
+
76
+ ```
77
+ Usage: gobi space create-thread [options]
78
+
79
+ Create a thread in a space.
80
+
81
+ Options:
82
+ --title <title> Title of the thread
83
+ --content <content> Thread content (markdown supported)
84
+ --auto-attachments Upload wiki-linked [[files]] to webdrive before posting
85
+ --vault-slug <vaultSlug> Vault slug for attachment uploads (overrides .gobi/settings.yaml)
86
+ -h, --help display help for command
87
+ ```
88
+
89
+ ## edit-thread
90
+
91
+ ```
92
+ Usage: gobi space edit-thread [options] <threadId>
93
+
94
+ Edit a thread. You must be the author.
95
+
96
+ Options:
97
+ --title <title> New title for the thread
98
+ --content <content> New content for the thread (markdown supported)
99
+ -h, --help display help for command
100
+ ```
101
+
102
+ ## delete-thread
103
+
104
+ ```
105
+ Usage: gobi space delete-thread [options] <threadId>
106
+
107
+ Delete a thread. You must be the author.
108
+
109
+ Options:
110
+ -h, --help display help for command
111
+ ```
112
+
113
+ ## create-reply
114
+
115
+ ```
116
+ Usage: gobi space create-reply [options] <threadId>
117
+
118
+ Create a reply to a thread in a space.
119
+
120
+ Options:
121
+ --content <content> Reply content (markdown supported)
122
+ -h, --help display help for command
123
+ ```
124
+
125
+ ## edit-reply
126
+
127
+ ```
128
+ Usage: gobi space edit-reply [options] <replyId>
129
+
130
+ Edit a reply. You must be the author.
131
+
132
+ Options:
133
+ --content <content> New content for the reply (markdown supported)
134
+ -h, --help display help for command
135
+ ```
136
+
137
+ ## delete-reply
138
+
139
+ ```
140
+ Usage: gobi space delete-reply [options] <replyId>
141
+
142
+ Delete a reply. You must be the author.
143
+
144
+ Options:
145
+ -h, --help display help for command
146
+ ```
@@ -0,0 +1,199 @@
1
+ import { execSync } from "node:child_process";
2
+ import {
3
+ readFileSync,
4
+ writeFileSync,
5
+ mkdirSync,
6
+ existsSync,
7
+ readdirSync,
8
+ unlinkSync,
9
+ } from "node:fs";
10
+ import { join, dirname } from "node:path";
11
+ import { fileURLToPath } from "node:url";
12
+ import { createRequire } from "node:module";
13
+
14
+ const __filename = fileURLToPath(import.meta.url);
15
+ const __dirname = dirname(__filename);
16
+ const SKILL_DIR = join(__dirname, "..");
17
+ const REFERENCES_DIR = join(SKILL_DIR, "references");
18
+ const TEMPLATE_PATH = join(SKILL_DIR, "SKILL.template.md");
19
+ const OUTPUT_PATH = join(SKILL_DIR, "SKILL.md");
20
+ const PROJECT_ROOT = join(SKILL_DIR, "..", "..");
21
+ // Read version from package.json
22
+ const require = createRequire(import.meta.url);
23
+ const { version } = require(join(PROJECT_ROOT, "package.json")) as {
24
+ version: string;
25
+ };
26
+
27
+ // Use the built dist/index.js so this works in CI without global install
28
+ const GOBI_BIN = join(PROJECT_ROOT, "dist", "index.js");
29
+
30
+ if (!existsSync(GOBI_BIN)) {
31
+ console.error(
32
+ "Error: dist/index.js not found. Run 'npm run build' first."
33
+ );
34
+ process.exit(1);
35
+ }
36
+
37
+ // ---------------------------------------------------------------------------
38
+ // Helpers
39
+ // ---------------------------------------------------------------------------
40
+
41
+ function runHelp(args: string[]): string {
42
+ const cmd = `node ${GOBI_BIN} ${args.join(" ")} --help`;
43
+ return execSync(cmd, {
44
+ encoding: "utf-8",
45
+ env: { ...process.env, NO_COLOR: "1" },
46
+ timeout: 10_000,
47
+ }).trim();
48
+ }
49
+
50
+ interface CommandInfo {
51
+ name: string;
52
+ description: string;
53
+ }
54
+
55
+ /**
56
+ * Parse Commander.js help output to extract the Commands section.
57
+ * Commander format:
58
+ * Commands:
59
+ * name [options] description
60
+ * help [command] display help for command
61
+ */
62
+ function parseCommands(helpText: string): CommandInfo[] {
63
+ const commands: CommandInfo[] = [];
64
+ const lines = helpText.split("\n");
65
+ let inCommands = false;
66
+
67
+ for (const line of lines) {
68
+ if (line.trim() === "Commands:") {
69
+ inCommands = true;
70
+ continue;
71
+ }
72
+ if (inCommands) {
73
+ // Match: leading whitespace, command name, possible args, 2+ spaces, description
74
+ const match = line.match(/^\s{2,}(\S+)\s+.*?\s{2,}(.+)$/);
75
+ if (match) {
76
+ const [, name, desc] = match;
77
+ if (name !== "help") {
78
+ commands.push({ name, description: desc.trim() });
79
+ }
80
+ } else if (line.trim() === "") {
81
+ break;
82
+ } else if (commands.length > 0 && line.match(/^\s{10,}\S/)) {
83
+ // Continuation line of the previous command's wrapped description
84
+ commands[commands.length - 1].description += " " + line.trim();
85
+ }
86
+ }
87
+ }
88
+
89
+ return commands;
90
+ }
91
+
92
+ // ---------------------------------------------------------------------------
93
+ // Generate reference docs
94
+ // ---------------------------------------------------------------------------
95
+
96
+ function generateReferenceDoc(
97
+ commandPath: string[],
98
+ helpText: string,
99
+ subcommands: { name: string; helpText: string }[]
100
+ ): string {
101
+ const lines: string[] = [];
102
+ const fullCommand = ["gobi", ...commandPath].join(" ");
103
+
104
+ lines.push(`# ${fullCommand}`);
105
+ lines.push("");
106
+ lines.push("```");
107
+ lines.push(helpText);
108
+ lines.push("```");
109
+
110
+ for (const sub of subcommands) {
111
+ lines.push("");
112
+ lines.push(`## ${sub.name}`);
113
+ lines.push("");
114
+ lines.push("```");
115
+ lines.push(sub.helpText);
116
+ lines.push("```");
117
+ }
118
+
119
+ return lines.join("\n") + "\n";
120
+ }
121
+
122
+ // ---------------------------------------------------------------------------
123
+ // Main
124
+ // ---------------------------------------------------------------------------
125
+
126
+ // Ensure references directory exists
127
+ if (!existsSync(REFERENCES_DIR)) {
128
+ mkdirSync(REFERENCES_DIR, { recursive: true });
129
+ }
130
+
131
+ // Clean existing generated reference files
132
+ for (const file of readdirSync(REFERENCES_DIR)) {
133
+ if (file.endsWith(".md")) {
134
+ unlinkSync(join(REFERENCES_DIR, file));
135
+ }
136
+ }
137
+
138
+ // 1. Get top-level commands
139
+ const topHelp = runHelp([]);
140
+ const topCommands = parseCommands(topHelp);
141
+
142
+ // 2. Generate reference files for each command group
143
+ const referenceFiles: { name: string; path: string; title: string }[] = [];
144
+ const commandLines: string[] = [];
145
+
146
+ for (const cmd of topCommands) {
147
+ let cmdHelp: string;
148
+ try {
149
+ cmdHelp = runHelp([cmd.name]);
150
+ } catch {
151
+ // Leaf command with no subcommands — use top-level help
152
+ cmdHelp = `${cmd.description}`;
153
+ }
154
+
155
+ const subCommands = parseCommands(cmdHelp);
156
+
157
+ // Build command listing
158
+ commandLines.push(`- \`gobi ${cmd.name}\` — ${cmd.description}`);
159
+
160
+ const subHelpTexts: { name: string; helpText: string }[] = [];
161
+ for (const sub of subCommands) {
162
+ try {
163
+ const subHelp = runHelp([cmd.name, sub.name]);
164
+ subHelpTexts.push({ name: sub.name, helpText: subHelp });
165
+ commandLines.push(
166
+ ` - \`gobi ${cmd.name} ${sub.name}\` — ${sub.description}`
167
+ );
168
+ } catch {
169
+ // Skip commands that fail (e.g. interactive-only)
170
+ }
171
+ }
172
+
173
+ const refContent = generateReferenceDoc([cmd.name], cmdHelp, subHelpTexts);
174
+ const refFile = `${cmd.name}.md`;
175
+ writeFileSync(join(REFERENCES_DIR, refFile), refContent);
176
+ referenceFiles.push({
177
+ name: refFile,
178
+ path: `references/${refFile}`,
179
+ title: `gobi ${cmd.name}`,
180
+ });
181
+ }
182
+
183
+ // 3. Build placeholders
184
+ const COMMANDS = commandLines.join("\n");
185
+ const REFERENCE_TOC = referenceFiles
186
+ .map((ref) => `- [${ref.title}](${ref.path})`)
187
+ .join("\n");
188
+
189
+ // 4. Read template and fill placeholders
190
+ let template = readFileSync(TEMPLATE_PATH, "utf-8");
191
+ template = template.replace(/\{\{VERSION\}\}/g, version);
192
+ template = template.replace("{{COMMANDS}}", COMMANDS);
193
+ template = template.replace("{{REFERENCE_TOC}}", REFERENCE_TOC);
194
+
195
+ // 5. Write generated SKILL.md
196
+ writeFileSync(OUTPUT_PATH, template);
197
+
198
+ console.log(`Generated SKILL.md (v${version})`);
199
+ console.log(`Generated ${referenceFiles.length} reference files`);