hacklab 0.22.3 → 26.921.2

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 (259) hide show
  1. package/README.md +39 -415
  2. package/dist/index.js +317 -23
  3. package/package.json +22 -40
  4. package/LICENSE +0 -21
  5. package/dist/agent-handoff.d.ts +0 -51
  6. package/dist/agent-handoff.d.ts.map +0 -1
  7. package/dist/agent-handoff.js +0 -91
  8. package/dist/agent-handoff.js.map +0 -1
  9. package/dist/api-client.d.ts +0 -18
  10. package/dist/api-client.d.ts.map +0 -1
  11. package/dist/api-client.js +0 -40
  12. package/dist/api-client.js.map +0 -1
  13. package/dist/belt.d.ts +0 -25
  14. package/dist/belt.d.ts.map +0 -1
  15. package/dist/belt.js +0 -113
  16. package/dist/belt.js.map +0 -1
  17. package/dist/card.d.ts +0 -66
  18. package/dist/card.d.ts.map +0 -1
  19. package/dist/card.js +0 -151
  20. package/dist/card.js.map +0 -1
  21. package/dist/cli.d.ts +0 -2
  22. package/dist/cli.d.ts.map +0 -1
  23. package/dist/cli.js +0 -238
  24. package/dist/cli.js.map +0 -1
  25. package/dist/commands/book.d.ts +0 -14
  26. package/dist/commands/book.d.ts.map +0 -1
  27. package/dist/commands/book.js +0 -90
  28. package/dist/commands/book.js.map +0 -1
  29. package/dist/commands/chat.d.ts +0 -36
  30. package/dist/commands/chat.d.ts.map +0 -1
  31. package/dist/commands/chat.js +0 -642
  32. package/dist/commands/chat.js.map +0 -1
  33. package/dist/commands/config.d.ts +0 -2
  34. package/dist/commands/config.d.ts.map +0 -1
  35. package/dist/commands/config.js +0 -84
  36. package/dist/commands/config.js.map +0 -1
  37. package/dist/commands/daemon.d.ts +0 -19
  38. package/dist/commands/daemon.d.ts.map +0 -1
  39. package/dist/commands/daemon.js +0 -72
  40. package/dist/commands/daemon.js.map +0 -1
  41. package/dist/commands/drop.d.ts +0 -12
  42. package/dist/commands/drop.d.ts.map +0 -1
  43. package/dist/commands/drop.js +0 -103
  44. package/dist/commands/drop.js.map +0 -1
  45. package/dist/commands/drops.d.ts +0 -9
  46. package/dist/commands/drops.d.ts.map +0 -1
  47. package/dist/commands/drops.js +0 -96
  48. package/dist/commands/drops.js.map +0 -1
  49. package/dist/commands/essay.d.ts +0 -40
  50. package/dist/commands/essay.d.ts.map +0 -1
  51. package/dist/commands/essay.js +0 -651
  52. package/dist/commands/essay.js.map +0 -1
  53. package/dist/commands/hackathon.d.ts +0 -2
  54. package/dist/commands/hackathon.d.ts.map +0 -1
  55. package/dist/commands/hackathon.js +0 -893
  56. package/dist/commands/hackathon.js.map +0 -1
  57. package/dist/commands/hacker.d.ts +0 -2
  58. package/dist/commands/hacker.d.ts.map +0 -1
  59. package/dist/commands/hacker.js +0 -87
  60. package/dist/commands/hacker.js.map +0 -1
  61. package/dist/commands/jobs.d.ts +0 -18
  62. package/dist/commands/jobs.d.ts.map +0 -1
  63. package/dist/commands/jobs.js +0 -138
  64. package/dist/commands/jobs.js.map +0 -1
  65. package/dist/commands/keys.d.ts +0 -2
  66. package/dist/commands/keys.d.ts.map +0 -1
  67. package/dist/commands/keys.js +0 -138
  68. package/dist/commands/keys.js.map +0 -1
  69. package/dist/commands/login.d.ts +0 -83
  70. package/dist/commands/login.d.ts.map +0 -1
  71. package/dist/commands/login.js +0 -236
  72. package/dist/commands/login.js.map +0 -1
  73. package/dist/commands/logout.d.ts +0 -2
  74. package/dist/commands/logout.d.ts.map +0 -1
  75. package/dist/commands/logout.js +0 -22
  76. package/dist/commands/logout.js.map +0 -1
  77. package/dist/commands/org-jobs.d.ts +0 -50
  78. package/dist/commands/org-jobs.d.ts.map +0 -1
  79. package/dist/commands/org-jobs.js +0 -383
  80. package/dist/commands/org-jobs.js.map +0 -1
  81. package/dist/commands/org.d.ts +0 -67
  82. package/dist/commands/org.d.ts.map +0 -1
  83. package/dist/commands/org.js +0 -1244
  84. package/dist/commands/org.js.map +0 -1
  85. package/dist/commands/ping.d.ts +0 -2
  86. package/dist/commands/ping.d.ts.map +0 -1
  87. package/dist/commands/ping.js +0 -50
  88. package/dist/commands/ping.js.map +0 -1
  89. package/dist/commands/profile.d.ts +0 -72
  90. package/dist/commands/profile.d.ts.map +0 -1
  91. package/dist/commands/profile.js +0 -672
  92. package/dist/commands/profile.js.map +0 -1
  93. package/dist/commands/project.d.ts +0 -2
  94. package/dist/commands/project.d.ts.map +0 -1
  95. package/dist/commands/project.js +0 -583
  96. package/dist/commands/project.js.map +0 -1
  97. package/dist/commands/referral.d.ts +0 -6
  98. package/dist/commands/referral.d.ts.map +0 -1
  99. package/dist/commands/referral.js +0 -93
  100. package/dist/commands/referral.js.map +0 -1
  101. package/dist/commands/rtfm.d.ts +0 -9
  102. package/dist/commands/rtfm.d.ts.map +0 -1
  103. package/dist/commands/rtfm.js +0 -114
  104. package/dist/commands/rtfm.js.map +0 -1
  105. package/dist/commands/scan.d.ts +0 -13
  106. package/dist/commands/scan.d.ts.map +0 -1
  107. package/dist/commands/scan.js +0 -286
  108. package/dist/commands/scan.js.map +0 -1
  109. package/dist/commands/scout.d.ts +0 -18
  110. package/dist/commands/scout.d.ts.map +0 -1
  111. package/dist/commands/scout.js +0 -232
  112. package/dist/commands/scout.js.map +0 -1
  113. package/dist/commands/setup-rail.d.ts +0 -58
  114. package/dist/commands/setup-rail.d.ts.map +0 -1
  115. package/dist/commands/setup-rail.js +0 -135
  116. package/dist/commands/setup-rail.js.map +0 -1
  117. package/dist/commands/setup.d.ts +0 -29
  118. package/dist/commands/setup.d.ts.map +0 -1
  119. package/dist/commands/setup.js +0 -442
  120. package/dist/commands/setup.js.map +0 -1
  121. package/dist/commands/sync.d.ts +0 -9
  122. package/dist/commands/sync.d.ts.map +0 -1
  123. package/dist/commands/sync.js +0 -345
  124. package/dist/commands/sync.js.map +0 -1
  125. package/dist/commands/update.d.ts +0 -2
  126. package/dist/commands/update.d.ts.map +0 -1
  127. package/dist/commands/update.js +0 -92
  128. package/dist/commands/update.js.map +0 -1
  129. package/dist/commands/whoami.d.ts +0 -2
  130. package/dist/commands/whoami.d.ts.map +0 -1
  131. package/dist/commands/whoami.js +0 -41
  132. package/dist/commands/whoami.js.map +0 -1
  133. package/dist/config.d.ts +0 -78
  134. package/dist/config.d.ts.map +0 -1
  135. package/dist/config.js +0 -93
  136. package/dist/config.js.map +0 -1
  137. package/dist/daily-sync.d.ts +0 -150
  138. package/dist/daily-sync.d.ts.map +0 -1
  139. package/dist/daily-sync.js +0 -665
  140. package/dist/daily-sync.js.map +0 -1
  141. package/dist/dossier.d.ts +0 -73
  142. package/dist/dossier.d.ts.map +0 -1
  143. package/dist/dossier.js +0 -141
  144. package/dist/dossier.js.map +0 -1
  145. package/dist/field-normalize.d.ts +0 -4
  146. package/dist/field-normalize.d.ts.map +0 -1
  147. package/dist/field-normalize.js +0 -8
  148. package/dist/field-normalize.js.map +0 -1
  149. package/dist/index.d.ts +0 -3
  150. package/dist/index.d.ts.map +0 -1
  151. package/dist/index.js.map +0 -1
  152. package/dist/job-fields.d.ts +0 -47
  153. package/dist/job-fields.d.ts.map +0 -1
  154. package/dist/job-fields.js +0 -148
  155. package/dist/job-fields.js.map +0 -1
  156. package/dist/machine.d.ts +0 -15
  157. package/dist/machine.d.ts.map +0 -1
  158. package/dist/machine.js +0 -64
  159. package/dist/machine.js.map +0 -1
  160. package/dist/org-fields.d.ts +0 -57
  161. package/dist/org-fields.d.ts.map +0 -1
  162. package/dist/org-fields.js +0 -167
  163. package/dist/org-fields.js.map +0 -1
  164. package/dist/posthog.d.ts +0 -38
  165. package/dist/posthog.d.ts.map +0 -1
  166. package/dist/posthog.js +0 -224
  167. package/dist/posthog.js.map +0 -1
  168. package/dist/project-fields.d.ts +0 -40
  169. package/dist/project-fields.d.ts.map +0 -1
  170. package/dist/project-fields.js +0 -79
  171. package/dist/project-fields.js.map +0 -1
  172. package/dist/prompt-consent.d.ts +0 -73
  173. package/dist/prompt-consent.d.ts.map +0 -1
  174. package/dist/prompt-consent.js +0 -191
  175. package/dist/prompt-consent.js.map +0 -1
  176. package/dist/prompt-stats.d.ts +0 -226
  177. package/dist/prompt-stats.d.ts.map +0 -1
  178. package/dist/prompt-stats.js +0 -561
  179. package/dist/prompt-stats.js.map +0 -1
  180. package/dist/referral.d.ts +0 -14
  181. package/dist/referral.d.ts.map +0 -1
  182. package/dist/referral.js +0 -28
  183. package/dist/referral.js.map +0 -1
  184. package/dist/registry.d.ts +0 -15
  185. package/dist/registry.d.ts.map +0 -1
  186. package/dist/registry.js +0 -180
  187. package/dist/registry.js.map +0 -1
  188. package/dist/resolve-command.d.ts +0 -20
  189. package/dist/resolve-command.d.ts.map +0 -1
  190. package/dist/resolve-command.js +0 -21
  191. package/dist/resolve-command.js.map +0 -1
  192. package/dist/scanners/antigravity.d.ts +0 -41
  193. package/dist/scanners/antigravity.d.ts.map +0 -1
  194. package/dist/scanners/antigravity.js +0 -210
  195. package/dist/scanners/antigravity.js.map +0 -1
  196. package/dist/scanners/github-copilot-telemetry.d.ts +0 -23
  197. package/dist/scanners/github-copilot-telemetry.d.ts.map +0 -1
  198. package/dist/scanners/github-copilot-telemetry.js +0 -220
  199. package/dist/scanners/github-copilot-telemetry.js.map +0 -1
  200. package/dist/scanners/github-copilot.d.ts +0 -68
  201. package/dist/scanners/github-copilot.d.ts.map +0 -1
  202. package/dist/scanners/github-copilot.js +0 -262
  203. package/dist/scanners/github-copilot.js.map +0 -1
  204. package/dist/scanners/incremental.d.ts +0 -282
  205. package/dist/scanners/incremental.d.ts.map +0 -1
  206. package/dist/scanners/incremental.js +0 -788
  207. package/dist/scanners/incremental.js.map +0 -1
  208. package/dist/scanners/index.d.ts +0 -116
  209. package/dist/scanners/index.d.ts.map +0 -1
  210. package/dist/scanners/index.js +0 -726
  211. package/dist/scanners/index.js.map +0 -1
  212. package/dist/scanners/sqlite.d.ts +0 -18
  213. package/dist/scanners/sqlite.d.ts.map +0 -1
  214. package/dist/scanners/sqlite.js +0 -63
  215. package/dist/scanners/sqlite.js.map +0 -1
  216. package/dist/scanners/util.d.ts +0 -112
  217. package/dist/scanners/util.d.ts.map +0 -1
  218. package/dist/scanners/util.js +0 -167
  219. package/dist/scanners/util.js.map +0 -1
  220. package/dist/session.d.ts +0 -106
  221. package/dist/session.d.ts.map +0 -1
  222. package/dist/session.js +0 -191
  223. package/dist/session.js.map +0 -1
  224. package/dist/share-card-colors.d.ts +0 -19
  225. package/dist/share-card-colors.d.ts.map +0 -1
  226. package/dist/share-card-colors.js +0 -36
  227. package/dist/share-card-colors.js.map +0 -1
  228. package/dist/share-card.d.ts +0 -31
  229. package/dist/share-card.d.ts.map +0 -1
  230. package/dist/share-card.js +0 -380
  231. package/dist/share-card.js.map +0 -1
  232. package/dist/share.d.ts +0 -17
  233. package/dist/share.d.ts.map +0 -1
  234. package/dist/share.js +0 -75
  235. package/dist/share.js.map +0 -1
  236. package/dist/sync.d.ts +0 -100
  237. package/dist/sync.d.ts.map +0 -1
  238. package/dist/sync.js +0 -304
  239. package/dist/sync.js.map +0 -1
  240. package/dist/ui.d.ts +0 -33
  241. package/dist/ui.d.ts.map +0 -1
  242. package/dist/ui.js +0 -127
  243. package/dist/ui.js.map +0 -1
  244. package/dist/utils/npmGlobal.d.ts +0 -30
  245. package/dist/utils/npmGlobal.d.ts.map +0 -1
  246. package/dist/utils/npmGlobal.js +0 -69
  247. package/dist/utils/npmGlobal.js.map +0 -1
  248. package/dist/utils/openBrowser.d.ts +0 -14
  249. package/dist/utils/openBrowser.d.ts.map +0 -1
  250. package/dist/utils/openBrowser.js +0 -47
  251. package/dist/utils/openBrowser.js.map +0 -1
  252. package/dist/utils/updateCheck.d.ts +0 -16
  253. package/dist/utils/updateCheck.d.ts.map +0 -1
  254. package/dist/utils/updateCheck.js +0 -111
  255. package/dist/utils/updateCheck.js.map +0 -1
  256. package/dist/utils/waitForEnter.d.ts +0 -12
  257. package/dist/utils/waitForEnter.d.ts.map +0 -1
  258. package/dist/utils/waitForEnter.js +0 -43
  259. package/dist/utils/waitForEnter.js.map +0 -1
