@gobi-ai/cli 2.0.28 → 2.0.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,12 +4,12 @@
4
4
  "name": "gobi-ai"
5
5
  },
6
6
  "description": "Claude Code plugin for the Gobi collaborative knowledge platform CLI",
7
- "version": "2.0.28",
7
+ "version": "2.0.30",
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.28",
12
+ "version": "2.0.30",
13
13
  "author": {
14
14
  "name": "gobi-ai"
15
15
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "gobi",
3
3
  "description": "Manage the Gobi collaborative knowledge platform from the command line",
4
- "version": "2.0.28",
4
+ "version": "2.0.30",
5
5
  "author": {
6
6
  "name": "gobi-ai"
7
7
  },
@@ -4,6 +4,7 @@ import { basename, join, extname, isAbsolute, resolve } from "path";
4
4
  import ignore from "ignore";
5
5
  import { WEBDRIVE_BASE_URL } from "./constants.js";
6
6
  import { apiPost } from "./client.js";
7
+ import { normalizeSyncPattern } from "./commands/sync.js";
7
8
  // Best-effort extension → MIME mapping. Anything we don't recognize falls
8
9
  // back to `application/octet-stream`; the backend caps size per content-type
9
10
  // tier (5MB photos / 15MB GIFs / 512MB video) so it's the authority on what's
@@ -129,8 +130,9 @@ function addToLocalSyncfiles(gobiDir, filePath) {
129
130
  if (isPathCovered(filePath, patterns))
130
131
  return;
131
132
  const syncfilesPath = join(gobiDir, "syncfiles");
132
- appendFileSync(syncfilesPath, `${EOL}${filePath}`);
133
- console.log(`Added to syncfiles: ${filePath}`);
133
+ const pattern = normalizeSyncPattern(filePath);
134
+ appendFileSync(syncfilesPath, `${EOL}${pattern}`);
135
+ console.log(`Added to syncfiles: ${pattern}`);
134
136
  }
135
137
  export async function uploadAttachments(vaultSlug, links, token, options) {
136
138
  const addToSyncfiles = options?.addToSyncfiles ?? false;
@@ -235,13 +235,16 @@ export function registerGlobalCommand(program) {
235
235
  .option("--content <content>", "New content (markdown supported, use \"-\" for stdin)")
236
236
  .option("--rich-text <richText>", "Rich-text JSON array (mutually exclusive with --content)")
237
237
  .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], [])
238
+ .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], [])
238
239
  .action(async (postId, opts) => {
239
240
  const wantsAttachChange = !!(opts.attach && opts.attach.length > 0);
241
+ const wantsArtifactChange = !!(opts.artifact && opts.artifact.length > 0);
240
242
  if (opts.title == null &&
241
243
  opts.content == null &&
242
244
  opts.richText == null &&
243
- !wantsAttachChange) {
244
- throw new Error("Provide at least --title, --content, --rich-text, or --attach to update.");
245
+ !wantsAttachChange &&
246
+ !wantsArtifactChange) {
247
+ throw new Error("Provide at least --title, --content, --rich-text, --attach, or --artifact to update.");
245
248
  }
246
249
  if (opts.content && opts.richText) {
247
250
  throw new Error("--content and --rich-text are mutually exclusive.");
@@ -266,6 +269,8 @@ export function registerGlobalCommand(program) {
266
269
  assertPostAttachmentMix(opts.attach);
267
270
  body.attachments = await uploadPostAttachments(opts.attach);
268
271
  }
272
+ if (opts.artifact && opts.artifact.length > 0)
273
+ body.artifactIds = opts.artifact;
269
274
  const resp = (await apiPatch(`/posts/${postId}`, body));
270
275
  const post = unwrapResp(resp);
271
276
  if (isJsonMode(global)) {
@@ -31,6 +31,15 @@ export function getVaultSlug() {
31
31
  }
32
32
  return vault;
33
33
  }
34
+ /**
35
+ * Starter PUBLISH.md frontmatter. Shared by `vault init` (seed) and
36
+ * `vault publish` (scaffold-on-missing) so the two paths can't drift.
37
+ * `description` is intentionally left blank — the vault won't list publicly
38
+ * until the user fills in both `title` and `description`.
39
+ */
40
+ export function defaultPublishMd(title) {
41
+ return `---\ntitle: ${title}\ntags: []\ndescription:\nthumbnail:\nprompt:\n---\n`;
42
+ }
34
43
  // Per-command requirement markers. Tri-state: true / false override / inherit
35
44
  // from parent. The pre-action warning uses these to decide whether to remind
36
45
  // the user to run `gobi vault init` / `gobi space warp`.
@@ -221,7 +230,7 @@ export async function runVaultInitFlow() {
221
230
  // Create default PUBLISH.md if it doesn't exist
222
231
  const publishPath = join(process.cwd(), "PUBLISH.md");
223
232
  if (!existsSync(publishPath)) {
224
- writeFileSync(publishPath, `---\ntitle: ${vaultName}\ntags: []\ndescription:\nthumbnail:\nprompt:\n---\n`, "utf-8");
233
+ writeFileSync(publishPath, defaultPublishMd(vaultName), "utf-8");
225
234
  console.log("Created PUBLISH.md");
226
235
  }
227
236
  }
@@ -246,13 +246,16 @@ export function registerPersonalCommand(program) {
246
246
  .option("--content <content>", "New content (markdown supported, use \"-\" for stdin)")
247
247
  .option("--rich-text <richText>", "Rich-text JSON array (mutually exclusive with --content)")
248
248
  .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], [])
249
+ .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], [])
249
250
  .action(async (postId, opts) => {
250
251
  const wantsAttachChange = !!(opts.attach && opts.attach.length > 0);
252
+ const wantsArtifactChange = !!(opts.artifact && opts.artifact.length > 0);
251
253
  if (opts.title == null &&
252
254
  opts.content == null &&
253
255
  opts.richText == null &&
254
- !wantsAttachChange) {
255
- throw new Error("Provide at least --title, --content, --rich-text, or --attach to update.");
256
+ !wantsAttachChange &&
257
+ !wantsArtifactChange) {
258
+ throw new Error("Provide at least --title, --content, --rich-text, --attach, or --artifact to update.");
256
259
  }
257
260
  if (opts.content && opts.richText) {
258
261
  throw new Error("--content and --rich-text are mutually exclusive.");
@@ -277,6 +280,8 @@ export function registerPersonalCommand(program) {
277
280
  assertPostAttachmentMix(opts.attach);
278
281
  body.attachments = await uploadPostAttachments(opts.attach);
279
282
  }
283
+ if (opts.artifact && opts.artifact.length > 0)
284
+ body.artifactIds = opts.artifact;
280
285
  const resp = (await apiPatch(`/posts/${postId}`, body));
281
286
  const post = unwrapResp(resp);
282
287
  if (isJsonMode(personal)) {
@@ -377,13 +377,16 @@ export function registerSpaceCommand(program) {
377
377
  .option("--rich-text <richText>", "Rich-text JSON array (mutually exclusive with --content)")
378
378
  .option("--space-slug <spaceSlug>", "Space slug (overrides .gobi/settings.yaml)")
379
379
  .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], [])
380
+ .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], [])
380
381
  .action(async (postId, opts) => {
381
382
  const wantsAttachChange = !!(opts.attach && opts.attach.length > 0);
383
+ const wantsArtifactChange = !!(opts.artifact && opts.artifact.length > 0);
382
384
  if (opts.title == null &&
383
385
  opts.content == null &&
384
386
  opts.richText == null &&
385
- !wantsAttachChange) {
386
- throw new Error("Provide at least --title, --content, --rich-text, or --attach to update.");
387
+ !wantsAttachChange &&
388
+ !wantsArtifactChange) {
389
+ throw new Error("Provide at least --title, --content, --rich-text, --attach, or --artifact to update.");
387
390
  }
388
391
  if (opts.content && opts.richText) {
389
392
  throw new Error("--content and --rich-text are mutually exclusive.");
@@ -409,6 +412,8 @@ export function registerSpaceCommand(program) {
409
412
  assertPostAttachmentMix(opts.attach);
410
413
  body.attachments = await uploadPostAttachments(opts.attach);
411
414
  }
415
+ if (opts.artifact && opts.artifact.length > 0)
416
+ body.artifactIds = opts.artifact;
412
417
  const resp = (await apiPatch(`/spaces/${spaceSlug}/posts/${postId}`, body));
413
418
  const post = unwrapResp(resp);
414
419
  if (isJsonMode(space)) {
@@ -142,6 +142,22 @@ export function saveSyncState(gobiDir, state) {
142
142
  }
143
143
  }
144
144
  // ─── Syncfiles ────────────────────────────────────────────────────────────────
145
+ /**
146
+ * Webdrive requires every sync/private pattern to be root-anchored — the server
147
+ * rejects anything not starting with "/" (HTTP 400: "Pattern must start with
148
+ * '/'"). Older syncfiles, hand-edits, and `--auto-attachments` appends can all
149
+ * produce slash-less entries that hard-fail the whole sync with a cryptic 400,
150
+ * so anchor them to the vault root here. Negation patterns ("!foo") keep their
151
+ * leading "!". Patterns already starting with "/" are returned unchanged, so
152
+ * anything the server currently accepts is untouched.
153
+ */
154
+ export function normalizeSyncPattern(pattern) {
155
+ if (pattern.startsWith("!")) {
156
+ const rest = pattern.slice(1);
157
+ return rest.startsWith("/") ? pattern : `!/${rest}`;
158
+ }
159
+ return pattern.startsWith("/") ? pattern : `/${pattern}`;
160
+ }
145
161
  export function readSyncfiles(gobiDir) {
146
162
  const syncfilesPath = join(gobiDir, "syncfiles");
147
163
  if (!existsSync(syncfilesPath)) {
@@ -151,7 +167,8 @@ export function readSyncfiles(gobiDir) {
151
167
  const patterns = content
152
168
  .split("\n")
153
169
  .map((l) => l.trim())
154
- .filter((l) => l.length > 0 && !l.startsWith("#"));
170
+ .filter((l) => l.length > 0 && !l.startsWith("#"))
171
+ .map(normalizeSyncPattern);
155
172
  const contentHash = createHash("md5").update(content).digest("hex");
156
173
  return { patterns, contentHash };
157
174
  }
@@ -162,7 +179,8 @@ export function readPrivatefiles(gobiDir) {
162
179
  return readFileSync(path, "utf-8")
163
180
  .split("\n")
164
181
  .map((l) => l.trim())
165
- .filter((l) => l.length > 0 && !l.startsWith("#"));
182
+ .filter((l) => l.length > 0 && !l.startsWith("#"))
183
+ .map(normalizeSyncPattern);
166
184
  }
167
185
  export function buildWhitelistMatcher(patterns) {
168
186
  if (patterns.length === 0)
@@ -1,10 +1,10 @@
1
- import { existsSync, readFileSync } from "fs";
1
+ import { existsSync, readFileSync, writeFileSync } from "fs";
2
2
  import { join, resolve as pathResolve } from "path";
3
3
  import { WEB_BASE_URL, WEBDRIVE_BASE_URL } from "../constants.js";
4
4
  import { getValidToken } from "../auth/manager.js";
5
5
  import { GobiError } from "../errors.js";
6
6
  import { apiDelete, apiGet, apiPatch, apiPost } from "../client.js";
7
- import { getVaultSlug, requireVault, runVaultInitFlow } from "./init.js";
7
+ import { defaultPublishMd, getVaultSlug, requireVault, runVaultInitFlow } from "./init.js";
8
8
  import { isJsonMode, jsonOut, unwrapResp } from "./utils.js";
9
9
  import { runSync } from "./sync.js";
10
10
  export const PUBLISH_FILENAME = "PUBLISH.md";
@@ -156,7 +156,19 @@ export function registerVaultCommand(program) {
156
156
  const vaultId = getVaultSlug();
157
157
  const filePath = join(process.cwd(), PUBLISH_FILENAME);
158
158
  if (!existsSync(filePath)) {
159
- throw new Error(`${PUBLISH_FILENAME} not found in ${process.cwd()}`);
159
+ // Scaffold a starter profile locally instead of dead-ending, but do NOT
160
+ // push it — an empty profile shouldn't go live without the user's review.
161
+ // (Legacy vaults that predate PUBLISH.md, e.g. BRAIN.md-only, land here.)
162
+ writeFileSync(filePath, defaultPublishMd(vaultId), "utf-8");
163
+ const msg = `${PUBLISH_FILENAME} not found, so a starter one was created at ${filePath}. ` +
164
+ `Fill in at least "title" and "description" (add "homepage" too if you have a custom homepage), ` +
165
+ `then re-run "gobi vault publish".`;
166
+ if (isJsonMode(vault)) {
167
+ jsonOut({ vaultId, published: false, created: PUBLISH_FILENAME, message: msg });
168
+ return;
169
+ }
170
+ console.log(msg);
171
+ return;
160
172
  }
161
173
  const content = readFileSync(filePath, "utf-8");
162
174
  const token = await getValidToken();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gobi-ai/cli",
3
- "version": "2.0.28",
3
+ "version": "2.0.30",
4
4
  "description": "CLI client for the Gobi collaborative knowledge platform",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -9,12 +9,12 @@ description: >-
9
9
  allowed-tools: Bash(gobi:*)
10
10
  metadata:
11
11
  author: gobi-ai
12
- version: "2.0.28"
12
+ version: "2.0.30"
13
13
  ---
14
14
 
15
15
  # gobi-artifact
16
16
 
17
- Gobi artifact commands for versioned, post-attachable creations (v2.0.28).
17
+ Gobi artifact commands for versioned, post-attachable creations (v2.0.30).
18
18
 
19
19
  Requires gobi-cli installed and authenticated. See gobi-core skill for setup.
20
20
 
@@ -103,9 +103,13 @@ gobi artifact download <artifactId> --revision <revisionId> --out image.png
103
103
 
104
104
  ## Attaching to a post
105
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.
106
+ Three ways to attach an artifact, depending on what already exists:
107
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>`.
108
+ 1. **At artifact-create time** `gobi artifact create … --post-id <id>` attaches the new artifact to an existing post **without clobbering** its current artifacts: the CLI reads the post's current artifact attachments, appends the new id, and writes the merged set via `PATCH /posts/:id` (`artifactIds`).
109
+ 2. **At post-create time** — `gobi <lane> create-post … --artifact <artifactId>` attaches one or more **already-created** artifacts to the new post (`--artifact` is repeatable).
110
+ 3. **Editing an existing post** — `gobi <lane> edit-post <id> --artifact <artifactId>` sets the post's artifact attachments. Unlike `create --post-id` (which merges), the post API's `artifactIds` is a **full replacement** — pass every artifact you want on the post, since omitted ones are removed (omitting `--artifact` entirely leaves them unchanged).
111
+
112
+ The same artifact can be attached to **multiple posts** (it's a reusable, versioned creation — each post renders its currently-published revision, so revising + publishing updates every post at once). Create it once, then reference its id via `--artifact` on each post.
109
113
 
110
114
  ## Available Commands
111
115
 
@@ -8,12 +8,12 @@ description: >-
8
8
  allowed-tools: Bash(gobi:*)
9
9
  metadata:
10
10
  author: gobi-ai
11
- version: "2.0.28"
11
+ version: "2.0.30"
12
12
  ---
13
13
 
14
14
  # gobi-core
15
15
 
16
- Core CLI commands for the Gobi collaborative knowledge platform (v2.0.28).
16
+ Core CLI commands for the Gobi collaborative knowledge platform (v2.0.30).
17
17
 
18
18
  ## Prerequisites
19
19
 
@@ -7,7 +7,7 @@ description: >-
7
7
  allowed-tools: Bash(gobi:*)
8
8
  metadata:
9
9
  author: gobi-ai
10
- version: "2.0.28"
10
+ version: "2.0.30"
11
11
  ---
12
12
 
13
13
  # Gobi Homepage Developer Guide
@@ -20,13 +20,26 @@ A **Gobi Homepage** is a custom HTML page hosted on a vault's webdrive and serve
20
20
 
21
21
  ## Setup
22
22
 
23
- 1. Create an HTML file in the vault (e.g. `app/home.html`) and upload:
23
+ A homepage is registered only after `PUBLISH.md` references it **and** the vault is re-published. Uploading the HTML alone does **not** set the homepage — `gobi vault status` will keep reporting `homepagePath: null` until you publish.
24
+
25
+ 1. **Create** the HTML file at a vault-root-relative path, e.g. `app/home.html` (not `_Gobi_/app/home.html` — paths are relative to the vault root).
26
+ 2. **Reference it** in `PUBLISH.md` frontmatter via the `homepage` property:
27
+ - `homepage: "[[app/home.html]]"` — Gobi sidebar visible alongside the homepage
28
+ - `homepage: "[[app/home.html?nav=false]]"` — full-screen, no Gobi chrome
29
+ 3. **Upload** the HTML to webdrive:
24
30
  ```bash
25
- gobi vault sync
31
+ gobi vault sync --upload-only --path app/home.html
26
32
  ```
27
- 2. Set `homepage` in PUBLISH.md (homepage property):
28
- - `homepage: "[[app/home.html]]"` — Gobi sidebars visible alongside the homepage
29
- - `homepage: "[[app/home.html?nav=false]]"` — full-screen, no Gobi chrome
33
+ 4. **Publish** so the updated frontmatter takes effect (this uploads `PUBLISH.md` and re-syncs vault metadata):
34
+ ```bash
35
+ gobi vault publish
36
+ ```
37
+ 5. **Verify** the homepage is registered:
38
+ ```bash
39
+ gobi --json vault status # expect "homepagePath": "app/home.html"
40
+ ```
41
+
42
+ > Any time you change the `homepage` frontmatter, re-run `gobi vault publish` — the file upload alone won't update the profile. See the **gobi-vault** skill for the full publishing workflow.
30
43
 
31
44
  ---
32
45
 
@@ -10,12 +10,12 @@ description: >-
10
10
  allowed-tools: Bash(gobi:*)
11
11
  metadata:
12
12
  author: gobi-ai
13
- version: "2.0.28"
13
+ version: "2.0.30"
14
14
  ---
15
15
 
16
16
  # gobi-media
17
17
 
18
- Gobi media generation commands (v2.0.28).
18
+ Gobi media generation commands (v2.0.30).
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.28"
10
+ version: "2.0.30"
11
11
  ---
12
12
 
13
13
  # gobi-sense
14
14
 
15
- Gobi sense commands for activity and transcription data (v2.0.28).
15
+ Gobi sense commands for activity and transcription data (v2.0.30).
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.28"
14
+ version: "2.0.30"
15
15
  ---
16
16
 
17
17
  # gobi-space
18
18
 
19
- Gobi space, global, and personal-space posts (v2.0.28).
19
+ Gobi space, global, and personal-space posts (v2.0.30).
20
20
 
21
21
  Requires gobi-cli installed and authenticated. See the **gobi-core** skill for setup.
22
22
 
@@ -47,7 +47,7 @@ The same applies to replies: a reply has only `--content` (no title), so do not
47
47
 
48
48
  ## Attaching artifacts (`--artifact`)
49
49
 
50
- Posts have no vault attribution. `create-post` across all three scopes (`gobi space`, `gobi global`, `gobi personal`) accepts `--artifact <artifactId>` (repeatable) to attach existing artifacts to the post. To author a vault-anchored document, create a markdown artifact (`gobi artifact create --kind markdown --vault-slug <slug>`) and attach it via `--artifact`. See the **gobi-artifact** skill.
50
+ Posts have no vault attribution. Both `create-post` and `edit-post` across all three scopes (`gobi space`, `gobi global`, `gobi personal`) accept `--artifact <artifactId>` (repeatable) to attach existing artifacts. On `create-post` it sets the new post's artifacts; on `edit-post` it **replaces** the post's artifact set wholesale (pass every artifact you want; omit `--artifact` to leave them unchanged). The same artifact can be attached to multiple posts — it's a reusable, versioned creation, and each post renders its currently-published revision. To author a vault-anchored document, create a markdown artifact (`gobi artifact create --kind markdown --vault-slug <slug>`) and attach it via `--artifact`. See the **gobi-artifact** skill.
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
 
@@ -89,12 +89,14 @@ Usage: gobi global edit-post [options] <postId>
89
89
  Edit a post you authored in the global feed.
90
90
 
91
91
  Options:
92
- --title <title> New title
93
- --content <content> New content (markdown supported, use "-" for stdin)
94
- --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. X-style mix rule: up to 4 photos OR 1 GIF OR 1 video. Size
96
- ceilings: 5MB photos / 15MB GIFs / 512MB video. Omit to leave attachments unchanged. (default: [])
97
- -h, --help display help for command
92
+ --title <title> New title
93
+ --content <content> New content (markdown supported, use "-" for stdin)
94
+ --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. X-style mix rule: up to 4 photos OR 1 GIF OR 1 video. Size
96
+ ceilings: 5MB photos / 15MB GIFs / 512MB video. Omit to leave attachments unchanged. (default: [])
97
+ --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
+ with `gobi artifact create`. (default: [])
99
+ -h, --help display help for command
98
100
  ```
99
101
 
100
102
  ## delete-post
@@ -88,12 +88,14 @@ Usage: gobi personal edit-post [options] <postId>
88
88
  Edit a post you authored in your personal space.
89
89
 
90
90
  Options:
91
- --title <title> New title
92
- --content <content> New content (markdown supported, use "-" for stdin)
93
- --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. X-style mix rule: up to 4 photos OR 1 GIF OR 1 video. Size
95
- ceilings: 5MB photos / 15MB GIFs / 512MB video. Omit to leave attachments unchanged. (default: [])
96
- -h, --help display help for command
91
+ --title <title> New title
92
+ --content <content> New content (markdown supported, use "-" for stdin)
93
+ --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. X-style mix rule: up to 4 photos OR 1 GIF OR 1 video. Size
95
+ ceilings: 5MB photos / 15MB GIFs / 512MB video. Omit to leave attachments unchanged. (default: [])
96
+ --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
+ with `gobi artifact create`. (default: [])
98
+ -h, --help display help for command
97
99
  ```
98
100
 
99
101
  ## delete-post
@@ -140,6 +140,8 @@ Options:
140
140
  --space-slug <spaceSlug> Space slug (overrides .gobi/settings.yaml)
141
141
  --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
142
142
  ceilings: 5MB photos / 15MB GIFs / 512MB video. Omit to leave attachments unchanged. (default: [])
143
+ --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
+ with `gobi artifact create`. (default: [])
143
145
  -h, --help display help for command
144
146
  ```
145
147
 
@@ -8,12 +8,12 @@ description: >-
8
8
  allowed-tools: Bash(gobi:*)
9
9
  metadata:
10
10
  author: gobi-ai
11
- version: "2.0.28"
11
+ version: "2.0.30"
12
12
  ---
13
13
 
14
14
  # gobi-vault
15
15
 
16
- Gobi vault commands for publishing your vault profile and syncing files (v2.0.28).
16
+ Gobi vault commands for publishing your vault profile and syncing files (v2.0.30).
17
17
 
18
18
  Requires gobi-cli installed and authenticated. See gobi-core skill for setup.
19
19
 
@@ -46,7 +46,7 @@ gobi --json vault publish
46
46
  - `gobi vault create <slug> --name <name>` — Create a new vault with the given slug and display name. Slug must be unique (use `vault list` to see what's taken). Does not change the configured vault — run `vault init` here afterwards if you want to anchor to it.
47
47
  - `gobi vault rename <newName>` — Rename the configured vault's display name. Pass `--vault-slug <slug>` to target another vault. Local handle only — the public profile title comes from `PUBLISH.md` frontmatter and is unaffected.
48
48
  - `gobi vault delete <slug>` — Delete a vault. Irreversible. Required arg, no `.gobi` fallback. The API will reject if the vault still owns content; clean up posts, members, and files first.
49
- - `gobi vault publish` — Upload `PUBLISH.md` to the vault root on webdrive. Triggers post-processing (vault profile sync, metadata update).
49
+ - `gobi vault publish` — Upload `PUBLISH.md` to the vault root on webdrive. Triggers post-processing (vault profile sync, metadata update). If `PUBLISH.md` is missing (e.g. a legacy vault that only has `BRAIN.md`), a starter `PUBLISH.md` is scaffolded locally and **nothing is pushed** — fill in at least `title` and `description`, then re-run.
50
50
  - `gobi vault unpublish` — Delete `PUBLISH.md` from the vault on webdrive.
51
51
  - `gobi vault sync` — Sync local vault files with Gobi Webdrive. Supports `--upload-only`, `--download-only`, `--conflict <ask|server|client|skip>`, `--dry-run`, `--full`, `--path <p>`, `--plan-file`, `--execute`.
52
52