@clubnet/seedclub 0.2.23 → 0.2.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,6 +4,7 @@ import { join, resolve } from "node:path";
4
4
  import { Type } from "@sinclair/typebox";
5
5
  import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
6
6
  import { api } from "../api-client.js";
7
+ import { getToolCallLabel, getToolSuccessLabel } from "../ui-copy.js";
7
8
  import { makeProgressCallRenderer, makeProgressResultRenderer } from "../tool-utils.js";
8
9
  import { getSessionContext } from "../tools/utility.js";
9
10
 
@@ -1092,8 +1093,8 @@ export function registerTranscriptIntentInterceptor(pi: ExtensionAPI) {
1092
1093
  outDir: Type.Optional(Type.String({ description: "Optional output directory. Defaults to ~/Downloads/<program-slug>/ with a per-request subfolder." })),
1093
1094
  latestCount: Type.Optional(Type.Number({ description: "Optional latest-result count, max 20." })),
1094
1095
  }),
1095
- renderCall: makeProgressCallRenderer("Exporting transcript files", (args) => args?.programSlug || args?.person || args?.date || undefined),
1096
- renderResult: makeProgressResultRenderer("Transcript export complete", (details) => {
1096
+ renderCall: makeProgressCallRenderer(getToolCallLabel("seedclub_export_transcripts"), (args) => args?.programSlug || args?.person || args?.date || undefined),
1097
+ renderResult: makeProgressResultRenderer(getToolSuccessLabel("seedclub_export_transcripts"), (details) => {
1097
1098
  const count = typeof details?.count === "number" ? details.count : 0;
1098
1099
  return `${count} file${count === 1 ? "" : "s"}`;
1099
1100
  }),
@@ -48,6 +48,40 @@ function compactToolResult(value: any, depth = 0): any {
48
48
  }
49
49
 
50
50
  const SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
51
+ const UI_DEBUG_VALUES = new Set(["1", "true", "yes", "on"]);
52
+ const MAX_DEBUG_TEXT_LINES = 16;
53
+ const MAX_DEBUG_TEXT_CHARS = 1800;
54
+
55
+ function isUiDebugEnabled() {
56
+ const value = process.env.SEEDCLUB_UI_DEBUG;
57
+ if (!value) return false;
58
+ return UI_DEBUG_VALUES.has(value.trim().toLowerCase());
59
+ }
60
+
61
+ function truncateDebugText(value: string): string {
62
+ const normalized = value.trim();
63
+ if (!normalized) return "";
64
+ const clipped = normalized.length > MAX_DEBUG_TEXT_CHARS
65
+ ? `${normalized.slice(0, MAX_DEBUG_TEXT_CHARS)}\n...[truncated ${normalized.length - MAX_DEBUG_TEXT_CHARS} chars]`
66
+ : normalized;
67
+ const lines = clipped.split("\n");
68
+ if (lines.length <= MAX_DEBUG_TEXT_LINES) return clipped;
69
+ return `${lines.slice(0, MAX_DEBUG_TEXT_LINES).join("\n")}\n...[truncated ${lines.length - MAX_DEBUG_TEXT_LINES} lines]`;
70
+ }
71
+
72
+ function getDebugText(result: any, expanded: boolean): string | null {
73
+ if (!isUiDebugEnabled() && !expanded) return null;
74
+ const details = result?.details == null ? null : truncateDebugText(JSON.stringify(result.details, null, 2));
75
+ const toolText =
76
+ typeof result?.content?.[0]?.text === "string" ? truncateDebugText(String(result.content[0].text)) : "";
77
+
78
+ const blocks = [
79
+ details ? `details:\n${details}` : "",
80
+ toolText ? `content:\n${toolText}` : "",
81
+ ].filter(Boolean);
82
+ if (!blocks.length) return null;
83
+ return blocks.join("\n\n");
84
+ }
51
85
 
52
86
  export function makeProgressCallRenderer(
53
87
  label: string,
@@ -111,11 +145,16 @@ export function makeProgressResultRenderer(
111
145
  }
112
146
  if (result?.isError) {
113
147
  const line = firstTextLine(result?.content) ?? "Request failed";
114
- return new Text(`${theme.fg("error", "✕")} ${theme.fg("error", line)}`, 0, 0);
148
+ let text = `${theme.fg("error", "✕")} ${theme.fg("error", line)}`;
149
+ const debugText = getDebugText(result, options?.expanded === true);
150
+ if (debugText) text += `\n${theme.fg("dim", debugText)}`;
151
+ return new Text(text, 0, 0);
115
152
  }
116
153
  const detailText = summary?.(result?.details, context?.args) ?? summarizeDetails(result?.details);
117
154
  let text = `${theme.fg("success", "✓")} ${theme.fg("muted", successLabel)}`;
118
155
  if (detailText) text += theme.fg("dim", ` · ${detailText}`);
156
+ const debugText = getDebugText(result, options?.expanded === true);
157
+ if (debugText) text += `\n${theme.fg("dim", debugText)}`;
119
158
  return new Text(text, 0, 0);
120
159
  };
121
160
  }
@@ -2,6 +2,7 @@ import { Type } from "@sinclair/typebox";
2
2
  import { Text } from "@mariozechner/pi-tui";
3
3
  import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
4
4
  import { ApiError, api } from "../api-client.js";
5
+ import { getToolCallLabel, getToolSuccessLabel } from "../ui-copy.js";
5
6
  import { makeProgressCallRenderer, makeProgressResultRenderer, wrapExecute } from "../tool-utils.js";
6
7
 
7
8
  const DEFAULT_RECORD_LIMIT = 5;
@@ -133,8 +134,8 @@ export function registerCrmTools(pi: ExtensionAPI) {
133
134
  ),
134
135
  }),
