@clubnet/seedclub 0.2.17 → 0.2.19

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.
Files changed (36) hide show
  1. package/README.md +88 -27
  2. package/assets/extensions/seedclub/api-client.ts +24 -6
  3. package/assets/extensions/seedclub/auth.ts +181 -10
  4. package/assets/extensions/seedclub/branding.ts +13 -0
  5. package/assets/extensions/seedclub/commands/clip-status.ts +187 -0
  6. package/assets/extensions/seedclub/commands/seedclub.ts +169 -25
  7. package/assets/extensions/seedclub/commands/transcript-intent.ts +998 -0
  8. package/assets/extensions/seedclub/commands/transcripts.ts +383 -0
  9. package/assets/extensions/seedclub/index.ts +218 -27
  10. package/assets/extensions/seedclub/package-lock.json +8 -1
  11. package/assets/extensions/seedclub/package.json +5 -2
  12. package/assets/extensions/seedclub/tool-utils.ts +45 -3
  13. package/assets/extensions/seedclub/tools/crm.ts +183 -0
  14. package/assets/extensions/seedclub/tools/media.ts +217 -0
  15. package/assets/extensions/seedclub/tools/meetings.ts +1053 -0
  16. package/assets/extensions/seedclub/tools/utility.ts +106 -9
  17. package/assets/extensions/seedclub-ui/editor.ts +16 -29
  18. package/assets/extensions/seedclub-ui/footer.ts +1 -3
  19. package/assets/extensions/seedclub-ui/state.ts +10 -1
  20. package/assets/extensions/seedclub-ui/welcome.ts +274 -85
  21. package/assets/theme/dark.json +47 -59
  22. package/assets/theme/light.json +49 -61
  23. package/bin/cli.js +380 -23
  24. package/bin/pi-main-launcher.js +29 -0
  25. package/package.json +8 -4
  26. package/postinstall.js +1 -1
  27. package/assets/extensions/seedclub/commands/add.ts +0 -601
  28. package/assets/extensions/seedclub/commands/extract.ts +0 -123
  29. package/assets/extensions/seedclub/commands/signals.ts +0 -86
  30. package/assets/extensions/seedclub/commands/sort.ts +0 -91
  31. package/assets/extensions/seedclub/dia-cookies.ts +0 -126
  32. package/assets/extensions/seedclub/extraction/capture.ts +0 -47
  33. package/assets/extensions/seedclub/extraction/schema.ts +0 -117
  34. package/assets/extensions/seedclub/tools/extractions.ts +0 -517
  35. package/assets/extensions/seedclub/tools/signals.ts +0 -275
  36. package/assets/extensions/seedclub/twitter-client.ts +0 -277
package/README.md CHANGED
@@ -1,19 +1,19 @@
1
1
  # seedclub
2
2
 
3
- The Human+ Venture Network AI agent for deal sourcing, research, and signal tracking.
3
+ Seed Club's pi-based agent for authenticated access to Seed Club programs, CRM, meetings, media, and headlines.
4
4
 
5
5
  Requirements: Node.js 22+
6
6
 
7
- ## Source of truth
7
+ ## What it is
8
8
 
9
- This repo is the source of truth for the published Seed Club wrapper package:
9
+ `seedclub` is the Seed Club distribution of pi. This repo provides the `seedclub` launcher, installs the Seed Club theme and extensions, and adds Seed Club-specific workflows for connecting your account, using CRM/meeting/media tools, retrieving transcripts, and working with Seed Club-backed app surfaces.
10
+
11
+ This repo is the source of truth for the published package:
10
12
 
11
13
  - GitHub repo: `seedclub/seedclub-agent`
12
14
  - npm package: `@clubnet/seedclub`
13
15
  - user-facing command: `seedclub`
14
16
 
15
- This is the Pi wrapper repo, not the older extracted full-agent monorepo.
16
-
17
17
  ## Install
18
18
 
19
19
  ```bash
@@ -26,7 +26,15 @@ Then:
26
26
  seedclub
27
27
  ```
28
28
 
