@gobi-ai/cli 1.3.3 → 1.3.5

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.
@@ -4,12 +4,12 @@
4
4
  "name": "gobi-ai"
5
5
  },
6
6
  "description": "Claude Code plugin for the Gobi collaborative knowledge platform CLI",
7
- "version": "1.3.3",
7
+ "version": "1.3.5",
8
8
  "plugins": [
9
9
  {
10
10
  "name": "gobi",
11
11
  "description": "Manage the Gobi collaborative knowledge platform from the command line. Search and ask brains, publish brain documents, create threads, manage sessions, generate images and videos.",
12
- "version": "1.3.3",
12
+ "version": "1.3.5",
13
13
  "author": {
14
14
  "name": "gobi-ai"
15
15
  },
@@ -21,6 +21,7 @@
21
21
  "./skills/gobi-brain",
22
22
  "./skills/gobi-feed",
23
23
  "./skills/gobi-notes",
24
+ "./skills/gobi-proposal",
24
25
  "./skills/gobi-media",
25
26
  "./skills/gobi-sense",
26
27
  "./skills/gobi-homepage"
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "gobi",
3
3
  "description": "Manage the Gobi collaborative knowledge platform from the command line",
4
- "version": "1.3.3",
4
+ "version": "1.3.5",
5
5
  "author": {
6
6
  "name": "gobi-ai"
7
7
  },
@@ -12,6 +12,7 @@
12
12
  "./skills/gobi-brain",
13
13
  "./skills/gobi-feed",
14
14
  "./skills/gobi-notes",
15
+ "./skills/gobi-proposal",
15
16
  "./skills/gobi-media",
16
17
  "./skills/gobi-sense",
17
18
  "./skills/gobi-homepage"
package/README.md CHANGED
@@ -78,15 +78,15 @@ Public brains are accessible at `https://gobispace.com/@{vaultSlug}`.
78
78
 
79
79
  `brain ask` also accepts `--rich-text <json>` (mutually exclusive with `--question`) and `--mode <auto|manual>`.
80
80
 
81
- ### Brain Updates
81
+ ### Spaces
82
+
83
+ > Space and member administration (creating spaces, inviting/approving members, joining/leaving) is web-UI only and not available in the CLI.
82
84
 
83
85
  | Command | Description |
84
86
  |---------|-------------|
85
- | `gobi brain post-update --title <t> --content <c>` | Post a brain update |
86
- | `gobi brain edit-update <id> [--title <t>] [--content <c>]` | Edit a brain update (at least one required) |
87
- | `gobi brain delete-update <id>` | Delete a brain update |
88
-
89
- `post-update` and `edit-update` accept `--auto-attachments` to upload wiki-linked `[[files]]` before posting.
87
+ | `gobi space get [spaceSlug]` | Show space details (uses current space if slug omitted) |
88
+ | `gobi space messages` | Unified message feed (threads + replies, newest first) |
89
+ | `gobi space ancestors <threadId>` | Walk a thread/reply's lineage from root → immediate parent |
90
90
 
91
91
  ### Feed
92
92
 
@@ -102,6 +102,8 @@ Public brains are accessible at `https://gobispace.com/@{vaultSlug}`.
102
102
 
103
103
  ### Threads
104
104
 
105
+ > **Migration note:** Brain-update commands have been removed. To post user-level content, use `gobi global create-thread` (global space) or `gobi space create-thread` (a specific space).
106
+
105
107
  | Command | Description |
106
108
  |---------|-------------|
107
109
  | `gobi space list-threads` | List threads in the current space |
@@ -118,6 +120,18 @@ Public brains are accessible at `https://gobispace.com/@{vaultSlug}`.
118
120
  | `gobi space edit-reply <replyId> --content <c>` | Edit a reply |
119
121
  | `gobi space delete-reply <replyId>` | Delete a reply |
120
122
 
123
+ ### Global thread space
124
+
125
+ The global thread space is a slugless message feed visible across all spaces.
126
+
127
+ | Command | Description |
128
+ |---------|-------------|
129
+ | `gobi global messages` | List the global unified message feed (newest first) |
130
+ | `gobi global get-thread <id>` | Get a global thread and its direct replies |
131
+ | `gobi global ancestors <id>` | Walk a global thread/reply's lineage |
132
+ | `gobi global create-thread [--title <t>] (--content <c> \| --rich-text <json>)` | Create a thread in the global space |
133
+ | `gobi global reply <threadId> (--content <c> \| --rich-text <json>)` | Reply to a global thread |
134
+
121
135
  ### Sessions
122
136
 
123
137
  | Command | Description |
@@ -150,6 +164,22 @@ Times are ISO 8601 UTC (e.g. `2026-03-20T00:00:00Z`).
150
164
  `notes list` and `notes create` accept `--timezone <iana>` (default: system timezone) for day boundaries.
151
165
  `notes list` also accepts `--limit`/`--cursor` for pagination.
152
166
 
167
+ ### Proposals
168
+
169
+ Proposals are authored by your agent during chat (or by external agents using `gobi proposal add` as their tool layer). The top 5 pending proposals (lowest priority first) feed the agent's system prompt every turn.
170
+
171
+ | Command | Description |
172
+ |---------|-------------|
173
+ | `gobi proposal list [--limit N]` | List proposals (priority ASC, then newest first) |
174
+ | `gobi proposal get <id>` | Show one proposal with its history |
175
+ | `gobi proposal add <content> [--session <id>] [--priority N]` | Add a proposal (use `-` for stdin) |
176
+ | `gobi proposal edit <id> <content>` | Replace content; bumps revision |
177
+ | `gobi proposal delete <id>` | Delete a proposal |
178
+ | `gobi proposal prioritize <id> <priority>` | Set priority (lower = higher) |
179
+ | `gobi proposal accept <id>` | Accept; posts "Accept your proposal X" into the originating session |
180
+ | `gobi proposal reject <id>` | Reject; posts "Reject your proposal X" into the originating session |
181
+ | `gobi proposal revise <id> <comment>` | Ask the agent to revise; posts the comment into the session |
182
+
153
183
  ### Sync
154
184
 
155
185
  | Command | Description |
@@ -175,7 +205,7 @@ Times are ISO 8601 UTC (e.g. `2026-03-20T00:00:00Z`).
175
205
  |--------|-------|-------------|
176
206
  | `--json` | All commands | Output results as JSON |
177
207
  | `--space-slug <slug>` | `space` commands | Override the default space (from `.gobi/settings.yaml`) |
178
- | `--vault-slug <slug>` | Per-command | Override the default vault; available on `brain post-update`, `brain edit-update`, `space create-thread`, `space edit-thread`, `space edit-reply` |
208
+ | `--vault-slug <slug>` | Per-command | Override the default vault; available on `space create-thread`, `space edit-thread`, `space edit-reply` |
179
209
 
180
210
  ## Configuration
181
211
 
@@ -1,15 +1,14 @@
1
1
  import { existsSync, readFileSync } from "fs";
2
2
  import { join } from "path";
3
- import { apiGet, apiPost, apiPatch, apiDelete } from "../client.js";
3
+ import { apiGet, apiPost } from "../client.js";
4
4
  import { WEBDRIVE_BASE_URL } from "../constants.js";
5
5
  import { getValidToken } from "../auth/manager.js";
6
6
  import { getVaultSlug } from "./init.js";
7
- import { isJsonMode, jsonOut, resolveVaultSlug, unwrapResp } from "./utils.js";
8
- import { extractWikiLinks, uploadAttachments } from "../attachments.js";
7
+ import { isJsonMode, jsonOut, unwrapResp } from "./utils.js";
9
8
  export function registerBrainCommand(program) {
10
9
  const brain = program
11
10
  .command("brain")
12
- .description("Brain commands (search, ask, publish, unpublish, updates).");
11
+ .description("Brain commands (search, ask, publish, unpublish).");
13
12
  // ── Search ──
14
13
  brain
15
14
  .command("search")
@@ -139,78 +138,4 @@ export function registerBrainCommand(program) {
139
138
  }
140
139
  console.log(`Deleted BRAIN.md from vault "${vaultId}"`);
141
140
  });
142
- // ── Updates (post, edit, delete) ──
143
- brain
144
- .command("post-update")
145
- .description("Post a brain update for a vault.")
146
- .option("--vault-slug <vaultSlug>", "Vault slug (overrides .gobi/settings.yaml)")
147
- .requiredOption("--title <title>", "Title of the update")
148
- .requiredOption("--content <content>", "Update content (markdown supported)")
149
- .option("--auto-attachments", "Upload wiki-linked [[files]] to webdrive before posting")
150
- .action(async (opts) => {
151
- const vaultSlug = resolveVaultSlug(opts);
152
- if (opts.autoAttachments) {
153
- const token = await getValidToken();
154
- const links = extractWikiLinks(opts.content);
155
- await uploadAttachments(vaultSlug, links, token, { addToSyncfiles: true });
156
- }
157
- const resp = (await apiPost(`/brain-updates/vault/${vaultSlug}`, {
158
- title: opts.title,
159
- content: opts.content,
160
- }));
161
- const u = unwrapResp(resp);
162
- if (isJsonMode(brain)) {
163
- jsonOut(u);
164
- return;
165
- }
166
- console.log(`Brain update posted!\n` +
167
- ` ID: ${u.id}\n` +
168
- ` Title: ${u.title}\n` +
169
- ` Vault: ${u.vaultSlug || vaultSlug}\n` +
170
- ` Created: ${u.createdAt}`);
171
- });
172
- brain
173
- .command("edit-update <updateId>")
174
- .description("Edit a published brain update. You must be the author.")
175
- .option("--title <title>", "New title for the update")
176
- .option("--content <content>", "New content for the update (markdown supported)")
177
- .option("--vault-slug <vaultSlug>", "Vault slug for attachment uploads (overrides .gobi/settings.yaml)")
178
- .option("--auto-attachments", "Upload wiki-linked [[files]] to webdrive before editing")
179
- .action(async (updateId, opts) => {
180
- if (!opts.title && !opts.content) {
181
- throw new Error("Provide at least --title or --content to update.");
182
- }
183
- if (opts.autoAttachments && opts.content) {
184
- const vaultSlug = resolveVaultSlug(opts);
185
- const token = await getValidToken();
186
- const links = extractWikiLinks(opts.content);
187
- await uploadAttachments(vaultSlug, links, token, { addToSyncfiles: true });
188
- }
189
- const body = {};
190
- if (opts.title != null)
191
- body.title = opts.title;
192
- if (opts.content != null)
193
- body.content = opts.content;
194
- const resp = (await apiPatch(`/brain-updates/${updateId}`, body));
195
- const u = unwrapResp(resp);
196
- if (isJsonMode(brain)) {
197
- jsonOut(u);
198
- return;
199
- }
200
- console.log(`Brain update edited!\n` +
201
- ` ID: ${u.id}\n` +
202
- ` Title: ${u.title}\n` +
203
- ` Updated: ${u.updatedAt}`);
204
- });
205
- brain
206
- .command("delete-update <updateId>")
207
- .description("Delete a published brain update. You must be the author.")
208
- .action(async (updateId) => {
209
- await apiDelete(`/brain-updates/${updateId}`);
210
- if (isJsonMode(brain)) {
211
- jsonOut({ id: updateId });
212
- return;
213
- }
214
- console.log(`Brain update ${updateId} deleted.`);
215
- });
216
141
  }
