@hackspot/cli 0.1.1

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 (3) hide show
  1. package/README.md +87 -0
  2. package/dist/index.js +66 -0
  3. package/package.json +27 -0
package/README.md ADDED
@@ -0,0 +1,87 @@
1
+ # hackspot
2
+
3
+ Turn a [hackspot.dev](https://hackspot.dev) idea into a project and hand it to your
4
+ AI coding agent. The website already generated the build playbook (pitch, why it
5
+ wins, scoped MVP, demo script) — this CLI fetches it by id, scaffolds the files an
6
+ agent reads, and launches that agent on the build.
7
+
8
+ It is **not** an LLM of its own: it drives your existing tooling (Claude Code,
9
+ Codex, opencode, or any command you pass).
10
+
11
+ Published as [`@hackspot/cli`](https://www.npmjs.com/package/@hackspot/cli); once
12
+ installed the command on your `PATH` is `hackspot`. Install instructions:
13
+ <https://docs.hackspot.dev/docs/install>.
14
+
15
+ ## 1. `login` — connect this machine
16
+
17
+ A device-code flow: the CLI shows a 6-digit code, you enter it on the website while
18
+ signed in, and the CLI is handed a token saved to `~/.hackspot/config.json`.
19
+
20
+ ```bash
21
+ npx @hackspot/cli login
22
+ # → shows a 6-digit code + opens https://hackspot.dev/app/cli
23
+ # enter the code there; the CLI saves your token
24
+ ```
25
+
26
+ Flags: `--dev` (target `http://localhost:3000`), `--base-url <url>`, `--no-open`.
27
+
28
+ ## 2. `list` — see the ideas you can build
29
+
30
+ Lists your saved ideas (newest first) with the id each `build` takes:
31
+
32
+ ```bash
33
+ npx @hackspot/cli list
34
+ # 42 🚀 Pitch Coach (fit 87)
35
+ # Real-time feedback on your demo delivery
36
+ # …
37
+ npx @hackspot/cli list --json # raw list, for scripting
38
+ ```
39
+
40
+ Flags: `--json`, `--dev` / `--base-url <url>`.
41
+
42
+ ## 3. `build` — scaffold an idea + launch your agent
43
+
44
+ Grab an idea's id from `hackspot list` (or its URL on the site, `/app/ideas/<id>`).
45
+
46
+ ```bash
47
+ npx @hackspot/cli build 42 # scaffolds ./<idea-slug>/ and launches your agent
48
+ npx @hackspot/cli build 42 ./my-app # choose the directory
49
+ ```
50
+
51
+ It writes three files into the target directory:
52
+
53
+ | File | Purpose |
54
+ | --------------- | ------------------------------------------------------------- |
55
+ | `AGENTS.md` | The playbook as the cross-tool context file agents auto-read. |
56
+ | `PROMPT.md` | The bootstrap instruction (paste into any agent). |
57
+ | `hackspot.json` | The structured idea (id, pitch, MVP, demo, event brief). |
58
+
59
+ Then it picks an agent and launches it in that directory, inheriting your terminal:
60
+
61
+ - `--agent claude` → `claude "<bootstrap prompt>"`
62
+ - `--agent codex` → `codex "<bootstrap prompt>"`
63
+ - `--agent opencode` → `opencode run "<bootstrap prompt>"`
64
+ - `--agent "<command>"` → any command; include `{prompt}` to substitute the bootstrap
65
+ prompt, otherwise it's appended as the final argument.
66
+
67
+ With no `--agent`, it auto-detects the first installed of `claude` → `codex` →
68
+ `opencode`.
69
+
70
+ Other flags: `--no-launch` (scaffold only, print the command), `--force` (allow a
71
+ non-empty directory), `--dev` / `--base-url <url>`.
72
+
73
+ ## `whoami` / `logout`
74
+
75
+ ```bash
76
+ npx @hackspot/cli whoami # show the linked account (validates the token)
77
+ npx @hackspot/cli logout # clear the local token
78
+ ```
79
+
80
+ ## Env
81
+
82
+ | Variable | Notes |
83
+ | -------------------- | ----------------------------------------------------------- |
84
+ | `HACKSPOT_TOKEN` | Overrides the saved token (CI / ephemeral shells). |
85
+ | `HACKSPOT_BASE_URL` | Default base URL; overridden by `--base-url` / `--dev`. |
86
+
87
+ Tokens last 90 days and are revocable from the website.
package/dist/index.js ADDED
@@ -0,0 +1,66 @@
1
+ #!/usr/bin/env node
2
+ import{spawnSync as P}from"node:child_process";import{existsSync as c,mkdirSync as p,readdirSync as g,writeFileSync as C}from"node:fs";import{join as W}from"node:path";import{parseArgs as d}from"node:util";import{chmodSync as S,mkdirSync as U,readFileSync as h,writeFileSync as y}from"node:fs";import{homedir as b}from"node:os";import{dirname as f,join as m}from"node:path";var v="https://hackspot.dev",u="http://localhost:3000";function x(){return m(b(),".hackspot","config.json")}function j(){try{return JSON.parse(h(x(),"utf8"))}catch{return{}}}function G(z){let J=x();U(f(J),{recursive:!0,mode:448});let Q={...j(),...z};y(J,`${JSON.stringify(Q,null,2)}
3
+ `,{mode:384});try{S(J,384)}catch{}}function N(){return process.env.HACKSPOT_TOKEN||j().token||void 0}function q(z){return(z["base-url"]??(z.dev?u:void 0)??process.env.HACKSPOT_BASE_URL??j().baseUrl??v).replace(/\/+$/,"")}var l=`hackspot build — scaffold an idea and hand it to your AI agent
4
+
5
+ Usage:
6
+ hackspot build <idea-id> [dir] [options]
7
+
8
+ Options:
9
+ --agent <name|cmd> claude | codex | opencode, or a custom command. A custom
10
+ command may contain {prompt} (substituted) — otherwise the
11
+ bootstrap prompt is appended as a final argument.
12
+ --no-launch Scaffold files only; print the command instead of running it
13
+ --force Allow scaffolding into a non-empty directory
14
+ --base-url <url> App base URL (default: production, https://hackspot.dev)
15
+ --dev Target local dev (http://localhost:3000)
16
+ -h, --help Show this help`,B={claude:{cmd:"claude",args:(z)=>[z]},codex:{cmd:"codex",args:(z)=>[z]},opencode:{cmd:"opencode",args:(z)=>["run",z]}};function t(z){return z.title.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"")||`idea-${z.id}`}function o(z){let J=process.platform==="win32"?"where":"which";return P(J,[z],{stdio:"ignore",shell:!0}).status===0}function n(z,J){if(z){let Q=B[z];if(Q)return{argv:[Q.cmd,...Q.args(J)],label:z};let X=z.split(/\s+/);return{argv:z.includes("{prompt}")?X.map(($)=>$.replace("{prompt}",J)):[...X,J],label:z}}for(let[Q,X]of Object.entries(B))if(o(X.cmd))return{argv:[X.cmd,...X.args(J)],label:Q};return null}function T(z){return/[\s"]/.test(z)?`"${z.replace(/"/g,"\\\"")}"`:z}async function I(z){let{values:J,positionals:Q}=d({args:z,allowPositionals:!0,options:{agent:{type:"string"},"no-launch":{type:"boolean"},force:{type:"boolean"},"base-url":{type:"string"},dev:{type:"boolean"},help:{type:"boolean",short:"h"}}});if(J.help){console.log(l);return}let X=Q[0];if(!X)throw Error("Usage: hackspot build <idea-id> [dir]");let Z=N();if(!Z)throw Error("Not logged in. Run `hackspot login` first.");let $=q(J),M=await fetch(`${$}/api/cli/ideas/${encodeURIComponent(X)}`,{headers:{authorization:`Bearer ${Z}`}});if(M.status===401)throw Error("Token invalid or expired. Run `hackspot login` again.");if(M.status===404)throw Error(`Idea ${X} not found (or not yours).`);if(!M.ok){let H=`Couldn't fetch idea ${X} (${M.status})`;try{let _=await M.json();if(_.error)H+=`: ${_.error}`}catch{}throw Error(H)}let K=await M.json(),Y=Q[1]??W(process.cwd(),t(K.json));if(p(Y,{recursive:!0}),!J.force&&c(Y)&&g(Y).length>0)throw Error(`${Y} is not empty. Pass --force to scaffold into it anyway.`);C(W(Y,"AGENTS.md"),K.agentsMarkdown),C(W(Y,"PROMPT.md"),`${K.bootstrapPrompt}
17
+ `),C(W(Y,"hackspot.json"),`${JSON.stringify(K.json,null,2)}
18
+ `);let D=K.json.emoji?`${K.json.emoji} ${K.json.title}`:K.json.title;console.log(`
19
+ Scaffolded "${D}" into ${Y}`),console.log(` AGENTS.md PROMPT.md hackspot.json
20
+ `);let V=n(J.agent,K.bootstrapPrompt);if(J["no-launch"]||!V){if(!V&&!J["no-launch"])console.log(` No agent found (looked for claude, codex, opencode). Install one, or run
21
+ `+` your agent in that directory — it'll read AGENTS.md. PROMPT.md has the prompt.
22
+ `);else if(V)console.log(` To start your agent, run:
23
+ `),console.log(` cd ${T(Y)} && ${V.argv.map(T).join(" ")}
24
+ `);return}console.log(` Launching ${V.label} in ${Y} …
25
+ `);let[k,...F]=V.argv,O=P(k,F,{cwd:Y,stdio:"inherit",shell:!0});if(O.error)throw Error(`Failed to launch ${V.label}: ${O.error.message}`);if(typeof O.status==="number"&&O.status!==0)process.exitCode=O.status}import{parseArgs as i}from"node:util";var s=`hackspot list — list the ideas you can build
26
+
27
+ Usage:
28
+ hackspot list [options]
29
+
30
+ Options:
31
+ --json Print the raw idea list as JSON
32
+ --base-url <url> App base URL (default: production, https://hackspot.dev)
33
+ --dev Target local dev (http://localhost:3000)
34
+ -h, --help Show this help`;async function A(z){let{values:J}=i({args:z,options:{json:{type:"boolean"},"base-url":{type:"string"},dev:{type:"boolean"},help:{type:"boolean",short:"h"}}});if(J.help){console.log(s);return}let Q=N();if(!Q)throw Error("Not logged in. Run `hackspot login` first.");let X=q(J),Z=await fetch(`${X}/api/cli/ideas`,{headers:{authorization:`Bearer ${Q}`}});if(Z.status===401)throw Error("Token invalid or expired. Run `hackspot login` again.");if(!Z.ok){let K=`Couldn't list ideas (${Z.status})`;try{let Y=await Z.json();if(Y.error)K+=`: ${Y.error}`}catch{}throw Error(K)}let{ideas:$}=await Z.json();if(J.json){console.log(JSON.stringify($,null,2));return}if($.length===0){console.log(`
35
+ No saved ideas yet. Generate and save some at https://hackspot.dev, then
36
+ run \`hackspot list\` again.
37
+ `);return}let M=Math.max(...$.map((K)=>String(K.id).length));console.log(`
38
+ ${$.length} idea${$.length===1?"":"s"}:
39
+ `);for(let K of $){let Y=String(K.id).padStart(M),D=K.emoji?`${K.emoji} ${K.title}`:K.title;if(console.log(` ${Y} ${D} (fit ${K.score})`),K.tagline)console.log(` ${" ".repeat(M)} ${K.tagline}`)}console.log("\n Build one with `hackspot build <id>`.\n")}import{spawn as a}from"node:child_process";import{parseArgs as r}from"node:util";var e=`hackspot login — connect this machine to your hackspot.dev account
40
+
41
+ Options:
42
+ --base-url <url> App base URL (default: production, https://hackspot.dev)
43
+ --dev Target local dev (http://localhost:3000)
44
+ --no-open Don't try to open the browser automatically
45
+ -h, --help Show this help`;function zz(z){return new Promise((J)=>setTimeout(J,z))}function Jz(z){let J=process.platform==="win32"?"start":process.platform==="darwin"?"open":"xdg-open";try{a(J,[z],{stdio:"ignore",shell:!0,detached:!0}).unref()}catch{}}async function E(z){let{values:J}=r({args:z,options:{"base-url":{type:"string"},dev:{type:"boolean"},"no-open":{type:"boolean"},help:{type:"boolean",short:"h"}}});if(J.help){console.log(e);return}let Q=q(J),X=await fetch(`${Q}/api/cli/auth/start`,{method:"POST"});if(!X.ok)throw Error(`Couldn't start login (${X.status}). Is ${Q} reachable?`);let{code:Z,deviceSecret:$,verifyUrl:M,expiresInSec:K}=await X.json();if(console.log(`
46
+ Your code: ${Z}
47
+ `),console.log(` Open ${M} and enter it to connect.`),console.log(` (Expires in ${Math.round(K/60)} minutes.)
48
+ `),!J["no-open"])Jz(M);let Y=Date.now()+K*1000;process.stderr.write(" Waiting for approval");while(Date.now()<Y){await zz(3000),process.stderr.write(".");let D=await fetch(`${Q}/api/cli/auth/poll`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({deviceSecret:$})});if(D.status===410)throw process.stderr.write(`
49
+ `),Error("This code expired before it was approved. Run `hackspot login` again.");if(!D.ok)continue;let V=await D.json();if(V.status==="approved"&&V.token){G({token:V.token,baseUrl:Q}),process.stderr.write(`
50
+ `),console.log(" Connected. You can now run `hackspot build <idea-id>`.\n");return}}throw process.stderr.write(`
51
+ `),Error("Timed out waiting for approval. Run `hackspot login` again.")}import{parseArgs as Kz}from"node:util";function L(){G({token:void 0}),console.log("Logged out (local token cleared).")}var Qz=`hackspot whoami — show the account this machine is linked to
52
+
53
+ Options:
54
+ --base-url <url> App base URL (default: production, https://hackspot.dev)
55
+ --dev Target local dev (http://localhost:3000)
56
+ -h, --help Show this help`;async function R(z){let{values:J}=Kz({args:z,options:{"base-url":{type:"string"},dev:{type:"boolean"},help:{type:"boolean",short:"h"}}});if(J.help){console.log(Qz);return}let Q=N();if(!Q){console.log("Not logged in. Run `hackspot login`."),process.exitCode=1;return}let X=q(J),Z=await fetch(`${X}/api/cli/me`,{headers:{authorization:`Bearer ${Q}`}});if(!Z.ok){console.log("Logged in, but the token is no longer valid. Run `hackspot login` again."),process.exitCode=1;return}let{userId:$}=await Z.json();console.log(`Logged in as ${$} on ${X}.`)}var w=`hackspot — scaffold a hackspot.dev idea for your AI coding agent
57
+
58
+ Usage:
59
+ hackspot login Connect this machine to your account
60
+ hackspot list List the ideas you can build
61
+ hackspot build <idea-id> [dir] Scaffold an idea and launch your agent
62
+ hackspot whoami Show the linked account
63
+ hackspot logout Clear the local token
64
+
65
+ Run 'hackspot <command> --help' for command-specific options.`;async function Xz(){let[z,...J]=process.argv.slice(2);switch(z){case"login":await E(J);return;case"list":await A(J);return;case"build":await I(J);return;case"whoami":await R(J);return;case"logout":L();return;case"-h":case"--help":case void 0:console.log(w);return;default:console.error(`Unknown command: ${z}
66
+ `),console.error(w),process.exitCode=1}}Xz().catch((z)=>{console.error(z instanceof Error?z.message:String(z)),process.exitCode=1});
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "@hackspot/cli",
3
+ "version": "0.1.1",
4
+ "type": "module",
5
+ "description": "Scaffold a hackspot.dev idea into a project and hand it to your AI coding agent.",
6
+ "keywords": ["hackspot", "hackathon", "cli", "ai", "scaffold", "claude", "codex"],
7
+ "homepage": "https://docs.hackspot.dev/docs/install",
8
+ "license": "MIT",
9
+ "bin": {
10
+ "hackspot": "./dist/index.js"
11
+ },
12
+ "files": ["dist", "README.md"],
13
+ "engines": {
14
+ "node": ">=18"
15
+ },
16
+ "publishConfig": {
17
+ "access": "public"
18
+ },
19
+ "scripts": {
20
+ "start": "bun run src/index.ts",
21
+ "login": "bun run src/index.ts login",
22
+ "login:dev": "bun run src/index.ts login --dev",
23
+ "cli:build": "bun run src/index.ts build",
24
+ "bundle": "bun build src/index.ts --target=node --outfile dist/index.js --minify",
25
+ "prepublishOnly": "bun run bundle"
26
+ }
27
+ }