135
136
  execute: wrapExecute(listCrmRecords),
136
- renderCall: makeProgressCallRenderer("Loading CRM records"),
137
- renderResult: makeProgressResultRenderer("CRM records loaded", (details) => {
137
+ renderCall: makeProgressCallRenderer(getToolCallLabel("seedclub_list_crm_records")),
138
+ renderResult: makeProgressResultRenderer(getToolSuccessLabel("seedclub_list_crm_records"), (details) => {
138
139
  const count = Array.isArray(details?.records) ? details.records.length : details?.total;
139
140
  return typeof count === "number" ? `${count} record${count === 1 ? "" : "s"}` : undefined;
140
141
  }),
@@ -148,8 +149,8 @@ export function registerCrmTools(pi: ExtensionAPI) {
148
149
  recordId: Type.String({ description: "CRM record id" }),
149
150
  }),
150
151
  execute: wrapExecute(getCrmRecord),
151
- renderCall: makeProgressCallRenderer("Loading CRM record", (args) => args?.recordId || undefined),
152
- renderResult: makeProgressResultRenderer("CRM record loaded", (details) => details?.record?.id || undefined),
152
+ renderCall: makeProgressCallRenderer(getToolCallLabel("seedclub_get_crm_record"), (args) => args?.recordId || undefined),
153
+ renderResult: makeProgressResultRenderer(getToolSuccessLabel("seedclub_get_crm_record"), (details) => details?.record?.id || undefined),
153
154
  });
154
155
 
155
156
  pi.registerTool({
@@ -162,8 +163,8 @@ export function registerCrmTools(pi: ExtensionAPI) {
162
163
  noteType: Type.Optional(Type.String({ description: "Optional note type" })),
163
164
  }),
164
165
  execute: wrapExecute(createCrmNote),
165
- renderCall: makeProgressCallRenderer("Saving CRM note", (args) => args?.recordId || undefined),
166
- renderResult: makeProgressResultRenderer("CRM note saved"),
166
+ renderCall: makeProgressCallRenderer(getToolCallLabel("seedclub_create_crm_note"), (args) => args?.recordId || undefined),
167
+ renderResult: makeProgressResultRenderer(getToolSuccessLabel("seedclub_create_crm_note")),
167
168
  });
168
169
 
169
170
  pi.registerTool({
@@ -178,8 +179,8 @@ export function registerCrmTools(pi: ExtensionAPI) {
178
179
  assignedUserId: Type.Optional(Type.String({ description: "Optional assigned user id" })),
179
180
  }),
180
181
  execute: wrapExecute(createCrmTask),
181
- renderCall: makeProgressCallRenderer("Creating CRM task", (args) => args?.recordId || undefined),
182
- renderResult: makeProgressResultRenderer("CRM task created"),
182
+ renderCall: makeProgressCallRenderer(getToolCallLabel("seedclub_create_crm_task"), (args) => args?.recordId || undefined),
183
+ renderResult: makeProgressResultRenderer(getToolSuccessLabel("seedclub_create_crm_task")),
183
184
  });
184
185
 
