@clubnet/seedclub 0.2.41 → 0.2.42

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.
@@ -104,6 +104,21 @@ export function registerSeedclubCommand(pi: ExtensionAPI, deps: SeedclubDeps) {
104
104
  },
105
105
  });
106
106
 
107
+ pi.registerCommand("disconnect", {
108
+ description: "Disconnect your Seed Club account",
109
+ handler: async (_args, ctx) => {
110
+ const confirmed = await ctx.ui.confirm(
111
+ "Disconnect Seed Club",
112
+ "This will remove local Seed Club credentials from this agent.",
113
+ );
114
+ if (!confirmed) {
115
+ ctx.ui.notify("Disconnect cancelled.", "info");
116
+ return;
117
+ }
118
+ await deps.disconnect(ctx);
119
+ },
120
+ });
121
+
107
122
  pi.registerCommand("connect-calendar", {
108
123
  description: "Connect a personal Google Calendar to your Seed Club account",
109
124
  handler: async (_args, ctx) => {
@@ -171,16 +186,6 @@ export function registerSeedclubCommand(pi: ExtensionAPI, deps: SeedclubDeps) {
171
186
  },
172
187
  });
173
188
 
174
- pi.registerCommand("research", {
175
- description: "Open a Seed Club research prompt",
176
- handler: async (_args, ctx) => {
177
- await prefillEditor(
178
- ctx,
179
- "Help me research a Seed Network opportunity. Ask me for the company, deck, memo, source URL, or local file if needed. Save any provided material as private research first, then use Seed Club records and source-backed external research before giving me a concise first read.",
180
- );
181
- },
182
- });
183
-
184
189
  pi.registerCommand("source", {
185
190
  description: "Open a source-backed research prompt",
186
191
  handler: async (_args, ctx) => {
@@ -191,32 +196,12 @@ export function registerSeedclubCommand(pi: ExtensionAPI, deps: SeedclubDeps) {
191
196
  },
192
197
  });
193
198
 
194
- pi.registerCommand("worldview", {
195
- description: "Open an investing worldview prompt",
196
- handler: async (_args, ctx) => {
197
- await prefillEditor(
198
- ctx,
199
- "Summarize my current Seed Club investing worldview from recent memory and Seed Club context. Focus on beliefs, decision heuristics, and places where my view has changed. Flag what evidence supports each point.",
200
- );
201
- },
202
- });
203
-
204
- pi.registerCommand("theses", {
205
- description: "Open an investment theses prompt",
206
- handler: async (_args, ctx) => {
207
- await prefillEditor(
208
- ctx,
209
- "Draft my current investment theses from Seed Club memory and recent deal context. Group by thesis, include supporting signals, counterexamples, and what would change my mind.",
210
- );
211
- },
212
- });
213
-
214
- pi.registerCommand("concerns", {
215
- description: "Open an investment concerns prompt",
199
+ pi.registerCommand("diligence", {
200
+ description: "Open a Seed Club diligence prompt",
216
201
  handler: async (_args, ctx) => {
217
202
  await prefillEditor(
218
203
  ctx,
219
- "List my current recurring investment concerns from Seed Club memory and recent deal context. Group concerns by theme, name the pattern, cite representative context when available, and separate known concerns from guesses.",
204
+ "Help me diligence this opportunity. Start by asking for the company, deck, memo, source URL, or local file if needed. Use Seed Club records and source-backed external research, separate known facts from assumptions, identify risks and open questions, and end with a concise recommendation.",
220
205
  );
221
206
  },
222
207
  });
@@ -63,7 +63,7 @@ export function markAuthRequired(options?: { authUrl?: string | null; message?:
63
63
  setState({
64
64
  status: "auth_required",
65
65
  authUrl: options?.authUrl ?? getSharedStore().state.authUrl,
66
- message: options?.message ?? "Seed Club sign-in is required before /login or /model.",
66
+ message: options?.message ?? "Seed Club sign-in is required before model setup.",
67
67
  error: options?.error ?? null,
68
68
  });
69
69
  }
@@ -352,7 +352,7 @@ export default function (pi: ExtensionAPI) {
352
352
  function getPostAuthInstruction(ctx: any): string | null {
353
353
  const hasProviderAuth = ctx.modelRegistry.getAvailable().length > 0;
354
354
  const hasSelectedModel = !!ctx.model;
355
- if (!hasProviderAuth) return "Next: /login, then /model.";
355
+ if (!hasProviderAuth) return "Next: /model to set up inference.";
356
356
  if (!hasSelectedModel) return "Next: /model.";
357
357
  return null;
358
358
  }
@@ -427,7 +427,7 @@ export default function (pi: ExtensionAPI) {
427
427
  usedSeedclubToolThisTurn = false;
428
428
  markAuthRequired({
429
429
  authUrl: null,
430
- message: "Seed Club sign-in is required before /login or /model.",
430
+ message: "Seed Club sign-in is required before model setup.",
431
431
  error: "Use a @seedclub.com account to connect this agent.",
432
432
  });
433
433
  return null;
@@ -496,7 +496,7 @@ export default function (pi: ExtensionAPI) {
496
496
  memory.clear(ctx);
497
497
  markAuthRequired({
498
498
  authUrl: null,
499
- message: "Seed Club sign-in is required before /login or /model.",
499
+ message: "Seed Club sign-in is required before model setup.",
500
500
  error: null,
501
501
  });
502
502
  }
@@ -506,7 +506,7 @@ export default function (pi: ExtensionAPI) {
506
506
  message: isReset
507
507
  ? "Resetting your Seed Club sign-in and opening the browser to switch accounts."
508
508
  : options?.autoStart
509
- ? "Seed Club sign-in is required before /login or /model. Opening your browser now."
509
+ ? "Seed Club sign-in is required before model setup. Opening your browser now."
510
510
  : "Opening your browser for Seed Club sign-in.",
511
511
  error: null,
512
512
  });
@@ -558,7 +558,7 @@ export default function (pi: ExtensionAPI) {
558
558
  usedSeedclubToolThisTurn = false;
559
559
  markAuthRequired({
560
560
  authUrl: null,
561
- message: "Seed Club sign-in is required before /login or /model.",
561
+ message: "Seed Club sign-in is required before model setup.",
562
562
  error: null,
563
563
  });
564
564
  ctx.ui.notify("Logged out", "info");
@@ -32,8 +32,20 @@ const MEMORY_COMMAND_COMPLETIONS = [
32
32
  { value: "recent", label: "recent", description: "Show recent memory" },
33
33
  { value: "on", label: "on", description: "Turn memory on" },
34
34
  { value: "off", label: "off", description: "Turn memory off" },
35
+ { value: "worldview", label: "worldview", description: "Open an investing worldview prompt" },
36
+ { value: "theses", label: "theses", description: "Open an investment theses prompt" },
37
+ { value: "concerns", label: "concerns", description: "Open an investment concerns prompt" },
35
38
  ];
36
39
 
40
+ const MEMORY_PROMPTS = {
41
+ worldview:
42
+ "Summarize my current Seed Club investing worldview from recent memory and Seed Club context. Focus on beliefs, decision heuristics, and places where my view has changed. Flag what evidence supports each point.",
43
+ theses:
44
+ "Draft my current investment theses from Seed Club memory and recent deal context. Group by thesis, include supporting signals, counterexamples, and what would change my mind.",
45
+ concerns:
46
+ "List my current recurring investment concerns from Seed Club memory and recent deal context. Group concerns by theme, name the pattern, cite representative context when available, and separate known concerns from guesses.",
47
+ } as const;
48
+
37
49
  type MemoryStatus = {
38
50
  available: boolean;
39
51
  enabled: boolean;
@@ -55,6 +67,11 @@ function errorMessage(error: unknown) {
55
67
  return "Seed Club memory request failed.";
56
68
  }
57
69
 
70
+ async function prefillEditor(ctx: any, text: string) {
71
+ await new Promise((r) => setTimeout(r, 300));
72
+ ctx.ui.setEditorText(text);
73
+ }
74
+
58
75
  function getSessionId(ctx: any) {
59
76
  const cwd = String(ctx?.cwd ?? process.cwd());
60
77
  if (MEMORY_SESSION_STRATEGY === "session") {
@@ -263,11 +280,17 @@ export function registerMemory(pi: ExtensionAPI): MemoryController {
263
280
  "Show recent memory",
264
281
  "Turn memory on",
265
282
  "Turn memory off",
283
+ "Investing worldview",
284
+ "Investment theses",
285
+ "Investment concerns",
266
286
  ]);
267
287
  if (choice === "Show status") await showStatus(ctx);
268
288
  if (choice === "Show recent memory") await showRecentMemory(ctx);
269
289
  if (choice === "Turn memory on") await setEnabled(ctx, true);
270
290
  if (choice === "Turn memory off") await setEnabled(ctx, false);
291
+ if (choice === "Investing worldview") await prefillEditor(ctx, MEMORY_PROMPTS.worldview);
292
+ if (choice === "Investment theses") await prefillEditor(ctx, MEMORY_PROMPTS.theses);
293
+ if (choice === "Investment concerns") await prefillEditor(ctx, MEMORY_PROMPTS.concerns);
271
294
  }
272
295
 
273
296
  async function showRecentMemory(ctx: any, args = "") {
@@ -323,7 +346,8 @@ export function registerMemory(pi: ExtensionAPI): MemoryController {
323
346
  }
324
347
 
325
348
  pi.registerCommand("memory", {
326
- description: "Show Seed Club memory status, recent memory, or turn memory on/off (`status`, `recent`, `on`, `off`)",
349
+ description:
350
+ "Open memory status, recent memory, memory on/off, worldview, theses, or concerns (`status`, `recent`, `on`, `off`, `worldview`, `theses`, `concerns`)",
327
351
  getArgumentCompletions: (argumentPrefix) => {
328
352
  const prefix = argumentPrefix.trim().toLowerCase();
329
353
  const completions = MEMORY_COMMAND_COMPLETIONS.filter((item) => item.value.startsWith(prefix));
@@ -350,11 +374,26 @@ export function registerMemory(pi: ExtensionAPI): MemoryController {
350
374
  await setEnabled(ctx, false);
351
375
  return;
352
376
  }
377
+ if (action === "worldview") {
378
+ await prefillEditor(ctx, MEMORY_PROMPTS.worldview);
379
+ return;
380
+ }
381
+ if (action === "theses") {
382
+ await prefillEditor(ctx, MEMORY_PROMPTS.theses);
383
+ return;
384
+ }
385
+ if (action === "concerns") {
386
+ await prefillEditor(ctx, MEMORY_PROMPTS.concerns);
387
+ return;
388
+ }
353
389
  if (action === "menu") {
354
390
  await showMenu(ctx);
355
391
  return;
356
392
  }
357
- ctx.ui.notify("Usage: /memory status | /memory recent | /memory on | /memory off", "error");
393
+ ctx.ui.notify(
394
+ "Usage: /memory status | /memory recent | /memory on | /memory off | /memory worldview | /memory theses | /memory concerns",
395
+ "error",
396
+ );
358
397
  },
359
398
  });
360
399
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clubnet/seedclub",
3
- "version": "0.2.41",
3
+ "version": "0.2.42",
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": {
@@ -915,7 +915,7 @@ function renderAuthGateLines(gate) {
915
915
  renderShellTitle(),
916
916
  "",
917
917
  ` ${theme.fg("accent", "Secure access required")}`,
918
- ` ${theme.fg("dim", gate.message || "Seed Club sign-in is required before /login or /model.")}`,
918
+ ` ${theme.fg("dim", gate.message || "Seed Club sign-in is required before model setup.")}`,
919
919
  ];
920
920
  if (gate.error) {
921
921
  lines.push("");
@@ -964,33 +964,27 @@ const MEMORY_COMMAND_COMPLETIONS = [
964
964
  { value: "recent", label: "recent", description: "Show recent memory" },
965
965
  { value: "on", label: "on", description: "Turn memory on" },
966
966
  { value: "off", label: "off", description: "Turn memory off" },
967
+ { value: "worldview", label: "worldview", description: "Open an investing worldview prompt" },
968
+ { value: "theses", label: "theses", description: "Open an investment theses prompt" },
969
+ { value: "concerns", label: "concerns", description: "Open an investment concerns prompt" },
967
970
  ];
968
971
 
969
972
  function getBuiltInSlashCommands() {
970
973
  return [
971
- { name: "new", description: "Start a new session" },
972
- { name: "compact", description: "Compact the current session context" },
973
- { name: "clear", description: "Clear the visible chat transcript" },
974
- { name: "calendar", description: "Open calendar connect and availability actions" },
975
- { name: "research", description: "Open a Seed Club research prompt" },
976
974
  { name: "source", description: "Open a source-backed research prompt" },
975
+ { name: "diligence", description: "Open a Seed Club diligence prompt" },
977
976
  {
978
977
  name: "memory",
979
- description: "Show Seed Club memory status, recent memory, or turn memory on/off",
978
+ description: "Open memory, recall, and investing perspective actions",
980
979
  getArgumentCompletions: (argumentPrefix) => {
981
980
  const prefix = argumentPrefix.trim().toLowerCase();
982
981
  const completions = MEMORY_COMMAND_COMPLETIONS.filter((item) => item.value.startsWith(prefix));
983
982
  return completions.length ? completions : null;
984
983
  },
985
984
  },
986
- { name: "worldview", description: "Open an investing worldview prompt" },
987
- { name: "theses", description: "Open an investment theses prompt" },
988
- { name: "concerns", description: "Open an investment concerns prompt" },
989
- { name: "model", description: "Select the current model" },
990
- { name: "login", description: "Log into a model provider" },
991
- { name: "logout", description: "Log out of a model provider" },
992
- { name: "update", description: "Show the seedclub package update menu" },
985
+ { name: "model", description: "Open active model and inference provider settings" },
993
986
  { name: "more", description: "Show everything else" },
987
+ { name: "disconnect", description: "Disconnect your Seed Club account" },
994
988
  { name: "quit", description: "Exit seedclub" },
995
989
  ];
996
990
  }
@@ -1004,19 +998,43 @@ function getAuxiliaryBuiltInSlashCommands() {
1004
998
  }
1005
999
 
1006
1000
  function getPrimaryExtensionCommandNames() {
1001
+ return new Set([
1002
+ "diligence",
1003
+ "memory",
1004
+ "source",
1005
+ ]);
1006
+ }
1007
+
1008
+ function getSeedclubCommandNames() {
1007
1009
  return new Set([
1008
1010
  "calendar",
1009
- "concerns",
1011
+ "clearcontext",
1012
+ "clip-status",
1013
+ "connect",
1014
+ "connect-calendar",
1015
+ "diligence",
1016
+ "disconnect",
1010
1017
  "memory",
1011
- "research",
1012
1018
  "seedclub",
1013
1019
  "seedenv",
1014
1020
  "source",
1015
- "theses",
1016
- "worldview",
1021
+ "transcripts",
1017
1022
  ]);
1018
1023
  }
1019
1024
 
1025
+ function isSeedclubCommand(command) {
1026
+ if (getSeedclubCommandNames().has(command.name)) return true;
1027
+ const sourcePath = command.sourceInfo?.path;
1028
+ return typeof sourcePath === "string" && sourcePath.includes("/extensions/seedclub/");
1029
+ }
1030
+
1031
+ function formatCommandList(commands) {
1032
+ return commands
1033
+ .filter((command) => shouldShowCommandInList(command.name))
1034
+ .map((command) => `- \`/${command.name}\`${command.description ? ` ${command.description}` : ""}`)
1035
+ .join("\n");
1036
+ }
1037
+
1020
1038
  class SeedclubSurfaceEditor extends CustomEditor {
1021
1039
  constructor(tui, editorTheme, keybindings, options) {
1022
1040
  super(tui, editorTheme, keybindings, {
@@ -1392,6 +1410,7 @@ export class SeedclubInteractiveModeApp {
1392
1410
  .map((command) => ({
1393
1411
  name: command.invocationName,
1394
1412
  description: command.description || "",
1413
+ sourceInfo: command.sourceInfo,
1395
1414
  })) ?? [];
1396
1415
  const promptCommands = (this.session.promptTemplates ?? [])
1397
1416
  .map((template) => ({
@@ -1402,12 +1421,20 @@ export class SeedclubInteractiveModeApp {
1402
1421
  for (const command of [...builtInCommands, ...extensionCommands, ...promptCommands]) {
1403
1422
  if (!deduped.has(command.name)) deduped.set(command.name, command);
1404
1423
  }
1405
- const lines = [...deduped.values()]
1406
- .filter((command) => shouldShowCommandInList(command.name))
1407
- .sort((left, right) => left.name.localeCompare(right.name))
1408
- .map((command) => `- \`/${command.name}\`${command.description ? ` ${command.description}` : ""}`)
1409
- .join("\n");
1410
- this.appendAssistantMarkdown(`## More Commands\n\n${lines}`);
1424
+ const commands = [...deduped.values()].sort((left, right) => left.name.localeCompare(right.name));
1425
+ const seedclubCommands = commands
1426
+ .filter((command) => isSeedclubCommand(command))
1427
+ .filter((command) => shouldShowCommandInList(command.name));
1428
+ const piCommands = commands
1429
+ .filter((command) => !isSeedclubCommand(command))
1430
+ .filter((command) => shouldShowCommandInList(command.name));
1431
+ const seedclubLines = formatCommandList(seedclubCommands);
1432
+ const piLines = formatCommandList(piCommands);
1433
+ const sections = [];
1434
+ if (seedclubLines) sections.push(seedclubLines);
1435
+ if (seedclubLines && piLines) sections.push("---");
1436
+ if (piLines) sections.push(piLines);
1437
+ this.appendAssistantMarkdown(`## More Commands\n\n${sections.join("\n\n")}`);
1411
1438
  this.setStatus("Listed all commands.", "accent");
1412
1439
  }
1413
1440
 
@@ -2001,11 +2028,15 @@ export class SeedclubInteractiveModeApp {
2001
2028
  }
2002
2029
  if (text === "/model" || text.startsWith("/model ")) {
2003
2030
  const filter = text.startsWith("/model ") ? text.slice(7).trim() : "";
2004
- await this.showModelSelector(filter);
2031
+ if (filter) {
2032
+ await this.showModelSelector(filter);
2033
+ } else {
2034
+ await this.showModelMenu();
2035
+ }
2005
2036
  return true;
2006
2037
  }
2007
2038
  if (text === "/login") {
2008
- await this.showOAuthSelector("login");
2039
+ await this.showModelMenu();
2009
2040
  return true;
2010
2041
  }
2011
2042
  if (text.startsWith("/login ")) {
@@ -2013,7 +2044,7 @@ export class SeedclubInteractiveModeApp {
2013
2044
  return true;
2014
2045
  }
2015
2046
  if (text === "/logout") {
2016
- await this.showOAuthSelector("logout");
2047
+ await this.showModelMenu();
2017
2048
  return true;
2018
2049
  }
2019
2050
  if (text === "/new") {
@@ -2052,7 +2083,7 @@ export class SeedclubInteractiveModeApp {
2052
2083
  }
2053
2084
  if (available.length === 0) {
2054
2085
  this.showExtensionNotify(
2055
- "No models are currently available. Use /login or set an API key environment variable first.",
2086
+ "No models are currently available. Use /model to log into an inference provider, or set an API key environment variable first.",
2056
2087
  "warning",
2057
2088
  );
2058
2089
  return;
@@ -2076,6 +2107,27 @@ export class SeedclubInteractiveModeApp {
2076
2107
  }
2077
2108
  }
2078
2109
 
2110
+ async showModelMenu() {
2111
+ const currentModel = this.session.model ?? this.session.state?.model;
2112
+ const activeModel = currentModel ? `${currentModel.provider}/${currentModel.id}` : "none";
2113
+ const choice = await this.showExtensionSelector(`Model\nActive model: ${activeModel}`, [
2114
+ "Change active model",
2115
+ "Login to inference provider",
2116
+ "Logout from inference provider",
2117
+ ]);
2118
+ if (choice === "Change active model") {
2119
+ await this.showModelSelector();
2120
+ return;
2121
+ }
2122
+ if (choice === "Login to inference provider") {
2123
+ await this.showOAuthSelector("login");
2124
+ return;
2125
+ }
2126
+ if (choice === "Logout from inference provider") {
2127
+ await this.showOAuthSelector("logout");
2128
+ }
2129
+ }
2130
+
2079
2131
  async showOAuthSelector(mode) {
2080
2132
  const oauthProviders = this.session.modelRegistry.authStorage.getOAuthProviders();
2081
2133
  if (mode === "logout") {
@@ -2083,7 +2135,7 @@ export class SeedclubInteractiveModeApp {
2083
2135
  .list()
2084
2136
  .filter((provider) => this.session.modelRegistry.authStorage.get(provider)?.type === "oauth");
2085
2137
  if (loggedInProviders.length === 0) {
2086
- this.showExtensionNotify("No OAuth providers are logged in. Use /login first.", "warning");
2138
+ this.showExtensionNotify("No inference providers are logged in. Use /model to log in first.", "warning");
2087
2139
  return;
2088
2140
  }
2089
2141
  }
@@ -2124,7 +2176,7 @@ export class SeedclubInteractiveModeApp {
2124
2176
 
2125
2177
  async showLoginDialog(providerId) {
2126
2178
  if (!providerId) {
2127
- this.showExtensionNotify("Usage: /login <provider> or /login", "warning");
2179
+ this.showExtensionNotify("Use /model to choose an inference provider login.", "warning");
2128
2180
  return;
2129
2181
  }
2130
2182
  const providerInfo = this.session.modelRegistry.authStorage
@@ -24,7 +24,8 @@ export class OAuthSelectorComponent extends Container {
24
24
  this.onCancelCallback = onCancel;
25
25
  this.addChild(new DynamicBorder());
26
26
  this.addChild(new Spacer(1));
27
- const title = mode === "login" ? "Select provider to login:" : "Select provider to logout:";
27
+ const title =
28
+ mode === "login" ? "Select inference provider to log in:" : "Select inference provider to log out:";
28
29
  this.addChild(new TruncatedText(theme.bold(title)));
29
30
  this.addChild(new Spacer(1));
30
31
  this.listContainer = new Container();
@@ -55,7 +56,9 @@ export class OAuthSelectorComponent extends Container {
55
56
  }
56
57
  if (this.allProviders.length === 0) {
57
58
  const message =
58
- this.mode === "login" ? "No OAuth providers available" : "No OAuth providers logged in. Use /login first.";
59
+ this.mode === "login"
60
+ ? "No inference providers available"
61
+ : "No inference providers logged in. Use /model to log in first.";
59
62
  this.listContainer.addChild(new TruncatedText(theme.fg("muted", ` ${message}`), 0, 0));
60
63
  }
61
64
  }