@clubnet/seedclub 0.2.25 → 0.2.27
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.
|
@@ -83,6 +83,30 @@ function getDebugText(result: any, expanded: boolean): string | null {
|
|
|
83
83
|
return blocks.join("\n\n");
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
+
function getCachedDebugText(state: any, result: any, expanded: boolean): string | null {
|
|
87
|
+
const debugMode = isUiDebugEnabled() || expanded;
|
|
88
|
+
if (!debugMode) {
|
|
89
|
+
state.__debugText = null;
|
|
90
|
+
state.__debugMode = false;
|
|
91
|
+
state.__debugDetailsRef = undefined;
|
|
92
|
+
state.__debugContentRef = undefined;
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const detailsRef = result?.details;
|
|
97
|
+
const contentRef = result?.content?.[0]?.text;
|
|
98
|
+
const modeChanged = state.__debugMode !== debugMode;
|
|
99
|
+
const changed =
|
|
100
|
+
modeChanged || state.__debugDetailsRef !== detailsRef || state.__debugContentRef !== contentRef;
|
|
101
|
+
if (changed) {
|
|
102
|
+
state.__debugText = getDebugText(result, expanded);
|
|
103
|
+
state.__debugMode = debugMode;
|
|
104
|
+
state.__debugDetailsRef = detailsRef;
|
|
105
|
+
state.__debugContentRef = contentRef;
|
|
106
|
+
}
|
|
107
|
+
return state.__debugText ?? null;
|
|
108
|
+
}
|
|
109
|
+
|
|
86
110
|
export function makeProgressCallRenderer(
|
|
87
111
|
label: string,
|
|
88
112
|
detail?: (args: any) => string | undefined,
|
|
@@ -146,14 +170,14 @@ export function makeProgressResultRenderer(
|
|
|
146
170
|
if (result?.isError) {
|
|
147
171
|
const line = firstTextLine(result?.content) ?? "Request failed";
|
|
148
172
|
let text = `${theme.fg("error", "✕")} ${theme.fg("error", line)}`;
|
|
149
|
-
const debugText =
|
|
173
|
+
const debugText = getCachedDebugText(state, result, options?.expanded === true);
|
|
150
174
|
if (debugText) text += `\n${theme.fg("dim", debugText)}`;
|
|
151
175
|
return new Text(text, 0, 0);
|
|
152
176
|
}
|
|
153
177
|
const detailText = summary?.(result?.details, context?.args) ?? summarizeDetails(result?.details);
|
|
154
178
|
let text = `${theme.fg("success", "✓")} ${theme.fg("muted", successLabel)}`;
|
|
155
179
|
if (detailText) text += theme.fg("dim", ` · ${detailText}`);
|
|
156
|
-
const debugText =
|
|
180
|
+
const debugText = getCachedDebugText(state, result, options?.expanded === true);
|
|
157
181
|
if (debugText) text += `\n${theme.fg("dim", debugText)}`;
|
|
158
182
|
return new Text(text, 0, 0);
|
|
159
183
|
};
|
|
@@ -11,6 +11,9 @@ const DEFAULT_SHOW_RECORDINGS_LIMIT = 10;
|
|
|
11
11
|
const MAX_SHOW_RECORDINGS_LIMIT = 25;
|
|
12
12
|
const DEFAULT_TRANSCRIPT_LIMIT = 5;
|
|
13
13
|
const MAX_TRANSCRIPT_LIMIT = 20;
|
|
14
|
+
const DEFAULT_PEOPLE_SEARCH_LIMIT = 5;
|
|
15
|
+
const MAX_PEOPLE_SEARCH_LIMIT = 10;
|
|
16
|
+
const DEFAULT_BOOKING_DURATION_MINUTES = 20;
|
|
14
17
|
const TRANSCRIPT_TEXT_PREVIEW_CHARS = 1800;
|
|
15
18
|
const TRANSCRIPT_VTT_PREVIEW_CHARS = 1000;
|
|
16
19
|
|
|
@@ -53,6 +56,103 @@ function truncateText(value: string | null | undefined, maxChars: number) {
|
|
|
53
56
|
return `${value.slice(0, maxChars)}\n...[truncated ${value.length - maxChars} chars]`;
|
|
54
57
|
}
|
|
55
58
|
|
|
59
|
+
function resolveBookingEndsAt(
|
|
60
|
+
startsAt: string,
|
|
61
|
+
endsAt: string | null | undefined,
|
|
62
|
+
durationMinutes: number | null | undefined,
|
|
63
|
+
) {
|
|
64
|
+
if (typeof endsAt === "string" && endsAt.trim()) {
|
|
65
|
+
return endsAt;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const startDate = new Date(startsAt);
|
|
69
|
+
if (Number.isNaN(startDate.getTime())) {
|
|
70
|
+
return endsAt ?? undefined;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const normalizedDuration =
|
|
74
|
+
typeof durationMinutes === "number" && Number.isFinite(durationMinutes) && durationMinutes > 0
|
|
75
|
+
? Math.round(durationMinutes)
|
|
76
|
+
: DEFAULT_BOOKING_DURATION_MINUTES;
|
|
77
|
+
|
|
78
|
+
return new Date(startDate.getTime() + normalizedDuration * 60 * 1000).toISOString();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function localMinutesToUTC(date: Date, minutes: number, timeZone: string) {
|
|
82
|
+
const year = date.getFullYear();
|
|
83
|
+
const month = date.getMonth();
|
|
84
|
+
const day = date.getDate();
|
|
85
|
+
const hours = Math.floor(minutes / 60);
|
|
86
|
+
const mins = minutes % 60;
|
|
87
|
+
|
|
88
|
+
const formatter = new Intl.DateTimeFormat("en-US", {
|
|
89
|
+
timeZone,
|
|
90
|
+
year: "numeric",
|
|
91
|
+
month: "2-digit",
|
|
92
|
+
day: "2-digit",
|
|
93
|
+
hour: "2-digit",
|
|
94
|
+
minute: "2-digit",
|
|
95
|
+
second: "2-digit",
|
|
96
|
+
hour12: false,
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
const referenceUtc = new Date(Date.UTC(year, month, day, 12, 0, 0));
|
|
100
|
+
const parts = formatter.formatToParts(referenceUtc);
|
|
101
|
+
const tzYear = Number.parseInt(parts.find((part) => part.type === "year")?.value ?? "", 10);
|
|
102
|
+
const tzMonth = Number.parseInt(parts.find((part) => part.type === "month")?.value ?? "", 10) - 1;
|
|
103
|
+
const tzDay = Number.parseInt(parts.find((part) => part.type === "day")?.value ?? "", 10);
|
|
104
|
+
const tzHour = Number.parseInt(parts.find((part) => part.type === "hour")?.value ?? "", 10);
|
|
105
|
+
const tzMinute = Number.parseInt(parts.find((part) => part.type === "minute")?.value ?? "", 10);
|
|
106
|
+
const tzSecond = Number.parseInt(parts.find((part) => part.type === "second")?.value ?? "", 10);
|
|
107
|
+
|
|
108
|
+
const tzMillis = Date.UTC(tzYear, tzMonth, tzDay, tzHour, tzMinute, tzSecond);
|
|
109
|
+
const offset = referenceUtc.getTime() - tzMillis;
|
|
110
|
+
const desiredUtc = Date.UTC(year, month, day, hours, mins, 0);
|
|
111
|
+
return new Date(desiredUtc + offset);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function getDateDayOfWeek(date: Date, timeZone: string) {
|
|
115
|
+
const weekday = new Intl.DateTimeFormat("en-US", {
|
|
116
|
+
timeZone,
|
|
117
|
+
weekday: "short",
|
|
118
|
+
})
|
|
119
|
+
.formatToParts(date)
|
|
120
|
+
.find((part) => part.type === "weekday")?.value;
|
|
121
|
+
|
|
122
|
+
switch (weekday) {
|
|
123
|
+
case "Sun":
|
|
124
|
+
return 0;
|
|
125
|
+
case "Mon":
|
|
126
|
+
return 1;
|
|
127
|
+
case "Tue":
|
|
128
|
+
return 2;
|
|
129
|
+
case "Wed":
|
|
130
|
+
return 3;
|
|
131
|
+
case "Thu":
|
|
132
|
+
return 4;
|
|
133
|
+
case "Fri":
|
|
134
|
+
return 5;
|
|
135
|
+
case "Sat":
|
|
136
|
+
return 6;
|
|
137
|
+
default:
|
|
138
|
+
return date.getDay();
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function formatSlotRange(startMinutes: number, endMinutes: number, timeZone: string) {
|
|
143
|
+
const formatter = new Intl.DateTimeFormat("en-US", {
|
|
144
|
+
timeZone,
|
|
145
|
+
hour: "numeric",
|
|
146
|
+
minute: "2-digit",
|
|
147
|
+
hour12: true,
|
|
148
|
+
timeZoneName: "short",
|
|
149
|
+
});
|
|
150
|
+
const anchor = new Date(Date.UTC(2026, 0, 5, 12, 0, 0));
|
|
151
|
+
const start = localMinutesToUTC(anchor, startMinutes, timeZone);
|
|
152
|
+
const end = localMinutesToUTC(anchor, endMinutes, timeZone);
|
|
153
|
+
return `${formatter.format(start)} - ${formatter.format(end)}`;
|
|
154
|
+
}
|
|
155
|
+
|
|
56
156
|
function shapeTranscriptSource(source: any, includeFullText: boolean, includePreview = true) {
|
|
57
157
|
if (!source) {
|
|
58
158
|
return null;
|
|
@@ -1024,6 +1124,147 @@ async function getMeeting(args: { meetingId: string }) {
|
|
|
1024
1124
|
}
|
|
1025
1125
|
}
|
|
1026
1126
|
|
|
1127
|
+
async function searchPeopleForBooking(args: {
|
|
1128
|
+
query: string;
|
|
1129
|
+
limit?: number;
|
|
1130
|
+
}) {
|
|
1131
|
+
try {
|
|
1132
|
+
const limit = normalizeLimit(args.limit, {
|
|
1133
|
+
fallback: DEFAULT_PEOPLE_SEARCH_LIMIT,
|
|
1134
|
+
max: MAX_PEOPLE_SEARCH_LIMIT,
|
|
1135
|
+
});
|
|
1136
|
+
const response = await api.get<any>("/core/parties", {
|
|
1137
|
+
party_type: "person",
|
|
1138
|
+
search: args.query,
|
|
1139
|
+
limit,
|
|
1140
|
+
});
|
|
1141
|
+
|
|
1142
|
+
return {
|
|
1143
|
+
data: Array.isArray(response?.data)
|
|
1144
|
+
? response.data.map((item: any) => ({
|
|
1145
|
+
partyId: item?.party?.id ?? item?.party?.party_id ?? item?.person?.id ?? null,
|
|
1146
|
+
displayName:
|
|
1147
|
+
item?.person?.full_name ??
|
|
1148
|
+
item?.party?.display_name ??
|
|
1149
|
+
item?.organization?.name ??
|
|
1150
|
+
null,
|
|
1151
|
+
email: item?.person?.email ?? null,
|
|
1152
|
+
avatarUrl: item?.person?.avatar_url ?? null,
|
|
1153
|
+
twitterUsername: item?.person?.twitter_username ?? null,
|
|
1154
|
+
organizationName:
|
|
1155
|
+
item?.primary_org_role?.organization_name ??
|
|
1156
|
+
item?.organization?.name ??
|
|
1157
|
+
null,
|
|
1158
|
+
}))
|
|
1159
|
+
: [],
|
|
1160
|
+
pagination: response?.pagination ?? null,
|
|
1161
|
+
};
|
|
1162
|
+
} catch (error) {
|
|
1163
|
+
if (error instanceof ApiError) return { error: error.message, status: error.status };
|
|
1164
|
+
throw error;
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
async function listMeetingAvailabilitySlots(args: {
|
|
1169
|
+
date: string;
|
|
1170
|
+
programSlug?: string;
|
|
1171
|
+
timeZone?: string;
|
|
1172
|
+
}) {
|
|
1173
|
+
try {
|
|
1174
|
+
const requestedDate = new Date(args.date);
|
|
1175
|
+
if (Number.isNaN(requestedDate.getTime())) {
|
|
1176
|
+
return { error: "date must be a valid ISO date or timestamp.", status: 400 };
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
const requestedTimeZone =
|
|
1180
|
+
typeof args.timeZone === "string" && args.timeZone.trim()
|
|
1181
|
+
? args.timeZone.trim()
|
|
1182
|
+
: "America/New_York";
|
|
1183
|
+
const dayOfWeek = getDateDayOfWeek(requestedDate, requestedTimeZone);
|
|
1184
|
+
const response = await api.get<any>("/meetings/availability", {
|
|
1185
|
+
program_slug: args.programSlug,
|
|
1186
|
+
day_of_week: dayOfWeek,
|
|
1187
|
+
});
|
|
1188
|
+
|
|
1189
|
+
const slots = Array.isArray(response?.data)
|
|
1190
|
+
? response.data.map((window: any) => {
|
|
1191
|
+
const slotTimeZone =
|
|
1192
|
+
typeof window?.timezone === "string" && window.timezone.trim()
|
|
1193
|
+
? window.timezone.trim()
|
|
1194
|
+
: requestedTimeZone;
|
|
1195
|
+
const startsAt = localMinutesToUTC(requestedDate, Number(window?.start_minutes ?? 0), slotTimeZone);
|
|
1196
|
+
const endsAt = localMinutesToUTC(requestedDate, Number(window?.end_minutes ?? 0), slotTimeZone);
|
|
1197
|
+
|
|
1198
|
+
return {
|
|
1199
|
+
id: window?.id ?? null,
|
|
1200
|
+
label: formatSlotRange(
|
|
1201
|
+
Number(window?.start_minutes ?? 0),
|
|
1202
|
+
Number(window?.end_minutes ?? 0),
|
|
1203
|
+
slotTimeZone,
|
|
1204
|
+
),
|
|
1205
|
+
timeZone: slotTimeZone,
|
|
1206
|
+
startMinutes: window?.start_minutes ?? null,
|
|
1207
|
+
endMinutes: window?.end_minutes ?? null,
|
|
1208
|
+
startsAt: startsAt.toISOString(),
|
|
1209
|
+
endsAt: endsAt.toISOString(),
|
|
1210
|
+
displayRange: formatSlotRange(
|
|
1211
|
+
Number(window?.start_minutes ?? 0),
|
|
1212
|
+
Number(window?.end_minutes ?? 0),
|
|
1213
|
+
slotTimeZone,
|
|
1214
|
+
),
|
|
1215
|
+
};
|
|
1216
|
+
})
|
|
1217
|
+
: [];
|
|
1218
|
+
|
|
1219
|
+
return {
|
|
1220
|
+
date: args.date,
|
|
1221
|
+
dayOfWeek,
|
|
1222
|
+
slots,
|
|
1223
|
+
};
|
|
1224
|
+
} catch (error) {
|
|
1225
|
+
if (error instanceof ApiError) return { error: error.message, status: error.status };
|
|
1226
|
+
throw error;
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
async function bookMeetingFull(args: {
|
|
1231
|
+
startsAt: string;
|
|
1232
|
+
endsAt?: string | null;
|
|
1233
|
+
durationMinutes?: number | null;
|
|
1234
|
+
guestName: string;
|
|
1235
|
+
guestEmail: string;
|
|
1236
|
+
timeZone?: string;
|
|
1237
|
+
title?: string | null;
|
|
1238
|
+
description?: string | null;
|
|
1239
|
+
partyId?: string | null;
|
|
1240
|
+
guestTwitterHandle?: string | null;
|
|
1241
|
+
profileImageUrl?: string | null;
|
|
1242
|
+
programSlug?: string;
|
|
1243
|
+
}) {
|
|
1244
|
+
try {
|
|
1245
|
+
const resolvedEndsAt = resolveBookingEndsAt(args.startsAt, args.endsAt, args.durationMinutes);
|
|
1246
|
+
return await api.post<any>("/meetings/book-full", {
|
|
1247
|
+
program_slug: args.programSlug,
|
|
1248
|
+
starts_at: args.startsAt,
|
|
1249
|
+
ends_at: resolvedEndsAt,
|
|
1250
|
+
duration_minutes: args.durationMinutes,
|
|
1251
|
+
time_zone: args.timeZone,
|
|
1252
|
+
title: args.title,
|
|
1253
|
+
description: args.description,
|
|
1254
|
+
guest: {
|
|
1255
|
+
party_id: args.partyId,
|
|
1256
|
+
full_name: args.guestName,
|
|
1257
|
+
email: args.guestEmail,
|
|
1258
|
+
twitter_handle: args.guestTwitterHandle,
|
|
1259
|
+
profile_image_url: args.profileImageUrl,
|
|
1260
|
+
},
|
|
1261
|
+
});
|
|
1262
|
+
} catch (error) {
|
|
1263
|
+
if (error instanceof ApiError) return { error: error.message, status: error.status };
|
|
1264
|
+
throw error;
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
|
|
1027
1268
|
async function updateMeeting(args: {
|
|
1028
1269
|
meetingId: string;
|
|
1029
1270
|
startsAt?: string | null;
|
|
@@ -1061,6 +1302,35 @@ async function assignMeeting(args: {
|
|
|
1061
1302
|
}
|
|
1062
1303
|
|
|
1063
1304
|
export function registerMeetingTools(pi: ExtensionAPI) {
|
|
1305
|
+
pi.registerTool({
|
|
1306
|
+
name: "seedclub_search_people",
|
|
1307
|
+
label: "Search People",
|
|
1308
|
+
description:
|
|
1309
|
+
"Search people in Seed Club before booking a meeting. Use this first to find an existing guest and get a partyId. If multiple plausible matches appear, ask the user to choose before booking.",
|
|
1310
|
+
parameters: Type.Object({
|
|
1311
|
+
query: Type.String({ description: "Guest name, email, or search text" }),
|
|
1312
|
+
limit: Type.Optional(
|
|
1313
|
+
Type.Number({ description: "Maximum matches to return (defaults to 5, max 10)." }),
|
|
1314
|
+
),
|
|
1315
|
+
}),
|
|
1316
|
+
execute: wrapExecute(searchPeopleForBooking),
|
|
1317
|
+
});
|
|
1318
|
+
|
|
1319
|
+
pi.registerTool({
|
|
1320
|
+
name: "seedclub_list_meeting_availability",
|
|
1321
|
+
label: "List Availability Slots",
|
|
1322
|
+
description:
|
|
1323
|
+
"List the configured availability slots for a specific date. Use this before booking and choose one of the returned slot startsAt/endsAt pairs instead of inventing a meeting length or time.",
|
|
1324
|
+
parameters: Type.Object({
|
|
1325
|
+
date: Type.String({
|
|
1326
|
+
description: "Target date as YYYY-MM-DD or ISO timestamp. The tool returns valid startsAt/endsAt pairs for that day.",
|
|
1327
|
+
}),
|
|
1328
|
+
programSlug: Type.Optional(Type.String({ description: "Program slug. Defaults to 11am on the server." })),
|
|
1329
|
+
timeZone: Type.Optional(Type.String({ description: "Optional timezone used to interpret the requested date. Defaults to America/New_York." })),
|
|
1330
|
+
}),
|
|
1331
|
+
execute: wrapExecute(listMeetingAvailabilitySlots),
|
|
1332
|
+
});
|
|
1333
|
+
|
|
1064
1334
|
pi.registerTool({
|
|
1065
1335
|
name: "seedclub_list_meetings",
|
|
1066
1336
|
label: "List Meetings",
|
|
@@ -1358,6 +1628,36 @@ export function registerMeetingTools(pi: ExtensionAPI) {
|
|
|
1358
1628
|
renderResult: makeProgressResultRenderer(getToolSuccessLabel("seedclub_get_meeting"), (details) => details?.meeting?.id || undefined),
|
|
1359
1629
|
});
|
|
1360
1630
|
|
|
1631
|
+
pi.registerTool({
|
|
1632
|
+
name: "seedclub_book_meeting_full",
|
|
1633
|
+
label: "Book Meeting",
|
|
1634
|
+
description:
|
|
1635
|
+
"Book one guest meeting end-to-end: create the meeting, create the studio link, and create the Google Calendar invite. Always fetch availability first with seedclub_list_meeting_availability and use one of the returned startsAt/endsAt pairs. Only use this after you know guestName, guestEmail, and startsAt. Prefer passing endsAt when using an availability slot. If they only give a slot or duration, pass durationMinutes instead; if neither is available, this tool defaults to a 20-minute meeting, but the backend will still reject times that do not exactly match a configured slot. Search for an existing guest first and pass partyId when you have a confident match; otherwise proceed without partyId and the backend will find or create a contact using guestEmail before booking.",
|
|
1636
|
+
parameters: Type.Object({
|
|
1637
|
+
startsAt: Type.String({ description: "Meeting start time as an ISO timestamp" }),
|
|
1638
|
+
endsAt: Type.Optional(
|
|
1639
|
+
Type.Union([Type.String(), Type.Null()], {
|
|
1640
|
+
description: "Optional meeting end time as an ISO timestamp. If omitted, durationMinutes or the default 20-minute duration is used.",
|
|
1641
|
+
}),
|
|
1642
|
+
),
|
|
1643
|
+
durationMinutes: Type.Optional(
|
|
1644
|
+
Type.Union([Type.Number(), Type.Null()], {
|
|
1645
|
+
description: "Optional meeting length in minutes. Use this when the user gives a duration or a 20-minute slot instead of an explicit end time.",
|
|
1646
|
+
}),
|
|
1647
|
+
),
|
|
1648
|
+
guestName: Type.String({ description: "Guest full name" }),
|
|
1649
|
+
guestEmail: Type.String({ description: "Guest email address" }),
|
|
1650
|
+
timeZone: Type.Optional(Type.String({ description: "IANA timezone like America/New_York" })),
|
|
1651
|
+
title: Type.Optional(Type.Union([Type.String(), Type.Null()])),
|
|
1652
|
+
description: Type.Optional(Type.Union([Type.String(), Type.Null()])),
|
|
1653
|
+
partyId: Type.Optional(Type.Union([Type.String(), Type.Null()])),
|
|
1654
|
+
guestTwitterHandle: Type.Optional(Type.Union([Type.String(), Type.Null()])),
|
|
1655
|
+
profileImageUrl: Type.Optional(Type.Union([Type.String(), Type.Null()])),
|
|
1656
|
+
programSlug: Type.Optional(Type.String({ description: "Program slug. Defaults to 11am on the server." })),
|
|
1657
|
+
}),
|
|
1658
|
+
execute: wrapExecute(bookMeetingFull),
|
|
1659
|
+
});
|
|
1660
|
+
|
|
1361
1661
|
pi.registerTool({
|
|
1362
1662
|
name: "seedclub_update_meeting",
|
|
1363
1663
|
label: "Update Meeting",
|
|
@@ -92,14 +92,12 @@ let lastCtx: ExtensionContext | undefined;
|
|
|
92
92
|
state.contextDisplay = live.contextDisplay;
|
|
93
93
|
state.costLine = live.costLine;
|
|
94
94
|
}
|
|
95
|
-
|
|
96
|
-
const viewStatus = footerData.getExtensionStatuses().get("seed-view") ?? "";
|
|
97
|
-
state.userLine = [seedStatus, viewStatus].filter(Boolean).join(" • ");
|
|
95
|
+
state.userLine = footerData.getExtensionStatuses().get("seed") ?? "";
|
|
98
96
|
const topRight = theme.fg("dim", `${state.costLine} • ${state.contextDisplay}`);
|
|
99
97
|
const top = theme.fg("dim", truncateToWidth(state.modelLine, usableWidth));
|
|
100
98
|
const topAligned = joinLeftRight(top, topRight, usableWidth);
|
|
101
99
|
if (!state.userLine) return [`${INSET}${topAligned}`];
|
|
102
|
-
const bottomLeft = theme.fg("
|
|
100
|
+
const bottomLeft = theme.fg("dim", state.userLine);
|
|
103
101
|
const bottom = joinLeftRight(
|
|
104
102
|
bottomLeft,
|
|
105
103
|
"",
|
|
@@ -4,31 +4,6 @@ import { getToolCallLabel } from "../seedclub/ui-copy.js";
|
|
|
4
4
|
export default function toolProgressExtension(pi: ExtensionAPI) {
|
|
5
5
|
let activeTools = 0;
|
|
6
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
|
-
|
|
32
7
|
pi.on("tool_execution_start", (event, ctx) => {
|
|
33
8
|
if (!ctx.hasUI) return;
|
|
34
9
|
activeTools += 1;
|
package/package.json
CHANGED