@@ -0,0 +1,203 @@
1
+ import { readFileSync } from "fs";
2
+ import { apiGet, apiPost } from "../client.js";
3
+ import { isJsonMode, jsonOut, unwrapResp } from "./utils.js";
4
+ function readContent(value) {
5
+ if (value === "-")
6
+ return readFileSync("/dev/stdin", "utf8");
7
+ return value;
8
+ }
9
+ function formatMessageLine(m) {
10
+ const isReply = m.parentThreadId != null;
11
+ const id = `[${isReply ? "r" : "t"}:${m.id}]`;
12
+ const kind = isReply ? "reply " : "thread";
13
+ const author = m.author?.name ||
14
+ `User ${m.authorId ?? "?"}`;
15
+ let label;
16
+ if (isReply) {
17
+ const text = m.content || "";
18
+ label = text.length > 80 ? text.slice(0, 80) + "…" : text;
19
+ label = label.replace(/\s+/g, " ").trim();
20
+ }
21
+ else {
22
+ label = m.title || m.content || "";
23
+ }
24
+ return `${id} ${kind} ${author} "${label}" ${m.createdAt}`;
25
+ }
26
+ export function registerGlobalCommand(program) {
27
+ const global = program
28
+ .command("global")
29
+ .description("Global thread space commands (no slug; visible across all spaces).");
30
+ // ── Messages (unified feed) ──
31
+ global
32
+ .command("messages")
33
+ .description("List the global unified message feed (threads and replies, newest first).")
34
+ .option("--limit <number>", "Items per page", "20")
35
+ .option("--cursor <string>", "Pagination cursor from previous response")
36
+ .action(async (opts) => {
37
+ const params = {
38
+ limit: parseInt(opts.limit, 10),
39
+ };
40
+ if (opts.cursor)
41
+ params.cursor = opts.cursor;
42
+ const resp = (await apiGet(`/global/messages`, params));
43
+ if (isJsonMode(global)) {
44
+ jsonOut({
45
+ items: resp.data || [],
46
+ pagination: resp.pagination || {},
47
+ });
48
+ return;
49
+ }
50
+ const items = (resp.data || []);
51
+ const pagination = (resp.pagination || {});
52
+ if (!items.length) {
53
+ console.log("No messages found.");
54
+ return;
55
+ }
56
+ const lines = items.map(formatMessageLine);
57
+ const footer = pagination.hasMore ? `\n Next cursor: ${pagination.nextCursor}` : "";
58
+ console.log(`Global messages (${items.length} items, newest first):\n` + lines.join("\n") + footer);
59
+ });
60
+ // ── Get thread ──
61
+ global
62
+ .command("get-thread <threadId>")
63
+ .description("Get a global thread and its direct replies (paginated).")
64
+ .option("--limit <number>", "Replies per page", "20")
65
+ .option("--cursor <string>", "Pagination cursor from previous response")
66
+ .action(async (threadId, opts) => {
67
+ const params = {
68
+ limit: parseInt(opts.limit, 10),
69
+ };
70
+ if (opts.cursor)
71
+ params.cursor = opts.cursor;
72
+ const resp = (await apiGet(`/global/threads/${threadId}`, params));
73
+ const data = unwrapResp(resp);
74
+ const pagination = (resp.pagination || {});
75
+ if (isJsonMode(global)) {
76
+ jsonOut({ ...data, pagination });
77
+ return;
78
+ }
79
+ const thread = (data.thread || data);
80
+ const replies = (data.items || []);
81
+ const author = thread.author?.name ||
82
+ `User ${thread.authorId}`;
83
+ const replyLines = [];
84
+ for (const r of replies) {
85
+ const rAuthor = r.author?.name ||
86
+ `User ${r.authorId}`;
87
+ const text = r.content || "";
88
+ const truncated = text.length > 200 ? text.slice(0, 200) + "…" : text;
89
+ replyLines.push(` - ${rAuthor}: ${truncated} (${r.createdAt})`);
90
+ }
91
+ const isReply = thread.parentThreadId != null;
92
+ const heading = isReply
93
+ ? `Reply [r:${thread.id}]`
94
+ : `Thread: ${thread.title || "(no title)"}`;
95
+ const output = [
96
+ heading,
97
+ `By: ${author} on ${thread.createdAt}`,
98
+ "",
99
+ thread.content,
100
+ "",
101
+ `Replies (${replies.length} items):`,
102
+ ...replyLines,
103
+ ...(pagination.hasMore ? [` Next cursor: ${pagination.nextCursor}`] : []),
104
+ ].join("\n");
105
+ console.log(output);
106
+ });
107
+ // ── Ancestors ──
108
+ global
109
+ .command("ancestors <threadId>")
110
+ .description("Show the ancestor lineage of a global thread or reply (root → immediate parent).")
111
+ .action(async (threadId) => {
112
+ const resp = (await apiGet(`/global/threads/${threadId}/ancestors`));
113
+ const data = unwrapResp(resp);
114
+ const ancestors = (data.ancestors || []);
115
+ if (isJsonMode(global)) {
116
+ jsonOut({ ancestors });
117
+ return;
118
+ }
119
+ if (!ancestors.length) {
120
+ console.log("No ancestors (this is a root thread).");
121
+ return;
122
+ }
123
+ const lines = [];
124
+ ancestors.forEach((a, i) => {
125
+ lines.push(`${i + 1}. ${formatMessageLine(a)}`);
126
+ });
127
+ console.log(`Ancestors (${ancestors.length} items, root first):\n` + lines.join("\n"));
128
+ });
129
+ // ── Create thread ──
130
+ global
131
+ .command("create-thread")
132
+ .description("Create a thread in the global space.")
133
+ .option("--title <title>", "Title of the thread")
134
+ .option("--content <content>", "Thread content (markdown supported, use \"-\" for stdin)")
135
+ .option("--rich-text <richText>", "Rich-text JSON array (mutually exclusive with --content)")
136
+ .action(async (opts) => {
137
+ if (!opts.content && !opts.richText) {
138
+ throw new Error("Provide either --content or --rich-text.");
139
+ }
140
+ if (opts.content && opts.richText) {
141
+ throw new Error("--content and --rich-text are mutually exclusive.");
142
+ }
143
+ const body = {};
144
+ if (opts.title != null)
145
+ body.title = opts.title;
146
+ if (opts.content != null)
147
+ body.content = readContent(opts.content);
148
+ if (opts.richText != null) {
149
+ let parsed;
150
+ try {
151
+ parsed = JSON.parse(opts.richText);
152
+ }
153
+ catch {
154
+ throw new Error("Invalid --rich-text JSON.");
155
+ }
156
+ body.richText = parsed;
157
+ }
158
+ const resp = (await apiPost(`/global/threads`, body));
159
+ const thread = unwrapResp(resp);
160
+ if (isJsonMode(global)) {
161
+ jsonOut(thread);
162
+ return;
163
+ }
164
+ console.log(`Global thread created!\n` +
165
+ ` ID: ${thread.id}\n` +
166
+ (thread.title ? ` Title: ${thread.title}\n` : "") +
167
+ ` Created: ${thread.createdAt}`);
168
+ });
169
+ // ── Reply ──
170
+ global
171
+ .command("reply <threadId>")
172
+ .description("Reply to a thread in the global space.")
173
+ .option("--content <content>", "Reply content (markdown supported, use \"-\" for stdin)")
174
+ .option("--rich-text <richText>", "Rich-text JSON array (mutually exclusive with --content)")
175
+ .action(async (threadId, opts) => {
176
+ if (!opts.content && !opts.richText) {
177
+ throw new Error("Provide either --content or --rich-text.");
178
+ }
179
+ if (opts.content && opts.richText) {
180
+ throw new Error("--content and --rich-text are mutually exclusive.");
181
+ }
182
+ const body = {};
183
+ if (opts.content != null)
184
+ body.content = readContent(opts.content);
185
+ if (opts.richText != null) {
186
+ let parsed;
187
+ try {
188
+ parsed = JSON.parse(opts.richText);
189
+ }
190
+ catch {
191
+ throw new Error("Invalid --rich-text JSON.");
192
+ }
193
+ body.richText = parsed;
194
+ }
195
+ const resp = (await apiPost(`/global/threads/${threadId}/replies`, body));
196
+ const reply = unwrapResp(resp);
197
+ if (isJsonMode(global)) {
198
+ jsonOut(reply);
199
+ return;
200
+ }
201
+ console.log(`Reply created!\n ID: ${reply.id}\n Created: ${reply.createdAt}`);
202
+ });
203
+ }
@@ -72,6 +72,26 @@ export function registerProposalCommand(program) {
72
72
  }
