@gobi-ai/cli 2.0.36 → 2.0.39

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.36",
7
+ "version": "2.0.39",
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.36",
12
+ "version": "2.0.39",
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.36",
4
+ "version": "2.0.39",
5
5
  "author": {
6
6
  "name": "gobi-ai"
7
7
  },
package/README.md CHANGED
@@ -218,16 +218,25 @@ Private posts and replies visible only to you. Same `Post` data model and subcom
218
218
  | `gobi personal edit-reply <replyId> [--content <c>] [--rich-text <json>]` | Edit a reply you authored |
219
219
  | `gobi personal delete-reply <replyId>` | Delete a reply you authored |
220
220
 
221
- ### Sense
221
+ ### Sense (activities & conversations)
222
222
 
223
- Activity and transcription data captured by Gobi Sense (or the mobile app).
223
+ Activity and conversation data captured by Gobi Sense (the wearable) and the mobile app, then ingested by the cloud pipeline. Read-only. See the `gobi-sense` skill for full workflows.
224
+
225
+ Like posts and artifacts, Sense data is **scoped to a space**: the subcommands live under `gobi personal …` (your personal space) and `gobi space …` (the active team space — `gobi space warp <slug>` or `gobi space --space-slug <slug> …`). `<scope>` below is `personal` or `space`.
226
+
227
+ - **activities** — what you were doing (category + details, start/end times). In a team space, every member's activities show up, attributed to their recorder.
228
+ - **conversations** — phone-mic Audio Logs plus Sense-detected conversations, each with a transcript and auto-generated summary. In a team space, every member's conversations show up, attributed to their recorder (transcript/audio stay owner-only). (This replaces the old `list-transcriptions` — transcriptions were unified into conversations.)
224
229
 
225
230
  | Command | Description |
226
231
  |---------|-------------|
227
- | `gobi sense list-activities --start-time <iso> --end-time <iso>` | List activity records in a time range |
228
- | `gobi sense list-transcriptions --start-time <iso> --end-time <iso>` | List transcription records in a time range |
232
+ | `gobi <scope> activities list [--limit N] [--before <cursor>] [--mine]` | List Sense activities in this scope (newest first) |
233
+ | `gobi <scope> activities get <activityId>` | Get one activity's details |
234
+ | `gobi <scope> activities transcript <activityId>` | Get an activity's transcript (owner-only) |
235
+ | `gobi <scope> conversations list [--limit N] [--before <cursor>] [--mine]` | List conversations captured in this scope (newest first) |
236
+ | `gobi <scope> conversations transcript <conversationId>` | Get a conversation's transcript and summary |
237
+ | `gobi <scope> conversations audio <conversationId>` | Get a signed URL for the recording (owner-only) |
229
238
 