185
186
  pi.registerTool({
@@ -191,7 +192,7 @@ export function registerCrmTools(pi: ExtensionAPI) {
191
192
  search: Type.Optional(Type.String({ description: "Optional contact search text" })),
192
193
  }),
193
194
  execute: wrapExecute(listProgramContacts),
194
- renderCall: makeProgressCallRenderer("Loading program contacts", (args) => args?.programSlug || undefined),
195
+ renderCall: makeProgressCallRenderer(getToolCallLabel("seedclub_list_program_contacts"), (args) => args?.programSlug || undefined),
195
196
  renderResult(result: any, _args: any, theme: any) {
196
197
  if (result.isError) return new Text(theme.fg("error", result.content?.[0]?.text || "Error"), 0, 0);
197
198
  const details = result.details ?? {};
@@ -1,6 +1,7 @@
1
1
  import { Type } from "@sinclair/typebox";
2
2
  import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
3
3
  import { ApiError, api } from "../api-client.js";
4
+ import { getToolCallLabel, getToolSuccessLabel } from "../ui-copy.js";
4
5
  import { makeProgressCallRenderer, makeProgressResultRenderer, wrapExecute } from "../tool-utils.js";
5
6
  import { Text } from "@mariozechner/pi-tui";
6
7
 
@@ -218,7 +219,7 @@ export function registerMediaTools(pi: ExtensionAPI) {
218
219
  ),
219
220
  }),
220
221
  execute: wrapExecute(listProgramMediaAssets),
221
- renderCall: makeProgressCallRenderer("Checking media assets", (args) => args?.programSlug || undefined),
222
+ renderCall: makeProgressCallRenderer(getToolCallLabel("seedclub_list_program_media_assets"), (args) => args?.programSlug || undefined),
222
223
  renderResult(result: any, { expanded }: any, theme: any) {
223
224
  if (result.isError) return new Text(theme.fg("error", result.content?.[0]?.text || "Error"), 0, 0);
224
225
  const rows = Array.isArray(result.details?.data) ? result.details.data : [];
@@ -255,7 +256,7 @@ export function registerMediaTools(pi: ExtensionAPI) {
255
256
  ),
256
257
  }),
257
258
  execute: wrapExecute(getProgramMediaAsset),
258
- renderCall: makeProgressCallRenderer("Loading media asset details", (args) => args?.assetId || undefined),
259
+ renderCall: makeProgressCallRenderer(getToolCallLabel("seedclub_get_program_media_asset"), (args) => args?.assetId || undefined),
259
260
  renderResult(result: any, _args: any, theme: any) {
260
261
  if (result.isError) return new Text(theme.fg("error", result.content?.[0]?.text || "Error"), 0, 0);
261
262
  const asset = result.details?.asset ?? null;
@@ -279,8 +280,8 @@ export function registerMediaTools(pi: ExtensionAPI) {
279
280
  slot: Type.Optional(Type.Union([Type.String(), Type.Null()])),
280
281
  }),
281
282
  execute: wrapExecute(listProgramHeadlines),
282
- renderCall: makeProgressCallRenderer("Loading program headlines", (args) => args?.date || undefined),
283
- renderResult: makeProgressResultRenderer("Program headlines loaded", (details) => {
283
+ renderCall: makeProgressCallRenderer(getToolCallLabel("seedclub_list_program_headlines"), (args) => args?.date || undefined),
284
+ renderResult: makeProgressResultRenderer(getToolSuccessLabel("seedclub_list_program_headlines"), (details) => {
284
285
  const count = Array.isArray(details?.data) ? details.data.length : undefined;
285
286
  return typeof count === "number" ? `${count} headline${count === 1 ? "" : "s"}` : undefined;
286
287
  }),
@@ -303,8 +304,8 @@ export function registerMediaTools(pi: ExtensionAPI) {
303
304
  isLive: Type.Optional(Type.Boolean({ description: "Optional live flag" })),
304
305
  }),
305
306
  execute: wrapExecute(createProgramHeadline),
306
- renderCall: makeProgressCallRenderer("Saving headline", (args) => args?.date || undefined),
307
- renderResult: makeProgressResultRenderer("Headline saved"),
307
+ renderCall: makeProgressCallRenderer(getToolCallLabel("seedclub_create_program_headline"), (args) => args?.date || undefined),
308
+ renderResult: makeProgressResultRenderer(getToolSuccessLabel("seedclub_create_program_headline")),
308
309
  });
309
310
 
310
311
  pi.registerTool({
@@ -326,7 +327,7 @@ export function registerMediaTools(pi: ExtensionAPI) {
326
327
  isLive: Type.Optional(Type.Boolean({ description: "Optional live flag" })),
327
328
  }),
328
329
  execute: wrapExecute(updateProgramHeadline),
329
- renderCall: makeProgressCallRenderer("Updating headline", (args) => args?.headlineId || undefined),
330
- renderResult: makeProgressResultRenderer("Headline updated"),
330
+ renderCall: makeProgressCallRenderer(getToolCallLabel("seedclub_update_program_headline"), (args) => args?.headlineId || undefined),
331
+ renderResult: makeProgressResultRenderer(getToolSuccessLabel("seedclub_update_program_headline")),
331
332
  });
332
333
  }