73
73
  }
74
74
  });
75
+ // ── Add ──
76
+ proposal
77
+ .command("add <content>")
78
+ .description("Add a proposal directly. Pass '-' to read from stdin.")
79
+ .option("--session <sessionId>", "Originate from a chat session (UUID)")
80
+ .option("--priority <number>", "Priority (lower = higher), default 100")
81
+ .action(async (content, opts) => {
82
+ const body = { content: readContent(content) };
83
+ if (opts.session)
84
+ body.sessionId = opts.session;
85
+ if (opts.priority)
86
+ body.priority = parseInt(opts.priority, 10);
87
+ const resp = (await apiPost("/app/proposals", body));
88
+ const p = unwrapResp(resp);
89
+ if (isJsonMode(proposal)) {
90
+ jsonOut(p);
91
+ return;
92
+ }
93
+ console.log(`Created ${p.proposalId} (priority ${p.priority}).`);
94
+ });
75
95
  // ── Edit content ──
76
96
  proposal
77
97
  .command("edit <proposalId> <content>")
@@ -9,10 +9,27 @@ function readContent(value) {
9
9
  return readFileSync("/dev/stdin", "utf8");
10
10
  return value;
11
11
  }
12
+ function formatMessageLine(m) {
13
+ const isReply = m.parentThreadId != null;
14
+ const id = `[${isReply ? "r" : "t"}:${m.id}]`;
15
+ const kind = isReply ? "reply " : "thread";
16
+ const author = m.author?.name ||
17
+ `User ${m.authorId ?? "?"}`;
18
+ let label;
19
+ if (isReply) {
20
+ const text = m.content || "";
21
+ label = text.length > 80 ? text.slice(0, 80) + "…" : text;
22
+ label = label.replace(/\s+/g, " ").trim();
23
+ }
24
+ else {
25
+ label = m.title || m.content || "";
26
+ }
27
+ return `${id} ${kind} ${author} "${label}" ${m.createdAt}`;
28
+ }
12
29
  export function registerSpaceCommand(program) {
13
30
  const space = program
14
31
  .command("space")
15
- .description("Space commands (threads, replies).")
32
+ .description("Space commands (threads, replies). Space and member admin is web-UI only.")
16
33
  .option("--space-slug <slug>", "Space slug (overrides .gobi/settings.yaml)");
17
34
  // ── List spaces ──
18
35
  space
@@ -36,6 +53,23 @@ export function registerSpaceCommand(program) {
36
53
  }
37
54
  console.log(`Spaces (${items.length}):\n` + lines.join("\n"));
38
55
  });