230
- Times are ISO 8601 UTC (e.g. `2026-03-20T00:00:00Z`).
239
+ `gobi space …` lists are a complete, fully-paginated per-space history (every member's records); add `--mine` to restrict either `list` to records you recorded. `gobi personal conversations list` is filtered from the user-global conversations feed, so it shows your recent personal conversations rather than a fully paginated history (`gobi personal activities list` is fully paginated).
231
240
 
232
241
  ### Artifacts
233
242
 
@@ -307,7 +316,7 @@ The CLI ships a `.claude-plugin/` manifest with skills that wrap the command gro
307
316
  | `gobi-space` | `gobi space …`, `gobi global …`, and `gobi personal …` |
308
317
  | `gobi-artifact` | `gobi personal artifact …` and `gobi space artifact …` |
309
318
  | `gobi-media` | `gobi media …` |
310
- | `gobi-sense` | `gobi sense list-activities/list-transcriptions` |
319
+ | `gobi-sense` | `gobi personal activities/conversations …` and `gobi space activities/conversations …` |
311
320
  | `gobi-homepage` | Building custom HTML homepages with `window.gobi` |
312
321
 
313
322
  Each skill's `SKILL.md` is hand-written orientation; `references/` is regenerated from `--help` output by `npm run generate-skill-docs`.
@@ -2,6 +2,7 @@ import { apiGet, apiPost, apiPatch, apiPut, apiDelete } from "../client.js";
2
2
  import { buildMentionMap, formatAttachmentLines, formatAttachmentSummary, formatPostLabel, formatReactionChips, formatReplyLine, isJsonMode, jsonOut, postBodyText, readStdin, unwrapResp, } from "./utils.js";
3
3
  import { uploadPostAttachments, assertPostAttachmentMix, } from "../attachments.js";
4
4
  import { registerArtifactSubcommands } from "./artifact.js";
5
+ import { registerActivitiesSubcommands, registerConversationsSubcommands, } from "./sense.js";
5
6
  function readContent(value) {
6
7
  if (value === "-")
7
8
  return readStdin();
@@ -505,4 +506,27 @@ export function registerPersonalCommand(program) {
505
506
  registerArtifactSubcommands(personal, { resolve: () => ({}) }, "Versioned creations attached to posts, scoped to your personal space (visible " +
506
507
  "only to you). Kinds: image | video | gif | markdown | meeting_summary. Always " +
507
508
  "human-owned; revisions form a draft/published tree (one published per artifact).");
509
+ // ── Sense: activities + conversations (scoped to your personal space) ──
510
+ const senseScope = {
511
+ label: "personal",
512
+ listActivities: async (params) => {
513
+ const resp = (await apiGet("/app/activities", params));
514
+ return {
515
+ items: (resp.activities || []),
516
+ pagination: resp.pagination,
517
+ };
518
+ },
519
+ // `/app/conversations` is user-global (all scopes, newest ~50, no paging);
520
+ // filter to the personal scope (spaceId 0). Params are ignored — the endpoint
521
+ // takes none.
522
+ listConversations: async () => {
523
+ const resp = (await apiGet("/app/conversations"));
524
+ const all = (resp.conversations || []);
525
+ return { items: all.filter((c) => Number(c.spaceId ?? 0) === 0) };
526
+ },
527
+ };
528
+ registerActivitiesSubcommands(personal, senseScope, "Your personal Sense activities (what you were doing, from the wearable/app), " +
529
+ "browse-only. Recorded in your personal space (visible only to you).");
530
+ registerConversationsSubcommands(personal, senseScope, "Your personal Sense conversations (phone-mic Audio Logs + detected conversations), " +
531
+ "browse-only. Recorded in your personal space (visible only to you).");
508
532
  }
@@ -1,129 +1,180 @@
1
1
  import { apiGet } from "../client.js";
2
2
  import { isJsonMode, jsonOut } from "./utils.js";
3
- export function registerSenseCommand(program) {
4
- const sense = program
5
- .command("sense")
6
- .description("Sense commands (activities, transcriptions).");
7
- // ── Activities ──
8
- sense
9
- .command("list-activities")
10
- .description("List activity records within a time range.")
11
- .requiredOption("--start-time <iso>", "Start of time range (ISO 8601 UTC, e.g. 2026-03-20T00:00:00Z)")
12
- .requiredOption("--end-time <iso>", "End of time range (ISO 8601 UTC, e.g. 2026-03-20T23:59:59Z)")
3
+ // One-liner for a transcript turn. The backend resolves speaker identities at
4
+ // read time into `speakerLabel` (a managed-voice name, "Me", a session-stable
5
+ // "Speaker N", or "Unknown"); fall back to the raw diarization token only if
6
+ // it's somehow absent.
7
+ function formatTurnLine(t) {
8
+ const who = t.speakerLabel || t.speaker || "Unknown";
9
+ return `- ${who} (${t.timestamp}): ${t.text}`;
10
+ }
11
+ function printTranscriptTurns(turns) {
12
+ const cleaned = (turns || [])
13
+ .map((t) => ({ ...t, text: String(t.text ?? "").trim() }))
14
+ .filter((t) => t.text);
15
+ if (!cleaned.length) {
16
+ console.log("No transcript available.");
17
+ return;
18
+ }
19
+ console.log(`Transcript (${cleaned.length} turns):\n` + cleaned.map(formatTurnLine).join("\n"));
20
+ }
21
+ function formatActivityLine(a) {
22
+ const end = a.end_time ? ` → ${a.end_time}` : " → ongoing";
23
+ const details = a.details ? `: ${a.details}` : "";
24
+ // Space activities carry the recorder(s); personal ones don't.
25
+ const recorders = Array.isArray(a.recorders) && a.recorders.length
26
+ ? ` by ${a.recorders
27
+ .map((r) => r.name || `user ${r.id}`)
28
+ .join(", ")}`
29
+ : "";
30
+ return `- [${a.id}] ${a.category ?? "activity"}${details} (${a.start_time}${end})${recorders}`;
31
+ }
32
+ function formatConversationLine(c) {
33
+ const durMs = typeof c.durationMs === "number" ? c.durationMs : null;
34
+ const dur = durMs != null ? ` (${Math.max(1, Math.round(durMs / 60000))}m)` : "";
35
+ const cat = c.category ? ` — ${c.category}` : "";
36
+ const status = c.status ? ` [${c.status}]` : "";
37
+ // Space conversations carry the recorder (whose conversation it is); personal
38
+ // ones don't.
39
+ const rec = c.recorder && typeof c.recorder === "object"
40
+ ? ` by ${c.recorder.name ?? "someone"}`
41
+ : "";
42
+ return `- [${c.id}] ${c.source ?? "conversation"}${status}${cat} (${c.startTime}${dur})${rec}`;
43
+ }
44
+ function paginationFooter(pagination) {
45
+ return pagination?.hasMore ? `\n Next cursor: ${pagination.nextCursor}` : "";
46
+ }
47
+ // ── Activities ──
48
+ //
49
+ // Registers the `activities` subcommand tree under `parent` (a `gobi space` or
50
+ // `gobi personal` group). Replaces the old top-level `gobi sense list-activities`.
51
+ export function registerActivitiesSubcommands(parent, scope, description) {
52
+ const activities = parent.command("activities").description(description);
53
+ // ── List ──
54
+ activities
55
+ .command("list")
56
+ .description("List Sense activities in this scope (newest first).")
57
+ .option("--limit <n>", "Max items to return (default 30, max 100)")
58
+ .option("--before <cursor>", "Pagination cursor from a previous response (nextCursor)")
59
+ .option("--mine", "Only activities you recorded (space scope; no-op for personal, already yours)")
13
60
  .action(async (opts) => {
14
- const params = {
15
- startTime: opts.startTime,
16
- endTime: opts.endTime,
17
- };
18
- const resp = (await apiGet("/app/activities", params));
19
- const strip = ({ id, device_id, created_at, updated_at, ...rest }) => rest;
20
- const allActivities = (resp.activities || []).map((a) => strip(a));
21
- const lastSeenTime = resp.latestTimestamp;
22
- // Pull out the last activity with null end_time as "last_activity"
23
- let lastActivityIdx = -1;
24
- for (let i = allActivities.length - 1; i >= 0; i--) {
25
- if (allActivities[i].end_time == null) {
26
- lastActivityIdx = i;
27
- break;
28
- }
29
- }
30
- const last_activity = lastActivityIdx !== -1 ? allActivities[lastActivityIdx] : undefined;
31
- const activities = lastActivityIdx !== -1
32
- ? allActivities.filter((_, i) => i !== lastActivityIdx)
33
- : allActivities;
34
- if (isJsonMode(sense)) {
35
- jsonOut({ activities, last_activity, lastSeenTime });
61
+ const { items, pagination } = await scope.listActivities({
62
+ limit: opts.limit ? parseInt(opts.limit, 10) : undefined,
63
+ before: opts.before,
64
+ mine: opts.mine,
65
+ });
66
+ if (isJsonMode(activities)) {
67
+ jsonOut({ activities: items, pagination: pagination ?? {} });
36
68
  return;
37
69
  }
38
- if (!activities.length && !last_activity) {
70
+ if (!items.length) {
39
71
  console.log("No activities found.");
40
- if (lastSeenTime)
41
- console.log(`Latest data available: ${lastSeenTime}`);
42
72
  return;
43
73
  }
44
- if (last_activity) {
45
- console.log(`Last activity: [${last_activity.device_id}] ${last_activity.category}: ${last_activity.details} (${last_activity.start_time} → ongoing)`);
74
+ console.log(`Activities (${items.length} items, newest first):\n` +
75
+ items.map(formatActivityLine).join("\n") +
76
+ paginationFooter(pagination));
77
+ });
78
+ // ── Get (by id; scope-independent) ──
79
+ activities
80
+ .command("get <activityId>")
81
+ .description("Get one activity's details (visible to you if you recorded it or are a member of its space).")
82
+ .action(async (activityId) => {
83
+ const a = (await apiGet(`/app/activity/${activityId}`));
84
+ if (isJsonMode(activities)) {
85
+ jsonOut(a);
86
+ return;
46
87
  }
47
- const lines = activities.map((a) => {
48
- const endStr = a.end_time ? ` ${a.end_time}` : "";
49
- return `- [${a.device_id}] ${a.category}: ${a.details} (${a.start_time}${endStr})`;
50
- });
51
- if (lines.length)
52
- console.log(`Activities (${activities.length} items):\n` + lines.join("\n"));
53
- if (lastSeenTime)
54
- console.log(`Latest data available: ${lastSeenTime}`);
88
+ console.log(`Activity ${a.id}\n` +
89
+ ` category: ${a.category ?? "(none)"}\n` +
90
+ (a.details ? ` details: ${a.details}\n` : "") +
91
+ ` start: ${a.start_time}\n` +
92
+ ` end: ${a.end_time ?? "ongoing"}`);
93
+ });
94
+ // ── Transcript (by id; owner-only) ──
95
+ activities
96
+ .command("transcript <activityId>")
97
+ .description("Get an activity's transcript (owner-only; 403 for other space members).")
98
+ .action(async (activityId) => {
99
+ const resp = (await apiGet(`/app/activity/${activityId}/transcript`));
100
+ const turns = (resp.turns || []);
101
+ if (isJsonMode(activities)) {
102
+ jsonOut({ turns });
103
+ return;
104
+ }
105
+ printTranscriptTurns(turns);
55
106
  });
56
- // ── Transcriptions ──
57
- sense
58
- .command("list-transcriptions")
59
- .description("List transcription records within a time range.")
60
- .requiredOption("--start-time <iso>", "Start of time range (ISO 8601 UTC, e.g. 2026-03-20T00:00:00Z)")
61
- .requiredOption("--end-time <iso>", "End of time range (ISO 8601 UTC, e.g. 2026-03-20T23:59:59Z)")
107
+ }
108
+ // ── Conversations ──
109
+ //
110
+ // Registers the `conversations` subcommand tree under `parent`. The `space`
111
+ // group lists via `/spaces/:slug/conversations` (every member's, keyset-paged);
112
+ // the `personal` group lists via the user-global `/app/conversations` filtered
113
+ // to the personal scope. Replaces the old top-level `gobi sense list-transcriptions`.
114
+ export function registerConversationsSubcommands(parent, scope, description) {
115
+ const conversations = parent.command("conversations").description(description);
116
+ // ── List ──
117
+ conversations
118
+ .command("list")
119
+ .description("List conversations captured in this scope (newest first).")
120
+ .option("--limit <n>", "Max items to return (default 30, max 100). Ignored for personal.")
121
+ .option("--before <cursor>", "Pagination cursor from a previous response (nextCursor). Space scope only.")
122
+ .option("--mine", "Only conversations you recorded (space scope; no-op for personal, already yours)")
62
123
  .action(async (opts) => {
63
- const params = {
64
- startTime: opts.startTime,
65
- endTime: opts.endTime,
66
- };
67
- const resp = (await apiGet("/app/transcriptions", params));
68
- const transcriptions = (resp.transcriptions || []);
69
- const lastSeenTime = resp.latestTimestamp;
70
- const allTurns = [];
71
- for (const t of transcriptions) {
72
- const rawTurns = (t.turns || []);
73
- for (const turn of rawTurns) {
74
- const text = String(turn.text ?? "").trim();
75
- if (!text)
76
- continue;
77
- allTurns.push({
78
- speaker: String(turn.speaker ?? ""),
79
- timestamp: String(turn.timestamp ?? ""),
80
- text,
81
- });
82
- }
124
+ const { items, pagination } = await scope.listConversations({
125
+ limit: opts.limit ? parseInt(opts.limit, 10) : undefined,
126
+ before: opts.before,
127
+ mine: opts.mine,
128
+ });
129
+ if (isJsonMode(conversations)) {
130
+ jsonOut({ conversations: items, pagination: pagination ?? {} });
131
+ return;
132
+ }
133
+ if (!items.length) {
134
+ console.log("No conversations found.");
135
+ return;
83
136
  }
84
- // Filter to requested time range and sort by time.
85
- const startMs = new Date(opts.startTime).getTime();
86
- const endMs = new Date(opts.endTime).getTime();
87
- const filtered = allTurns
88
- .filter((t) => {
89
- const ts = new Date(t.timestamp).getTime();
90
- return ts >= startMs && ts <= endMs;
91
- })
92
- .sort((a, b) => a.timestamp.localeCompare(b.timestamp));
93
- // Friendly speaker labels: named voices → their name, the user → "Me",
94
- // anonymous voices (bare index, Person:/uv:) → a stable "Speaker N"
95
- // numbered by first appearance across the sorted transcript.
96
- const anonOrdinals = new Map();
97
- let nextOrdinal = 1;
98
- const labelFor = (token) => {
99
- const named = token.match(/^(.+):(\d+)$/);
100
- if (named) {
101
- const name = named[1].trim();
102
- if (name === "Me")
103
- return "Me";
104
- if (name && name !== "Person" && name !== "uv")
105
- return name;
106
- }
107
- if (!anonOrdinals.has(token))
108
- anonOrdinals.set(token, nextOrdinal++);
109
- return `Speaker ${anonOrdinals.get(token)}`;
110
- };
111
- if (isJsonMode(sense)) {
112
- jsonOut({
113
- transcriptions: filtered.map((t) => ({ ...t, speaker: labelFor(t.speaker) })),
114
- lastSeenTime,
115
- });
137
+ console.log(`Conversations (${items.length} items, newest first):\n` +
138
+ items.map(formatConversationLine).join("\n") +
139
+ paginationFooter(pagination));
140
+ });
141
+ // ── Transcript (by id) ──
142
+ conversations
143
+ .command("transcript <conversationId>")
144
+ .description("Get a conversation's transcript and summary (owner-only).")
145
+ .action(async (conversationId) => {
146
+ const resp = (await apiGet(`/app/conversations/${conversationId}/transcript`));
147
+ const turns = (resp.turns || []);
148
+ const summary = resp.summary || null;
149
+ if (isJsonMode(conversations)) {
150
+ jsonOut(resp);
151
+ return;
152
+ }
153
+ if (summary && (summary.category || summary.details)) {
154
+ console.log(`Summary: ${summary.category ?? "(uncategorized)"}` +
155
+ (summary.details ? `\n ${summary.details}` : ""));
156
+ console.log("");
157
+ }
158
+ if (resp.status && resp.status !== "ready") {
159
+ console.log(`Status: ${resp.status}`);
160
+ }
161
+ printTranscriptTurns(turns);
162
+ });
163
+ // ── Audio (by id; signed URL, owner-only) ──
164
+ conversations
165
+ .command("audio <conversationId>")
166
+ .description("Get a signed URL for a conversation's combined recording (owner-only; null for analyzer conversations).")
167
+ .action(async (conversationId) => {
168
+ const resp = (await apiGet(`/app/conversations/${conversationId}/audio`));
169
+ const url = resp.url ?? null;
170
+ if (isJsonMode(conversations)) {
171
+ jsonOut({ url });
116
172
  return;
117
173
  }
118
- if (!filtered.length) {
119
- console.log("No transcriptions found.");
120
- if (lastSeenTime)
121
- console.log(`Latest data available: ${lastSeenTime}`);
174
+ if (!url) {
175
+ console.log("No audio available for this conversation.");
122
176
  return;
123
177
  }
124
- const lines = filtered.map((t) => `- ${labelFor(t.speaker)} (${t.timestamp}): ${t.text}`);
125
- console.log(`Transcriptions (${filtered.length} turns):\n` + lines.join("\n"));
126
- if (lastSeenTime)
127
- console.log(`Latest data available: ${lastSeenTime}`);
178
+ console.log(url);
128
179
  });
129
180
  }
@@ -4,6 +4,7 @@ import { requireSpace, selectSpace, setSpaceRequirement, writeSpaceSetting, } fr
4
4
  import { buildMentionMap, formatAttachmentLines, formatAttachmentSummary, formatPostLabel, formatReactionChips, formatReplyLine, isJsonMode, jsonOut, postBodyText, readStdin, resolveSpaceSlug, unwrapResp, } from "./utils.js";
5
5
  import { uploadPostAttachments, assertPostAttachmentMix, } from "../attachments.js";
6
6
  import { registerArtifactSubcommands } from "./artifact.js";
7
+ import { registerActivitiesSubcommands, registerConversationsSubcommands, } from "./sense.js";
7
8
  function readContent(value) {
8
9
  if (value === "-")
9
10
  return readStdin();
@@ -729,4 +730,38 @@ export function registerSpaceCommand(program) {
729
730
  registerArtifactSubcommands(space, { resolve: () => ({ spaceSlug: resolveSpaceSlug(space) }) }, "Versioned creations attached to posts, scoped to this space (visible to its " +
730
731
  "members). Kinds: image | video | gif | markdown | meeting_summary. Always " +
731
732
  "human-owned; revisions form a draft/published tree (one published per artifact).");
733
+ // ── Sense: activities + conversations (scoped to this space) ──
734
+ //
735
+ // The conversations list endpoint is user-global (returns the caller's recent
736
+ // conversations across all scopes, each tagged with spaceId), so the scope
737
+ // resolves this space's numeric id — via GET /spaces/:slug — to filter it.
738
+ const senseScope = {
739
+ label: "space",
740
+ listActivities: async (params) => {
741
+ const q = { limit: params.limit, before: params.before };
742
+ if (params.mine)
743
+ q.mine = true;
744
+ const resp = (await apiGet(`/spaces/${resolveSpaceSlug(space)}/activities`, q));
745
+ return {
746
+ items: (resp.activities || []),
747
+ pagination: resp.pagination,
748
+ };
749
+ },
750
+ listConversations: async (params) => {
751
+ const q = { limit: params.limit, before: params.before };
752
+ if (params.mine)
753
+ q.mine = true;
754
+ const resp = (await apiGet(`/spaces/${resolveSpaceSlug(space)}/conversations`, q));
755
+ return {
756
+ items: (resp.conversations || []),
757
+ pagination: resp.pagination,
758
+ };
759
+ },
760
+ };
761
+ registerActivitiesSubcommands(space, senseScope, "This space's Sense activities — every member's, attributed to each recorder " +
762
+ "(browse-only). Use `gobi space --space-slug <slug> activities …` or set the " +
763
+ "active space with `gobi space warp`.");
764
+ registerConversationsSubcommands(space, senseScope, "This space's Sense conversations — every member's, attributed to each recorder " +
765
+ "(browse-only; transcript/audio stay owner-only). Use `gobi space --space-slug " +
766
+ "<slug> conversations …` or set the active space with `gobi space warp`.");
732
767
  }
package/dist/main.js CHANGED
@@ -8,7 +8,6 @@ import { registerSpaceCommand } from "./commands/space.js";
8
8
  import { registerGlobalCommand } from "./commands/global.js";
9
9
  import { registerPersonalCommand } from "./commands/personal.js";
10
10
  import { registerVaultCommand } from "./commands/vault.js";
11
- import { registerSenseCommand } from "./commands/sense.js";
12
11
  import { registerUpdateCommand } from "./commands/update.js";
13
12
  import { registerMediaCommand } from "./commands/media.js";
14
13
  const require = createRequire(import.meta.url);
@@ -60,11 +59,10 @@ export async function cli() {
60
59
  registerGlobalCommand(program);
61
60
  registerPersonalCommand(program);
62
61
  registerVaultCommand(program);
63
- registerSenseCommand(program);
64
62
  registerUpdateCommand(program);
65
63
  registerMediaCommand(program);
66
- // Artifact subcommands live under `gobi space` and `gobi personal` (registered
67
- // by those groups), not as a top-level `gobi artifact` group.
64
+ // Artifact, activities, and conversations subcommands live under `gobi space`
65
+ // and `gobi personal` (registered by those groups), not as top-level groups.
68
66
  // Propagate helpWidth to all subcommands
69
67
  const helpWidth = process.stdout.columns || 200;
70
68
  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.36",
3
+ "version": "2.0.39",
4
4
  "description": "CLI client for the Gobi collaborative knowledge platform",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -11,12 +11,12 @@ description: >-
11
11
  allowed-tools: Bash(gobi:*)
12
12
  metadata:
13
13
  author: gobi-ai
14
- version: "2.0.36"
14
+ version: "2.0.39"
15
15
  ---
16
16
 
17
17
  # gobi-artifact
18
18
 
19
- Gobi artifact commands for versioned, post-attachable creations (v2.0.36).
19
+ Gobi artifact commands for versioned, post-attachable creations (v2.0.39).
20
20
 
21
21
  Requires gobi-cli installed and authenticated. See gobi-core skill for setup.
22
22
 
@@ -8,12 +8,12 @@ description: >-
8
8
  allowed-tools: Bash(gobi:*)
9
9
  metadata:
10
10
  author: gobi-ai
11
- version: "2.0.36"
11
+ version: "2.0.39"
12
12
  ---
13
13
 
14
14
  # gobi-core
15
15
 
16
- Core CLI commands for the Gobi collaborative knowledge platform (v2.0.36).
16
+ Core CLI commands for the Gobi collaborative knowledge platform (v2.0.39).
17
17
 
18
18
  ## Prerequisites
19
19
 
@@ -72,7 +72,8 @@ 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`, `artifact …`, `media …`, `sense …` | no | no | – |
75
+ | `auth …`, `update`, `media …`, `personal artifact/activities/conversations …` | no | no | – |
76
+ | `space artifact …` / `space activities …` / `space conversations …` | no | **yes** | parent `--space-slug <slug>` |
76
77
  | `vault publish` / `unpublish` / `sync` | **yes** | no | none — must run `gobi vault init` first |
77
78
  | `vault init` | no (it sets it up) | no | – |
78
79
  | `space list` / `warp [slug]` / `get [slug]` | no | no | – |
@@ -7,7 +7,7 @@ description: >-
7
7
  allowed-tools: Bash(gobi:*)
8
8
  metadata:
9
9
  author: gobi-ai
10
- version: "2.0.36"
10
+ version: "2.0.39"
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.36"
13
+ version: "2.0.39"
14
14
  ---
15
15
 
16
16
  # gobi-media
17
17
 
18
- Gobi media generation commands (v2.0.36).
18
+ Gobi media generation commands (v2.0.39).
19
19
 
20
20
  Requires gobi-cli installed and authenticated. See gobi-core skill for setup.
21
21
 
@@ -1,38 +1,88 @@
1
1
  ---
2
2
  name: gobi-sense
3
3
  description: >-
4
- Gobi sense commands for activity and transcription data: list activity
5
- records and list transcription records within a time range. Use when the
6
- user wants to review their activities or transcriptions from Gobi Sense.
4
+ Gobi Sense commands for browsing activities and conversations captured by the
5
+ wearable and mobile app. Activities (what you were doing) and conversations
6
+ (phone-mic Audio Logs + detected conversations, with transcripts) are scoped
7
+ to a space: `gobi personal activities/conversations …` (your personal space)
8
+ or `gobi space activities/conversations …` (the active team space). Read-only.
9
+ Use when the user wants to review their Sense activities or conversations.
7
10
  allowed-tools: Bash(gobi:*)
8
11
  metadata:
9
12
  author: gobi-ai
10
- version: "2.0.36"
13
+ version: "2.0.39"
11
14
  ---
12
15
 
13
16
  # gobi-sense
14
17
 
15
- Gobi sense commands for activity and transcription data (v2.0.36).
18
+ Gobi Sense commands for browsing activities and conversations (v2.0.39).
16
19
 
17
20
  Requires gobi-cli installed and authenticated. See the **gobi-core** skill for setup.
18
21
 
19
- Activities and transcriptions are captured by Gobi Sense (the wearable) and the mobile app, then ingested via the cloud pipeline. The CLI surface is read-only — fetch records in a time range and feed them to whatever analysis you want to run.
22
+ Sense data is captured by Gobi Sense (the wearable) and the mobile app, then ingested by the cloud pipeline. The CLI surface is **read-only**list records and fetch transcripts, then feed them to whatever analysis you want to run.
23
+
24
+ ## Scope: personal vs space
25
+
26
+ Sense data is tagged with the space it was captured in — either your **personal space** or a **team space**. The commands are the same under each scope; pick the group that matches where the data lives:
27
+
28
+ - `gobi personal activities …` / `gobi personal conversations …` — your personal space (visible only to you).
29
+ - `gobi space activities …` / `gobi space conversations …` — the active team space. The space comes from `.gobi/settings.yaml` (set with `gobi space warp <slug>`) or `gobi space --space-slug <slug> activities …`.
30
+
31
+ The examples below use `personal`; swap in `space` to browse a team space.
32
+
33
+ ## Activities vs conversations
34
+
35
+ - **activities** — a running log of what you were doing (category + details), each with a start/end time. In a team space, every member's activities show up, attributed to their recorder. Transcripts are owner-only.
36
+ - **conversations** — phone-mic Audio Log recordings plus Sense-detected conversations, each with a transcript and an auto-generated summary. In a team space, every member's conversations show up, attributed to their recorder (the transcript and `audio` signed URL stay owner-only). In your personal space, you see your own.
37
+
38
+ The old `gobi sense list-activities` / `gobi sense list-transcriptions` commands are gone — transcriptions were unified into **conversations**, and both concepts are now space-scoped.
20
39
 
21
40
  ## Important: JSON Mode
22
41
 
23
42
  For programmatic/agent usage, always pass `--json` as a **global** option (before the subcommand):
24
43
 
25
44
  ```bash
26
- gobi --json sense list-activities --start-time 2026-04-01T00:00:00Z --end-time 2026-04-08T00:00:00Z
45
+ gobi --json personal activities list --limit 30
46
+ gobi --json personal conversations list
47
+ gobi --json space --space-slug my-team activities transcript 978
27
48
  ```
28
49
 
29
- Times are ISO 8601 UTC.
50
+ JSON mode wraps the response as `{"success": true, "data": <…>}` (or `{"success": false, "error": "…"}`).
51
+
52
+ ## Typical workflow
53
+
54
+ List recent activities (newest first, paged with `--limit` / `--before`), then pull one's transcript:
55
+
56
+ ```bash
57
+ gobi --json personal activities list --limit 30
58
+ gobi --json personal activities get 978
59
+ gobi --json personal activities transcript 978
60
+ ```
61
+
62
+ List recent conversations in a space, then read a transcript (with its summary) or grab the recording:
63
+
64
+ ```bash
65
+ gobi --json space --space-slug my-team conversations list
66
+ gobi --json space --space-slug my-team conversations transcript 12345
67
+ gobi --json space --space-slug my-team conversations audio 12345
68
+ ```
69
+
70
+ Both list commands are newest-first and page with `--limit` / `--before` (pass a previous response's `nextCursor` to `--before`). Scope difference: **`gobi space … activities/conversations list`** is a complete, fully-paginated per-space history (every member's records). **`gobi personal … conversations list`** is filtered from the user-global conversations feed, so it shows your recent personal conversations rather than a fully paginated history (`gobi personal activities list` is fully paginated).
30
71
 
31
72
  ## Available Commands
32
73
 
33
- - `gobi sense list-activities` List activity records within a time range.
34
- - `gobi sense list-transcriptions` — List transcription records within a time range.
74
+ Under `gobi personal …` (personal space) or `gobi space …` (active team space):
75
+
76
+ - `activities list` — List Sense activities in this scope (`--limit`, `--before`, `--mine`).
77
+ - `activities get <activityId>` — Get one activity's details.
78
+ - `activities transcript <activityId>` — Get an activity's transcript (owner-only).
79
+ - `conversations list` — List conversations captured in this scope, newest first (`--limit`, `--before`, `--mine`). In a space, every member's (attributed to each recorder).
80
+ - `conversations transcript <conversationId>` — Get a conversation's transcript and summary.
81
+ - `conversations audio <conversationId>` — Get a signed URL for the recording (owner-only).
82
+
83
+ All commands are read-only. In a space, `--mine` on either `list` restricts it to records **you** recorded (`user_id = you`); it's a no-op in the personal lane, which is already all yours.
35
84
 
36
85
  ## Reference Documentation
37
86
 
38
- - [gobi sense](references/sense.md)
87
+ - [gobi personal](references/personal.md)
88
+ - [gobi space](references/space.md)
@@ -0,0 +1,50 @@
1
+ # gobi personal
2
+
3
+ ```
4
+ Usage: gobi personal [options] [command]
5
+
6
+ Personal-space commands (private posts and replies visible only to you). Mirrors the `global` subcommand shape — posts/replies live in the same data model, scoped via personalSpaceUserId so they
7
+ never surface on the public feed.
8
+
9
+ Options:
10
+ -h, --help display help for command
11
+
12
+ Commands:
13
+ activities Your personal Sense activities (what you were doing, from the wearable/app), browse-only. Recorded in your personal space (visible only to you).
14
+ conversations Your personal Sense conversations (phone-mic Audio Logs + detected conversations), browse-only. Recorded in your personal space (visible only to you).
15
+ help [command] display help for command
16
+ ```
17
+
18
+ ## activities
19
+
20
+ ```
21
+ Usage: gobi personal activities [options] [command]
22
+
23
+ Your personal Sense activities (what you were doing, from the wearable/app), browse-only. Recorded in your personal space (visible only to you).
24
+
25
+ Options:
26
+ -h, --help display help for command
27
+
28
+ Commands:
29
+ list [options] List Sense activities in this scope (newest first).
30
+ get <activityId> Get one activity's details (visible to you if you recorded it or are a member of its space).
31
+ transcript <activityId> Get an activity's transcript (owner-only; 403 for other space members).
32
+ help [command] display help for command
33
+ ```
34
+
35
+ ## conversations
36
+
37
+ ```
38
+ Usage: gobi personal conversations [options] [command]
39
+
40
+ Your personal Sense conversations (phone-mic Audio Logs + detected conversations), browse-only. Recorded in your personal space (visible only to you).
41
+
42
+ Options:
43
+ -h, --help display help for command
44
+
45
+ Commands:
46
+ list [options] List conversations captured in this scope (newest first).
47
+ transcript <conversationId> Get a conversation's transcript and summary (owner-only).
48
+ audio <conversationId> Get a signed URL for a conversation's combined recording (owner-only; null for analyzer conversations).
49
+ help [command] display help for command
50
+ ```
@@ -0,0 +1,53 @@
1
+ # gobi space
2
+
3
+ ```
4
+ Usage: gobi space [options] [command]
5
+
6
+ Space commands (posts, replies). Space and member admin is web-UI only.
7
+
8
+ Options:
9
+ --space-slug <spaceSlug> Space slug (overrides .gobi/settings.yaml)
10
+ -h, --help display help for command
11
+
12
+ Commands:
13
+ activities This space's Sense activities — every member's, attributed to each recorder (browse-only). Use `gobi space --space-slug <slug> activities …` or set the
14
+ active space with `gobi space warp`.
15
+ conversations This space's Sense conversations — every member's, attributed to each recorder (browse-only; transcript/audio stay owner-only). Use `gobi space
16
+ --space-slug <slug> conversations …` or set the active space with `gobi space warp`.
17
+ help [command] display help for command
18
+ ```
19
+
20
+ ## activities
21
+
22
+ ```
23
+ Usage: gobi space activities [options] [command]
24
+
25
+ This space's Sense activities — every member's, attributed to each recorder (browse-only). Use `gobi space --space-slug <slug> activities …` or set the active space with `gobi space warp`.
26
+
27
+ Options:
28
+ -h, --help display help for command
29
+
30
+ Commands:
31
+ list [options] List Sense activities in this scope (newest first).
32
+ get <activityId> Get one activity's details (visible to you if you recorded it or are a member of its space).
33
+ transcript <activityId> Get an activity's transcript (owner-only; 403 for other space members).
34
+ help [command] display help for command
35
+ ```
36
+
37
+ ## conversations
38
+
39
+ ```
40
+ Usage: gobi space conversations [options] [command]
41
+
42
+ This space's Sense conversations — every member's, attributed to each recorder (browse-only; transcript/audio stay owner-only). Use `gobi space --space-slug <slug> conversations …` or set the active
43
+ space with `gobi space warp`.
44
+
45
+ Options:
46
+ -h, --help display help for command
47
+
48
+ Commands:
49
+ list [options] List conversations captured in this scope (newest first).
50
+ transcript <conversationId> Get a conversation's transcript and summary (owner-only).
51
+ audio <conversationId> Get a signed URL for a conversation's combined recording (owner-only; null for analyzer conversations).
52
+ help [command] display help for command
53
+ ```
@@ -11,12 +11,12 @@ description: >-
11
11
  allowed-tools: Bash(gobi:*)
12
12
  metadata:
13
13
  author: gobi-ai
14
- version: "2.0.36"
14
+ version: "2.0.39"
15
15
  ---
16
16
 
17
17
  # gobi-space
18
18
 
19
- Gobi space, global, and personal-space posts (v2.0.36).
19
+ Gobi space, global, and personal-space posts (v2.0.39).
20
20
 
21
21
  Requires gobi-cli installed and authenticated. See the **gobi-core** skill for setup.
22
22
 
@@ -25,6 +25,8 @@ Commands:
25
25
  unreact <postId> <emoji> Remove your emoji reaction from a personal-space post or reply. <postId> is the numeric id of a post OR a reply.
26
26
  artifact Versioned creations attached to posts, scoped to your personal space (visible only to you). Kinds: image | video | gif | markdown | meeting_summary. Always
27
27
  human-owned; revisions form a draft/published tree (one published per artifact).
28
+ activities Your personal Sense activities (what you were doing, from the wearable/app), browse-only. Recorded in your personal space (visible only to you).
29
+ conversations Your personal Sense conversations (phone-mic Audio Logs + detected conversations), browse-only. Recorded in your personal space (visible only to you).
28
30
  help [command] display help for command
29
31
  ```
30
32
 
@@ -216,3 +218,37 @@ Commands:
216
218
  list [options] List this scope's artifacts (newest first).
217
219
  help [command] display help for command
218
220
  ```
221
+
222
+ ## activities
223
+
224
+ ```
225
+ Usage: gobi personal activities [options] [command]
226
+
227
+ Your personal Sense activities (what you were doing, from the wearable/app), browse-only. Recorded in your personal space (visible only to you).
228
+
229
+ Options:
230
+ -h, --help display help for command
231
+
232
+ Commands:
233
+ list [options] List Sense activities in this scope (newest first).
234
+ get <activityId> Get one activity's details (visible to you if you recorded it or are a member of its space).
235
+ transcript <activityId> Get an activity's transcript (owner-only; 403 for other space members).
236
+ help [command] display help for command
237
+ ```
238
+
239
+ ## conversations
240
+
241
+ ```
242
+ Usage: gobi personal conversations [options] [command]
243
+
244
+ Your personal Sense conversations (phone-mic Audio Logs + detected conversations), browse-only. Recorded in your personal space (visible only to you).
245
+
246
+ Options:
247
+ -h, --help display help for command
248
+
249
+ Commands:
250
+ list [options] List conversations captured in this scope (newest first).
251
+ transcript <conversationId> Get a conversation's transcript and summary (owner-only).
252
+ audio <conversationId> Get a signed URL for a conversation's combined recording (owner-only; null for analyzer conversations).
253
+ help [command] display help for command
254
+ ```
@@ -8,12 +8,12 @@ description: >-
8
8
  allowed-tools: Bash(gobi:*)
9
9
  metadata:
10
10
  author: gobi-ai
11
- version: "2.0.36"
11
+ version: "2.0.39"
12
12
  ---
13
13
 
14
14
  # gobi-vault
15
15
 
16
- Gobi vault commands for publishing your vault profile and syncing files (v2.0.36).
16
+ Gobi vault commands for publishing your vault profile and syncing files (v2.0.39).
17
17
 
18
18
  Requires gobi-cli installed and authenticated. See gobi-core skill for setup.
19
19
 
@@ -1,41 +0,0 @@
1
- # gobi sense
2
-
3
- ```
4
- Usage: gobi sense [options] [command]
5
-
6
- Sense commands (activities, transcriptions).
7
-
8
- Options:
9
- -h, --help display help for command
10
-
11
- Commands:
12
- list-activities [options] List activity records within a time range.
13
- list-transcriptions [options] List transcription records within a time range.
14
- help [command] display help for command
15
- ```
16
-
17
- ## list-activities
18
-
19
- ```
20
- Usage: gobi sense list-activities [options]
21
-
22
- List activity records within a time range.
23
-
24
- Options:
25
- --start-time <iso> Start of time range (ISO 8601 UTC, e.g. 2026-03-20T00:00:00Z)
26
- --end-time <iso> End of time range (ISO 8601 UTC, e.g. 2026-03-20T23:59:59Z)
27
- -h, --help display help for command
28
- ```
29
-
30
- ## list-transcriptions
31
-
32
- ```
33
- Usage: gobi sense list-transcriptions [options]
34
-
35
- List transcription records within a time range.
36
-
37
- Options:
38
- --start-time <iso> Start of time range (ISO 8601 UTC, e.g. 2026-03-20T00:00:00Z)
39
- --end-time <iso> End of time range (ISO 8601 UTC, e.g. 2026-03-20T23:59:59Z)
40
- -h, --help display help for command
41
- ```