@gobi-ai/cli 1.3.7 → 2.0.0

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.
Files changed (37) hide show
  1. package/.claude-plugin/marketplace.json +6 -7
  2. package/.claude-plugin/plugin.json +4 -5
  3. package/README.md +78 -89
  4. package/commands/space-explore.md +10 -10
  5. package/commands/space-share.md +13 -7
  6. package/dist/commands/draft.js +213 -0
  7. package/dist/commands/global.js +205 -70
  8. package/dist/commands/init.js +5 -5
  9. package/dist/commands/{notes.js → saved.js} +112 -19
  10. package/dist/commands/space.js +92 -97
  11. package/dist/commands/sync.js +2 -56
  12. package/dist/commands/vault.js +113 -0
  13. package/dist/main.js +6 -10
  14. package/package.json +2 -2
  15. package/skills/gobi-core/SKILL.md +5 -7
  16. package/skills/gobi-core/references/space.md +18 -19
  17. package/skills/gobi-draft/SKILL.md +74 -0
  18. package/skills/gobi-draft/references/draft.md +109 -0
  19. package/skills/gobi-homepage/SKILL.md +16 -16
  20. package/skills/gobi-saved/SKILL.md +59 -0
  21. package/skills/gobi-saved/references/saved.md +52 -0
  22. package/skills/gobi-space/SKILL.md +34 -31
  23. package/skills/gobi-space/references/global.md +84 -24
  24. package/skills/gobi-space/references/space.md +45 -57
  25. package/skills/gobi-vault/SKILL.md +92 -0
  26. package/skills/{gobi-core/references/sync.md → gobi-vault/references/vault.md} +41 -2
  27. package/dist/commands/brain.js +0 -141
  28. package/dist/commands/feed.js +0 -148
  29. package/dist/commands/proposal.js +0 -185
  30. package/skills/gobi-brain/SKILL.md +0 -100
  31. package/skills/gobi-brain/references/brain.md +0 -66
  32. package/skills/gobi-feed/SKILL.md +0 -43
  33. package/skills/gobi-feed/references/feed.md +0 -80
  34. package/skills/gobi-notes/SKILL.md +0 -52
  35. package/skills/gobi-notes/references/notes.md +0 -82
  36. package/skills/gobi-proposal/SKILL.md +0 -66
  37. package/skills/gobi-proposal/references/proposal.md +0 -116
@@ -4,12 +4,12 @@
4
4
  "name": "gobi-ai"
5
5
  },
6
6
  "description": "Claude Code plugin for the Gobi collaborative knowledge platform CLI",