@@ -2,6 +2,7 @@ import { Type } from "@sinclair/typebox";
2
2
  import { Text } from "@mariozechner/pi-tui";
3
3
  import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
4
4
  import { ApiError, api } from "../api-client.js";
5
+ import { getToolCallLabel, getToolSuccessLabel } from "../ui-copy.js";
5
6
  import { makeProgressCallRenderer, makeProgressResultRenderer, wrapExecute } from "../tool-utils.js";
6
7
 
7
8
  const DEFAULT_MEETINGS_LIMIT = 10;
@@ -1072,8 +1073,8 @@ export function registerMeetingTools(pi: ExtensionAPI) {
1072
1073
  limit: Type.Optional(Type.Number({ description: "Optional max meetings to return. Defaults to 10, max 25." })),
1073
1074
  }),
1074
1075
  execute: wrapExecute(listMeetings),
1075
- renderCall: makeProgressCallRenderer("Checking meeting schedule", (args) => args?.programSlug || undefined),
1076
- renderResult: makeProgressResultRenderer("Meeting schedule loaded", (details) => {
1076
+ renderCall: makeProgressCallRenderer(getToolCallLabel("seedclub_list_meetings"), (args) => args?.programSlug || undefined),
1077
+ renderResult: makeProgressResultRenderer(getToolSuccessLabel("seedclub_list_meetings"), (details) => {
1077
1078
  const count = Array.isArray(details?.data) ? details.data.length : undefined;
1078
1079
  return typeof count === "number" ? `${count} meeting${count === 1 ? "" : "s"}` : undefined;
1079
1080
  }),
@@ -1097,8 +1098,8 @@ export function registerMeetingTools(pi: ExtensionAPI) {
1097
1098
  ),
1098
1099
  }),
1099
1100
  execute: wrapExecute(listShowGuests),
1100
- renderCall: makeProgressCallRenderer("Looking up show guests", (args) => args?.programSlug || undefined),
1101
- renderResult: makeProgressResultRenderer("Show guests loaded", (details) => {
1101
+ renderCall: makeProgressCallRenderer(getToolCallLabel("seedclub_list_show_guests"), (args) => args?.programSlug || undefined),
1102
+ renderResult: makeProgressResultRenderer(getToolSuccessLabel("seedclub_list_show_guests"), (details) => {
1102
1103
  const count = Array.isArray(details?.data) ? details.data.length : undefined;
1103
1104
  return typeof count === "number" ? `${count} guest row${count === 1 ? "" : "s"}` : undefined;
1104
1105
  }),
@@ -1128,8 +1129,8 @@ export function registerMeetingTools(pi: ExtensionAPI) {
1128
1129
  ),
1129
1130
  }),
1130
1131
  execute: wrapExecute(listGuestRoster),
1131
- renderCall: makeProgressCallRenderer("Building guest roster", (args) => args?.programSlug || undefined),
1132
- renderResult: makeProgressResultRenderer("Guest roster ready", (details) => {
1132
+ renderCall: makeProgressCallRenderer(getToolCallLabel("seedclub_list_guest_roster"), (args) => args?.programSlug || undefined),
1133
+ renderResult: makeProgressResultRenderer(getToolSuccessLabel("seedclub_list_guest_roster"), (details) => {
1133
1134
  const count = Array.isArray(details?.rows) ? details.rows.length : undefined;
1134
1135
  return typeof count === "number" ? `${count} guest${count === 1 ? "" : "s"}` : undefined;
1135
1136
  }),
@@ -1153,8 +1154,8 @@ export function registerMeetingTools(pi: ExtensionAPI) {
1153
1154
  ),
1154
1155
  }),
1155
1156
  execute: wrapExecute(getGuestProfile),
1156
- renderCall: makeProgressCallRenderer("Resolving guest profile", (args) => args?.search || args?.email || args?.partyId || undefined),
1157
- renderResult: makeProgressResultRenderer("Guest profile loaded", (details) => details?.contact?.displayName || undefined),
1157
+ renderCall: makeProgressCallRenderer(getToolCallLabel("seedclub_get_guest_profile"), (args) => args?.search || args?.email || args?.partyId || undefined),
1158
+ renderResult: makeProgressResultRenderer(getToolSuccessLabel("seedclub_get_guest_profile"), (details) => details?.contact?.displayName || undefined),
1158
1159
  });
1159
1160
 
1160
1161
  pi.registerTool({
@@ -1182,7 +1183,7 @@ export function registerMeetingTools(pi: ExtensionAPI) {
1182
1183
  ),
1183
1184
  }),
1184
1185
  execute: wrapExecute(findLatestGuestTranscript),
