create-patties 0.0.6

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.
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env bun
2
+ import { run } from "../src/index.ts";
3
+
4
+ const code = await run(process.argv.slice(2));
5
+ process.exit(code);
package/package.json ADDED
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "create-patties",
3
+ "version": "0.0.6",
4
+ "type": "module",
5
+ "description": "Scaffolder for new Patties projects.",
6
+ "bin": {
7
+ "create-patties": "./bin/create-patties.ts"
8
+ },
9
+ "exports": {
10
+ ".": "./src/index.ts"
11
+ },
12
+ "files": [
13
+ "bin",
14
+ "src",
15
+ "templates",
16
+ "README.md"
17
+ ]
18
+ }
package/src/index.ts ADDED
@@ -0,0 +1,212 @@
1
+ import { existsSync, readdirSync } from "node:fs";
2
+ import { dirname, isAbsolute, resolve } from "node:path";
3
+
4
+ type AgentTemplate = "claude" | "codex" | "none";
5
+
6
+ interface Args {
7
+ name?: string;
8
+ template: AgentTemplate;
9
+ target: "bun" | "edge";
10
+ deploy: "cloudflare" | "vercel" | "deno" | "netlify" | "bun" | "none";
11
+ install: boolean;
12
+ git: boolean;
13
+ }
14
+
15
+ const TEMPLATES_ROOT = resolve(dirname(import.meta.dir), "templates");
16
+ const BASE_TEMPLATE = "default";
17
+ const VALID_TEMPLATES: AgentTemplate[] = ["claude", "codex", "none"];
18
+
19
+ export async function run(argv: string[]): Promise<number> {
20
+ const args = parseArgs(argv);
21
+
22
+ if (!args.name) {
23
+ printUsage();
24
+ return 2;
25
+ }
26
+
27
+ if (!isValidName(args.name)) {
28
+ stderr(`✗ invalid project name: "${args.name}"`);
29
+ return 2;
30
+ }
31
+
32
+ if (!VALID_TEMPLATES.includes(args.template)) {
33
+ stderr(
34
+ `✗ unknown --template "${args.template}" (expected: ${VALID_TEMPLATES.join(", ")})`,
35
+ );
36
+ return 2;
37
+ }
38
+
39
+ const targetDir = isAbsolute(args.name)
40
+ ? args.name
41
+ : resolve(process.cwd(), args.name);
42
+
43
+ if (existsSync(targetDir) && readdirSync(targetDir).length > 0) {
44
+ stderr(`✗ directory not empty: ${targetDir}`);
45
+ return 2;
46
+ }
47
+
48
+ const baseDir = resolve(TEMPLATES_ROOT, BASE_TEMPLATE);
49
+ if (!existsSync(baseDir)) {
50
+ stderr(`✗ base template missing: ${baseDir}`);
51
+ return 1;
52
+ }
53
+
54
+ await Bun.$`mkdir -p ${targetDir}`.quiet();
55
+ await Bun.$`cp -R ${baseDir}/. ${targetDir}`.quiet();
56
+
57
+ await renameTemplateFiles(targetDir);
58
+ await writePackageJson(targetDir, args.name);
59
+ await patchPattiesConfig(targetDir, args);
60
+
61
+ if (args.template !== "none") {
62
+ const overlay = resolve(TEMPLATES_ROOT, `_${args.template}`);
63
+ if (existsSync(overlay)) {
64
+ await Bun.$`cp -R ${overlay}/. ${targetDir}`.quiet();
65
+ }
66
+ }
67
+
68
+ if (args.install) {
69
+ await Bun.$`bun install`.cwd(targetDir).quiet().nothrow();
70
+ }
71
+
72
+ if (args.git) {
73
+ await Bun.$`git init`.cwd(targetDir).quiet().nothrow();
74
+ await Bun.$`git add -A`.cwd(targetDir).quiet().nothrow();
75
+ await Bun.$`git commit -m ${"chore: initial commit from create-patties"}`
76
+ .cwd(targetDir)
77
+ .quiet()
78
+ .nothrow();
79
+ }
80
+
81
+ process.stdout.write(
82
+ `\n✓ created ${args.name}\n\n cd ${args.name}\n bun dev\n`,
83
+ );
84
+ if (args.template === "claude") {
85
+ process.stdout.write(
86
+ "\nClaude Code is configured (CLAUDE.md). Run `claude` in the project to start a session.\n",
87
+ );
88
+ } else if (args.template === "codex") {
89
+ process.stdout.write(
90
+ "\nCodex is configured (AGENTS.md). Run `codex` in the project to start a session.\n",
91
+ );
92
+ }
93
+ return 0;
94
+ }
95
+
96
+ function parseArgs(argv: string[]): Args {
97
+ const out: Args = {
98
+ template: "claude",
99
+ target: "bun",
100
+ deploy: "none",
101
+ install: true,
102
+ git: true,
103
+ };
104
+ for (let i = 0; i < argv.length; i++) {
105
+ const a = argv[i];
106
+ if (a === undefined) continue;
107
+ if (a === "--template") out.template = next(argv, ++i) as AgentTemplate;
108
+ else if (a.startsWith("--template="))
109
+ out.template = a.slice(11) as AgentTemplate;
110
+ else if (a === "--target") out.target = next(argv, ++i) as Args["target"];
111
+ else if (a.startsWith("--target="))
112
+ out.target = a.slice(9) as Args["target"];
113
+ else if (a === "--deploy") out.deploy = next(argv, ++i) as Args["deploy"];
114
+ else if (a.startsWith("--deploy="))
115
+ out.deploy = a.slice(9) as Args["deploy"];
116
+ else if (a === "--no-install") out.install = false;
117
+ else if (a === "--no-git") out.git = false;
118
+ else if (!out.name && !a.startsWith("-")) out.name = a;
119
+ }
120
+ return out;
121
+ }
122
+
123
+ function next(argv: string[], i: number): string {
124
+ return argv[i] ?? "";
125
+ }
126
+
127
+ function isValidName(name: string): boolean {
128
+ if (!name) return false;
129
+ if (name.includes("/") || name.includes("\\")) return false;
130
+ if (name.startsWith(".")) return false;
131
+ return /^[a-z0-9][a-z0-9_-]*$/.test(name);
132
+ }
133
+
134
+ async function renameTemplateFiles(dir: string): Promise<void> {
135
+ const renames: Array<[string, string]> = [
136
+ ["gitignore", ".gitignore"],
137
+ ["README-template.md", "README.md"],
138
+ ];
139
+ for (const [from, to] of renames) {
140
+ const src = `${dir}/${from}`;
141
+ if (await Bun.file(src).exists()) {
142
+ await Bun.$`mv ${src} ${dir}/${to}`.quiet();
143
+ }
144
+ }
145
+ }
146
+
147
+ async function writePackageJson(dir: string, name: string): Promise<void> {
148
+ const pkg = {
149
+ name,
150
+ version: "0.1.0",
151
+ private: true,
152
+ type: "module",
153
+ scripts: {
154
+ dev: "patties dev",
155
+ build: "patties build",
156
+ start: "patties start",
157
+ },
158
+ dependencies: sorted({
159
+ patties: "latest",
160
+ react: "^19.0.0",
161
+ "react-dom": "^19.0.0",
162
+ }),
163
+ devDependencies: sorted({
164
+ "@types/react": "^19.0.0",
165
+ "@types/react-dom": "^19.0.0",
166
+ "bun-types": "latest",
167
+ typescript: "^5.5.0",
168
+ }),
169
+ };
170
+ await Bun.write(`${dir}/package.json`, `${JSON.stringify(pkg, null, 2)}\n`);
171
+ }
172
+
173
+ function sorted(deps: Record<string, string>): Record<string, string> {
174
+ const out: Record<string, string> = {};
175
+ for (const k of Object.keys(deps).sort()) out[k] = deps[k] as string;
176
+ return out;
177
+ }
178
+
179
+ async function patchPattiesConfig(dir: string, args: Args): Promise<void> {
180
+ const path = `${dir}/patties.config.ts`;
181
+ if (!(await Bun.file(path).exists())) return;
182
+ const current = await Bun.file(path).text();
183
+ const next = current.replace(
184
+ /target:\s*"(bun|edge)"/,
185
+ `target: "${args.target}"`,
186
+ );
187
+ if (next !== current) await Bun.write(path, next);
188
+ }
189
+
190
+ function stderr(msg: string): void {
191
+ process.stderr.write(`${msg}\n`);
192
+ }
193
+
194
+ function printUsage(): void {
195
+ process.stdout.write(`create-patties — scaffold a new Patties project
196
+
197
+ Usage:
198
+ bunx create-patties@latest <name> [options]
199
+
200
+ Options:
201
+ --template <claude|codex|none> Agent platform (default: claude)
202
+ --target <bun|edge> Runtime target (default: bun)
203
+ --deploy <cloudflare|vercel|deno|netlify|bun|none>
204
+ --no-install Skip 'bun install'
205
+ --no-git Skip 'git init'
206
+
207
+ Examples:
208
+ bunx create-patties@latest my-app
209
+ bunx create-patties@latest my-app --template codex
210
+ bunx create-patties@latest my-app --template none --no-git
211
+ `);
212
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "permissions": {
3
+ "allow": ["Bash(bun:*)", "Bash(bunx:*)", "Bash(git status:*)"]
4
+ }
5
+ }
@@ -0,0 +1,56 @@
1
+ # {{PROJECT_NAME}}
2
+
3
+ Built with Patties — Bun-native full-stack meta-framework.
4
+
5
+ ## Stack
6
+
7
+ Bun runtime · `Bun.serve({ routes })` dispatcher · React renderer (`react-dom/server` + `react-dom/client`) · `{{DEPLOY_TARGET}}` deploy.
8
+
9
+ ## File conventions
10
+
11
+ | Path | Becomes |
12
+ |---|---|
13
+ | `app/routes/index.tsx` | `/` |
14
+ | `app/routes/about.tsx` | `/about` |
15
+ | `app/routes/hotels/[city].tsx` | `/hotels/:city` |
16
+ | `app/routes/api/revenue.ts` | `/api/revenue` |
17
+ | `app/islands/counter.tsx` | interactive island `Counter` |
18
+ | `app/agents/booking.ts` | agent `booking` |
19
+ | `app/tools/search.ts` | MCP tool `search` |
20
+ | `app/middleware.ts` | global middleware |
21
+ | `patties.config.ts` | framework config |
22
+
23
+ Files starting with `_` are private and never routed. The URL prefix `/__patties_*` and output directory `/_patties/*` are reserved.
24
+
25
+ ## Critical rules
26
+
27
+ 1. Never import from `"next"`.
28
+ 2. Never use Node.js `http`, `fs.watch`, or `chokidar`. Use `Bun.serve` and `bun --watch`.
29
+ 3. Never use webpack or vite. Use `Bun.build`.
30
+ 4. Use React for rendering. Server: `react-dom/server.renderToReadableStream`. Client: `react-dom/client.hydrateRoot`. Never `renderToPipeableStream` or `renderToString`. Never `hono/jsx` or `hono/jsx/dom`.
31
+ 5. `tsconfig.json` sets `"jsx": "react-jsx"` and `"jsxImportSource": "react"` — no manual `import React` in user code.
32
+ 6. All routes are plain `(req: Request, ctx: PattiesContext) => Response | Promise<Response>`. Never import from `"hono"`.
33
+ 7. Islands live in `app/islands/` — no exceptions.
34
+ 8. API routes export named `GET`, `POST`, `PUT`, `DELETE` functions. Default exports are reserved for page components.
35
+ 9. Middleware default-exports a `Middleware = (req, ctx, next) => Promise<Response>`. Never `MiddlewareHandler` from Hono.
36
+ 10. Use Bun primitives for I/O: `Bun.file`, `Bun.write`, `Bun.spawn`, `Bun.CryptoHasher`, `Bun.env`, `Bun.password`, `bun:sqlite`, `Bun.sql`, `Bun.RedisClient`, `Bun.S3Client`. `node:fs` only when no Bun built-in exists.
37
+
38
+ ## How to run
39
+
40
+ - `bun install`
41
+ - `bun dev` — start the dev server
42
+ - `bun build` — build for production
43
+ - `bun test`
44
+
45
+ ## How to add a feature
46
+
47
+ Use the slash commands under `.claude/commands/`:
48
+
49
+ - `/new-route <path>` — page or API route
50
+ - `/new-island <Name>` — interactive island
51
+ - `/new-agent <name>` — agent
52
+ - `/new-tool <name>` — MCP tool
53
+
54
+ ## Where to find things
55
+
56
+ `AGENTS.md` (auto-regenerated by `patties build`) is the live inventory of routes, islands, agents, and tools. Trust `AGENTS.md` over this file for inventory questions; trust this file for rules and conventions.
@@ -0,0 +1,48 @@
1
+ # {{PROJECT_NAME}}
2
+
3
+ Built with Patties — Bun-native full-stack meta-framework.
4
+
5
+ > This file is read by Codex CLI and other AGENTS.md-aware tools. Patties also
6
+ > auto-regenerates a live inventory `AGENTS.md` on `patties build` — trust the
7
+ > generated inventory for routes/islands/agents/tools, and this file for rules
8
+ > and conventions.
9
+
10
+ ## Stack
11
+
12
+ Bun runtime · `Bun.serve({ routes })` dispatcher · React renderer (`react-dom/server` + `react-dom/client`) · `{{DEPLOY_TARGET}}` deploy.
13
+
14
+ ## File conventions
15
+
16
+ | Path | Becomes |
17
+ |---|---|
18
+ | `app/routes/index.tsx` | `/` |
19
+ | `app/routes/about.tsx` | `/about` |
20
+ | `app/routes/hotels/[city].tsx` | `/hotels/:city` |
21
+ | `app/routes/api/revenue.ts` | `/api/revenue` |
22
+ | `app/islands/counter.tsx` | interactive island `Counter` |
23
+ | `app/agents/booking.ts` | agent `booking` |
24
+ | `app/tools/search.ts` | MCP tool `search` |
25
+ | `app/middleware.ts` | global middleware |
26
+ | `patties.config.ts` | framework config |
27
+
28
+ Files starting with `_` are private and never routed. The URL prefix `/__patties_*` and output directory `/_patties/*` are reserved.
29
+
30
+ ## Critical rules
31
+
32
+ 1. Never import from `"next"`.
33
+ 2. Never use Node.js `http`, `fs.watch`, or `chokidar`. Use `Bun.serve` and `bun --watch`.
34
+ 3. Never use webpack or vite. Use `Bun.build`.
35
+ 4. Use React for rendering. Server: `react-dom/server.renderToReadableStream`. Client: `react-dom/client.hydrateRoot`. Never `renderToPipeableStream` or `renderToString`. Never `hono/jsx` or `hono/jsx/dom`.
36
+ 5. `tsconfig.json` sets `"jsx": "react-jsx"` and `"jsxImportSource": "react"` — no manual `import React` in user code.
37
+ 6. All routes are plain `(req: Request, ctx: PattiesContext) => Response | Promise<Response>`. Never import from `"hono"`.
38
+ 7. Islands live in `app/islands/` — no exceptions.
39
+ 8. API routes export named `GET`, `POST`, `PUT`, `DELETE` functions. Default exports are reserved for page components.
40
+ 9. Middleware default-exports a `Middleware = (req, ctx, next) => Promise<Response>`. Never `MiddlewareHandler` from Hono.
41
+ 10. Use Bun primitives for I/O: `Bun.file`, `Bun.write`, `Bun.spawn`, `Bun.CryptoHasher`, `Bun.env`, `Bun.password`, `bun:sqlite`, `Bun.sql`, `Bun.RedisClient`, `Bun.S3Client`. `node:fs` only when no Bun built-in exists.
42
+
43
+ ## How to run
44
+
45
+ - `bun install`
46
+ - `bun dev` — start the dev server
47
+ - `bun build` — build for production
48
+ - `bun test`
@@ -0,0 +1,16 @@
1
+ # Patties app
2
+
3
+ Bun-native full-stack app. Generated by `create-patties`.
4
+
5
+ ## Develop
6
+
7
+ ```
8
+ bun install
9
+ bun dev
10
+ ```
11
+
12
+ ## Build
13
+
14
+ ```
15
+ bun run build
16
+ ```
@@ -0,0 +1,12 @@
1
+ "use client";
2
+
3
+ import { useState } from "react";
4
+
5
+ export default function Counter(): JSX.Element {
6
+ const [n, setN] = useState(0);
7
+ return (
8
+ <button type="button" onClick={() => setN(n + 1)}>
9
+ clicked {n} times
10
+ </button>
11
+ );
12
+ }
@@ -0,0 +1,10 @@
1
+ import Counter from "../islands/Counter.tsx";
2
+
3
+ export default function Index(): JSX.Element {
4
+ return (
5
+ <main>
6
+ <h1>Hello from Patties</h1>
7
+ <Counter />
8
+ </main>
9
+ );
10
+ }
@@ -0,0 +1,7 @@
1
+ node_modules
2
+ .patties
3
+ dist
4
+ .env
5
+ .env.local
6
+ *.log
7
+ .DS_Store
@@ -0,0 +1,5 @@
1
+ import { defineConfig } from "patties/config";
2
+
3
+ export default defineConfig({
4
+ target: "bun",
5
+ });
@@ -0,0 +1,12 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "Bundler",
6
+ "jsx": "react-jsx",
7
+ "strict": true,
8
+ "skipLibCheck": true,
9
+ "types": ["bun-types", "react", "react-dom"]
10
+ },
11
+ "include": ["app", "patties.config.ts"]
12
+ }