package/README.md CHANGED
@@ -1,453 +1,77 @@
1
- # hacklab
1
+ # Hacklab CLI
2
2
 
3
- The terminal-native way to join [Hacklab](https://hacklab.so) — a social network
4
- for AI-native hackers. Sign in to Hacklab, scan your local AI token usage, share
5
- a card.
3
+ A CLI for updating your Hacklab bio and social links with your coding agent.
6
4
 
7
- ## Install
5
+ Requires Node.js 22 or newer. Bun 1.4.2 or newer is also supported.
8
6
 
9
- Install, then run setup:
10
-
11
- ```bash
12
- curl -fsSL https://hacklab.so/install | sh
13
- hacklab setup
7
+ ```sh
8
+ npm install --global hacklab@beta
9
+ hacklab --help
14
10
  ```
15
11
 
16
- The script checks for Node 20+ and installs the CLI globally — that's all it
17
- does. After it, `hacklab` is a real command on your PATH, and `hacklab setup` is
18
- the guided first run: it scans this machine's AI usage, signs you in to Hacklab,
19
- uploads your usage, and starts the background sync. If you already have Node 20
20
- or newer (including a version manager), you can skip the script:
21
-
22
- ```bash
23
- npm i -g hacklab@latest
24
- hacklab setup
25
- ```
12
+ For Bun, use `bun install --global hacklab@beta` and `bunx --bun hacklab@beta --help`.
26
13
 
27
- `login`, `scan`, and `sync` all still exist as standalone commands for re-running
28
- one piece on its own.
14
+ Copy the agent instructions from the signed-in onboarding dialog. They contain no credentials. When you upload, the CLI opens a browser page to sign in with GitHub and authorize the CLI through Better Auth. Check that the code matches your terminal before approving.
29
15
 
30
- ### Windows
16
+ ## Preview a profile
31
17
 
32
- On native Windows (PowerShell), use the `.ps1` installer instead — it checks
33
- for Node 20+ and installs the CLI globally, then points you at `hacklab setup`:
18
+ Create a local JSON file containing a one-line `bio` (at most 160 characters) and a `socials` object. Supported social keys are `github`, `linkedin`, `x`, `instagram`, `youtube`, `huggingface`, `producthunt`, and `goodreads`. Use full HTTPS profile URLs. Omitted links remain unchanged; `null` removes a link.
34
19
 
35
- ```powershell
36
- irm https://hacklab.so/install.ps1 | iex
20
+ ```sh
21
+ hacklab onboard --file /path/to/profile.json
37
22
  ```
38
23
 
39
- The CLI reads your agents' usage from your Windows home (`%USERPROFILE%\.claude`,
40
- `.codex`, `.grok`, and Cursor's native tracking DB), and `hacklab daemon` registers the
41
- background sync as two Task Scheduler tasks. If you run Claude Code or Codex
42
- **inside WSL**, install there instead with the `curl … | sh` command above, run
43
- from your WSL shell.
44
-
45
- The installer also enables local PowerShell scripts for your user
46
- (`Set-ExecutionPolicy -Scope CurrentUser RemoteSigned`) so the `hacklab` command
47
- runs. If you install manually instead (`npm i -g hacklab@latest`) and PowerShell
48
- says *"running scripts is disabled on this system"*, run that one line yourself,
49
- or call `hacklab.cmd …` (which no execution policy blocks).
50
-
51
- The `curl | sh` installer itself only installs — it never touches your session.
24
+ This validates and previews locally without making a network request.
52
25
 
53
- The CLI prints a one-line nudge when a newer version is published (checked at
54
- most once a day); update with `npm i -g hacklab@latest`.
26
+ ## Upload after approval
55
27
 
56
- ## Scan
28
+ Review the exact bio and links first. Agents must ask the user for approval before using `--confirm-upload`.
57
29
 
30
+ ```sh
31
+ hacklab onboard --file /path/to/profile.json --confirm-upload
58
32
  ```
59
- login → scan this machine → upload → receipt → share on X → daemon on
60
- ```
61
-
62
- `hacklab scan` requires `hacklab login`. It reads local AI token usage from
63
- Claude Code, Codex, Cursor, OpenClaw, Hermes, OpenCode, Grok Build, GitHub Copilot,
64
- and Antigravity, uploads it to your profile, and prints a text usage receipt
65
- without displaying a card or image in the terminal. Then it asks whether to
66
- share a card on X, using the live account's rank, belt, and streak.
67
- A successful scan summons the daemon so the card stays current; `hacklab scan
68
- --no-daemon` skips the schedule, and `hacklab daemon off` tears it down.
69
- Skip the whole command if this computer isn't yours — it would upload that
70
- machine's usage to your profile. Cursor users with no API key are offered one
71
- (local Cursor data is only an estimate).
72
-
73
- ## Commands
74
-
75
- - `hacklab setup` — the guided first run: scan, sign-in, one question
76
- about syncing your prompt activity, upload, background sync on. Safe to
77
- re-run; it checks your saved session with the server first, skips whatever is
78
- already done, and stops early once everything is. If that session has expired
79
- or been revoked it signs you in again instead of reporting you as set up; if
80
- the server can't be reached it says so and carries on with what's on disk.
81
- - `hacklab scan` — scan this machine, upload to your profile, share the card.
82
- Requires login. Summons the daemon afterwards (`--no-daemon` to skip).
83
- - `hacklab sync` — re-scan local AI usage and sync it to your profile.
84
- - `hacklab daemon` — summon the daemon: two OS-native background jobs (launchd on
85
- macOS, systemd user timers on Linux, Task Scheduler tasks on Windows) so your
86
- tokens, rank, and streak stay current without you running anything. A **tick
87
- every minute** reads only what your tools appended since the last run — no
88
- network call at all on a minute where nothing happened — and a **full sync once
89
- a day** re-scans everything and repairs whatever the tick missed. With prompt
90
- sync on (see below), the same minutely read also pushes your prompt activity.
91
- No daemon, no streak. Re-running it is idempotent; `hacklab daemon off` tears both down, and
92
- `hacklab logout` removes them too. On a platform we can't schedule (BSD, a
93
- locked-down box) it prints the commands to schedule yourself instead of
94
- pretending it worked. `hacklab sync --install-daily` still forwards here.
95
- - `hacklab whoami` — show who you're logged in as.
96
- - `hacklab drop "message"` — post a drop to your feed (`-u <url>` to attach a
97
- link). Human output prints its profile URL; `--json` returns a stable envelope
98
- (`schemaVersion`, `id`, `path`, full `url`).
99
- - `hacklab chat` — open the live channel (typing posts; this is the default, so
100
- bare `hacklab chat` and `hacklab chat live` are the same). Other subcommands:
101
- `tail`, `post`, `history`, `dms`, `dm <handle>`, `flag`. Author handles are
102
- coloured by belt rank. Add `--json` to any non-interactive subcommand for
103
- machine-readable output an agent can drive.
104
- - `hacklab login` — sign in to Hacklab (creates an account if you don't have one).
105
- - `hacklab logout` — clear your saved session on this machine.
106
- - `hacklab config <key> <value>` — set config (`cursor-api-key`, `cursor-email`,
107
- `prompt-sync`).
108
- Bare `hacklab config` prints the effective values and where each came from.
109
- - `hacklab project` — agent help for publishing a project to your profile.
110
- `project add --title "…" [--repo <git url>] [--url <live url>] [--desc "…"]`
111
- posts it (`--json` for agents); one of `--repo`/`--url` is required. `--repo`
112
- takes any git host; a github.com `--url` with no `--repo` is treated as the
113
- repo. A github repo is probed for visibility so a private one stays hidden on
114
- the web; `--private`/`--public` override that. Re-run with the same title to
115
- update. `project list` shows yours, `project view <slug>` prints one full
116
- page, and `project edit <slug> [--title/--desc/--repo/--url/--private/--public]`
117
- changes fields — editing a GitHub-synced project ends that sync, so it needs
118
- `--yes`. `project delete <slug>` removes one; it confirms first, and `--yes`
119
- is required when there's no terminal to ask (scripts, agents, `--json`).
120
- - `hacklab essay` — agent help for posting an essay. `essay post --title "…"
121
- --content <md>` publishes it (`--file <path.md>`, or a bare `<path.md>`, for
122
- a file on disk; `--json` for agents). `essay update <id>` replaces the body at
123
- the same URL. `essay list` shows yours — `essay list <handle>` a hacker's,
124
- `essay list org <slug>` an org's, `--page N` for more. `essay view <id>` reads
125
- one essay. `essay delete <id>` removes one of yours; like `project delete` it
126
- confirms first and needs `--yes` when nothing can ask.
127
- - `hacklab org` — hub for company management. If you already own a company, pick
128
- a field, type the new value, and it saves as you go. If you don't own one yet,
129
- it offers to claim or create. Subcommands: `hacklab org claim` (take ownership
130
- of a YC-seeded company you're a member of, or whose domain matches your login
131
- email) and `hacklab org create` (register a brand-new company from the CLI).
132
- - `hacklab org access` — manage who controls a company, and at what level.
133
- Several people can be on one company's access list, at one of two roles:
134
- **admin**, who can do everything (edit the profile, change this list, post
135
- jobs), and **recruiter**, who can only reach `hacklab org jobs`. Bare `org
136
- access` (or `org access list`) shows everyone and their role; `org access
137
- grant <handle> [--role admin|recruiter]` adds someone (defaulting to admin —
138
- re-granting an existing person changes their role); `org access revoke
139
- <handle>` removes them — including yourself, though the last remaining
140
- **admin** can't be removed or demoted. Only admins change the list; a
141
- recruiter can stand down but nothing else. Use `--org <slug>` when you're on
142
- more than one, and `--json` on any of them for agents.
143
- - `hacklab org jobs` — your company's Job Shop listings. Bare `org jobs` (or
144
- `org jobs list`) shows every listing you've posted and its status; `org jobs
145
- view <id>` reads one; `org jobs post` creates one, interactively or from
146
- flags (`--role`, `--description`, `--apply-url`, `--contact`, plus optional
147
- `--company`, `--company-url`, `--salary`, `--work-style`, `--min-belt`);
148
- `org jobs close <id>` takes a live one down early. A listing costs
149
- **$1,000**, so `post` can't finish in the terminal — it creates the listing
150
- and hands back a Stripe checkout link to open. Once that's paid we review it
151
- before it goes live, and it runs for 30 days. Admins and recruiters both
152
- reach all of this; `--json` on any subcommand for agents.
153
- - `hacklab jobs` — browse the Job Shop. Bare `hacklab jobs` (or `jobs list`,
154
- with `--limit 1-100`) lists what's hiring; `jobs view <id>` reads one listing
155
- in full with its apply link. Read-only — posting is `hacklab org jobs post`.
156
- `hacklab jo` resolves to `jobs`.
157
- - `hacklab profile` — view and edit your own profile. Bare `profile` (or
158
- `profile view`) shows it; `profile edit` is an org-style autosave editor;
159
- `profile set <field> <value>` writes one field (`--clear` unsets, handles like
160
- `x mattbratos` become full links); `profile set https://x.com/mattbratos`
161
- picks the field from the host (x, youtube, instagram, goodreads); `profile
162
- set readme --file profile.md` writes a long Markdown README without shell
163
- quoting; `profile apply profile.yaml` writes many fields in one shot. Fields:
164
- `name`, `bio`, `readme`, `website`, `blog`, `x`, `youtube`, `instagram`,
165
- `goodreads`, `rss`, `open-to-work`.
166
- Add `--json` to `view`/`set`/`apply` for machine-readable output.
167
- - `hacklab hacker <username> --json` — read the rich agent profile including
168
- links, counts, skills, recent projects, essays, and drops.
169
- - `hacklab hackathon` — RSVP, team up, and submit for a hacklab hackathon. Each
170
- event has a challenge mode — `open` (build anything, no theme or tracks),
171
- `theme` (one subject everyone builds to), or `tracks` (teams each pick one
172
- of several) — and the organizer may keep the theme/track list hidden from
173
- participants until the hackathon starts.
174
- `hackathon list [--past]` shows upcoming (or past) events; `hackathon view
175
- <slug>` shows one event's phase, highlights its next deadline (RSVP
176
- closes / teams lock / tracks lock / submissions due), and shows the
177
- challenge — the theme/tracks if revealed, or a note that it's announced
178
- when the hackathon starts; `hackathon rsvp <slug> [--token <t>]` RSVPs (an
179
- invite link from the organizer also works if you're not on the invite
180
- list); `hackathon invite <slug> --file <path>` or `--emails
181
- a@b.com,c@d.com` sends invites and lists every rejected line; `hackathon
182
- team create <slug> --name "X" [--summary S] [--max N] [--closed]` starts a
183
- team, `team join <slug> <teamSlug>` requests to join one, `team
184
- accept|reject <slug> <teamSlug> <handle>` decides a request, and `team list
185
- <slug>` lists teams; `hackathon track <slug> <teamSlug> <trackSlug>` sets a
186
- team's track (refused once the challenge is locked, or if this hackathon
187
- has no tracks); `hackathon tracks <slug>` prints the theme or track list on
188
- its own, honouring the same reveal rule; `hackathon submit <slug>
189
- <teamSlug> --title T --description D [--repo/--video/--site/--track]`
190
- submits a project; `hackathon export <slug> [--format csv|json] [--out
191
- <path>]` downloads the participant list (this contains personal data —
192
- handle exported files carefully). Every subcommand takes `--json` for
193
- agents.
194
- - `hacklab --version` / `hacklab --help`.
195
-
196
- ## Cursor usage
197
33
 