56
+ // ── Get space ──
57
+ space
58
+ .command("get [spaceSlug]")
59
+ .description("Get details for a space. Pass a slug or omit to use the current space (from .gobi/settings.yaml or --space-slug).")
60
+ .action(async (spaceSlug) => {
61
+ const slug = spaceSlug || resolveSpaceSlug(space);
62
+ const resp = (await apiGet(`/spaces/${slug}`));
63
+ const s = unwrapResp(resp);
64
+ if (isJsonMode(space)) {
65
+ jsonOut(s);
66
+ return;
67
+ }
68
+ const desc = s.description ? `\n Description: ${s.description}` : "";
69
+ console.log(`Space [${s.slug}] ${s.name}${desc}\n` +
70
+ ` ID: ${s.id}\n` +
71
+ ` Created: ${s.createdAt}`);
72
+ });
39
73
  // ── Warp (space selection) ──
40
74
  space
41
75
  .command("warp [spaceSlug]")
@@ -126,6 +160,60 @@ export function registerSpaceCommand(program) {
126
160
  lines.join("\n") +
127
161
  footer);
128
162
  });
163
+ // ── Messages (unified feed) ──
164
+ space
165
+ .command("messages")
166
+ .description("List the unified message feed (threads and replies, newest first) in a space.")
167
+ .option("--limit <number>", "Items per page", "20")
168
+ .option("--cursor <string>", "Pagination cursor from previous response")
169
+ .action(async (opts) => {
170
+ const spaceSlug = resolveSpaceSlug(space);
171
+ const params = {
172
+ limit: parseInt(opts.limit, 10),
173
+ };
174
+ if (opts.cursor)
175
+ params.cursor = opts.cursor;
176
+ const resp = (await apiGet(`/spaces/${spaceSlug}/messages`, params));
177
+ if (isJsonMode(space)) {
178
+ jsonOut({
179
+ items: resp.data || [],
180
+ pagination: resp.pagination || {},
181
+ });
182
+ return;
183
+ }
184
+ const items = (resp.data || []);
185
+ const pagination = (resp.pagination || {});
186
+ if (!items.length) {
187
+ console.log("No messages found.");
188
+ return;
189
+ }
190
+ const lines = items.map(formatMessageLine);
191
+ const footer = pagination.hasMore ? `\n Next cursor: ${pagination.nextCursor}` : "";
192
+ console.log(`Messages (${items.length} items, newest first):\n` + lines.join("\n") + footer);
193
+ });
194
+ // ── Ancestors ──
195
+ space
196
+ .command("ancestors <threadId>")
197
+ .description("Show the ancestor lineage of a thread or reply (root → immediate parent).")
198
+ .action(async (threadId) => {
199
+ const spaceSlug = resolveSpaceSlug(space);
200
+ const resp = (await apiGet(`/spaces/${spaceSlug}/threads/${threadId}/ancestors`));
201
+ const data = unwrapResp(resp);
202
+ const ancestors = (data.ancestors || []);
203
+ if (isJsonMode(space)) {
204
+ jsonOut({ ancestors });
205
+ return;
206
+ }
207
+ if (!ancestors.length) {
208
+ console.log("No ancestors (this is a root thread).");
209
+ return;
210
+ }
211
+ const lines = [];
212
+ ancestors.forEach((a, i) => {
213
+ lines.push(`${i + 1}. ${formatMessageLine(a)}`);
214
+ });
215
+ console.log(`Ancestors (${ancestors.length} items, root first):\n` + lines.join("\n"));
216
+ });
129
217
  // ── Threads (get, list, create, edit, delete) ──