1185
- renderCall: makeProgressCallRenderer("Finding latest guest transcript", (args) => args?.guest || undefined),
1186
+ renderCall: makeProgressCallRenderer(getToolCallLabel("seedclub_find_latest_guest_transcript"), (args) => args?.guest || undefined),
1186
1187
  renderResult(result: any, _args: any, theme: any) {
1187
1188
  if (result.isError) return new Text(theme.fg("error", result.content?.[0]?.text || "Error"), 0, 0);
1188
1189
  const found = result.details?.found === true;
@@ -1210,7 +1211,7 @@ export function registerMeetingTools(pi: ExtensionAPI) {
1210
1211
  ),
1211
1212
  }),
1212
1213
  execute: wrapExecute(prepareClipPacket),
1213
- renderCall: makeProgressCallRenderer("Checking clip readiness", (args) => args?.meetingId || undefined),
1214
+ renderCall: makeProgressCallRenderer(getToolCallLabel("seedclub_prepare_clip_packet"), (args) => args?.meetingId || undefined),
1214
1215
  renderResult(result: any, _args: any, theme: any) {
1215
1216
  if (result.isError) return new Text(theme.fg("error", result.content?.[0]?.text || "Error"), 0, 0);
1216
1217
 
@@ -1260,8 +1261,8 @@ export function registerMeetingTools(pi: ExtensionAPI) {
1260
1261
  ),
1261
1262
  }),
1262
1263
  execute: wrapExecute(listShowRecordings),
1263
- renderCall: makeProgressCallRenderer("Checking show recordings", (args) => args?.programSlug || undefined),
1264
- renderResult: makeProgressResultRenderer("Show recordings loaded", (details) => {
1264
+ renderCall: makeProgressCallRenderer(getToolCallLabel("seedclub_list_show_recordings"), (args) => args?.programSlug || undefined),
1265
+ renderResult: makeProgressResultRenderer(getToolSuccessLabel("seedclub_list_show_recordings"), (details) => {
1265
1266
  const count = Array.isArray(details?.data) ? details.data.length : undefined;
1266
1267
  return typeof count === "number" ? `${count} recording${count === 1 ? "" : "s"}` : undefined;
1267
1268
  }),
@@ -1291,7 +1292,7 @@ export function registerMeetingTools(pi: ExtensionAPI) {
1291
1292
  ),
1292
1293
  }),
1293
1294
  execute: wrapExecute(listMeetingTranscripts),
1294
- renderCall: makeProgressCallRenderer("Checking transcript inventory", (args) => args?.programSlug || undefined),
1295
+ renderCall: makeProgressCallRenderer(getToolCallLabel("seedclub_list_meeting_transcripts"), (args) => args?.programSlug || undefined),
1295
1296
  renderResult(result: any, renderArgs: any, theme: any) {
1296
1297
  if (result.isError) return new Text(theme.fg("error", result.content?.[0]?.text || "Error"), 0, 0);
1297
1298
  const expanded = renderArgs?.expanded === true;
@@ -1337,8 +1338,8 @@ export function registerMeetingTools(pi: ExtensionAPI) {
1337
1338
  ),
1338
1339
  }),
1339
1340
  execute: wrapExecute(getMeetingTranscript),
1340
- renderCall: makeProgressCallRenderer("Loading meeting transcript", (args) => args?.meetingId || undefined),
1341
- renderResult: makeProgressResultRenderer("Meeting transcript loaded", (details) => {
1341
+ renderCall: makeProgressCallRenderer(getToolCallLabel("seedclub_get_meeting_transcript"), (args) => args?.meetingId || undefined),
1342
+ renderResult: makeProgressResultRenderer(getToolSuccessLabel("seedclub_get_meeting_transcript"), (details) => {
1342
1343
  const date = details?.transcript?.event_date;
1343
1344
  return typeof date === "string" ? date : undefined;
1344
1345
  }),
@@ -1353,8 +1354,8 @@ export function registerMeetingTools(pi: ExtensionAPI) {
1353
1354
  meetingId: Type.String({ description: "Meeting id" }),
1354
1355
  }),
1355
1356
  execute: wrapExecute(getMeeting),
1356
- renderCall: makeProgressCallRenderer("Loading meeting details", (args) => args?.meetingId || undefined),
1357
- renderResult: makeProgressResultRenderer("Meeting details loaded", (details) => details?.meeting?.id || undefined),
1357
+ renderCall: makeProgressCallRenderer(getToolCallLabel("seedclub_get_meeting"), (args) => args?.meetingId || undefined),
1358
+ renderResult: makeProgressResultRenderer(getToolSuccessLabel("seedclub_get_meeting"), (details) => details?.meeting?.id || undefined),
1358
1359
  });
1359
1360
 
