@gobi-ai/cli 2.0.36 → 2.0.40
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/README.md +24 -33
- package/commands/login.md +1 -1
- package/commands/logout.md +1 -1
- package/commands/post.md +1 -1
- package/commands/space-explore.md +3 -5
- package/commands/space-share.md +7 -7
- package/commands/warp.md +1 -1
- package/dist/commands/personal.js +39 -15
- package/dist/commands/sense.js +162 -111
- package/dist/commands/space.js +35 -0
- package/dist/commands/update.js +2 -2
- package/dist/commands/utils.js +1 -1
- package/dist/main.js +2 -6
- package/package.json +1 -1
- package/skills/gobi-artifact/SKILL.md +3 -3
- package/skills/gobi-artifact/references/personal.md +2 -2
- package/skills/gobi-core/SKILL.md +5 -6
- package/skills/gobi-homepage/SKILL.md +1 -1
- package/skills/gobi-media/SKILL.md +3 -3
- package/skills/gobi-sense/SKILL.md +62 -12
- package/skills/gobi-sense/references/personal.md +50 -0
- package/skills/gobi-sense/references/space.md +53 -0
- package/skills/gobi-space/SKILL.md +22 -40
- package/skills/gobi-space/references/personal.md +42 -6
- package/skills/gobi-vault/SKILL.md +3 -3
- package/dist/commands/global.js +0 -438
- package/skills/gobi-sense/references/sense.md +0 -41
- package/skills/gobi-space/references/global.md +0 -175
package/dist/commands/global.js
DELETED
|
@@ -1,438 +0,0 @@
|
|
|
1
|
-
import { WEB_BASE_URL } from "../constants.js";
|
|
2
|
-
import { apiGet, apiPost, apiPatch, apiPut, apiDelete } from "../client.js";
|
|
3
|
-
import { buildMentionMap, formatAttachmentLines, formatAttachmentSummary, formatPostLabel, formatReactionChips, formatReplyLine, isJsonMode, jsonOut, postBodyText, readStdin, unwrapResp, } from "./utils.js";
|
|
4
|
-
import { uploadPostAttachments, assertPostAttachmentMix, } from "../attachments.js";
|
|
5
|
-
function readContent(value) {
|
|
6
|
-
if (value === "-")
|
|
7
|
-
return readStdin();
|
|
8
|
-
return value;
|
|
9
|
-
}
|
|
10
|
-
function buildPersonalPostUrl(post) {
|
|
11
|
-
return `${WEB_BASE_URL}/posts/${post.id}`;
|
|
12
|
-
}
|
|
13
|
-
function formatFeedLine(m, mentions) {
|
|
14
|
-
const isReply = m.parentPostId != null ||
|
|
15
|
-
m.type === "post-reply";
|
|
16
|
-
const id = `[${isReply ? "r" : "p"}:${m.id}]`;
|
|
17
|
-
const kind = isReply ? "reply" : "post ";
|
|
18
|
-
const author = m.author?.name ||
|
|
19
|
-
`User ${m.authorId ?? "?"}`;
|
|
20
|
-
let label;
|
|
21
|
-
if (isReply) {
|
|
22
|
-
const text = postBodyText(m, mentions).replace(/\s+/g, " ").trim();
|
|
23
|
-
label = text.length > 80 ? text.slice(0, 80) + "…" : text;
|
|
24
|
-
}
|
|
25
|
-
else {
|
|
26
|
-
label = formatPostLabel(m, mentions);
|
|
27
|
-
}
|
|
28
|
-
const chips = formatReactionChips(m);
|
|
29
|
-
const attachSummary = formatAttachmentSummary(m);
|
|
30
|
-
return (`${id} ${kind} ${author} "${label}" ${m.createdAt}` +
|
|
31
|
-
(attachSummary ? ` ${attachSummary}` : "") +
|
|
32
|
-
(chips ? ` ${chips}` : ""));
|
|
33
|
-
}
|
|
34
|
-
export function registerGlobalCommand(program) {
|
|
35
|
-
const global = program
|
|
36
|
-
.command("global")
|
|
37
|
-
.description("Global commands (posts and replies in the public feed across all vaults).");
|
|
38
|
-
// ── Feed (unified) ──
|
|
39
|
-
global
|
|
40
|
-
.command("feed")
|
|
41
|
-
.description("List the unified feed (posts and replies, newest first) in the global public feed.")
|
|
42
|
-
.option("--limit <number>", "Items per page", "20")
|
|
43
|
-
.option("--cursor <string>", "Pagination cursor from previous response")
|
|
44
|
-
.option("--following", "Only include posts from authors you follow")
|
|
45
|
-
.action(async (opts) => {
|
|
46
|
-
const params = {
|
|
47
|
-
limit: parseInt(opts.limit, 10),
|
|
48
|
-
};
|
|
49
|
-
if (opts.cursor)
|
|
50
|
-
params.cursor = opts.cursor;
|
|
51
|
-
if (opts.following)
|
|
52
|
-
params.following = "true";
|
|
53
|
-
const resp = (await apiGet(`/posts/feed`, params));
|
|
54
|
-
if (isJsonMode(global)) {
|
|
55
|
-
jsonOut({
|
|
56
|
-
items: resp.data || [],
|
|
57
|
-
pagination: resp.pagination || {},
|
|
58
|
-
mentions: resp.mentions || {},
|
|
59
|
-
});
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
const items = (resp.data || []);
|
|
63
|
-
const pagination = (resp.pagination || {});
|
|
64
|
-
if (!items.length) {
|
|
65
|
-
console.log("No items found.");
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
const mentions = buildMentionMap(resp);
|
|
69
|
-
const lines = items.map((m) => formatFeedLine(m, mentions));
|
|
70
|
-
const footer = pagination.hasMore ? `\n Next cursor: ${pagination.nextCursor}` : "";
|
|
71
|
-
console.log(`Global feed (${items.length} items, newest first):\n` + lines.join("\n") + footer);
|
|
72
|
-
});
|
|
73
|
-
// ── List posts ──
|
|
74
|
-
global
|
|
75
|
-
.command("list-posts")
|
|
76
|
-
.description("List posts in the global feed (paginated). Pass --mine to limit to your own posts.")
|
|
77
|
-
.option("--limit <number>", "Items per page", "20")
|
|
78
|
-
.option("--cursor <string>", "Pagination cursor from previous response")
|
|
79
|
-
.option("--mine", "Only include posts authored by you")
|
|
80
|
-
.action(async (opts) => {
|
|
81
|
-
const params = {
|
|
82
|
-
limit: parseInt(opts.limit, 10),
|
|
83
|
-
};
|
|
84
|
-
if (opts.cursor)
|
|
85
|
-
params.cursor = opts.cursor;
|
|
86
|
-
if (opts.mine)
|
|
87
|
-
params.mine = "true";
|
|
88
|
-
const resp = (await apiGet(`/posts`, params));
|
|
89
|
-
if (isJsonMode(global)) {
|
|
90
|
-
jsonOut({
|
|
91
|
-
items: resp.data || [],
|
|
92
|
-
pagination: resp.pagination || {},
|
|
93
|
-
mentions: resp.mentions || {},
|
|
94
|
-
});
|
|
95
|
-
return;
|
|
96
|
-
}
|
|
97
|
-
const items = (resp.data || []);
|
|
98
|
-
const pagination = (resp.pagination || {});
|
|
99
|
-
if (!items.length) {
|
|
100
|
-
console.log("No posts found.");
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
const mentions = buildMentionMap(resp);
|
|
104
|
-
const lines = [];
|
|
105
|
-
for (const t of items) {
|
|
106
|
-
const author = t.author?.name ||
|
|
107
|
-
`User ${t.authorId}`;
|
|
108
|
-
lines.push(`- [${t.id}] "${formatPostLabel(t, mentions)}" by ${author} (${t.replyCount ?? 0} replies, ${t.createdAt})`);
|
|
109
|
-
for (const line of formatAttachmentLines(t, " ", "📎")) {
|
|
110
|
-
lines.push(line);
|
|
111
|
-
}
|
|
112
|
-
const replies = t.replies || [];
|
|
113
|
-
for (const r of replies) {
|
|
114
|
-
lines.push(formatReplyLine(r, mentions));
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
const footer = pagination.hasMore ? `\n Next cursor: ${pagination.nextCursor}` : "";
|
|
118
|
-
console.log(`Posts (${items.length} items):\n` + lines.join("\n") + footer);
|
|
119
|
-
});
|
|
120
|
-
// ── Get post (with ancestors and replies) ──
|
|
121
|
-
global
|
|
122
|
-
.command("get-post <postId>")
|
|
123
|
-
.description("Get a global post with its ancestors and replies (paginated).")
|
|
124
|
-
.option("--limit <number>", "Items per page", "20")
|
|
125
|
-
.option("--cursor <string>", "Pagination cursor from previous response")
|
|
126
|
-
.option("--full", "Show full reply content without truncation")
|
|
127
|
-
.action(async (postId, opts) => {
|
|
128
|
-
const params = {
|
|
129
|
-
limit: parseInt(opts.limit, 10),
|
|
130
|
-
};
|
|
131
|
-
if (opts.cursor)
|
|
132
|
-
params.cursor = opts.cursor;
|
|
133
|
-
const [postResp, ancestorsResp] = await Promise.all([
|
|
134
|
-
apiGet(`/posts/${postId}`, params),
|
|
135
|
-
apiGet(`/posts/${postId}/ancestors`),
|
|
136
|
-
]);
|
|
137
|
-
const data = unwrapResp(postResp);
|
|
138
|
-
const pagination = (postResp.pagination || {});
|
|
139
|
-
const mentions = (postResp.mentions || {});
|
|
140
|
-
const ancestorsData = unwrapResp(ancestorsResp);
|
|
141
|
-
const ancestors = (ancestorsData.ancestors || []);
|
|
142
|
-
if (isJsonMode(global)) {
|
|
143
|
-
jsonOut({ ...data, ancestors, pagination, mentions });
|
|
144
|
-
return;
|
|
145
|
-
}
|
|
146
|
-
const post = (data.update || data.post || data);
|
|
147
|
-
const replies = (data.replies || []);
|
|
148
|
-
const mentionMap = buildMentionMap(postResp);
|
|
149
|
-
const author = post.author?.name ||
|
|
150
|
-
`User ${post.authorId}`;
|
|
151
|
-
const ancestorLines = [];
|
|
152
|
-
if (ancestors.length) {
|
|
153
|
-
ancestors.forEach((a, i) => {
|
|
154
|
-
ancestorLines.push(` ${i + 1}. ${formatFeedLine(a, mentionMap)}`);
|
|
155
|
-
});
|
|
156
|
-
}
|
|
157
|
-
const replyLines = [];
|
|
158
|
-
for (const r of replies) {
|
|
159
|
-
const rAuthor = r.author?.name ||
|
|
160
|
-
`User ${r.authorId}`;
|
|
161
|
-
const text = postBodyText(r, mentionMap);
|
|
162
|
-
const truncated = opts.full || text.length <= 200 ? text : text.slice(0, 200) + "…";
|
|
163
|
-
const rChips = formatReactionChips(r);
|
|
164
|
-
const rAttach = formatAttachmentSummary(r);
|
|
165
|
-
replyLines.push(` - [r:${r.id}] ${rAuthor}: ${truncated} (${r.createdAt})${rAttach ? ` ${rAttach}` : ""}${rChips ? ` ${rChips}` : ""}`);
|
|
166
|
-
}
|
|
167
|
-
const isReplyPost = post.parentPostId != null;
|
|
168
|
-
const heading = isReplyPost
|
|
169
|
-
? `Reply [r:${post.id}]`
|
|
170
|
-
: `Post: ${post.title || "(no title)"}`;
|
|
171
|
-
const postChips = formatReactionChips(post);
|
|
172
|
-
const attachmentLines = formatAttachmentLines(post);
|
|
173
|
-
const output = [
|
|
174
|
-
heading,
|
|
175
|
-
`By: ${author} on ${post.createdAt}`,
|
|
176
|
-
...(postChips ? [`Reactions: ${postChips}`] : []),
|
|
177
|
-
...(ancestorLines.length
|
|
178
|
-
? ["", `Ancestors (${ancestors.length} items, root first):`, ...ancestorLines]
|
|
179
|
-
: []),
|
|
180
|
-
"",
|
|
181
|
-
postBodyText(post, mentionMap),
|
|
182
|
-
...(attachmentLines.length
|
|
183
|
-
? ["", `Attachments (${attachmentLines.length}):`, ...attachmentLines]
|
|
184
|
-
: []),
|
|
185
|
-
"",
|
|
186
|
-
`Replies (${replies.length} items):`,
|
|
187
|
-
...replyLines,
|
|
188
|
-
...(pagination.hasMore
|
|
189
|
-
? [` Next cursor: ${pagination.nextCursor}`]
|
|
190
|
-
: []),
|
|
191
|
-
].join("\n");
|
|
192
|
-
console.log(output);
|
|
193
|
-
});
|
|
194
|
-
// ── Create post ──
|
|
195
|
-
global
|
|
196
|
-
.command("create-post")
|
|
197
|
-
.description("Create a post in the global feed.")
|
|
198
|
-
.option("--title <title>", "Title of the post")
|
|
199
|
-
.option("--content <content>", "Post content (markdown supported, use \"-\" for stdin)")
|
|
200
|
-
.option("--rich-text <richText>", "Rich-text JSON array (mutually exclusive with --content)")
|
|
201
|
-
.option("--artifact <artifactId>", "Attach an existing artifact to the post (repeatable). Create artifacts with `gobi personal artifact create`.", (value, prev = []) => [...prev, value], [])
|
|
202
|
-
.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], [])
|
|
203
|
-
.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.")
|
|
204
|
-
.action(async (opts) => {
|
|
205
|
-
// A post is substantive if it has a text body OR carries an attachment
|
|
206
|
-
// (artifact card / media) OR embeds a repost. Only block the truly empty
|
|
207
|
-
// case — this is what lets an artifact-only post be created with no content.
|
|
208
|
-
const hasAttachmentPayload = (opts.artifact && opts.artifact.length > 0) ||
|
|
209
|
-
(opts.attach && opts.attach.length > 0) ||
|
|
210
|
-
opts.repostPostId != null;
|
|
211
|
-
if (!opts.content && !opts.richText && !hasAttachmentPayload) {
|
|
212
|
-
throw new Error("Provide --content, --rich-text, or an attachment (--artifact / --attach / --repost-post-id).");
|
|
213
|
-
}
|
|
214
|
-
if (opts.content && opts.richText) {
|
|
215
|
-
throw new Error("--content and --rich-text are mutually exclusive.");
|
|
216
|
-
}
|
|
217
|
-
const body = {};
|
|
218
|
-
if (opts.title != null)
|
|
219
|
-
body.title = opts.title;
|
|
220
|
-
if (opts.content != null) {
|
|
221
|
-
body.content = readContent(opts.content);
|
|
222
|
-
}
|
|
223
|
-
if (opts.richText != null) {
|
|
224
|
-
let parsed;
|
|
225
|
-
try {
|
|
226
|
-
parsed = JSON.parse(opts.richText);
|
|
227
|
-
}
|
|
228
|
-
catch {
|
|
229
|
-
throw new Error("Invalid --rich-text JSON.");
|
|
230
|
-
}
|
|
231
|
-
body.richText = parsed;
|
|
232
|
-
}
|
|
233
|
-
if (opts.artifact && opts.artifact.length > 0)
|
|
234
|
-
body.artifactIds = opts.artifact;
|
|
235
|
-
if (opts.attach && opts.attach.length > 0) {
|
|
236
|
-
assertPostAttachmentMix(opts.attach);
|
|
237
|
-
body.attachments = await uploadPostAttachments(opts.attach);
|
|
238
|
-
}
|
|
239
|
-
if (opts.repostPostId != null) {
|
|
240
|
-
const n = Number(opts.repostPostId);
|
|
241
|
-
if (!Number.isFinite(n) || n <= 0 || !Number.isInteger(n)) {
|
|
242
|
-
throw new Error("--repost-post-id must be a positive integer.");
|
|
243
|
-
}
|
|
244
|
-
body.repostPostId = n;
|
|
245
|
-
}
|
|
246
|
-
const resp = (await apiPost(`/posts`, body));
|
|
247
|
-
const post = unwrapResp(resp);
|
|
248
|
-
const shareUrl = buildPersonalPostUrl(post);
|
|
249
|
-
if (isJsonMode(global)) {
|
|
250
|
-
jsonOut({ ...post, shareUrl });
|
|
251
|
-
return;
|
|
252
|
-
}
|
|
253
|
-
console.log(`Post created!\n` +
|
|
254
|
-
` ID: ${post.id}\n` +
|
|
255
|
-
(post.title ? ` Title: ${post.title}\n` : "") +
|
|
256
|
-
` Created: ${post.createdAt}\n` +
|
|
257
|
-
` URL: ${shareUrl}`);
|
|
258
|
-
});
|
|
259
|
-
// ── Edit post ──
|
|
260
|
-
global
|
|
261
|
-
.command("edit-post <postId>")
|
|
262
|
-
.description("Edit a post you authored in the global feed.")
|
|
263
|
-
.option("--title <title>", "New title")
|
|
264
|
-
.option("--content <content>", "New content (markdown supported, use \"-\" for stdin)")
|
|
265
|
-
.option("--rich-text <richText>", "Rich-text JSON array (mutually exclusive with --content)")
|
|
266
|
-
.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], [])
|
|
267
|
-
.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 personal artifact create`.", (value, prev = []) => [...prev, value], [])
|
|
268
|
-
.action(async (postId, opts) => {
|
|
269
|
-
const wantsAttachChange = !!(opts.attach && opts.attach.length > 0);
|
|
270
|
-
const wantsArtifactChange = !!(opts.artifact && opts.artifact.length > 0);
|
|
271
|
-
if (opts.title == null &&
|
|
272
|
-
opts.content == null &&
|
|
273
|
-
opts.richText == null &&
|
|
274
|
-
!wantsAttachChange &&
|
|
275
|
-
!wantsArtifactChange) {
|
|
276
|
-
throw new Error("Provide at least --title, --content, --rich-text, --attach, or --artifact to update.");
|
|
277
|
-
}
|
|
278
|
-
if (opts.content && opts.richText) {
|
|
279
|
-
throw new Error("--content and --rich-text are mutually exclusive.");
|
|
280
|
-
}
|
|
281
|
-
const body = {};
|
|
282
|
-
if (opts.title != null)
|
|
283
|
-
body.title = opts.title;
|
|
284
|
-
if (opts.content != null) {
|
|
285
|
-
body.content = readContent(opts.content);
|
|
286
|
-
}
|
|
287
|
-
if (opts.richText != null) {
|
|
288
|
-
let parsed;
|
|
289
|
-
try {
|
|
290
|
-
parsed = JSON.parse(opts.richText);
|
|
291
|
-
}
|
|
292
|
-
catch {
|
|
293
|
-
throw new Error("Invalid --rich-text JSON.");
|
|
294
|
-
}
|
|
295
|
-
body.richText = parsed;
|
|
296
|
-
}
|
|
297
|
-
if (opts.attach && opts.attach.length > 0) {
|
|
298
|
-
assertPostAttachmentMix(opts.attach);
|
|
299
|
-
body.attachments = await uploadPostAttachments(opts.attach);
|
|
300
|
-
}
|
|
301
|
-
if (opts.artifact && opts.artifact.length > 0)
|
|
302
|
-
body.artifactIds = opts.artifact;
|
|
303
|
-
const resp = (await apiPatch(`/posts/${postId}`, body));
|
|
304
|
-
const post = unwrapResp(resp);
|
|
305
|
-
if (isJsonMode(global)) {
|
|
306
|
-
jsonOut(post);
|
|
307
|
-
return;
|
|
308
|
-
}
|
|
309
|
-
console.log(`Post edited!\n ID: ${post.id}\n Edited: ${post.editedAt ?? post.updatedAt}`);
|
|
310
|
-
});
|
|
311
|
-
// ── Delete post ──
|
|
312
|
-
global
|
|
313
|
-
.command("delete-post <postId>")
|
|
314
|
-
.description("Delete a post you authored in the global feed.")
|
|
315
|
-
.action(async (postId) => {
|
|
316
|
-
await apiDelete(`/posts/${postId}`);
|
|
317
|
-
if (isJsonMode(global)) {
|
|
318
|
-
jsonOut({ id: postId });
|
|
319
|
-
return;
|
|
320
|
-
}
|
|
321
|
-
console.log(`Post ${postId} deleted.`);
|
|
322
|
-
});
|
|
323
|
-
// ── Reply ──
|
|
324
|
-
global
|
|
325
|
-
.command("create-reply <postId>")
|
|
326
|
-
.description("Create a reply to a post in the global feed.")
|
|
327
|
-
.option("--content <content>", "Reply content (markdown supported, use \"-\" for stdin)")
|
|
328
|
-
.option("--rich-text <richText>", "Rich-text JSON array (mutually exclusive with --content)")
|
|
329
|
-
.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], [])
|
|
330
|
-
.action(async (postId, opts) => {
|
|
331
|
-
if (!opts.content && !opts.richText) {
|
|
332
|
-
throw new Error("Provide either --content or --rich-text.");
|
|
333
|
-
}
|
|
334
|
-
if (opts.content && opts.richText) {
|
|
335
|
-
throw new Error("--content and --rich-text are mutually exclusive.");
|
|
336
|
-
}
|
|
337
|
-
const body = {};
|
|
338
|
-
if (opts.content != null) {
|
|
339
|
-
body.content = readContent(opts.content);
|
|
340
|
-
}
|
|
341
|
-
if (opts.richText != null) {
|
|
342
|
-
let parsed;
|
|
343
|
-
try {
|
|
344
|
-
parsed = JSON.parse(opts.richText);
|
|
345
|
-
}
|
|
346
|
-
catch {
|
|
347
|
-
throw new Error("Invalid --rich-text JSON.");
|
|
348
|
-
}
|
|
349
|
-
body.richText = parsed;
|
|
350
|
-
}
|
|
351
|
-
if (opts.attach && opts.attach.length > 0) {
|
|
352
|
-
assertPostAttachmentMix(opts.attach);
|
|
353
|
-
body.attachments = await uploadPostAttachments(opts.attach);
|
|
354
|
-
}
|
|
355
|
-
const resp = (await apiPost(`/posts/${postId}/replies`, body));
|
|
356
|
-
const reply = unwrapResp(resp);
|
|
357
|
-
if (isJsonMode(global)) {
|
|
358
|
-
jsonOut(reply);
|
|
359
|
-
return;
|
|
360
|
-
}
|
|
361
|
-
console.log(`Reply created!\n ID: ${reply.id}\n Created: ${reply.createdAt}`);
|
|
362
|
-
});
|
|
363
|
-
global
|
|
364
|
-
.command("edit-reply <replyId>")
|
|
365
|
-
.description("Edit a reply you authored in the global feed.")
|
|
366
|
-
.option("--content <content>", "New reply content (markdown supported, use \"-\" for stdin)")
|
|
367
|
-
.option("--rich-text <richText>", "Rich-text JSON array (mutually exclusive with --content)")
|
|
368
|
-
.action(async (replyId, opts) => {
|
|
369
|
-
if (opts.content == null && opts.richText == null) {
|
|
370
|
-
throw new Error("Provide at least --content or --rich-text to update.");
|
|
371
|
-
}
|
|
372
|
-
if (opts.content && opts.richText) {
|
|
373
|
-
throw new Error("--content and --rich-text are mutually exclusive.");
|
|
374
|
-
}
|
|
375
|
-
const body = {};
|
|
376
|
-
if (opts.content != null) {
|
|
377
|
-
body.content = readContent(opts.content);
|
|
378
|
-
}
|
|
379
|
-
if (opts.richText != null) {
|
|
380
|
-
let parsed;
|
|
381
|
-
try {
|
|
382
|
-
parsed = JSON.parse(opts.richText);
|
|
383
|
-
}
|
|
384
|
-
catch {
|
|
385
|
-
throw new Error("Invalid --rich-text JSON.");
|
|
386
|
-
}
|
|
387
|
-
body.richText = parsed;
|
|
388
|
-
}
|
|
389
|
-
const resp = (await apiPatch(`/posts/replies/${replyId}`, body));
|
|
390
|
-
const reply = unwrapResp(resp);
|
|
391
|
-
if (isJsonMode(global)) {
|
|
392
|
-
jsonOut(reply);
|
|
393
|
-
return;
|
|
394
|
-
}
|
|
395
|
-
console.log(`Reply edited!\n ID: ${reply.id}\n Edited: ${reply.editedAt ?? reply.updatedAt}`);
|
|
396
|
-
});
|
|
397
|
-
global
|
|
398
|
-
.command("delete-reply <replyId>")
|
|
399
|
-
.description("Delete a reply you authored in the global feed.")
|
|
400
|
-
.action(async (replyId) => {
|
|
401
|
-
await apiDelete(`/posts/replies/${replyId}`);
|
|
402
|
-
if (isJsonMode(global)) {
|
|
403
|
-
jsonOut({ id: replyId });
|
|
404
|
-
return;
|
|
405
|
-
}
|
|
406
|
-
console.log(`Reply ${replyId} deleted.`);
|
|
407
|
-
});
|
|
408
|
-
// ── Reactions (react, unreact) ──
|
|
409
|
-
global
|
|
410
|
-
.command("react <postId> <emoji>")
|
|
411
|
-
.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.")
|
|
412
|
-
.action(async (postId, emoji) => {
|
|
413
|
-
const resp = (await apiPut(`/posts/${postId}/reactions`, {
|
|
414
|
-
emoji,
|
|
415
|
-
}));
|
|
416
|
-
const data = unwrapResp(resp);
|
|
417
|
-
if (isJsonMode(global)) {
|
|
418
|
-
jsonOut(data);
|
|
419
|
-
return;
|
|
420
|
-
}
|
|
421
|
-
const chips = formatReactionChips(data);
|
|
422
|
-
console.log(`Reacted ${emoji} to ${postId}.` + (chips ? `\n Now: ${chips}` : ""));
|
|
423
|
-
});
|
|
424
|
-
global
|
|
425
|
-
.command("unreact <postId> <emoji>")
|
|
426
|
-
.description("Remove your emoji reaction from a global-feed post or reply. <postId> is the numeric id of a post OR a reply.")
|
|
427
|
-
.action(async (postId, emoji) => {
|
|
428
|
-
const resp = (await apiDelete(`/posts/${postId}/reactions/${encodeURIComponent(emoji)}`));
|
|
429
|
-
const data = unwrapResp(resp);
|
|
430
|
-
if (isJsonMode(global)) {
|
|
431
|
-
jsonOut(data);
|
|
432
|
-
return;
|
|
433
|
-
}
|
|
434
|
-
const chips = formatReactionChips(data);
|
|
435
|
-
console.log(`Removed ${emoji} reaction from ${postId}.` +
|
|
436
|
-
(chips ? `\n Now: ${chips}` : ""));
|
|
437
|
-
});
|
|
438
|
-
}
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
# gobi sense
|
|
2
|
-
|
|
3
|
-
```
|
|
4
|
-
Usage: gobi sense [options] [command]
|
|
5
|
-
|
|
6
|
-
Sense commands (activities, transcriptions).
|
|
7
|
-
|
|
8
|
-
Options:
|
|
9
|
-
-h, --help display help for command
|
|
10
|
-
|
|
11
|
-
Commands:
|
|
12
|
-
list-activities [options] List activity records within a time range.
|
|
13
|
-
list-transcriptions [options] List transcription records within a time range.
|
|
14
|
-
help [command] display help for command
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
## list-activities
|
|
18
|
-
|
|
19
|
-
```
|
|
20
|
-
Usage: gobi sense list-activities [options]
|
|
21
|
-
|
|
22
|
-
List activity records within a time range.
|
|
23
|
-
|
|
24
|
-
Options:
|
|
25
|
-
--start-time <iso> Start of time range (ISO 8601 UTC, e.g. 2026-03-20T00:00:00Z)
|
|
26
|
-
--end-time <iso> End of time range (ISO 8601 UTC, e.g. 2026-03-20T23:59:59Z)
|
|
27
|
-
-h, --help display help for command
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
## list-transcriptions
|
|
31
|
-
|
|
32
|
-
```
|
|
33
|
-
Usage: gobi sense list-transcriptions [options]
|
|
34
|
-
|
|
35
|
-
List transcription records within a time range.
|
|
36
|
-
|
|
37
|
-
Options:
|
|
38
|
-
--start-time <iso> Start of time range (ISO 8601 UTC, e.g. 2026-03-20T00:00:00Z)
|
|
39
|
-
--end-time <iso> End of time range (ISO 8601 UTC, e.g. 2026-03-20T23:59:59Z)
|
|
40
|
-
-h, --help display help for command
|
|
41
|
-
```
|
|
@@ -1,175 +0,0 @@
|
|
|
1
|
-
# gobi global
|
|
2
|
-
|
|
3
|
-
```
|
|
4
|
-
Usage: gobi global [options] [command]
|
|
5
|
-
|
|
6
|
-
Global commands (posts and replies in the public feed across all vaults).
|
|
7
|
-
|
|
8
|
-
Options:
|
|
9
|
-
-h, --help display help for command
|
|
10
|
-
|
|
11
|
-
Commands:
|
|
12
|
-
feed [options] List the unified feed (posts and replies, newest first) in the global public feed.
|
|
13
|
-
list-posts [options] List posts in the global feed (paginated). Pass --mine to limit to your own posts.
|
|
14
|
-
get-post [options] <postId> Get a global post with its ancestors and replies (paginated).
|
|
15
|
-
create-post [options] Create a post in the global feed.
|
|
16
|
-
edit-post [options] <postId> Edit a post you authored in the global feed.
|
|
17
|
-
delete-post <postId> Delete a post you authored in the global feed.
|
|
18
|
-
create-reply [options] <postId> Create a reply to a post in the global feed.
|
|
19
|
-
edit-reply [options] <replyId> Edit a reply you authored in the global feed.
|
|
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.
|
|
23
|
-
help [command] display help for command
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
## feed
|
|
27
|
-
|
|
28
|
-
```
|
|
29
|
-
Usage: gobi global feed [options]
|
|
30
|
-
|
|
31
|
-
List the unified feed (posts and replies, newest first) in the global public feed.
|
|
32
|
-
|
|
33
|
-
Options:
|
|
34
|
-
--limit <number> Items per page (default: "20")
|
|
35
|
-
--cursor <string> Pagination cursor from previous response
|
|
36
|
-
--following Only include posts from authors you follow
|
|
37
|
-
-h, --help display help for command
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
## list-posts
|
|
41
|
-
|
|
42
|
-
```
|
|
43
|
-
Usage: gobi global list-posts [options]
|
|
44
|
-
|
|
45
|
-
List posts in the global feed (paginated). Pass --mine to limit to your own posts.
|
|
46
|
-
|
|
47
|
-
Options:
|
|
48
|
-
--limit <number> Items per page (default: "20")
|
|
49
|
-
--cursor <string> Pagination cursor from previous response
|
|
50
|
-
--mine Only include posts authored by you
|
|
51
|
-
-h, --help display help for command
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
## get-post
|
|
55
|
-
|
|
56
|
-
```
|
|
57
|
-
Usage: gobi global get-post [options] <postId>
|
|
58
|
-
|
|
59
|
-
Get a global post with its ancestors and replies (paginated).
|
|
60
|
-
|
|
61
|
-
Options:
|
|
62
|
-
--limit <number> Items per page (default: "20")
|
|
63
|
-
--cursor <string> Pagination cursor from previous response
|
|
64
|
-
--full Show full reply content without truncation
|
|
65
|
-
-h, --help display help for command
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
## create-post
|
|
69
|
-
|
|
70
|
-
```
|
|
71
|
-
Usage: gobi global create-post [options]
|
|
72
|
-
|
|
73
|
-
Create a post in the global feed.
|
|
74
|
-
|
|
75
|
-
Options:
|
|
76
|
-
--title <title> Title of the post
|
|
77
|
-
--content <content> Post content (markdown supported, use "-" for stdin)
|
|
78
|
-
--rich-text <richText> Rich-text JSON array (mutually exclusive with --content)
|
|
79
|
-
--artifact <artifactId> Attach an existing artifact to the post (repeatable). Create artifacts with `gobi personal artifact create`. (default: [])
|
|
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: [])
|
|
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
|
|
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.
|
|
84
|
-
-h, --help display help for command
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
## edit-post
|
|
88
|
-
|
|
89
|
-
```
|
|
90
|
-
Usage: gobi global edit-post [options] <postId>
|
|
91
|
-
|
|
92
|
-
Edit a post you authored in the global feed.
|
|
93
|
-
|
|
94
|
-
Options:
|
|
95
|
-
--title <title> New title
|
|
96
|
-
--content <content> New content (markdown supported, use "-" for stdin)
|
|
97
|
-
--rich-text <richText> Rich-text JSON array (mutually exclusive with --content)
|
|
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: [])
|
|
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
|
|
101
|
-
with `gobi personal artifact create`. (default: [])
|
|
102
|
-
-h, --help display help for command
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
## delete-post
|
|
106
|
-
|
|
107
|
-
```
|
|
108
|
-
Usage: gobi global delete-post [options] <postId>
|
|
109
|
-
|
|
110
|
-
Delete a post you authored in the global feed.
|
|
111
|
-
|
|
112
|
-
Options:
|
|
113
|
-
-h, --help display help for command
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
## create-reply
|
|
117
|
-
|
|
118
|
-
```
|
|
119
|
-
Usage: gobi global create-reply [options] <postId>
|
|
120
|
-
|
|
121
|
-
Create a reply to a post in the global feed.
|
|
122
|
-
|
|
123
|
-
Options:
|
|
124
|
-
--content <content> Reply content (markdown supported, use "-" for stdin)
|
|
125
|
-
--rich-text <richText> Rich-text JSON array (mutually exclusive with --content)
|
|
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: [])
|
|
128
|
-
-h, --help display help for command
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
## edit-reply
|
|
132
|
-
|
|
133
|
-
```
|
|
134
|
-
Usage: gobi global edit-reply [options] <replyId>
|
|
135
|
-
|
|
136
|
-
Edit a reply you authored in the global feed.
|
|
137
|
-
|
|
138
|
-
Options:
|
|
139
|
-
--content <content> New reply content (markdown supported, use "-" for stdin)
|
|
140
|
-
--rich-text <richText> Rich-text JSON array (mutually exclusive with --content)
|
|
141
|
-
-h, --help display help for command
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
## delete-reply
|
|
145
|
-
|
|
146
|
-
```
|
|
147
|
-
Usage: gobi global delete-reply [options] <replyId>
|
|
148
|
-
|
|
149
|
-
Delete a reply you authored in the global feed.
|
|
150
|
-
|
|
151
|
-
Options:
|
|
152
|
-
-h, --help display help for command
|
|
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
|
-
```
|