29
- Use `/login` to set up your API keys.
29
+ ### First run
30
+
31
+ On a fresh install, the normal setup flow is:
32
+
33
+ 1. `/login` to sign in to a model provider such as Anthropic, OpenAI, or Gemini
34
+ 2. `/model` to choose the model you want to use
35
+ 3. `/connect` to connect your Seed Club account
36
+
37
+ After that, run `/seedclub` to open the main Seed Club menu.
30
38
 
31
39
  ### Alternative: curl | bash
32
40
 
@@ -34,9 +42,9 @@ Use `/login` to set up your API keys.
34
42
  curl -fsSL https://raw.githubusercontent.com/seedclub/seedclub-agent/main/install.sh | bash
35
43
  ```
36
44
 
37
- ### Team onboarding (private package)
45
+ ### Internal install auth
38
46
 
39
- seedclub is published to npmjs as a private package in the `@clubnet` org.
47
+ `@clubnet/seedclub` is currently a private npm package. This auth is only for installing or updating the package from npm. It is separate from `/login` and `/connect` inside the app.
40
48
 
41
49
  Fast path:
42
50
 
@@ -54,15 +62,56 @@ echo "//registry.npmjs.org/:_authToken=YOUR_NPM_TOKEN" >> ~/.npmrc
54
62
 
55
63
  Then `npm install -g @clubnet/seedclub` works.
56
64
 
65
+ ## Core workflow
66
+
67
+ The normal interactive flow is:
68
+
69
+ 1. Start the app with `seedclub`
70
+ 2. Complete `/login`, `/model`, and `/connect` if this is your first run
71
+ 3. Open `/seedclub`
72
+ 4. Choose the CRM, meetings, media, recordings, or transcript workflow you need
73
+
57
74
  ## Commands
58
75
 
59
76
  | Command | What it does |
60
77
  |---|---|
61
- | `/seedclub` | Main menu connect, add signals, sort signals |
62
- | `/add <url>` | Add a signal (any URL) |
63
- | `/sort` | Sort signals into angel.md values |
78
+ | `/login` | Sign in to a model provider for the underlying agent |
79
+ | `/model` | Choose which model to use |
80
+ | `/connect` | Connect your Seed Club account |
81
+ | `/seedclub` | Main menu — connect, inspect access, and jump into CRM/meetings/media/headlines workflows |
82
+ | `/transcripts` | Export transcript VTT files with filters (date, person, time, output dir) |
64
83
  | `seedclub setup-auth` | Configure npm auth for npmjs private package access in `~/.npmrc` |
65
84
 
85
+ Natural-language transcript retrieval is also supported (no slash command required). Examples: `download vibhu transcripts from 11am`, `i need transcripts for all guests on 11am last week`. Seed Club will run metadata-first export confirmation and then write VTT files.
86
+
87
+ ## Auth
88
+
89
+ There are two separate auth layers in the product:
90
+
91
+ 1. Model auth: `/login`
92
+ This signs you into the LLM provider you want the agent to use.
93
+ 2. Seed Club auth: `/connect`
94
+ This connects the CLI to your Seed Club account so Seed Club tools and commands can read and write account data.
95
+
96
+ `/seedclub` is the main entry point for Seed Club actions. If you are not connected yet, it will start the Seed Club connect flow automatically.
97
+
98
+ Power-user env overrides:
99
+
100
+ ```bash
101
+ export SEEDCLUB_API_URL=http://localhost:3001
102
+ export SEEDCLUB_AUTH_URL=http://localhost:3000
103
+ export SEEDCLUB_ACCESS_TOKEN=<bearer-token>
104
+ ```
105
+
106
+ Production defaults are already built into the auth extension:
107
+
108
+ ```bash
109
+ SEEDCLUB_API_URL=https://api.seedclub.com
110
+ SEEDCLUB_AUTH_URL=https://auth.seedclub.com
111
+ ```
112
+
113
+ You do not need to export those for normal use. Only set env vars when you want to override them for local/dev.
114
+
66
115
  ## How it works
67
116
 
68
117
  seedclub is an npm package (`@clubnet/seedclub`) that wraps [pi](https://github.com/badlogic/pi-mono) as a dependency. Installing the package globally gives you the `seedclub` command.
@@ -98,9 +147,9 @@ seedclub pins versions in `package.json`:
98
147
 
99
148
  ```json