1360
1361
  pi.registerTool({
@@ -1369,8 +1370,8 @@ export function registerMeetingTools(pi: ExtensionAPI) {
1369
1370
  producerFraming: Type.Optional(Type.Union([Type.String(), Type.Null()])),
1370
1371
  }),
1371
1372
  execute: wrapExecute(updateMeeting),
1372
- renderCall: makeProgressCallRenderer("Updating meeting", (args) => args?.meetingId || undefined),
1373
- renderResult: makeProgressResultRenderer("Meeting updated"),
1373
+ renderCall: makeProgressCallRenderer(getToolCallLabel("seedclub_update_meeting"), (args) => args?.meetingId || undefined),
1374
+ renderResult: makeProgressResultRenderer(getToolSuccessLabel("seedclub_update_meeting")),
1374
1375
  });
1375
1376
 
1376
1377
  pi.registerTool({
@@ -1383,7 +1384,7 @@ export function registerMeetingTools(pi: ExtensionAPI) {
1383
1384
  syncPendingEmailToPerson: Type.Optional(Type.Boolean({ description: "Whether to sync pending email to the person" })),
1384
1385
  }),
1385
1386
  execute: wrapExecute(assignMeeting),
1386
- renderCall: makeProgressCallRenderer("Assigning meeting", (args) => args?.meetingId || undefined),
1387
- renderResult: makeProgressResultRenderer("Meeting assigned"),
1387
+ renderCall: makeProgressCallRenderer(getToolCallLabel("seedclub_assign_meeting"), (args) => args?.meetingId || undefined),
1388
+ renderResult: makeProgressResultRenderer(getToolSuccessLabel("seedclub_assign_meeting")),
1388
1389
  });
1389
1390
  }
@@ -1,6 +1,7 @@
1
1
  import { Type } from "@sinclair/typebox";
2
2
  import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
3
3
  import { ApiError, api } from "../api-client.js";
4
+ import { getToolCallLabel, getToolSuccessLabel } from "../ui-copy.js";
4
5
  import { makeProgressCallRenderer, makeProgressResultRenderer, wrapExecute } from "../tool-utils.js";
5
6
 
6
7
  export async function getSessionContext() {
@@ -98,8 +99,8 @@ export function registerUtilityTools(pi: ExtensionAPI) {
98
99
  "Get the current Seed Club session, workspace, and accessible program context. Use only for auth, workspace, or program access discovery; do not use for transcript, guest, media, clip, or show-content retrieval.",
99
100
  parameters: Type.Object({}),
100
101
  execute: wrapExecute(getSessionContext),
101
- renderCall: makeProgressCallRenderer("Loading workspace session context"),
102
- renderResult: makeProgressResultRenderer("Workspace context loaded"),
102
+ renderCall: makeProgressCallRenderer(getToolCallLabel("seedclub_get_session_context")),
103
+ renderResult: makeProgressResultRenderer(getToolSuccessLabel("seedclub_get_session_context")),
103
104
  });
104
105
 
