@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.
- package/README.md +87 -0
- package/dist/index.js +66 -0
- 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
|
+
}
|