@lucasygu/yc-cli 0.1.0
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/SKILL.md +120 -0
- package/dist/cli.d.ts +9 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +260 -0
- package/dist/cli.js.map +1 -0
- package/dist/lib/client.d.ts +91 -0
- package/dist/lib/client.d.ts.map +1 -0
- package/dist/lib/client.js +186 -0
- package/dist/lib/client.js.map +1 -0
- package/dist/lib/cookies.d.ts +19 -0
- package/dist/lib/cookies.d.ts.map +1 -0
- package/dist/lib/cookies.js +180 -0
- package/dist/lib/cookies.js.map +1 -0
- package/dist/lib/queries.d.ts +20 -0
- package/dist/lib/queries.d.ts.map +1 -0
- package/dist/lib/queries.js +118 -0
- package/dist/lib/queries.js.map +1 -0
- package/package.json +58 -0
- package/scripts/postinstall.js +129 -0
- package/scripts/preuninstall.js +49 -0
package/SKILL.md
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Manage Y Combinator Startup School from the terminal — weekly updates, dashboard, progress tracking
|
|
3
|
+
allowed-tools: Bash, Read, Write
|
|
4
|
+
name: yc
|
|
5
|
+
version: 0.1.0
|
|
6
|
+
metadata:
|
|
7
|
+
openclaw:
|
|
8
|
+
requires:
|
|
9
|
+
bins:
|
|
10
|
+
- yc
|
|
11
|
+
install:
|
|
12
|
+
- kind: node
|
|
13
|
+
package: "@lucasygu/yc-cli"
|
|
14
|
+
bins: [yc]
|
|
15
|
+
os: [macos]
|
|
16
|
+
homepage: https://github.com/lucasygu/yc-cli
|
|
17
|
+
tags:
|
|
18
|
+
- ycombinator
|
|
19
|
+
- startup-school
|
|
20
|
+
- productivity
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
# YC CLI — Y Combinator Startup School
|
|
24
|
+
|
|
25
|
+
CLI tool for managing your YC Startup School journey. Submit weekly updates, track your streak, and view your dashboard — all from the terminal.
|
|
26
|
+
|
|
27
|
+
## Prerequisites
|
|
28
|
+
|
|
29
|
+
- Node.js 22+
|
|
30
|
+
- Logged into [startupschool.org](https://www.startupschool.org/) in Chrome
|
|
31
|
+
- macOS (for cookie extraction from Chrome Keychain)
|
|
32
|
+
|
|
33
|
+
## Quick Reference
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
yc whoami # Test connection, show user info
|
|
37
|
+
yc dashboard # Show streak, curriculum, weekly status
|
|
38
|
+
yc updates # List all weekly updates
|
|
39
|
+
yc show <id> # Show a single update in detail
|
|
40
|
+
yc new # Submit new weekly update (interactive)
|
|
41
|
+
yc new --metric 5 --morale 7 --talked-to 3 # Non-interactive
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Commands
|
|
45
|
+
|
|
46
|
+
### `yc whoami`
|
|
47
|
+
Test your connection and display user info (name, track, slug).
|
|
48
|
+
|
|
49
|
+
### `yc dashboard`
|
|
50
|
+
Show your Startup School dashboard:
|
|
51
|
+
- Current streak (consecutive weeks of updates)
|
|
52
|
+
- Curriculum progress (completed/required)
|
|
53
|
+
- Next curriculum item
|
|
54
|
+
- Recent weekly update status (submitted/missing)
|
|
55
|
+
|
|
56
|
+
### `yc updates`
|
|
57
|
+
List all your weekly updates with metric values, morale scores, and highlights.
|
|
58
|
+
|
|
59
|
+
### `yc show <id>`
|
|
60
|
+
Display a single update in full detail including goals and their completion status.
|
|
61
|
+
|
|
62
|
+
### `yc new`
|
|
63
|
+
Submit a new weekly update. Runs in interactive mode by default (prompts for each field).
|
|
64
|
+
|
|
65
|
+
**Interactive mode:**
|
|
66
|
+
```bash
|
|
67
|
+
yc new
|
|
68
|
+
# Prompts for: metric value, morale, users talked to, changes, blockers, goals
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**Flag mode (for automation):**
|
|
72
|
+
```bash
|
|
73
|
+
yc new \
|
|
74
|
+
--metric 10 \
|
|
75
|
+
--morale 8 \
|
|
76
|
+
--talked-to 5 \
|
|
77
|
+
--change "Shipped MVP to first 10 users" \
|
|
78
|
+
--blocker "Payment integration delayed" \
|
|
79
|
+
--learned "Users want simpler onboarding" \
|
|
80
|
+
--goal "Launch public beta" \
|
|
81
|
+
--goal "Set up analytics"
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Global Options
|
|
85
|
+
|
|
86
|
+
All commands support:
|
|
87
|
+
- `--cookie-source <browser>` — Browser to read cookies from (chrome, safari, firefox). Default: chrome
|
|
88
|
+
- `--chrome-profile <name>` — Specific Chrome profile directory name
|
|
89
|
+
- `--json` — Output raw JSON (for scripting)
|
|
90
|
+
|
|
91
|
+
## Workflows
|
|
92
|
+
|
|
93
|
+
### Weekly Update Routine
|
|
94
|
+
```bash
|
|
95
|
+
# Check if this week's update is submitted
|
|
96
|
+
yc dashboard
|
|
97
|
+
|
|
98
|
+
# If not, submit it
|
|
99
|
+
yc new
|
|
100
|
+
|
|
101
|
+
# Verify it shows up
|
|
102
|
+
yc updates
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Automation with Claude Code
|
|
106
|
+
When the user asks to submit their weekly update, use the `yc new` command with flags:
|
|
107
|
+
```bash
|
|
108
|
+
yc new --metric <value> --morale <1-10> --talked-to <count> \
|
|
109
|
+
--change "summary" --blocker "obstacle" --goal "goal1" --goal "goal2"
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Authentication
|
|
113
|
+
|
|
114
|
+
YC CLI extracts session cookies directly from your browser — no API keys or tokens needed. Just log in to startupschool.org in Chrome and the CLI handles the rest.
|
|
115
|
+
|
|
116
|
+
If you get authentication errors:
|
|
117
|
+
1. Open Chrome and visit https://www.startupschool.org/
|
|
118
|
+
2. Make sure you're logged in (can see dashboard)
|
|
119
|
+
3. Try running `yc whoami` again
|
|
120
|
+
4. If still failing, log out and back in to refresh your session
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* yc — CLI for Y Combinator Startup School
|
|
4
|
+
*
|
|
5
|
+
* Submit weekly updates, track your streak, manage your YC journey
|
|
6
|
+
* from the terminal. Cookie-based auth from your browser session.
|
|
7
|
+
*/
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=cli.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;GAKG"}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* yc — CLI for Y Combinator Startup School
|
|
4
|
+
*
|
|
5
|
+
* Submit weekly updates, track your streak, manage your YC journey
|
|
6
|
+
* from the terminal. Cookie-based auth from your browser session.
|
|
7
|
+
*/
|
|
8
|
+
import { Command } from "commander";
|
|
9
|
+
import kleur from "kleur";
|
|
10
|
+
import { createInterface } from "node:readline/promises";
|
|
11
|
+
import { stdin, stdout } from "node:process";
|
|
12
|
+
import { extractCookies } from "./lib/cookies.js";
|
|
13
|
+
import { YcClient, YcApiError, NotAuthenticatedError } from "./lib/client.js";
|
|
14
|
+
const program = new Command();
|
|
15
|
+
program
|
|
16
|
+
.name("yc")
|
|
17
|
+
.description("CLI for Y Combinator Startup School")
|
|
18
|
+
.version("0.1.0");
|
|
19
|
+
// --- Global options ---
|
|
20
|
+
function addCookieOption(cmd) {
|
|
21
|
+
return cmd
|
|
22
|
+
.option("--cookie-source <browser>", "Browser to read cookies from (chrome, safari, firefox)", "chrome")
|
|
23
|
+
.option("--chrome-profile <name>", "Chrome profile directory name");
|
|
24
|
+
}
|
|
25
|
+
function addJsonOption(cmd) {
|
|
26
|
+
return cmd.option("--json", "Output raw JSON");
|
|
27
|
+
}
|
|
28
|
+
async function getClient(cookieSource, chromeProfile) {
|
|
29
|
+
const source = (cookieSource || "chrome");
|
|
30
|
+
const cookies = await extractCookies(source, chromeProfile);
|
|
31
|
+
return new YcClient(cookies);
|
|
32
|
+
}
|
|
33
|
+
function handleError(err) {
|
|
34
|
+
if (err instanceof NotAuthenticatedError) {
|
|
35
|
+
console.error(kleur.red("Not authenticated."));
|
|
36
|
+
console.error(kleur.dim(err.message));
|
|
37
|
+
}
|
|
38
|
+
else if (err instanceof YcApiError) {
|
|
39
|
+
console.error(kleur.red(`API error: ${err.message}`));
|
|
40
|
+
if (err.response) {
|
|
41
|
+
console.error(kleur.dim(err.response.slice(0, 300)));
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
else if (err instanceof Error) {
|
|
45
|
+
console.error(kleur.red(err.message));
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
console.error(kleur.red("Unknown error"));
|
|
49
|
+
}
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
// --- whoami ---
|
|
53
|
+
const whoami = program.command("whoami").description("Test connection and show user info");
|
|
54
|
+
addCookieOption(whoami);
|
|
55
|
+
addJsonOption(whoami);
|
|
56
|
+
whoami.action(async (opts) => {
|
|
57
|
+
try {
|
|
58
|
+
const client = await getClient(opts.cookieSource, opts.chromeProfile);
|
|
59
|
+
const user = await client.getCurrentUser();
|
|
60
|
+
if (opts.json) {
|
|
61
|
+
console.log(JSON.stringify(user, null, 2));
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
console.log(kleur.bold(`Hello, ${user.firstName}!`));
|
|
65
|
+
console.log(` Track: ${user.track === "active_founder" ? kleur.green("Active Founder") : kleur.yellow("Aspiring Founder")}`);
|
|
66
|
+
console.log(` Slug: ${kleur.dim(user.slug)}`);
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
handleError(err);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
// --- dashboard ---
|
|
73
|
+
const dashboard = program.command("dashboard").description("Show dashboard — streak, curriculum, weekly status");
|
|
74
|
+
addCookieOption(dashboard);
|
|
75
|
+
addJsonOption(dashboard);
|
|
76
|
+
dashboard.action(async (opts) => {
|
|
77
|
+
try {
|
|
78
|
+
const client = await getClient(opts.cookieSource, opts.chromeProfile);
|
|
79
|
+
const { user, dashboard: dash } = await client.getDashboard();
|
|
80
|
+
if (opts.json) {
|
|
81
|
+
console.log(JSON.stringify({ user, dashboard: dash }, null, 2));
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
console.log(kleur.bold(`Dashboard — ${user.firstName}`));
|
|
85
|
+
console.log();
|
|
86
|
+
// Streak
|
|
87
|
+
const streakColor = dash.currentStreak > 0 ? kleur.green : kleur.dim;
|
|
88
|
+
console.log(` Streak: ${streakColor(`${dash.currentStreak} week(s)`)}`);
|
|
89
|
+
// Curriculum
|
|
90
|
+
const pct = Math.round((dash.curriculum.completed / dash.curriculum.required) * 100);
|
|
91
|
+
console.log(` Curriculum: ${dash.curriculum.completed}/${dash.curriculum.required} (${pct}%)`);
|
|
92
|
+
if (dash.curriculum.nextItem) {
|
|
93
|
+
console.log(` Next: ${kleur.cyan(dash.curriculum.nextItem.title)}`);
|
|
94
|
+
}
|
|
95
|
+
// Recent weeks
|
|
96
|
+
console.log();
|
|
97
|
+
console.log(kleur.bold(" Recent Updates:"));
|
|
98
|
+
const recentWeeks = dash.updatesByWeek.slice(0, 4);
|
|
99
|
+
for (const week of recentWeeks) {
|
|
100
|
+
const status = week.url ? kleur.green("submitted") : kleur.red("missing");
|
|
101
|
+
console.log(` ${week.weekLabel} ${status}`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
catch (err) {
|
|
105
|
+
handleError(err);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
// --- updates ---
|
|
109
|
+
const updates = program
|
|
110
|
+
.command("updates")
|
|
111
|
+
.description("List weekly updates");
|
|
112
|
+
addCookieOption(updates);
|
|
113
|
+
addJsonOption(updates);
|
|
114
|
+
updates.action(async (opts) => {
|
|
115
|
+
try {
|
|
116
|
+
const client = await getClient(opts.cookieSource, opts.chromeProfile);
|
|
117
|
+
const result = await client.getUpdates();
|
|
118
|
+
if (opts.json) {
|
|
119
|
+
console.log(JSON.stringify(result, null, 2));
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
console.log(kleur.bold(`${result.companyName} — Weekly Updates`));
|
|
123
|
+
console.log(` This week: ${result.thisWeekSubmitted ? kleur.green("submitted") : kleur.red("not submitted")}`);
|
|
124
|
+
console.log();
|
|
125
|
+
for (const update of result.updates) {
|
|
126
|
+
const moraleBar = "█".repeat(update.morale) + "░".repeat(10 - update.morale);
|
|
127
|
+
console.log(` ${kleur.bold(update.formattedDate)} ${kleur.dim(`[${update.id}]`)}`);
|
|
128
|
+
console.log(` ${update.metricDisplayName}: ${kleur.cyan(String(update.metricValue))} ` +
|
|
129
|
+
`Morale: ${moraleBar} ${update.morale}/10 ` +
|
|
130
|
+
`Talked to: ${update.talkedTo}`);
|
|
131
|
+
if (update.biggestChange) {
|
|
132
|
+
console.log(` Change: ${kleur.dim(update.biggestChange.slice(0, 80))}`);
|
|
133
|
+
}
|
|
134
|
+
if (update.biggestBlocker) {
|
|
135
|
+
console.log(` Blocker: ${kleur.dim(update.biggestBlocker.slice(0, 80))}`);
|
|
136
|
+
}
|
|
137
|
+
console.log();
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
catch (err) {
|
|
141
|
+
handleError(err);
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
// --- updates show ---
|
|
145
|
+
const updatesShow = program
|
|
146
|
+
.command("show <id>")
|
|
147
|
+
.description("Show a single update");
|
|
148
|
+
addCookieOption(updatesShow);
|
|
149
|
+
addJsonOption(updatesShow);
|
|
150
|
+
updatesShow.action(async (id, opts) => {
|
|
151
|
+
try {
|
|
152
|
+
const client = await getClient(opts.cookieSource, opts.chromeProfile);
|
|
153
|
+
const result = await client.getUpdates();
|
|
154
|
+
const update = result.updates.find((u) => u.id === id);
|
|
155
|
+
if (!update) {
|
|
156
|
+
console.error(kleur.red(`Update "${id}" not found.`));
|
|
157
|
+
process.exit(1);
|
|
158
|
+
}
|
|
159
|
+
if (opts.json) {
|
|
160
|
+
console.log(JSON.stringify(update, null, 2));
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
console.log(kleur.bold(update.formattedDate));
|
|
164
|
+
console.log(` ${update.metricDisplayName}: ${update.metricValue}`);
|
|
165
|
+
console.log(` Morale: ${update.morale}/10`);
|
|
166
|
+
console.log(` Users talked to: ${update.talkedTo}`);
|
|
167
|
+
if (update.learnedFromUsers) {
|
|
168
|
+
console.log(` Learned: ${update.learnedFromUsers}`);
|
|
169
|
+
}
|
|
170
|
+
if (update.biggestChange) {
|
|
171
|
+
console.log(` Biggest change: ${update.biggestChange}`);
|
|
172
|
+
}
|
|
173
|
+
if (update.biggestBlocker) {
|
|
174
|
+
console.log(` Biggest blocker: ${update.biggestBlocker}`);
|
|
175
|
+
}
|
|
176
|
+
if (update.completableGoals && update.completableGoals.length > 0) {
|
|
177
|
+
console.log(` Goals:`);
|
|
178
|
+
for (const g of update.completableGoals) {
|
|
179
|
+
const check = g.completed ? kleur.green("x") : " ";
|
|
180
|
+
console.log(` [${check}] ${g.goal}`);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
catch (err) {
|
|
185
|
+
handleError(err);
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
// --- updates new ---
|
|
189
|
+
const updatesNew = program
|
|
190
|
+
.command("new")
|
|
191
|
+
.description("Submit a new weekly update")
|
|
192
|
+
.option("--metric <value>", "Primary metric value (number)")
|
|
193
|
+
.option("--morale <value>", "Morale 1-10")
|
|
194
|
+
.option("--talked-to <value>", "Users talked to (number)")
|
|
195
|
+
.option("--change <text>", "What most improved your metric")
|
|
196
|
+
.option("--blocker <text>", "Biggest obstacle")
|
|
197
|
+
.option("--learned <text>", "What you learned from users")
|
|
198
|
+
.option("--goal <goals...>", "Goals for next week (can specify multiple)");
|
|
199
|
+
addCookieOption(updatesNew);
|
|
200
|
+
updatesNew.action(async (opts) => {
|
|
201
|
+
try {
|
|
202
|
+
const client = await getClient(opts.cookieSource, opts.chromeProfile);
|
|
203
|
+
let input;
|
|
204
|
+
// If all required flags provided, skip interactive mode
|
|
205
|
+
if (opts.metric && opts.morale && opts.talkedTo) {
|
|
206
|
+
input = {
|
|
207
|
+
metric_value: Number(opts.metric),
|
|
208
|
+
morale: Number(opts.morale),
|
|
209
|
+
talked_to: Number(opts.talkedTo),
|
|
210
|
+
biggest_change: opts.change,
|
|
211
|
+
biggest_blocker: opts.blocker,
|
|
212
|
+
learned_from_users: opts.learned,
|
|
213
|
+
goals: opts.goal,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
else {
|
|
217
|
+
// Interactive mode
|
|
218
|
+
const rl = createInterface({ input: stdin, output: stdout });
|
|
219
|
+
console.log(kleur.bold("New Weekly Update"));
|
|
220
|
+
console.log(kleur.dim("Fill in your update (press Enter to skip optional fields)"));
|
|
221
|
+
console.log();
|
|
222
|
+
const metricStr = opts.metric || (await rl.question("Primary metric value *: "));
|
|
223
|
+
const moraleStr = opts.morale || (await rl.question("Morale (1-10) *: "));
|
|
224
|
+
const talkedToStr = opts.talkedTo || (await rl.question("Users talked to *: "));
|
|
225
|
+
const change = opts.change || (await rl.question("What most improved your metric? "));
|
|
226
|
+
const blocker = opts.blocker || (await rl.question("Biggest obstacle? "));
|
|
227
|
+
const learned = opts.learned || (await rl.question("What did you learn from users? "));
|
|
228
|
+
const goalsStr = await rl.question("Goals for next week (comma-separated): ");
|
|
229
|
+
rl.close();
|
|
230
|
+
input = {
|
|
231
|
+
metric_value: Number(metricStr),
|
|
232
|
+
morale: Number(moraleStr),
|
|
233
|
+
talked_to: Number(talkedToStr),
|
|
234
|
+
biggest_change: change || undefined,
|
|
235
|
+
biggest_blocker: blocker || undefined,
|
|
236
|
+
learned_from_users: learned || undefined,
|
|
237
|
+
goals: goalsStr ? goalsStr.split(",").map((g) => g.trim()).filter(Boolean) : undefined,
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
// Validate
|
|
241
|
+
if (isNaN(input.metric_value) || isNaN(input.morale) || isNaN(input.talked_to)) {
|
|
242
|
+
console.error(kleur.red("Metric, morale, and talked-to must be numbers."));
|
|
243
|
+
process.exit(1);
|
|
244
|
+
}
|
|
245
|
+
if (input.morale < 1 || input.morale > 10) {
|
|
246
|
+
console.error(kleur.red("Morale must be between 1 and 10."));
|
|
247
|
+
process.exit(1);
|
|
248
|
+
}
|
|
249
|
+
console.log();
|
|
250
|
+
console.log(kleur.dim("Submitting update..."));
|
|
251
|
+
const resultUrl = await client.createUpdate(input);
|
|
252
|
+
console.log(kleur.green("Update submitted!"));
|
|
253
|
+
console.log(kleur.dim(resultUrl));
|
|
254
|
+
}
|
|
255
|
+
catch (err) {
|
|
256
|
+
handleError(err);
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
program.parse();
|
|
260
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* YC Startup School API client.
|
|
3
|
+
*
|
|
4
|
+
* Hybrid architecture:
|
|
5
|
+
* - Reading: GraphQL at POST /graphql (Apollo Client on frontend)
|
|
6
|
+
* - Writing: Traditional Rails REST (form POST with CSRF token)
|
|
7
|
+
*/
|
|
8
|
+
import { type YcCookies } from "./cookies.js";
|
|
9
|
+
export declare class YcApiError extends Error {
|
|
10
|
+
statusCode?: number | undefined;
|
|
11
|
+
response?: string | undefined;
|
|
12
|
+
constructor(message: string, statusCode?: number | undefined, response?: string | undefined);
|
|
13
|
+
}
|
|
14
|
+
export declare class NotAuthenticatedError extends YcApiError {
|
|
15
|
+
constructor();
|
|
16
|
+
}
|
|
17
|
+
export interface Update {
|
|
18
|
+
id: string;
|
|
19
|
+
formattedDate: string;
|
|
20
|
+
createdAt: string;
|
|
21
|
+
canEdit: boolean;
|
|
22
|
+
metricDisplayName: string;
|
|
23
|
+
metricValue: number;
|
|
24
|
+
morale: number;
|
|
25
|
+
talkedTo: number;
|
|
26
|
+
learnedFromUsers: string | null;
|
|
27
|
+
biggestChange: string | null;
|
|
28
|
+
biggestBlocker: string | null;
|
|
29
|
+
goals: string | null;
|
|
30
|
+
path: string;
|
|
31
|
+
completableGoals: Array<{
|
|
32
|
+
key: string;
|
|
33
|
+
goal: string;
|
|
34
|
+
completed: boolean | null;
|
|
35
|
+
}>;
|
|
36
|
+
}
|
|
37
|
+
export interface Dashboard {
|
|
38
|
+
currentStreak: number;
|
|
39
|
+
curriculum: {
|
|
40
|
+
completed: number;
|
|
41
|
+
required: number;
|
|
42
|
+
nextItem: {
|
|
43
|
+
id: string;
|
|
44
|
+
title: string;
|
|
45
|
+
url: string;
|
|
46
|
+
} | null;
|
|
47
|
+
};
|
|
48
|
+
updatesByWeek: Array<{
|
|
49
|
+
url: string | null;
|
|
50
|
+
weekLabel: string;
|
|
51
|
+
}>;
|
|
52
|
+
}
|
|
53
|
+
export interface CurrentUser {
|
|
54
|
+
slug: string;
|
|
55
|
+
firstName: string;
|
|
56
|
+
track: string;
|
|
57
|
+
returningUser: boolean;
|
|
58
|
+
}
|
|
59
|
+
export interface UpdateInput {
|
|
60
|
+
metric_value: number;
|
|
61
|
+
talked_to: number;
|
|
62
|
+
morale: number;
|
|
63
|
+
learned_from_users?: string;
|
|
64
|
+
biggest_change?: string;
|
|
65
|
+
biggest_blocker?: string;
|
|
66
|
+
goals?: string[];
|
|
67
|
+
}
|
|
68
|
+
export declare class YcClient {
|
|
69
|
+
private cookies;
|
|
70
|
+
private csrfToken;
|
|
71
|
+
constructor(cookies: YcCookies);
|
|
72
|
+
private baseHeaders;
|
|
73
|
+
graphqlQuery<T>(operationName: string, query: string, variables?: Record<string, unknown>): Promise<T>;
|
|
74
|
+
getCsrfToken(): Promise<string>;
|
|
75
|
+
railsPost(path: string, data: Record<string, string>): Promise<string>;
|
|
76
|
+
railsPut(path: string, data: Record<string, string>): Promise<string>;
|
|
77
|
+
getCurrentUser(): Promise<CurrentUser>;
|
|
78
|
+
getDashboard(): Promise<{
|
|
79
|
+
user: CurrentUser;
|
|
80
|
+
dashboard: Dashboard;
|
|
81
|
+
completedActions: string[];
|
|
82
|
+
}>;
|
|
83
|
+
getUpdates(companyId?: number): Promise<{
|
|
84
|
+
companyName: string;
|
|
85
|
+
updates: Update[];
|
|
86
|
+
thisWeekSubmitted: boolean;
|
|
87
|
+
}>;
|
|
88
|
+
createUpdate(input: UpdateInput): Promise<string>;
|
|
89
|
+
editUpdate(id: string, input: Partial<UpdateInput>): Promise<string>;
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/lib/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* YC Startup School API client.
|
|
3
|
+
*
|
|
4
|
+
* Hybrid architecture:
|
|
5
|
+
* - Reading: GraphQL at POST /graphql (Apollo Client on frontend)
|
|
6
|
+
* - Writing: Traditional Rails REST (form POST with CSRF token)
|
|
7
|
+
*/
|
|
8
|
+
import { cookiesToString } from "./cookies.js";
|
|
9
|
+
const BASE_URL = "https://www.startupschool.org";
|
|
10
|
+
const USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36";
|
|
11
|
+
// --- Error types ---
|
|
12
|
+
export class YcApiError extends Error {
|
|
13
|
+
statusCode;
|
|
14
|
+
response;
|
|
15
|
+
constructor(message, statusCode, response) {
|
|
16
|
+
super(message);
|
|
17
|
+
this.statusCode = statusCode;
|
|
18
|
+
this.response = response;
|
|
19
|
+
this.name = "YcApiError";
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export class NotAuthenticatedError extends YcApiError {
|
|
23
|
+
constructor() {
|
|
24
|
+
super("Session expired or invalid. Log in at https://www.startupschool.org/ in Chrome, then try again.");
|
|
25
|
+
this.name = "NotAuthenticatedError";
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
// --- Client ---
|
|
29
|
+
export class YcClient {
|
|
30
|
+
cookies;
|
|
31
|
+
csrfToken = null;
|
|
32
|
+
constructor(cookies) {
|
|
33
|
+
this.cookies = cookies;
|
|
34
|
+
}
|
|
35
|
+
baseHeaders() {
|
|
36
|
+
return {
|
|
37
|
+
"User-Agent": USER_AGENT,
|
|
38
|
+
Cookie: cookiesToString(this.cookies),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
// --- GraphQL ---
|
|
42
|
+
async graphqlQuery(operationName, query, variables) {
|
|
43
|
+
// Rails requires CSRF token for all POST requests, including GraphQL
|
|
44
|
+
const csrf = await this.getCsrfToken();
|
|
45
|
+
const res = await fetch(`${BASE_URL}/graphql`, {
|
|
46
|
+
method: "POST",
|
|
47
|
+
headers: {
|
|
48
|
+
...this.baseHeaders(),
|
|
49
|
+
"Content-Type": "application/json",
|
|
50
|
+
Accept: "application/json",
|
|
51
|
+
"X-CSRF-Token": csrf,
|
|
52
|
+
},
|
|
53
|
+
body: JSON.stringify({ operationName, query, variables }),
|
|
54
|
+
});
|
|
55
|
+
if (res.status === 401 || res.status === 302) {
|
|
56
|
+
throw new NotAuthenticatedError();
|
|
57
|
+
}
|
|
58
|
+
if (!res.ok) {
|
|
59
|
+
const text = await res.text().catch(() => "");
|
|
60
|
+
throw new YcApiError(`GraphQL request failed: ${res.status} ${res.statusText}`, res.status, text.slice(0, 500));
|
|
61
|
+
}
|
|
62
|
+
const json = (await res.json());
|
|
63
|
+
if (json.errors && json.errors.length > 0) {
|
|
64
|
+
throw new YcApiError(`GraphQL error: ${json.errors.map((e) => e.message).join(", ")}`);
|
|
65
|
+
}
|
|
66
|
+
if (!json.data) {
|
|
67
|
+
throw new YcApiError("GraphQL response missing data field");
|
|
68
|
+
}
|
|
69
|
+
return json.data;
|
|
70
|
+
}
|
|
71
|
+
// --- CSRF Token ---
|
|
72
|
+
async getCsrfToken() {
|
|
73
|
+
if (this.csrfToken)
|
|
74
|
+
return this.csrfToken;
|
|
75
|
+
const res = await fetch(`${BASE_URL}/dashboard`, {
|
|
76
|
+
headers: this.baseHeaders(),
|
|
77
|
+
redirect: "follow",
|
|
78
|
+
});
|
|
79
|
+
if (res.status === 401 || res.url.includes("account.ycombinator.com")) {
|
|
80
|
+
throw new NotAuthenticatedError();
|
|
81
|
+
}
|
|
82
|
+
const html = await res.text();
|
|
83
|
+
const match = html.match(/<meta\s+name="csrf-token"\s+content="([^"]+)"/);
|
|
84
|
+
if (!match) {
|
|
85
|
+
throw new YcApiError("Could not extract CSRF token from page");
|
|
86
|
+
}
|
|
87
|
+
this.csrfToken = match[1];
|
|
88
|
+
return this.csrfToken;
|
|
89
|
+
}
|
|
90
|
+
// --- Rails REST ---
|
|
91
|
+
async railsPost(path, data) {
|
|
92
|
+
const csrf = await this.getCsrfToken();
|
|
93
|
+
const formData = new URLSearchParams({
|
|
94
|
+
...data,
|
|
95
|
+
authenticity_token: csrf,
|
|
96
|
+
});
|
|
97
|
+
const res = await fetch(`${BASE_URL}${path}`, {
|
|
98
|
+
method: "POST",
|
|
99
|
+
headers: {
|
|
100
|
+
...this.baseHeaders(),
|
|
101
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
102
|
+
Accept: "text/html, application/json",
|
|
103
|
+
},
|
|
104
|
+
body: formData.toString(),
|
|
105
|
+
redirect: "follow",
|
|
106
|
+
});
|
|
107
|
+
if (res.status === 401 || res.status === 422) {
|
|
108
|
+
throw new YcApiError(`Rails POST failed: ${res.status} ${res.statusText}`, res.status);
|
|
109
|
+
}
|
|
110
|
+
return res.url; // Returns redirect URL on success
|
|
111
|
+
}
|
|
112
|
+
async railsPut(path, data) {
|
|
113
|
+
// Rails uses _method=put for PUT via POST
|
|
114
|
+
return this.railsPost(path, { ...data, _method: "put" });
|
|
115
|
+
}
|
|
116
|
+
// --- High-level API ---
|
|
117
|
+
async getCurrentUser() {
|
|
118
|
+
const { CURRENT_USER } = await import("./queries.js");
|
|
119
|
+
const data = await this.graphqlQuery("CURRENT_USER", CURRENT_USER);
|
|
120
|
+
return data.currentUser;
|
|
121
|
+
}
|
|
122
|
+
async getDashboard() {
|
|
123
|
+
const { DASHBOARD_DATA } = await import("./queries.js");
|
|
124
|
+
const data = await this.graphqlQuery("DASHBOARD_DATA", DASHBOARD_DATA);
|
|
125
|
+
return {
|
|
126
|
+
user: data.currentUser,
|
|
127
|
+
dashboard: data.dashboard,
|
|
128
|
+
completedActions: data.completedActions,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
async getUpdates(companyId) {
|
|
132
|
+
const { UPDATES_INDEX } = await import("./queries.js");
|
|
133
|
+
const data = await this.graphqlQuery("UPDATES_INDEX", UPDATES_INDEX, companyId ? { companyId } : undefined);
|
|
134
|
+
return data.updates;
|
|
135
|
+
}
|
|
136
|
+
async createUpdate(input) {
|
|
137
|
+
const data = {
|
|
138
|
+
"update[metric_value]": String(input.metric_value),
|
|
139
|
+
"update[talked_to]": String(input.talked_to),
|
|
140
|
+
"update[morale]": String(input.morale),
|
|
141
|
+
};
|
|
142
|
+
if (input.learned_from_users) {
|
|
143
|
+
data["update[learned_from_users]"] = input.learned_from_users;
|
|
144
|
+
}
|
|
145
|
+
if (input.biggest_change) {
|
|
146
|
+
data["update[biggest_change]"] = input.biggest_change;
|
|
147
|
+
}
|
|
148
|
+
if (input.biggest_blocker) {
|
|
149
|
+
data["update[biggest_blocker]"] = input.biggest_blocker;
|
|
150
|
+
}
|
|
151
|
+
if (input.goals) {
|
|
152
|
+
input.goals.forEach((goal, i) => {
|
|
153
|
+
data[`update[goals][${i}]`] = goal;
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
return this.railsPost("/updates", data);
|
|
157
|
+
}
|
|
158
|
+
async editUpdate(id, input) {
|
|
159
|
+
const data = {};
|
|
160
|
+
if (input.metric_value !== undefined) {
|
|
161
|
+
data["update[metric_value]"] = String(input.metric_value);
|
|
162
|
+
}
|
|
163
|
+
if (input.talked_to !== undefined) {
|
|
164
|
+
data["update[talked_to]"] = String(input.talked_to);
|
|
165
|
+
}
|
|
166
|
+
if (input.morale !== undefined) {
|
|
167
|
+
data["update[morale]"] = String(input.morale);
|
|
168
|
+
}
|
|
169
|
+
if (input.learned_from_users !== undefined) {
|
|
170
|
+
data["update[learned_from_users]"] = input.learned_from_users;
|
|
171
|
+
}
|
|
172
|
+
if (input.biggest_change !== undefined) {
|
|
173
|
+
data["update[biggest_change]"] = input.biggest_change;
|
|
174
|
+
}
|
|
175
|
+
if (input.biggest_blocker !== undefined) {
|
|
176
|
+
data["update[biggest_blocker]"] = input.biggest_blocker;
|
|
177
|
+
}
|
|
178
|
+
if (input.goals) {
|
|
179
|
+
input.goals.forEach((goal, i) => {
|
|
180
|
+
data[`update[goals][${i}]`] = goal;
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
return this.railsPut(`/updates/${id}`, data);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/lib/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Cookie extraction module — wraps @steipete/sweet-cookie
|
|
4
|
+
* Extracts YC session cookies from Chrome/Safari/Firefox
|
|
5
|
+
*/
|
|
6
|
+
export interface YcCookies {
|
|
7
|
+
[key: string]: string;
|
|
8
|
+
}
|
|
9
|
+
export type CookieSource = "chrome" | "safari" | "firefox";
|
|
10
|
+
/**
|
|
11
|
+
* Extract YC cookies from browser cookie store.
|
|
12
|
+
* For Chrome on macOS, auto-discovers all profiles.
|
|
13
|
+
*/
|
|
14
|
+
export declare function extractCookies(source?: CookieSource, chromeProfile?: string): Promise<YcCookies>;
|
|
15
|
+
/**
|
|
16
|
+
* Format cookies as a cookie header string.
|
|
17
|
+
*/
|
|
18
|
+
export declare function cookiesToString(cookies: YcCookies): string;
|
|
19
|
+
//# sourceMappingURL=cookies.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cookies.d.ts","sourceRoot":"","sources":["../../src/lib/cookies.ts"],"names":[],"mappings":";AACA;;;GAGG;AAQH,MAAM,WAAW,SAAS;IACxB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;CACvB;AAED,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC;AAuH3D;;;GAGG;AACH,wBAAsB,cAAc,CAClC,MAAM,GAAE,YAAuB,EAC/B,aAAa,CAAC,EAAE,MAAM,GACrB,OAAO,CAAC,SAAS,CAAC,CA+FpB;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,SAAS,GAAG,MAAM,CAI1D"}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Cookie extraction module — wraps @steipete/sweet-cookie
|
|
4
|
+
* Extracts YC session cookies from Chrome/Safari/Firefox
|
|
5
|
+
*/
|
|
6
|
+
import { getCookies } from "@steipete/sweet-cookie";
|
|
7
|
+
import { readFileSync, existsSync } from "node:fs";
|
|
8
|
+
import { homedir } from "node:os";
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
import kleur from "kleur";
|
|
11
|
+
/**
|
|
12
|
+
* Discover Chrome profiles from Local State file.
|
|
13
|
+
*/
|
|
14
|
+
function discoverChromeProfiles() {
|
|
15
|
+
if (process.platform !== "darwin")
|
|
16
|
+
return [];
|
|
17
|
+
const localStatePath = join(homedir(), "Library", "Application Support", "Google", "Chrome", "Local State");
|
|
18
|
+
if (!existsSync(localStatePath))
|
|
19
|
+
return [];
|
|
20
|
+
try {
|
|
21
|
+
const raw = readFileSync(localStatePath, "utf-8");
|
|
22
|
+
const state = JSON.parse(raw);
|
|
23
|
+
const infoCache = state?.profile?.info_cache;
|
|
24
|
+
if (!infoCache || typeof infoCache !== "object")
|
|
25
|
+
return [];
|
|
26
|
+
const profiles = [];
|
|
27
|
+
for (const [dirName, meta] of Object.entries(infoCache)) {
|
|
28
|
+
const m = meta;
|
|
29
|
+
const displayName = String(m.name || m.gaia_name || dirName);
|
|
30
|
+
profiles.push({ dirName, displayName });
|
|
31
|
+
}
|
|
32
|
+
profiles.sort((a, b) => {
|
|
33
|
+
if (a.dirName === "Default")
|
|
34
|
+
return -1;
|
|
35
|
+
if (b.dirName === "Default")
|
|
36
|
+
return 1;
|
|
37
|
+
return a.dirName.localeCompare(b.dirName);
|
|
38
|
+
});
|
|
39
|
+
return profiles;
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return [];
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function toCookieMap(cookies) {
|
|
46
|
+
const map = {};
|
|
47
|
+
for (const cookie of cookies) {
|
|
48
|
+
map[cookie.name] = cookie.value;
|
|
49
|
+
}
|
|
50
|
+
return map;
|
|
51
|
+
}
|
|
52
|
+
function buildNoCookieError(source, warnings, checkedProfiles) {
|
|
53
|
+
const lines = [
|
|
54
|
+
`No session cookie found for startupschool.org in ${source}.`,
|
|
55
|
+
"",
|
|
56
|
+
];
|
|
57
|
+
if (checkedProfiles && checkedProfiles.length > 0) {
|
|
58
|
+
lines.push(`Checked ${checkedProfiles.length} Chrome profile(s): ` +
|
|
59
|
+
checkedProfiles.map((p) => `"${p.dirName}" (${p.displayName})`).join(", "));
|
|
60
|
+
lines.push("");
|
|
61
|
+
}
|
|
62
|
+
if (warnings.length > 0) {
|
|
63
|
+
lines.push("Warnings from cookie extraction:");
|
|
64
|
+
for (const w of warnings) {
|
|
65
|
+
lines.push(` - ${w}`);
|
|
66
|
+
}
|
|
67
|
+
lines.push("");
|
|
68
|
+
}
|
|
69
|
+
lines.push(`Debug info:`, ` - Platform: ${process.platform}`, ` - Node: ${process.version}`, ` - Cookie source: ${source}`, "", "Troubleshooting:", " 1. Keychain access: when macOS prompts for your password, click 'Always Allow'", " to avoid being asked again.", " 2. Login: open Chrome and visit https://www.startupschool.org/ — make sure you", " are logged in and can see your dashboard.", " 3. Session expired: try logging out and back in on startupschool.org.", " 4. Non-standard browser: if you use Brave, Arc, or another Chromium browser,", " try --cookie-source safari instead.");
|
|
70
|
+
return new Error(lines.join("\n"));
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Check if cookies contain a valid YC session.
|
|
74
|
+
* YC uses Rails session cookies — the exact name may vary,
|
|
75
|
+
* but we look for common Rails session cookie patterns.
|
|
76
|
+
*/
|
|
77
|
+
function hasValidSession(cookieMap) {
|
|
78
|
+
// Check for any cookie that looks like a session
|
|
79
|
+
const sessionKeys = Object.keys(cookieMap).filter((k) => k.includes("session") ||
|
|
80
|
+
k.includes("_sso") ||
|
|
81
|
+
k === "_startup_school_session" ||
|
|
82
|
+
k === "_ycombinator_session");
|
|
83
|
+
return sessionKeys.length > 0;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Extract YC cookies from browser cookie store.
|
|
87
|
+
* For Chrome on macOS, auto-discovers all profiles.
|
|
88
|
+
*/
|
|
89
|
+
export async function extractCookies(source = "chrome", chromeProfile) {
|
|
90
|
+
const log = (msg) => console.error(kleur.dim(msg));
|
|
91
|
+
// We need cookies from both startupschool.org and ycombinator.com
|
|
92
|
+
// (SSO cookies may be on the ycombinator.com domain)
|
|
93
|
+
const domains = [
|
|
94
|
+
"https://www.startupschool.org/",
|
|
95
|
+
"https://account.ycombinator.com/",
|
|
96
|
+
];
|
|
97
|
+
if (chromeProfile || source !== "chrome") {
|
|
98
|
+
log(`Reading cookies from ${source}${chromeProfile ? ` (profile: ${chromeProfile})` : ""}...`);
|
|
99
|
+
const allCookies = {};
|
|
100
|
+
for (const url of domains) {
|
|
101
|
+
const result = await getCookies({
|
|
102
|
+
url,
|
|
103
|
+
browsers: [source],
|
|
104
|
+
timeoutMs: 30_000,
|
|
105
|
+
...(chromeProfile ? { chromeProfile } : {}),
|
|
106
|
+
});
|
|
107
|
+
Object.assign(allCookies, toCookieMap(result.cookies));
|
|
108
|
+
}
|
|
109
|
+
if (!hasValidSession(allCookies)) {
|
|
110
|
+
throw buildNoCookieError(source, []);
|
|
111
|
+
}
|
|
112
|
+
log(`Authenticated via ${source}.`);
|
|
113
|
+
return allCookies;
|
|
114
|
+
}
|
|
115
|
+
// Auto-discover Chrome profiles
|
|
116
|
+
const profiles = discoverChromeProfiles();
|
|
117
|
+
if (profiles.length === 0) {
|
|
118
|
+
log("Reading cookies from Chrome (default profile)...");
|
|
119
|
+
const allCookies = {};
|
|
120
|
+
for (const url of domains) {
|
|
121
|
+
const result = await getCookies({
|
|
122
|
+
url,
|
|
123
|
+
browsers: [source],
|
|
124
|
+
timeoutMs: 30_000,
|
|
125
|
+
});
|
|
126
|
+
Object.assign(allCookies, toCookieMap(result.cookies));
|
|
127
|
+
}
|
|
128
|
+
if (!hasValidSession(allCookies)) {
|
|
129
|
+
throw buildNoCookieError(source, []);
|
|
130
|
+
}
|
|
131
|
+
log("Authenticated via Chrome.");
|
|
132
|
+
return allCookies;
|
|
133
|
+
}
|
|
134
|
+
log(`Found ${profiles.length} Chrome profile(s): ${profiles.map((p) => `${p.dirName} (${p.displayName})`).join(", ")}`);
|
|
135
|
+
const found = [];
|
|
136
|
+
let lastWarnings = [];
|
|
137
|
+
for (const profile of profiles) {
|
|
138
|
+
log(` Checking "${profile.dirName}" (${profile.displayName})...`);
|
|
139
|
+
const allCookies = {};
|
|
140
|
+
for (const url of domains) {
|
|
141
|
+
const result = await getCookies({
|
|
142
|
+
url,
|
|
143
|
+
browsers: ["chrome"],
|
|
144
|
+
chromeProfile: profile.dirName,
|
|
145
|
+
timeoutMs: 30_000,
|
|
146
|
+
});
|
|
147
|
+
lastWarnings = result.warnings;
|
|
148
|
+
Object.assign(allCookies, toCookieMap(result.cookies));
|
|
149
|
+
}
|
|
150
|
+
if (hasValidSession(allCookies)) {
|
|
151
|
+
log(` -> Found YC session in "${profile.dirName}"`);
|
|
152
|
+
found.push({ profile, cookies: allCookies });
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
log(` -> No YC session`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
if (found.length === 0) {
|
|
159
|
+
throw buildNoCookieError(source, lastWarnings, profiles);
|
|
160
|
+
}
|
|
161
|
+
if (found.length === 1) {
|
|
162
|
+
log(`Authenticated via Chrome profile "${found[0].profile.dirName}" (${found[0].profile.displayName}).`);
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
const names = found.map((f) => `"${f.profile.dirName}" (${f.profile.displayName})`);
|
|
166
|
+
log(`Found YC sessions in ${found.length} profiles: ${names.join(", ")}. ` +
|
|
167
|
+
`Using "${found[0].profile.dirName}". ` +
|
|
168
|
+
`To choose a specific one: --chrome-profile "${found[found.length - 1].profile.dirName}"`);
|
|
169
|
+
}
|
|
170
|
+
return found[0].cookies;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Format cookies as a cookie header string.
|
|
174
|
+
*/
|
|
175
|
+
export function cookiesToString(cookies) {
|
|
176
|
+
return Object.entries(cookies)
|
|
177
|
+
.map(([k, v]) => `${k}=${v}`)
|
|
178
|
+
.join("; ");
|
|
179
|
+
}
|
|
180
|
+
//# sourceMappingURL=cookies.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cookies.js","sourceRoot":"","sources":["../../src/lib/cookies.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GraphQL query strings for Startup School API.
|
|
3
|
+
* Captured from live Apollo Client inspection (2026-03-03).
|
|
4
|
+
*
|
|
5
|
+
* Endpoint: POST https://www.startupschool.org/graphql
|
|
6
|
+
* Content-Type: application/json
|
|
7
|
+
*/
|
|
8
|
+
export declare const UPDATES_INDEX = "\nquery UPDATES_INDEX($companyId: ID) {\n currentUser {\n slug\n track\n __typename\n }\n updates(companyId: $companyId) {\n companyName\n updates {\n biggestBlocker\n biggestChange\n canEdit\n completableGoals {\n key\n goal\n completed\n __typename\n }\n createdAt\n formattedDate\n goals\n id\n learnedFromUsers\n metricDisplayName\n metricValue\n morale\n path\n talkedTo\n __typename\n }\n thisWeekSubmitted\n __typename\n }\n}\n";
|
|
9
|
+
export declare const UPDATE_CHARTS = "\nquery UPDATE_CHARTS($companyId: ID) {\n updates(companyId: $companyId) {\n updates {\n createdAt\n metricDisplayName\n metricValue\n morale\n talkedTo\n __typename\n }\n __typename\n }\n}\n";
|
|
10
|
+
/**
|
|
11
|
+
* Dashboard query — reconstructed from Apollo cache inspection.
|
|
12
|
+
* Fields confirmed: currentStreak, curriculum (completed, required, nextItem),
|
|
13
|
+
* updatesByWeek (url, weekLabel), cofounderMatching, completedActions.
|
|
14
|
+
*/
|
|
15
|
+
export declare const DASHBOARD_DATA = "\nquery DASHBOARD_DATA {\n currentUser {\n slug\n firstName\n track\n returningUser\n __typename\n }\n completedActions\n dashboard {\n currentStreak\n curriculum {\n completed\n required\n nextItem {\n id\n title\n previewLink\n contentType\n url\n __typename\n }\n __typename\n }\n updatesByWeek {\n url\n weekLabel\n __typename\n }\n __typename\n }\n cofounderMatching {\n __typename\n }\n updates {\n __typename\n }\n}\n";
|
|
16
|
+
/**
|
|
17
|
+
* Current user query — lightweight identity check.
|
|
18
|
+
*/
|
|
19
|
+
export declare const CURRENT_USER = "\nquery CURRENT_USER {\n currentUser {\n slug\n firstName\n track\n returningUser\n __typename\n }\n}\n";
|
|
20
|
+
//# sourceMappingURL=queries.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queries.d.ts","sourceRoot":"","sources":["../../src/lib/queries.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,eAAO,MAAM,aAAa,+jBAmCzB,CAAC;AAEF,eAAO,MAAM,aAAa,6OAczB,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,cAAc,+iBAuC1B,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,YAAY,6HAUxB,CAAC"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GraphQL query strings for Startup School API.
|
|
3
|
+
* Captured from live Apollo Client inspection (2026-03-03).
|
|
4
|
+
*
|
|
5
|
+
* Endpoint: POST https://www.startupschool.org/graphql
|
|
6
|
+
* Content-Type: application/json
|
|
7
|
+
*/
|
|
8
|
+
export const UPDATES_INDEX = `
|
|
9
|
+
query UPDATES_INDEX($companyId: ID) {
|
|
10
|
+
currentUser {
|
|
11
|
+
slug
|
|
12
|
+
track
|
|
13
|
+
__typename
|
|
14
|
+
}
|
|
15
|
+
updates(companyId: $companyId) {
|
|
16
|
+
companyName
|
|
17
|
+
updates {
|
|
18
|
+
biggestBlocker
|
|
19
|
+
biggestChange
|
|
20
|
+
canEdit
|
|
21
|
+
completableGoals {
|
|
22
|
+
key
|
|
23
|
+
goal
|
|
24
|
+
completed
|
|
25
|
+
__typename
|
|
26
|
+
}
|
|
27
|
+
createdAt
|
|
28
|
+
formattedDate
|
|
29
|
+
goals
|
|
30
|
+
id
|
|
31
|
+
learnedFromUsers
|
|
32
|
+
metricDisplayName
|
|
33
|
+
metricValue
|
|
34
|
+
morale
|
|
35
|
+
path
|
|
36
|
+
talkedTo
|
|
37
|
+
__typename
|
|
38
|
+
}
|
|
39
|
+
thisWeekSubmitted
|
|
40
|
+
__typename
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
`;
|
|
44
|
+
export const UPDATE_CHARTS = `
|
|
45
|
+
query UPDATE_CHARTS($companyId: ID) {
|
|
46
|
+
updates(companyId: $companyId) {
|
|
47
|
+
updates {
|
|
48
|
+
createdAt
|
|
49
|
+
metricDisplayName
|
|
50
|
+
metricValue
|
|
51
|
+
morale
|
|
52
|
+
talkedTo
|
|
53
|
+
__typename
|
|
54
|
+
}
|
|
55
|
+
__typename
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
`;
|
|
59
|
+
/**
|
|
60
|
+
* Dashboard query — reconstructed from Apollo cache inspection.
|
|
61
|
+
* Fields confirmed: currentStreak, curriculum (completed, required, nextItem),
|
|
62
|
+
* updatesByWeek (url, weekLabel), cofounderMatching, completedActions.
|
|
63
|
+
*/
|
|
64
|
+
export const DASHBOARD_DATA = `
|
|
65
|
+
query DASHBOARD_DATA {
|
|
66
|
+
currentUser {
|
|
67
|
+
slug
|
|
68
|
+
firstName
|
|
69
|
+
track
|
|
70
|
+
returningUser
|
|
71
|
+
__typename
|
|
72
|
+
}
|
|
73
|
+
completedActions
|
|
74
|
+
dashboard {
|
|
75
|
+
currentStreak
|
|
76
|
+
curriculum {
|
|
77
|
+
completed
|
|
78
|
+
required
|
|
79
|
+
nextItem {
|
|
80
|
+
id
|
|
81
|
+
title
|
|
82
|
+
previewLink
|
|
83
|
+
contentType
|
|
84
|
+
url
|
|
85
|
+
__typename
|
|
86
|
+
}
|
|
87
|
+
__typename
|
|
88
|
+
}
|
|
89
|
+
updatesByWeek {
|
|
90
|
+
url
|
|
91
|
+
weekLabel
|
|
92
|
+
__typename
|
|
93
|
+
}
|
|
94
|
+
__typename
|
|
95
|
+
}
|
|
96
|
+
cofounderMatching {
|
|
97
|
+
__typename
|
|
98
|
+
}
|
|
99
|
+
updates {
|
|
100
|
+
__typename
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
`;
|
|
104
|
+
/**
|
|
105
|
+
* Current user query — lightweight identity check.
|
|
106
|
+
*/
|
|
107
|
+
export const CURRENT_USER = `
|
|
108
|
+
query CURRENT_USER {
|
|
109
|
+
currentUser {
|
|
110
|
+
slug
|
|
111
|
+
firstName
|
|
112
|
+
track
|
|
113
|
+
returningUser
|
|
114
|
+
__typename
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
`;
|
|
118
|
+
//# sourceMappingURL=queries.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queries.js","sourceRoot":"","sources":["../../src/lib/queries.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,CAAC,MAAM,aAAa,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmC5B,CAAC;AAEF,MAAM,CAAC,MAAM,aAAa,GAAG;;;;;;;;;;;;;;CAc5B,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuC7B,CAAC;AAEF;;GAEG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG;;;;;;;;;;CAU3B,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lucasygu/yc-cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "CLI tool for Y Combinator Startup School — submit weekly updates, track progress, manage your YC journey from the terminal",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/lib/client.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./dist/lib/client.js",
|
|
9
|
+
"./cookies": "./dist/lib/cookies.js"
|
|
10
|
+
},
|
|
11
|
+
"bin": {
|
|
12
|
+
"yc": "dist/cli.js"
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist/",
|
|
16
|
+
"scripts/",
|
|
17
|
+
"SKILL.md",
|
|
18
|
+
"LICENSE",
|
|
19
|
+
"README.md"
|
|
20
|
+
],
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "tsc && chmod +x dist/cli.js",
|
|
23
|
+
"dev": "tsc --watch",
|
|
24
|
+
"prepublishOnly": "npm run build",
|
|
25
|
+
"postinstall": "node scripts/postinstall.js",
|
|
26
|
+
"preuninstall": "node scripts/preuninstall.js",
|
|
27
|
+
"start": "node dist/cli.js"
|
|
28
|
+
},
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=22"
|
|
31
|
+
},
|
|
32
|
+
"keywords": [
|
|
33
|
+
"ycombinator",
|
|
34
|
+
"yc",
|
|
35
|
+
"startup-school",
|
|
36
|
+
"cli",
|
|
37
|
+
"claude-code",
|
|
38
|
+
"claude-code-skill"
|
|
39
|
+
],
|
|
40
|
+
"author": "lucasygu",
|
|
41
|
+
"license": "MIT",
|
|
42
|
+
"repository": {
|
|
43
|
+
"type": "git",
|
|
44
|
+
"url": "git+https://github.com/lucasygu/yc-cli.git"
|
|
45
|
+
},
|
|
46
|
+
"bugs": {
|
|
47
|
+
"url": "https://github.com/lucasygu/yc-cli/issues"
|
|
48
|
+
},
|
|
49
|
+
"dependencies": {
|
|
50
|
+
"@steipete/sweet-cookie": "^0.1.0",
|
|
51
|
+
"commander": "^13.1.0",
|
|
52
|
+
"kleur": "^4.1.5"
|
|
53
|
+
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"@types/node": "^22.0.0",
|
|
56
|
+
"typescript": "^5.7.0"
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Post-install script for yc-cli.
|
|
4
|
+
*
|
|
5
|
+
* Sets up Claude Code skill by creating a symlink:
|
|
6
|
+
* ~/.claude/skills/yc-cli -> <npm-package-location>
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { existsSync, mkdirSync, unlinkSync, symlinkSync, lstatSync, readlinkSync, rmSync, readFileSync, writeFileSync } from 'fs';
|
|
10
|
+
import { join, dirname } from 'path';
|
|
11
|
+
import { fileURLToPath } from 'url';
|
|
12
|
+
import { homedir } from 'os';
|
|
13
|
+
|
|
14
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
15
|
+
const __dirname = dirname(__filename);
|
|
16
|
+
|
|
17
|
+
const PACKAGE_ROOT = join(__dirname, '..');
|
|
18
|
+
const SKILL_DIR = join(homedir(), '.claude', 'skills');
|
|
19
|
+
const SKILL_LINK = join(SKILL_DIR, 'yc-cli');
|
|
20
|
+
|
|
21
|
+
function setupClaudeSkill() {
|
|
22
|
+
try {
|
|
23
|
+
if (!existsSync(SKILL_DIR)) {
|
|
24
|
+
mkdirSync(SKILL_DIR, { recursive: true });
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (existsSync(SKILL_LINK)) {
|
|
28
|
+
try {
|
|
29
|
+
const stats = lstatSync(SKILL_LINK);
|
|
30
|
+
if (stats.isSymbolicLink()) {
|
|
31
|
+
const currentTarget = readlinkSync(SKILL_LINK);
|
|
32
|
+
if (currentTarget === PACKAGE_ROOT) {
|
|
33
|
+
console.log('[yc-cli] Claude Code skill already configured.');
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
unlinkSync(SKILL_LINK);
|
|
37
|
+
} else {
|
|
38
|
+
rmSync(SKILL_LINK, { recursive: true });
|
|
39
|
+
}
|
|
40
|
+
} catch (err) {
|
|
41
|
+
console.log(`[yc-cli] Warning: ${err.message}`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
symlinkSync(PACKAGE_ROOT, SKILL_LINK);
|
|
46
|
+
console.log('[yc-cli] Claude Code skill installed:');
|
|
47
|
+
console.log(`[yc-cli] ~/.claude/skills/yc-cli -> ${PACKAGE_ROOT}`);
|
|
48
|
+
return true;
|
|
49
|
+
} catch (error) {
|
|
50
|
+
console.error(`[yc-cli] Failed to set up skill: ${error.message}`);
|
|
51
|
+
console.log('[yc-cli] You can manually create the symlink:');
|
|
52
|
+
console.log(`[yc-cli] ln -s "${PACKAGE_ROOT}" "${SKILL_LINK}"`);
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Patch @steipete/sweet-cookie keychain timeout bug.
|
|
59
|
+
*/
|
|
60
|
+
function patchSweetCookieTimeout() {
|
|
61
|
+
const target = join(
|
|
62
|
+
PACKAGE_ROOT,
|
|
63
|
+
'node_modules',
|
|
64
|
+
'@steipete',
|
|
65
|
+
'sweet-cookie',
|
|
66
|
+
'dist',
|
|
67
|
+
'providers',
|
|
68
|
+
'chromeSqliteMac.js'
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
if (!existsSync(target)) return;
|
|
72
|
+
|
|
73
|
+
try {
|
|
74
|
+
const content = readFileSync(target, 'utf-8');
|
|
75
|
+
const needle = 'timeoutMs: 3_000,';
|
|
76
|
+
if (!content.includes(needle)) return;
|
|
77
|
+
const patched = content.replace(needle, 'timeoutMs: options.timeoutMs ?? 30_000,');
|
|
78
|
+
writeFileSync(target, patched, 'utf-8');
|
|
79
|
+
console.log('[yc-cli] Patched sweet-cookie keychain timeout (3s -> 30s).');
|
|
80
|
+
} catch (err) {
|
|
81
|
+
console.log(`[yc-cli] Warning: could not patch sweet-cookie: ${err.message}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Patch node:sqlite BigInt overflow on Node < 24.4.
|
|
87
|
+
*/
|
|
88
|
+
function patchSweetCookieBigInt() {
|
|
89
|
+
const target = join(
|
|
90
|
+
PACKAGE_ROOT,
|
|
91
|
+
'node_modules',
|
|
92
|
+
'@steipete',
|
|
93
|
+
'sweet-cookie',
|
|
94
|
+
'dist',
|
|
95
|
+
'providers',
|
|
96
|
+
'chromeSqlite',
|
|
97
|
+
'shared.js'
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
if (!existsSync(target)) return;
|
|
101
|
+
|
|
102
|
+
try {
|
|
103
|
+
const content = readFileSync(target, 'utf-8');
|
|
104
|
+
const needle = 'SELECT name, value, host_key, path, expires_utc, samesite, encrypted_value,';
|
|
105
|
+
if (!content.includes(needle)) return;
|
|
106
|
+
const patched = content.replace(
|
|
107
|
+
needle,
|
|
108
|
+
'SELECT name, value, host_key, path, CAST(expires_utc AS TEXT) AS expires_utc, samesite, encrypted_value,'
|
|
109
|
+
);
|
|
110
|
+
writeFileSync(target, patched, 'utf-8');
|
|
111
|
+
console.log('[yc-cli] Patched sweet-cookie BigInt overflow.');
|
|
112
|
+
} catch (err) {
|
|
113
|
+
console.log(`[yc-cli] Warning: could not patch sweet-cookie BigInt: ${err.message}`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function main() {
|
|
118
|
+
console.log('[yc-cli] Running post-install...');
|
|
119
|
+
const success = setupClaudeSkill();
|
|
120
|
+
patchSweetCookieTimeout();
|
|
121
|
+
patchSweetCookieBigInt();
|
|
122
|
+
console.log('');
|
|
123
|
+
console.log('[yc-cli] Installation complete!');
|
|
124
|
+
if (success) {
|
|
125
|
+
console.log('[yc-cli] Use /yc in Claude Code, or run: yc --help');
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
main();
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Pre-uninstall script for yc-cli.
|
|
4
|
+
*
|
|
5
|
+
* Removes the Claude Code skill symlink at ~/.claude/skills/yc-cli
|
|
6
|
+
* (only if it points to this package).
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { existsSync, unlinkSync, lstatSync, readlinkSync } from 'fs';
|
|
10
|
+
import { join, dirname } from 'path';
|
|
11
|
+
import { fileURLToPath } from 'url';
|
|
12
|
+
import { homedir } from 'os';
|
|
13
|
+
|
|
14
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
15
|
+
const __dirname = dirname(__filename);
|
|
16
|
+
|
|
17
|
+
const PACKAGE_ROOT = join(__dirname, '..');
|
|
18
|
+
const SKILL_LINK = join(homedir(), '.claude', 'skills', 'yc-cli');
|
|
19
|
+
|
|
20
|
+
function main() {
|
|
21
|
+
console.log('[yc-cli] Running pre-uninstall...');
|
|
22
|
+
|
|
23
|
+
if (!existsSync(SKILL_LINK)) {
|
|
24
|
+
console.log('[yc-cli] No skill symlink found, nothing to clean up.');
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
const stats = lstatSync(SKILL_LINK);
|
|
30
|
+
if (!stats.isSymbolicLink()) {
|
|
31
|
+
console.log('[yc-cli] Skill path is not a symlink, leaving it alone.');
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const target = readlinkSync(SKILL_LINK);
|
|
36
|
+
if (target === PACKAGE_ROOT || target.includes('node_modules/@lucasygu/yc-cli')) {
|
|
37
|
+
unlinkSync(SKILL_LINK);
|
|
38
|
+
console.log('[yc-cli] Removed Claude Code skill symlink.');
|
|
39
|
+
} else {
|
|
40
|
+
console.log('[yc-cli] Skill symlink points elsewhere, leaving it alone.');
|
|
41
|
+
}
|
|
42
|
+
} catch (error) {
|
|
43
|
+
console.error(`[yc-cli] Warning: Could not remove skill: ${error.message}`);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
console.log('[yc-cli] Uninstall cleanup complete.');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
main();
|