launchframe 0.3.0 → 0.3.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 (70) hide show
  1. package/README.md +56 -121
  2. package/bin/launchframe.mjs +315 -0
  3. package/package.json +52 -54
  4. package/template/LICENSE +21 -0
  5. package/template/README.md +121 -0
  6. package/template/package-lock.json +9873 -0
  7. package/template/package.json +54 -0
  8. package/CHANGELOG.md +0 -80
  9. /package/{.aider.conf.yml → template/.aider.conf.yml} +0 -0
  10. /package/{.amazonq → template/.amazonq}/cli-agents/clone-website.json +0 -0
  11. /package/{.amazonq → template/.amazonq}/rules/project.md +0 -0
  12. /package/{.augment → template/.augment}/commands/clone-website.md +0 -0
  13. /package/{.claude → template/.claude}/skills/clone-website/SKILL.md +0 -0
  14. /package/{.claude → template/.claude}/skills/marketing-social-proof-motion/SKILL.md +0 -0
  15. /package/{.clinerules → template/.clinerules} +0 -0
  16. /package/{.codex → template/.codex}/skills/clone-website/SKILL.md +0 -0
  17. /package/{.continue → template/.continue}/commands/clone-website.md +0 -0
  18. /package/{.continue → template/.continue}/rules/project.md +0 -0
  19. /package/{.cursor → template/.cursor}/commands/clone-website.md +0 -0
  20. /package/{.cursor → template/.cursor}/commands/marketing-social-proof-motion.md +0 -0
  21. /package/{.cursor → template/.cursor}/rules/project.mdc +0 -0
  22. /package/{.dockerignore → template/.dockerignore} +0 -0
  23. /package/{.gemini → template/.gemini}/commands/clone-website.toml +0 -0
  24. /package/{.gitattributes → template/.gitattributes} +0 -0
  25. /package/{.github → template/.github}/ISSUE_TEMPLATE/bug_report.yml +0 -0
  26. /package/{.github → template/.github}/ISSUE_TEMPLATE/config.yml +0 -0
  27. /package/{.github → template/.github}/ISSUE_TEMPLATE/feature_request.yml +0 -0
  28. /package/{.github → template/.github}/PULL_REQUEST_TEMPLATE.md +0 -0
  29. /package/{.github → template/.github}/copilot-instructions.md +0 -0
  30. /package/{.github → template/.github}/copilot-setup-steps.yml +0 -0
  31. /package/{.github → template/.github}/skills/clone-website/SKILL.md +0 -0
  32. /package/{.github → template/.github}/workflows/ci.yml +0 -0
  33. /package/{.nvmrc → template/.nvmrc} +0 -0
  34. /package/{.opencode → template/.opencode}/commands/clone-website.md +0 -0
  35. /package/{.windsurf → template/.windsurf}/workflows/clone-website.md +0 -0
  36. /package/{.windsurfrules → template/.windsurfrules} +0 -0
  37. /package/{AGENTS.md → template/AGENTS.md} +0 -0
  38. /package/{CLAUDE.md → template/CLAUDE.md} +0 -0
  39. /package/{Dockerfile → template/Dockerfile} +0 -0
  40. /package/{Dockerfile.dev → template/Dockerfile.dev} +0 -0
  41. /package/{GEMINI.md → template/GEMINI.md} +0 -0
  42. /package/{START_HERE.md → template/START_HERE.md} +0 -0
  43. /package/{components.json → template/components.json} +0 -0
  44. /package/{docker-compose.yml → template/docker-compose.yml} +0 -0
  45. /package/{docs → template/docs}/design-references/.gitkeep +0 -0
  46. /package/{docs → template/docs}/design-references/comparison.png +0 -0
  47. /package/{docs → template/docs}/design-references/playwright-example.com-1440px.png +0 -0
  48. /package/{docs → template/docs}/design-references/playwright-example.com-390px.png +0 -0
  49. /package/{docs → template/docs}/research/INSPECTION_GUIDE.md +0 -0
  50. /package/{eslint.config.mjs → template/eslint.config.mjs} +0 -0
  51. /package/{launchframe.config.json → template/launchframe.config.json} +0 -0
  52. /package/{next.config.ts → template/next.config.ts} +0 -0
  53. /package/{postcss.config.mjs → template/postcss.config.mjs} +0 -0
  54. /package/{public → template/public}/images/.gitkeep +0 -0
  55. /package/{public → template/public}/seo/.gitkeep +0 -0
  56. /package/{public → template/public}/videos/.gitkeep +0 -0
  57. /package/{scripts → template/scripts}/.gitkeep +0 -0
  58. /package/{scripts → template/scripts}/recon-playwright.mjs +0 -0
  59. /package/{scripts → template/scripts}/sync-agent-rules.sh +0 -0
  60. /package/{scripts → template/scripts}/sync-skills.mjs +0 -0
  61. /package/{src → template/src}/app/favicon.ico +0 -0
  62. /package/{src → template/src}/app/globals.css +0 -0
  63. /package/{src → template/src}/app/layout.tsx +0 -0
  64. /package/{src → template/src}/app/page.tsx +0 -0
  65. /package/{src → template/src}/components/marketing/scribewise-landing.tsx +0 -0
  66. /package/{src → template/src}/components/ui/button.tsx +0 -0
  67. /package/{src → template/src}/hooks/.gitkeep +0 -0
  68. /package/{src → template/src}/lib/utils.ts +0 -0
  69. /package/{src → template/src}/types/.gitkeep +0 -0
  70. /package/{tsconfig.json → template/tsconfig.json} +0 -0
