@gobi-ai/cli 2.0.23 → 2.0.25

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.
@@ -1,9 +1,8 @@
1
1
  import { WEB_BASE_URL } from "../constants.js";
2
2
  import { apiGet, apiPost, apiPatch, apiDelete } from "../client.js";
3
3
  import { requireSpace, selectSpace, setSpaceRequirement, writeSpaceSetting, } from "./init.js";
4
- import { fetchDraftSummary, isJsonMode, jsonOut, readStdin, resolveSpaceSlug, resolveVaultSlug, unwrapResp, } from "./utils.js";
5
- import { extractWikiLinks, uploadAttachments, uploadPostAttachments, assertPostAttachmentMix, } from "../attachments.js";
6
- import { getValidToken } from "../auth/manager.js";
4
+ import { isJsonMode, jsonOut, readStdin, resolveSpaceSlug, resolveVaultSlug, unwrapResp, } from "./utils.js";
5
+ import { uploadPostAttachments, assertPostAttachmentMix, } from "../attachments.js";
7
6
  function readContent(value) {
8
7
  if (value === "-")
9
8
  return readStdin();
@@ -316,45 +315,26 @@ export function registerSpaceCommand(program) {
316
315
  .option("--title <title>", "Title of the post")
317
316
  .option("--content <content>", "Post content (markdown supported, use \"-\" for stdin)")
318
317
  .option("--rich-text <richText>", "Rich-text JSON array (mutually exclusive with --content)")
319
- .option("--auto-attachments", "Upload wiki-linked [[files]] to webdrive before posting (also attributes the post to that vault)")
320
- .option("--vault-slug <vaultSlug>", "Attribute the post to this vault (sets authorVaultSlug). Also used as upload destination for --auto-attachments.")
318
+ .option("--vault-slug <vaultSlug>", "Attribute the post to this vault (sets authorVaultSlug). Caller must own the vault.")
321
319
  .option("--space-slug <spaceSlug>", "Space slug (overrides .gobi/settings.yaml)")
322
- .option("--draft-id <draftId>", "Use this draft as the source of title and content (mutually exclusive with --title/--content/--rich-text). On success, links the post back by recording postId/spaceSlug on draft.metadata so the client can render an 'Open post' button. The draft's vaultSlug seeds --vault-slug when not given explicitly.")
323
320
  .option("--attach <file>", "Local media file to attach. Repeatable. X-style mix rule: up to 4 photos OR 1 GIF OR 1 video. Size ceilings: 5MB photos / 15MB GIFs / 512MB video.", (value, prev = []) => [...prev, value], [])
324
321
  .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.")
325
322
  .action(async (opts) => {
326
- if (opts.draftId) {
327
- if (opts.title || opts.content || opts.richText) {
328
- throw new Error("--draft-id sources title and content from the draft; --title, --content, and --rich-text are not allowed alongside it.");
329
- }
323
+ if (!opts.content && !opts.richText) {
324
+ throw new Error("Provide either --content or --rich-text.");
330
325
  }
331
- else {
332
- if (!opts.content && !opts.richText) {
333
- throw new Error("Provide either --content or --rich-text (or --draft-id).");
334
- }
335
- if (opts.content && opts.richText) {
336
- throw new Error("--content and --rich-text are mutually exclusive.");
337
- }
326
+ if (opts.content && opts.richText) {
327
+ throw new Error("--content and --rich-text are mutually exclusive.");
338
328
  }
339
- const draft = opts.draftId ? await fetchDraftSummary(opts.draftId) : null;
340
- const effectiveTitle = draft ? draft.title : opts.title;
341
- const effectiveContent = draft ? draft.content : opts.content;
342
- const effectiveVaultSlugOpt = opts.vaultSlug ?? draft?.vaultSlug ?? undefined;
343
329
  let authorVaultSlug;
344
- if (effectiveVaultSlugOpt || opts.autoAttachments) {
345
- authorVaultSlug = resolveVaultSlug({ vaultSlug: effectiveVaultSlugOpt });
330
+ if (opts.vaultSlug) {
331
+ authorVaultSlug = resolveVaultSlug({ vaultSlug: opts.vaultSlug });
346
332
  }
347
333
  const body = {};
348
- if (effectiveTitle != null)
349
- body.title = effectiveTitle;
350
- if (effectiveContent != null) {
351
- const content = draft ? effectiveContent : readContent(effectiveContent);
352
- if (opts.autoAttachments) {
353
- const token = await getValidToken();
354
- const links = extractWikiLinks(content);
355
- await uploadAttachments(authorVaultSlug, links, token, { addToSyncfiles: true });
356
- }
357
- body.content = content;
334
+ if (opts.title != null)
335
+ body.title = opts.title;
336
+ if (opts.content != null) {
337
+ body.content = readContent(opts.content);
358
338
  }
359
339
  if (opts.richText != null) {
360
340
  let parsed;
@@ -382,19 +362,6 @@ export function registerSpaceCommand(program) {
382
362
  const spaceSlug = resolveSpaceSlug(space, opts);
383
363
  const resp = (await apiPost(`/spaces/${spaceSlug}/posts`, body));
384
364
  const post = unwrapResp(resp);
385
- if (opts.draftId && post.id != null) {
386
- try {
387
- await apiPatch(`/app/drafts/${opts.draftId}/metadata`, {
388
- postId: typeof post.id === "number" ? post.id : Number(post.id),
389
- spaceSlug,
390
- });
391
- }
392
- catch (e) {
393
- // Don't fail the create if linking fails — the post is live; just
394
- // surface a warning so the agent can mention it.
395
- console.error(`Warning: failed to link post to draft ${opts.draftId}: ${e.message}`);
396
- }
397
- }
398
365
  const shareUrl = `${WEB_BASE_URL}/spaces/${spaceSlug}/posts/${post.id}`;
399
366
  if (isJsonMode(space)) {
400
367
  jsonOut({ ...post, shareUrl });
@@ -404,8 +371,7 @@ export function registerSpaceCommand(program) {
404
371
  ` ID: ${post.id}\n` +
405
372
  (post.title ? ` Title: ${post.title}\n` : "") +
406
373
  ` Created: ${post.createdAt}\n` +
407
- ` URL: ${shareUrl}` +
408
- (opts.draftId ? `\n Linked to draft: ${opts.draftId}` : ""));
374
+ ` URL: ${shareUrl}`);
409
375
  });
410
376
  space
411
377
  .command("edit-post <postId>")
@@ -413,36 +379,32 @@ export function registerSpaceCommand(program) {
413
379
  .option("--title <title>", "New title for the post")
414
380
  .option("--content <content>", "New content for the post (markdown supported, use \"-\" for stdin)")
415
381
  .option("--rich-text <richText>", "Rich-text JSON array (mutually exclusive with --content)")
416
- .option("--auto-attachments", "Upload wiki-linked [[files]] to webdrive before editing (also attributes the post to that vault)")
417
- .option("--vault-slug <vaultSlug>", "Attribute the post to this vault (sets authorVaultSlug). Also used as upload destination for --auto-attachments.")
382
+ .option("--vault-slug <vaultSlug>", "Attribute the post to this vault (sets authorVaultSlug). Caller must own the vault.")
418
383
  .option("--space-slug <spaceSlug>", "Space slug (overrides .gobi/settings.yaml)")
384
+ .option("--attach <file>", "Replace the post's media attachments with the given files (existing attachments are removed). Repeatable. X-style mix rule: up to 4 photos OR 1 GIF OR 1 video. Size ceilings: 5MB photos / 15MB GIFs / 512MB video. Omit to leave attachments unchanged.", (value, prev = []) => [...prev, value], [])
419
385
  .action(async (postId, opts) => {
420
- const wantsVaultChange = !!(opts.vaultSlug || opts.autoAttachments);
386
+ const wantsVaultChange = !!opts.vaultSlug;
387
+ const wantsAttachChange = !!(opts.attach && opts.attach.length > 0);
421
388
  if (opts.title == null &&
422
389
  opts.content == null &&
423
390
  opts.richText == null &&
424
- !wantsVaultChange) {
425
- throw new Error("Provide at least --title, --content, --rich-text, or --vault-slug to update.");
391
+ !wantsVaultChange &&
392
+ !wantsAttachChange) {
393
+ throw new Error("Provide at least --title, --content, --rich-text, --vault-slug, or --attach to update.");
426
394
  }
427
395
  if (opts.content && opts.richText) {
428
396
  throw new Error("--content and --rich-text are mutually exclusive.");
429
397
  }
430
398
  const spaceSlug = resolveSpaceSlug(space, opts);
431
399
  let authorVaultSlug;
432
- if (opts.vaultSlug || opts.autoAttachments) {
400
+ if (opts.vaultSlug) {
433
401
  authorVaultSlug = resolveVaultSlug(opts);
434
402
  }
435
403
  const body = {};
436
404
  if (opts.title != null)
437
405
  body.title = opts.title;
438
406
  if (opts.content != null) {
439
- const content = readContent(opts.content);
440
- if (opts.autoAttachments) {
441
- const token = await getValidToken();
442
- const links = extractWikiLinks(content);
443
- await uploadAttachments(authorVaultSlug, links, token, { addToSyncfiles: true });
444
- }
445
- body.content = content;
407
+ body.content = readContent(opts.content);
446
408
  }
447
409
  if (opts.richText != null) {
448
410
  let parsed;
@@ -456,6 +418,10 @@ export function registerSpaceCommand(program) {
456
418
  }
457
419
  if (authorVaultSlug !== undefined)
458
420
  body.authorVaultSlug = authorVaultSlug;
421
+ if (opts.attach && opts.attach.length > 0) {
422
+ assertPostAttachmentMix(opts.attach);
423
+ body.attachments = await uploadPostAttachments(opts.attach);
424
+ }
459
425
  const resp = (await apiPatch(`/spaces/${spaceSlug}/posts/${postId}`, body));
460
426
  const post = unwrapResp(resp);
461
427
  if (isJsonMode(space)) {
@@ -486,8 +452,7 @@ export function registerSpaceCommand(program) {
486
452
  .description("Create a reply to a post in a space.")
487
453
  .option("--content <content>", "Reply content (markdown supported, use \"-\" for stdin)")
488
454
  .option("--rich-text <richText>", "Rich-text JSON array (mutually exclusive with --content)")
489
- .option("--auto-attachments", "Upload wiki-linked [[files]] to webdrive before posting (also attributes the reply to that vault)")
490
- .option("--vault-slug <vaultSlug>", "Attribute the reply to this vault (sets authorVaultSlug). Also used as upload destination for --auto-attachments.")
455
+ .option("--vault-slug <vaultSlug>", "Attribute the reply to this vault (sets authorVaultSlug). Caller must own the vault.")
491
456
  .option("--space-slug <spaceSlug>", "Space slug (overrides .gobi/settings.yaml)")
492
457
  .option("--attach <file>", "Local media file to attach to this reply. Repeatable. X-style mix rule: up to 4 photos OR 1 GIF OR 1 video. Size ceilings: 5MB photos / 15MB GIFs / 512MB video.", (value, prev = []) => [...prev, value], [])
493
458
  .action(async (postId, opts) => {
@@ -498,18 +463,12 @@ export function registerSpaceCommand(program) {
498
463
  throw new Error("--content and --rich-text are mutually exclusive.");
499
464
  }
500
465
  let authorVaultSlug;
501
- if (opts.vaultSlug || opts.autoAttachments) {
466
+ if (opts.vaultSlug) {
502
467
  authorVaultSlug = resolveVaultSlug(opts);
503
468
  }
504
469
  const body = {};
505
470
  if (opts.content != null) {
506
- const content = readContent(opts.content);
507
- if (opts.autoAttachments) {
508
- const token = await getValidToken();
509
- const links = extractWikiLinks(content);
510
- await uploadAttachments(authorVaultSlug, links, token, { addToSyncfiles: true });
511
- }
512
- body.content = content;
471
+ body.content = readContent(opts.content);
513
472
  }
514
473
  if (opts.richText != null) {
515
474
  let parsed;
@@ -542,11 +501,10 @@ export function registerSpaceCommand(program) {
542
501
  .description("Edit a reply you authored in a space.")
543
502
  .option("--content <content>", "New content for the reply (markdown supported, use \"-\" for stdin)")
544
503
  .option("--rich-text <richText>", "Rich-text JSON array (mutually exclusive with --content)")
545
- .option("--auto-attachments", "Upload wiki-linked [[files]] to webdrive before editing (also attributes the reply to that vault)")
546
- .option("--vault-slug <vaultSlug>", "Attribute the reply to this vault (sets authorVaultSlug). Also used as upload destination for --auto-attachments.")
504
+ .option("--vault-slug <vaultSlug>", "Attribute the reply to this vault (sets authorVaultSlug). Caller must own the vault.")
547
505
  .option("--space-slug <spaceSlug>", "Space slug (overrides .gobi/settings.yaml)")
548
506
  .action(async (replyId, opts) => {
549
- const wantsVaultChange = !!(opts.vaultSlug || opts.autoAttachments);
507
+ const wantsVaultChange = !!opts.vaultSlug;
550
508
  if (opts.content == null && opts.richText == null && !wantsVaultChange) {
551
509
  throw new Error("Provide at least --content, --rich-text, or --vault-slug to update.");
552
510
  }
@@ -555,18 +513,12 @@ export function registerSpaceCommand(program) {
555
513
  }
556
514
  const spaceSlug = resolveSpaceSlug(space, opts);
557
515
  let authorVaultSlug;
558
- if (opts.vaultSlug || opts.autoAttachments) {
516
+ if (opts.vaultSlug) {
559
517
  authorVaultSlug = resolveVaultSlug(opts);
560
518
  }
561
519
  const body = {};
562
520
  if (opts.content != null) {
563
- const content = readContent(opts.content);
564
- if (opts.autoAttachments) {
565
- const token = await getValidToken();
566
- const links = extractWikiLinks(content);
567
- await uploadAttachments(authorVaultSlug, links, token, { addToSyncfiles: true });
568
- }
569
- body.content = content;
521
+ body.content = readContent(opts.content);
570
522
  }
571
523
  if (opts.richText != null) {
572
524
  let parsed;
@@ -1,20 +1,5 @@
1
1
  import { readFileSync } from "fs";
2
- import { apiGet } from "../client.js";
3
2
  import { getSpaceSlug, getVaultSlug } from "./init.js";
4
- // Fetch a draft and return just the fields a post-creation flow needs. Used
5
- // by `space create-post` and `global create-post` when --draft-id is passed:
6
- // title/content come from the draft, vaultSlug seeds the post's authorship
7
- // when the caller did not pass --vault-slug.
8
- export async function fetchDraftSummary(draftId) {
9
- const resp = (await apiGet(`/app/drafts/${draftId}`));
10
- const data = unwrapResp(resp);
11
- return {
12
- draftId: String(data.draftId ?? draftId),
13
- title: String(data.title ?? ""),
14
- content: String(data.content ?? ""),
15
- vaultSlug: data.vaultSlug ?? null,
16
- };
17
- }
18
3
  // Reads all of stdin synchronously. Uses fd 0 (cross-platform) instead of
19
4
  // "/dev/stdin", which doesn't exist on Windows.
20
5
  export function readStdin() {
@@ -103,7 +103,7 @@ export function registerVaultCommand(program) {
103
103
  });
104
104
  const statusCmd = vault
105
105
  .command("status")
106
- .description("Show the configured vault's publish state and metadata (use before posting with --auto-attachments to confirm the vault is public).")
106
+ .description("Show the configured vault's publish state and metadata (use before authoring a markdown artifact with --auto-attachments to confirm the vault is public).")
107
107
  .option("--vault-slug <vaultSlug>", "Vault slug to inspect (defaults to .gobi/settings.yaml)")
108
108
  .action(async (opts) => {
109
109
  const slug = opts.vaultSlug ?? getVaultSlug();
package/dist/main.js CHANGED
@@ -11,7 +11,7 @@ import { registerVaultCommand } from "./commands/vault.js";
11
11
  import { registerSenseCommand } from "./commands/sense.js";
12
12
  import { registerUpdateCommand } from "./commands/update.js";
13
13
  import { registerMediaCommand } from "./commands/media.js";
14
- import { registerDraftCommand } from "./commands/draft.js";
14
+ import { registerArtifactCommand } from "./commands/artifact.js";
15
15
  const require = createRequire(import.meta.url);
16
16
  const { version } = require("../package.json");
17
17
  function hasParentOption(cmd, key) {
@@ -64,7 +64,7 @@ export async function cli() {
64
64
  registerSenseCommand(program);
65
65
  registerUpdateCommand(program);
66
66
  registerMediaCommand(program);
67
- registerDraftCommand(program);
67
+ registerArtifactCommand(program);
68
68
  // Propagate helpWidth to all subcommands
69
69
  const helpWidth = process.stdout.columns || 200;
70
70
  for (const cmd of program.commands) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gobi-ai/cli",
3
- "version": "2.0.23",
3
+ "version": "2.0.25",
4
4
  "description": "CLI client for the Gobi collaborative knowledge platform",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -0,0 +1,133 @@
1
+ ---
2
+ name: gobi-artifact
3
+ description: >-
4
+ Gobi artifact commands for versioned creations attached to posts: create,
5
+ revise, publish, revert, history, download, delete, get, list. An artifact
6
+ is a human-owned creation (image, video, gif, markdown, or meeting_summary)
7
+ whose revisions form a draft/published tree. Use when the user wants to
8
+ author, version, publish, or attach an artifact to a post.
9
+ allowed-tools: Bash(gobi:*)
10
+ metadata:
11
+ author: gobi-ai
12
+ version: "2.0.25"
13
+ ---
14
+
15
+ # gobi-artifact
16
+
17
+ Gobi artifact commands for versioned, post-attachable creations (v2.0.25).
18
+
19
+ Requires gobi-cli installed and authenticated. See gobi-core skill for setup.
20
+
21
+ ## What is an artifact?
22
+
23
+ An artifact is a versioned creation that can be attached to one or more posts. Each artifact has:
24
+
25
+ - **kind** — one of `image | video | gif | markdown | meeting_summary`. `markdown` and `meeting_summary` carry a markdown **body**; `image`, `gif`, and `video` carry an uploaded **media file**.
26
+ - **title** — optional display title.
27
+ - **owner** — always a human (the calling user). Even when an agent runs the CLI, the artifact is owned by the agent's owner.
28
+ - **revisions** — a draft/published tree. New revisions start as `draft`; publishing one makes it the artifact's single `published` revision (at most one published per artifact). `revise --from <revisionId>` branches off an earlier revision instead of the latest, so the history can fork.
29
+ - **metadata** — per-kind extras. For markdown kinds, `metadata.vaultSlug` is the anchor vault used to resolve `[[wikilinks]]` in the body.
30
+
31
+ Markdown bodies can reference vault notes with `[[wikilinks]]`. Resolution against the anchor vault (`--vault-slug` on create) only works for viewers who can read that vault.
32
+
33
+ ## Important: JSON Mode
34
+
35
+ For programmatic/agent usage, always pass `--json` as a **global** option (before the subcommand):
36
+
37
+ ```bash
38
+ gobi --json artifact list --limit 20
39
+ gobi --json artifact create --kind markdown --content "# Notes" --title "My note"
40
+ gobi --json artifact get <artifactId>
41
+ ```
42
+
43
+ JSON mode wraps the response as `{"success": true, "data": <artifact|revision|...>}` (or `{"success": false, "error": "..."}`).
44
+
45
+ ## Typical Workflow (markdown artifact)
46
+
47
+ Create a markdown artifact, attaching it to a post in the same call:
48
+
49
+ ```bash
50
+ gobi --json artifact create --kind markdown --file notes.md --title "Design notes" \
51
+ --vault-slug my-vault --post-id 12345
52
+ ```
53
+
54
+ The body can come from `--file <path>`, `--content <md>` inline, or stdin (`--content -`). `--vault-slug` anchors `[[wikilink]]` resolution and is stored as `metadata.vaultSlug`.
55
+
56
+ Add `--auto-attachments` (markdown kinds only) to upload any `[[wiki-linked files]]` in the body to the `--vault-slug` vault on webdrive before creating:
57
+
58
+ ```bash
59
+ gobi --json artifact create --kind markdown --file notes.md --vault-slug my-vault --auto-attachments
60
+ ```
61
+
62
+ Revise it (adds a new draft revision), then publish that revision:
63
+
64
+ ```bash
65
+ gobi --json artifact revise <artifactId> --file notes-v2.md --change-note "Tighten intro"
66
+ gobi --json artifact publish <artifactId> --revision <revisionId>
67
+ ```
68
+
69
+ `revise --auto-attachments` reuses the artifact's stored `metadata.vaultSlug` (it GETs the artifact first), so you don't repeat `--vault-slug`.
70
+
71
+ Inspect and roll back:
72
+
73
+ ```bash
74
+ gobi --json artifact history <artifactId> # full revision tree (owner only)
75
+ gobi --json artifact revert <artifactId> --to <revisionId>
76
+ ```
77
+
78
+ ## Typical Workflow (media artifact)
79
+
80
+ Image / gif / video kinds upload a local file (init → PUT → create) instead of a body:
81
+
82
+ ```bash
83
+ gobi --json artifact create --kind image --file diagram.png --title "Architecture" --post-id 12345
84
+ ```
85
+
86
+ Media-file size ceilings mirror post media: 5MB photos / 15MB GIFs / 512MB video, derived from the file's content type. Revise a media artifact by uploading a replacement file:
87
+
88
+ ```bash
89
+ gobi --json artifact revise <artifactId> --file diagram-v2.png --change-note "Add cache layer"
90
+ ```
91
+
92
+ ## Download
93
+
94
+ `download` defaults to the artifact's current (published, else latest) revision; pass `--revision` to pick one.
95
+
96
+ - markdown → writes the body to `--out <path>`, or prints to stdout when `--out` is omitted.
97
+ - media → fetches the `mediaUrl` bytes to `--out <path>` (defaults to `<artifactId>.<ext>`).
98
+
99
+ ```bash
100
+ gobi artifact download <artifactId> --out notes.md
101
+ gobi artifact download <artifactId> --revision <revisionId> --out image.png
102
+ ```
103
+
104
+ ## Attaching to a post
105
+
106
+ `create --post-id <id>` attaches the new artifact to a post **without clobbering** the post's existing artifacts: the CLI reads the post's current artifact attachments, appends the new id, and writes the merged set via the post edit endpoint (`PATCH /posts/:id` with `artifactIds`). The post API's `artifactIds` is otherwise a full replacement.
107
+
108
+ Attaching is done at artifact-create time via `--post-id`. The post commands (`create-post` / `edit-post`) do **not** expose an artifact-attach flag — to attach to an existing post, create the artifact with `--post-id <that post's id>`.
109
+
110
+ ## Available Commands
111
+
112
+ - `gobi artifact create` — Create an artifact (markdown body or uploaded media). `--post-id` attaches it to a post; `--auto-attachments` (markdown) uploads `[[wikilinks]]`.
113
+ - `gobi artifact revise` — Add a draft revision (new body or media file). `--from` branches off a specific revision.
114
+ - `gobi artifact publish` — Publish a revision (becomes the single published revision).
115
+ - `gobi artifact revert` — Move the published pointer to an earlier revision.
116
+ - `gobi artifact history` — List the full revision tree (owner only).
117
+ - `gobi artifact download` — Download a revision's content (markdown body or media bytes).
118
+ - `gobi artifact delete` — Delete an artifact and its revision tree.
119
+ - `gobi artifact get` — Get one artifact with its current revision.
120
+ - `gobi artifact list` — List your artifacts (`--kind`, `--limit`).
121
+
122
+ ## Confirm before mutating
123
+
124
+ Artifacts are user-owned creations. The authoring commands (`create`, `revise`) are the normal flow and run without extra confirmation. Two commands change what's live or destroy data — confirm first:
125
+
126
+ - `publish <id> --revision <id>` / `revert <id> --to <id>` — change which revision is published (visible on attached posts). Confirm the target revision with the user.
127
+ - `delete <id>` — irreversible (removes the artifact and its whole revision tree). Confirm the target id before running.
128
+
129
+ Read-only commands (`get`, `list`, `history`) and `download` run without confirmation.
130
+
131
+ ## Reference Documentation
132
+
133
+ - [gobi artifact](references/artifact.md)
@@ -0,0 +1,142 @@
1
+ # gobi artifact
2
+
3
+ ```
4
+ Usage: gobi artifact [options] [command]
5
+
6
+ Versioned creations attached to posts. Kinds: image | video | gif | markdown | meeting_summary. Always human-owned; revisions form a draft/published tree (at most one published per artifact).
7
+
8
+ Options:
9
+ -h, --help display help for command
10
+
11
+ Commands:
12
+ create [options] Create an artifact. markdown/meeting_summary kinds take a body via --file, --content, or stdin ("-"). image/gif/video kinds upload --file. Pass --post-id to attach
13
+ the new artifact to a post.
14
+ revise [options] <artifactId> Add a draft revision to an artifact. New body via --file, --content, or stdin (markdown), or --file (media). Use --from to branch off a specific revision.
15
+ publish [options] <artifactId> Publish a revision (becomes the artifact's single published revision).
16
+ revert [options] <artifactId> Revert the artifact's published pointer to an earlier revision.
17
+ history <artifactId> List the artifact's full revision tree (owner only).
18
+ download [options] <artifactId> Download an artifact's content. markdown → write the body; media → fetch the bytes. Defaults to the published/latest revision; pass --revision to pick one. Writes
19
+ to --out or stdout (markdown).
20
+ delete <artifactId> Delete an artifact (and its revision tree).
21
+ get <artifactId> Get one artifact with its current revision.
22
+ list [options] List your artifacts (newest first).
23
+ help [command] display help for command
24
+ ```
25
+
26
+ ## create
27
+
28
+ ```
29
+ Usage: gobi artifact create [options]
30
+
31
+ Create an artifact. markdown/meeting_summary kinds take a body via --file, --content, or stdin ("-"). image/gif/video kinds upload --file. Pass --post-id to attach the new artifact to a post.
32
+
33
+ Options:
34
+ --kind <kind> Artifact kind: image | video | gif | markdown | meeting_summary
35
+ --file <path> Local file: markdown body (markdown kinds) or media file (media kinds)
36
+ --content <md> Markdown body inline (markdown kinds; pass "-" for stdin)
37
+ --title <t> Display title
38
+ --vault-slug <slug> Anchor vault for [[wikilink]] resolution (markdown kinds). Stored in metadata.vaultSlug.
39
+ --post-id <id> Attach the created artifact to this post afterward
40
+ --auto-attachments Upload wiki-linked [[files]] to webdrive before creating (markdown kinds; uses --vault-slug)
41
+ --change-note <note> Note describing this revision
42
+ -h, --help display help for command
43
+ ```
44
+
45
+ ## revise
46
+
47
+ ```
48
+ Usage: gobi artifact revise [options] <artifactId>
49
+
50
+ Add a draft revision to an artifact. New body via --file, --content, or stdin (markdown), or --file (media). Use --from to branch off a specific revision.
51
+
52
+ Options:
53
+ --file <path> Local file: markdown body (markdown kinds) or media file (media kinds)
54
+ --content <md> Markdown body inline (markdown kinds; pass "-" for stdin)
55
+ --change-note <note> Note describing this revision
56
+ --from <revisionId> Branch the new draft off this revision (defaults to the latest)
57
+ --auto-attachments Upload wiki-linked [[files]] to webdrive before revising (markdown kinds; uses the artifact's stored metadata.vaultSlug)
58
+ -h, --help display help for command
59
+ ```
60
+
61
+ ## publish
62
+
63
+ ```
64
+ Usage: gobi artifact publish [options] <artifactId>
65
+
66
+ Publish a revision (becomes the artifact's single published revision).
67
+
68
+ Options:
69
+ --revision <revisionId> Revision to publish
70
+ -h, --help display help for command
71
+ ```
72
+
73
+ ## revert
74
+
75
+ ```
76
+ Usage: gobi artifact revert [options] <artifactId>
77
+
78
+ Revert the artifact's published pointer to an earlier revision.
79
+
80
+ Options:
81
+ --to <revisionId> Revision to revert to
82
+ -h, --help display help for command
83
+ ```
84
+
85
+ ## history
86
+
87
+ ```
88
+ Usage: gobi artifact history [options] <artifactId>
89
+
90
+ List the artifact's full revision tree (owner only).
91
+
92
+ Options:
93
+ -h, --help display help for command
94
+ ```
95
+
96
+ ## download
97
+
98
+ ```
99
+ Usage: gobi artifact download [options] <artifactId>
100
+
101
+ Download an artifact's content. markdown → write the body; media → fetch the bytes. Defaults to the published/latest revision; pass --revision to pick one. Writes to --out or stdout (markdown).
102
+
103
+ Options:
104
+ --revision <revisionId> Specific revision (defaults to the artifact's current revision)
105
+ --out <path> Write to this file (markdown defaults to stdout)
106
+ -h, --help display help for command
107
+ ```
108
+
109
+ ## delete
110
+
111
+ ```
112
+ Usage: gobi artifact delete [options] <artifactId>
113
+
114
+ Delete an artifact (and its revision tree).
115
+
116
+ Options:
117
+ -h, --help display help for command
118
+ ```
119
+
120
+ ## get
121
+
122
+ ```
123
+ Usage: gobi artifact get [options] <artifactId>
124
+
125
+ Get one artifact with its current revision.
126
+
127
+ Options:
128
+ -h, --help display help for command
129
+ ```
130
+
131
+ ## list
132
+
133
+ ```
134
+ Usage: gobi artifact list [options]
135
+
136
+ List your artifacts (newest first).
137
+
138
+ Options:
139
+ --kind <kind> Filter by kind: image | video | gif | markdown | meeting_summary
140
+ --limit <n> Max items to return
141
+ -h, --help display help for command
142
+ ```
@@ -8,12 +8,12 @@ description: >-
8
8
  allowed-tools: Bash(gobi:*)
9
9
  metadata:
10
10
  author: gobi-ai
11
- version: "2.0.23"
11
+ version: "2.0.25"
12
12
  ---
13
13
 
14
14
  # gobi-core
15
15
 
16
- Core CLI commands for the Gobi collaborative knowledge platform (v2.0.23).
16
+ Core CLI commands for the Gobi collaborative knowledge platform (v2.0.25).
17
17
 
18
18
  ## Prerequisites
19
19
 
@@ -41,7 +41,7 @@ brew tap gobi-ai/tap && brew install gobi
41
41
  - **Personal Post**: A post on the author's profile that surfaces in the public global feed. Same `Post` data model as a Space Post — only the scope differs.
42
42
  - **Space Post**: A post inside a community space.
43
43
  - **Space**: A shared community knowledge area. A user can be a member of one or more spaces; each space contains posts, replies, and connected vaults.
44
- - **Draft**: A unit of standing guidance authored by an agent during chat. Each draft carries 0–3 AI-suggested actions the user picks from. The top 5 pending drafts feed the agent's system prompt every turn.
44
+ - **Artifact**: A versioned, human-owned creation (image, video, gif, markdown, or meeting_summary) attached to posts. Its revisions form a draft/published tree (at most one published). See the **gobi-artifact** skill.
45
45
 
46
46
  ## Setup steps (run only what you need)
47
47
 
@@ -50,7 +50,7 @@ There is **no `gobi init`** command — each setup step is its own command, and
50
50
  | Step | Command | Unlocks |
51
51
  |------|---------|---------|
52
52
  | 1. Log in | `gobi auth login` | All authenticated commands |
53
- | 2. Configure a vault for this directory | `gobi vault init` | Every `gobi vault …` command; also lets `global create-post --auto-attachments` resolve that vault automatically |
53
+ | 2. Configure a vault for this directory | `gobi vault init` | Every `gobi vault …` command; also lets `artifact create --auto-attachments` resolve that vault automatically |
54
54
  | 3. Pick an active space for this directory | `gobi space warp` | Every `gobi space …` post/reply/feed command without needing `--space-slug` |
55
55
 
56
56
  After step 2 + step 3, `.gobi/settings.yaml` looks like:
@@ -72,7 +72,7 @@ gobi auth status
72
72
 
73
73
  | Command family | Needs vault in `.gobi`? | Needs space in `.gobi`? | Per-call override |
74
74
  |----------------|------------------------|------------------------|-------------------|
75
- | `auth …`, `update`, `draft …`, `media …`, `sense …` | no | no | – |
75
+ | `auth …`, `update`, `artifact …`, `media …`, `sense …` | no | no | – |
76
76
  | `vault publish` / `unpublish` / `sync` | **yes** | no | none — must run `gobi vault init` first |
77
77
  | `vault init` | no (it sets it up) | no | – |
78
78
  | `space list` / `warp [slug]` / `get [slug]` | no | no | – |
@@ -84,7 +84,7 @@ gobi auth status
84
84
  | `personal create-post` | optional¹ | no | command-level `--vault-slug <slug>` |
85
85
  | `personal edit-post` | optional² | no | command-level `--vault-slug <slug>` |
86
86
 
87
- ¹ `global create-post` accepts `--vault-slug` and `--auto-attachments`, both optional. With neither flag and no `vaultSlug` in `.gobi`, the post is created with no `authorVaultSlug` (vault-less personal post) — same as a Space post that isn't attributed to any vault. Set `--vault-slug` (or have `vaultSlug` in `.gobi` plus pass `--auto-attachments`) to attribute it.
87
+ ¹ `global create-post` accepts `--vault-slug`, optional. With no flag and no `vaultSlug` in `.gobi`, the post is created with no `authorVaultSlug` (vault-less personal post) — same as a Space post that isn't attributed to any vault. Set `--vault-slug` to attribute it.
88
88
 
89
89
  ² `global edit-post` only consults `--vault-slug` when you pass it explicitly. Use `--vault-slug ""` to detach an existing attribution; non-empty re-attaches.
90
90
 
@@ -139,4 +139,3 @@ Read-only commands (`auth status`, `space list`) run without confirmation.
139
139
  | `GOBI_BASE_URL` | `https://api.joingobi.com` | API server URL |
140
140
  | `GOBI_WEBDRIVE_BASE_URL` | `https://webdrive.joingobi.com` | File storage URL |
141
141
  | `GOBI_WEB_BASE_URL` | `https://gobispace.com` | Public web URL (used when assembling shareable links) |
142
- | `GOBI_SESSION_ID` | — | Default `--session` for `gobi draft add` (set automatically inside agent runs) |
@@ -7,7 +7,7 @@ description: >-
7
7
  allowed-tools: Bash(gobi:*)
8
8
  metadata:
9
9
  author: gobi-ai
10
- version: "2.0.23"
10
+ version: "2.0.25"
11
11
  ---
12
12
 
13
13
  # Gobi Homepage Developer Guide
@@ -10,12 +10,12 @@ description: >-
10
10
  allowed-tools: Bash(gobi:*)
11
11
  metadata:
12
12
  author: gobi-ai
13
- version: "2.0.23"
13
+ version: "2.0.25"
14
14
  ---
15
15
 
16
16
  # gobi-media
17
17
 
18
- Gobi media generation commands (v2.0.23).
18
+ Gobi media generation commands (v2.0.25).
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.23"
10
+ version: "2.0.25"
11
11
  ---
12
12
 
13
13
  # gobi-sense
14
14
 
15
- Gobi sense commands for activity and transcription data (v2.0.23).
15
+ Gobi sense commands for activity and transcription data (v2.0.25).
16
16
 
17
17
  Requires gobi-cli installed and authenticated. See the **gobi-core** skill for setup.
18
18