@gobi-ai/cli 1.3.1 → 1.3.3

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": "1.3.1",
7
+ "version": "1.3.3",
8
8
  "plugins": [
9
9
  {
10
10
  "name": "gobi",
11
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.1",
12
+ "version": "1.3.3",
13
13
  "author": {
14
14
  "name": "gobi-ai"
15
15
  },
@@ -20,6 +20,7 @@
20
20
  "./skills/gobi-space",
21
21
  "./skills/gobi-brain",
22
22
  "./skills/gobi-feed",
23
+ "./skills/gobi-notes",
23
24
  "./skills/gobi-media",
24
25
  "./skills/gobi-sense",
25
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.1",
4
+ "version": "1.3.3",
5
5
  "author": {
6
6
  "name": "gobi-ai"
7
7
  },
@@ -11,6 +11,7 @@
11
11
  "./skills/gobi-space",
12
12
  "./skills/gobi-brain",
13
13
  "./skills/gobi-feed",
14
+ "./skills/gobi-notes",
14
15
  "./skills/gobi-media",
15
16
  "./skills/gobi-sense",
16
17
  "./skills/gobi-homepage"
package/README.md CHANGED
@@ -137,6 +137,19 @@ Public brains are accessible at `https://gobispace.com/@{vaultSlug}`.
137
137
 
138
138
  Times are ISO 8601 UTC (e.g. `2026-03-20T00:00:00Z`).
139
139
 
140
+ ### Notes
141
+
142
+ | Command | Description |
143
+ |---------|-------------|
144
+ | `gobi notes list [--date YYYY-MM-DD]` | List your notes (recent via cursor, or all for a day) |
145
+ | `gobi notes get <id>` | Get a single note |
146
+ | `gobi notes create --content <c>` | Create a note (use `-` to read content from stdin) |
147
+ | `gobi notes edit <id> [--content <c>] [--agent-id <id>]` | Edit a note (at least one required; `--agent-id null` clears the link) |
148
+ | `gobi notes delete <id>` | Delete a note you authored |
149
+
150
+ `notes list` and `notes create` accept `--timezone <iana>` (default: system timezone) for day boundaries.
151
+ `notes list` also accepts `--limit`/`--cursor` for pagination.
152
+
140
153
  ### Sync
141
154
 
142
155
  | Command | Description |
@@ -0,0 +1,153 @@
1
+ import { readFileSync } from "fs";
2
+ import { apiGet, apiPost, apiPatch, apiDelete } from "../client.js";
3
+ import { isJsonMode, jsonOut, unwrapResp } from "./utils.js";
4
+ function defaultTimezone() {
5
+ return Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC";
6
+ }
7
+ function formatNoteLine(note) {
8
+ const content = note.content ?? "";
9
+ const snippet = content
10
+ ? content.length > 80
11
+ ? content.slice(0, 80) + "…"
12
+ : content
13
+ : "(no content)";
14
+ const attachments = (note.attachments || []);
15
+ const attachStr = attachments.length
16
+ ? `, ${attachments.length} ${attachments.length === 1 ? "attachment" : "attachments"}`
17
+ : "";
18
+ const agent = note.agentId != null ? `, agent: ${note.agentId}` : "";
19
+ return `- [${note.id}] "${snippet.replace(/\n/g, " ")}" (${note.eventDate}${agent}${attachStr}, updated ${note.updatedAt})`;
20
+ }
21
+ export function registerNotesCommand(program) {
22
+ const notes = program
23
+ .command("notes")
24
+ .description("Personal notes (create, list, get, edit, delete).");
25
+ // ── List ──
26
+ notes
27
+ .command("list")
28
+ .description("List your notes. Without --date, returns recent notes via cursor pagination. With --date, returns all notes for that day.")
29
+ .option("--date <date>", "Filter to a single day (YYYY-MM-DD)")
30
+ .option("--timezone <tz>", "IANA timezone name (default: system timezone)")
31
+ .option("--limit <number>", "Items per page (1-100)", "50")
32
+ .option("--cursor <string>", "Pagination cursor from previous response")
33
+ .action(async (opts) => {
34
+ const params = {
35
+ timezone: opts.timezone || defaultTimezone(),
36
+ limit: parseInt(opts.limit, 10),
37
+ };
38
+ if (opts.date)
39
+ params.date = opts.date;
40
+ if (opts.cursor)
41
+ params.cursor = opts.cursor;
42
+ const resp = (await apiGet(`/app/notes`, params));
43
+ const items = (resp.data || []);
44
+ const pagination = (resp.pagination || {});
45
+ if (isJsonMode(notes)) {
46
+ jsonOut({ items, pagination });
47
+ return;
48
+ }
49
+ if (!items.length) {
50
+ console.log("No notes found.");
51
+ return;
52
+ }
53
+ const lines = items.map(formatNoteLine);
54
+ const footer = pagination.hasMore
55
+ ? `\n Next cursor: ${pagination.nextCursor}`
56
+ : "";
57
+ console.log(`Notes (${items.length} items):\n` + lines.join("\n") + footer);
58
+ });
59
+ // ── Get ──
60
+ notes
61
+ .command("get <noteId>")
62
+ .description("Get a single note by id.")
63
+ .action(async (noteId) => {
64
+ const resp = (await apiGet(`/app/notes/${noteId}`));
65
+ const note = unwrapResp(resp);
66
+ if (isJsonMode(notes)) {
67
+ jsonOut(note);
68
+ return;
69
+ }
70
+ const attachments = (note.attachments || []);
71
+ const attachLines = attachments.map((a) => ` - [${a.id}] ${a.mediaUrl} (position ${a.position})`);
72
+ const agentLine = note.agentId != null ? `Agent: ${note.agentId}\n` : "";
73
+ const output = [
74
+ `Note: ${note.id}`,
75
+ `Date: ${note.eventDate}`,
76
+ `Created: ${note.createdAt}`,
77
+ `Updated: ${note.updatedAt}`,
78
+ agentLine.trimEnd(),
79
+ "",
80
+ note.content || "(no content)",
81
+ ...(attachLines.length ? ["", `Attachments (${attachLines.length}):`, ...attachLines] : []),
82
+ ]
83
+ .filter((line) => line !== "")
84
+ .concat([""])
85
+ .join("\n")
86
+ .trimEnd();
87
+ console.log(output);
88
+ });
89
+ // ── Create ──
90
+ notes
91
+ .command("create")
92
+ .description("Create a note. Provide --content (use '-' for stdin) and/or attachments.")
93
+ .option("--content <content>", 'Note content (markdown supported, use "-" for stdin)')
94
+ .option("--timezone <tz>", "IANA timezone name (default: system timezone)")
95
+ .option("--agent-id <number>", "Optional agent id to associate with the note")
96
+ .action(async (opts) => {
97
+ if (!opts.content) {
98
+ throw new Error("--content is required (use '-' to read from stdin)");
99
+ }
100
+ const content = opts.content === "-" ? readFileSync("/dev/stdin", "utf8") : opts.content;
101
+ const body = {
102
+ content,
103
+ timezone: opts.timezone || defaultTimezone(),
104
+ };
105
+ if (opts.agentId)
106
+ body.agentId = parseInt(opts.agentId, 10);
107
+ const resp = (await apiPost(`/app/notes`, body));
108
+ const note = unwrapResp(resp);
109
+ if (isJsonMode(notes)) {
110
+ jsonOut(note);
111
+ return;
112
+ }
113
+ console.log(`Note created!\n ID: ${note.id}\n Date: ${note.eventDate}\n Created: ${note.createdAt}`);
114
+ });
115
+ // ── Edit ──
116
+ notes
117
+ .command("edit <noteId>")
118
+ .description("Edit a note. Provide --content and/or --agent-id.")
119
+ .option("--content <content>", 'New note content (markdown supported, use "-" for stdin)')
120
+ .option("--agent-id <number>", 'New agent id, or "null" to clear the association')
121
+ .action(async (noteId, opts) => {
122
+ if (opts.content == null && opts.agentId == null) {
123
+ throw new Error("Provide at least --content or --agent-id to update.");
124
+ }
125
+ const body = {};
126
+ if (opts.content != null) {
127
+ body.content =
128
+ opts.content === "-" ? readFileSync("/dev/stdin", "utf8") : opts.content;
129
+ }
130
+ if (opts.agentId != null) {
131
+ body.agentId = opts.agentId === "null" ? null : parseInt(opts.agentId, 10);
132
+ }
133
+ const resp = (await apiPatch(`/app/notes/${noteId}`, body));
134
+ const note = unwrapResp(resp);
135
+ if (isJsonMode(notes)) {
136
+ jsonOut(note);
137
+ return;
138
+ }
139
+ console.log(`Note edited!\n ID: ${note.id}\n Updated: ${note.updatedAt}`);
140
+ });
141
+ // ── Delete ──
142
+ notes
143
+ .command("delete <noteId>")
144
+ .description("Delete a note you authored.")
145
+ .action(async (noteId) => {
146
+ await apiDelete(`/app/notes/${noteId}`);
147
+ if (isJsonMode(notes)) {
148
+ jsonOut({ id: noteId });
149
+ return;
150
+ }
151
+ console.log(`Note ${noteId} deleted.`);
152
+ });
153
+ }
@@ -0,0 +1,158 @@
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 formatProposalLine(p) {
14
+ const status = p.status === "pending" ? "·" : p.status === "accepted" ? "✓" : "✗";
15
+ return `- [${status}] p${p.priority} rev${p.revision} ${p.proposalId.slice(0, 8)} ${snippet(p.content)}`;
16
+ }
17
+ export function registerProposalCommand(program) {
18
+ const proposal = program
19
+ .command("proposal")
20
+ .description("Proposals authored by your agent during chat. Top-5 feed the system prompt; accept/reject/revise post into the originating chat session.");
21
+ // ── List ──
22
+ proposal
23
+ .command("list")
24
+ .description("List proposals (priority ASC, then newest first).")
25
+ .option("--limit <number>", "Max proposals to return (1-200)", "50")
26
+ .action(async (opts) => {
27
+ const params = { limit: parseInt(opts.limit, 10) };
28
+ const resp = (await apiGet("/app/proposals", params));
29
+ const items = (resp.data || []);
30
+ if (isJsonMode(proposal)) {
31
+ jsonOut(items);
32
+ return;
33
+ }
34
+ if (!items.length) {
35
+ console.log("No proposals.");
36
+ return;
37
+ }
38
+ console.log(`Proposals (${items.length}):`);
39
+ for (const p of items)
40
+ console.log(formatProposalLine(p));
41
+ });
42
+ // ── Get ──
43
+ proposal
44
+ .command("get <proposalId>")
45
+ .description("Show one proposal with its history.")
46
+ .action(async (proposalId) => {
47
+ const resp = (await apiGet(`/app/proposals/${proposalId}`));
48
+ const p = unwrapResp(resp);
49
+ if (isJsonMode(proposal)) {
50
+ jsonOut(p);
51
+ return;
52
+ }
53
+ console.log(`Proposal ${p.proposalId}`);
54
+ console.log(` status: ${p.status}`);
55
+ console.log(` priority: ${p.priority}`);
56
+ console.log(` revision: ${p.revision}`);
57
+ console.log(` session: ${p.sessionId ?? "(none)"}`);
58
+ console.log(` created: ${p.createdAt}`);
59
+ console.log("");
60
+ console.log("Content:");
61
+ console.log(p.content);
62
+ if (p.history.length) {
63
+ console.log("");
64
+ console.log("History:");
65
+ for (const h of p.history) {
66
+ const detail = h.type === "revise_requested"
67
+ ? `: ${h.comment}`
68
+ : h.type === "prioritized"
69
+ ? `: priority=${h.priority}`
70
+ : "";
71
+ console.log(` ${h.createdAt} rev${h.revision} ${h.type}${detail}`);
72
+ }
73
+ }
74
+ });
75
+ // ── Edit content ──
76
+ proposal
77
+ .command("edit <proposalId> <content>")
78
+ .description("Replace proposal content (bumps revision). Pass '-' for stdin.")
79
+ .action(async (proposalId, content) => {
80
+ const resp = (await apiPatch(`/app/proposals/${proposalId}`, {
81
+ content: readContent(content),
82
+ }));
83
+ const p = unwrapResp(resp);
84
+ if (isJsonMode(proposal)) {
85
+ jsonOut(p);
86
+ return;
87
+ }
88
+ console.log(`Updated ${p.proposalId} → rev${p.revision}.`);
89
+ });
90
+ // ── Delete ──
91
+ proposal
92
+ .command("delete <proposalId>")
93
+ .description("Delete a proposal.")
94
+ .action(async (proposalId) => {
95
+ await apiDelete(`/app/proposals/${proposalId}`);
96
+ if (isJsonMode(proposal)) {
97
+ jsonOut({ deleted: proposalId });
98
+ return;
99
+ }
100
+ console.log(`Deleted ${proposalId}.`);
101
+ });
102
+ // ── Prioritize ──
103
+ proposal
104
+ .command("prioritize <proposalId> <priority>")
105
+ .description("Set priority (lower = higher). Top 5 feed the system prompt.")
106
+ .action(async (proposalId, priority) => {
107
+ const resp = (await apiPatch(`/app/proposals/${proposalId}/priority`, {
108
+ priority: parseInt(priority, 10),
109
+ }));
110
+ const p = unwrapResp(resp);
111
+ if (isJsonMode(proposal)) {
112
+ jsonOut(p);
113
+ return;
114
+ }
115
+ console.log(`Set ${p.proposalId} priority to ${p.priority}.`);
116
+ });
117
+ // ── Accept ──
118
+ proposal
119
+ .command("accept <proposalId>")
120
+ .description('Accept — posts "Accept your proposal X" into the originating chat session.')
121
+ .action(async (proposalId) => {
122
+ const resp = (await apiPost(`/app/proposals/${proposalId}/accept`));
123
+ const p = unwrapResp(resp);
124
+ if (isJsonMode(proposal)) {
125
+ jsonOut(p);
126
+ return;
127
+ }
128
+ console.log(`Accepted ${p.proposalId}.`);
129
+ });
130
+ // ── Reject ──
131
+ proposal
132
+ .command("reject <proposalId>")
133
+ .description('Reject — posts "Reject your proposal X" into the originating chat session.')
134
+ .action(async (proposalId) => {
135
+ const resp = (await apiPost(`/app/proposals/${proposalId}/reject`));
136
+ const p = unwrapResp(resp);
137
+ if (isJsonMode(proposal)) {
138
+ jsonOut(p);
139
+ return;
140
+ }
141
+ console.log(`Rejected ${p.proposalId}.`);
142
+ });
143
+ // ── Revise ──
144
+ proposal
145
+ .command("revise <proposalId> <comment>")
146
+ .description('Ask the agent to revise — posts "Update your proposal X. Here\'s my comment. {comment}" into the chat session.')
147
+ .action(async (proposalId, comment) => {
148
+ const resp = (await apiPost(`/app/proposals/${proposalId}/revise`, {
149
+ comment: readContent(comment),
150
+ }));
151
+ const p = unwrapResp(resp);
152
+ if (isJsonMode(proposal)) {
153
+ jsonOut(p);
154
+ return;
155
+ }
156
+ console.log(`Revision requested on ${p.proposalId}.`);
157
+ });
158
+ }
package/dist/main.js CHANGED
@@ -7,11 +7,13 @@ import { registerInitCommand, printContext } from "./commands/init.js";
7
7
  import { registerSpaceCommand } from "./commands/space.js";
8
8
  import { registerBrainCommand } from "./commands/brain.js";
9
9
  import { registerFeedCommand } from "./commands/feed.js";
10
+ import { registerNotesCommand } from "./commands/notes.js";
10
11
  import { registerSessionsCommand } from "./commands/sessions.js";
11
12
  import { registerSenseCommand } from "./commands/sense.js";
12
13
  import { registerSyncCommand } from "./commands/sync.js";
13
14
  import { registerUpdateCommand } from "./commands/update.js";
14
15
  import { registerMediaCommand } from "./commands/media.js";
16
+ import { registerProposalCommand } from "./commands/proposal.js";
15
17
  const require = createRequire(import.meta.url);
16
18
  const { version } = require("../package.json");
17
19
  const SKIP_BANNER_COMMANDS = new Set(["auth", "init", "update"]);
@@ -35,11 +37,13 @@ export async function cli() {
35
37
  registerSpaceCommand(program);
36
38
  registerBrainCommand(program);
37
39
  registerFeedCommand(program);
40
+ registerNotesCommand(program);
38
41
  registerSessionsCommand(program);
39
42
  registerSenseCommand(program);
40
43
  registerSyncCommand(program);
41
44
  registerUpdateCommand(program);
42
45
  registerMediaCommand(program);
46
+ registerProposalCommand(program);
43
47
  // Propagate helpWidth to all subcommands
44
48
  const helpWidth = process.stdout.columns || 200;
45
49
  for (const cmd of program.commands) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gobi-ai/cli",
3
- "version": "1.3.1",
3
+ "version": "1.3.3",
4
4
  "description": "CLI client for the Gobi collaborative knowledge platform",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -0,0 +1,52 @@
1
+ ---
2
+ name: gobi-notes
3
+ description: >-
4
+ Gobi notes commands for personal note-taking: create, list (by day or
5
+ cursor), get, edit, and delete notes. Notes are private, dated entries
6
+ optionally tied to an agent. Use when the user wants to capture, review,
7
+ or modify their own notes.
8
+ allowed-tools: Bash(gobi:*)
9
+ metadata:
10
+ author: gobi-ai
11
+ version: "1.3.2"
12
+ ---
13
+
14
+ # gobi-notes
15
+
16
+ Gobi notes commands for personal note-taking (v1.3.2).
17
+
18
+ Requires gobi-cli installed and authenticated. See gobi-core skill for setup.
19
+
20
+ ## What is a note?
21
+
22
+ A note is a private, dated entry you keep for yourself. Each note has:
23
+ - **content** — markdown text (optional if attachments are present, but the CLI requires `--content` since it doesn't yet upload attachments)
24
+ - **eventDate** — derived from your timezone at create time, used to group notes by day
25
+ - **agentId** — optional association with a Gobi agent
26
+
27
+ Notes are user-private — only you can see, edit, or delete your own notes.
28
+
29
+ ## Timezone
30
+
31
+ `gobi notes list` and `gobi notes create` need a timezone to compute the calendar day. The CLI auto-detects your system timezone via `Intl.DateTimeFormat().resolvedOptions().timeZone`. Override with `--timezone <iana-name>` (e.g. `America/Los_Angeles`).
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 notes list --date 2026-04-27
39
+ ```
40
+
41
+ ## Available Commands
42
+
43
+ - `gobi notes` — Personal notes (create, list, get, edit, delete).
44
+ - `gobi notes list` — List your notes. Without --date, returns recent notes via cursor pagination. With --date, returns all notes for that day.
45
+ - `gobi notes get` — Get a single note by id.
46
+ - `gobi notes create` — Create a note. Provide --content (use '-' for stdin) and/or attachments.
47
+ - `gobi notes edit` — Edit a note. Provide --content and/or --agent-id.
48
+ - `gobi notes delete` — Delete a note you authored.
49
+
50
+ ## Reference Documentation
51
+
52
+ - [gobi notes](references/notes.md)
@@ -0,0 +1,82 @@
1
+ # gobi notes
2
+
3
+ ```
4
+ Usage: gobi notes [options] [command]
5
+
6
+ Personal notes (create, list, get, edit, delete).
7
+
8
+ Options:
9
+ -h, --help display help for command
10
+
11
+ Commands:
12
+ list [options] List your notes. Without --date, returns recent notes via cursor pagination. With --date, returns all notes for that day.
13
+ get <noteId> Get a single note by id.
14
+ create [options] Create a note. Provide --content (use '-' for stdin) and/or attachments.
15
+ edit [options] <noteId> Edit a note. Provide --content and/or --agent-id.
16
+ delete <noteId> Delete a note you authored.
17
+ help [command] display help for command
18
+ ```
19
+
20
+ ## list
21
+
22
+ ```
23
+ Usage: gobi notes list [options]
24
+
25
+ List your notes. Without --date, returns recent notes via cursor pagination. With --date, returns all notes for that day.
26
+
27
+ Options:
28
+ --date <date> Filter to a single day (YYYY-MM-DD)
29
+ --timezone <tz> IANA timezone name (default: system timezone)
30
+ --limit <number> Items per page (1-100) (default: "50")
31
+ --cursor <string> Pagination cursor from previous response
32
+ -h, --help display help for command
33
+ ```
34
+
35
+ ## get
36
+
37
+ ```
38
+ Usage: gobi notes get [options] <noteId>
39
+
40
+ Get a single note by id.
41
+
42
+ Options:
43
+ -h, --help display help for command
44
+ ```
45
+
46
+ ## create
47
+
48
+ ```
49
+ Usage: gobi notes create [options]
50
+
51
+ Create a note. Provide --content (use '-' for stdin) and/or attachments.
52
+
53
+ Options:
54
+ --content <content> Note content (markdown supported, use "-" for stdin)
55
+ --timezone <tz> IANA timezone name (default: system timezone)
56
+ --agent-id <number> Optional agent id to associate with the note
57
+ -h, --help display help for command
58
+ ```
59
+
60
+ ## edit
61
+
62
+ ```
63
+ Usage: gobi notes edit [options] <noteId>
64
+
65
+ Edit a note. Provide --content and/or --agent-id.
66
+
67
+ Options:
68
+ --content <content> New note content (markdown supported, use "-" for stdin)
69
+ --agent-id <number> New agent id, or "null" to clear the association
70
+ -h, --help display help for command
71
+ ```
72
+
73
+ ## delete
74
+
75
+ ```
76
+ Usage: gobi notes delete [options] <noteId>
77
+
78
+ Delete a note you authored.
79
+
80
+ Options:
81
+ -h, --help display help for command
82
+ ```