198
- Cursor is the one tool whose local data can't give exact numbers. Without a key
199
- the scanner estimates from Cursor's commit-tracking database (AI lines written
200
- × 30, all attributed to today, so no real daily history). With a key it reads
201
- exact per-event token counts and real dates from Cursor's API.
34
+ Confirmed onboarding first syncs usage, then saves the profile, sharing one browser authorization. Approval must cover both the profile and the usage report. Preview mode does neither. If syncing fails, onboarding stops before saving the profile.
202
35
 
203
- Supply a key three ways, highest priority first:
36
+ ## Sync usage
204
37
 
205
38
  ```sh
206
- hacklab --cursor-api-key <key> sync # flag: one run
207
- CURSOR_API_KEY=<key> hacklab sync # env var: one shell / CI
208
- hacklab config cursor-api-key <key> # config file: persists
39
+ hacklab sync
209
40
  ```
210
41
 
211
- `--cursor-email <email>` / `CURSOR_EMAIL` / `hacklab config cursor-email` set the
212
- account email, which resolves independently of the key. **If your key is a team
213
- key, set the email.** Cursor's usage endpoint returns every team member's events
214
- otherwise, and all of it would land on your profile as your own.
42
+ The CLI includes pinned `ccusage@20.0.24` and runs `ccusage session --json`. No separate ccusage installation is needed. Its stdout is posted unchanged to `/api/token-usage`. This report includes agent/session identifiers, tokens, costs, model breakdowns, and metadata, which can include project paths. Source code and transcript contents are not uploaded.
215
43
 