105
106
  pi.registerTool({
@@ -108,8 +109,8 @@ export function registerUtilityTools(pi: ExtensionAPI) {
108
109
  description: "Get information about the currently authenticated Seed Club user and their accessible program count.",
109
110
  parameters: Type.Object({}),
110
111
  execute: wrapExecute(getCurrentUser),
111
- renderCall: makeProgressCallRenderer("Loading current user"),
112
- renderResult: makeProgressResultRenderer("Current user loaded", (details) => details?.name || undefined),
112
+ renderCall: makeProgressCallRenderer(getToolCallLabel("seedclub_get_current_user")),
113
+ renderResult: makeProgressResultRenderer(getToolSuccessLabel("seedclub_get_current_user"), (details) => details?.name || undefined),
113
114
  });
114
115
 
115
116
  pi.registerTool({
@@ -118,8 +119,8 @@ export function registerUtilityTools(pi: ExtensionAPI) {
118
119
  description: "List the Seed Club programs accessible to the current user.",
119
120
  parameters: Type.Object({}),
120
121
  execute: wrapExecute(listAccessiblePrograms),
121
- renderCall: makeProgressCallRenderer("Loading accessible programs"),
122
- renderResult: makeProgressResultRenderer("Accessible programs loaded", (details) => {
122
+ renderCall: makeProgressCallRenderer(getToolCallLabel("seedclub_list_accessible_programs")),
123
+ renderResult: makeProgressResultRenderer(getToolSuccessLabel("seedclub_list_accessible_programs"), (details) => {
123
124
  const count = Array.isArray(details?.programs) ? details.programs.length : 0;
124
125
  return `${count} program${count === 1 ? "" : "s"}`;
125
126
  }),
@@ -133,7 +134,7 @@ export function registerUtilityTools(pi: ExtensionAPI) {
133
134
  programSlug: Type.String({ description: "Program slug" }),
134
135
  }),
135
136
  execute: wrapExecute(getProgramAccess),
136
- renderCall: makeProgressCallRenderer("Checking program access", (args) => args?.programSlug || undefined),
137
- renderResult: makeProgressResultRenderer("Program access loaded", (details) => details?.program?.slug || undefined),
137
+ renderCall: makeProgressCallRenderer(getToolCallLabel("seedclub_get_program_access"), (args) => args?.programSlug || undefined),
138
+ renderResult: makeProgressResultRenderer(getToolSuccessLabel("seedclub_get_program_access"), (details) => details?.program?.slug || undefined),
138
139
  });
139
140
  }
@@ -0,0 +1,73 @@
1
+ export const TOOL_CALL_LABELS: Record<string, string> = {
2
+ seedclub_get_session_context: "Loading workspace session context",
3
+ seedclub_get_current_user: "Loading current user",
4
+ seedclub_list_accessible_programs: "Loading accessible programs",
5
+ seedclub_get_program_access: "Checking program access",
6
+ seedclub_list_crm_records: "Loading CRM records",
7
+ seedclub_get_crm_record: "Loading CRM record",
8
+ seedclub_create_crm_note: "Saving CRM note",
9
+ seedclub_create_crm_task: "Creating CRM task",
10
+ seedclub_list_program_contacts: "Loading program contacts",
11
+ seedclub_list_meetings: "Checking meeting schedule",
12
+ seedclub_list_show_guests: "Looking up show guests",
13
+ seedclub_list_guest_roster: "Building guest roster",
14
+ seedclub_get_guest_profile: "Resolving guest profile",
15
+ seedclub_find_latest_guest_transcript: "Finding the latest guest transcript",
16
+ seedclub_prepare_clip_packet: "Checking clip readiness",
17
+ seedclub_list_show_recordings: "Checking show recordings",
18
+ seedclub_list_meeting_transcripts: "Checking transcript availability",
19
+ seedclub_get_meeting_transcript: "Loading meeting transcript",
20
+ seedclub_get_meeting: "Loading meeting details",
21
+ seedclub_update_meeting: "Updating meeting",
22
+ seedclub_assign_meeting: "Assigning meeting",
23
+ seedclub_list_program_media_assets: "Checking media assets",
24
+ seedclub_get_program_media_asset: "Loading media asset details",
25
+ seedclub_list_program_headlines: "Loading program headlines",
26
+ seedclub_create_program_headline: "Saving headline",
27
+ seedclub_update_program_headline: "Updating headline",
28
+ seedclub_export_transcripts: "Exporting transcript files",
29
+ };
30
+
31
+ export const TOOL_SUCCESS_LABELS: Record<string, string> = {
32
+ seedclub_get_session_context: "Workspace context loaded",
33
+ seedclub_get_current_user: "Current user loaded",
34
+ seedclub_list_accessible_programs: "Accessible programs loaded",
35
+ seedclub_get_program_access: "Program access loaded",
36
+ seedclub_list_crm_records: "CRM records loaded",
37
+ seedclub_get_crm_record: "CRM record loaded",
38
+ seedclub_create_crm_note: "CRM note saved",
39
+ seedclub_create_crm_task: "CRM task created",
40
+ seedclub_list_program_contacts: "Program contacts loaded",
41
+ seedclub_list_meetings: "Meeting schedule loaded",
42
+ seedclub_list_show_guests: "Show guests loaded",
43
+ seedclub_list_guest_roster: "Guest roster ready",
44
+ seedclub_get_guest_profile: "Guest profile loaded",
45
+ seedclub_find_latest_guest_transcript: "Guest transcript loaded",
46
+ seedclub_prepare_clip_packet: "Clip readiness checked",
47
+ seedclub_list_show_recordings: "Show recordings loaded",
48
+ seedclub_list_meeting_transcripts: "Transcript availability checked",
49
+ seedclub_get_meeting_transcript: "Meeting transcript loaded",
50
+ seedclub_get_meeting: "Meeting details loaded",
51
+ seedclub_update_meeting: "Meeting updated",
52
+ seedclub_assign_meeting: "Meeting assigned",
53
+ seedclub_list_program_media_assets: "Media assets checked",
54
+ seedclub_get_program_media_asset: "Media asset details loaded",
55
+ seedclub_list_program_headlines: "Program headlines loaded",
56
+ seedclub_create_program_headline: "Headline saved",
57
+ seedclub_update_program_headline: "Headline updated",
58
+ seedclub_export_transcripts: "Transcript export complete",
59
+ };
60
+
61
+ export function humanizeToolName(toolName: string): string {
62
+ const cleaned = toolName.replace(/^seedclub_/, "").replace(/_/g, " ").trim();
63
+ if (!cleaned) return "Processing request";
64
+ return cleaned.charAt(0).toUpperCase() + cleaned.slice(1);
65
+ }
66
+
67
+ export function getToolCallLabel(toolName: string): string {
68
+ return TOOL_CALL_LABELS[toolName] ?? humanizeToolName(toolName);
69
+ }
70
+
71
+ export function getToolSuccessLabel(toolName: string): string {
72
+ return TOOL_SUCCESS_LABELS[toolName] ?? "Completed";
73
+ }
@@ -92,7 +92,9 @@ let lastCtx: ExtensionContext | undefined;
92
92
  state.contextDisplay = live.contextDisplay;
93
93
  state.costLine = live.costLine;
94
94
  }
95
- state.userLine = footerData.getExtensionStatuses().get("seed") ?? "";
95
+ const seedStatus = footerData.getExtensionStatuses().get("seed") ?? "";
96
+ const viewStatus = footerData.getExtensionStatuses().get("seed-view") ?? "";
97
+ state.userLine = [seedStatus, viewStatus].filter(Boolean).join(" • ");
96
98
  const topRight = theme.fg("dim", `${state.costLine} • ${state.contextDisplay}`);
97
99
  const top = theme.fg("dim", truncateToWidth(state.modelLine, usableWidth));
98
100
  const topAligned = joinLeftRight(top, topRight, usableWidth);
@@ -1,28 +1,38 @@
1
1
  import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
2
-
3
- const TOOL_PROGRESS_LABELS: Record<string, string> = {
4
- seedclub_find_latest_guest_transcript: "Finding the latest guest transcript",
5
- seedclub_prepare_clip_packet: "Checking clip readiness",
6
- seedclub_list_meeting_transcripts: "Checking transcript availability",
7
- seedclub_list_show_recordings: "Checking show recordings",
8
- seedclub_list_program_media_assets: "Checking media assets",
9
- seedclub_export_transcripts: "Exporting transcript files",
10
- };
11
-
12
- function humanizeToolName(toolName: string): string {
13
- const cleaned = toolName.replace(/^seedclub_/, "").replace(/_/g, " ").trim();
14
- if (!cleaned) return "Processing request";
15
- return cleaned.charAt(0).toUpperCase() + cleaned.slice(1);
16
- }
2
+ import { getToolCallLabel } from "../seedclub/ui-copy.js";
17
3
 
18
4
  export default function toolProgressExtension(pi: ExtensionAPI) {
19
5
  let activeTools = 0;
20
6
 
7
+ const applyViewStatus = (ctx: { hasUI: boolean; ui: { getToolsExpanded: () => boolean; setStatus: (key: string, text: string | undefined) => void } }) => {
8
+ if (!ctx.hasUI) return;
9
+ const mode = ctx.ui.getToolsExpanded() ? "advanced" : "standard";
10
+ ctx.ui.setStatus("seed-view", `view: ${mode}`);
11
+ };
12
+
13
+ pi.registerShortcut("alt+o", {
14
+ description: "Toggle advanced tool detail view",
15
+ handler: (ctx) => {
16
+ if (!ctx.hasUI) return;
17
+ const next = !ctx.ui.getToolsExpanded();
18
+ ctx.ui.setToolsExpanded(next);
19
+ applyViewStatus(ctx);
20
+ ctx.ui.notify(next ? "Advanced tool view: on" : "Advanced tool view: off", "info");
21
+ },
22
+ });
23
+
24
+ pi.on("session_start", (_event, ctx) => {
25
+ applyViewStatus(ctx);
26
+ });
27
+
28
+ pi.on("session_switch", (_event, ctx) => {
29
+ applyViewStatus(ctx);
30
+ });
31
+
21
32
  pi.on("tool_execution_start", (event, ctx) => {
22
33
  if (!ctx.hasUI) return;
23
34
  activeTools += 1;
24
- const label = TOOL_PROGRESS_LABELS[event.toolName] ?? humanizeToolName(event.toolName);
25
- ctx.ui.setWorkingMessage(`${label}...`);
35
+ ctx.ui.setWorkingMessage(`${getToolCallLabel(event.toolName)}...`);
26
36
  });
27
37
 
28
38
  pi.on("tool_execution_end", (_event, ctx) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clubnet/seedclub",
3
- "version": "0.2.23",
3
+ "version": "0.2.25",
4
4
  "description": "A branded command-line agent wrapper around pi, with integrated Seed Club commands, tools, and app actions",
5
5
  "license": "MIT",
6
6
  "repository": {