@gobi-ai/cli 2.0.30 → 2.0.31
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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/dist/attachments.js +31 -9
- package/dist/client.js +3 -0
- package/dist/commands/global.js +49 -7
- package/dist/commands/personal.js +49 -7
- package/dist/commands/space.js +147 -8
- package/dist/commands/utils.js +70 -0
- package/package.json +1 -1
- package/skills/gobi-artifact/SKILL.md +2 -2
- package/skills/gobi-core/SKILL.md +2 -2
- package/skills/gobi-core/references/space.md +5 -5
- package/skills/gobi-homepage/SKILL.md +1 -1
- package/skills/gobi-media/SKILL.md +2 -2
- package/skills/gobi-sense/SKILL.md +2 -2
- package/skills/gobi-space/SKILL.md +27 -8
- package/skills/gobi-space/references/global.md +30 -5
- package/skills/gobi-space/references/personal.md +30 -5
- package/skills/gobi-space/references/space.md +91 -20
- package/skills/gobi-vault/SKILL.md +2 -2
|
@@ -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": "2.0.
|
|
7
|
+
"version": "2.0.31",
|
|
8
8
|
"plugins": [
|
|
9
9
|
{
|
|
10
10
|
"name": "gobi",
|
|
11
11
|
"description": "Manage the Gobi collaborative knowledge platform from the command line. Publish vault profiles, create posts and replies, generate images and videos.",
|
|
12
|
-
"version": "2.0.
|
|
12
|
+
"version": "2.0.31",
|
|
13
13
|
"author": {
|
|
14
14
|
"name": "gobi-ai"
|
|
15
15
|
},
|
package/dist/attachments.js
CHANGED
|
@@ -7,8 +7,10 @@ import { apiPost } from "./client.js";
|
|
|
7
7
|
import { normalizeSyncPattern } from "./commands/sync.js";
|
|
8
8
|
// Best-effort extension → MIME mapping. Anything we don't recognize falls
|
|
9
9
|
// back to `application/octet-stream`; the backend caps size per content-type
|
|
10
|
-
// tier (
|
|
11
|
-
// allowed. We're just trying to set a usable
|
|
10
|
+
// tier (10MB photos / 15MB GIFs / 512MB video / 250MB document files) so
|
|
11
|
+
// it's the authority on what's allowed. We're just trying to set a usable
|
|
12
|
+
// Content-Type for the S3 PUT — and for document files the declared MIME is
|
|
13
|
+
// what routes the post row into the 'file' kind, so it must be accurate.
|
|
12
14
|
const POST_MEDIA_MIME_MAP = {
|
|
13
15
|
".jpg": "image/jpeg",
|
|
14
16
|
".jpeg": "image/jpeg",
|
|
@@ -26,20 +28,30 @@ const POST_MEDIA_MIME_MAP = {
|
|
|
26
28
|
".webm": "video/webm",
|
|
27
29
|
".m4v": "video/x-m4v",
|
|
28
30
|
".pdf": "application/pdf",
|
|
31
|
+
".md": "text/markdown",
|
|
32
|
+
".markdown": "text/markdown",
|
|
33
|
+
".txt": "text/plain",
|
|
34
|
+
".csv": "text/csv",
|
|
29
35
|
};
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
// the 4
|
|
36
|
+
const FILE_EXTENSIONS = [".pdf", ".md", ".markdown", ".txt", ".csv"];
|
|
37
|
+
const VIDEO_EXTENSIONS = [".mp4", ".mov", ".webm", ".m4v"];
|
|
38
|
+
// Mirrors the backend mix rule: up to 4 photos + up to 4 document files
|
|
39
|
+
// (pdf/md/txt/csv) together, OR 1 GIF, OR 1 video — GIF and video are
|
|
40
|
+
// exclusive with everything. Unknown extensions count against the photo cap
|
|
41
|
+
// (the backend's 'other' kind) so nothing slips through unlimited.
|
|
33
42
|
export function assertPostAttachmentMix(paths) {
|
|
34
43
|
let photos = 0;
|
|
35
44
|
let gifs = 0;
|
|
36
45
|
let videos = 0;
|
|
46
|
+
let files = 0;
|
|
37
47
|
for (const p of paths) {
|
|
38
48
|
const ext = extname(p).toLowerCase();
|
|
39
49
|
if (ext === ".gif")
|
|
40
50
|
gifs += 1;
|
|
41
|
-
else if (
|
|
51
|
+
else if (VIDEO_EXTENSIONS.includes(ext))
|
|
42
52
|
videos += 1;
|
|
53
|
+
else if (FILE_EXTENSIONS.includes(ext))
|
|
54
|
+
files += 1;
|
|
43
55
|
else
|
|
44
56
|
photos += 1;
|
|
45
57
|
}
|
|
@@ -49,10 +61,12 @@ export function assertPostAttachmentMix(paths) {
|
|
|
49
61
|
throw new Error("Only 1 GIF allowed per post");
|
|
50
62
|
if (photos > 4)
|
|
51
63
|
throw new Error("Up to 4 photos allowed per post");
|
|
52
|
-
if (
|
|
64
|
+
if (files > 4)
|
|
65
|
+
throw new Error("Up to 4 files allowed per post");
|
|
66
|
+
if (videos > 0 && (gifs > 0 || photos > 0 || files > 0)) {
|
|
53
67
|
throw new Error("A video can't be combined with other media");
|
|
54
68
|
}
|
|
55
|
-
if (gifs > 0 && (videos > 0 || photos > 0)) {
|
|
69
|
+
if (gifs > 0 && (videos > 0 || photos > 0 || files > 0)) {
|
|
56
70
|
throw new Error("A GIF can't be combined with other media");
|
|
57
71
|
}
|
|
58
72
|
}
|
|
@@ -90,7 +104,15 @@ export async function uploadPostAttachment(filePath) {
|
|
|
90
104
|
if (!putRes.ok) {
|
|
91
105
|
throw new Error(`Failed to PUT ${filePath} to S3: HTTP ${putRes.status}`);
|
|
92
106
|
}
|
|
93
|
-
|
|
107
|
+
const attachment = { mediaUrl, mediaKey };
|
|
108
|
+
// Document files carry name + MIME on the row: the declared mimeType is
|
|
109
|
+
// what the backend trusts for kind/preview routing, and the original
|
|
110
|
+
// filename only survives here (the S3 key is a UUID).
|
|
111
|
+
if (FILE_EXTENSIONS.includes(ext)) {
|
|
112
|
+
attachment.fileName = basename(abs);
|
|
113
|
+
attachment.mimeType = contentType;
|
|
114
|
+
}
|
|
115
|
+
return attachment;
|
|
94
116
|
}
|
|
95
117
|
export async function uploadPostAttachments(paths) {
|
|
96
118
|
const out = [];
|
package/dist/client.js
CHANGED
|
@@ -38,6 +38,9 @@ export function apiPost(path, body) {
|
|
|
38
38
|
export function apiPatch(path, body) {
|
|
39
39
|
return request("PATCH", path, { body });
|
|
40
40
|
}
|
|
41
|
+
export function apiPut(path, body) {
|
|
42
|
+
return request("PUT", path, { body });
|
|
43
|
+
}
|
|
41
44
|
export function apiDelete(path) {
|
|
42
45
|
return request("DELETE", path);
|
|
43
46
|
}
|
package/dist/commands/global.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { WEB_BASE_URL } from "../constants.js";
|
|
2
|
-
import { apiGet, apiPost, apiPatch, apiDelete } from "../client.js";
|
|
3
|
-
import { isJsonMode, jsonOut, readStdin, unwrapResp, } from "./utils.js";
|
|
2
|
+
import { apiGet, apiPost, apiPatch, apiPut, apiDelete } from "../client.js";
|
|
3
|
+
import { formatAttachmentLines, formatAttachmentSummary, formatReactionChips, isJsonMode, jsonOut, readStdin, unwrapResp, } from "./utils.js";
|
|
4
4
|
import { uploadPostAttachments, assertPostAttachmentMix, } from "../attachments.js";
|
|
5
5
|
function readContent(value) {
|
|
6
6
|
if (value === "-")
|
|
@@ -26,7 +26,11 @@ function formatFeedLine(m) {
|
|
|
26
26
|
else {
|
|
27
27
|
label = m.title || m.content || "";
|
|
28
28
|
}
|
|
29
|
-
|
|
29
|
+
const chips = formatReactionChips(m);
|
|
30
|
+
const attachSummary = formatAttachmentSummary(m);
|
|
31
|
+
return (`${id} ${kind} ${author} "${label}" ${m.createdAt}` +
|
|
32
|
+
(attachSummary ? ` ${attachSummary}` : "") +
|
|
33
|
+
(chips ? ` ${chips}` : ""));
|
|
30
34
|
}
|
|
31
35
|
export function registerGlobalCommand(program) {
|
|
32
36
|
const global = program
|
|
@@ -145,20 +149,28 @@ export function registerGlobalCommand(program) {
|
|
|
145
149
|
`User ${r.authorId}`;
|
|
146
150
|
const text = r.content || "";
|
|
147
151
|
const truncated = opts.full || text.length <= 200 ? text : text.slice(0, 200) + "…";
|
|
148
|
-
|
|
152
|
+
const rChips = formatReactionChips(r);
|
|
153
|
+
const rAttach = formatAttachmentSummary(r);
|
|
154
|
+
replyLines.push(` - [r:${r.id}] ${rAuthor}: ${truncated} (${r.createdAt})${rAttach ? ` ${rAttach}` : ""}${rChips ? ` ${rChips}` : ""}`);
|
|
149
155
|
}
|
|
150
156
|
const isReplyPost = post.parentPostId != null;
|
|
151
157
|
const heading = isReplyPost
|
|
152
158
|
? `Reply [r:${post.id}]`
|
|
153
159
|
: `Post: ${post.title || "(no title)"}`;
|
|
160
|
+
const postChips = formatReactionChips(post);
|
|
161
|
+
const attachmentLines = formatAttachmentLines(post);
|
|
154
162
|
const output = [
|
|
155
163
|
heading,
|
|
156
164
|
`By: ${author} on ${post.createdAt}`,
|
|
165
|
+
...(postChips ? [`Reactions: ${postChips}`] : []),
|
|
157
166
|
...(ancestorLines.length
|
|
158
167
|
? ["", `Ancestors (${ancestors.length} items, root first):`, ...ancestorLines]
|
|
159
168
|
: []),
|
|
160
169
|
"",
|
|
161
170
|
post.content || "",
|
|
171
|
+
...(attachmentLines.length
|
|
172
|
+
? ["", `Attachments (${attachmentLines.length}):`, ...attachmentLines]
|
|
173
|
+
: []),
|
|
162
174
|
"",
|
|
163
175
|
`Replies (${replies.length} items):`,
|
|
164
176
|
...replyLines,
|
|
@@ -176,7 +188,7 @@ export function registerGlobalCommand(program) {
|
|
|
176
188
|
.option("--content <content>", "Post content (markdown supported, use \"-\" for stdin)")
|
|
177
189
|
.option("--rich-text <richText>", "Rich-text JSON array (mutually exclusive with --content)")
|
|
178
190
|
.option("--artifact <artifactId>", "Attach an existing artifact to the post (repeatable). Create artifacts with `gobi artifact create`.", (value, prev = []) => [...prev, value], [])
|
|
179
|
-
.option("--attach <file>", "Local media file to attach. Repeatable.
|
|
191
|
+
.option("--attach <file>", "Local media or document file to attach. Repeatable. Mix rule: up to 4 photos + up to 4 document files (pdf/md/txt/csv) OR 1 GIF OR 1 video. Size ceilings: 10MB photos / 15MB GIFs / 512MB video / 250MB files.", (value, prev = []) => [...prev, value], [])
|
|
180
192
|
.option("--repost-post-id <postId>", "Wrap an existing top-level post as the embedded card on this new post. Composes with --content / --rich-text / --attach (the wrapping author's text + media render above the embedded card). Reposts-of-reposts are collapsed to the transitive root server-side. The referenced post must exist, not be deleted, and not itself be a reply.")
|
|
181
193
|
.action(async (opts) => {
|
|
182
194
|
if (!opts.content && !opts.richText) {
|
|
@@ -234,7 +246,7 @@ export function registerGlobalCommand(program) {
|
|
|
234
246
|
.option("--title <title>", "New title")
|
|
235
247
|
.option("--content <content>", "New content (markdown supported, use \"-\" for stdin)")
|
|
236
248
|
.option("--rich-text <richText>", "Rich-text JSON array (mutually exclusive with --content)")
|
|
237
|
-
.option("--attach <file>", "Replace the post's media attachments with the given files (existing attachments are removed). Repeatable.
|
|
249
|
+
.option("--attach <file>", "Replace the post's media attachments with the given files (existing attachments are removed). Repeatable. Mix rule: up to 4 photos + up to 4 document files (pdf/md/txt/csv) OR 1 GIF OR 1 video. Size ceilings: 10MB photos / 15MB GIFs / 512MB video / 250MB files. Omit to leave attachments unchanged.", (value, prev = []) => [...prev, value], [])
|
|
238
250
|
.option("--artifact <artifactId>", "Replace the post's artifact attachments with the given artifact(s) (existing artifact attachments are removed). Repeatable. Omit to leave them unchanged. Create artifacts with `gobi artifact create`.", (value, prev = []) => [...prev, value], [])
|
|
239
251
|
.action(async (postId, opts) => {
|
|
240
252
|
const wantsAttachChange = !!(opts.attach && opts.attach.length > 0);
|
|
@@ -297,7 +309,7 @@ export function registerGlobalCommand(program) {
|
|
|
297
309
|
.description("Create a reply to a post in the global feed.")
|
|
298
310
|
.option("--content <content>", "Reply content (markdown supported, use \"-\" for stdin)")
|
|
299
311
|
.option("--rich-text <richText>", "Rich-text JSON array (mutually exclusive with --content)")
|
|
300
|
-
.option("--attach <file>", "Local media file to attach to this reply. Repeatable.
|
|
312
|
+
.option("--attach <file>", "Local media or document file to attach to this reply. Repeatable. Mix rule: up to 4 photos + up to 4 document files (pdf/md/txt/csv) OR 1 GIF OR 1 video. Size ceilings: 10MB photos / 15MB GIFs / 512MB video / 250MB files.", (value, prev = []) => [...prev, value], [])
|
|
301
313
|
.action(async (postId, opts) => {
|
|
302
314
|
if (!opts.content && !opts.richText) {
|
|
303
315
|
throw new Error("Provide either --content or --rich-text.");
|
|
@@ -376,4 +388,34 @@ export function registerGlobalCommand(program) {
|
|
|
376
388
|
}
|
|
377
389
|
console.log(`Reply ${replyId} deleted.`);
|
|
378
390
|
});
|
|
391
|
+
// ── Reactions (react, unreact) ──
|
|
392
|
+
global
|
|
393
|
+
.command("react <postId> <emoji>")
|
|
394
|
+
.description("Add an emoji reaction to a global-feed post or reply (idempotent). <postId> is the numeric id of a post OR a reply — the [p:N]/[r:N] ids shown in feed output.")
|
|
395
|
+
.action(async (postId, emoji) => {
|
|
396
|
+
const resp = (await apiPut(`/posts/${postId}/reactions`, {
|
|
397
|
+
emoji,
|
|
398
|
+
}));
|
|
399
|
+
const data = unwrapResp(resp);
|
|
400
|
+
if (isJsonMode(global)) {
|
|
401
|
+
jsonOut(data);
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
const chips = formatReactionChips(data);
|
|
405
|
+
console.log(`Reacted ${emoji} to ${postId}.` + (chips ? `\n Now: ${chips}` : ""));
|
|
406
|
+
});
|
|
407
|
+
global
|
|
408
|
+
.command("unreact <postId> <emoji>")
|
|
409
|
+
.description("Remove your emoji reaction from a global-feed post or reply. <postId> is the numeric id of a post OR a reply.")
|
|
410
|
+
.action(async (postId, emoji) => {
|
|
411
|
+
const resp = (await apiDelete(`/posts/${postId}/reactions/${encodeURIComponent(emoji)}`));
|
|
412
|
+
const data = unwrapResp(resp);
|
|
413
|
+
if (isJsonMode(global)) {
|
|
414
|
+
jsonOut(data);
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
const chips = formatReactionChips(data);
|
|
418
|
+
console.log(`Removed ${emoji} reaction from ${postId}.` +
|
|
419
|
+
(chips ? `\n Now: ${chips}` : ""));
|
|
420
|
+
});
|
|
379
421
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { apiGet, apiPost, apiPatch, apiDelete } from "../client.js";
|
|
2
|
-
import { isJsonMode, jsonOut, readStdin, unwrapResp, } from "./utils.js";
|
|
1
|
+
import { apiGet, apiPost, apiPatch, apiPut, apiDelete } from "../client.js";
|
|
2
|
+
import { formatAttachmentLines, formatAttachmentSummary, formatReactionChips, isJsonMode, jsonOut, readStdin, unwrapResp, } from "./utils.js";
|
|
3
3
|
import { uploadPostAttachments, assertPostAttachmentMix, } from "../attachments.js";
|
|
4
4
|
function readContent(value) {
|
|
5
5
|
if (value === "-")
|
|
@@ -22,7 +22,11 @@ function formatFeedLine(m) {
|
|
|
22
22
|
else {
|
|
23
23
|
label = m.title || m.content || "";
|
|
24
24
|
}
|
|
25
|
-
|
|
25
|
+
const chips = formatReactionChips(m);
|
|
26
|
+
const attachSummary = formatAttachmentSummary(m);
|
|
27
|
+
return (`${id} ${kind} ${author} "${label}" ${m.createdAt}` +
|
|
28
|
+
(attachSummary ? ` ${attachSummary}` : "") +
|
|
29
|
+
(chips ? ` ${chips}` : ""));
|
|
26
30
|
}
|
|
27
31
|
export function registerPersonalCommand(program) {
|
|
28
32
|
const personal = program
|
|
@@ -147,20 +151,28 @@ export function registerPersonalCommand(program) {
|
|
|
147
151
|
`User ${r.authorId}`;
|
|
148
152
|
const text = r.content || "";
|
|
149
153
|
const truncated = opts.full || text.length <= 200 ? text : text.slice(0, 200) + "…";
|
|
150
|
-
|
|
154
|
+
const rChips = formatReactionChips(r);
|
|
155
|
+
const rAttach = formatAttachmentSummary(r);
|
|
156
|
+
replyLines.push(` - [r:${r.id}] ${rAuthor}: ${truncated} (${r.createdAt})${rAttach ? ` ${rAttach}` : ""}${rChips ? ` ${rChips}` : ""}`);
|
|
151
157
|
}
|
|
152
158
|
const isReplyPost = post.parentPostId != null;
|
|
153
159
|
const heading = isReplyPost
|
|
154
160
|
? `Reply [r:${post.id}] (private)`
|
|
155
161
|
: `Post: ${post.title || "(no title)"} (private)`;
|
|
162
|
+
const postChips = formatReactionChips(post);
|
|
163
|
+
const attachmentLines = formatAttachmentLines(post);
|
|
156
164
|
const output = [
|
|
157
165
|
heading,
|
|
158
166
|
`By: ${author} on ${post.createdAt}`,
|
|
167
|
+
...(postChips ? [`Reactions: ${postChips}`] : []),
|
|
159
168
|
...(ancestorLines.length
|
|
160
169
|
? ["", `Ancestors (${ancestors.length} items, root first):`, ...ancestorLines]
|
|
161
170
|
: []),
|
|
162
171
|
"",
|
|
163
172
|
post.content || "",
|
|
173
|
+
...(attachmentLines.length
|
|
174
|
+
? ["", `Attachments (${attachmentLines.length}):`, ...attachmentLines]
|
|
175
|
+
: []),
|
|
164
176
|
"",
|
|
165
177
|
`Replies (${replies.length} items):`,
|
|
166
178
|
...replyLines,
|
|
@@ -184,7 +196,7 @@ export function registerPersonalCommand(program) {
|
|
|
184
196
|
.option("--content <content>", "Post content (markdown supported, use \"-\" for stdin)")
|
|
185
197
|
.option("--rich-text <richText>", "Rich-text JSON array (mutually exclusive with --content)")
|
|
186
198
|
.option("--artifact <artifactId>", "Attach an existing artifact to the post (repeatable). Create artifacts with `gobi artifact create`.", (value, prev = []) => [...prev, value], [])
|
|
187
|
-
.option("--attach <file>", "Local media file to attach. Repeatable.
|
|
199
|
+
.option("--attach <file>", "Local media or document file to attach. Repeatable. Mix rule: up to 4 photos + up to 4 document files (pdf/md/txt/csv) OR 1 GIF OR 1 video. Size ceilings: 10MB photos / 15MB GIFs / 512MB video / 250MB files.", (value, prev = []) => [...prev, value], [])
|
|
188
200
|
.option("--repost-post-id <postId>", "Wrap an existing top-level post as the embedded card on this new private post. The referenced post must be visible to you (your own personal-space post, a global-feed post, or a post in a space you're a member of). Reposting someone else's personal-space post returns 404.")
|
|
189
201
|
.action(async (opts) => {
|
|
190
202
|
if (!opts.content && !opts.richText) {
|
|
@@ -245,7 +257,7 @@ export function registerPersonalCommand(program) {
|
|
|
245
257
|
.option("--title <title>", "New title")
|
|
246
258
|
.option("--content <content>", "New content (markdown supported, use \"-\" for stdin)")
|
|
247
259
|
.option("--rich-text <richText>", "Rich-text JSON array (mutually exclusive with --content)")
|
|
248
|
-
.option("--attach <file>", "Replace the post's media attachments with the given files (existing attachments are removed). Repeatable.
|
|
260
|
+
.option("--attach <file>", "Replace the post's media attachments with the given files (existing attachments are removed). Repeatable. Mix rule: up to 4 photos + up to 4 document files (pdf/md/txt/csv) OR 1 GIF OR 1 video. Size ceilings: 10MB photos / 15MB GIFs / 512MB video / 250MB files. Omit to leave attachments unchanged.", (value, prev = []) => [...prev, value], [])
|
|
249
261
|
.option("--artifact <artifactId>", "Replace the post's artifact attachments with the given artifact(s) (existing artifact attachments are removed). Repeatable. Omit to leave them unchanged. Create artifacts with `gobi artifact create`.", (value, prev = []) => [...prev, value], [])
|
|
250
262
|
.action(async (postId, opts) => {
|
|
251
263
|
const wantsAttachChange = !!(opts.attach && opts.attach.length > 0);
|
|
@@ -313,7 +325,7 @@ export function registerPersonalCommand(program) {
|
|
|
313
325
|
.description("Reply to a personal-space post. The reply inherits the parent's private scope automatically.")
|
|
314
326
|
.option("--content <content>", "Reply content (markdown supported, use \"-\" for stdin)")
|
|
315
327
|
.option("--rich-text <richText>", "Rich-text JSON array (mutually exclusive with --content)")
|
|
316
|
-
.option("--attach <file>", "Local media file to attach to this reply. Repeatable.
|
|
328
|
+
.option("--attach <file>", "Local media or document file to attach to this reply. Repeatable. Mix rule: up to 4 photos + up to 4 document files (pdf/md/txt/csv) OR 1 GIF OR 1 video. Size ceilings: 10MB photos / 15MB GIFs / 512MB video / 250MB files.", (value, prev = []) => [...prev, value], [])
|
|
317
329
|
.action(async (postId, opts) => {
|
|
318
330
|
if (!opts.content && !opts.richText) {
|
|
319
331
|
throw new Error("Provide either --content or --rich-text.");
|
|
@@ -392,4 +404,34 @@ export function registerPersonalCommand(program) {
|
|
|
392
404
|
}
|
|
393
405
|
console.log(`Reply ${replyId} deleted.`);
|
|
394
406
|
});
|
|
407
|
+
// ── Reactions (react, unreact) ──
|
|
408
|
+
personal
|
|
409
|
+
.command("react <postId> <emoji>")
|
|
410
|
+
.description("Add an emoji reaction to a personal-space post or reply (idempotent). <postId> is the numeric id of a post OR a reply.")
|
|
411
|
+
.action(async (postId, emoji) => {
|
|
412
|
+
const resp = (await apiPut(`/posts/${postId}/reactions`, {
|
|
413
|
+
emoji,
|
|
414
|
+
}));
|
|
415
|
+
const data = unwrapResp(resp);
|
|
416
|
+
if (isJsonMode(personal)) {
|
|
417
|
+
jsonOut(data);
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
const chips = formatReactionChips(data);
|
|
421
|
+
console.log(`Reacted ${emoji} to ${postId}.` + (chips ? `\n Now: ${chips}` : ""));
|
|
422
|
+
});
|
|
423
|
+
personal
|
|
424
|
+
.command("unreact <postId> <emoji>")
|
|
425
|
+
.description("Remove your emoji reaction from a personal-space post or reply. <postId> is the numeric id of a post OR a reply.")
|
|
426
|
+
.action(async (postId, emoji) => {
|
|
427
|
+
const resp = (await apiDelete(`/posts/${postId}/reactions/${encodeURIComponent(emoji)}`));
|
|
428
|
+
const data = unwrapResp(resp);
|
|
429
|
+
if (isJsonMode(personal)) {
|
|
430
|
+
jsonOut(data);
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
const chips = formatReactionChips(data);
|
|
434
|
+
console.log(`Removed ${emoji} reaction from ${postId}.` +
|
|
435
|
+
(chips ? `\n Now: ${chips}` : ""));
|
|
436
|
+
});
|
|
395
437
|
}
|
package/dist/commands/space.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { WEB_BASE_URL } from "../constants.js";
|
|
2
|
-
import { apiGet, apiPost, apiPatch, apiDelete } from "../client.js";
|
|
2
|
+
import { apiGet, apiPost, apiPatch, apiPut, apiDelete } from "../client.js";
|
|
3
3
|
import { requireSpace, selectSpace, setSpaceRequirement, writeSpaceSetting, } from "./init.js";
|
|
4
|
-
import { isJsonMode, jsonOut, readStdin, resolveSpaceSlug, unwrapResp, } from "./utils.js";
|
|
4
|
+
import { formatAttachmentLines, formatAttachmentSummary, formatReactionChips, isJsonMode, jsonOut, readStdin, resolveSpaceSlug, unwrapResp, } from "./utils.js";
|
|
5
5
|
import { uploadPostAttachments, assertPostAttachmentMix, } from "../attachments.js";
|
|
6
6
|
function readContent(value) {
|
|
7
7
|
if (value === "-")
|
|
@@ -23,7 +23,20 @@ function formatFeedLine(m) {
|
|
|
23
23
|
else {
|
|
24
24
|
label = m.title || m.content || "";
|
|
25
25
|
}
|
|
26
|
-
|
|
26
|
+
const chips = formatReactionChips(m);
|
|
27
|
+
const attachSummary = formatAttachmentSummary(m);
|
|
28
|
+
return (`${id} ${kind} ${author} "${label}" ${m.createdAt}` +
|
|
29
|
+
(attachSummary ? ` ${attachSummary}` : "") +
|
|
30
|
+
(chips ? ` ${chips}` : ""));
|
|
31
|
+
}
|
|
32
|
+
function parseChannelIdOption(value) {
|
|
33
|
+
if (value == null)
|
|
34
|
+
return undefined;
|
|
35
|
+
const n = Number(value);
|
|
36
|
+
if (!Number.isInteger(n) || n <= 0) {
|
|
37
|
+
throw new Error("--channel must be a positive integer channel id.");
|
|
38
|
+
}
|
|
39
|
+
return n;
|
|
27
40
|
}
|
|
28
41
|
export function registerSpaceCommand(program) {
|
|
29
42
|
const space = program
|
|
@@ -179,6 +192,7 @@ export function registerSpaceCommand(program) {
|
|
|
179
192
|
.description("List the unified feed (posts and replies, newest first) in a space.")
|
|
180
193
|
.option("--limit <number>", "Items per page", "20")
|
|
181
194
|
.option("--cursor <string>", "Pagination cursor from previous response")
|
|
195
|
+
.option("--channel <channelId>", "Channel id to read instead of the main feed (see `list-channels`). Omit for the main feed.")
|
|
182
196
|
.option("--space-slug <spaceSlug>", "Space slug (overrides .gobi/settings.yaml)")
|
|
183
197
|
.action(async (opts) => {
|
|
184
198
|
const spaceSlug = resolveSpaceSlug(space, opts);
|
|
@@ -187,6 +201,7 @@ export function registerSpaceCommand(program) {
|
|
|
187
201
|
};
|
|
188
202
|
if (opts.cursor)
|
|
189
203
|
params.cursor = opts.cursor;
|
|
204
|
+
params.channelId = parseChannelIdOption(opts.channel);
|
|
190
205
|
const resp = (await apiGet(`/spaces/${spaceSlug}/feed`, params));
|
|
191
206
|
if (isJsonMode(space)) {
|
|
192
207
|
jsonOut({
|
|
@@ -251,20 +266,28 @@ export function registerSpaceCommand(program) {
|
|
|
251
266
|
const body = opts.full || !text || text.length <= 200
|
|
252
267
|
? text
|
|
253
268
|
: text.slice(0, 200) + "…";
|
|
254
|
-
|
|
269
|
+
const rChips = formatReactionChips(r);
|
|
270
|
+
const rAttach = formatAttachmentSummary(r);
|
|
271
|
+
replyLines.push(` - [r:${r.id}] ${rAuthor}: ${body} (${r.createdAt})${rAttach ? ` ${rAttach}` : ""}${rChips ? ` ${rChips}` : ""}`);
|
|
255
272
|
}
|
|
256
273
|
const isReplyPost = post.parentPostId != null;
|
|
257
274
|
const heading = isReplyPost
|
|
258
275
|
? `Reply [r:${post.id}]`
|
|
259
276
|
: `Post: ${post.title || "(no title)"}`;
|
|
277
|
+
const postChips = formatReactionChips(post);
|
|
278
|
+
const attachmentLines = formatAttachmentLines(post);
|
|
260
279
|
const output = [
|
|
261
280
|
heading,
|
|
262
281
|
`By: ${author} on ${post.createdAt}`,
|
|
282
|
+
...(postChips ? [`Reactions: ${postChips}`] : []),
|
|
263
283
|
...(ancestorLines.length
|
|
264
284
|
? ["", `Ancestors (${ancestors.length} items, root first):`, ...ancestorLines]
|
|
265
285
|
: []),
|
|
266
286
|
"",
|
|
267
287
|
post.content || "",
|
|
288
|
+
...(attachmentLines.length
|
|
289
|
+
? ["", `Attachments (${attachmentLines.length}):`, ...attachmentLines]
|
|
290
|
+
: []),
|
|
268
291
|
"",
|
|
269
292
|
`Replies (${replies.length} items):`,
|
|
270
293
|
...replyLines,
|
|
@@ -277,6 +300,7 @@ export function registerSpaceCommand(program) {
|
|
|
277
300
|
.description("List posts in a space (paginated).")
|
|
278
301
|
.option("--limit <number>", "Items per page", "20")
|
|
279
302
|
.option("--cursor <string>", "Pagination cursor from previous response")
|
|
303
|
+
.option("--channel <channelId>", "Channel id to read instead of the main feed (see `list-channels`). Omit for the main feed.")
|
|
280
304
|
.option("--space-slug <spaceSlug>", "Space slug (overrides .gobi/settings.yaml)")
|
|
281
305
|
.action(async (opts) => {
|
|
282
306
|
const spaceSlug = resolveSpaceSlug(space, opts);
|
|
@@ -285,6 +309,7 @@ export function registerSpaceCommand(program) {
|
|
|
285
309
|
};
|
|
286
310
|
if (opts.cursor)
|
|
287
311
|
params.cursor = opts.cursor;
|
|
312
|
+
params.channelId = parseChannelIdOption(opts.channel);
|
|
288
313
|
const resp = (await apiGet(`/spaces/${spaceSlug}/posts`, params));
|
|
289
314
|
if (isJsonMode(space)) {
|
|
290
315
|
jsonOut({
|
|
@@ -304,7 +329,8 @@ export function registerSpaceCommand(program) {
|
|
|
304
329
|
for (const t of items) {
|
|
305
330
|
const author = t.author?.name ||
|
|
306
331
|
`User ${t.authorId}`;
|
|
307
|
-
|
|
332
|
+
const attachSummary = formatAttachmentSummary(t);
|
|
333
|
+
lines.push(`- [${t.id}] "${t.title}" by ${author} (${t.replyCount} replies, ${t.createdAt})${attachSummary ? ` ${attachSummary}` : ""}`);
|
|
308
334
|
}
|
|
309
335
|
const footer = pagination.hasMore ? `\n Next cursor: ${pagination.nextCursor}` : "";
|
|
310
336
|
console.log(`Posts (${items.length} items):\n` + lines.join("\n") + footer);
|
|
@@ -317,8 +343,9 @@ export function registerSpaceCommand(program) {
|
|
|
317
343
|
.option("--rich-text <richText>", "Rich-text JSON array (mutually exclusive with --content)")
|
|
318
344
|
.option("--artifact <artifactId>", "Attach an existing artifact to the post (repeatable). Create artifacts with `gobi artifact create`.", (value, prev = []) => [...prev, value], [])
|
|
319
345
|
.option("--space-slug <spaceSlug>", "Space slug (overrides .gobi/settings.yaml)")
|
|
320
|
-
.option("--attach <file>", "Local media file to attach. Repeatable.
|
|
346
|
+
.option("--attach <file>", "Local media or document file to attach. Repeatable. Mix rule: up to 4 photos + up to 4 document files (pdf/md/txt/csv) OR 1 GIF OR 1 video. Size ceilings: 10MB photos / 15MB GIFs / 512MB video / 250MB files.", (value, prev = []) => [...prev, value], [])
|
|
321
347
|
.option("--repost-post-id <postId>", "Wrap an existing top-level post as the embedded card on this new post. Composes with --content / --rich-text / --attach (the wrapping author's text + media render above the embedded card). Reposts-of-reposts are collapsed to the transitive root server-side. The referenced post must exist, not be deleted, and not itself be a reply.")
|
|
348
|
+
.option("--channel <channelId>", "Channel id to post into (see `list-channels`). Omit to post to the space's main feed. You must be able to see the channel (member, space owner/admin, or the space agent on an agent-enabled channel).")
|
|
322
349
|
.action(async (opts) => {
|
|
323
350
|
if (!opts.content && !opts.richText) {
|
|
324
351
|
throw new Error("Provide either --content or --rich-text.");
|
|
@@ -355,6 +382,9 @@ export function registerSpaceCommand(program) {
|
|
|
355
382
|
}
|
|
356
383
|
body.repostPostId = n;
|
|
357
384
|
}
|
|
385
|
+
const channelId = parseChannelIdOption(opts.channel);
|
|
386
|
+
if (channelId != null)
|
|
387
|
+
body.channelId = channelId;
|
|
358
388
|
const spaceSlug = resolveSpaceSlug(space, opts);
|
|
359
389
|
const resp = (await apiPost(`/spaces/${spaceSlug}/posts`, body));
|
|
360
390
|
const post = unwrapResp(resp);
|
|
@@ -376,7 +406,7 @@ export function registerSpaceCommand(program) {
|
|
|
376
406
|
.option("--content <content>", "New content for the post (markdown supported, use \"-\" for stdin)")
|
|
377
407
|
.option("--rich-text <richText>", "Rich-text JSON array (mutually exclusive with --content)")
|
|
378
408
|
.option("--space-slug <spaceSlug>", "Space slug (overrides .gobi/settings.yaml)")
|
|
379
|
-
.option("--attach <file>", "Replace the post's media attachments with the given files (existing attachments are removed). Repeatable.
|
|
409
|
+
.option("--attach <file>", "Replace the post's media attachments with the given files (existing attachments are removed). Repeatable. Mix rule: up to 4 photos + up to 4 document files (pdf/md/txt/csv) OR 1 GIF OR 1 video. Size ceilings: 10MB photos / 15MB GIFs / 512MB video / 250MB files. Omit to leave attachments unchanged.", (value, prev = []) => [...prev, value], [])
|
|
380
410
|
.option("--artifact <artifactId>", "Replace the post's artifact attachments with the given artifact(s) (existing artifact attachments are removed). Repeatable. Omit to leave them unchanged. Create artifacts with `gobi artifact create`.", (value, prev = []) => [...prev, value], [])
|
|
381
411
|
.action(async (postId, opts) => {
|
|
382
412
|
const wantsAttachChange = !!(opts.attach && opts.attach.length > 0);
|
|
@@ -445,7 +475,7 @@ export function registerSpaceCommand(program) {
|
|
|
445
475
|
.option("--content <content>", "Reply content (markdown supported, use \"-\" for stdin)")
|
|
446
476
|
.option("--rich-text <richText>", "Rich-text JSON array (mutually exclusive with --content)")
|
|
447
477
|
.option("--space-slug <spaceSlug>", "Space slug (overrides .gobi/settings.yaml)")
|
|
448
|
-
.option("--attach <file>", "Local media file to attach to this reply. Repeatable.
|
|
478
|
+
.option("--attach <file>", "Local media or document file to attach to this reply. Repeatable. Mix rule: up to 4 photos + up to 4 document files (pdf/md/txt/csv) OR 1 GIF OR 1 video. Size ceilings: 10MB photos / 15MB GIFs / 512MB video / 250MB files.", (value, prev = []) => [...prev, value], [])
|
|
449
479
|
.action(async (postId, opts) => {
|
|
450
480
|
if (!opts.content && !opts.richText) {
|
|
451
481
|
throw new Error("Provide either --content or --rich-text.");
|
|
@@ -530,4 +560,113 @@ export function registerSpaceCommand(program) {
|
|
|
530
560
|
}
|
|
531
561
|
console.log(`Reply ${replyId} deleted.`);
|
|
532
562
|
});
|
|
563
|
+
// ── Reactions (react, unreact) ──
|
|
564
|
+
space
|
|
565
|
+
.command("react <postId> <emoji>")
|
|
566
|
+
.description("Add an emoji reaction to a post or reply (idempotent). <postId> is the numeric id of a post OR a reply — the [p:N]/[r:N] ids shown in feed output.")
|
|
567
|
+
.option("--space-slug <spaceSlug>", "Space slug (overrides .gobi/settings.yaml)")
|
|
568
|
+
.action(async (postId, emoji, opts) => {
|
|
569
|
+
const spaceSlug = resolveSpaceSlug(space, opts);
|
|
570
|
+
const resp = (await apiPut(`/spaces/${spaceSlug}/posts/${postId}/reactions`, { emoji }));
|
|
571
|
+
const data = unwrapResp(resp);
|
|
572
|
+
if (isJsonMode(space)) {
|
|
573
|
+
jsonOut(data);
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
576
|
+
const chips = formatReactionChips(data);
|
|
577
|
+
console.log(`Reacted ${emoji} to ${postId}.` + (chips ? `\n Now: ${chips}` : ""));
|
|
578
|
+
});
|
|
579
|
+
space
|
|
580
|
+
.command("unreact <postId> <emoji>")
|
|
581
|
+
.description("Remove your emoji reaction from a post or reply. <postId> is the numeric id of a post OR a reply.")
|
|
582
|
+
.option("--space-slug <spaceSlug>", "Space slug (overrides .gobi/settings.yaml)")
|
|
583
|
+
.action(async (postId, emoji, opts) => {
|
|
584
|
+
const spaceSlug = resolveSpaceSlug(space, opts);
|
|
585
|
+
const resp = (await apiDelete(`/spaces/${spaceSlug}/posts/${postId}/reactions/${encodeURIComponent(emoji)}`));
|
|
586
|
+
const data = unwrapResp(resp);
|
|
587
|
+
if (isJsonMode(space)) {
|
|
588
|
+
jsonOut(data);
|
|
589
|
+
return;
|
|
590
|
+
}
|
|
591
|
+
const chips = formatReactionChips(data);
|
|
592
|
+
console.log(`Removed ${emoji} reaction from ${postId}.` +
|
|
593
|
+
(chips ? `\n Now: ${chips}` : ""));
|
|
594
|
+
});
|
|
595
|
+
// ── Channels (read-only; channel admin is web-UI only) ──
|
|
596
|
+
//
|
|
597
|
+
// Private member-gated sub-feeds inside a space. The main feed is virtual
|
|
598
|
+
// (no channel row): feed/list-posts/create-post without --channel target
|
|
599
|
+
// it. Members see their channels; space owners/admins see all; the space
|
|
600
|
+
// agent sees agent-enabled channels only. Create/rename/delete and roster
|
|
601
|
+
// management are deliberately not exposed here — like space and member
|
|
602
|
+
// admin, that's web-UI territory.
|
|
603
|
+
space
|
|
604
|
+
.command("list-channels")
|
|
605
|
+
.description("List channels visible to you in a space (members: yours; space owner/admin: all). The main feed is not a channel — read it by omitting --channel on `feed`.")
|
|
606
|
+
.option("--space-slug <spaceSlug>", "Space slug (overrides .gobi/settings.yaml)")
|
|
607
|
+
.action(async (opts) => {
|
|
608
|
+
const spaceSlug = resolveSpaceSlug(space, opts);
|
|
609
|
+
const resp = (await apiGet(`/spaces/${spaceSlug}/channels`));
|
|
610
|
+
const items = (resp.data || []);
|
|
611
|
+
if (isJsonMode(space)) {
|
|
612
|
+
jsonOut(items);
|
|
613
|
+
return;
|
|
614
|
+
}
|
|
615
|
+
if (!items.length) {
|
|
616
|
+
console.log("No channels found.");
|
|
617
|
+
return;
|
|
618
|
+
}
|
|
619
|
+
const lines = [];
|
|
620
|
+
for (const c of items) {
|
|
621
|
+
const flags = [
|
|
622
|
+
`${c.memberCount} member${c.memberCount === 1 ? "" : "s"}`,
|
|
623
|
+
c.isMember ? "member: you" : "member: no",
|
|
624
|
+
c.agentAccess ? "agent: on" : "agent: off",
|
|
625
|
+
].join(", ");
|
|
626
|
+
lines.push(`- [${c.id}] #${c.name} (${flags})`);
|
|
627
|
+
if (c.description)
|
|
628
|
+
lines.push(` Description: ${c.description}`);
|
|
629
|
+
}
|
|
630
|
+
console.log(`Channels (${items.length}):\n` + lines.join("\n"));
|
|
631
|
+
});
|
|
632
|
+
space
|
|
633
|
+
.command("get-channel <channelId>")
|
|
634
|
+
.description("Get one channel (channel members, space owner/admin, or the agent on agent-enabled channels).")
|
|
635
|
+
.option("--space-slug <spaceSlug>", "Space slug (overrides .gobi/settings.yaml)")
|
|
636
|
+
.action(async (channelId, opts) => {
|
|
637
|
+
const spaceSlug = resolveSpaceSlug(space, opts);
|
|
638
|
+
const resp = (await apiGet(`/spaces/${spaceSlug}/channels/${channelId}`));
|
|
639
|
+
const c = unwrapResp(resp);
|
|
640
|
+
if (isJsonMode(space)) {
|
|
641
|
+
jsonOut(c);
|
|
642
|
+
return;
|
|
643
|
+
}
|
|
644
|
+
const desc = c.description ? `\n Description: ${c.description}` : "";
|
|
645
|
+
console.log(`Channel [${c.id}] #${c.name}${desc}\n` +
|
|
646
|
+
` Agent access: ${c.agentAccess ? "on" : "off"}\n` +
|
|
647
|
+
` Created: ${c.createdAt}`);
|
|
648
|
+
});
|
|
649
|
+
space
|
|
650
|
+
.command("list-channel-members <channelId>")
|
|
651
|
+
.description("List the members of a channel.")
|
|
652
|
+
.option("--space-slug <spaceSlug>", "Space slug (overrides .gobi/settings.yaml)")
|
|
653
|
+
.action(async (channelId, opts) => {
|
|
654
|
+
const spaceSlug = resolveSpaceSlug(space, opts);
|
|
655
|
+
const resp = (await apiGet(`/spaces/${spaceSlug}/channels/${channelId}/members`));
|
|
656
|
+
const items = (resp.data || []);
|
|
657
|
+
if (isJsonMode(space)) {
|
|
658
|
+
jsonOut(items);
|
|
659
|
+
return;
|
|
660
|
+
}
|
|
661
|
+
if (!items.length) {
|
|
662
|
+
console.log("No members found.");
|
|
663
|
+
return;
|
|
664
|
+
}
|
|
665
|
+
const lines = [];
|
|
666
|
+
for (const m of items) {
|
|
667
|
+
const user = (m.user || {});
|
|
668
|
+
lines.push(`- [${m.userId}] ${user.name || "Unknown"} (joined ${m.createdAt})`);
|
|
669
|
+
}
|
|
670
|
+
console.log(`Channel members (${items.length}):\n` + lines.join("\n"));
|
|
671
|
+
});
|
|
533
672
|
}
|
package/dist/commands/utils.js
CHANGED
|
@@ -28,3 +28,73 @@ export function unwrapResp(resp) {
|
|
|
28
28
|
}
|
|
29
29
|
return resp;
|
|
30
30
|
}
|
|
31
|
+
// Compact reaction chips for single-line output: `👍2* 🎉1` — the trailing
|
|
32
|
+
// `*` marks emojis the caller has reacted with (remove via `unreact`).
|
|
33
|
+
export function formatReactionChips(m) {
|
|
34
|
+
const reactions = m.reactions || [];
|
|
35
|
+
if (!Array.isArray(reactions) || !reactions.length)
|
|
36
|
+
return "";
|
|
37
|
+
return reactions
|
|
38
|
+
.map((r) => `${r.emoji}${r.count}${r.reactedByMe ? "*" : ""}`)
|
|
39
|
+
.join(" ");
|
|
40
|
+
}
|
|
41
|
+
// Classify a wire attachment the way the clients do: declared mimeType first,
|
|
42
|
+
// then the media-url extension (covers rows that predate the mimeType field).
|
|
43
|
+
function attachmentKind(a) {
|
|
44
|
+
if (a.artifact)
|
|
45
|
+
return "artifact";
|
|
46
|
+
const mime = (a.mimeType || "").toLowerCase();
|
|
47
|
+
const url = (a.mediaUrl || "").toLowerCase().split("?")[0];
|
|
48
|
+
if (mime === "image/gif" || (!mime && url.endsWith(".gif")))
|
|
49
|
+
return "gif";
|
|
50
|
+
if (mime.startsWith("image/"))
|
|
51
|
+
return "photo";
|
|
52
|
+
if (mime.startsWith("video/"))
|
|
53
|
+
return "video";
|
|
54
|
+
if (mime)
|
|
55
|
+
return "file";
|
|
56
|
+
if (/\.(jpe?g|png|webp|heic|heif|avif|svg|bmp|tiff)$/.test(url))
|
|
57
|
+
return "photo";
|
|
58
|
+
if (/\.(mp4|mov|webm|m4v)$/.test(url))
|
|
59
|
+
return "video";
|
|
60
|
+
if (/\.(pdf|md|txt|csv)$/.test(url))
|
|
61
|
+
return "file";
|
|
62
|
+
return "media";
|
|
63
|
+
}
|
|
64
|
+
// Compact attachment marker for single-line output: `📎 2 photos, 1 file`.
|
|
65
|
+
// Counts per kind; full URLs are in `get-post` (or --json).
|
|
66
|
+
export function formatAttachmentSummary(m) {
|
|
67
|
+
const attachments = m.attachments || [];
|
|
68
|
+
if (!Array.isArray(attachments) || !attachments.length)
|
|
69
|
+
return "";
|
|
70
|
+
const counts = new Map();
|
|
71
|
+
for (const a of attachments) {
|
|
72
|
+
const kind = attachmentKind(a);
|
|
73
|
+
counts.set(kind, (counts.get(kind) ?? 0) + 1);
|
|
74
|
+
}
|
|
75
|
+
const parts = [];
|
|
76
|
+
for (const [kind, count] of counts) {
|
|
77
|
+
parts.push(count === 1 ? `1 ${kind}` : `${count} ${kind}s`);
|
|
78
|
+
}
|
|
79
|
+
return `📎 ${parts.join(", ")}`;
|
|
80
|
+
}
|
|
81
|
+
// One line per attachment with the fetchable URL — used by `get-post` so an
|
|
82
|
+
// agent can download/describe the media without re-querying in --json mode.
|
|
83
|
+
export function formatAttachmentLines(m, indent = " ") {
|
|
84
|
+
const attachments = m.attachments || [];
|
|
85
|
+
if (!Array.isArray(attachments) || !attachments.length)
|
|
86
|
+
return [];
|
|
87
|
+
return attachments.map((a) => {
|
|
88
|
+
const kind = attachmentKind(a);
|
|
89
|
+
if (kind === "artifact") {
|
|
90
|
+
const art = a.artifact;
|
|
91
|
+
const title = art.title ? ` "${art.title}"` : "";
|
|
92
|
+
const status = art.isPublished ? "published" : "unpublished";
|
|
93
|
+
return `${indent}- artifact [${art.kind}]${title} (${art.artifactId}, ${status})${art.mediaUrl ? ` — ${art.mediaUrl}` : ""}`;
|
|
94
|
+
}
|
|
95
|
+
const dims = a.width && a.height ? ` ${a.width}×${a.height}` : "";
|
|
96
|
+
const name = a.fileName ? ` "${a.fileName}"` : "";
|
|
97
|
+
const mime = a.mimeType ? ` (${a.mimeType})` : "";
|
|
98
|
+
return `${indent}- ${kind}${name}${dims}${mime} — ${a.mediaUrl}`;
|
|
99
|
+
});
|
|
100
|
+
}
|
package/package.json
CHANGED
|
@@ -9,12 +9,12 @@ description: >-
|
|
|
9
9
|
allowed-tools: Bash(gobi:*)
|
|
10
10
|
metadata:
|
|
11
11
|
author: gobi-ai
|
|
12
|
-
version: "2.0.
|
|
12
|
+
version: "2.0.31"
|
|
13
13
|
---
|
|
14
14
|
|
|
15
15
|
# gobi-artifact
|
|
16
16
|
|
|
17
|
-
Gobi artifact commands for versioned, post-attachable creations (v2.0.
|
|
17
|
+
Gobi artifact commands for versioned, post-attachable creations (v2.0.31).
|
|
18
18
|
|
|
19
19
|
Requires gobi-cli installed and authenticated. See gobi-core skill for setup.
|
|
20
20
|
|
|
@@ -8,12 +8,12 @@ description: >-
|
|
|
8
8
|
allowed-tools: Bash(gobi:*)
|
|
9
9
|
metadata:
|
|
10
10
|
author: gobi-ai
|
|
11
|
-
version: "2.0.
|
|
11
|
+
version: "2.0.31"
|
|
12
12
|
---
|
|
13
13
|
|
|
14
14
|
# gobi-core
|
|
15
15
|
|
|
16
|
-
Core CLI commands for the Gobi collaborative knowledge platform (v2.0.
|
|
16
|
+
Core CLI commands for the Gobi collaborative knowledge platform (v2.0.31).
|
|
17
17
|
|
|
18
18
|
## Prerequisites
|
|
19
19
|
|
|
@@ -6,13 +6,13 @@ Usage: gobi space [options] [command]
|
|
|
6
6
|
Space commands (posts, replies). Space and member admin is web-UI only.
|
|
7
7
|
|
|
8
8
|
Options:
|
|
9
|
-
--space-slug <spaceSlug>
|
|
10
|
-
-h, --help
|
|
9
|
+
--space-slug <spaceSlug> Space slug (overrides .gobi/settings.yaml)
|
|
10
|
+
-h, --help display help for command
|
|
11
11
|
|
|
12
12
|
Commands:
|
|
13
|
-
list
|
|
14
|
-
warp [spaceSlug]
|
|
15
|
-
help [command]
|
|
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
|
+
help [command] display help for command
|
|
16
16
|
```
|
|
17
17
|
|
|
18
18
|
## list
|
|
@@ -10,12 +10,12 @@ description: >-
|
|
|
10
10
|
allowed-tools: Bash(gobi:*)
|
|
11
11
|
metadata:
|
|
12
12
|
author: gobi-ai
|
|
13
|
-
version: "2.0.
|
|
13
|
+
version: "2.0.31"
|
|
14
14
|
---
|
|
15
15
|
|
|
16
16
|
# gobi-media
|
|
17
17
|
|
|
18
|
-
Gobi media generation commands (v2.0.
|
|
18
|
+
Gobi media generation commands (v2.0.31).
|
|
19
19
|
|
|
20
20
|
Requires gobi-cli installed and authenticated. See gobi-core skill for setup.
|
|
21
21
|
|
|
@@ -7,12 +7,12 @@ description: >-
|
|
|
7
7
|
allowed-tools: Bash(gobi:*)
|
|
8
8
|
metadata:
|
|
9
9
|
author: gobi-ai
|
|
10
|
-
version: "2.0.
|
|
10
|
+
version: "2.0.31"
|
|
11
11
|
---
|
|
12
12
|
|
|
13
13
|
# gobi-sense
|
|
14
14
|
|
|
15
|
-
Gobi sense commands for activity and transcription data (v2.0.
|
|
15
|
+
Gobi sense commands for activity and transcription data (v2.0.31).
|
|
16
16
|
|
|
17
17
|
Requires gobi-cli installed and authenticated. See the **gobi-core** skill for setup.
|
|
18
18
|
|
|
@@ -11,12 +11,12 @@ description: >-
|
|
|
11
11
|
allowed-tools: Bash(gobi:*)
|
|
12
12
|
metadata:
|
|
13
13
|
author: gobi-ai
|
|
14
|
-
version: "2.0.
|
|
14
|
+
version: "2.0.31"
|
|
15
15
|
---
|
|
16
16
|
|
|
17
17
|
# gobi-space
|
|
18
18
|
|
|
19
|
-
Gobi space, global, and personal-space posts (v2.0.
|
|
19
|
+
Gobi space, global, and personal-space posts (v2.0.31).
|
|
20
20
|
|
|
21
21
|
Requires gobi-cli installed and authenticated. See the **gobi-core** skill for setup.
|
|
22
22
|
|
|
@@ -51,13 +51,15 @@ Posts have no vault attribution. Both `create-post` and `edit-post` across all t
|
|
|
51
51
|
|
|
52
52
|
> **Wiki-link uploads moved to artifacts.** The `--auto-attachments` flag that used to upload `[[wiki-linked files]]` from a post body now lives on `gobi artifact create` / `gobi artifact revise` (markdown kinds). To publish a markdown creation with resolvable wikilinks, create a markdown artifact with `--vault-slug` + `--auto-attachments` and attach it to the post (`--post-id`). See the **gobi-artifact** skill. Before relying on wikilink resolution, confirm the anchor vault is published: `gobi --json vault status --vault-slug <slug>` should report `isPublished: true`.
|
|
53
53
|
|
|
54
|
-
## Post media attachments (`--attach`)
|
|
54
|
+
## Post media + file attachments (`--attach`)
|
|
55
55
|
|
|
56
|
-
`create-post` and `create-reply` across all three scopes (`gobi space`, `gobi global`, `gobi personal`) accept `--attach <file>` (repeatable) for inline post
|
|
56
|
+
`create-post` and `create-reply` across all three scopes (`gobi space`, `gobi global`, `gobi personal`) accept `--attach <file>` (repeatable) for inline post attachments — photos/GIF/video that render in-feed alongside the post body, and document files (**pdf/md/txt/csv**) that render as Slack-style file cards. The CLI uploads each file to S3 via `POST /posts/upload-url`; document files additionally carry `fileName` + `mimeType` on the row (the S3 key is a UUID, so that's the only place the original name survives).
|
|
57
57
|
|
|
58
|
-
|
|
58
|
+
Mix rule (enforced client-side before upload): up to **4 photos + 4 document files** together, OR **1 GIF**, OR **1 video** — GIF and video are exclusive with everything. Size ceilings: 10MB photos, 15MB GIFs, 512MB video, 250MB document files.
|
|
59
59
|
|
|
60
|
-
Use `--attach` for media you want shown in the post itself; use a markdown **artifact** (`gobi artifact create`) for `[[wikilinks]]`-bearing creations attached to the post.
|
|
60
|
+
Use `--attach` for media/files you want shown in the post itself; use a markdown **artifact** (`gobi artifact create`) for `[[wikilinks]]`-bearing creations attached to the post.
|
|
61
|
+
|
|
62
|
+
**Reading attachments back:** feed and list lines show a compact marker like `📎 2 photos, 1 file`; `get-post` prints an `Attachments (N):` block with one line per attachment — kind, original fileName, dimensions/MIME, and the fetchable CDN URL (artifact attachments show kind/title/artifactId instead). In `--json` mode the full `attachments` array is on every post/reply object.
|
|
61
63
|
|
|
62
64
|
## Public link formats
|
|
63
65
|
|
|
@@ -106,7 +108,7 @@ gobi --json space list-posts
|
|
|
106
108
|
- `gobi space list-topic-posts` — List posts tagged with a topic in a space (cursor-paginated).
|
|
107
109
|
|
|
108
110
|
### Feed
|
|
109
|
-
- `gobi space feed` — List the unified feed (posts and replies, newest first).
|
|
111
|
+
- `gobi space feed` — List the unified feed (posts and replies, newest first). `--channel <channelId>` reads a channel's feed instead of the main feed.
|
|
110
112
|
|
|
111
113
|
### Space posts
|
|
112
114
|
- `gobi space list-posts` — List posts in a space (paginated).
|
|
@@ -120,6 +122,22 @@ gobi --json space list-posts
|
|
|
120
122
|
- `gobi space edit-reply <replyId>` — Edit a reply. You must be the author.
|
|
121
123
|
- `gobi space delete-reply <replyId>` — Delete a reply. You must be the author.
|
|
122
124
|
|
|
125
|
+
### Reactions
|
|
126
|
+
|
|
127
|
+
Emoji reactions on posts **and** replies, across all three scopes (`gobi space`, `gobi global`, `gobi personal`). The id is the bare number from the `[p:N]`/`[r:N]` ids in feed output. Feed and `get-post` lines render existing reactions as compact chips like `👍2* 🎉1` — the count follows each emoji, and a trailing `*` marks ones you reacted with.
|
|
128
|
+
|
|
129
|
+
- `gobi space react <postId> <emoji>` — Add a reaction (idempotent; re-reacting with the same emoji is a no-op).
|
|
130
|
+
- `gobi space unreact <postId> <emoji>` — Remove your reaction. Pass the emoji literally (`gobi space unreact 123 👍`).
|
|
131
|
+
|
|
132
|
+
### Channels (space scope only)
|
|
133
|
+
|
|
134
|
+
Channels are private, member-gated sub-feeds inside a space. The **main feed is not a channel** — `feed` / `list-posts` / `create-post` without `--channel` target it, as always. Members see their own channels; space owners/admins see all of them; the space agent sees only channels with agent access enabled. Posting into a channel requires being able to see it. Channel administration (create, rename, delete, add/remove members, leave) is web-UI only — like space and member admin, the CLI deliberately doesn't expose it.
|
|
135
|
+
|
|
136
|
+
- `gobi space list-channels` — List channels visible to you (shows member count, your membership, agent access).
|
|
137
|
+
- `gobi space get-channel <channelId>` — Get one channel's details.
|
|
138
|
+
- `gobi space list-channel-members <channelId>` — List a channel's members.
|
|
139
|
+
- `--channel <channelId>` on `feed`, `list-posts`, and `create-post` reads/writes that channel instead of the main feed.
|
|
140
|
+
|
|
123
141
|
### Personal posts (global feed)
|
|
124
142
|
|
|
125
143
|
`gobi global` is the same surface for Personal Posts — posts that live on the author's profile and surface in the public global feed.
|
|
@@ -157,8 +175,9 @@ Most posts and replies are publicly visible — in a community space (`gobi spac
|
|
|
157
175
|
- `create-post` / `create-reply` — content goes live on submission.
|
|
158
176
|
- `edit-post` / `edit-reply` — confirm the *new* content; people who already saw the original may re-see it.
|
|
159
177
|
- `delete-post` / `delete-reply` — irreversible. Flag that explicitly and confirm the target id before running.
|
|
178
|
+
- `react` / `unreact` are lightweight and reversible — when the user asked for the reaction, no extra confirmation needed.
|
|
160
179
|
|
|
161
|
-
Read-only commands (`list-posts`, `get-post`, `feed`, `list-topics`, `list-topic-posts`, `get`) run without confirmation.
|
|
180
|
+
Read-only commands (`list-posts`, `get-post`, `feed`, `list-topics`, `list-topic-posts`, `get`, `list-channels`, `get-channel`, `list-channel-members`) run without confirmation.
|
|
162
181
|
|
|
163
182
|
## Reference Documentation
|
|
164
183
|
|
|
@@ -18,6 +18,8 @@ Commands:
|
|
|
18
18
|
create-reply [options] <postId> Create a reply to a post in the global feed.
|
|
19
19
|
edit-reply [options] <replyId> Edit a reply you authored in the global feed.
|
|
20
20
|
delete-reply <replyId> Delete a reply you authored in the global feed.
|
|
21
|
+
react <postId> <emoji> Add an emoji reaction to a global-feed post or reply (idempotent). <postId> is the numeric id of a post OR a reply — the [p:N]/[r:N] ids shown in feed output.
|
|
22
|
+
unreact <postId> <emoji> Remove your emoji reaction from a global-feed post or reply. <postId> is the numeric id of a post OR a reply.
|
|
21
23
|
help [command] display help for command
|
|
22
24
|
```
|
|
23
25
|
|
|
@@ -75,7 +77,8 @@ Options:
|
|
|
75
77
|
--content <content> Post content (markdown supported, use "-" for stdin)
|
|
76
78
|
--rich-text <richText> Rich-text JSON array (mutually exclusive with --content)
|
|
77
79
|
--artifact <artifactId> Attach an existing artifact to the post (repeatable). Create artifacts with `gobi artifact create`. (default: [])
|
|
78
|
-
--attach <file> Local media file to attach. Repeatable.
|
|
80
|
+
--attach <file> Local media or document file to attach. Repeatable. Mix rule: up to 4 photos + up to 4 document files (pdf/md/txt/csv) OR 1 GIF OR 1 video. Size ceilings: 10MB photos /
|
|
81
|
+
15MB GIFs / 512MB video / 250MB files. (default: [])
|
|
79
82
|
--repost-post-id <postId> Wrap an existing top-level post as the embedded card on this new post. Composes with --content / --rich-text / --attach (the wrapping author's text + media render above
|
|
80
83
|
the embedded card). Reposts-of-reposts are collapsed to the transitive root server-side. The referenced post must exist, not be deleted, and not itself be a reply.
|
|
81
84
|
-h, --help display help for command
|
|
@@ -92,8 +95,8 @@ Options:
|
|
|
92
95
|
--title <title> New title
|
|
93
96
|
--content <content> New content (markdown supported, use "-" for stdin)
|
|
94
97
|
--rich-text <richText> Rich-text JSON array (mutually exclusive with --content)
|
|
95
|
-
--attach <file> Replace the post's media attachments with the given files (existing attachments are removed). Repeatable.
|
|
96
|
-
ceilings:
|
|
98
|
+
--attach <file> Replace the post's media attachments with the given files (existing attachments are removed). Repeatable. Mix rule: up to 4 photos + up to 4 document files (pdf/md/txt/csv)
|
|
99
|
+
OR 1 GIF OR 1 video. Size ceilings: 10MB photos / 15MB GIFs / 512MB video / 250MB files. Omit to leave attachments unchanged. (default: [])
|
|
97
100
|
--artifact <artifactId> Replace the post's artifact attachments with the given artifact(s) (existing artifact attachments are removed). Repeatable. Omit to leave them unchanged. Create artifacts
|
|
98
101
|
with `gobi artifact create`. (default: [])
|
|
99
102
|
-h, --help display help for command
|
|
@@ -120,8 +123,8 @@ Create a reply to a post in the global feed.
|
|
|
120
123
|
Options:
|
|
121
124
|
--content <content> Reply content (markdown supported, use "-" for stdin)
|
|
122
125
|
--rich-text <richText> Rich-text JSON array (mutually exclusive with --content)
|
|
123
|
-
--attach <file> Local media file to attach to this reply. Repeatable.
|
|
124
|
-
[])
|
|
126
|
+
--attach <file> Local media or document file to attach to this reply. Repeatable. Mix rule: up to 4 photos + up to 4 document files (pdf/md/txt/csv) OR 1 GIF OR 1 video. Size ceilings: 10MB
|
|
127
|
+
photos / 15MB GIFs / 512MB video / 250MB files. (default: [])
|
|
125
128
|
-h, --help display help for command
|
|
126
129
|
```
|
|
127
130
|
|
|
@@ -148,3 +151,25 @@ Delete a reply you authored in the global feed.
|
|
|
148
151
|
Options:
|
|
149
152
|
-h, --help display help for command
|
|
150
153
|
```
|
|
154
|
+
|
|
155
|
+
## react
|
|
156
|
+
|
|
157
|
+
```
|
|
158
|
+
Usage: gobi global react [options] <postId> <emoji>
|
|
159
|
+
|
|
160
|
+
Add an emoji reaction to a global-feed post or reply (idempotent). <postId> is the numeric id of a post OR a reply — the [p:N]/[r:N] ids shown in feed output.
|
|
161
|
+
|
|
162
|
+
Options:
|
|
163
|
+
-h, --help display help for command
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## unreact
|
|
167
|
+
|
|
168
|
+
```
|
|
169
|
+
Usage: gobi global unreact [options] <postId> <emoji>
|
|
170
|
+
|
|
171
|
+
Remove your emoji reaction from a global-feed post or reply. <postId> is the numeric id of a post OR a reply.
|
|
172
|
+
|
|
173
|
+
Options:
|
|
174
|
+
-h, --help display help for command
|
|
175
|
+
```
|
|
@@ -19,6 +19,8 @@ Commands:
|
|
|
19
19
|
create-reply [options] <postId> Reply to a personal-space post. The reply inherits the parent's private scope automatically.
|
|
20
20
|
edit-reply [options] <replyId> Edit a reply you authored in your personal space.
|
|
21
21
|
delete-reply <replyId> Delete a reply you authored in your personal space.
|
|
22
|
+
react <postId> <emoji> Add an emoji reaction to a personal-space post or reply (idempotent). <postId> is the numeric id of a post OR a reply.
|
|
23
|
+
unreact <postId> <emoji> Remove your emoji reaction from a personal-space post or reply. <postId> is the numeric id of a post OR a reply.
|
|
22
24
|
help [command] display help for command
|
|
23
25
|
```
|
|
24
26
|
|
|
@@ -74,7 +76,8 @@ Options:
|
|
|
74
76
|
--content <content> Post content (markdown supported, use "-" for stdin)
|
|
75
77
|
--rich-text <richText> Rich-text JSON array (mutually exclusive with --content)
|
|
76
78
|
--artifact <artifactId> Attach an existing artifact to the post (repeatable). Create artifacts with `gobi artifact create`. (default: [])
|
|
77
|
-
--attach <file> Local media file to attach. Repeatable.
|
|
79
|
+
--attach <file> Local media or document file to attach. Repeatable. Mix rule: up to 4 photos + up to 4 document files (pdf/md/txt/csv) OR 1 GIF OR 1 video. Size ceilings: 10MB photos /
|
|
80
|
+
15MB GIFs / 512MB video / 250MB files. (default: [])
|
|
78
81
|
--repost-post-id <postId> Wrap an existing top-level post as the embedded card on this new private post. The referenced post must be visible to you (your own personal-space post, a global-feed
|
|
79
82
|
post, or a post in a space you're a member of). Reposting someone else's personal-space post returns 404.
|
|
80
83
|
-h, --help display help for command
|
|
@@ -91,8 +94,8 @@ Options:
|
|
|
91
94
|
--title <title> New title
|
|
92
95
|
--content <content> New content (markdown supported, use "-" for stdin)
|
|
93
96
|
--rich-text <richText> Rich-text JSON array (mutually exclusive with --content)
|
|
94
|
-
--attach <file> Replace the post's media attachments with the given files (existing attachments are removed). Repeatable.
|
|
95
|
-
ceilings:
|
|
97
|
+
--attach <file> Replace the post's media attachments with the given files (existing attachments are removed). Repeatable. Mix rule: up to 4 photos + up to 4 document files (pdf/md/txt/csv)
|
|
98
|
+
OR 1 GIF OR 1 video. Size ceilings: 10MB photos / 15MB GIFs / 512MB video / 250MB files. Omit to leave attachments unchanged. (default: [])
|
|
96
99
|
--artifact <artifactId> Replace the post's artifact attachments with the given artifact(s) (existing artifact attachments are removed). Repeatable. Omit to leave them unchanged. Create artifacts
|
|
97
100
|
with `gobi artifact create`. (default: [])
|
|
98
101
|
-h, --help display help for command
|
|
@@ -119,8 +122,8 @@ Reply to a personal-space post. The reply inherits the parent's private scope au
|
|
|
119
122
|
Options:
|
|
120
123
|
--content <content> Reply content (markdown supported, use "-" for stdin)
|
|
121
124
|
--rich-text <richText> Rich-text JSON array (mutually exclusive with --content)
|
|
122
|
-
--attach <file> Local media file to attach to this reply. Repeatable.
|
|
123
|
-
[])
|
|
125
|
+
--attach <file> Local media or document file to attach to this reply. Repeatable. Mix rule: up to 4 photos + up to 4 document files (pdf/md/txt/csv) OR 1 GIF OR 1 video. Size ceilings: 10MB
|
|
126
|
+
photos / 15MB GIFs / 512MB video / 250MB files. (default: [])
|
|
124
127
|
-h, --help display help for command
|
|
125
128
|
```
|
|
126
129
|
|
|
@@ -147,3 +150,25 @@ Delete a reply you authored in your personal space.
|
|
|
147
150
|
Options:
|
|
148
151
|
-h, --help display help for command
|
|
149
152
|
```
|
|
153
|
+
|
|
154
|
+
## react
|
|
155
|
+
|
|
156
|
+
```
|
|
157
|
+
Usage: gobi personal react [options] <postId> <emoji>
|
|
158
|
+
|
|
159
|
+
Add an emoji reaction to a personal-space post or reply (idempotent). <postId> is the numeric id of a post OR a reply.
|
|
160
|
+
|
|
161
|
+
Options:
|
|
162
|
+
-h, --help display help for command
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## unreact
|
|
166
|
+
|
|
167
|
+
```
|
|
168
|
+
Usage: gobi personal unreact [options] <postId> <emoji>
|
|
169
|
+
|
|
170
|
+
Remove your emoji reaction from a personal-space post or reply. <postId> is the numeric id of a post OR a reply.
|
|
171
|
+
|
|
172
|
+
Options:
|
|
173
|
+
-h, --help display help for command
|
|
174
|
+
```
|
|
@@ -6,23 +6,29 @@ Usage: gobi space [options] [command]
|
|
|
6
6
|
Space commands (posts, replies). Space and member admin is web-UI only.
|
|
7
7
|
|
|
8
8
|
Options:
|
|
9
|
-
--space-slug <spaceSlug>
|
|
10
|
-
-h, --help
|
|
9
|
+
--space-slug <spaceSlug> Space slug (overrides .gobi/settings.yaml)
|
|
10
|
+
-h, --help display help for command
|
|
11
11
|
|
|
12
12
|
Commands:
|
|
13
|
-
get [options] [spaceSlug]
|
|
14
|
-
list-topics [options]
|
|
15
|
-
list-topic-posts [options] <topicSlug>
|
|
16
|
-
feed [options]
|
|
17
|
-
get-post [options] <postId>
|
|
18
|
-
list-posts [options]
|
|
19
|
-
create-post [options]
|
|
20
|
-
edit-post [options] <postId>
|
|
21
|
-
delete-post [options] <postId>
|
|
22
|
-
create-reply [options] <postId>
|
|
23
|
-
edit-reply [options] <replyId>
|
|
24
|
-
delete-reply [options] <replyId>
|
|
25
|
-
|
|
13
|
+
get [options] [spaceSlug] Get details for a space. Pass a slug or omit to use the current space (from .gobi/settings.yaml or --space-slug).
|
|
14
|
+
list-topics [options] List topics in a space, ordered by most recent content linkage.
|
|
15
|
+
list-topic-posts [options] <topicSlug> List posts tagged with a topic in a space (cursor-paginated).
|
|
16
|
+
feed [options] List the unified feed (posts and replies, newest first) in a space.
|
|
17
|
+
get-post [options] <postId> Get a post with its ancestors and replies (paginated).
|
|
18
|
+
list-posts [options] List posts in a space (paginated).
|
|
19
|
+
create-post [options] Create a post in a space.
|
|
20
|
+
edit-post [options] <postId> Edit a post you authored in a space.
|
|
21
|
+
delete-post [options] <postId> Delete a post you authored in a space.
|
|
22
|
+
create-reply [options] <postId> Create a reply to a post in a space.
|
|
23
|
+
edit-reply [options] <replyId> Edit a reply you authored in a space.
|
|
24
|
+
delete-reply [options] <replyId> Delete a reply you authored in a space.
|
|
25
|
+
react [options] <postId> <emoji> Add an emoji reaction to a post or reply (idempotent). <postId> is the numeric id of a post OR a reply — the [p:N]/[r:N] ids shown in feed output.
|
|
26
|
+
unreact [options] <postId> <emoji> Remove your emoji reaction from a post or reply. <postId> is the numeric id of a post OR a reply.
|
|
27
|
+
list-channels [options] List channels visible to you in a space (members: yours; space owner/admin: all). The main feed is not a channel — read it by omitting --channel on
|
|
28
|
+
`feed`.
|
|
29
|
+
get-channel [options] <channelId> Get one channel (channel members, space owner/admin, or the agent on agent-enabled channels).
|
|
30
|
+
list-channel-members [options] <channelId> List the members of a channel.
|
|
31
|
+
help [command] display help for command
|
|
26
32
|
```
|
|
27
33
|
|
|
28
34
|
## get
|
|
@@ -74,6 +80,7 @@ List the unified feed (posts and replies, newest first) in a space.
|
|
|
74
80
|
Options:
|
|
75
81
|
--limit <number> Items per page (default: "20")
|
|
76
82
|
--cursor <string> Pagination cursor from previous response
|
|
83
|
+
--channel <channelId> Channel id to read instead of the main feed (see `list-channels`). Omit for the main feed.
|
|
77
84
|
--space-slug <spaceSlug> Space slug (overrides .gobi/settings.yaml)
|
|
78
85
|
-h, --help display help for command
|
|
79
86
|
```
|
|
@@ -103,6 +110,7 @@ List posts in a space (paginated).
|
|
|
103
110
|
Options:
|
|
104
111
|
--limit <number> Items per page (default: "20")
|
|
105
112
|
--cursor <string> Pagination cursor from previous response
|
|
113
|
+
--channel <channelId> Channel id to read instead of the main feed (see `list-channels`). Omit for the main feed.
|
|
106
114
|
--space-slug <spaceSlug> Space slug (overrides .gobi/settings.yaml)
|
|
107
115
|
-h, --help display help for command
|
|
108
116
|
```
|
|
@@ -120,9 +128,12 @@ Options:
|
|
|
120
128
|
--rich-text <richText> Rich-text JSON array (mutually exclusive with --content)
|
|
121
129
|
--artifact <artifactId> Attach an existing artifact to the post (repeatable). Create artifacts with `gobi artifact create`. (default: [])
|
|
122
130
|
--space-slug <spaceSlug> Space slug (overrides .gobi/settings.yaml)
|
|
123
|
-
--attach <file> Local media file to attach. Repeatable.
|
|
131
|
+
--attach <file> Local media or document file to attach. Repeatable. Mix rule: up to 4 photos + up to 4 document files (pdf/md/txt/csv) OR 1 GIF OR 1 video. Size ceilings: 10MB photos /
|
|
132
|
+
15MB GIFs / 512MB video / 250MB files. (default: [])
|
|
124
133
|
--repost-post-id <postId> Wrap an existing top-level post as the embedded card on this new post. Composes with --content / --rich-text / --attach (the wrapping author's text + media render above
|
|
125
134
|
the embedded card). Reposts-of-reposts are collapsed to the transitive root server-side. The referenced post must exist, not be deleted, and not itself be a reply.
|
|
135
|
+
--channel <channelId> Channel id to post into (see `list-channels`). Omit to post to the space's main feed. You must be able to see the channel (member, space owner/admin, or the space agent
|
|
136
|
+
on an agent-enabled channel).
|
|
126
137
|
-h, --help display help for command
|
|
127
138
|
```
|
|
128
139
|
|
|
@@ -138,8 +149,8 @@ Options:
|
|
|
138
149
|
--content <content> New content for the post (markdown supported, use "-" for stdin)
|
|
139
150
|
--rich-text <richText> Rich-text JSON array (mutually exclusive with --content)
|
|
140
151
|
--space-slug <spaceSlug> Space slug (overrides .gobi/settings.yaml)
|
|
141
|
-
--attach <file> Replace the post's media attachments with the given files (existing attachments are removed). Repeatable.
|
|
142
|
-
ceilings:
|
|
152
|
+
--attach <file> Replace the post's media attachments with the given files (existing attachments are removed). Repeatable. Mix rule: up to 4 photos + up to 4 document files
|
|
153
|
+
(pdf/md/txt/csv) OR 1 GIF OR 1 video. Size ceilings: 10MB photos / 15MB GIFs / 512MB video / 250MB files. Omit to leave attachments unchanged. (default: [])
|
|
143
154
|
--artifact <artifactId> Replace the post's artifact attachments with the given artifact(s) (existing artifact attachments are removed). Repeatable. Omit to leave them unchanged. Create artifacts
|
|
144
155
|
with `gobi artifact create`. (default: [])
|
|
145
156
|
-h, --help display help for command
|
|
@@ -168,8 +179,8 @@ Options:
|
|
|
168
179
|
--content <content> Reply content (markdown supported, use "-" for stdin)
|
|
169
180
|
--rich-text <richText> Rich-text JSON array (mutually exclusive with --content)
|
|
170
181
|
--space-slug <spaceSlug> Space slug (overrides .gobi/settings.yaml)
|
|
171
|
-
--attach <file> Local media file to attach to this reply. Repeatable.
|
|
172
|
-
[])
|
|
182
|
+
--attach <file> Local media or document file to attach to this reply. Repeatable. Mix rule: up to 4 photos + up to 4 document files (pdf/md/txt/csv) OR 1 GIF OR 1 video. Size ceilings:
|
|
183
|
+
10MB photos / 15MB GIFs / 512MB video / 250MB files. (default: [])
|
|
173
184
|
-h, --help display help for command
|
|
174
185
|
```
|
|
175
186
|
|
|
@@ -198,3 +209,63 @@ Options:
|
|
|
198
209
|
--space-slug <spaceSlug> Space slug (overrides .gobi/settings.yaml)
|
|
199
210
|
-h, --help display help for command
|
|
200
211
|
```
|
|
212
|
+
|
|
213
|
+
## react
|
|
214
|
+
|
|
215
|
+
```
|
|
216
|
+
Usage: gobi space react [options] <postId> <emoji>
|
|
217
|
+
|
|
218
|
+
Add an emoji reaction to a post or reply (idempotent). <postId> is the numeric id of a post OR a reply — the [p:N]/[r:N] ids shown in feed output.
|
|
219
|
+
|
|
220
|
+
Options:
|
|
221
|
+
--space-slug <spaceSlug> Space slug (overrides .gobi/settings.yaml)
|
|
222
|
+
-h, --help display help for command
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
## unreact
|
|
226
|
+
|
|
227
|
+
```
|
|
228
|
+
Usage: gobi space unreact [options] <postId> <emoji>
|
|
229
|
+
|
|
230
|
+
Remove your emoji reaction from a post or reply. <postId> is the numeric id of a post OR a reply.
|
|
231
|
+
|
|
232
|
+
Options:
|
|
233
|
+
--space-slug <spaceSlug> Space slug (overrides .gobi/settings.yaml)
|
|
234
|
+
-h, --help display help for command
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
## list-channels
|
|
238
|
+
|
|
239
|
+
```
|
|
240
|
+
Usage: gobi space list-channels [options]
|
|
241
|
+
|
|
242
|
+
List channels visible to you in a space (members: yours; space owner/admin: all). The main feed is not a channel — read it by omitting --channel on `feed`.
|
|
243
|
+
|
|
244
|
+
Options:
|
|
245
|
+
--space-slug <spaceSlug> Space slug (overrides .gobi/settings.yaml)
|
|
246
|
+
-h, --help display help for command
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
## get-channel
|
|
250
|
+
|
|
251
|
+
```
|
|
252
|
+
Usage: gobi space get-channel [options] <channelId>
|
|
253
|
+
|
|
254
|
+
Get one channel (channel members, space owner/admin, or the agent on agent-enabled channels).
|
|
255
|
+
|
|
256
|
+
Options:
|
|
257
|
+
--space-slug <spaceSlug> Space slug (overrides .gobi/settings.yaml)
|
|
258
|
+
-h, --help display help for command
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
## list-channel-members
|
|
262
|
+
|
|
263
|
+
```
|
|
264
|
+
Usage: gobi space list-channel-members [options] <channelId>
|
|
265
|
+
|
|
266
|
+
List the members of a channel.
|
|
267
|
+
|
|
268
|
+
Options:
|
|
269
|
+
--space-slug <spaceSlug> Space slug (overrides .gobi/settings.yaml)
|
|
270
|
+
-h, --help display help for command
|
|
271
|
+
```
|
|
@@ -8,12 +8,12 @@ description: >-
|
|
|
8
8
|
allowed-tools: Bash(gobi:*)
|
|
9
9
|
metadata:
|
|
10
10
|
author: gobi-ai
|
|
11
|
-
version: "2.0.
|
|
11
|
+
version: "2.0.31"
|
|
12
12
|
---
|
|
13
13
|
|
|
14
14
|
# gobi-vault
|
|
15
15
|
|
|
16
|
-
Gobi vault commands for publishing your vault profile and syncing files (v2.0.
|
|
16
|
+
Gobi vault commands for publishing your vault profile and syncing files (v2.0.31).
|
|
17
17
|
|
|
18
18
|
Requires gobi-cli installed and authenticated. See gobi-core skill for setup.
|
|
19
19
|
|