100
149
  {
101
- "version": "0.2.0",
150
+ "version": "0.2.18",
102
151
  "dependencies": {
103
- "@mariozechner/pi-coding-agent": "0.52.12"
152
+ "@mariozechner/pi-coding-agent": "0.65.2"
104
153
  }
105
154
  }
106
155
  ```
@@ -124,6 +173,8 @@ It's a standard pi theme with 51 color tokens. Edit it to change any visual aspe
124
173
 
125
174
  The `vars` block at the top defines reusable colors (e.g. `brand: "#00C853"`) that are referenced throughout `colors`. To change the brand color, just update `vars.brand`.
126
175
 
176
+ Seed Club keeps product-level names in `vars` (`editorBg`, `messageBg`, `successBg`, `errorBg`) and maps pi's required tokens to those names (`selectedBg`, `userMessageBg`, `toolPendingBg`, etc.). The CLI probes the terminal background with OSC 11 and sets `COLORFGBG` before pi starts when no explicit theme is configured, so light/dark defaults track the actual terminal window instead of pi's fallback.
177
+
127
178
  **Hot reload:** Edit the theme file while seedclub is running and it reloads instantly.
128
179
 
129
180
  Colors can be hex (`"#00C853"`), 256-color palette index (`242`), a reference to a `vars` entry (`"brand"`), or empty string (`""`) for the terminal default.
@@ -165,36 +216,46 @@ The `assets/` directory contains the canonical source for extensions and themes.
165
216
 
166
217
  ### Updating pi
167
218
 
168
- When a new pi version comes out:
219
+ When a new pi version comes out, upgrading it for Seed Club users means shipping a new `@clubnet/seedclub` package that depends on the newer pi release.
169
220
 
170
- 1. **Test it locally:**
221
+ Recommended flow:
222
+
223
+ 1. **Update and test locally:**
171
224
  ```bash
172
- # Update the dependency
173
225
  npm install @mariozechner/pi-coding-agent@NEW_VERSION
174
226
  npm install -g ./
175
- seedclub # test everything works
227
+ seedclub version
228
+ seedclub
176
229
  ```
177
230
 
178
- 2. **Bump versions:**
179
- Update the `package.json` dependency and package version:
231
+ Verify the wrapper still launches, the Seed Club UI loads, and the core flows you care about still work.
232
+
233
+ 2. **Commit the dependency change and bump the package version:**
234
+ The pi version is carried by this package, so users only receive it once the Seed Club package version is bumped and released.
180
235
  ```json
181
236
  {
182
237
  "version": "0.3.0",
183
238
  "dependencies": {
184
- "@mariozechner/pi-coding-agent": "0.52.12"
239
+ "@mariozechner/pi-coding-agent": "NEW_VERSION"
185
240
  }
186
241
  }
187
242
  ```
188
243
 
189
- 3. **Publish:**
244
+ 3. **Push the release:**
190
245
  ```bash
191
246
  git add -A
192
- git commit -m "bump pi to 0.52.12"
193
- git push
194
- npm publish
247
+ git commit -m "bump pi to NEW_VERSION"
248
+ npm version patch|minor|major
249
+ git push --follow-tags
250
+ ```
251
+
252
+ 4. **Users pull the update:**
253
+ Users get the newer pi runtime when they run:
254
+ ```bash
255
+ seedclub update
195
256
  ```
196
257
 
197
- Users get the update when they run `seedclub update`.
258
+ A fresh `npm install -g @clubnet/seedclub` also installs the new packaged pi version.
198
259
 
199
260
  ### Updating extensions
200
261
 
@@ -208,8 +269,8 @@ seedclub # test
208
269
  # When ready, bump package version and publish
209
270
  git add -A
210
271
  git commit -m "update extensions"
211
- git push
212
- npm publish
272
+ npm version patch|minor|major
273
+ git push --follow-tags
213
274
  ```
214
275
 
215
276
  For reproducible extension dependency installs, commit `assets/extensions/seedclub/package-lock.json` and keep it in sync when changing extension deps.
