@clubnet/seedclub 0.2.38 → 0.2.40
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.
- package/assets/SYSTEM.md +54 -5
- package/assets/extensions/seedclub/commands/seedclub.ts +116 -0
- package/assets/extensions/seedclub/index.ts +5 -2
- package/assets/extensions/seedclub/memory-client.js +8 -0
- package/assets/extensions/seedclub/memory.ts +290 -28
- package/assets/extensions/seedclub/tools/opportunity-research.ts +429 -0
- package/assets/extensions/seedclub/ui-copy.ts +8 -0
- package/package.json +4 -3
- package/packages/seedclub-tui/src/app/interactive-mode.mjs +96 -9
- package/postinstall.js +6 -1
package/assets/SYSTEM.md
CHANGED
|
@@ -1,11 +1,51 @@
|
|
|
1
|
-
You are a
|
|
1
|
+
You are a Seed Club member agent.
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
You help Seed Club members research, understand, remember, and act on early-stage startup deals. You are a sharp investing thought partner first, and a workflow agent second.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Seed Club's goal is to become the core knowledge base for angel investors and the network they invest from. Every deal conversation should make the member smarter, improve their private investing memory, and, when approved, add useful intelligence to the network.
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
## Core Posture
|
|
8
|
+
|
|
9
|
+
Members should send you every potentially interesting deck, founder pitch, company, link, or idea. They do not need conviction first. If something seems potentially interesting, help them dig in and consider surfacing it.
|
|
10
|
+
|
|
11
|
+
Default stance:
|
|
12
|
+
|
|
13
|
+
- Be concise in chat.
|
|
14
|
+
- Be opinionated, but provisional.
|
|
15
|
+
- Lead with the core insight.
|
|
16
|
+
- Use specifics and numbers.
|
|
17
|
+
- Flag unknowns clearly.
|
|
18
|
+
- Ask only high-leverage follow-up questions.
|
|
19
|
+
- Help the member form their own judgment.
|
|
20
|
+
- Do not act like your opinion is authoritative.
|
|
21
|
+
- Do not tell the member what to invest in.
|
|
22
|
+
|
|
23
|
+
You are allowed to share takes. Your take exists to start a better conversation.
|
|
24
|
+
|
|
25
|
+
Good:
|
|
26
|
+
|
|
27
|
+
"My first read is positive, but the deal turns on venue dependency."
|
|
28
|
+
"The category signal is real; I'd pressure-test whether this team has distribution."
|
|
29
|
+
"I might be wrong if the founder has a non-obvious GTM wedge."
|
|
30
|
+
|
|
31
|
+
Bad:
|
|
32
|
+
|
|
33
|
+
"This is a good deal."
|
|
34
|
+
"You should invest."
|
|
35
|
+
"The right answer is to push."
|
|
36
|
+
|
|
37
|
+
## Opinion Format
|
|
38
|
+
|
|
39
|
+
When sharing a take, use this structure:
|
|
40
|
+
|
|
41
|
+
1. First read: positive / negative / mixed / unclear
|
|
42
|
+
2. Why: 1-3 strongest reasons
|
|
43
|
+
3. Main risk or open question
|
|
44
|
+
4. What to pull on next
|
|
45
|
+
|
|
46
|
+
Example:
|
|
47
|
+
|
|
48
|
+
"First read: worth digging in. The category signal is real, the price is reasonable, and they've shipped more than most pre-seed teams. The main risk is whether Polymarket/Kalshi can build or block the consumer layer. I'd pull on venue relationships first."
|
|
9
49
|
|
|
10
50
|
Operating model:
|
|
11
51
|
|
|
@@ -36,6 +76,15 @@ Seed Club platform policy:
|
|
|
36
76
|
- Stay scoped to the user's platform permissions and do not imply access the user does not have.
|
|
37
77
|
- Do not claim Seed Club platform data is unavailable unless a relevant Seed Club tool actually returns an auth, permission, or not-found failure.
|
|
38
78
|
|
|
79
|
+
Research and review policy:
|
|
80
|
+
|
|
81
|
+
- Treat deal, company, founder, pitch deck, memo, and research workflows as `seed-network` by default unless the user explicitly names another program.
|
|
82
|
+
- When a member drops a deck, memo, PDF, document, or image, use the Seed Club research upload tool without asking for the program and let it create a private research draft first.
|
|
83
|
+
- Do not submit a deal for Seed Club review during the initial upload, even if the user sounds excited. First review the returned document preview and summarize Founder info, Company info, Deal info, and Opportunity Summary.
|
|
84
|
+
- After summarizing, ask for missing founder/contact path, ask amount or explicit ask unknown, founder relationship, and excitement/vouch context before pushing.
|
|
85
|
+
- Submit for Seed Club review only after the member explicitly confirms. Passing or holding should stay private and should not create a shared review handoff.
|
|
86
|
+
- If document preview is partial or failed, say what could not be read and ask targeted questions instead of inventing missing facts.
|
|
87
|
+
|
|
39
88
|
External research policy:
|
|
40
89
|
|
|
41
90
|
- If the user asks an open-ended research question that is not explicitly scoped to Seed Club records, do a fast external research pass before answering.
|
|
@@ -57,6 +57,45 @@ async function setSeedEnvironment(mode: "local" | "prod", ctx: any) {
|
|
|
57
57
|
);
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
async function getDefaultProgramSlug() {
|
|
61
|
+
const session = await getSessionContext();
|
|
62
|
+
if ("error" in session || !Array.isArray(session.program_access)) return "<program-slug>";
|
|
63
|
+
return session.program_access[0]?.program?.slug || "<program-slug>";
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async function prefillEditor(ctx: any, text: string) {
|
|
67
|
+
await new Promise((r) => setTimeout(r, 300));
|
|
68
|
+
ctx.ui.setEditorText(text);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async function showCalendarMenu(ctx: any, deps: SeedclubDeps) {
|
|
72
|
+
const choice = await ctx.ui.select("Calendar", [
|
|
73
|
+
"Connect personal calendar",
|
|
74
|
+
"Disconnect personal calendar",
|
|
75
|
+
"Check availability",
|
|
76
|
+
]);
|
|
77
|
+
|
|
78
|
+
switch (choice) {
|
|
79
|
+
case "Connect personal calendar":
|
|
80
|
+
await deps.connectCalendar(ctx);
|
|
81
|
+
break;
|
|
82
|
+
case "Disconnect personal calendar":
|
|
83
|
+
await prefillEditor(
|
|
84
|
+
ctx,
|
|
85
|
+
"Help me disconnect my personal Google Calendar from Seed Club. Check the currently connected calendar account first and only proceed with a real disconnect if a supported Seed Club tool or route exists; otherwise tell me what manual action is needed.",
|
|
86
|
+
);
|
|
87
|
+
break;
|
|
88
|
+
case "Check availability": {
|
|
89
|
+
const defaultProgramSlug = await getDefaultProgramSlug();
|
|
90
|
+
await prefillEditor(
|
|
91
|
+
ctx,
|
|
92
|
+
`Check meeting availability for program ${defaultProgramSlug}. Ask me for the target date first if I have not provided one, then use configured availability slots exactly.`,
|
|
93
|
+
);
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
60
99
|
export function registerSeedclubCommand(pi: ExtensionAPI, deps: SeedclubDeps) {
|
|
61
100
|
pi.registerCommand("connect", {
|
|
62
101
|
description: "Connect your Seed Club account",
|
|
@@ -105,6 +144,83 @@ export function registerSeedclubCommand(pi: ExtensionAPI, deps: SeedclubDeps) {
|
|
|
105
144
|
},
|
|
106
145
|
});
|
|
107
146
|
|
|
147
|
+
pi.registerCommand("calendar", {
|
|
148
|
+
description: "Open calendar connect, disconnect, and availability actions",
|
|
149
|
+
handler: async (args, ctx) => {
|
|
150
|
+
const action = args?.trim().toLowerCase();
|
|
151
|
+
if (action === "connect") {
|
|
152
|
+
await deps.connectCalendar(ctx);
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
if (action === "disconnect") {
|
|
156
|
+
await prefillEditor(
|
|
157
|
+
ctx,
|
|
158
|
+
"Help me disconnect my personal Google Calendar from Seed Club. Check the currently connected calendar account first and only proceed with a real disconnect if a supported Seed Club tool or route exists; otherwise tell me what manual action is needed.",
|
|
159
|
+
);
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
if (action === "availability") {
|
|
163
|
+
const defaultProgramSlug = await getDefaultProgramSlug();
|
|
164
|
+
await prefillEditor(
|
|
165
|
+
ctx,
|
|
166
|
+
`Check meeting availability for program ${defaultProgramSlug}. Ask me for the target date first if I have not provided one, then use configured availability slots exactly.`,
|
|
167
|
+
);
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
await showCalendarMenu(ctx, deps);
|
|
171
|
+
},
|
|
172
|
+
});
|
|
173
|
+
|
|
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
|
+
pi.registerCommand("source", {
|
|
185
|
+
description: "Open a source-backed research prompt",
|
|
186
|
+
handler: async (_args, ctx) => {
|
|
187
|
+
await prefillEditor(
|
|
188
|
+
ctx,
|
|
189
|
+
"Research this using source-backed evidence. Gather and verify primary or reputable sources, cite source URLs inline, separate Seed Club records from external sources, and flag unknowns clearly.",
|
|
190
|
+
);
|
|
191
|
+
},
|
|
192
|
+
});
|
|
193
|
+
|
|
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",
|
|
216
|
+
handler: async (_args, ctx) => {
|
|
217
|
+
await prefillEditor(
|
|
218
|
+
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.",
|
|
220
|
+
);
|
|
221
|
+
},
|
|
222
|
+
});
|
|
223
|
+
|
|
108
224
|
pi.registerCommand("seedclub", {
|
|
109
225
|
description: "Seed Club",
|
|
110
226
|
handler: async (args, ctx) => {
|
|
@@ -32,6 +32,7 @@ import {
|
|
|
32
32
|
} from "./recent-entities.js";
|
|
33
33
|
import { getCurrentUser, getSessionContext, registerUtilityTools } from "./tools/utility.js";
|
|
34
34
|
import { registerCrmTools } from "./tools/crm.js";
|
|
35
|
+
import { registerOpportunityResearchTools } from "./tools/opportunity-research.js";
|
|
35
36
|
import { registerMeetingTools } from "./tools/meetings.js";
|
|
36
37
|
import { registerMediaTools } from "./tools/media.js";
|
|
37
38
|
import { registerWebTools } from "./tools/web.js";
|
|
@@ -208,6 +209,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
208
209
|
// Tools
|
|
209
210
|
registerUtilityTools(pi);
|
|
210
211
|
registerCrmTools(pi);
|
|
212
|
+
registerOpportunityResearchTools(pi);
|
|
211
213
|
registerMeetingTools(pi);
|
|
212
214
|
registerMediaTools(pi);
|
|
213
215
|
registerWebTools(pi);
|
|
@@ -433,6 +435,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
433
435
|
|
|
434
436
|
await applyConnectedStatus(ctx, user);
|
|
435
437
|
void refreshPromptContext();
|
|
438
|
+
void memory.enableByInstallDefault(ctx);
|
|
436
439
|
markAuthComplete(getPostAuthInstruction(ctx));
|
|
437
440
|
return user;
|
|
438
441
|
}
|
|
@@ -451,7 +454,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
451
454
|
markAuthInProgress({ message: "Checking Seed Club access..." });
|
|
452
455
|
}
|
|
453
456
|
void ensureSeedclubAuthenticated(ctx).then((authenticated) => {
|
|
454
|
-
if (authenticated) void memory.
|
|
457
|
+
if (authenticated) void memory.enableByInstallDefault(ctx);
|
|
455
458
|
}).catch((error) => {
|
|
456
459
|
const message = error instanceof Error ? error.message : "Unable to verify Seed Club access.";
|
|
457
460
|
markAuthRequired({
|
|
@@ -679,7 +682,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
679
682
|
await storeToken(token, result.email, apiBase, { authBase, name: result.name });
|
|
680
683
|
await applyConnectedStatus(ctx, result);
|
|
681
684
|
await refreshPromptContext();
|
|
682
|
-
void memory.
|
|
685
|
+
void memory.enableByInstallDefault(ctx);
|
|
683
686
|
const nextStep = getPostAuthInstruction(ctx);
|
|
684
687
|
markAuthComplete(nextStep);
|
|
685
688
|
if (options?.notifyOnSuccess) {
|
|
@@ -48,6 +48,14 @@ export async function fetchMemoryContextFromApi(apiClient, payload, timeoutMs) {
|
|
|
48
48
|
);
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
export async function fetchMemoryRecentFromApi(apiClient, payload, timeoutMs) {
|
|
52
|
+
return requestWithTimeout(
|
|
53
|
+
apiClient.post("/agent-memory/recent", payload),
|
|
54
|
+
timeoutMs,
|
|
55
|
+
"Seed Club recent memory",
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
51
59
|
export async function writeMemoryEventToApi(apiClient, payload, timeoutMs) {
|
|
52
60
|
return requestWithTimeout(
|
|
53
61
|
apiClient.post("/agent-memory/events", payload),
|