7
- "version": "1.3.7",
7
+ "version": "1.3.8",
8
8
  "plugins": [
9
9
  {
10
10
  "name": "gobi",
11
- "description": "Manage the Gobi collaborative knowledge platform from the command line. Search and ask brains, publish brain documents, create threads, manage sessions, generate images and videos.",
12
- "version": "1.3.7",
11
+ "description": "Manage the Gobi collaborative knowledge platform from the command line. Publish vault profiles, create posts and replies, manage saved notes and posts, manage sessions, generate images and videos.",
12
+ "version": "1.3.8",
13
13
  "author": {
14
14
  "name": "gobi-ai"
15
15
  },
@@ -18,10 +18,9 @@
18
18
  "skills": [
19
19
  "./skills/gobi-core",
20
20
  "./skills/gobi-space",
21
- "./skills/gobi-brain",
22
- "./skills/gobi-feed",
23
- "./skills/gobi-notes",
24
- "./skills/gobi-proposal",
21
+ "./skills/gobi-vault",
22
+ "./skills/gobi-saved",
23
+ "./skills/gobi-draft",
25
24
  "./skills/gobi-media",
26
25
  "./skills/gobi-sense",
27
26
  "./skills/gobi-homepage"
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "gobi",
3
3
  "description": "Manage the Gobi collaborative knowledge platform from the command line",
4
- "version": "1.3.7",
4
+ "version": "1.3.8",
5
5
  "author": {
6
6
  "name": "gobi-ai"
7
7
  },
@@ -9,10 +9,9 @@
9
9
  "skills": [
10
10
  "./skills/gobi-core",
11
11
  "./skills/gobi-space",
12
- "./skills/gobi-brain",
13
- "./skills/gobi-feed",
14
- "./skills/gobi-notes",
15
- "./skills/gobi-proposal",
12
+ "./skills/gobi-vault",
13
+ "./skills/gobi-saved",
14
+ "./skills/gobi-draft",
16
15
  "./skills/gobi-media",
17
16
  "./skills/gobi-sense",
18
17
  "./skills/gobi-homepage"
package/README.md CHANGED
@@ -34,17 +34,21 @@ npm link
34
34
  ## Quick start
35
35
 
36
36
  ```sh
37
- # Initialize — logs in and sets up your vault
37
+ # Initialize — logs in and sets up your vault (creates PUBLISH.md)
38
38
  gobi init
39
39
 
40
40
  # Select a space
41
41
  gobi space warp
42
42
 
43
- # Search brains across your spaces
44
- gobi brain search --query "machine learning"
43
+ # Publish your vault profile (after editing PUBLISH.md frontmatter)
44
+ gobi vault publish
45
45
 
46
- # Ask a brain a question
47
- gobi brain ask --vault-slug my-vault --question "What is RAG?"
46
+ # Sync local files with the webdrive
47
+ gobi vault sync
48
+
49
+ # Browse the global feed and create a post
50
+ gobi global feed
51
+ gobi global create-post --title "Hello" --content "Trying gobi"
48
52
  ```
49
53
 
50
54
  ## Commands
@@ -61,22 +65,34 @@ gobi brain ask --vault-slug my-vault --question "What is RAG?"
61
65
 
62
66
  | Command | Description |
63
67
  |---------|-------------|
64
- | `gobi init` | Log in (if needed) and select or create a vault |
68
+ | `gobi init` | Log in (if needed) and select or create a vault. Creates `PUBLISH.md` if missing. |
65
69
  | `gobi space list` | List spaces you are a member of |
66
70
  | `gobi space warp [spaceSlug]` | Select the active space (interactive if slug omitted) |
67
71
 
68
- ### Brains
72
+ ### Vault
69
73
 
70
74
  | Command | Description |
71
75
  |---------|-------------|
72
- | `gobi brain search --query <q>` | Search public brains by text and semantic similarity |
73
- | `gobi brain ask --vault-slug <slug> --question <q>` | Ask a brain a question (creates a 1:1 session) |
74
- | `gobi brain publish` | Upload `BRAIN.md` to your vault |
75
- | `gobi brain unpublish` | Remove `BRAIN.md` from your vault |
76
+ | `gobi vault publish` | Upload `PUBLISH.md` to your vault. Triggers profile/metadata refresh. |
77
+ | `gobi vault unpublish` | Remove `PUBLISH.md` from your vault. |
78
+ | `gobi vault sync` | Sync local vault files with Gobi Webdrive. |
76
79
 
77
- Public brains are accessible at `https://gobispace.com/@{vaultSlug}`.
80
+ Public vaults are accessible at `https://gobispace.com/@{vaultSlug}`.
78
81
 
79
- `brain ask` also accepts `--rich-text <json>` (mutually exclusive with `--question`) and `--mode <auto|manual>`.
82
+ `vault sync` options:
83
+
84
+ | Option | Description |
85
+ |--------|-------------|
86
+ | `--upload-only` | Only upload local changes to server |
87
+ | `--download-only` | Only download server changes to local |
88
+ | `--conflict <strategy>` | Conflict resolution: `ask` (default), `server`, `client`, `skip` |
89
+ | `--dir <path>` | Local vault directory (default: current directory) |
90
+ | `--dry-run` | Preview changes without making them |
91
+ | `--full` | Full sync: ignore cursor and hash cache, re-check every file |
92
+ | `--path <path>` | Restrict sync to specific file/folder (repeatable) |
93
+ | `--plan-file <path>` | Write dry-run plan to file, or read plan to execute |
94
+ | `--execute` | Execute a previously written plan file (requires `--plan-file`) |
95
+ | `--conflict-choices <json>` | Per-file conflict resolutions as JSON (use with `--execute`) |
80
96
 
81
97
  ### Spaces
82
98
 
@@ -85,52 +101,33 @@ Public brains are accessible at `https://gobispace.com/@{vaultSlug}`.
85
101
  | Command | Description |
86
102
  |---------|-------------|
87
103
  | `gobi space get [spaceSlug]` | Show space details (uses current space if slug omitted) |
88
- | `gobi space messages` | Unified message feed (threads + replies, newest first) |
89
- | `gobi space ancestors <threadId>` | Walk a thread/reply's lineage from root immediate parent |
90
-
91
- ### Feed
92
-
93
- | Command | Description |
94
- |---------|-------------|
95
- | `gobi feed list` | List recent brain updates from the global public feed |
96
- | `gobi feed get <updateId>` | Get a feed brain update and its replies |
97
- | `gobi feed post-reply <updateId> --content <c>` | Post a reply to a brain update in the feed |
98
- | `gobi feed edit-reply <replyId> --content <c>` | Edit a reply you authored |
99
- | `gobi feed delete-reply <replyId>` | Delete a reply you authored |
100
-
101
- `feed list` and `feed get` accept `--limit`/`--cursor` for pagination.
102
-
103
- ### Threads
104
-
105
- > **Migration note:** Brain-update commands have been removed. To post user-level content, use `gobi global create-thread` (global space) or `gobi space create-thread` (a specific space).
106
-
107
- | Command | Description |
108
- |---------|-------------|
109
- | `gobi space list-threads` | List threads in the current space |
110
- | `gobi space get-thread <id>` | Get a thread and its replies |
111
- | `gobi space create-thread --title <t> --content <c>` | Create a thread |
112
- | `gobi space edit-thread <id> [--title <t>] [--content <c>]` | Edit a thread (at least one required) |
113
- | `gobi space delete-thread <id>` | Delete a thread |
114
-
115
- ### Replies
116
-
117
- | Command | Description |
118
- |---------|-------------|
119
- | `gobi space create-reply <threadId> --content <c>` | Reply to a thread |
104
+ | `gobi space feed` | Unified feed (posts + replies, newest first) in the space |
105
+ | `gobi space list-topics` | List topics in the space, ordered by most recent linkage |
106
+ | `gobi space list-topic-posts <topicSlug>` | List posts tagged with a topic |
107
+ | `gobi space list-posts` | List posts in the space |
108
+ | `gobi space get-post <postId>` | Get a post with its ancestors and replies |
109
+ | `gobi space create-post --title <t> --content <c>` | Create a post |
110
+ | `gobi space edit-post <postId> [--title <t>] [--content <c>]` | Edit a post (at least one required) |
111
+ | `gobi space delete-post <postId>` | Delete a post |
112
+ | `gobi space create-reply <postId> --content <c>` | Reply to a post |
120
113
  | `gobi space edit-reply <replyId> --content <c>` | Edit a reply |
121
114
  | `gobi space delete-reply <replyId>` | Delete a reply |
122
115
 
123
- ### Global thread space
116
+ ### Global feed
124
117
 
125
- The global thread space is a slugless message feed visible across all spaces.
118
+ The global feed is the public, slugless feed of vault-authored posts visible across all spaces.
126
119
 
127
120
  | Command | Description |
128
121
  |---------|-------------|
129
- | `gobi global messages` | List the global unified message feed (newest first) |
130
- | `gobi global get-thread <id>` | Get a global thread and its direct replies |
131
- | `gobi global ancestors <id>` | Walk a global thread/reply's lineage |
132
- | `gobi global create-thread [--title <t>] (--content <c> \| --rich-text <json>)` | Create a thread in the global space |
133
- | `gobi global reply <threadId> (--content <c> \| --rich-text <json>)` | Reply to a global thread |
122
+ | `gobi global feed` | List the global public feed (posts + replies, newest first) |
123
+ | `gobi global list-posts [--mine] [--vault-slug <slug>]` | List posts in the global feed |
124
+ | `gobi global get-post <postId>` | Get a global post with its ancestors and replies |
125
+ | `gobi global create-post [--title <t>] (--content <c> \| --rich-text <json>)` | Create a post in the global feed |
126
+ | `gobi global edit-post <postId> [--title <t>] [--content <c>]` | Edit a post you authored |
127
+ | `gobi global delete-post <postId>` | Delete a post you authored |
128
+ | `gobi global create-reply <postId> (--content <c> \| --rich-text <json>)` | Reply to a global post |
129
+ | `gobi global edit-reply <replyId> --content <c>` | Edit a reply you authored |
130
+ | `gobi global delete-reply <replyId>` | Delete a reply you authored |
134
131
 
135
132
  ### Sessions
136
133
 
@@ -151,53 +148,44 @@ The global thread space is a slugless message feed visible across all spaces.
151
148
 
152
149
  Times are ISO 8601 UTC (e.g. `2026-03-20T00:00:00Z`).
153
150
 
154
- ### Notes
151
+ ### Saved
152
+
153
+ `gobi saved` is the user's personal saved-knowledge collection — notes you author and posts you bookmark.
154
+
155
+ #### Saved notes
155
156
 
156
157
  | Command | Description |
157
158
  |---------|-------------|
158
- | `gobi notes list [--date YYYY-MM-DD]` | List your notes (recent via cursor, or all for a day) |
159
- | `gobi notes get <id>` | Get a single note |
160
- | `gobi notes create --content <c>` | Create a note (use `-` to read content from stdin) |
161
- | `gobi notes edit <id> [--content <c>] [--agent-id <id>]` | Edit a note (at least one required; `--agent-id null` clears the link) |
162
- | `gobi notes delete <id>` | Delete a note you authored |
163
-
164
- `notes list` and `notes create` accept `--timezone <iana>` (default: system timezone) for day boundaries.
165
- `notes list` also accepts `--limit`/`--cursor` for pagination.
159
+ | `gobi saved note list [--date YYYY-MM-DD]` | List your notes (recent via cursor, or all for a day) |
160
+ | `gobi saved note get <id>` | Get a single note |
161
+ | `gobi saved note create --content <c>` | Create a note (use `-` to read content from stdin) |
162
+ | `gobi saved note edit <id> [--content <c>] [--agent-id <id>]` | Edit a note (at least one required; `--agent-id null` clears the link) |
163
+ | `gobi saved note delete <id>` | Delete a note you authored |
166
164
 
167
- ### Proposals
165
+ `saved note list` and `saved note create` accept `--timezone <iana>` (default: system timezone).
168
166
 
169
- Proposals are authored by your agent during chat (or by external agents using `gobi proposal add` as their tool layer). The top 5 pending proposals (lowest priority first) feed the agent's system prompt every turn. Every proposal is anchored to the chat session that produced it.
167
+ #### Saved posts
170
168
 
171
169
  | Command | Description |
172
170
  |---------|-------------|
173
- | `gobi proposal list [--limit N]` | List proposals (priority ASC, then newest first) |
174
- | `gobi proposal get <id>` | Show one proposal with its history |
175
- | `gobi proposal add <title> <content> [--session <id>] [--priority N]` | Add a proposal (use `-` for content to read from stdin). `--session` falls back to `$GOBI_SESSION_ID`. |
176
- | `gobi proposal edit <id> [--title <t>] [--content <c>]` | Update title and/or content; bumps revision (use `-` for stdin) |
177
- | `gobi proposal delete <id>` | Delete a proposal |
178
- | `gobi proposal prioritize <id> <priority>` | Set priority (lower = higher) |
179
- | `gobi proposal accept <id>` | Mark as accepted (the client posts the synthesized message into the session) |
180
- | `gobi proposal reject <id>` | Mark as rejected |
181
- | `gobi proposal revise <id> <comment>` | Mark for revision and record the user's comment |
182
-
183
- ### Sync
171
+ | `gobi saved post list [--type all\|article\|space-post]` | List posts you've saved |
172
+ | `gobi saved post get <postId>` | Get a saved post snapshot |
173
+ | `gobi saved post create --source <id>` | Save a post or reply by id |
174
+ | `gobi saved post delete <postId>` | Remove a post from your saved collection |
175
+
176
+ ### Drafts
177
+
178
+ Drafts are authored by your agent during chat (or by external agents using `gobi draft add` as their tool layer). Each draft carries 0–3 AI-suggested actions the user can pick from. The top 5 pending drafts (lowest priority first) feed the agent's system prompt every turn. Every draft is anchored to the chat session that produced it.
184
179
 
185
180
  | Command | Description |
186
181
  |---------|-------------|
187
- | `gobi sync` | Sync local vault files with Gobi Webdrive |
188
-
189
- | Option | Description |
190
- |--------|-------------|
191
- | `--upload-only` | Only upload local changes to server |
192
- | `--download-only` | Only download server changes to local |
193
- | `--conflict <strategy>` | Conflict resolution: `ask` (default), `server`, `client`, `skip` |
194
- | `--dir <path>` | Local vault directory (default: current directory) |
195
- | `--dry-run` | Preview changes without making them |
196
- | `--full` | Full sync: ignore cursor and hash cache, re-check every file |
197
- | `--path <path>` | Restrict sync to specific file/folder (repeatable) |
198
- | `--plan-file <path>` | Write dry-run plan to file, or read plan to execute |
199
- | `--execute` | Execute a previously written plan file (requires `--plan-file`) |
200
- | `--conflict-choices <json>` | Per-file conflict resolutions as JSON (use with `--execute`) |
182
+ | `gobi draft list [--limit N]` | List drafts (priority ASC, then newest first) |
183
+ | `gobi draft get <id>` | Show one draft with its history and suggested actions |
184
+ | `gobi draft add <title> <content> [--session <id>] [--priority N] [--action <label>]…` | Add a draft. Pass `--action` up to 3 times to attach AI-suggested actions. `--session` falls back to `$GOBI_SESSION_ID`. Use `-` for content to read from stdin. |
185
+ | `gobi draft delete <id>` | Delete a draft |
186
+ | `gobi draft prioritize <id> <priority>` | Set priority (lower = higher) |
187
+ | `gobi draft action <id> <index>` | Take one of the draft's suggested actions by 0-based index. Marks `actioned` and posts the synthesized message into the originating session. |
188
+ | `gobi draft revise <id> <comment> [--title <t>] [--content <c>] [--action <label>]…` | Bump revision with a comment; optionally replace title / content / actions in the same call |
201
189
 
202
190
  ### Global options
203
191
 
@@ -205,7 +193,7 @@ Proposals are authored by your agent during chat (or by external agents using `g
205
193
  |--------|-------|-------------|
206
194
  | `--json` | All commands | Output results as JSON |
207
195
  | `--space-slug <slug>` | `space` commands | Override the default space (from `.gobi/settings.yaml`) |
208
- | `--vault-slug <slug>` | Per-command | Override the default vault; available on `space create-thread`, `space edit-thread`, `space edit-reply` |
196
+ | `--vault-slug <slug>` | Per-command | Override the default vault; available on post/reply commands that upload attachments and on `global create-post` |
209
197
 
210
198
  ## Configuration
211
199
 
@@ -222,6 +210,7 @@ Proposals are authored by your agent during chat (or by external agents using `g
222
210
  |------|-------------|
223
211
  | `~/.gobi/credentials.json` | Stored authentication tokens |
224
212
  | `.gobi/settings.yaml` | Per-project vault and space configuration |
213
+ | `PUBLISH.md` | Vault profile document with YAML frontmatter, published via `gobi vault publish` |
225
214
 
226
215
  ## Development
227
216
 
@@ -1,20 +1,20 @@
1
1
  ---
2
2
  name: space-explore
3
- description: Explore what's happening in the active Gobi space — threads and learnings shared by others.
3
+ description: Explore what's happening in the active Gobi space — posts and learnings shared by others.
4
4
  argument-hint: "[topic or keyword]"
5
5
  ---
6
6
 
7
7
  Always use the globally installed `gobi` binary (not via npx or ts-node).
8
8
 
9
- Explore the active Gobi space to surface discussions, topics, and learnings from others:
9
+ Explore the active Gobi space to surface posts, topics, and learnings from others:
10
10
 
11
11
  1. Run these three commands in parallel:
12
- - `gobi --json space list-threads` — recent discussions in the space
13
- - `gobi --json space list-topics` — available topics across the platform
14
- - `gobi --json feed list` — learnings and brain updates shared by members across the platform
15
- 2. Display results in a readable summary, grouped by type (Topics / Discussions / Learnings).
16
- 3. If `$ARGUMENTS` is provided, filter or highlight entries relevant to that topic or keyword. If a matching topic is found, also run `gobi --json space list-topic-threads <topicSlug>` to show threads tagged with that topic.
12
+ - `gobi --json space list-posts` — recent posts in the space
13
+ - `gobi --json space list-topics` — available topics in the space
14
+ - `gobi --json global feed` — recent posts and replies shared by members across the platform
15
+ 2. Display results in a readable summary, grouped by type (Topics / Space posts / Global feed).
16
+ 3. If `$ARGUMENTS` is provided, filter or highlight entries relevant to that topic or keyword. If a matching topic is found, also run `gobi --json space list-topic-posts <topicSlug>` to show posts tagged with that topic.
17
17
  4. Ask the user if they'd like to read anything in full:
18
- - For a topic: run `gobi space list-topic-threads <topicSlug>` and show the threads.
19
- - For a thread: run `gobi space get-thread <threadId>` and show it with replies.
20
- - For a feed brain update: run `gobi feed get <updateId>` to show the update with its replies.
18
+ - For a topic: run `gobi space list-topic-posts <topicSlug>` and show the posts.
19
+ - For a space post: run `gobi space get-post <postId>` and show it with ancestors and replies.
20
+ - For a global feed post: run `gobi global get-post <postId>` to show it with ancestors and replies.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: space-share
3
- description: Summarize recent learnings from this session and draft a brain update to share to the active Gobi space.
3
+ description: Summarize recent learnings from this session and draft a vault post to share to the global feed.
4
4
  argument-hint: "[context]"
5
5
  ---
6
6
 
@@ -8,15 +8,15 @@ Always use the globally installed `gobi` binary (not via npx or ts-node).
8
8
 
9
9
  ## Pre-flight check
10
10
 
11
- First, verify the user is warped into a space:
11
+ First, verify the user is set up:
12
12
 
13
13
  ```bash
14
14
  gobi --json auth status
15
15
  ```
16
16
 
17
- Check that `.gobi/settings.yaml` exists and contains both `vaultSlug` and `selectedSpaceSlug`. If not warped, stop and ask the user to run `/gobi:warp` first.
17
+ Check that `.gobi/settings.yaml` exists and contains both `vaultSlug` and `selectedSpaceSlug`. If not, stop and ask the user to run `gobi init` and `gobi space warp` first.
18
18
 
19
- ## Draft a brain update
19
+ ## Draft a vault post
20
20
 
21
21
  If `$ARGUMENTS` is provided, treat it as additional context or emphasis to guide the draft (e.g. "Emphasize the auth fix" or "Focus on the API design decision").
22
22
 
@@ -34,12 +34,18 @@ Focus on:
34
34
 
35
35
  ## Present to the user
36
36
 
37
- Format the draft as a short brain update (2–5 bullet points max). Show it to the user and ask for confirmation before posting.
37
+ Format the draft as a short post (2–5 bullet points max). Show it to the user and ask for confirmation before posting.
38
38
 
39
- Once confirmed, post it:
39
+ Once confirmed, post it to the global feed:
40
40
 
41
41
  ```bash
42
- gobi brain post-update --title "<short title>" --content "<confirmed content>"
42
+ gobi global create-post --title "<short title>" --content "<confirmed content>"
43
+ ```
44
+
45
+ Or to the active space:
46
+
47
+ ```bash
48
+ gobi space create-post --title "<short title>" --content "<confirmed content>"
43
49
  ```
44
50
 
45
51
  Confirm success and show the user the result.
@@ -0,0 +1,213 @@
1
+ import { readFileSync } from "fs";
2
+ import { apiDelete, apiGet, apiPatch, apiPost } from "../client.js";
3
+ import { isJsonMode, jsonOut, unwrapResp } from "./utils.js";
4
+ function readContent(value) {
5
+ if (value === "-")
6
+ return readFileSync("/dev/stdin", "utf8");
7
+ return value;
8
+ }
9
+ function snippet(content, max = 80) {
10
+ const single = content.replace(/\s+/g, " ");
11
+ return single.length > max ? `${single.slice(0, max)}…` : single;
12
+ }
13
+ function parseActionFlags(values) {
14
+ if (!values)
15
+ return [];
16
+ return values
17
+ .map((v) => v.trim())
18
+ .filter(Boolean)
19
+ .slice(0, 3)
20
+ .map((label) => ({ label }));
21
+ }
22
+ function formatDraftLine(d) {
23
+ const status = d.status === "pending" ? "·" : "✓";
24
+ const actionCount = d.actions.length;
25
+ return `- [${status}] p${d.priority} rev${d.revision} ${d.draftId.slice(0, 8)} ${snippet(d.title)} (${actionCount} action${actionCount === 1 ? "" : "s"})`;
26
+ }
27
+ export function registerDraftCommand(program) {
28
+ // The draft command surface is designed for agent use: every subcommand
29
+ // accepts `--json` (set globally) and returns the same envelope shape, and
30
+ // the agent-authoring flow funnels through `add` + `revise` while user-
31
+ // facing decisions go through `action` and `revise` (with --comment).
32
+ const draft = program
33
+ .command("draft")
34
+ .description("Drafts authored by your agent during chat. Each carries up to 3 AI-suggested actions. Top-5 pending feed the system prompt; picking an action posts a synthesized message into the originating session.");
35
+ // ── List ──
36
+ draft
37
+ .command("list")
38
+ .description("List drafts (priority ASC, then newest first).")
39
+ .option("--limit <number>", "Max drafts to return (1-200)", "50")
40
+ .action(async (opts) => {
41
+ const params = { limit: parseInt(opts.limit, 10) };
42
+ const resp = (await apiGet("/app/drafts", params));
43
+ const items = (resp.data || []);
44
+ if (isJsonMode(draft)) {
45
+ jsonOut(items);
46
+ return;
47
+ }
48
+ if (!items.length) {
49
+ console.log("No drafts.");
50
+ return;
51
+ }
52
+ console.log(`Drafts (${items.length}):`);
53
+ for (const d of items)
54
+ console.log(formatDraftLine(d));
55
+ });
56
+ // ── Get ──
57
+ draft
58
+ .command("get <draftId>")
59
+ .description("Show one draft with its history and suggested actions.")
60
+ .action(async (draftId) => {
61
+ const resp = (await apiGet(`/app/drafts/${draftId}`));
62
+ const d = unwrapResp(resp);
63
+ if (isJsonMode(draft)) {
64
+ jsonOut(d);
65
+ return;
66
+ }
67
+ console.log(`Draft ${d.draftId}`);
68
+ console.log(` title: ${d.title}`);
69
+ console.log(` status: ${d.status}`);
70
+ console.log(` priority: ${d.priority}`);
71
+ console.log(` revision: ${d.revision}`);
72
+ console.log(` session: ${d.sessionId}`);
73
+ console.log(` created: ${d.createdAt}`);
74
+ console.log("");
75
+ console.log("Content:");
76
+ console.log(d.content);
77
+ if (d.actions.length) {
78
+ console.log("");
79
+ console.log("Suggested actions:");
80
+ d.actions.forEach((a, i) => console.log(` [${i}] ${a.label}`));
81
+ }
82
+ if (d.history.length) {
83
+ console.log("");
84
+ console.log("History:");
85
+ for (const h of d.history) {
86
+ console.log(` ${h.createdAt} rev${h.revision} ${h.type}`);
87
+ if (h.type === "created" || h.type === "revised") {
88
+ if (h.title !== undefined)
89
+ console.log(` title: ${h.title}`);
90
+ if (h.content !== undefined) {
91
+ console.log(` content: ${snippet(h.content, 200)}`);
92
+ }
93
+ if (h.actions !== undefined && h.actions.length) {
94
+ console.log(` actions: ${h.actions.map((a) => a.label).join(" | ")}`);
95
+ }
96
+ if (h.comment !== undefined)
97
+ console.log(` comment: ${h.comment}`);
98
+ }
99
+ else if (h.type === "prioritized" && h.priority !== undefined) {
100
+ console.log(` priority=${h.priority}`);
101
+ }
102
+ else if (h.type === "actioned") {
103
+ console.log(` action[${h.actionIndex}]=${h.actionLabel}`);
104
+ }
105
+ }
106
+ }
107
+ });
108
+ // ── Add ──
109
+ draft
110
+ .command("add <title> <content>")
111
+ .description("Add a draft. Pass '-' for content to read from stdin. Pass --action up to 3 times to attach AI-suggested actions. Requires a chat session — the agent runtime exports GOBI_SESSION_ID automatically; outside that, pass --session.")
112
+ .option("--session <sessionId>", "Originating chat session UUID. Falls back to $GOBI_SESSION_ID when set.")
113
+ .option("--priority <number>", "Priority (lower = higher), default 100")
114
+ .option("--action <label>", "Suggested action label (repeatable, max 3). Each label is what the user sees on the button.", (value, prev = []) => [...prev, value], [])
115
+ .action(async (title, content, opts) => {
116
+ const sessionId = opts.session || process.env.GOBI_SESSION_ID || "";
117
+ if (!sessionId) {
118
+ console.error("Error: missing session id. Pass --session <uuid> or set GOBI_SESSION_ID in the environment.");
119
+ process.exit(1);
120
+ }
121
+ const body = {
122
+ title,
123
+ content: readContent(content),
124
+ sessionId,
125
+ };
126
+ if (opts.priority)
127
+ body.priority = parseInt(opts.priority, 10);
128
+ const actions = parseActionFlags(opts.action);
129
+ if (actions.length)
130
+ body.actions = actions;
131
+ const resp = (await apiPost("/app/drafts", body));
132
+ const d = unwrapResp(resp);
133
+ if (isJsonMode(draft)) {
134
+ jsonOut(d);
135
+ return;
136
+ }
137
+ console.log(`Created ${d.draftId} (priority ${d.priority}, ${d.actions.length} action${d.actions.length === 1 ? "" : "s"}).`);
138
+ });
139
+ // ── Delete ──
140
+ draft
141
+ .command("delete <draftId>")
142
+ .description("Delete a draft.")
143
+ .action(async (draftId) => {
144
+ await apiDelete(`/app/drafts/${draftId}`);
145
+ if (isJsonMode(draft)) {
146
+ jsonOut({ deleted: draftId });
147
+ return;
148
+ }
149
+ console.log(`Deleted ${draftId}.`);
150
+ });
151
+ // ── Prioritize ──
152
+ draft
153
+ .command("prioritize <draftId> <priority>")
154
+ .description("Set priority (lower = higher). Top 5 feed the system prompt.")
155
+ .action(async (draftId, priority) => {
156
+ const resp = (await apiPatch(`/app/drafts/${draftId}/priority`, {
157
+ priority: parseInt(priority, 10),
158
+ }));
159
+ const d = unwrapResp(resp);
160
+ if (isJsonMode(draft)) {
161
+ jsonOut(d);
162
+ return;
163
+ }
164
+ console.log(`Set ${d.draftId} priority to ${d.priority}.`);
165
+ });
166
+ // ── Action ──
167
+ draft
168
+ .command("action <draftId> <actionIndex>")
169
+ .description("Take one of the draft's suggested actions by 0-based index. Marks the draft 'actioned' and the client posts the synthesized message into the originating session.")
170
+ .action(async (draftId, actionIndex) => {
171
+ const idx = parseInt(actionIndex, 10);
172
+ if (Number.isNaN(idx) || idx < 0 || idx > 2) {
173
+ console.error("Error: actionIndex must be 0, 1, or 2.");
174
+ process.exit(1);
175
+ }
176
+ const resp = (await apiPost(`/app/drafts/${draftId}/action`, {
177
+ actionIndex: idx,
178
+ }));
179
+ const d = unwrapResp(resp);
180
+ if (isJsonMode(draft)) {
181
+ jsonOut(d);
182
+ return;
183
+ }
184
+ const label = d.actions[idx]?.label ?? `action ${idx}`;
185
+ console.log(`Took action "${label}" on ${d.draftId}.`);
186
+ });
187
+ // ── Revise ──
188
+ draft
189
+ .command("revise <draftId> <comment>")
190
+ .description("Bump the draft to a new revision. Comment is required. Pass --title, --content, and/or --action to update the draft in the same call (--action repeatable, max 3, replaces all). Pass '-' for any of comment/title/content to read from stdin.")
191
+ .option("--title <title>", "Replacement title")
192
+ .option("--content <content>", "Replacement content; pass '-' to read from stdin")
193
+ .option("--action <label>", "Replacement suggested action label (repeatable, max 3). When passed, replaces the entire actions array.", (value, prev = []) => [...prev, value], [])
194
+ .action(async (draftId, comment, opts) => {
195
+ const body = {
196
+ comment: readContent(comment),
197
+ };
198
+ if (opts.title !== undefined)
199
+ body.title = opts.title;
200
+ if (opts.content !== undefined)
201
+ body.content = readContent(opts.content);
202
+ if (opts.action && opts.action.length > 0) {
203
+ body.actions = parseActionFlags(opts.action);
204
+ }
205
+ const resp = (await apiPost(`/app/drafts/${draftId}/revise`, body));
206
+ const d = unwrapResp(resp);
207
+ if (isJsonMode(draft)) {
208
+ jsonOut(d);
209
+ return;
210
+ }
211
+ console.log(`Revised ${d.draftId} → rev${d.revision}.`);
212
+ });
213
+ }