216
- If a key is set but Cursor rejects it, the scan says so and falls back to the
217
- local estimate — it won't quietly hand you an estimate you think is exact.
44
+ The API accepts the exact `{ session, totals }` envelope. `token_usage` stores each session with the upstream camelCase field names and nested JSON unchanged; only the authenticated `user_id` is added by the server. Totals are validated but not stored because they can be derived from the session rows. Repeated syncs replace counts for the same user, agent, and period rather than adding them. Missing sessions are retained, so syncing another machine does not delete earlier uploads. Reports are limited to 20 MB. The first upload uses browser authorization. Later commands reuse the saved session, checking that it is still valid; expired or revoked sessions open the browser again. Credentials are stored per Hacklab origin in `~/.config/hacklab/credentials/`, with owner-only directory and file permissions on Unix. Run `hacklab logout` to forget local authorization for the current Hacklab server.
218
45
 
219
- ## Prompt sync
220
-
221
- `sync` can also track *how* you prompt, not just how many tokens you burned.
222
- With it on, the same minutely tick that pushes your token counts also pushes
223
- prompt metadata — so your profile shows your coding sessions, how many you run
224
- at once, and your prompt counts, kept current rather than once a day. Every
225
- full sync carries whatever is still outstanding too, so a machine with no
226
- daemon gets its prompt activity up by running `hacklab sync` by hand. The daily
227
- full sync also adds a histogram of your prompt lengths — every bar an exact
228
- word count, and everything past the end of the axis as a list of length/count
229
- pairs instead, so a tail of unusually long prompts keeps its real lengths
230
- rather than piling into a final catch-all bar — and a prompt count per project.
231
- Sources are local Claude Code transcripts (`~/.claude/projects`), GitHub Copilot
232
- transcripts, and readable Antigravity CLI/IDE logs.
233
-
234
- **Only what you actually typed counts.** A transcript records far more than
235
- your prompts, and a lot of it is stored in the same shape: a background
236
- subagent's report, the body of a skill that got invoked, the echo of a slash
237
- command and its output, the marker left when you hit escape, bash-mode
238
- commands, hook output, and the context Claude Code injects into a turn. Left
239
- in, those were 39% of the "prompts" and 65% of the words on a typical machine —
240
- one `/browse` looked like a 7,268-word prompt. The scan drops any entry the
241
- harness flagged as its own, plus anything that *starts* with one of those
242
- markers. Starts with, not contains: a real prompt that quotes
243
- `<command-name>` while asking about it is still your prompt, and still counts.
244
-
245
- For Antigravity, an explicit-user-input entry counts only when it contains a
246
- nonempty `<USER_REQUEST>` body. That body is preserved verbatim, including blank
247
- lines; surrounding metadata, model-setting changes, and artifact-comment prose
248
- are excluded from both word counts and the `full`-tier sample. Empty request
249
- bodies, including artifact approvals, do not count as prompts. `created_at` is
250
- the preferred timestamp, with a valid `timestamp` as fallback. If neither is
251
- valid, the prompt stays undated — no timestamp is inferred.
252
-
253
- Nothing conversation-derived leaves your machine until you say so. The first
254
- interactive `sync` asks and remembers the answer for the disclosed sources.
255
- Adding IDE chats requires fresh consent from anyone whose previous answer
256
- covered only Claude Code; an earlier `none` remains a refusal. Until you answer
257
- again, unattended sync uploads tokens only. There are three tiers:
258
-
259
- | Tier | What leaves your machine |
260
- | ------- | ------------------------ |
261
- | `none` | token counts only — nothing prompt-related |
262
- | `stats` | + prompt counts, word counts, timestamps and session ids, synced continuously, plus the length histogram and per-project counts. Your prompt text never leaves the machine |
263
- | `full` | + a rolling sample of your most recent prompts (≤20k chars) sent with the daily sync, used only to estimate a technical-level score and then discarded server-side. Never stored |
264
-
265
- Project counts currently come from Claude Code and are matched by git `origin`
266
- remote, so a project count only lands on a project you've already added to
267
- hacklab. Missing remotes skip **project attribution**, not the aggregate prompt
268
- statistics or the separately consented text sample.
269
-
270
- Answer up front, without the prompt — the agent-friendly path:
271
-
272
- ```sh
273
- hacklab sync --share-prompt-sync # metadata only
274
- hacklab sync --share-prompt-sync=full # metadata + prompt sample
275
- hacklab sync --no-share-prompt-sync # refuse
276
- ```
277
-
278
- Change or revoke it any time:
46
+ ## Log in
279
47
 
