zerostarter 0.0.0-93a2b9d → 0.0.0-9ed3044

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 ADDED
@@ -0,0 +1,71 @@
1
+ # zerostarter
2
+
3
+ Go from zero to a production-ready SaaS, rebranded and ready to ship.
4
+
5
+ `zerostarter` scaffolds [ZeroStarter](https://zerostarter.dev), a full-stack TypeScript starter (Next.js web, Hono API, Drizzle + Postgres, better-auth), into a fresh, rebranded project in one command. It fetches the latest starter, renames it to your directory, installs dependencies, and (when Docker is running) provisions a local Postgres and runs migrations.
6
+
7
+ <p align="center">
8
+ <img src="https://raw.githubusercontent.com/nrjdalal/zerostarter/canary/.github/assets/cli.gif" alt="bunx zerostarter init scaffolds a rebranded product from ZeroStarter" width="900" />
9
+ </p>
10
+
11
+ ## Quick start
12
+
13
+ ```bash
14
+ # In a new, empty directory (its name becomes your project name):
15
+ bunx zerostarter init
16
+
17
+ # Start the dev servers (web on :3000, api on :4000)
18
+ bun run dev
19
+ ```
20
+
21
+ That is the whole setup. When Docker is running, `init` provisions a local Postgres and migrates for you; otherwise set `POSTGRES_URL` in `.env` (a hosted database like Neon works) and run `bun run db:migrate`.
22
+
23
+ ## Commands
24
+
25
+ ```bash
26
+ bunx zerostarter <command> [dir] [options]
27
+ ```
28
+
29
+ | Command | What it does |
30
+ | -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
31
+ | `init [dir]` | Scaffold ZeroStarter into `dir` (default `.`) as a fresh product. Fetches, rebrands to the dir name, installs, and optionally provisions Postgres. |
32
+ | `reinit [dir]` | Re-scaffold an existing git repo (default `.`) as a fresh ZeroStarter, keeping `.git` and your `.env*` files so history, remote, and secrets survive. |
33
+ | `sync [dir]` | Re-baseline an existing fork (default `.`) on the latest ZeroStarter, preserving your content, branding, `package.json` identity, and favicon. |
34
+
35
+ ### `init`
36
+
37
+ Scaffold a new product. Run it in an empty directory (its name becomes the project name), or pass a name: `bunx zerostarter init my-app`.
38
+
39
+ | Option | Description |
40
+ | -------------- | --------------------------------------------------------------------- |
41
+ | `-y`, `--yes` | Skip prompts, taking defaults (provisions Postgres when Docker is up) |
42
+ | `--db` | Provision a local Postgres (via pglaunch) and migrate; needs Docker |
43
+ | `--dry-run` | Print the plan without writing anything |
44
+ | `-h`, `--help` | Display help |
45
+
46
+ ### `reinit`
47
+
48
+ Re-scaffold an existing repo in place. Every file is deleted (except `.git` and `.env*`), the latest ZeroStarter is fetched, and it is rebranded to the directory name. The commit lands on the current branch; push when ready. Requires a clean tree.
49
+
50
+ | Option | Description |
51
+ | -------------- | ---------------------------- |
52
+ | `-y`, `--yes` | Skip the confirmation prompt |
53
+ | `-h`, `--help` | Display help |
54
+
55
+ ### `sync`
56
+
57
+ Re-baseline a fork on the latest ZeroStarter. A gitpick overlay updates the starter files while your content, `public/marketing`, branding, `package.json` identity, and favicon are preserved. Requires a clean tree; lands as a reviewable diff you commit yourself.
58
+
59
+ ## Requirements
60
+
61
+ - **[Bun](https://bun.sh)** runs the scaffolded project. If it is missing, the CLI offers to install it for you.
62
+ - **Docker** (optional) lets `init` provision a local Postgres and migrate automatically. Without it, point `POSTGRES_URL` at any Postgres and run `bun run db:migrate`.
63
+
64
+ ## Links
65
+
66
+ - Docs: [zerostarter.dev/docs](https://zerostarter.dev/docs)
67
+ - Repository: [github.com/nrjdalal/zerostarter](https://github.com/nrjdalal/zerostarter)
68
+
69
+ ## License
70
+
71
+ MIT
@@ -1,12 +1,17 @@
1
1
  #!/usr/bin/env node
2
- import{a as e,c as t,d as n,f as r,i,l as a,n as o,o as s,r as c,s as l,t as u,u as d}from"../convert-DMU8fiBc.mjs";import{basename as f,delimiter as p,join as m,resolve as h}from"node:path";import{existsSync as g,readdirSync as _}from"node:fs";import{createInterface as v}from"node:readline/promises";import{parseArgs as y}from"node:util";import{randomBytes as b}from"node:crypto";import{spawn as ee}from"node:child_process";import{access as te}from"node:fs/promises";import{homedir as ne}from"node:os";var re={name:`zerostarter`,version:`0.0.0-93a2b9d`,description:`Go from zero to a production-ready SaaS, rebranded and ready to ship.`,keywords:[`cli`,`saas`,`scaffold`,`starter`,`template`,`zerostarter`],homepage:`https://github.com/nrjdalal/zerostarter#readme`,bugs:`https://github.com/nrjdalal/zerostarter/issues`,license:`MIT`,author:{name:`Neeraj Dalal`,email:`admin@nrjdalal.com`,url:`https://nrjdalal.com`},repository:`nrjdalal/zerostarter`,funding:`https://github.com/sponsors/nrjdalal`,bin:{zerostarter:`dist/bin/index.mjs`},files:[`dist`],type:`module`,types:`./dist/index.d.mts`,exports:`./dist/index.mjs`,scripts:{build:`tsdown`,"check-types":`tsc --noEmit`,dev:`tsdown --watch`,test:`bun test`},devDependencies:{"@packages/config":`workspace:*`,"@types/bun":`catalog:`,"@types/node":`catalog:`,tsdown:`catalog:`,typescript:`catalog:`}};const x=()=>!!(process.stdin.isTTY&&process.stdout.isTTY),ie=(e,t=e)=>process.stdout.isTTY?`\x1b]8;;${e}\x07${t}\x1b]8;;\x07`:e,S=(e,t=process.stdout)=>n=>t.isTTY?`\x1b[${e}m${n}\x1b[0m`:n,C=S(`38;5;208`),w=S(`38;2;63;185;80`),T=S(`38;2;210;153;34`),E=S(`38;2;248;81;73`,process.stderr),ae=async(e,t=``)=>{let n=v({input:process.stdin,output:process.stdout});try{return(await n.question(`${e}${t?` (${t})`:``}: `)).trim()||t}finally{n.close()}},D=async(e,t=!0)=>{let n=v({input:process.stdin,output:process.stdout});try{let r=(await n.question(`${e} (${t?`Y/n`:`y/N`}) `)).trim().toLowerCase();return r?r===`y`||r===`yes`:t}finally{n.close()}};var oe=class extends Error{constructor(...e){super(...e),this.name=`SubprocessError`,this.stdout=``,this.stderr=``}};const O=[`.exe`,`.com`],k=e=>e.replaceAll(/([()\][%!^"`<>&|;, *?])/g,`^$1`),se=e=>k(k(`"${e.replaceAll(/(\\*)"/g,`$1$1\\"`).replace(/(\\*)$/,`$1$1`)}"`)),ce=async(e,t,n)=>{if(O.some(t=>e.toLowerCase().endsWith(t)))return!0;let r=(n.PATH||n.Path||``).split(p).filter(Boolean).map(e=>e.replace(/^"(.*)"$/,`$1`));try{return await Promise.any([t,...r].flatMap(t=>O.map(n=>te(`${h(t,e)}${n}`)))),!0}catch{return!1}},le=async(e,t,n,r=process.platform===`win32`)=>{let i=n.cwd?String(n.cwd):`.`,a=n.env||process.env;return r&&!n.shell&&!await ce(e,i,a)?[k(e),t.map(se),{...n,shell:!0}]:[e,t,n]},A=async(e,t,n,r)=>{let i=[e,...t].join(` `),[a,o,s]=await le(e,t,n);s.shell&&o.length>0&&(a=[a,...o].join(` `),o=[]);let c=ee(a,o,s),l=``,u=``;return new Promise((e,t)=>{let n=(e,n,r)=>{let i=new oe(e,r?{cause:r}:void 0);i.stdout=l,i.stderr=u,i.exitCode=n,t(i)},a=e=>{e.code!==`ERR_STREAM_PREMATURE_CLOSE`&&e.code!==`EPIPE`&&n(`Command failed: ${i}`,void 0,e)};c.stdout&&(c.stdout.setEncoding(`utf8`),c.stdout.on(`data`,e=>{l+=e,r?.(e)}),c.stdout.on(`error`,a)),c.stderr&&(c.stderr.setEncoding(`utf8`),c.stderr.on(`data`,e=>{u+=e,r?.(e)}),c.stderr.on(`error`,a)),c.on(`error`,e=>n(`Command failed: ${i}`,void 0,e)),c.on(`close`,(t,r)=>{if(r)return n(`Command was terminated with ${r}: ${i}`);if(t!==0)return n(`Command failed with exit code ${t}: ${i}`,t??void 0);e({stdout:l,stderr:u})})})},j=async(e,t,n)=>{let{stdout:r}=await A(e,t,{cwd:n,stdio:[`ignore`,`pipe`,`pipe`]});return r},M=async(e,t,n)=>{await A(e,t,{cwd:n,stdio:`inherit`})},N=async(e,t)=>{try{return await A(e,t,{stdio:`ignore`}),!0}catch{return!1}},ue=RegExp(`\x1B\\[[0-9;?]*[a-zA-Z]`,`g`),de=e=>e.replace(ue,``),P=e=>e>=1e3?`[${(e/1e3).toFixed(2)}s]`:`[${e.toFixed(2)}ms]`,F=async(e,t,n)=>{let r=Date.now();if(!process.stdout.isTTY){await M(e,t,n.cwd);return}let i=n.lines??3,a=[],o=0,s=``,c=``,l=()=>(process.stdout.columns||80)-1,u=e=>{process.stdout.write(e?`\x1B[?7h`:`\x1B[?7l`)},d=()=>{o>0&&(process.stdout.write(`[${o}A`),o=0)},f=()=>{d();let e=a.slice(-i);for(let t of e)process.stdout.write(`${t.slice(0,l())}\n`);o=e.length},p=e=>{c+=e,s+=e;let t=s.split(`
3
- `);s=t.pop()??``;for(let e of t)a.push(de(e).replace(/\r/g,``).replace(/\t/g,` `).trimEnd()),f()};u(!1);try{await A(e,t,{cwd:n.cwd,stdio:[`ignore`,`pipe`,`pipe`]},p)}catch(e){throw d(),u(!0),process.stdout.write(c),e}d(),u(!0);let m=n.summarize(c,Date.now()-r).trim();m&&(console.log(),console.log(m))},I=`pglaunch@5.5.7`,fe=async()=>N(`docker`,[`info`]),L=e=>{let r=m(e,`.env`);if(!s(r)){let i=m(e,`.env.example`);n(r,s(i)?t(i):``)}return r},R=(e,n)=>{let r=`${n}=`,i=t(e).split(`
4
- `).find(e=>e.startsWith(r));return i?i.slice(r.length).trim():``},z=(e,r,i)=>{let a=`${r}=`,o=t(e).split(`
2
+ import{a as e,c as t,d as n,f as r,i,l as a,n as o,o as s,p as c,r as l,s as u,t as d,u as f}from"../convert-DHCJQjgd.mjs";import{basename as p,delimiter as m,dirname as h,join as g,parse as _,resolve as v}from"node:path";import{existsSync as y,readFileSync as b,readdirSync as ee}from"node:fs";import{spawn as x}from"node:child_process";import{access as te}from"node:fs/promises";import{createInterface as S}from"node:readline/promises";import{randomBytes as C}from"node:crypto";import{parseArgs as ne}from"node:util";import{homedir as w}from"node:os";const T=(e,t,n=process.stdout)=>r=>n.isTTY?`\x1b[${e}m${r}\x1b[${t}m`:r,E=T(`36`,`39`),D=T(`32`,`39`),O=T(`33`,`39`),k=T(`90`,`39`),A=T(`2`,`22`),j=T(`31`,`39`,process.stderr),re=T(`2`,`22`,process.stderr),M=T(`38;5;208`,`39`),N=[`◇`,`◆`],P=(e,t)=>{let n=(e%N.length+N.length)%N.length;return`${E(N[n])} ${t}`},F={barStart:`┌`,bar:`│`,barEnd:`└`,active:`◆`,submit:`◇`,radioOn:`●`,radioOff:`○`,checkboxOn:`◼`,checkboxOff:`◻`,warn:`▲`,error:`■`,barH:`─`,cornerTR:`╮`,connectL:`├`,cornerBL:`╰`,cornerBR:`╯`};var ie=class extends Error{constructor(...e){super(...e),this.name=`SubprocessError`,this.stdout=``,this.stderr=``}};const ae=[`.exe`,`.com`],I=e=>e.replaceAll(/([()\][%!^"`<>&|;, *?])/g,`^$1`),oe=e=>I(I(`"${e.replaceAll(/(\\*)"/g,`$1$1\\"`).replace(/(\\*)$/,`$1$1`)}"`)),se=async(e,t,n)=>{if(ae.some(t=>e.toLowerCase().endsWith(t)))return!0;let r=(n.PATH||n.Path||``).split(m).filter(Boolean).map(e=>e.replace(/^"(.*)"$/,`$1`));try{return await Promise.any([t,...r].flatMap(t=>ae.map(n=>te(`${v(t,e)}${n}`)))),!0}catch{return!1}},ce=async(e,t,n,r=process.platform===`win32`)=>{let i=n.cwd?String(n.cwd):`.`,a=n.env||process.env;return r&&!n.shell&&!await se(e,i,a)?[I(e),t.map(oe),{...n,shell:!0}]:[e,t,n]},L=async(e,t,n,r)=>{let i=[e,...t].join(` `),[a,o,s]=await ce(e,t,n);s.shell&&o.length>0&&(a=[a,...o].join(` `),o=[]);let c=x(a,o,s),l=``,u=``;return new Promise((e,t)=>{let n=(e,n,r)=>{let i=new ie(e,r?{cause:r}:void 0);i.stdout=l,i.stderr=u,i.exitCode=n,t(i)},a=e=>{e.code!==`ERR_STREAM_PREMATURE_CLOSE`&&e.code!==`EPIPE`&&n(`Command failed: ${i}`,void 0,e)};c.stdout&&(c.stdout.setEncoding(`utf8`),c.stdout.on(`data`,e=>{l+=e,r?.(e)}),c.stdout.on(`error`,a)),c.stderr&&(c.stderr.setEncoding(`utf8`),c.stderr.on(`data`,e=>{u+=e,r?.(e)}),c.stderr.on(`error`,a)),c.on(`error`,e=>n(`Command failed: ${i}`,void 0,e)),c.on(`close`,(t,r)=>{if(r)return n(`Command was terminated with ${r}: ${i}`);if(t!==0)return n(`Command failed with exit code ${t}: ${i}`,t??void 0);e({stdout:l,stderr:u})})})},R=async(e,t,n)=>{let{stdout:r}=await L(e,t,{cwd:n,stdio:[`ignore`,`pipe`,`pipe`]});return r},le=async(e,t,n)=>{await L(e,t,{cwd:n,stdio:`inherit`})},z=async(e,t)=>{try{return await L(e,t,{stdio:`ignore`}),!0}catch{return!1}},ue=RegExp(`\x1B\\[[0-9;?]*[a-zA-Z]`,`g`),de=e=>e.replace(ue,``),fe=new WeakSet,pe=e=>{if(process.stderr.write(`\n ${j(e instanceof Error?e.message:String(e))}\n`),!(e instanceof ie)||fe.has(e))return;let t=(e.stderr||e.stdout||``).trim();if(!t)return;let n=de(t).split(`
3
+ `).map(e=>e.trimEnd()).filter(Boolean).slice(-12);for(let e of n)process.stderr.write(` ${re(e)}\n`)},me=async(e,t,n)=>{let r=process.stdout,i=n.label??``;if(!r.isTTY){i&&r.write(` ${E(F.active)} ${i}\n`),await le(e,t,n.cwd);return}let a=n.lines??3,o=[],s=0,c=0,l=``,u=``,d=()=>(r.columns||80)-1,f=e=>{r.write(e?`\x1B[?7h`:`\x1B[?7l`)},p=()=>{c>0&&(r.write(`[${c}A`),c=0)},m=()=>{p();let e=0;r.write(` ${P(s,i)}\n`),e++;for(let t of o.slice(-a))r.write(` ${k(F.bar)} ${A(t.slice(0,Math.max(0,d()-5)))}\n`),e++;c=e},h=e=>{u+=e,l+=e;let t=l.split(`
4
+ `);l=t.pop()??``;for(let e of t)o.push(de(e).replace(/\r/g,``).replace(/\t/g,` `).trimEnd()),m()};m(),f(!1);let g=()=>{f(!0),process.exit(130)};process.once(`SIGINT`,g);let _=setInterval(()=>{s+=1,m()},400),v=()=>{clearInterval(_),process.removeListener(`SIGINT`,g)};try{await L(e,t,{cwd:n.cwd,stdio:[`ignore`,`pipe`,`pipe`]},h)}catch(e){throw v(),p(),f(!0),r.write(u),e&&typeof e==`object`&&fe.add(e),e}v(),p(),f(!0),r.write(` ${E(F.active)} ${n.done??i}\n`);for(let e of o.slice(-a))r.write(` ${k(F.bar)} ${A(e.slice(0,Math.max(0,d()-5)))}\n`)};var he={name:`zerostarter`,version:`0.0.0-9ed3044`,description:`Go from zero to a production-ready SaaS, rebranded and ready to ship.`,keywords:[`cli`,`saas`,`scaffold`,`starter`,`template`,`zerostarter`],homepage:`https://github.com/nrjdalal/zerostarter#readme`,bugs:`https://github.com/nrjdalal/zerostarter/issues`,license:`MIT`,author:{name:`Neeraj Dalal`,email:`admin@nrjdalal.com`,url:`https://nrjdalal.com`},repository:`nrjdalal/zerostarter`,funding:`https://github.com/sponsors/nrjdalal`,bin:{zerostarter:`dist/bin/index.mjs`},files:[`dist`],type:`module`,types:`./dist/index.d.mts`,exports:`./dist/index.mjs`,scripts:{build:`tsdown`,"check-types":`tsc --noEmit`,dev:`tsdown --watch`,test:`bun test`},devDependencies:{"@packages/config":`workspace:*`,"@types/bun":`catalog:`,"@types/node":`catalog:`,tsdown:`catalog:`,typescript:`catalog:`}};const B=()=>!!(process.stdin.isTTY&&process.stdout.isTTY),ge=(e,t=e)=>process.stdout.isTTY?`\x1b]8;;${e}\x07${t}\x1b]8;;\x07`:e,V=e=>ge(e,E(e)),H=e=>{process.stdout.write(`\n ${k(F.barStart)} ${e}\n ${k(F.bar)}\n`)},U=e=>{process.stdout.write(` ${k(F.bar)}\n ${k(F.barEnd)} ${e}\n`)},W=(e=`Cancelled`)=>{process.stdout.write(` ${k(F.barEnd)} ${j(e)}\n`)},G=e=>e.replace(RegExp(`\x1B\\[[0-9;?]*[a-zA-Z]`,`g`),``).replace(RegExp(`\x1B\\]8;;.*?\\x07`,`g`),``).length,K=(e,t=``,n=!1)=>{let r=`\n${e}\n`.split(`
5
+ `),i=G(t),a=r.reduce((e,t)=>Math.max(e,G(t)),i),o=Math.max(a,54)+2,s=r.map(e=>` ${k(F.bar)} ${e}${` `.repeat(o-G(e))}${k(F.bar)}`).join(`
6
+ `),c=n?F.cornerBL:F.connectL;process.stdout.write(` ${E(F.active)} ${t} ${k(F.barH.repeat(Math.max(o-i-1,1))+F.cornerTR)}\n${s}\n ${k(c+F.barH.repeat(o+2)+F.cornerBR)}\n`)},q=(e,t=[])=>{process.stdout.write(` ${E(F.active)} ${e}\n`);for(let e of t)process.stdout.write(` ${k(F.bar)} ${e}\n`)},_e=e=>{process.stdout.write(` ${D(F.active)} ${D(e)}\n`)},J=(e,t=[])=>{process.stdout.write(` ${O(F.warn)} ${O(e)}\n`);for(let e of t)process.stdout.write(` ${k(F.bar)} ${O(e)}\n`)},ve=async(e,t,n)=>{let r=process.stdout,i=null;if(r.isTTY){let t=0;r.write(` ${P(0,e)}`),i=setInterval(()=>{t+=1,r.write(`\r\x1b[K ${P(t,e)}`)},400)}try{let e=await n();return i&&clearInterval(i),r.write(r.isTTY?`\r\x1b[K ${E(F.active)} ${t}\n`:` ${E(F.active)} ${t}\n`),e}catch(e){throw i&&clearInterval(i),r.isTTY&&r.write(`\r\x1B[K`),e}},ye=e=>{let t=e?`${D(F.radioOn)} Yes`:A(`${F.radioOff} Yes`),n=e?A(`${F.radioOff} No`):`${D(F.radioOn)} No`;return` ${k(F.bar)} ${t} ${A(`/`)} ${n}`},Y=async(e,t=!0)=>{let n=process.stdout;return B()?new Promise(r=>{let i=t,a=process.stdin,o=Math.max(1,Math.ceil((5+G(e))/(n.columns||80)));n.write(` ${E(F.submit)} ${e}\n`),n.write(ye(i)),a.setRawMode(!0),a.resume(),a.setEncoding(`utf8`);let s=()=>{a.setRawMode(!1),a.pause(),a.removeListener(`data`,u)},c=()=>{n.write(`\r\x1b[K\x1b[${o}A\r\x1b[0J`)},l=()=>{s(),c(),r(i)};function u(t){t===``?(s(),c(),n.write(` ${E(F.submit)} ${A(e)}\n`),W(),process.exit(130)):t===`\r`||t===`
7
+ `?l():t===`y`||t===`Y`?(i=!0,l()):t===`n`||t===`N`?(i=!1,l()):(t===`\x1B[C`||t===`\x1B[D`||t===`\x1B[A`||t===`\x1B[B`||t===`h`||t===`l`)&&(i=!i,n.write(`\r\x1b[K${ye(i)}`))}a.on(`data`,u)}):(n.write(` ${E(F.active)} ${e} ${A(t?`Yes`:`No`)}\n`),t)},be=async(e,t)=>{let n=process.stdout,r=new Set(t.filter(e=>e.checked).map(e=>e.value)),i=()=>t.filter(e=>r.has(e.value)).map(e=>e.label).join(`, `)||`none`;return B()?new Promise(a=>{let o=process.stdin,s=0,c=e=>{let n=t[e],i=r.has(n.value)?D(F.checkboxOn):A(F.checkboxOff),a=e===s?n.label:A(n.label);return` ${k(F.bar)} ${i} ${a}`},l=e=>{e||n.write(`\x1b[${t.length}A`);for(let e=0;e<t.length;e++)n.write(`\r\x1b[K${c(e)}\n`)};n.write(` ${E(F.submit)} ${e}\n`),l(!0),o.setRawMode(!0),o.resume(),o.setEncoding(`utf8`);let u=()=>{o.setRawMode(!1),o.pause(),o.removeListener(`data`,d)};function d(o){if(o===``)u(),n.write(`\x1b[${t.length+1}A\r\x1b[0J ${E(F.submit)} ${A(e)}\n`),W(),process.exit(130);else if(o===`\r`||o===`
8
+ `)u(),n.write(`\x1b[${t.length+1}A\r\x1b[0J ${E(F.submit)} ${e} ${A(i())}\n`),a([...r]);else if(o===`\x1B[A`||o===`k`)s=(s-1+t.length)%t.length,l(!1);else if(o===`\x1B[B`||o===`j`)s=(s+1)%t.length,l(!1);else if(o===` `){let e=t[s].value;r.has(e)?r.delete(e):r.add(e),l(!1)}}o.on(`data`,d)}):(n.write(` ${E(F.submit)} ${e} ${A(i())}\n`),[...r])},xe=async(e,t=``)=>{process.stdout.write(` ${E(F.submit)} ${e}${t?` ${A(`(${t})`)}`:``}\n`);let n=S({input:process.stdin,output:process.stdout});try{return(await n.question(` ${k(F.bar)} `)).trim()||t}finally{n.close()}},Se=`pglaunch@5.5.7`,Ce=async()=>z(`docker`,[`info`]),we=e=>{let r=g(e,`.env`);if(!s(r)){let i=g(e,`.env.example`);n(r,s(i)?t(i):``)}return r},Te=(e,n)=>{let r=`${n}=`,i=t(e).split(`
9
+ `).find(e=>e.startsWith(r));return i?i.slice(r.length).trim():``},Ee=(e,r,i)=>{let a=`${r}=`,o=t(e).split(`
5
10
  `),s=o.findIndex(e=>e.startsWith(a));if(s>=0)o[s]=`${a}${i}`;else{for(;o.length&&o[o.length-1]===``;)o.pop();o.push(`${a}${i}`,``)}n(e,o.join(`
6
- `))},B=e=>{let t=L(e);R(t,`BETTER_AUTH_SECRET`)||z(t,`BETTER_AUTH_SECRET`,b(32).toString(`base64`))},V=e=>{let t=m(e,`.env`);return s(t)&&R(t,`POSTGRES_URL`)!==``},pe=e=>{let t=e.match(/postgres(?:ql)?:\/\/[\w.:@\-/%?=&]+/);if(!t)return null;let n=e.match(/name "([^" ]+) :\d+" started/),r=e.match(/similar name "([^"]+)"/);return{url:t[0],container:n?n[1]:r?r[1]:``}},H=async(e,t)=>{let n=t?[`--bun`,I,`-k`,`-c`]:[`--bun`,I,`-k`],r;try{r=await j(`bunx`,n,e)}catch(e){let t=e;r=[t.stdout,t.stderr].filter(Boolean).join(`
7
- `)}return pe(r)},me=e=>new Promise(t=>setTimeout(t,e)),he=async e=>{if(e)for(let t=0;t<30;t++){if(await N(`docker`,[`exec`,e,`pg_isready`,`-U`,`postgres`]))return;await me(1e3)}},ge=async e=>{let t=L(e),n=await H(e,!1)||await H(e,!0);if(!n)throw Error(`pglaunch did not print a connection URL`);z(t,`POSTGRES_URL`,n.url),await he(n.container);let r=_e(e);await F(`bun`,[`run`,`db:migrate`],{cwd:e,summarize:(e,t)=>e.includes(`migrations applied successfully`)?`${r} migration${r===1?``:`s`} applied ${P(t)}`:`Database migrated ${P(t)}`})},_e=e=>{try{return _(m(e,`packages/db/drizzle`)).filter(e=>e.endsWith(`.sql`)).length}catch{return 0}},U=async e=>{await F(`bun`,[`install`],{cwd:e,summarize:e=>(e.match(/[\d,]+ packages installed[^\n]*/g)??e.match(/Checked [^\n]*install[^\n]*/g))?.at(-1)?.trim()??`Dependencies installed.`})},ve=async()=>{if(!await N(`bun`,[`--version`]))throw Error(`bun is not on PATH`)},W=async(e=ve,t=!!process.versions.bun,n=process.env.npm_config_user_agent||``)=>{if(t||n.startsWith(`bun/`))return!0;try{return await e(),!0}catch{return!1}},ye=async(e,t=`main`)=>{await j(`bunx`,[`--bun`,`gitpick@6.0.0`,`https://github.com/nrjdalal/zerostarter/tree/${t}`,e])},G=async(e,t=`main`)=>{await j(`bunx`,[`--bun`,`gitpick@6.0.0`,`https://github.com/nrjdalal/zerostarter/tree/${t}`,e,`-o`])},be=async(e=`main`)=>{let t=`https://raw.githubusercontent.com/nrjdalal/zerostarter/${e}/.gitpickignore`,n=await fetch(t);if(!n.ok)throw Error(`Could not fetch .gitpickignore from ${e} (HTTP ${n.status}).`);return n.text()},xe=async e=>(await j(`git`,[`status`,`--porcelain`],e)).trim()===``,Se=async(e,t)=>{for(let n of t)try{await j(`git`,[`checkout`,`--`,n],e),await j(`git`,[`clean`,`-fd`,`--`,n],e)}catch{}},Ce=async e=>{await j(`git`,[`reset`,`--hard`,`HEAD`],e),await j(`git`,[`clean`,`-fd`],e)},K=async(e,t,n)=>{if(!s(m(e,`.git`)))throw Error(t);if(!await xe(e))throw Error(n)},q=async(e,t,n)=>{try{await n()}catch(n){throw await Ce(e),console.log(t),n}},we=async e=>{await j(`git`,[`init`,`-q`,`-b`,`canary`],e)},Te=async(e,t)=>{await j(`git`,[`branch`,t],e)},J=async(e,t)=>{await j(`git`,[`add`,`-A`],e);try{await j(`git`,[`commit`,`--no-verify`,`-q`,`-m`,t],e)}catch{}},Ee=(e=process.env.npm_config_user_agent||``)=>e.startsWith(`bun/`)?`bunx`:e.startsWith(`pnpm/`)?`pnpm dlx`:e.startsWith(`yarn/`)?`yarn dlx`:e.startsWith(`npm/`)?`npx`:`unknown`,De=(e=process.platform)=>e===`win32`?{cmd:`powershell`,args:[`-Command`,`irm bun.sh/install.ps1 | iex`]}:{cmd:`bash`,args:[`-c`,`curl -fsSL https://bun.sh/install | bash`]},Oe=(e=process.argv.slice(2))=>[`zerostarter`,...e].join(` `),ke=()=>m(process.env.BUN_INSTALL||m(ne(),`.bun`),`bin`),Y=async(e=!1)=>{if(await W())return;let t=Ee(),n=`bunx --bun ${Oe()}`;if(console.error(E(`Seems like you are using ${t===`unknown`?`npx/pnpx`:t} or Windows, try using:`)),console.error(C(` ${n}`)),console.error(``),e||x()&&await D(`Or install bun now and continue?`,!0)){let{cmd:e,args:t}=De();console.error(`Installing bun from https://bun.sh/install ...`);try{if(await M(e,t),process.env.PATH=`${ke()}${p}${process.env.PATH||``}`,await W())return;console.error(E(`
8
- bun is installed but isn't on this shell's PATH yet. Open a new terminal and re-run:`)),console.error(C(` ${n}`))}catch{console.error(E(`
9
- Could not install bun. Install it from https://bun.sh, then re-run:`)),console.error(C(` ${n}`))}}process.exit(1)},Ae=e=>!g(e)||_(e).filter(e=>e!==`.git`).length===0,X=e=>s(m(e,`packages/config/src/site.ts`)),je=async e=>{let{positionals:t,values:n}=y({allowPositionals:!0,args:e,options:{db:{type:`boolean`},"dry-run":{type:`boolean`},help:{short:`h`,type:`boolean`},yes:{short:`y`,type:`boolean`}}});if(n.help){console.log(`Usage:
11
+ `))},De=e=>{let t=we(e);Te(t,`BETTER_AUTH_SECRET`)||Ee(t,`BETTER_AUTH_SECRET`,C(32).toString(`base64`))},X=e=>{let t=g(e,`.env`);return s(t)&&Te(t,`POSTGRES_URL`)!==``},Oe=e=>{let t=e.match(/postgres(?:ql)?:\/\/[\w.:@\-/%?=&]+/);if(!t)return null;let n=e.match(/name "([^" ]+) :\d+" started/),r=e.match(/similar name "([^"]+)"/);return{url:t[0],container:n?n[1]:r?r[1]:``}},ke=async(e,t)=>{let n=t?[`--bun`,Se,`-k`,`-c`]:[`--bun`,Se,`-k`],r;try{r=await R(`bunx`,n,e)}catch(e){let t=e;r=[t.stdout,t.stderr].filter(Boolean).join(`
12
+ `)}return{launch:Oe(r),out:r}},Ae=e=>new Promise(t=>setTimeout(t,e)),je=async e=>{if(e)for(let t=0;t<30;t++){if(await z(`docker`,[`exec`,e,`pg_isready`,`-U`,`postgres`]))return;await Ae(1e3)}},Me=async e=>{let t=we(e),n=await ke(e,!1),r=n.launch?n:await ke(e,!0);if(!r.launch)throw Error(r.out.trim()||`pglaunch did not print a connection URL`);Ee(t,`POSTGRES_URL`,r.launch.url),await je(r.launch.container),await me(`bun`,[`run`,`db:migrate`],{cwd:e,label:`Provisioning a local Postgres database`,done:`Provisioned a local Postgres database`})},Ne=async e=>{await me(`bun`,[`install`],{cwd:e,label:`Installing dependencies`,done:`Installed dependencies`})},Pe=async()=>{if(!await z(`bun`,[`--version`]))throw Error(`bun is not on PATH`)},Fe=async(e=Pe,t=!!process.versions.bun,n=process.env.npm_config_user_agent||``)=>{if(t||n.startsWith(`bun/`))return!0;try{return await e(),!0}catch{return!1}},Ie=async(e,t=`main`)=>{await R(`bunx`,[`--bun`,`gitpick@6.0.0`,`https://github.com/nrjdalal/zerostarter/tree/${t}`,e])},Le=async(e,t=`main`)=>{await R(`bunx`,[`--bun`,`gitpick@6.0.0`,`https://github.com/nrjdalal/zerostarter/tree/${t}`,e,`-o`])},Re=async(e=`main`)=>{let t=`https://raw.githubusercontent.com/nrjdalal/zerostarter/${e}/.gitpickignore`,n=await fetch(t);if(!n.ok)throw Error(`Could not fetch .gitpickignore from ${e} (HTTP ${n.status}).`);return n.text()},ze=async e=>(await R(`git`,[`status`,`--porcelain`],e)).trim()===``,Be=async(e,t)=>{for(let n of t)try{await R(`git`,[`checkout`,`--`,n],e),await R(`git`,[`clean`,`-fd`,`--`,n],e)}catch{}},Ve=async e=>{await R(`git`,[`reset`,`--hard`,`HEAD`],e),await R(`git`,[`clean`,`-fd`],e)},He=async(e,t,n)=>{if(!s(g(e,`.git`)))throw Error(t);if(!await ze(e))throw Error(n)},Ue=async(e,t,n)=>{try{await n()}catch(n){throw await Ve(e),console.log(t),n}},We=async e=>{await R(`git`,[`init`,`-q`,`-b`,`canary`],e)},Ge=async(e,t)=>{await R(`git`,[`branch`,t],e)},Ke=async(e,t)=>{await R(`git`,[`add`,`-A`],e);try{await R(`git`,[`commit`,`--no-verify`,`-q`,`-m`,t],e)}catch{}},qe=(e,t)=>{try{return ne(t)}catch(t){process.stderr.write(`${j(t instanceof Error?t.message:String(t))}\n\n`),process.stdout.write(`${e}\n`),process.exit(1)}},Je=(e=process.env.npm_config_user_agent||``)=>e.startsWith(`bun/`)?`bunx`:e.startsWith(`pnpm/`)?`pnpm dlx`:e.startsWith(`yarn/`)?`yarn dlx`:e.startsWith(`npm/`)?`npx`:`unknown`,Ye=(e=process.platform)=>e===`win32`?{cmd:`powershell`,args:[`-Command`,`irm bun.sh/install.ps1 | iex`]}:{cmd:`bash`,args:[`-c`,`curl -fsSL https://bun.sh/install | bash`]},Xe=(e=process.argv.slice(2))=>[`zerostarter`,...e].join(` `),Ze=()=>g(process.env.BUN_INSTALL||g(w(),`.bun`),`bin`),Qe=async(e=!1)=>{if(await Fe())return;let t=Je(),n=`bunx --bun ${Xe()}`;if(console.error(j(`Seems like you are using ${t===`unknown`?`npx/pnpx`:t} or Windows, try using:`)),console.error(M(` ${n}`)),console.error(``),e||B()&&await Y(`Or install bun now and continue?`,!0)){let{cmd:e,args:t}=Ye();console.error(`Installing bun from https://bun.sh/install ...`);try{if(await le(e,t),process.env.PATH=`${Ze()}${m}${process.env.PATH||``}`,await Fe())return;console.error(j(`
13
+ bun is installed but isn't on this shell's PATH yet. Open a new terminal and re-run:`)),console.error(M(` ${n}`))}catch{console.error(j(`
14
+ Could not install bun. Install it from https://bun.sh, then re-run:`)),console.error(M(` ${n}`))}}process.exit(1)},Z=[{value:`apiDocs`,flag:`api-docs`,label:`API docs`},{value:`blog`,flag:`blog`,label:`Blog`},{value:`docs`,flag:`docs`,label:`Docs`},{value:`internalDocs`,flag:`internal-docs`,label:`Internal (console) docs`},{value:`waitlist`,flag:`waitlist`,label:`Waitlist (else a plain landing home)`}],$e=`Usage:
10
15
  $ bunx zerostarter init [dir] [options]
11
16
 
12
17
  Scaffold ZeroStarter into dir (default .) as a fresh product. The author's
@@ -17,16 +22,20 @@ latest ZeroStarter is fetched into it first.
17
22
 
18
23
  Options:
19
24
  -y, --yes Skip prompts, taking defaults (provisions Postgres when Docker is running)
25
+ --canary Scaffold from the canary branch instead of main (for testing)
20
26
  --db Provision a local Postgres (pglaunch) and migrate; needs Docker
21
27
  --dry-run Print the plan without writing anything
22
- -h, --help Display help`);return}n[`dry-run`]||await Y(!!n.yes);let r=x()&&!n.yes,i=t[0]??`.`,a=h(i),o=X(a);if(!o&&!Ae(a)){if(!r)throw Error(`Directory is not empty. Run it in an empty directory, or pass a project name: bunx zerostarter init <name>`);let e=await ae(`Directory isn't empty. Project name`);if(!e)throw Error(`No directory name provided.`);i=e}let c=h(i),l=f(c),d={name:l};if(n[`dry-run`]){console.log(`bunx zerostarter init (dry run)`),console.log(` target: ${c}`),console.log(` name: ${l}`),console.log(` mode: ${X(c)?`in place`:`fetch first`}`);return}if(o&&r&&!await D(T(`Convert ${c} in place? This rewrites files and commits.`),!1)){console.log(`Aborted.`);return}console.log(),X(c)||(console.log(`Fetching the latest ZeroStarter ...`),await ye(c)),s(m(c,`.git`))||(await we(c),await J(c,`ci(init): scaffold from zerostarter`),await Te(c,`main`)),console.log(`Removing starter content and rebranding ...`),u(c,d),console.log(`Installing dependencies ...`),await U(c),await J(c,`ci(init): re-baseline as ${l}`),B(c);let p=!1,g=await fe(),_=V(c),v=!1;if(_?n.db&&console.log(T(` --db ignored: POSTGRES_URL is already set in .env.`)):n.db?v=!0:r?(console.log(),v=await D(`Provision a local Postgres database now?`,g)):v=g,v&&g)try{await ge(c),p=!0}catch(e){let t=e.stderr,n=t?String(t).trim():e instanceof Error?e.message:String(e);V(c)?console.log(T(`
23
- Postgres is provisioned, but the migration failed; run bun run db:migrate.`)):console.log(T(`
24
- Database setup failed; set POSTGRES_URL in .env yourself.`)),n&&console.log(T(` ${n}`))}else v&&console.log(T(`
25
- Docker isn't running, so the database wasn't provisioned. Set POSTGRES_URL in .env, or start Docker and re-run for automatic setup.`));let b=[[`packages/config/src/site.ts`,`your brand: name, tagline, links`],[`web/next/content`,`your docs and blog`],[`web/next/public`,`your logo and assets`]];console.log(`\n${w(`✓`)} ${l} is ready.\n`),console.log(`Next steps:`),c!==process.cwd()&&console.log(` ${C(`cd ${i}`)}`),V(c)||console.log(` ${C(`set POSTGRES_URL in .env`)} # your Postgres connection string`),p||console.log(` ${C(`bun run db:migrate`)}`),console.log(` ${C(`bun run dev`)}`),console.log(`
26
- Push to an empty GitHub repo when ready:`),console.log(` ${C(`git push origin canary`)}`),console.log(`canary becomes the default branch; your next push seeds main and opens the release PR.`),console.log(`
27
- Make it yours:`);for(let[e,t]of b)console.log(` ${e.padEnd(29)} ${t}`);console.log(p?`
28
- Everything works out of the box: dependencies are installed and the local Postgres is migrated. Add OAuth or other credentials to .env whenever you like.`:`
29
- It needs a Postgres database to run: a hosted one like Neon works, or a local Docker one (re-run with Docker running to auto-provision). OAuth and other credentials are optional.`)},Me=async t=>{let{positionals:n,values:r}=y({allowPositionals:!0,args:t,options:{help:{short:`h`,type:`boolean`},yes:{short:`y`,type:`boolean`}}});if(r.help){console.log(`Usage:
28
+ -h, --help Display help
29
+
30
+ Features (default on, except the waitlist; pass any flag to skip the interactive picker):
31
+ --api-docs, --no-api-docs The /api/docs API reference
32
+ --blog, --no-blog The /blog
33
+ --docs, --no-docs The /docs
34
+ --internal-docs, --no-internal-docs The /console/docs internal docs
35
+ --waitlist, --no-waitlist The /waitlist (off leaves a plain landing home)`,et=e=>!y(e)||ee(e).filter(e=>e!==`.git`).length===0,Q=e=>s(g(e,`packages/config/src/site.ts`)),tt=e=>{let t=e,{root:n}=_(t);for(;;){if(y(g(t,`bun.lock`))||y(g(t,`bun.lockb`)))return t;let e=g(t,`package.json`);if(y(e))try{if(JSON.parse(b(e,`utf8`)).workspaces)return t}catch{}if(t===n)return null;t=h(t)}},nt=async e=>{let{positionals:t,values:n}=qe($e,{allowPositionals:!0,args:e,options:{canary:{type:`boolean`},db:{type:`boolean`},"dry-run":{type:`boolean`},help:{short:`h`,type:`boolean`},yes:{short:`y`,type:`boolean`},"api-docs":{type:`boolean`},"no-api-docs":{type:`boolean`},blog:{type:`boolean`},"no-blog":{type:`boolean`},docs:{type:`boolean`},"no-docs":{type:`boolean`},"internal-docs":{type:`boolean`},"no-internal-docs":{type:`boolean`},waitlist:{type:`boolean`},"no-waitlist":{type:`boolean`}}});if(n.help){console.log($e);return}n[`dry-run`]||await Qe(!!n.yes);let r=B()&&!n.yes,i=n.canary?`canary`:`main`,a=n,o=Z.some(e=>a[e.flag]||a[`no-${e.flag}`]),c={...l};for(let e of Z)c[e.value]=a[`no-${e.flag}`]?!1:a[e.flag]?!0:l[e.value];let u=t[0]??`.`,f=v(u),m=Q(f),_=!m&&!et(f);if(!m&&!n[`dry-run`]){let e=tt(_?process.cwd():h(f));if(e)throw Error(`Cannot scaffold inside an existing project (a workspace was found at ${e}). Run it in a fresh directory outside that project.`)}if(n[`dry-run`]||H(V(`https://zerostarter.dev`)),_){if(!r)throw Error(`Directory is not empty. Run it in an empty directory, or pass a project name: bunx zerostarter init <name>`);let e=await xe(`What should we name your project?`);if(!e)throw Error(`No directory name provided.`);u=e}let y=v(u),b=p(y),ee={name:b},x=`--canary ignored: converting the existing checkout in place, so nothing is fetched.`,te=e=>Z.filter(t=>e[t.value]).map(e=>e.value).join(`, `)||`none`;if(n[`dry-run`]){console.log(`bunx zerostarter init (dry run)`),console.log(` target: ${y}`),console.log(` name: ${b}`),console.log(` mode: ${Q(y)?`in place`:`fetch ${i}`}`),console.log(` features: ${te(c)}`),Q(y)&&n.canary&&console.log(` note: ${x}`);return}if(m&&r&&!await Y(`Convert ${b} in place? This rewrites files and commits.`,!1)){W(`Aborted`);return}if(r&&!o){let e=new Set(await be(`Which optional surfaces should this fork ship with?`,Z.map(e=>({value:e.value,label:e.label,checked:l[e.value]}))));for(let t of Z)c[t.value]=e.has(t.value)}if(Q(y))n.canary&&J(x);else{let e=n.canary?` (canary)`:``;await ve(`Fetching the latest ZeroStarter${e}`,`Fetched the latest ZeroStarter${e}`,()=>Ie(y,i))}s(g(y,`.git`))||(await We(y),await Ke(y,`ci(init): scaffold from zerostarter`),await Ge(y,`main`)),await ve(`Rebranding to ${b}`,`Rebranded to ${b}`,()=>d(y,ee,c)),await Ne(y),await Ke(y,`ci(init): re-baseline as ${b}`),De(y);let S=!1,C=await Ce(),ne=X(y),w=!1;if(ne?n.db&&J(`--db ignored: POSTGRES_URL is already set in .env.`):w=n.db?!0:r?await Y(`Provision a local Postgres database?`,C):C,w&&C)try{await Me(y),S=!0}catch(e){if(X(y))J(`Postgres is provisioned, but the migration failed; run bun run db:migrate.`);else{let t=e instanceof Error?e.message.trim():String(e);J(`Database setup failed; set POSTGRES_URL in .env yourself.`,t?t.split(`
36
+ `).filter(e=>e.trim()).slice(-5):[])}}else w&&J(`Docker isn't running, so the database wasn't provisioned. Set POSTGRES_URL in .env, or start Docker and re-run for automatic setup.`);K([`${M(`packages/config/src/site.ts`)} and ${M(`web/next/content`)}`,`to manage branding and blogs & docs respectively`].join(`
37
+ `),`Edit and make it yours`),_e(`${b} is ready`);let T=[];y!==process.cwd()&&T.push(M(`cd ${u}`)),X(y)||T.push(`set POSTGRES_URL in .env`),S||T.push(M(`bun run db:migrate`)),T.push(M(`bun run dev`)),K(T.join(`
38
+ `),`Next steps`),U(`Learn more ${V(`https://zerostarter.dev/docs`)}`)},rt=`Usage:
30
39
  $ bunx zerostarter reinit [dir] [options]
31
40
 
32
41
  Re-scaffold an existing git repo (default .) as a fresh ZeroStarter: delete every
@@ -36,7 +45,8 @@ branch; push when ready.
36
45
 
37
46
  Options:
38
47
  -y, --yes Skip the confirmation prompt
39
- -h, --help Display help`);return}await Y(!!r.yes);let i=h(n[0]??`.`),a=f(i),o=x()&&!r.yes;if(await K(i,`No git repository in ${i}. reinit re-scaffolds an existing repo; use init for a new project.`,`Working tree has uncommitted changes. Commit or stash them first; reinit deletes every tracked file.`),o&&!await D(T(`Delete every file in ${i} (keeping .git and .env*) and re-scaffold as ${a}?`),!1)){console.log(`Aborted.`);return}console.log(),console.log(`Removing all files (keeping .git and your .env* files) ...`),await q(i,T(`reinit failed; restored your committed files (deleted gitignored files, except .env*, are gone).`),async()=>{e(i),console.log(`Fetching the latest ZeroStarter ...`),await G(i),console.log(`Rebranding ...`),u(i,{name:a}),console.log(`Installing dependencies ...`),await U(i),B(i),await J(i,`ci(reinit): re-baseline as ${a}`)}),console.log(`\n${w(`✓`)} ${a} re-scaffolded; .git history, remote, and .env* files are intact.`),console.log(`Next steps:`),V(i)||console.log(` ${C(`set POSTGRES_URL in .env`)} # your Postgres connection string`),console.log(` ${C(`bun run db:migrate`)}`),console.log(` ${C(`bun run dev`)}`),console.log(` ${C(`git push`)} # to your existing remote`)},Ne=async e=>{let{positionals:t,values:n}=y({allowPositionals:!0,args:e,options:{help:{short:`h`,type:`boolean`}}});if(n.help){console.log(`Usage:
48
+ -h, --help Display help`,it=async t=>{let{positionals:n,values:r}=qe(rt,{allowPositionals:!0,args:t,options:{help:{short:`h`,type:`boolean`},yes:{short:`y`,type:`boolean`}}});if(r.help){console.log(rt);return}await Qe(!!r.yes);let i=v(n[0]??`.`),a=p(i),o=B()&&!r.yes;if(await He(i,`No git repository in ${i}. reinit re-scaffolds an existing repo; use init for a new project.`,`Working tree has uncommitted changes. Commit or stash them first; reinit deletes every tracked file.`),H(V(`https://zerostarter.dev`)),o&&!await Y(`Delete every file in ${a} (keeping .git and .env*) and re-scaffold?`,!1)){W(`Aborted`);return}await Ue(i,O(`reinit failed; restored your committed files (deleted gitignored files, except .env*, are gone).`),async()=>{e(i),await Le(i),q(`Fetched the latest ZeroStarter (kept .git and .env*)`),d(i,{name:a}),q(`Rebranded to ${a}`),await Ne(i),De(i),await Ke(i,`ci(reinit): re-baseline as ${a}`)});let s=[];X(i)||s.push(`set POSTGRES_URL in .env`),s.push(M(`bun run db:migrate`)),s.push(M(`bun run dev`)),s.push(M(`git push`)),K(s.join(`
49
+ `),`Next steps`),U(D(`${a} re-scaffolded; .git history, remote, and .env* files are intact`))},at=`Usage:
40
50
  $ bunx zerostarter sync [dir] [options]
41
51
 
42
52
  Re-baseline an existing fork (default .) on the latest ZeroStarter: a gitpick overlay
@@ -45,13 +55,14 @@ identity, and favicon are preserved. Requires a clean tree; lands as a reviewabl
45
55
  you commit yourself.
46
56
 
47
57
  Options:
48
- -h, --help Display help`);return}await Y();let u=h(t[0]??`.`);await K(u,`No git repository in ${u}. Run sync inside an existing fork.`,`Working tree has uncommitted changes. Commit or stash them first so the sync lands as a reviewable diff.`);let f=m(u,`package.json`),p=new Map(l(u).map(e=>[e,a(e)])),g=i(await be());g.length===0&&console.log(T(`Warning: no PRESERVE_ON_SYNC directive found; fork-owned files may be overwritten.`)),console.log(),console.log(`Overlaying the latest ZeroStarter (content, public/marketing, and site.ts preserved) ...`),await q(u,T(`Sync failed; rolled the working tree back to your last commit.`),async()=>{await G(u),o(u),d(m(u,`.gitpickignore`));for(let[e,t]of p)s(e)&&r(e,c(t,a(e),e===f));await Se(u,g)}),console.log(`Installing dependencies ...`),await U(u),console.log(),console.log(C(`Synced to the latest ZeroStarter.`)),console.log(`Starter files were updated (edits to them were overwritten); files you added and your`),console.log(`content, public/marketing, and branding were preserved.`),console.log(T(`Review the diff and commit: git -C ${u} status`))},{author:Z,name:Q,version:Pe}=re,$=`Version:
49
- ${Q}@${Pe}
58
+ -h, --help Display help`,ot=async e=>{let{positionals:t,values:n}=qe(at,{allowPositionals:!0,args:e,options:{help:{short:`h`,type:`boolean`}}});if(n.help){console.log(at);return}await Qe();let l=v(t[0]??`.`);await He(l,`No git repository in ${l}. Run sync inside an existing fork.`,`Working tree has uncommitted changes. Commit or stash them first so the sync lands as a reviewable diff.`),H(V(`https://zerostarter.dev`));let d=g(l,`package.json`),p=new Map(u(l).map(e=>[e,a(e)])),m=c(await Re()).preserve;m.length===0&&J(`No PRESERVE_ON_SYNC directive found; fork-owned files may be overwritten.`),await Ue(l,O(`Sync failed; rolled the working tree back to your last commit.`),async()=>{await Le(l),o(l),f(g(l,`.gitpickignore`));for(let[e,t]of p)s(e)&&r(e,i(t,a(e),e===d));await Be(l,m)}),q(`Overlaid the latest ZeroStarter (content, public/marketing, and branding preserved)`),await Ne(l),K([`Starter files were updated (edits overwritten); your content, public/marketing, and branding were preserved.`,O(`Review the diff and commit: git -C ${l} status`)].join(`
59
+ `),`Review the changes`),U(M(`Synced to the latest ZeroStarter`))},{author:st,name:$,version:ct}=he,lt=`Version:
60
+ ${$}@${ct}
50
61
 
51
62
  Go from zero to a production-ready SaaS, rebranded and ready to ship.
52
63
 
53
64
  Usage:
54
- $ ${Q} <command> [options]
65
+ $ ${$} <command> [options]
55
66
 
56
67
  Commands:
57
68
  init [dir] Scaffold ZeroStarter into dir (default .) as a fresh product.
@@ -64,4 +75,4 @@ Options:
64
75
  -h, --help Display help
65
76
 
66
77
  Author:
67
- ${Z.name} <${Z.email}> (${ie(Z.url)})`;(async()=>{try{let e=process.argv.slice(2),t=e[0],n=e.slice(1);switch(t){case`init`:return await je(n);case`reinit`:return await Me(n);case`sync`:return await Ne(n);case void 0:case`-h`:case`--help`:console.log($);return;case`-v`:case`--version`:console.log(`${Q}@${Pe}`);return;default:console.error(E(`Unknown command: ${t}\n`)),console.log($),process.exit(1)}}catch(e){console.error(E(e instanceof Error?e.message:String(e))),process.exit(1)}})();export{};
78
+ ${st.name} <${st.email}> (${ge(st.url)})`;(async()=>{try{let e=process.argv.slice(2),t=e[0],n=e.slice(1);switch(t){case`init`:return await nt(n);case`reinit`:return await it(n);case`sync`:return await ot(n);case void 0:case`-h`:case`--help`:console.log(lt);return;case`-v`:case`--version`:console.log(`${$}@${ct}`);return;default:console.error(j(`Unknown command: ${t}\n`)),console.log(lt),process.exit(1)}}catch(e){pe(e),process.exit(1)}})();export{};
@@ -0,0 +1,124 @@
1
+ import{dirname as e,join as t}from"node:path";import{existsSync as n,mkdirSync as r,readFileSync as i,readdirSync as a,rmSync as o,writeFileSync as s}from"node:fs";const c=e=>{let t=[],n=[],r=!1;for(let i of e.split(`
2
+ `)){let e=i.trim();if(e){if(!r&&e.startsWith(`# PRESERVE_ON_SYNC`)){n=e.slice(18).replace(/^\s*-\s*/,``).split(`,`).map(e=>e.trim()).filter(Boolean),r=!0;continue}e.startsWith(`#`)||t.push(e)}}return{excludes:t,preserve:n}},l=e=>n(e),u=e=>i(e,`utf8`),d=(t,n)=>{r(e(t),{recursive:!0}),s(t,n)},f=e=>{o(e,{force:!0,recursive:!0})},p=new Set([`node_modules`,`.next`,`.turbo`,`dist`]),m=(e,n=!0)=>{for(let r of a(e,{withFileTypes:!0})){let i=t(e,r.name);if(r.name===`.git`){if(n)continue;o(i,{force:!0,recursive:!0});continue}r.isDirectory()?p.has(r.name)?o(i,{force:!0,recursive:!0}):(m(i,!1),a(i).length===0&&o(i,{force:!0,recursive:!0})):r.name.startsWith(`.env`)||o(i,{force:!0})}},h=e=>{let n=[];for(let r of a(e,{withFileTypes:!0})){if(r.name===`.git`||p.has(r.name))continue;let i=t(e,r.name);r.isDirectory()?n.push(...h(i)):r.name===`package.json`&&n.push(i)}return n},g=e=>JSON.parse(u(e)),_=(e,t)=>{d(e,`${JSON.stringify(t,null,2)}\n`)},v=(e,t)=>{if(!n(e))return!1;let r=u(e),i=r.replace(t,``);return i===r?!1:(s(e,i),!0)},y=[`homepage`,`bugs`,`license`,`author`,`repository`,`funding`],b=[`name`,`version`,...y],x=(e,t)=>{let n={...e,...t};return Object.keys(n).length>0?n:void 0},S=(e,t)=>{let n=Array.isArray(e)?e:[],r=Array.isArray(t)?t:[];return n.length||r.length?[...new Set([...r,...n])]:void 0},C=[`dependencies`,`devDependencies`,`optionalDependencies`,`peerDependencies`,`resolutions`,`catalog`,`scripts`,`overrides`],w=(e,t,n)=>{let r={...t};for(let n of Object.keys(e))n in t||(r[n]=e[n]);for(let n of C){let i=x(e[n],t[n]);i&&(r[n]=i)}let i=S(e.workspaces,t.workspaces);if(i&&(r.workspaces=i),n)for(let t of b)t in e?r[t]=e[t]:delete r[t];return r},T={apiDocs:!0,blog:!0,docs:!0,internalDocs:!0,waitlist:!1},E=e=>`// Optional surfaces a fork enables or disables. Typed boolean (not \`as const\`) so a fork can flip them and the runtime gates are not dead code. Off means the routes 404 and the links, nav, sitemap, llms, and search drop the surface. waitlist off makes the home a plain landing page.
3
+ export const features = {
4
+ apiDocs: ${e.apiDocs},
5
+ blog: ${e.blog},
6
+ docs: ${e.docs},
7
+ internalDocs: ${e.internalDocs},
8
+ waitlist: ${e.waitlist},
9
+ }
10
+
11
+ export type Feature = keyof typeof features`,D=({name:e},t=T)=>{let n=e.charAt(0).toUpperCase()+e.slice(1);return`// Brand identity for this app: the single source a fork edits to rebrand. web reads it via lib/config.ts.
12
+ export const site = {
13
+ name: "${n}",
14
+ description: "${n} is just getting started. Tell its story here.",
15
+ tagline: "Your tagline, ready when you are.",
16
+ social: {
17
+ github: "",
18
+ x: "",
19
+ discord: "",
20
+ },
21
+ // Local-only dev agent identity (api/hono agents router).
22
+ agent: {
23
+ name: "LocalAgent",
24
+ email: "agent@local.host",
25
+ },
26
+ // Injectable long-form text blocks. A product sets its own, or leaves them empty.
27
+ apiReferenceDescription: "",
28
+ llmsFullPreamble: "",
29
+ } as const
30
+
31
+ export type Site = typeof site
32
+
33
+ ${E(t)}
34
+ `},O=()=>`import { features, site } from "@packages/config/site"
35
+ import { redirect } from "next/navigation"
36
+
37
+ // Fresh fork: the waitlist capture when the waitlist feature is on, otherwise a plain landing page. Replace this with your real home when ready.
38
+ export default function Home() {
39
+ if (features.waitlist) redirect("/waitlist")
40
+
41
+ return (
42
+ <main className="flex min-h-svh flex-col items-center justify-center p-8 text-center">
43
+ <div className="mx-auto flex w-full max-w-xl flex-col items-center">
44
+ <h1 className="mb-4 text-5xl font-bold tracking-tight sm:text-6xl">{site.name}</h1>
45
+ <p className="text-muted-foreground max-w-md text-lg">{site.tagline}</p>
46
+ </div>
47
+ </main>
48
+ )
49
+ }
50
+ `,k=()=>'# AGENTS.md\n\nGuidance for AI coding agents working in this repository.\n\n## Instructions\n\n- ALWAYS: Use `@/` for imports, and follow the `design` skill for UI and styling conventions.\n- ALWAYS: Keep documentation in sync with every change.\n- NEVER: Include "Co-authored-by" in commit messages.\n- NEVER: Use em-dashes (the long dash, U+2014) in code, comments, docs, or copy. Regular hyphens are fine; for a pause or aside, use a comma, colon, or period.\n\n## Skills\n\nCustom skills live in `.agents/skills` (symlinked to `.claude/skills`). Start with the `codebase-map` skill to orient, then load the task skill that fits (`api-endpoint`, `db-migration`, `dev`, `design`, and more).\n',A=({name:e})=>`# ${e.charAt(0).toUpperCase()+e.slice(1)}
51
+
52
+ Built on top of [ZeroStarter](https://zerostarter.dev).
53
+
54
+ ## Development
55
+
56
+ \`\`\`bash
57
+ bun run dev
58
+ \`\`\`
59
+
60
+ Web runs on http://localhost:3000 and the API on http://localhost:4000.
61
+ `,j=()=>`---
62
+ slug: /docs
63
+ title: Introduction
64
+ description: Documentation.
65
+ ---
66
+
67
+ # Introduction
68
+
69
+ This is your documentation home. Start writing, this page is yours.
70
+ `,M=()=>`---
71
+ slug: /console/docs
72
+ title: Introduction
73
+ description: Internal documentation.
74
+ ---
75
+
76
+ # Introduction
77
+
78
+ Your team's internal docs live here.
79
+ `,N=e=>`---
80
+ title: Blog
81
+ description: Latest articles and updates
82
+ createdAt: ${e}
83
+ ---
84
+
85
+ ## Recent Posts
86
+
87
+ <BlogPostList />
88
+ `,P=e=>`---
89
+ title: Hello World
90
+ description: The first post on your new blog.
91
+ createdAt: ${e}
92
+ publishedAt: ${e}
93
+ ---
94
+
95
+ ## Hello World
96
+
97
+ Your very first post. Replace it with something worth reading.
98
+ `,F=()=>`import type { DocsConfig } from "./src/lib/docs"
99
+
100
+ const docsConfig = {
101
+ docs: {
102
+ "Getting Started": [
103
+ {
104
+ "/docs": {
105
+ title: "Introduction",
106
+ description: "Documentation.",
107
+ },
108
+ },
109
+ ],
110
+ },
111
+ console: {
112
+ "Getting Started": [
113
+ {
114
+ "/console/docs": {
115
+ title: "Introduction",
116
+ description: "Internal documentation.",
117
+ },
118
+ },
119
+ ],
120
+ },
121
+ } satisfies DocsConfig
122
+
123
+ export default docsConfig
124
+ `,I=(e,...n)=>t(e,...n),L=e=>e.toLowerCase().replace(/[^a-z0-9]+/g,`-`).replace(/^-+|-+$/g,``)||`app`,R=e=>{let t=I(e,`.gitpickignore`);if(l(t)){for(let n of c(u(t)).excludes){if(/[*?![\]]/.test(n))throw Error(`.gitpickignore entry "${n}" is not a literal path; the in-place converter only supports literal paths (a glob or negation would diverge from gitpick's fetch).`);f(I(e,n))}f(t)}},z=/[ \t]*\{[^}\r\n]*href:[ \t]*["']\/hire["'][^}\r\n]*\},?[ \t]*\r?\n/,B=(e,t)=>{let n=new Date,r=n.toISOString().slice(0,10),i=[n.getFullYear(),n.getMonth()+1,n.getDate()].map(e=>String(e).padStart(2,`0`)).join(`-`),a=i<r?i:r;d(I(e,`web/next/content/docs/index.mdx`),j()),d(I(e,`web/next/content/blog/index.mdx`),N(a)),d(I(e,`web/next/content/blog/hello-world.mdx`),P(a)),d(I(e,`web/next/content/console/docs/index.mdx`),M()),d(I(e,`web/next/docs.config.ts`),F()),d(I(e,`web/next/public/.gitkeep`),``),d(I(e,`web/next/src/app/page.tsx`),O()),d(I(e,`AGENTS.md`),k()),d(I(e,`README.md`),A(t))},V=e=>{let t=I(e,`web/next/src/lib/fonts.ts`),n=I(e,`web/next/src/lib/marketing/fonts.ts`),r=I(e,`web/next/src/components/common/navbar.tsx`);if(v(r,z),l(n))throw Error(`web/next/src/lib/marketing/fonts.ts survived the fork strip (add web/next/src/lib/marketing/ to .gitpickignore).`);if(l(t)&&u(t).includes(`fonts/marketing/`))throw Error(`fonts.ts references fonts/marketing/; author-only marketing fonts must live in the fork-excluded web/next/src/lib/marketing/, not the shared fonts.ts.`);if(l(r)&&/href:\s*["']\/hire["']/.test(u(r)))throw Error(`common/navbar.tsx still has the /hire entry after fixDangling (regex drift). Update packages/cli/src/convert.ts.`)},H=(e,t,n)=>{d(I(e,`packages/config/src/site.ts`),D(t,n));let r=I(e,`package.json`),i=g(r);i.name=L(t.name),i.version=`0.0.0`;for(let e of y)delete i[e];_(r,i)},U=(e,t,n=T)=>{R(e),B(e,t),V(e),H(e,t,n)};export{m as a,u as c,d,_ as f,w as i,g as l,V as n,l as o,c as p,T as r,h as s,U as t,f as u};
package/dist/index.d.mts CHANGED
@@ -2,8 +2,15 @@
2
2
  interface Brand {
3
3
  name: string;
4
4
  }
5
+ type FeatureFlags = {
6
+ apiDocs: boolean;
7
+ blog: boolean;
8
+ docs: boolean;
9
+ internalDocs: boolean;
10
+ waitlist: boolean;
11
+ };
5
12
  //#endregion
6
13
  //#region src/convert.d.ts
7
- declare const convertRepo: (root: string, brand: Brand) => void;
14
+ declare const convertRepo: (root: string, brand: Brand, features?: FeatureFlags) => void;
8
15
  //#endregion
9
16
  export { type Brand, convertRepo };
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import{t as e}from"./convert-DMU8fiBc.mjs";export{e as convertRepo};
1
+ import{t as e}from"./convert-DHCJQjgd.mjs";export{e as convertRepo};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zerostarter",
3
- "version": "0.0.0-93a2b9d",
3
+ "version": "0.0.0-9ed3044",
4
4
  "description": "Go from zero to a production-ready SaaS, rebranded and ready to ship.",
5
5
  "keywords": [
6
6
  "cli",
@@ -1,104 +0,0 @@
1
- import{dirname as e,join as t}from"node:path";import{existsSync as n,mkdirSync as r,readFileSync as i,readdirSync as a,rmSync as o,writeFileSync as s}from"node:fs";const c=e=>n(e),l=e=>i(e,`utf8`),u=(t,n)=>{r(e(t),{recursive:!0}),s(t,n)},d=e=>{o(e,{force:!0,recursive:!0})},f=new Set([`node_modules`,`.next`,`.turbo`,`dist`]),p=(e,n=!0)=>{for(let r of a(e,{withFileTypes:!0})){let i=t(e,r.name);if(r.name===`.git`){if(n)continue;o(i,{force:!0,recursive:!0});continue}r.isDirectory()?f.has(r.name)?o(i,{force:!0,recursive:!0}):(p(i,!1),a(i).length===0&&o(i,{force:!0,recursive:!0})):r.name.startsWith(`.env`)||o(i,{force:!0})}},m=e=>{let n=[];for(let r of a(e,{withFileTypes:!0})){if(r.name===`.git`||f.has(r.name))continue;let i=t(e,r.name);r.isDirectory()?n.push(...m(i)):r.name===`package.json`&&n.push(i)}return n},h=e=>JSON.parse(l(e)),g=(e,t)=>{u(e,`${JSON.stringify(t,null,2)}\n`)},_=(e,t)=>{if(!n(e))return!1;let r=l(e),i=r.replace(t,``);return i===r?!1:(s(e,i),!0)},v=[`homepage`,`bugs`,`license`,`author`,`repository`,`funding`],y=[`name`,`version`,...v],b=e=>{let t=e.split(`
2
- `).find(e=>e.trim().startsWith(`# PRESERVE_ON_SYNC`));return t?t.trim().slice(18).replace(/^\s*-\s*/,``).split(`,`).map(e=>e.trim()).filter(Boolean):[]},x=(e,t)=>{let n={...e,...t};return Object.keys(n).length>0?n:void 0},S=(e,t)=>{let n=Array.isArray(e)?e:[],r=Array.isArray(t)?t:[];return n.length||r.length?[...new Set([...r,...n])]:void 0},C=[`dependencies`,`devDependencies`,`optionalDependencies`,`peerDependencies`,`resolutions`,`catalog`,`scripts`,`overrides`],w=(e,t,n)=>{let r={...t};for(let n of Object.keys(e))n in t||(r[n]=e[n]);for(let n of C){let i=x(e[n],t[n]);i&&(r[n]=i)}let i=S(e.workspaces,t.workspaces);if(i&&(r.workspaces=i),n)for(let t of y)t in e?r[t]=e[t]:delete r[t];return r},T=({name:e})=>{let t=e.charAt(0).toUpperCase()+e.slice(1);return`// Brand identity for this app: the single source a fork edits to rebrand. web reads it via lib/config.ts.
3
- export const site = {
4
- name: "${t}",
5
- description: "${t} is just getting started. Tell its story here.",
6
- tagline: "Your tagline, ready when you are.",
7
- social: {
8
- github: "",
9
- x: "",
10
- discord: "",
11
- },
12
- // Local-only dev agent identity (api/hono agents router).
13
- agent: {
14
- name: "LocalAgent",
15
- email: "agent@local.host",
16
- },
17
- // Injectable long-form text blocks. A product sets its own, or leaves them empty.
18
- apiReferenceDescription: "",
19
- llmsFullPreamble: "",
20
- } as const
21
-
22
- export type Site = typeof site
23
- `},E=()=>`import { redirect } from "next/navigation"
24
-
25
- // Fresh fork: redirect to the waitlist until you build your real home page.
26
- export default function Home() {
27
- redirect("/waitlist")
28
- }
29
- `,D=()=>'# AGENTS.md\n\nGuidance for AI coding agents working in this repository.\n\n## Instructions\n\n- ALWAYS: Use `@/` for imports, and follow the `design` skill for UI and styling conventions.\n- ALWAYS: Keep documentation in sync with every change.\n- NEVER: Include "Co-authored-by" in commit messages.\n- NEVER: Use em-dashes (the long dash, U+2014) in code, comments, docs, or copy. Regular hyphens are fine; for a pause or aside, use a comma, colon, or period.\n\n## Skills\n\nCustom skills live in `.agents/skills` (symlinked to `.claude/skills`). Start with the `codebase-map` skill to orient, then load the task skill that fits (`api-endpoint`, `db-migration`, `dev`, `design`, and more).\n',O=({name:e})=>`# ${e.charAt(0).toUpperCase()+e.slice(1)}
30
-
31
- Built on top of [ZeroStarter](https://zerostarter.dev).
32
-
33
- ## Development
34
-
35
- \`\`\`bash
36
- bun run dev
37
- \`\`\`
38
-
39
- Web runs on http://localhost:3000 and the API on http://localhost:4000.
40
- `,k=()=>`---
41
- slug: /docs
42
- title: Introduction
43
- description: Documentation.
44
- ---
45
-
46
- # Introduction
47
-
48
- This is your documentation home. Start writing, this page is yours.
49
- `,A=()=>`---
50
- slug: /console/docs
51
- title: Introduction
52
- description: Internal documentation.
53
- ---
54
-
55
- # Introduction
56
-
57
- Your team's internal docs live here.
58
- `,j=e=>`---
59
- title: Blog
60
- description: Latest articles and updates
61
- createdAt: ${e}
62
- ---
63
-
64
- ## Recent Posts
65
-
66
- <BlogPostList />
67
- `,M=e=>`---
68
- title: Hello World
69
- description: The first post on your new blog.
70
- createdAt: ${e}
71
- publishedAt: ${e}
72
- ---
73
-
74
- ## Hello World
75
-
76
- Your very first post. Replace it with something worth reading.
77
- `,N=()=>`import type { DocsConfig } from "./src/lib/docs/types"
78
-
79
- const docsConfig = {
80
- docs: {
81
- "Getting Started": [
82
- {
83
- "/docs": {
84
- title: "Introduction",
85
- description: "Documentation.",
86
- },
87
- },
88
- ],
89
- },
90
- console: {
91
- "Getting Started": [
92
- {
93
- "/console/docs": {
94
- title: "Introduction",
95
- description: "Internal documentation.",
96
- },
97
- },
98
- ],
99
- },
100
- } satisfies DocsConfig
101
-
102
- export default docsConfig
103
- `,P=(e,...n)=>t(e,...n),F=e=>e.toLowerCase().replace(/[^a-z0-9]+/g,`-`).replace(/^-+|-+$/g,``)||`app`,I=e=>{let t=P(e,`.gitpickignore`);if(c(t)){for(let n of l(t).split(`
104
- `)){let t=n.trim();if(!(!t||t.startsWith(`#`))){if(/[*?![\]]/.test(t))throw Error(`.gitpickignore entry "${t}" is not a literal path; the in-place converter only supports literal paths (a glob or negation would diverge from gitpick's fetch).`);d(P(e,t))}}d(t)}},L=/\r?\nexport const caveat = localFont\(\{[\s\S]*?\r?\n\}\)\r?\n/,R=/\r?\nexport const newsreader = localFont\(\{[\s\S]*?\r?\n\}\)\r?\n/,z=/[ \t]*\{[^}\r\n]*href:[ \t]*["']\/hire["'][^}\r\n]*\},?[ \t]*\r?\n/,B=(e,t)=>{let n=new Date,r=n.toISOString().slice(0,10),i=[n.getFullYear(),n.getMonth()+1,n.getDate()].map(e=>String(e).padStart(2,`0`)).join(`-`),a=i<r?i:r;u(P(e,`web/next/content/docs/index.mdx`),k()),u(P(e,`web/next/content/blog/index.mdx`),j(a)),u(P(e,`web/next/content/blog/hello-world.mdx`),M(a)),u(P(e,`web/next/content/console/docs/index.mdx`),A()),u(P(e,`web/next/docs.config.ts`),N()),u(P(e,`web/next/public/.gitkeep`),``),u(P(e,`web/next/src/app/page.tsx`),E()),u(P(e,`AGENTS.md`),D()),u(P(e,`README.md`),O(t))},V=e=>{let t=P(e,`web/next/src/lib/fonts.ts`),n=P(e,`web/next/src/components/navbar/home.tsx`);if(_(t,L),_(t,R),_(n,z),c(t)&&l(t).includes(`fonts/marketing/`))throw Error(`fonts.ts still references fonts/marketing/ after fixDangling (regex drift). Update packages/cli/src/convert.ts.`);if(c(n)&&/href:\s*["']\/hire["']/.test(l(n)))throw Error(`navbar/home.tsx still has the /hire entry after fixDangling (regex drift). Update packages/cli/src/convert.ts.`)},H=(e,t)=>{u(P(e,`packages/config/src/site.ts`),T(t));let n=P(e,`package.json`),r=h(n);r.name=F(t.name),r.version=`0.0.0`;for(let e of v)delete r[e];g(n,r)},U=(e,t)=>{I(e),B(e,t),V(e),H(e,t)};export{p as a,l as c,u as d,g as f,b as i,h as l,V as n,c as o,w as r,m as s,U as t,d as u};