130
218
  space
131
219
  .command("get-thread <threadId>")
package/dist/main.js CHANGED
@@ -5,6 +5,7 @@ import { ApiError, GobiError } from "./errors.js";
5
5
  import { registerAuthCommand } from "./commands/auth.js";
6
6
  import { registerInitCommand, printContext } from "./commands/init.js";
7
7
  import { registerSpaceCommand } from "./commands/space.js";
8
+ import { registerGlobalCommand } from "./commands/global.js";
8
9
  import { registerBrainCommand } from "./commands/brain.js";
9
10
  import { registerFeedCommand } from "./commands/feed.js";
10
11
  import { registerNotesCommand } from "./commands/notes.js";
@@ -35,6 +36,7 @@ export async function cli() {
35
36
  registerAuthCommand(program);
36
37
  registerInitCommand(program);
37
38
  registerSpaceCommand(program);
39
+ registerGlobalCommand(program);
38
40
  registerBrainCommand(program);
39
41
  registerFeedCommand(program);
40
42
  registerNotesCommand(program);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gobi-ai/cli",
3
- "version": "1.3.3",
3
+ "version": "1.3.5",
4
4
  "description": "CLI client for the Gobi collaborative knowledge platform",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -2,20 +2,18 @@
2
2
  name: gobi-brain
3
3
  description: >-
4
4
  Gobi brain commands for knowledge management: search public brains by text
5
- and semantic similarity, ask brains questions, publish/unpublish BRAIN.md,
6
- and manage brain updates (post/edit/delete). Use when the user wants to
7
- search knowledge, ask a brain, publish their brain document, or manage
8
- their brain updates. To browse the global feed of brain updates from
9
- others, see the gobi-feed skill.
5
+ and semantic similarity, ask brains questions, and publish/unpublish
6
+ BRAIN.md. Use when the user wants to search knowledge, ask a brain, or
7
+ publish their brain document.
10
8
  allowed-tools: Bash(gobi:*)
11
9
  metadata:
12
10
  author: gobi-ai
13
- version: "0.8.0"
11
+ version: "0.9.13"
14
12
  ---
15
13
 
16
14
  # gobi-brain
17
15
 
18
- Gobi brain commands for knowledge management (v0.8.0).
16
+ Gobi brain commands for knowledge management (v0.9.13).
19
17
 
20
18
  Requires gobi-cli installed and authenticated. See gobi-core skill for setup.
21
19
 
@@ -23,6 +21,8 @@ Requires gobi-cli installed and authenticated. See gobi-core skill for setup.
23
21
 
24
22
  `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}`.
25
23
 
24
+ > **Brain updates have moved to threads.** To post user-level content, use `gobi global create-thread` (global space) or `gobi space create-thread` (a specific space). See the **gobi-space** skill.
25
+
26
26
  ## Important: JSON Mode
27
27
 
28
28
  For programmatic/agent usage, always pass `--json` as a **global** option (before the subcommand):
@@ -37,9 +37,6 @@ gobi --json brain search --query "machine learning"
37
37
  - `gobi brain ask` — Ask a brain a question. Creates a targeted session (1:1 conversation).
38
38
  - `gobi brain publish` — Upload BRAIN.md to the vault root on webdrive. Triggers post-processing (brain sync, metadata update, Discord notification).
39
39
  - `gobi brain unpublish` — Delete BRAIN.md from the vault on webdrive.
40
- - `gobi brain post-update` — Post a brain update for a vault.
41
- - `gobi brain edit-update` — Edit a published brain update. You must be the author.
42
- - `gobi brain delete-update` — Delete a published brain update. You must be the author.
43
40
 
44
41
  ## BRAIN.md Frontmatter Reference
45
42
 
@@ -3,20 +3,17 @@
3
3
  ```
4
4
  Usage: gobi brain [options] [command]
5
5
 
6
- Brain commands (search, ask, publish, unpublish, updates).
6
+ Brain commands (search, ask, publish, unpublish).
7
7
 
8
8
  Options:
9
- -h, --help display help for command
9
+ -h, --help display help for command
10
10
 
11
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
- post-update [options] Post a brain update for a vault.
17
- edit-update [options] <updateId> Edit a published brain update. You must be the author.
18
- delete-update <updateId> Delete a published brain update. You must be the author.
19
- help [command] display help for command
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
+ help [command] display help for command
20
17
  ```
21
18
 
22
19
  ## search
@@ -67,44 +64,3 @@ Delete BRAIN.md from the vault on webdrive.
67
64
  Options:
68
65
  -h, --help display help for command
69
66
  ```
70
-
71
- ## post-update
72
-
73
- ```
74
- Usage: gobi brain post-update [options]
75
-
76
- Post a brain update for a vault.
77
-
78
- Options:
79
- --vault-slug <vaultSlug> Vault slug (overrides .gobi/settings.yaml)
80
- --title <title> Title of the update
81
- --content <content> Update content (markdown supported)
82
- --auto-attachments Upload wiki-linked [[files]] to webdrive before posting
83
- -h, --help display help for command
84
- ```
85
-
86
- ## edit-update
87
-
88
- ```
89
- Usage: gobi brain edit-update [options] <updateId>
90
-
91
- Edit a published brain update. You must be the author.
92
-
93
- Options:
94
- --title <title> New title for the update
95
- --content <content> New content for the update (markdown supported)
96
- --vault-slug <vaultSlug> Vault slug for attachment uploads (overrides .gobi/settings.yaml)
97
- --auto-attachments Upload wiki-linked [[files]] to webdrive before editing
98
- -h, --help display help for command
99
- ```
100
-
101
- ## delete-update
102
-
103
- ```
104
- Usage: gobi brain delete-update [options] <updateId>
105
-
106
- Delete a published brain update. You must be the author.
107
-
108
- Options:
109
- -h, --help display help for command
110
- ```
@@ -3,7 +3,7 @@
3
3
  ```
4
4
  Usage: gobi space [options] [command]
5
5
 
6
- Space commands (threads, replies).
6
+ Space commands (threads, replies). Space and member admin is web-UI only.
7
7
 
8
8
  Options:
9
9
  --space-slug <slug> Space slug (overrides .gobi/settings.yaml)
@@ -11,9 +11,12 @@ Options:
11
11
 
12
12
  Commands:
13
13
  list List spaces you are a member of.
14
+ get [spaceSlug] Get details for a space. Pass a slug or omit to use the current space (from .gobi/settings.yaml or --space-slug).
14
15
  warp [spaceSlug] Select the active space. Pass a slug to warp directly, or omit for interactive selection.
15
16
  list-topics [options] List topics in a space, ordered by most recent content linkage.
16
17
  list-topic-threads [options] <topicSlug> List threads tagged with a topic in a space (cursor-paginated).
18
+ messages [options] List the unified message feed (threads and replies, newest first) in a space.
19
+ ancestors <threadId> Show the ancestor lineage of a thread or reply (root → immediate parent).
17
20
  get-thread [options] <threadId> Get a thread and its replies (paginated).
18
21
  list-threads [options] List threads in a space (paginated).
19
22
  create-thread [options] Create a thread in a space.
@@ -0,0 +1,63 @@
1
+ ---
2
+ name: gobi-proposal
3
+ description: >-
4
+ Gobi proposal commands for managing agent-authored proposals: list, get, add,
5
+ edit, delete, prioritize, accept, reject, or revise. The top-priority pending
6
+ proposals feed into the agent's system prompt every turn. Use when the user
7
+ wants to review, organize, or respond to proposals — or when an agent (using
8
+ gobi-cli as its tool layer) wants to record a proposal it just composed.
9
+ allowed-tools: Bash(gobi:*)
10
+ metadata:
11
+ author: gobi-ai
12
+ version: "1.3.4"
13
+ ---
14
+
15
+ # gobi-proposal
16
+
17
+ Gobi proposal commands for managing agent-authored proposals (v1.3.4).
18
+
19
+ Requires gobi-cli installed and authenticated. See gobi-core skill for setup.
20
+
21
+ ## What is a proposal?
22
+
23
+ A proposal is a unit of standing guidance authored by an agent (in-process during chat, or via `gobi proposal add` when the agent uses gobi-cli as its tool layer). Each proposal has:
24
+
25
+ - **content** — the proposal text (markdown, up to 8000 chars)
26
+ - **priority** — lower number = higher priority; default `100`
27
+ - **status** — `pending`, `accepted`, or `rejected`
28
+ - **revision** — bumped each time the content is edited
29
+ - **sessionId** — optional; the chat session the proposal originated from
30
+ - **history** — append-only log of `created`, `edited`, `prioritized`, `accepted`, `rejected`, and `revise_requested` events
31
+
32
+ The top 5 pending proposals (lowest priority first) are injected into the agent's system prompt every turn — that's how proposals turn into standing instructions.
33
+
34
+ ## Lifecycle
35
+
36
+ `accept` and `reject` are terminal: they post a synthesized user message into the originating chat session ("Accept your proposal X" / "Reject your proposal X") so the agent can react. `revise` keeps the proposal pending and posts the user's comment into the session, asking the agent to revise. Only pending proposals can be revised.
37
+
38
+ ## Important: JSON Mode
39
+
40
+ For programmatic/agent usage, always pass `--json` as a **global** option (before the subcommand):
41
+
42
+ ```bash
43
+ gobi --json proposal list --limit 20
44
+ gobi --json proposal add "Prefer concise titles" --priority 50
45
+ ```
46
+
47
+ JSON mode wraps the response as `{"success": true, "data": <proposal>}` (or `{"success": false, "error": "..."}`).
48
+
49
+ ## Available Commands
50
+
51
+ - `gobi proposal` — Proposals authored by your agent during chat. Top-5 feed the system prompt; accept/reject/revise post into the originating chat session.
52
+ - `gobi proposal list` — List proposals (priority ASC, then newest first).
53
+ - `gobi proposal get` — Show one proposal with its history.
54
+ - `gobi proposal edit` — Replace proposal content (bumps revision). Pass '-' for stdin.
55
+ - `gobi proposal delete` — Delete a proposal.
56
+ - `gobi proposal prioritize` — Set priority (lower = higher). Top 5 feed the system prompt.
57
+ - `gobi proposal accept` — Accept — posts "Accept your proposal X" into the originating chat session.
58
+ - `gobi proposal reject` — Reject — posts "Reject your proposal X" into the originating chat session.
59
+ - `gobi proposal revise` — Ask the agent to revise — posts "Update your proposal X. Here's my comment. {comment}" into the chat session.
60
+
61
+ ## Reference Documentation
62
+
63
+ - [gobi proposal](references/proposal.md)
@@ -0,0 +1,124 @@
1
+ # gobi proposal
2
+
3
+ ```
4
+ Usage: gobi proposal [options] [command]
5
+
6
+ Proposals authored by your agent during chat. Top-5 feed the system prompt; accept/reject/revise post into the originating chat session.
7
+
8
+ Options:
9
+ -h, --help display help for command
10
+
11
+ Commands:
12
+ list [options] List proposals (priority ASC, then newest first).
13
+ get <proposalId> Show one proposal with its history.
14
+ add [options] <content> Add a proposal directly. Pass '-' to read from stdin.
15
+ edit <proposalId> <content> Replace proposal content (bumps revision). Pass '-' for stdin.
16
+ delete <proposalId> Delete a proposal.
17
+ prioritize <proposalId> <priority> Set priority (lower = higher). Top 5 feed the system prompt.
18
+ accept <proposalId> Accept — posts "Accept your proposal X" into the originating chat session.
19
+ reject <proposalId> Reject — posts "Reject your proposal X" into the originating chat session.
20
+ revise <proposalId> <comment> Ask the agent to revise — posts "Update your proposal X. Here's my comment. {comment}" into the chat session.
21
+ help [command] display help for command
22
+ ```
23
+
24
+ ## list
25
+
26
+ ```
27
+ Usage: gobi proposal list [options]
28
+
29
+ List proposals (priority ASC, then newest first).
30
+
31
+ Options:
32
+ --limit <number> Max proposals to return (1-200) (default: "50")
33
+ -h, --help display help for command
34
+ ```
35
+
36
+ ## get
37
+
38
+ ```
39
+ Usage: gobi proposal get [options] <proposalId>
40
+
41
+ Show one proposal with its history.
42
+
43
+ Options:
44
+ -h, --help display help for command
45
+ ```
46
+
47
+ ## add
48
+
49
+ ```
50
+ Usage: gobi proposal add [options] <content>
51
+
52
+ Add a proposal directly. Pass '-' to read from stdin.
53
+
54
+ Options:
55
+ --session <sessionId> Originate from a chat session (UUID)
56
+ --priority <number> Priority (lower = higher), default 100
57
+ -h, --help display help for command
58
+ ```
59
+
60
+ ## edit
61
+
62
+ ```
63
+ Usage: gobi proposal edit [options] <proposalId> <content>
64
+
65
+ Replace proposal content (bumps revision). Pass '-' for stdin.
66
+
67
+ Options:
68
+ -h, --help display help for command
69
+ ```
70
+
71
+ ## delete
72
+
73
+ ```
74
+ Usage: gobi proposal delete [options] <proposalId>
75
+
76
+ Delete a proposal.
77
+
78
+ Options:
79
+ -h, --help display help for command
80
+ ```
81
+
82
+ ## prioritize
83
+
84
+ ```
85
+ Usage: gobi proposal prioritize [options] <proposalId> <priority>
86
+
87
+ Set priority (lower = higher). Top 5 feed the system prompt.
88
+
89
+ Options:
90
+ -h, --help display help for command
91
+ ```
92
+
93
+ ## accept
94
+
95
+ ```
96
+ Usage: gobi proposal accept [options] <proposalId>
97
+
98
+ Accept — posts "Accept your proposal X" into the originating chat session.
99
+
100
+ Options:
101
+ -h, --help display help for command
102
+ ```
103
+
104
+ ## reject
105
+
106
+ ```
107
+ Usage: gobi proposal reject [options] <proposalId>
108
+
109
+ Reject — posts "Reject your proposal X" into the originating chat session.
110
+
111
+ Options:
112
+ -h, --help display help for command
113
+ ```
114
+
115
+ ## revise
116
+
117
+ ```
118
+ Usage: gobi proposal revise [options] <proposalId> <comment>
119
+
120
+ Ask the agent to revise — posts "Update your proposal X. Here's my comment. {comment}" into the chat session.
121
+
122
+ Options:
123
+ -h, --help display help for command
124
+ ```
@@ -1,19 +1,20 @@
1
1
  ---
2
2
  name: gobi-space
3
3
  description: >-
4
- Gobi space commands for community interaction: list/create/edit/delete
5
- threads and replies, browse topics and topic threads, explore what's
6
- happening in a space. Use when the user wants to read, write, or manage
7
- threads and replies in their Gobi community spaces.
4
+ Gobi space commands for community interaction: post threads and
5
+ replies, browse the unified message feed and topic feeds, walk reply
6
+ lineage, and post to the global (slugless) space. Use when the user
7
+ wants to read or write threads and replies in their Gobi community
8
+ spaces. Space and member administration is web-UI only.
8
9
  allowed-tools: Bash(gobi:*)
9
10
  metadata:
10
11
  author: gobi-ai
11
- version: "0.8.0"
12
+ version: "0.9.13"
12
13
  ---
13
14
 
14
15
  # gobi-space
15
16
 
16
- Gobi space commands for community interaction (v0.8.0).
17
+ Gobi space commands for community interaction (v0.9.13).
17
18
 
18
19
  Requires gobi-cli installed and authenticated. See gobi-core skill for setup.
19
20
 
@@ -40,19 +41,43 @@ For programmatic/agent usage, always pass `--json` as a **global** option (befor
40
41
  gobi --json space list-threads
41
42
  ```
42
43
 
44
+ > Space and member administration (creating spaces, inviting/approving members, joining/leaving) is web-UI only and not available in the CLI.
45
+
43
46
  ## Available Commands
44
47
 
48
+ ### Space details
49
+ - `gobi space get` — Get details for a space.
50
+
51
+ ### Topics
45
52
  - `gobi space list-topics` — List topics in a space, ordered by most recent content linkage.
46
53
  - `gobi space list-topic-threads` — List threads tagged with a topic in a space (cursor-paginated).
54
+
55
+ ### Feed & lineage
56
+ - `gobi space messages` — List the unified message feed (threads and replies, newest first).
57
+ - `gobi space ancestors` — Show the ancestor lineage of a thread or reply (root → immediate parent).
58
+
59
+ ### Threads
47
60
  - `gobi space get-thread` — Get a thread and its replies (paginated).
48
61
  - `gobi space list-threads` — List threads in a space (paginated).
49
62
  - `gobi space create-thread` — Create a thread in a space.
50
63
  - `gobi space edit-thread` — Edit a thread. You must be the author.
51
64
  - `gobi space delete-thread` — Delete a thread. You must be the author.
65
+
66
+ ### Replies
52
67
  - `gobi space create-reply` — Create a reply to a thread in a space.
53
68
  - `gobi space edit-reply` — Edit a reply. You must be the author.
54
69
  - `gobi space delete-reply` — Delete a reply. You must be the author.
55
70
 
71
+ ### Global thread space
72
+ The global thread space has no slug and is visible across all spaces.
73
+
74
+ - `gobi global messages` — List the global unified message feed (newest first).
75
+ - `gobi global get-thread` — Get a global thread and its direct replies.
76
+ - `gobi global ancestors` — Show the ancestor lineage of a global thread or reply.
77
+ - `gobi global create-thread` — Create a thread in the global space.
78
+ - `gobi global reply` — Reply to a thread in the global space.
79
+
56
80
  ## Reference Documentation
57
81
 
58
82
  - [gobi space](references/space.md)
83
+ - [gobi global](references/global.md)
@@ -0,0 +1,82 @@
1
+ # gobi global
2
+
3
+ ```
4
+ Usage: gobi global [options] [command]
5
+
6
+ Global thread space commands (no slug; visible across all spaces).
7
+
8
+ Options:
9
+ -h, --help display help for command
10
+
11
+ Commands:
12
+ messages [options] List the global unified message feed (threads and replies, newest first).
13
+ get-thread [options] <threadId> Get a global thread and its direct replies (paginated).
14
+ ancestors <threadId> Show the ancestor lineage of a global thread or reply (root → immediate parent).
15
+ create-thread [options] Create a thread in the global space.
16
+ reply [options] <threadId> Reply to a thread in the global space.
17
+ help [command] display help for command
18
+ ```
19
+
20
+ ## messages
21
+
22
+ ```
23
+ Usage: gobi global messages [options]
24
+
25
+ List the global unified message feed (threads and replies, newest first).
26
+
27
+ Options:
28
+ --limit <number> Items per page (default: "20")
29
+ --cursor <string> Pagination cursor from previous response
30
+ -h, --help display help for command
31
+ ```
32
+
33
+ ## get-thread
34
+
35
+ ```
36
+ Usage: gobi global get-thread [options] <threadId>
37
+
38
+ Get a global thread and its direct replies (paginated).
39
+
40
+ Options:
41
+ --limit <number> Replies per page (default: "20")
42
+ --cursor <string> Pagination cursor from previous response
43
+ -h, --help display help for command
44
+ ```
45
+
46
+ ## ancestors
47
+
48
+ ```
49
+ Usage: gobi global ancestors [options] <threadId>
50
+
51
+ Show the ancestor lineage of a global thread or reply (root → immediate parent).
52
+
53
+ Options:
54
+ -h, --help display help for command
55
+ ```
56
+
57
+ ## create-thread
58
+
59
+ ```
60
+ Usage: gobi global create-thread [options]
61
+
62
+ Create a thread in the global space.
63
+
64
+ Options:
65
+ --title <title> Title of the thread
66
+ --content <content> Thread content (markdown supported, use "-" for stdin)
67
+ --rich-text <richText> Rich-text JSON array (mutually exclusive with --content)
68
+ -h, --help display help for command
69
+ ```
70
+
71
+ ## reply
72
+
73
+ ```
74
+ Usage: gobi global reply [options] <threadId>
75
+
76
+ Reply to a thread in the global space.
77
+
78
+ Options:
79
+ --content <content> Reply content (markdown supported, use "-" for stdin)
80
+ --rich-text <richText> Rich-text JSON array (mutually exclusive with --content)
81
+ -h, --help display help for command
82
+ ```
@@ -3,7 +3,7 @@
3
3
  ```
4
4
  Usage: gobi space [options] [command]
5
5
 
6
- Space commands (threads, replies).
6
+ Space commands (threads, replies). Space and member admin is web-UI only.
7
7
 
8
8
  Options:
9
9
  --space-slug <slug> Space slug (overrides .gobi/settings.yaml)
@@ -11,9 +11,12 @@ Options:
11
11
 
12
12
  Commands:
13
13
  list List spaces you are a member of.
14
+ get [spaceSlug] Get details for a space. Pass a slug or omit to use the current space (from .gobi/settings.yaml or --space-slug).
14
15
  warp [spaceSlug] Select the active space. Pass a slug to warp directly, or omit for interactive selection.
15
16
  list-topics [options] List topics in a space, ordered by most recent content linkage.
16
17
  list-topic-threads [options] <topicSlug> List threads tagged with a topic in a space (cursor-paginated).
18
+ messages [options] List the unified message feed (threads and replies, newest first) in a space.
19
+ ancestors <threadId> Show the ancestor lineage of a thread or reply (root → immediate parent).
17
20
  get-thread [options] <threadId> Get a thread and its replies (paginated).
18
21
  list-threads [options] List threads in a space (paginated).
19
22
  create-thread [options] Create a thread in a space.
@@ -25,6 +28,17 @@ Commands:
25
28
  help [command] display help for command
26
29
  ```
27
30
 
31
+ ## get
32
+
33
+ ```
34
+ Usage: gobi space get [options] [spaceSlug]
35
+
36
+ Get details for a space. Pass a slug or omit to use the current space (from .gobi/settings.yaml or --space-slug).
37
+
38
+ Options:
39
+ -h, --help display help for command
40
+ ```
41
+
28
42
  ## list-topics
29
43
 
30
44
  ```
@@ -50,6 +64,30 @@ Options:
50
64
  -h, --help display help for command
51
65
  ```
52
66
 
67
+ ## messages
68
+
69
+ ```
70
+ Usage: gobi space messages [options]
71
+
72
+ List the unified message feed (threads and replies, newest first) in a space.
73
+
74
+ Options:
75
+ --limit <number> Items per page (default: "20")
76
+ --cursor <string> Pagination cursor from previous response
77
+ -h, --help display help for command
78
+ ```
79
+
80
+ ## ancestors
81
+
82
+ ```
83
+ Usage: gobi space ancestors [options] <threadId>
84
+
85
+ Show the ancestor lineage of a thread or reply (root → immediate parent).
86
+
87
+ Options:
88
+ -h, --help display help for command
89
+ ```
90
+
53
91
  ## get-thread
54
92
 
55
93
  ```