280
48
  ```sh
281
- hacklab config prompt-sync none # stop syncing; nothing further is uploaded
282
- hacklab config # show the current tier
49
+ hacklab login
283
50
  ```
284
51
 
285
- The unattended daily sync and the minutely tick never ask. A machine that has
286
- never answered uploads token counts only.
287
-
288
- ## IDE and agent-chat coverage
52
+ Authorizes the CLI and saves the session without collecting or uploading usage. An existing valid session is reused. To check the authorization dialog again, run `hacklab logout` followed by `hacklab login`.
289
53
 
290
- **Copilot and Antigravity are token harnesses**, like Claude Code and Codex.
291
- They contribute to total tokens, daily usage, model breakdowns, rank, and arena.
292
- A Claude model used through Copilot counts under Copilot, not Claude Code.
293
- Model totals are another view of the same tokens, never an extra addition.
294
- No tokens are estimated from prompts, quota, subscription spend, or context size.
54
+ ## Log out
295
55
 
296
- | Source | Token usage | Consented prompts |
297
- | ------ | ----------- | ----------------- |
298
- | GitHub Copilot CLI | Persisted `session.shutdown.modelMetrics` in `${COPILOT_HOME:-~/.copilot}/session-state/<session-id>/events.jsonl`; available after shutdown. Supported local trace exports can supply completed calls before the next shutdown. | User messages in the same events file; synthetic user events excluded. |
299
- | GitHub Copilot in VS Code Stable / Insiders | Native Copilot `chat` spans in `User/globalStorage/github.copilot-chat/agent-traces.db` (including user profiles) or configured local OTel JSONL exports. Telemetry must already be enabled; ordinary chat transcripts do not contain usage. | Agent JSONL in `User/workspaceStorage/<workspace>/github.copilot-chat/transcripts/`; only the Copilot producer and user messages count. |
300
- | Antigravity CLI / GUI | `~/.gemini/{antigravity-cli,antigravity,antigravity-ide}/conversations/<UUID>.db`, where the supported SQLite `steps.metadata` usage schema exists. | Readable `brain/<UUID>/.system_generated/logs/transcript_full.jsonl` or `transcript.jsonl` under those roots, when present in the explicit-user-input format. Full transcript preferred; copies not counted together. |
301
- | Android Studio's built-in Gemini | Not supported: no reliable local, attributable usage schema was established. API/account dashboards can include other clients and cannot safely be counted as Studio usage. | Not supported. Saved Prompt Library entries are templates, not chat activity. |
302
-
303
- ### Accounting and deduplication
304
-
305
- - Copilot shutdown counters are cumulative across resumed sessions. Count the
306
- first snapshot and then only positive growth, dated to each shutdown.
307
- `inputTokens` includes cache reads/writes; `outputTokens` includes reasoning.
308
- Never add nested `agentMetrics` to the same root totals.
309
- - Copilot trace exports count only completed `chat` calls with an identifiable
310
- native Copilot owning agent. Root `invoke_agent` totals, metrics, and duplicate
311
- log events are not extra usage. File/SQLite copies share trace/span IDs and are
312
- deduplicated. Calls already covered by a session/model shutdown are suppressed;
313
- later resumed calls remain until the next shutdown covers them.
314
- - Claude/Codex agent delegates are excluded from Copilot telemetry so their native
315
- scanners own that usage. This is about the **agent**, not the model: BYOK Claude
316
- calls under a native Copilot agent still count as Copilot.
317
- - Antigravity counts uncached input + cached input + total output. Thinking and
318
- visible-answer fields are output subsets, not additions. Response IDs identify
319
- duplicate generations; conversation UUID + step index is the fallback when no
320
- response ID exists. Numeric model enums remain `antigravity-model-<id>` rather
321
- than being assigned an unverified model name.
322
-
323
- ### Local telemetry and coverage limits
324
-
325
- Hacklab never enables telemetry or overwrites harness settings. For VS Code,
326
- `github.copilot.chat.otel.dbSpanExporter.enabled` enables the local trace DB;
327
- alternatively use the documented OTel file exporter with an absolute
328
- `github.copilot.chat.otel.outfile`. The scanner reads that setting from default
329
- user/profile JSONC files, `${COPILOT_HOME:-~/.copilot}/otel/**/*.jsonl`, and
330
- `COPILOT_OTEL_FILE_EXPORTER_PATH` when supplied to Hacklab. Prompt/response content
331
- capture is **not needed**. Broken exporter records containing only `{}` cannot
332
- provide usage; prefer the local trace DB on affected VS Code versions.
333
- JSONL parsing supports the direct ReadableSpan form emitted by VS Code. The
334
- standalone CLI's exact exporter serialization is not a documented stable
335
- contract; unrecognized record forms are ignored, not interpreted as estimates.
336
-
337
- VS Code roots are `%APPDATA%/Code[ - Insiders]` on Windows,
338
- `~/Library/Application Support/Code[ - Insiders]` on macOS, and
339
- `${XDG_CONFIG_HOME:-~/.config}/Code[ - Insiders]` on Linux. Custom user-data roots,
340
- remote hosts, other Copilot IDEs, and generic chat exports are not swept.
341
- Trace calls wait for an identifiable completed owning-agent span. Unknown
342
- schemas/owners are not guessed. The read-only SQLite reader observes the main
343
- database; recent WAL-only writes become visible after the harness checkpoints.
344
- Antigravity's DB schema is reverse-engineered, not a vendor stability guarantee;
345
- opaque `.pb` histories and unsupported transcript formats remain unreadable.
346
-
347
- Only retained history is available. VS Code prompt transcripts normally retain
348
- 20 sessions per workspace plus active sessions; trace DB retention is also
349
- bounded. Missing prompt timestamps allow a histogram and `full`-tier sample,
350
- but **never** dated activity. Token records need their own real timestamp.
351
- The minutely tick fingerprints snapshot sources and replaces changed aggregates,
352
- not adds the whole snapshot again. Full syncs rebuild the same sources. Prompt
353
- sharing remains separately consented; incremental state contains no prompt text.
354
-
355
- Storage evidence:
356
- [Copilot usage schema](https://github.com/github/copilot-sdk/blob/main/nodejs/src/generated/session-events.ts),
357
- [shutdown/cache semantics](https://ccusage.com/guide/copilot/),
358
- [VS Code telemetry](https://code.visualstudio.com/docs/agents/guides/monitoring-agents),
359
- [VS Code trace DB](https://github.com/microsoft/vscode/blob/main/extensions/copilot/src/platform/otel/node/sqlite/otelSqliteStore.ts),
360
- [VS Code transcript writer](https://github.com/microsoft/vscode/blob/main/extensions/copilot/src/extension/chat/vscode-node/sessionTranscriptService.ts),
361
- [Antigravity DB metadata](https://github.com/Eneasf/antigravity-token-dashboard/blob/main/docs/TELEMETRY_SPEC.md),
362
- [CLI/GUI schema audit](https://github.com/mjacobs/agy-reader/blob/main/COMPATIBILITY.md),
363
- [readable Antigravity exporter](https://github.com/mehdawimohamed/antigravity-conversation-exporter/blob/main/export_chat.py),
364
- [Android Studio API-key usage](https://developer.android.com/studio/gemini/add-api-key).
365
-
366
- ## Choosing a backend
367
-
368
- Every command resolves which backend to talk to by the same precedence:
369
-
370
- 1. **`--env <name>`** on the command line (highest);
371
- 2. **`HACKLAB_APP_URL`** set in the environment;
372
- 3. **the backend you logged into** (saved in the session file);
373
- 4. **production** (`https://hacklab.so`) by default.
374
-
375
- ```bash
376
- hacklab login --env development # http://localhost:3000
377
- hacklab drop "hi" --env dev # the override applies to every command, not just login
378
- ```
379
-
380
- `--env` accepts the named backends `production` and `development` (any
381
- unambiguous abbreviation — `prod`, `dev` — plus the aliases `local`/`localhost`
382
- for development). Under the hood it just sets `HACKLAB_APP_URL` for that run, so
383
- an explicit `--env` overrides an inherited `HACKLAB_APP_URL`. To target any other
384
- backend, set `HACKLAB_APP_URL` to its base URL directly:
385
-
386
- ```bash
387
- HACKLAB_APP_URL=https://your-backend.example.com hacklab login
56
+ ```sh
57
+ hacklab logout
58
+ hacklab sync
388
59
  ```
389
60
 
390
- Note that **sessions are per-backend**: your token is only valid on the backend
391
- you logged into. Overriding `--env` (or `HACKLAB_APP_URL`) to a backend you
392
- haven't authenticated against will 401 until you `hacklab login` there.
393
- `hacklab whoami` shows the effective backend and warns when it differs from where
394
- you logged in.
395
-
396
- ## Environment
397
-
398
- - `HACKLAB_APP_URL` — explicit app base URL (read from the real environment; the
399
- CLI does not load any `.env` file). A command-line `--env` sets this for the
400
- run, so `--env` wins over an inherited value. See **Choosing a backend** for
401
- the full precedence.
402
- - `HACKLAB_SESSION_PATH` — custom path for the session file
403
- (default `~/.hacklab/session.json`).
404
- - `HACKLAB_DEV` — set to `1` to mark this as a developer run. `hacklab whoami`
405
- then always prints the backend host, including production (which it otherwise
406
- leaves out as noise). `0`, `false`, empty and unset are off.
407
- - `HACKLAB_NO_UPDATE_CHECK` — set to any value to disable the once-a-day
408
- "newer version available" nudge. The check is already skipped for piped /
409
- scripted / `--json` runs; this turns it off for interactive runs too.
410
- - `POSTHOG_API_KEY` / `POSTHOG_HOST` — override the built-in (public) analytics
411
- project/host. Set `HACKLAB_NO_TELEMETRY` (or the cross-tool `DO_NOT_TRACK`) to
412
- disable anonymous usage analytics entirely.
413
-
414
- ## Signing in
415
-
416
- `hacklab login` uses a **device flow**, everywhere (desktop or headless): it
417
- prints a short code and a URL like `https://hacklab.so/cli/login?code=857F-61CE`.
418
- Open that on any device with a browser; the code is prefilled. Approve the login —
419
- you sign in to Hacklab there with GitHub
420
- or Google, and if you don't have an account yet you create one on the spot. The
421
- terminal picks up the session as soon as you approve. No local server, no port
422
- forwarding, no localhost/app URL.
61
+ Logout clears the saved CLI credential for the current Hacklab server, even when offline. The next sync opens the authorization dialog on the Arena again. Your browser stays signed in. Other Hacklab servers' credentials are preserved; this command does not revoke the server session.
423
62
 
424
63
  ## Local development
425
64
 
426
- Requires Node 20+ and [pnpm](https://pnpm.io). From the repo root:
65
+ Building from source requires Bun 1.4.2 or newer; the published package runs with Node.js without Bun.
427
66
 
428
- ```bash
429
- pnpm install
430
- pnpm dev <command> # run from source (tsx, no build), e.g. `pnpm dev chat`
431
- pnpm build # compile to dist/
432
- pnpm test # run the vitest suite
433
- ```
434
-
435
- `pnpm dev` is `tsx src/index.ts`, so it defaults to the **production** backend
436
- like the published package. Point it elsewhere per-command with `--env
437
- development` (localhost) or `HACKLAB_APP_URL=<url>`. Use a throwaway session to
438
- avoid touching a real account:
67
+ From the Hacklab repository:
439
68
 
440
- ```bash
441
- HACKLAB_SESSION_PATH=/tmp/hl-test.json pnpm dev login --env development
69
+ ```sh
70
+ bun install --frozen-lockfile
71
+ bun run --cwd packages/cli build:dev
72
+ cd packages/cli
73
+ bun link
74
+ hacklab onboard --file /path/to/profile.json
442
75
  ```
443
76
 
444
- ## Telemetry
445
-
446
- The CLI sends anonymous usage analytics to a dedicated, public PostHog project
447
- (the write-only project key ships in the published package). It is opt-out: set
448
- `HACKLAB_NO_TELEMETRY=1` or the cross-tool `DO_NOT_TRACK=1` to disable it. See
449
- `src/posthog.ts`.
450
-
451
- ## License
452
-
453
- [MIT](./LICENSE) © Homebrew Hackers Club inc
77
+ Source runs and `build:dev` always use `http://localhost:3000`. Production builds embed `BETTER_AUTH_URL` from the build environment or `apps/web/.env` / `.env.local`; it must be an HTTPS origin. Set the production URL before running `bun run build` or publishing. No `--url` override is supported. Credentials are saved locally for reuse and are never copied into the prompt. Development and production credentials are stored separately.