@@ -52,12 +52,26 @@ interface RequestOptions {
52
52
  params?: Record<string, string | number | undefined>;
53
53
  }
54
54
 
55
+ async function shouldClearCredentialsAfterUnauthorized(apiBase: string, token: string): Promise<boolean> {
56
+ try {
57
+ const url = new URL("/session/context", apiBase.endsWith("/") ? apiBase : `${apiBase}/`);
58
+ const response = await fetch(url.toString(), {
59
+ method: "GET",
60
+ headers: { Authorization: `Bearer ${token}` },
61
+ });
62
+ return response.status === 401;
63
+ } catch {
64
+ return false;
65
+ }
66
+ }
67
+
55
68
  async function apiRequest<T>(endpoint: string, options: RequestOptions = {}): Promise<T> {
56
69
  const { method = "GET", body, params } = options;
57
70
  const token = await getAuthToken();
58
71
  const apiBase = cachedApiBase || getApiBase();
59
72
 
60
- const url = new URL(`/api/mcp${endpoint}`, apiBase);
73
+ const path = endpoint.startsWith("/") ? endpoint : `/${endpoint}`;
74
+ const url = new URL(path, apiBase.endsWith("/") ? apiBase : `${apiBase}/`);
61
75
  if (params) {
62
76
  for (const [key, value] of Object.entries(params)) {
63
77
  if (value !== undefined) url.searchParams.set(key, String(value));
@@ -70,11 +84,6 @@ async function apiRequest<T>(endpoint: string, options: RequestOptions = {}): Pr
70
84
  body: body ? JSON.stringify(body) : undefined,
71
85
  });
72
86
 
73
- if (response.status === 401) {
74
- await clearCredentials();
75
- throw new ApiError(401, "Token expired or revoked. Run /seedclub to reconnect.");
76
- }
77
-
78
87
  const text = await response.text();
79
88
  let data: any;
80
89
  try {
@@ -86,6 +95,15 @@ async function apiRequest<T>(endpoint: string, options: RequestOptions = {}): Pr
86
95
  throw new ApiError(response.status, `Invalid JSON response from server: ${text.slice(0, 200)}`);
87
96
  }
88
97
 
98
+ if (response.status === 401) {
99
+ const shouldClear = await shouldClearCredentialsAfterUnauthorized(apiBase, token);
100
+ if (shouldClear) {
101
+ await clearCredentials();
102
+ throw new ApiError(401, "Token expired or revoked. Run /seedclub to reconnect.");
103
+ }
104
+ throw new ApiError(401, data?.error || "Request failed (401)", data?.details);
105
+ }
106
+
89
107
  if (!response.ok) {
90
108
  throw new ApiError(response.status, data.error || `Request failed (${response.status})`, data.details);
91
109
  }
@@ -1,19 +1,26 @@
1
1
  /**
2
2
  * Token storage for Seed Club.
3
3
  *
4
- * Priority: SEEDCLUB_TOKEN env var > stored token file.
4
+ * Priority: SEEDCLUB_ACCESS_TOKEN / SEEDCLUB_TOKEN env var > stored token file.
5
5
  * Use /seedclub to connect.
6
6
  */
7
7
 
8
8
  import { chmod, mkdir, readFile, unlink, writeFile } from "node:fs/promises";
9
+ import { readFileSync } from "node:fs";
9
10
  import { homedir } from "node:os";
10
11
  import { join } from "node:path";
11
12
 
12
13
  const CONFIG_DIR = join(homedir(), ".config", "seedclub");
13
14
  const TOKEN_FILE = join(CONFIG_DIR, "token");
15
+ const BASES_FILE = join(CONFIG_DIR, "bases.json");
14
16
  const LEGACY_CONFIG_DIR = join(homedir(), ".config", "looseleaf");
15
17
  const LEGACY_TOKEN_FILE = join(LEGACY_CONFIG_DIR, "token");
16
- const DEFAULT_API_BASE = "https://looseleaf-rouge.vercel.app";
18
+ const DEFAULT_API_BASE = "https://api.seedclub.com";
19
+ const DEFAULT_AUTH_BASE = "https://auth.seedclub.com";
20
+ const LOCAL_API_BASE = "http://localhost:3001";
21
+ const LOCAL_AUTH_BASE = "http://localhost:3000";
22
+
23
+ export type SeedclubEnvironmentMode = "local" | "prod" | "custom";
17
24
 
18
25
  export interface StoredToken {
19
26
  token: string;
@@ -21,33 +28,134 @@ export interface StoredToken {
21
28
  name?: string;
22
29
  createdAt: string;
23
30
  apiBase: string;
31
+ authBase?: string;
32
+ }
33
+
34
+ export interface StoredBases {
35
+ apiBase: string;
36
+ authBase: string;
37
+ mode: SeedclubEnvironmentMode;
38
+ updatedAt: string;
24
39
  }
25
40
 
26
41
  let _cachedApiBase: string | null = null;
42
+ let _cachedAuthBase: string | null = null;
43
+ let _cachedBases: StoredBases | null | undefined = undefined;
44
+
45
+ function shouldPreferLocalBases(): boolean {
46
+ return (
47
+ process.env.SEEDCLUB_LOCAL === "1" ||
48
+ process.env.SEEDCLUB_LOCAL === "true" ||
49
+ process.env.SEEDCLUB_ENV === "local"
50
+ );
51
+ }
52
+
53
+ function tryReadStoredBasesSync(): StoredBases | null {
54
+ if (_cachedBases !== undefined) {
55
+ return _cachedBases;
56
+ }
57
+
58
+ try {
59
+ const content = readFileSync(BASES_FILE, "utf-8");
60
+ const stored = JSON.parse(content) as StoredBases;
61
+ if (
62
+ !stored ||
63
+ typeof stored.apiBase !== "string" ||
64
+ typeof stored.authBase !== "string" ||
65
+ typeof stored.mode !== "string"
66
+ ) {
67
+ _cachedBases = null;
68
+ return null;
69
+ }
70
+ _cachedBases = stored;
71
+ return stored;
72
+ } catch {
73
+ _cachedBases = null;
74
+ return null;
75
+ }
76
+ }
27
77
 
28
78
  export function getApiBase(): string {
29
- if (process.env.SEEDCLUB_API || process.env.SEED_NETWORK_API)
30
- return process.env.SEEDCLUB_API || process.env.SEED_NETWORK_API!;
79
+ if (
80
+ process.env.SEEDCLUB_API_URL ||
81
+ process.env.SEEDCLUB_API ||
82
+ process.env.SEED_NETWORK_API
83
+ )
84
+ return process.env.SEEDCLUB_API_URL || process.env.SEEDCLUB_API || process.env.SEED_NETWORK_API!;
85
+ if (shouldPreferLocalBases()) return LOCAL_API_BASE;
86
+ const storedBases = tryReadStoredBasesSync();
87
+ if (storedBases?.apiBase) return storedBases.apiBase;
31
88
  if (_cachedApiBase) return _cachedApiBase;
32
89
  return DEFAULT_API_BASE;
33
90
  }
34
91
 
92
+ export function getAuthBase(): string {
93
+ if (
94
+ process.env.SEEDCLUB_AUTH_URL ||
95
+ process.env.SEEDCLUB_AUTH ||
96
+ process.env.SEED_NETWORK_AUTH
97
+ )
98
+ return process.env.SEEDCLUB_AUTH_URL || process.env.SEEDCLUB_AUTH || process.env.SEED_NETWORK_AUTH!;
99
+ if (shouldPreferLocalBases()) return LOCAL_AUTH_BASE;
100
+ const storedBases = tryReadStoredBasesSync();
101
+ if (storedBases?.authBase) return storedBases.authBase;
102
+ if (_cachedAuthBase) return _cachedAuthBase;
103
+ return DEFAULT_AUTH_BASE;
104
+ }
105
+
35
106
  export function setCachedApiBase(apiBase: string): void {
36
107
  _cachedApiBase = apiBase;
37
108
  }
38
109
 
110
+ export function setCachedAuthBase(authBase: string): void {
111
+ _cachedAuthBase = authBase;
112
+ }
113
+
39
114
  export function clearCachedApiBase(): void {
40
115
  _cachedApiBase = null;
41
116
  }
42
117
 
118
+ export function clearCachedAuthBase(): void {
119
+ _cachedAuthBase = null;
120
+ }
121
+
122
+ export function getDefaultBases(mode: "local" | "prod"): { apiBase: string; authBase: string } {
123
+ if (mode === "local") {
124
+ return {
125
+ apiBase: LOCAL_API_BASE,
126
+ authBase: LOCAL_AUTH_BASE,
127
+ };
128
+ }
129
+
130
+ return {
131
+ apiBase: DEFAULT_API_BASE,
132
+ authBase: DEFAULT_AUTH_BASE,
133
+ };
134
+ }
135
+
43
136
  async function tryReadTokenFile(path: string): Promise<StoredToken | null> {
44
137
  try {
45
138
  const content = await readFile(path, "utf-8");
46
139
  const stored = JSON.parse(content) as StoredToken;
47
- if (!stored.token || !stored.token.startsWith("sn_")) return null;
48
- if (stored.apiBase && !process.env.SEEDCLUB_API) {
140
+ if (!stored.token || typeof stored.token !== "string" || !stored.token.trim()) return null;
141
+ if (
142
+ stored.apiBase &&
143
+ !shouldPreferLocalBases() &&
144
+ !process.env.SEEDCLUB_API_URL &&
145
+ !process.env.SEEDCLUB_API &&
146
+ !process.env.SEED_NETWORK_API
147
+ ) {
49
148
  _cachedApiBase = stored.apiBase;
50
149
  }
150
+ if (
151
+ stored.authBase &&
152
+ !shouldPreferLocalBases() &&
153
+ !process.env.SEEDCLUB_AUTH_URL &&
154
+ !process.env.SEEDCLUB_AUTH &&
155
+ !process.env.SEED_NETWORK_AUTH
156
+ ) {
157
+ _cachedAuthBase = stored.authBase;
158
+ }
51
159
  return stored;
52
160
  } catch {
53
161
  return null;
@@ -61,24 +169,85 @@ export async function getStoredToken(): Promise<StoredToken | null> {
61
169
  return await tryReadTokenFile(LEGACY_TOKEN_FILE);
62
170
  }
63
171
 
172
+ export async function getStoredBases(): Promise<StoredBases | null> {
173
+ try {
174
+ const content = await readFile(BASES_FILE, "utf-8");
175
+ const stored = JSON.parse(content) as StoredBases;
176
+ if (
177
+ !stored ||
178
+ typeof stored.apiBase !== "string" ||
179
+ typeof stored.authBase !== "string" ||
180
+ typeof stored.mode !== "string"
181
+ ) {
182
+ return null;
183
+ }
184
+ _cachedBases = stored;
185
+ return stored;
186
+ } catch {
187
+ return null;
188
+ }
189
+ }
190
+
64
191
  export async function getToken(): Promise<string | null> {
65
- if (process.env.SEEDCLUB_TOKEN || process.env.SEED_NETWORK_TOKEN)
66
- return (process.env.SEEDCLUB_TOKEN || process.env.SEED_NETWORK_TOKEN)!;
192
+ if (process.env.SEEDCLUB_ACCESS_TOKEN || process.env.SEEDCLUB_TOKEN || process.env.SEED_NETWORK_TOKEN)
193
+ return (process.env.SEEDCLUB_ACCESS_TOKEN || process.env.SEEDCLUB_TOKEN || process.env.SEED_NETWORK_TOKEN)!;
67
194
  const stored = await getStoredToken();
68
195
  return stored?.token ?? null;
69
196
  }
70
197
 
198
+ export async function storeBases(
199
+ apiBase: string,
200
+ authBase: string,
201
+ mode: SeedclubEnvironmentMode = "custom",
202
+ ): Promise<void> {
203
+ const stored: StoredBases = {
204
+ apiBase,
205
+ authBase,
206
+ mode,
207
+ updatedAt: new Date().toISOString(),
208
+ };
209
+ _cachedApiBase = apiBase;
210
+ _cachedAuthBase = authBase;
211
+ _cachedBases = stored;
212
+ await mkdir(CONFIG_DIR, { recursive: true, mode: 0o700 });
213
+ await writeFile(BASES_FILE, JSON.stringify(stored, null, 2), { mode: 0o600 });
214
+ try {
215
+ await chmod(BASES_FILE, 0o600);
216
+ } catch {}
217
+ }
218
+
219
+ export async function clearStoredBases(): Promise<boolean> {
220
+ _cachedBases = null;
221
+ try {
222
+ await unlink(BASES_FILE);
223
+ return true;
224
+ } catch {
225
+ return false;
226
+ }
227
+ }
228
+
71
229
  export async function storeToken(
72
230
  token: string,
73
231
  email: string,
74
232
  apiBase: string,
75
- options?: { name?: string },
233
+ options?: { authBase?: string; name?: string },
76
234
  ): Promise<void> {
235
+ _cachedApiBase = apiBase;
236
+ if (options?.authBase) {
237
+ _cachedAuthBase = options.authBase;
238
+ }
77
239
  await mkdir(CONFIG_DIR, { recursive: true, mode: 0o700 });
78
240
  await writeFile(
79
241
  TOKEN_FILE,
80
242
  JSON.stringify(
81
- { token, email, name: options?.name, createdAt: new Date().toISOString(), apiBase },
243
+ {
244
+ token,
245
+ email,
246
+ name: options?.name,
247
+ createdAt: new Date().toISOString(),
248
+ apiBase,
249
+ authBase: options?.authBase,
250
+ },
82
251
  null,
83
252
  2,
84
253
  ),
@@ -90,6 +259,8 @@ export async function storeToken(
90
259
  }
91
260
 
92
261
  export async function clearStoredToken(): Promise<boolean> {
262
+ _cachedApiBase = null;
263
+ _cachedAuthBase = null;
93
264
  try {
94
265
  await unlink(TOKEN_FILE);
95
266
  return true;
@@ -6,6 +6,19 @@ IMPORTANT BRANDING RULES:
6
6
  - Never mention the name \"pi\" in any response.
7
7
  - If the user mentions \"pi\", interpret it as Seed Club and respond using \"Seed Club\".
8
8
  - When summarizing intent or describing the system, use Seed Club wording only.
9
+
10
+ IMPORTANT SEED CLUB API ROUTING:
11
+ - For transcript-first discovery/review asks (not file retrieval), call seedclub_list_meeting_transcripts first with the program slug and limit 20.
12
+ - For transcript inventory questions like "what was the last day we have transcripts for 11am?", keep the user prompt in the normal agent flow and answer with the date plus the full transcript_for names from returned rows.
13
+ - Treat conversation-level transcripts as valid 11am interview/show transcripts. For "latest/last transcript" questions, use seedclub_list_meeting_transcripts.latest.overall first; only use latest.meeting when the user explicitly asks for meeting-level records.
14
+ - Treat "on 11am" or "for 11am" as the 11am program slug when that program is accessible, not as an 11am time filter.
15
+ - If the user asks for "all transcripts" and the tool returns a limited page, summarize the returned transcript rows and say you can fetch full text for selected rows. Do not claim exhaustive coverage unless the API result proves it.
16
+ - Use includeFullText only after narrowing to rows the user wants to inspect.
17
+ - Use seedclub_list_program_media_assets(assetKind=full_conversation) only when the user asks for full conversations or media assets. Do not use it as the default source for transcript inventory.
18
+ - For transcript retrieval asks (e.g., "I need transcripts...", "get transcripts...", guest/date transcript pulls), first use chat and metadata tools to establish exact constraints when needed, then call seedclub_export_transcripts with the user's original request. This preserves the user prompt in context, confirms destination, then writes VTT files.
19
+ - Treat short transcript artifact prompts like "today's transcripts", "2pm transcripts", "Tuesday VTTs", or "recent captions" as local transcript export tool requests, not inline transcript display requests.
20
+ - Do not paste full transcript JSON or raw session context into the reply unless the user explicitly asks for raw data.
21
+ - Use seedclub_list_show_recordings for show recording URLs, then follow transcript event_date only when needed.
9
22
  `;
10
23
 
11
24
  const BRAND_REGEX = /\bpi\b/gi;