package/README.md CHANGED
@@ -1,121 +1,56 @@
1
- # Launchframe Project
2
-
3
- This project was scaffolded by **[Launchframe](https://github.com/evangruhlkey/launchframe)** an AI-powered website cloner + SaaS rebrander.
4
-
5
- ```bash
6
- npx launchframe@latest <url> "<saas idea>"
7
- ```
8
-
9
- Dependencies were installed for you. Files were written to **this directory** (project root) so **`.cursor`**, **`.claude`**, etc. work when you open this folder in your editor. Config lives in **`launchframe.config.json`** (`url` + SaaS `idea`).
10
-
11
- ---
12
-
13
- ## Quick start (two steps)
14
-
15
- 1. **Open this folder** in [Cursor](https://cursor.com/) the directory that **contains** `.cursor/` (not a parent folder).
16
- 2. In chat, say: **Build it.**
17
-
18
- Your AI reads [`launchframe.config.json`](./launchframe.config.json) and [`AGENTS.md`](./AGENTS.md) and runs the full clone + rebrand pipeline (same as **`/clone-website`**).
19
-
20
- Rather read a postcard? See [`START_HERE.md`](./START_HERE.md).
21
-
22
- ## What `/clone-website` does
23
-
24
- A multi-phase pipeline runs inside your AI agent:
25
-
26
- 1. **Reconnaissance** — screenshots, design-token extraction, **image/video inventory** (`MEDIA_MANIFEST.md`), interaction sweep (scroll, click, hover, responsive)
27
- 2. **Foundation** — fonts, globals, **`framer-motion`**, **download images & videos** to `public/` before most UI build
28
- 3. **Component Specs** writes detailed spec files (`docs/research/components/`) with exact CSS, **local media paths**, and **Motion** (CSS vs Framer)
29
- 4. **Parallel Build** — dispatches builder agents in git worktrees, one per section
30
- 5. **SaaS Rebrand Pass** swaps product name, headlines, feature copy, CTAs, and brand marks to match `launchframe.config.json#idea`. Visuals stay 1:1.
31
- 6. **Assembly & Visual QA** — merges worktrees, wires up the page, runs visual diff against the original
32
-
33
- Each builder agent receives the full component spec inline — exact `getComputedStyle()` values, interaction models, multi-state content, responsive breakpoints, asset paths. No guessing.
34
-
35
- ## Supported AI Agents
36
-
37
- | Agent | Status |
38
- | ------------------------------------------------------------- | -------------------------- |
39
- | [Claude Code](https://docs.anthropic.com/en/docs/claude-code) | **Recommended** Opus 4.7 |
40
- | [Codex CLI](https://github.com/openai/codex) | Supported |
41
- | [OpenCode](https://opencode.ai/) | Supported |
42
- | [GitHub Copilot](https://github.com/features/copilot) | Supported |
43
- | [Cursor](https://cursor.com/) | Supported |
44
- | [Windsurf](https://codeium.com/windsurf) | Supported |
45
- | [Gemini CLI](https://github.com/google-gemini/gemini-cli) | Supported |
46
- | [Cline](https://github.com/cline/cline) | Supported |
47
- | [Roo Code](https://github.com/RooCodeInc/Roo-Code) | Supported |
48
- | [Continue](https://continue.dev/) | Supported |
49
- | [Amazon Q](https://aws.amazon.com/q/developer/) | Supported |
50
- | [Augment Code](https://www.augmentcode.com/) | Supported |
51
- | [Aider](https://aider.chat/) | Supported |
52
-
53
- ## Tech Stack
54
-
55
- - **Next.js 16** — App Router, React 19, TypeScript strict
56
- - **shadcn/ui** Radix primitives + Tailwind CSS v4
57
- - **Tailwind CSS v4** — oklch design tokens
58
- - **Framer Motion** — default for non-trivial marketing animation (scroll reveals, staggers, layout); see `AGENTS.md`
59
- - **Lucide React** — default icons (replaced by extracted SVGs during cloning)
60
- - **Media** — targets download to `public/images/` & `public/videos/` per `docs/research/MEDIA_MANIFEST.md` (see `INSPECTION_GUIDE.md`)
61
-
62
- ## Prerequisites
63
-
64
- - [Node.js](https://nodejs.org/) 24+
65
- - An AI coding agent with browser automation MCP **or** [Playwright](https://playwright.dev/) for `npm run recon` when MCP is unavailable
66
- - **First-time Playwright browsers:** after `npm install`, run `npx playwright install chromium`
67
-
68
- ## Project Structure
69
-
70
- ```
71
- src/
72
- app/ # Next.js routes
73
- components/ # React components
74
- ui/ # shadcn/ui primitives
75
- icons.tsx # Extracted SVG icons
76
- lib/utils.ts # cn() utility
77
- types/ # TypeScript interfaces
78
- hooks/ # Custom React hooks
79
- public/
80
- images/ # Downloaded images from target
81
- videos/ # Downloaded videos from target
82
- seo/ # Favicons, OG images
83
- docs/
84
- research/ # Extraction output, component specs, REBRAND.md
85
- design-references/ # Screenshots
86
- scripts/
87
- sync-agent-rules.sh # Regenerate agent instruction files
88
- sync-skills.mjs # Regenerate /clone-website for all platforms
89
- launchframe.config.json # ← URL + SaaS idea (single source of truth)
90
- AGENTS.md # Agent instructions (single source of truth)
91
- START_HERE.md # "Open Cursor → say Build it"
92
- CLAUDE.md # Claude Code config (imports AGENTS.md)
93
- GEMINI.md # Gemini CLI config (imports AGENTS.md)
94
- ```
95
-
96
- ## Commands
97
-
98
- ```bash
99
- npm run dev # Start dev server
100
- npm run build # Production build
101
- npm run lint # ESLint check
102
- npm run typecheck # TypeScript check
103
- npm run check # Run lint + typecheck + build
104
- ```
105
-
106
- ## Use Cases
107
-
108
- - **Launch a SaaS faster** — start from a proven landing page, not a blank canvas
109
- - **Platform migration** — rebuild a site you own from WordPress/Webflow/Squarespace into a modern Next.js codebase
110
- - **Lost source code** — your site is live but the repo is gone; get the code back in a modern format
111
- - **Learning** — deconstruct how production sites achieve specific layouts, animations, and responsive behavior
112
-
113
- ## Not Intended For
114
-
115
- - **Phishing or impersonation** — this project must not be used for deceptive purposes, impersonation, or any activity that breaks the law
116
- - **Passing off someone's design as your own** — logos, brand assets, and original copy belong to their owners
117
- - **Violating terms of service** — some sites prohibit scraping or reproduction. Check first
118
-
119
- ## License
120
-
121
- MIT
1
+ # Launchframe
2
+
3
+ > **Scaffold a SaaS-ready Next.js codebase from any URL in one command.**
4
+
5
+ ```bash
6
+ npx launchframe@latest <url> "<saas idea>"
7
+ ```
8
+
9
+ Launchframe drops an [AI-cloner template](https://github.com/JCodesMore/ai-website-cloner-template) into **your current folder (project root)** by default — so **`.cursor`**, **`.claude`**, and the rest of the dotfolders sit where your editor expects them when you open that folder. It **runs `npm install` for you**. Then tell your AI **Build it** (same as `/clone-website`).
10
+
11
+ ## Why Launchframe
12
+
13
+ Cloning a website is a solved problem if you have a great AI agent and a great template. What's missing is **a one-command entrypoint that wires those together with your specific intent**.
14
+
15
+ - Pick a real, beautiful, production-tested landing page
16
+ - Pick the SaaS you want to ship
17
+ - Get a buildable Next.js codebase in seconds, ready for your AI agent to clone + rebrand
18
+
19
+ ## Quick Start
20
+
21
+ From an **empty** project folder (or after `git init` only):
22
+
23
+ ```bash
24
+ mkdir my-saas && cd my-saas
25
+ npx launchframe@latest https://linear.app "AI-powered customer feedback platform"
26
+ ```
27
+
28
+ Then open **this folder** in [Cursor](https://cursor.com/) and chat: **Build it.**
29
+
30
+ Files land in the **current directory** so workspace rules apply. Prefer a subfolder? Use `--dir launchframe-app`.
31
+
32
+ ## CLI Reference
33
+
34
+ ```
35
+ npx launchframe@latest <url> "<saas idea>" [options]
36
+
37
+ Options:
38
+ --dir <path> Output folder (default: .)
39
+ --force Merge into a non-empty directory (use with care)
40
+ --skip-install Skip npm install (CI / debugging)
41
+ --help, -h Help
42
+ --version, -v Version
43
+ ```
44
+
45
+ ## Monorepo (this repository)
46
+
47
+ - **`template/`** — Next.js app + agent skills (what `npx launchframe` copies).
48
+ - **`bin/launchframe.mjs`** — CLI entrypoint.
49
+
50
+ To work on the app: `cd template && npm install && npm run dev`.
51
+
52
+ To verify the npm tarball before publish: `npm pack` at the repo root — the package must include `bin/` and `template/`.
53
+
54
+ ## License
55
+
56
+ MITsee [LICENSE](./LICENSE).
@@ -0,0 +1,315 @@
1
+ #!/usr/bin/env node
2
+ // @ts-check
3
+ /**
4
+ * launchframe — scaffold an AI-cloner project pointed at any URL + SaaS idea.
5
+ *
6
+ * Usage:
7
+ * npx launchframe@latest <url> "<saas idea>" [--dir <path>] [--force] [--skip-install]
8
+ *
9
+ * Behavior:
10
+ * 1. Validates the URL and SaaS-idea string.
11
+ * 2. Copies the bundled `template/` payload into the project root (current
12
+ * directory by default) so dotfolders like `.cursor` and `.claude` live at
13
+ * the workspace root when you open that folder in your editor. Use
14
+ * `--dir <name>` for a subdirectory if you prefer.
15
+ * 3. Writes `launchframe.config.json` into the new project so the bundled
16
+ * `/clone-website` skill knows which URL to clone and how to re-skin it
17
+ * for your SaaS idea.
18
+ * 4. Runs `npm install` in the new project (so the user does not have to).
19
+ * 5. Prints one line: open the folder in Cursor and tell the AI **Build it**.
20
+ */
21
+
22
+ import { spawnSync } from "node:child_process";
23
+ import { cpSync, existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
24
+ import { createRequire } from "node:module";
25
+ import { dirname, basename, isAbsolute, join, resolve } from "node:path";
26
+ import { fileURLToPath } from "node:url";
27
+
28
+ const __filename = fileURLToPath(import.meta.url);
29
+ const __dirname = dirname(__filename);
30
+ const pkgRoot = resolve(__dirname, "..");
31
+ const templateDir = join(pkgRoot, "template");
32
+ const require = createRequire(import.meta.url);
33
+ const pkgJson = require(join(pkgRoot, "package.json"));
34
+
35
+ const COLORS = {
36
+ reset: "\x1b[0m",
37
+ bold: "\x1b[1m",
38
+ dim: "\x1b[2m",
39
+ red: "\x1b[31m",
40
+ green: "\x1b[32m",
41
+ yellow: "\x1b[33m",
42
+ cyan: "\x1b[36m",
43
+ };
44
+ const supportsColor = process.stdout.isTTY && !process.env.NO_COLOR;
45
+ const c = (color, s) => (supportsColor ? COLORS[color] + s + COLORS.reset : s);
46
+
47
+ function printHelp() {
48
+ console.log(`
49
+ ${c("bold", "launchframe")} ${c("dim", `v${pkgJson.version}`)}
50
+ Scaffold an AI-cloner project pointed at any URL + SaaS idea.
51
+
52
+ ${c("bold", "Usage:")}
53
+ npx launchframe@latest <url> "<saas idea>" [options]
54
+
55
+ ${c("bold", "Arguments:")}
56
+ <url> URL of the site you want to clone (e.g. https://linear.app)
57
+ <saas idea> One-line description of the SaaS you're building
58
+ (used to re-skin copy/branding after the visual clone)
59
+
60
+ ${c("bold", "Options:")}
61
+ --dir <path> Output folder (default: . — current directory / project root)
62
+ --force Overwrite merging files into a non-empty directory (use with care)
63
+ --skip-install Skip npm install after scaffold (faster for CI / debugging)
64
+ --help, -h Show this message
65
+ --version, -v Show the launchframe version
66
+
67
+ ${c("bold", "Example:")}
68
+ npx launchframe@latest https://linear.app "AI-powered customer feedback platform"
69
+ # From an empty folder (or git init only), files land in . so .cursor/ works at workspace root
70
+ npx launchframe@latest https://vercel.com "DevOps for ML" --dir launchframe-app
71
+ npx launchframe@latest https://stripe.com "Billing for AI agents" --skip-install
72
+ `);
73
+ }
74
+
75
+ function exitErr(msg, code = 1) {
76
+ console.error(c("red", `\nlaunchframe: ${msg}`));
77
+ console.error(c("dim", "Run `npx launchframe --help` for usage.\n"));
78
+ process.exit(code);
79
+ }
80
+
81
+ function parseArgs(argv) {
82
+ const positional = [];
83
+ const opts = {
84
+ dir: ".",
85
+ force: false,
86
+ skipInstall: false,
87
+ help: false,
88
+ version: false,
89
+ };
90
+ for (let i = 0; i < argv.length; i++) {
91
+ const a = argv[i];
92
+ if (a === "--help" || a === "-h") opts.help = true;
93
+ else if (a === "--version" || a === "-v") opts.version = true;
94
+ else if (a === "--force" || a === "-f") opts.force = true;
95
+ else if (a === "--skip-install") opts.skipInstall = true;
96
+ else if (a === "--dir" || a === "-d") {
97
+ const next = argv[++i];
98
+ if (!next || next.startsWith("-")) exitErr("`--dir` requires a value");
99
+ opts.dir = next;
100
+ } else if (a.startsWith("--dir=")) {
101
+ opts.dir = a.slice("--dir=".length);
102
+ } else if (a.startsWith("-")) {
103
+ exitErr(`unknown option: ${a}`);
104
+ } else {
105
+ positional.push(a);
106
+ }
107
+ }
108
+ return { positional, opts };
109
+ }
110
+
111
+ function normalizeUrl(raw) {
112
+ let candidate = raw.trim();
113
+ if (!/^https?:\/\//i.test(candidate)) candidate = `https://${candidate}`;
114
+ try {
115
+ const u = new URL(candidate);
116
+ if (!u.hostname || !u.hostname.includes(".")) throw new Error("missing hostname");
117
+ return u.toString();
118
+ } catch {
119
+ exitErr(`invalid URL: ${raw}`);
120
+ }
121
+ }
122
+
123
+ function isDirEmpty(dir) {
124
+ try {
125
+ return readdirSync(dir).length === 0;
126
+ } catch {
127
+ return true;
128
+ }
129
+ }
130
+
131
+ /** True if the folder is empty or only has git/bootstrap noise (so we can scaffold at project root). */
132
+ function isScaffoldRootUsable(dir) {
133
+ let names;
134
+ try {
135
+ names = readdirSync(dir);
136
+ } catch {
137
+ return true;
138
+ }
139
+ if (names.length === 0) return true;
140
+ const allowedOnly = new Set([".git", ".gitignore", ".gitattributes"]);
141
+ return names.every((n) => allowedOnly.has(n));
142
+ }
143
+
144
+ function npmPackageSlug(dirName, targetDir) {
145
+ const raw =
146
+ dirName === "." || dirName === "./"
147
+ ? basename(targetDir) || "launchframe-app"
148
+ : dirName;
149
+ const slug = raw
150
+ .toLowerCase()
151
+ .replace(/[^a-z0-9-_]/g, "-")
152
+ .replace(/-+/g, "-")
153
+ .replace(/^-|-$/g, "")
154
+ .slice(0, 64);
155
+ return slug || "launchframe-app";
156
+ }
157
+
158
+ function rewritePackageJson(targetDir, packageSlug, url, idea) {
159
+ const pkgPath = join(targetDir, "package.json");
160
+ if (!existsSync(pkgPath)) return;
161
+ let pkg;
162
+ try {
163
+ pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
164
+ } catch {
165
+ return;
166
+ }
167
+ pkg.name = packageSlug;
168
+ pkg.version = "0.1.0";
169
+ pkg.private = true;
170
+ pkg.description = `Launchframe project — clone of ${url} reframed as: ${idea}`;
171
+ delete pkg.author;
172
+ delete pkg.homepage;
173
+ delete pkg.repository;
174
+ delete pkg.bugs;
175
+ writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
176
+ }
177
+
178
+ function writeLaunchframeConfig(targetDir, url, idea) {
179
+ const cfg = {
180
+ $schema: "https://launchframe.dev/schema/launchframe.config.json",
181
+ url,
182
+ idea,
183
+ createdAt: new Date().toISOString(),
184
+ launchframeVersion: pkgJson.version,
185
+ notes: [
186
+ "The /clone-website skill reads this file at the start of every run.",
187
+ "After scaffold: open this folder in Cursor (or your AI editor) and say **Build it** — same workflow.",
188
+ "`url` is the visual source-of-truth (clone its layout, spacing, tokens, motion).",
189
+ "`idea` is the rebranding directive applied AFTER the pixel-perfect clone.",
190
+ "Edit either field and re-invoke the skill to re-run.",
191
+ ],
192
+ };
193
+ writeFileSync(
194
+ join(targetDir, "launchframe.config.json"),
195
+ JSON.stringify(cfg, null, 2) + "\n",
196
+ "utf8"
197
+ );
198
+ }
199
+
200
+ function runNpmInstall(targetDir) {
201
+ console.log(c("dim", "\nRunning npm install (this may take a minute)...\n"));
202
+ const result = spawnSync("npm", ["install", "--no-fund", "--no-audit"], {
203
+ cwd: targetDir,
204
+ stdio: "inherit",
205
+ shell: true,
206
+ env: process.env,
207
+ });
208
+ if (result.status !== 0) {
209
+ console.error(
210
+ c("yellow", "\nlaunchframe: npm install exited with an error. ") +
211
+ c("dim", `Fix the issue and run \`npm install\` inside the project folder, or re-run with \`--force\`.\n`)
212
+ );
213
+ process.exit(result.status === null ? 1 : result.status);
214
+ }
215
+ console.log(c("green", "\n\u2713 npm install finished.\n"));
216
+ }
217
+
218
+ function nextSteps(projectDir, dirLabel, openHint, url, idea) {
219
+ console.log(`
220
+ ${c("green", "\u2713")} Done — ${c("bold", dirLabel)} is ready.
221
+
222
+ ${c("dim", "Target URL:")} ${c("cyan", url)}
223
+ ${c("dim", "SaaS idea:")} ${c("cyan", idea)}
224
+ ${c("dim", "Folder:")} ${projectDir}
225
+
226
+ ${c("bold", "All you do next:")}
227
+ 1. Open ${c("cyan", openHint)} in ${c("bold", "Cursor")} ${c("dim", "(File \u2192 Open Folder)")}
228
+ 2. In chat, say: ${c("bold", "Build it")}
229
+ ${c("dim", "Your AI reads launchframe.config.json + AGENTS.md and runs the full clone + rebrand workflow.")}
230
+ ${c("dim", "You can also type ") + c("cyan", "/clone-website") + c("dim", " if you prefer.")}
231
+
232
+ ${c("dim", "Dotfolders (.cursor, .claude, …) are at this project root so rules and skills apply when you open this folder.")}
233
+ ${c("dim", "Other editors: same folder — say Build it, or run the /clone-website skill for your tool.")}
234
+ ${c("dim", "Edit launchframe.config.json anytime to change URL or SaaS idea.")}
235
+ `);
236
+ }
237
+
238
+ function main() {
239
+ const { positional, opts } = parseArgs(process.argv.slice(2));
240
+ if (opts.help) return printHelp();
241
+ if (opts.version) return console.log(pkgJson.version);
242
+
243
+ if (positional.length < 2) {
244
+ if (positional.length === 0) {
245
+ console.error(c("red", "\nlaunchframe: missing <url> and <saas idea>"));
246
+ } else {
247
+ console.error(c("red", '\nlaunchframe: missing <saas idea> (wrap it in quotes: "...")'));
248
+ }
249
+ printHelp();
250
+ process.exit(1);
251
+ }
252
+ if (positional.length > 2) {
253
+ exitErr(
254
+ `too many positional arguments. Wrap your SaaS idea in quotes: ` +
255
+ `\`npx launchframe ${positional[0]} "${positional.slice(1).join(" ")}"\``
256
+ );
257
+ }
258
+
259
+ const url = normalizeUrl(positional[0]);
260
+ const idea = positional[1].trim();
261
+ if (!idea) exitErr("SaaS idea cannot be empty");
262
+
263
+ const dirNameRaw = opts.dir;
264
+ const targetDir = isAbsolute(dirNameRaw) ? dirNameRaw : resolve(process.cwd(), dirNameRaw);
265
+
266
+ const isRootDot = dirNameRaw === "." || dirNameRaw === "./";
267
+ const usable = isDirEmpty(targetDir) || isScaffoldRootUsable(targetDir);
268
+ if (existsSync(targetDir) && !usable && !opts.force) {
269
+ exitErr(
270
+ `target directory \`${dirNameRaw}\` is not empty.\n` +
271
+ `Create an empty folder (or \`git init\` only), run again from there, or pass \`--force\` to merge files, ` +
272
+ `or use \`--dir launchframe-app\` to scaffold into a subdirectory.`
273
+ );
274
+ }
275
+
276
+ if (!existsSync(templateDir)) {
277
+ exitErr(
278
+ `bundled template not found at \`${templateDir}\`.\n` +
279
+ `This usually means the launchframe package was installed incompletely. ` +
280
+ `Try: \`npm install -g launchframe@latest\` or re-run \`npx launchframe@latest ...\`.`
281
+ );
282
+ }
283
+
284
+ mkdirSync(targetDir, { recursive: true });
285
+
286
+ console.log(c("dim", `\nCopying template \u2192 ${targetDir} ...`));
287
+ cpSync(templateDir, targetDir, {
288
+ recursive: true,
289
+ force: opts.force,
290
+ filter: (src) => {
291
+ const lower = src.toLowerCase();
292
+ if (lower.endsWith(`${"\\"}node_modules`) || lower.endsWith("/node_modules")) return false;
293
+ if (lower.endsWith(`${"\\"}.next`) || lower.endsWith("/.next")) return false;
294
+ if (lower.endsWith("package-lock.json")) return false;
295
+ return true;
296
+ },
297
+ });
298
+
299
+ const packageSlug = npmPackageSlug(dirNameRaw, targetDir);
300
+ rewritePackageJson(targetDir, packageSlug, url, idea);
301
+ writeLaunchframeConfig(targetDir, url, idea);
302
+
303
+ if (!opts.skipInstall) runNpmInstall(targetDir);
304
+
305
+ const dirLabel = isRootDot ? "this folder (project root)" : dirNameRaw;
306
+ const openHint = isRootDot ? "this folder" : `./${dirNameRaw}`;
307
+ nextSteps(targetDir, dirLabel, openHint, url, idea);
308
+ }
309
+
310
+ try {
311
+ main();
312
+ } catch (err) {
313
+ console.error(c("red", `\nlaunchframe: ${err?.message || err}\n`));
314
+ process.exit(1);
315
+ }
package/package.json CHANGED
@@ -1,54 +1,52 @@
1
- {
2
- "name": "launchframe",
3
- "version": "0.3.0",
4
- "private": false,
5
- "description": "clone any website into a clean, modern Next.js codebase using AI coding agents",
6
- "license": "MIT",
7
- "keywords": [
8
- "claude-code",
9
- "website-clone",
10
- "reverse-engineering",
11
- "nextjs",
12
- "ai",
13
- "template",
14
- "tailwindcss",
15
- "shadcn-ui"
16
- ],
17
- "engines": {
18
- "node": ">=24"
19
- },
20
- "scripts": {
21
- "dev": "next dev",
22
- "build": "next build",
23
- "start": "next start",
24
- "lint": "eslint",
25
- "typecheck": "tsc --noEmit",
26
- "check": "npm run lint && npm run typecheck && npm run build",
27
- "recon": "node scripts/recon-playwright.mjs",
28
- "recon:headed": "node scripts/recon-playwright.mjs --headed"
29
- },
30
- "dependencies": {
31
- "@base-ui/react": "^1.3.0",
32
- "class-variance-authority": "^0.7.1",
33
- "clsx": "^2.1.1",
34
- "framer-motion": "^12.4.0",
35
- "lucide-react": "^1.6.0",
36
- "next": "16.2.1",
37
- "react": "19.2.4",
38
- "react-dom": "19.2.4",
39
- "shadcn": "^4.1.0",
40
- "tailwind-merge": "^3.5.0",
41
- "tw-animate-css": "^1.4.0"
42
- },
43
- "devDependencies": {
44
- "@tailwindcss/postcss": "^4",
45
- "@types/node": "^24",
46
- "@types/react": "^19",
47
- "@types/react-dom": "^19",
48
- "eslint": "^9",
49
- "eslint-config-next": "16.2.1",
50
- "playwright": "^1.49.1",
51
- "tailwindcss": "^4",
52
- "typescript": "^5"
53
- }
54
- }
1
+ {
2
+ "name": "launchframe",
3
+ "version": "0.3.1",
4
+ "description": "Scaffold a SaaS-ready Next.js codebase from any URL. Copies the AI-cloner template wired with your URL and SaaS idea so your agent can run /clone-website.",
5
+ "license": "MIT",
6
+ "author": "JCodesMore",
7
+ "homepage": "https://github.com/JCodesMore/ai-website-cloner-template#readme",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/JCodesMore/ai-website-cloner-template.git"
11
+ },
12
+ "bugs": {
13
+ "url": "https://github.com/JCodesMore/ai-website-cloner-template/issues"
14
+ },
15
+ "keywords": [
16
+ "launchframe",
17
+ "scaffold",
18
+ "ai",
19
+ "ai-agents",
20
+ "website-clone",
21
+ "reverse-engineering",
22
+ "saas",
23
+ "boilerplate",
24
+ "template",
25
+ "nextjs",
26
+ "shadcn",
27
+ "shadcn-ui",
28
+ "tailwindcss",
29
+ "claude-code",
30
+ "cursor",
31
+ "codex",
32
+ "cli"
33
+ ],
34
+ "bin": {
35
+ "launchframe": "bin/launchframe.mjs"
36
+ },
37
+ "files": [
38
+ "bin",
39
+ "template",
40
+ "README.md",
41
+ "LICENSE"
42
+ ],
43
+ "scripts": {
44
+ "test:scaffold": "node bin/launchframe.mjs https://linear.app \"AI feedback platform\" --dir tmp-scaffold --force --skip-install",
45
+ "sync:skill": "node template/scripts/sync-skills.mjs",
46
+ "sync:agents": "bash template/scripts/sync-agent-rules.sh"
47
+ },
48
+ "engines": {
49
+ "node": ">=18.0.0"
50
+ },
51
+ "type": "module"
52
+ }
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 JCodesMore
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.