create-nyoworks 2.3.0

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,72 @@
1
+ # create-nyoworks
2
+
3
+ Create a new NYOWORKS project with one command.
4
+
5
+ ## Usage
6
+
7
+ ```bash
8
+ npx create-nyoworks my-project
9
+ ```
10
+
11
+ ## Interactive Setup
12
+
13
+ The CLI will ask you:
14
+
15
+ 1. **Project name** - Your project's display name
16
+ 2. **Platforms** - Web, Mobile, Desktop (multi-select)
17
+ 3. **Features** - Optional features to enable
18
+
19
+ ## What Gets Created
20
+
21
+ ```
22
+ my-project/
23
+ ├── apps/
24
+ │ ├── server/ # Hono + tRPC API
25
+ │ ├── web/ # Next.js 16 (if selected)
26
+ │ ├── mobile/ # Expo SDK 54 (if selected)
27
+ │ └── desktop/ # Tauri 2.0 (if selected)
28
+ ├── packages/
29
+ │ ├── api/ # tRPC routers
30
+ │ ├── api-client/ # React hooks
31
+ │ ├── database/ # Drizzle ORM
32
+ │ ├── validators/ # Zod schemas
33
+ │ ├── shared/ # Utilities
34
+ │ ├── ui/ # shadcn/ui
35
+ │ └── assets/ # Images, icons
36
+ ├── docs/
37
+ │ └── bible/ # Project documentation
38
+ ├── mcp-server/ # AI agent orchestration
39
+ └── nyoworks.config.yaml # Project config
40
+ ```
41
+
42
+ ## Automatic Placeholder Replacement
43
+
44
+ These placeholders are replaced during project creation:
45
+
46
+ | Placeholder | Example Value |
47
+ |-------------|---------------|
48
+ | `${PROJECT_NAME}` | My Project |
49
+ | `${PROJECT_CODE}` | MYPR |
50
+ | `${PROJECT_SLUG}` | my-project |
51
+ | `${DATABASE_NAME}` | my_project_dev |
52
+
53
+ ## Core vs Optional
54
+
55
+ **Always Included (Core):**
56
+ - Authentication (JWT + Argon2id)
57
+ - Database (PostgreSQL + Drizzle)
58
+ - API (Hono + tRPC)
59
+
60
+ **Optional Features:**
61
+ - Analytics, CRM, Payments, Notifications
62
+ - Appointments, Audit, Export, Realtime
63
+
64
+ ## Requirements
65
+
66
+ - Node.js 20+
67
+ - pnpm 9+
68
+ - Docker (for PostgreSQL)
69
+
70
+ ## License
71
+
72
+ MIT
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+ import { createProject } from "./init.js";
3
+ const args = process.argv.slice(2);
4
+ const projectName = args[0];
5
+ createProject(projectName);
package/dist/init.d.ts ADDED
@@ -0,0 +1 @@
1
+ export declare function createProject(projectName?: string): Promise<void>;
package/dist/init.js ADDED
@@ -0,0 +1,227 @@
1
+ import prompts from "prompts";
2
+ import pc from "picocolors";
3
+ import fs from "fs-extra";
4
+ import path from "path";
5
+ import { execa } from "execa";
6
+ import { replacePlaceholders } from "./replace.js";
7
+ const REPO = "naimozcan/nyoworks-framework";
8
+ const BRANCH = "main";
9
+ const AVAILABLE_FEATURES = [
10
+ { title: "Analytics", value: "analytics", description: "User behavior tracking" },
11
+ { title: "CRM", value: "crm", description: "Customer relationship management" },
12
+ { title: "Payments", value: "payments", description: "Stripe integration" },
13
+ { title: "Notifications", value: "notifications", description: "Email, SMS, Push" },
14
+ { title: "Appointments", value: "appointments", description: "Booking system" },
15
+ { title: "Audit", value: "audit", description: "Activity logging" },
16
+ { title: "Export", value: "export", description: "PDF/CSV export" },
17
+ { title: "Realtime", value: "realtime", description: "WebSocket support" },
18
+ ];
19
+ const AVAILABLE_PLATFORMS = [
20
+ { title: "Web", value: "web", description: "Next.js 16" },
21
+ { title: "Mobile", value: "mobile", description: "Expo SDK 54" },
22
+ { title: "Desktop", value: "desktop", description: "Tauri 2.0" },
23
+ ];
24
+ function generateCode(name) {
25
+ return name
26
+ .toUpperCase()
27
+ .replace(/[^A-Z0-9]/g, "")
28
+ .slice(0, 4);
29
+ }
30
+ function generateSlug(name) {
31
+ return name
32
+ .toLowerCase()
33
+ .replace(/[^a-z0-9]+/g, "-")
34
+ .replace(/^-|-$/g, "");
35
+ }
36
+ function generateDatabaseName(name) {
37
+ return name
38
+ .toLowerCase()
39
+ .replace(/[^a-z0-9]+/g, "_")
40
+ .replace(/^_|_$/g, "") + "_dev";
41
+ }
42
+ async function downloadFromGitHub(repo, subPath, targetDir, branch = "main") {
43
+ const url = `https://codeload.github.com/${repo}/tar.gz/${branch}`;
44
+ const tempDir = path.join(targetDir, ".download-temp");
45
+ await fs.ensureDir(tempDir);
46
+ try {
47
+ await execa("curl", ["-sL", url, "-o", `${tempDir}/repo.tar.gz`]);
48
+ await execa("tar", ["-xzf", `${tempDir}/repo.tar.gz`, "-C", tempDir]);
49
+ const extractedDir = path.join(tempDir, `nyoworks-framework-${branch}`);
50
+ const sourcePath = subPath ? path.join(extractedDir, subPath) : extractedDir;
51
+ if (await fs.pathExists(sourcePath)) {
52
+ await fs.copy(sourcePath, targetDir, { overwrite: true });
53
+ }
54
+ }
55
+ finally {
56
+ await fs.remove(tempDir);
57
+ }
58
+ }
59
+ export async function createProject(projectName) {
60
+ console.log();
61
+ console.log(pc.cyan(pc.bold(" NYOWORKS Framework")));
62
+ console.log(pc.dim(" Create a new project"));
63
+ console.log();
64
+ const response = await prompts([
65
+ {
66
+ type: projectName ? null : "text",
67
+ name: "name",
68
+ message: "Project name:",
69
+ initial: projectName || "my-project",
70
+ validate: (value) => (value.length > 0 ? true : "Project name is required"),
71
+ },
72
+ {
73
+ type: "multiselect",
74
+ name: "platforms",
75
+ message: "Select platforms:",
76
+ choices: AVAILABLE_PLATFORMS,
77
+ min: 1,
78
+ hint: "- Space to select. Return to submit",
79
+ instructions: false,
80
+ },
81
+ {
82
+ type: "multiselect",
83
+ name: "features",
84
+ message: "Select features:",
85
+ choices: AVAILABLE_FEATURES,
86
+ hint: "- Space to select. Return to submit",
87
+ instructions: false,
88
+ },
89
+ ]);
90
+ if (!response.name && !projectName) {
91
+ console.log(pc.red("Aborted."));
92
+ process.exit(1);
93
+ }
94
+ const name = response.name || projectName;
95
+ const code = generateCode(name);
96
+ const slug = generateSlug(name);
97
+ const databaseName = generateDatabaseName(name);
98
+ const platforms = response.platforms || ["web"];
99
+ const features = response.features || [];
100
+ const targetDir = path.resolve(process.cwd(), slug);
101
+ if (fs.existsSync(targetDir)) {
102
+ console.log(pc.red(`Directory ${slug} already exists.`));
103
+ process.exit(1);
104
+ }
105
+ await fs.ensureDir(targetDir);
106
+ console.log();
107
+ console.log(pc.cyan("Downloading from GitHub..."));
108
+ const coreItems = [
109
+ { path: "packages/api", desc: "tRPC routers" },
110
+ { path: "packages/api-client", desc: "API client" },
111
+ { path: "packages/database", desc: "Database" },
112
+ { path: "packages/validators", desc: "Validators" },
113
+ { path: "packages/shared", desc: "Shared utils" },
114
+ { path: "packages/ui", desc: "UI components" },
115
+ { path: "packages/assets", desc: "Assets" },
116
+ { path: "apps/server", desc: "API server" },
117
+ { path: "docs", desc: "Documentation" },
118
+ { path: "mcp-server", desc: "MCP server" },
119
+ { path: ".claude", desc: "Agent commands" },
120
+ ];
121
+ const rootFiles = [
122
+ "package.json",
123
+ "pnpm-workspace.yaml",
124
+ "turbo.json",
125
+ "tsconfig.json",
126
+ ".env.example",
127
+ ".gitignore",
128
+ "nyoworks.config.yaml",
129
+ ];
130
+ for (const item of coreItems) {
131
+ process.stdout.write(pc.dim(` Downloading ${item.desc}...`));
132
+ await downloadFromGitHub(REPO, item.path, path.join(targetDir, item.path), BRANCH);
133
+ console.log(pc.green(" ✓"));
134
+ }
135
+ for (const platform of platforms) {
136
+ process.stdout.write(pc.dim(` Downloading apps/${platform}...`));
137
+ await downloadFromGitHub(REPO, `apps/${platform}`, path.join(targetDir, `apps/${platform}`), BRANCH);
138
+ console.log(pc.green(" ✓"));
139
+ }
140
+ process.stdout.write(pc.dim(" Downloading root files..."));
141
+ await downloadFromGitHub(REPO, "", targetDir, BRANCH);
142
+ const allPlatforms = ["web", "mobile", "desktop"];
143
+ for (const platform of allPlatforms) {
144
+ if (!platforms.includes(platform)) {
145
+ const platformDir = path.join(targetDir, "apps", platform);
146
+ if (await fs.pathExists(platformDir)) {
147
+ await fs.remove(platformDir);
148
+ }
149
+ }
150
+ }
151
+ const createNyoworksDir = path.join(targetDir, "packages", "create-nyoworks");
152
+ if (await fs.pathExists(createNyoworksDir)) {
153
+ await fs.remove(createNyoworksDir);
154
+ }
155
+ console.log(pc.green(" ✓"));
156
+ const placeholders = {
157
+ "${PROJECT_NAME}": name,
158
+ "${PROJECT_CODE}": code,
159
+ "${PROJECT_SLUG}": slug,
160
+ "${DATABASE_NAME}": databaseName,
161
+ };
162
+ process.stdout.write(pc.dim(" Replacing placeholders..."));
163
+ await replacePlaceholders(targetDir, placeholders);
164
+ console.log(pc.green(" ✓"));
165
+ for (const feature of features) {
166
+ const featureDoc = path.join(targetDir, "docs", "bible", "features", `${feature}.md`);
167
+ const content = `# Feature: ${feature.charAt(0).toUpperCase() + feature.slice(1)}
168
+
169
+ ## Overview
170
+
171
+ > Describe what this feature does
172
+
173
+ ## Requirements
174
+
175
+ - [ ] Requirement 1
176
+ - [ ] Requirement 2
177
+
178
+ ## API Endpoints
179
+
180
+ | Method | Path | Description |
181
+ |--------|------|-------------|
182
+ | GET | /api/${feature} | List items |
183
+ | POST | /api/${feature} | Create item |
184
+
185
+ ## Data Model
186
+
187
+ See \`docs/bible/data/schema.md\`
188
+
189
+ ## UI Components
190
+
191
+ - [ ] Component 1
192
+ - [ ] Component 2
193
+
194
+ ## Decisions
195
+
196
+ | ID | Decision | Rationale |
197
+ |----|----------|-----------|
198
+ | T-xxx | [Decision] | [Why] |
199
+ `;
200
+ await fs.outputFile(featureDoc, content);
201
+ console.log(pc.dim(` Created docs/bible/features/${feature}.md`));
202
+ }
203
+ const configPath = path.join(targetDir, "nyoworks.config.yaml");
204
+ if (await fs.pathExists(configPath)) {
205
+ let config = await fs.readFile(configPath, "utf8");
206
+ if (features.length > 0) {
207
+ config = config.replace(/enabled: \[\]/, `enabled:\n${features.map((f) => ` - ${f}`).join("\n")}`);
208
+ }
209
+ config = config.replace(/targets:\n - web/, `targets:\n${platforms.map((p) => ` - ${p}`).join("\n")}`);
210
+ await fs.writeFile(configPath, config);
211
+ }
212
+ console.log();
213
+ console.log(pc.green(pc.bold("Project created successfully!")));
214
+ console.log();
215
+ console.log(" Next steps:");
216
+ console.log();
217
+ console.log(pc.cyan(` cd ${slug}`));
218
+ console.log(pc.cyan(" pnpm install"));
219
+ console.log(pc.cyan(" pnpm dev"));
220
+ console.log();
221
+ console.log(pc.dim(" Configuration:"));
222
+ console.log(pc.dim(` Name: ${name}`));
223
+ console.log(pc.dim(` Code: ${code}`));
224
+ console.log(pc.dim(` Platforms: ${platforms.join(", ")}`));
225
+ console.log(pc.dim(` Features: ${features.join(", ") || "none"}`));
226
+ console.log();
227
+ }
@@ -0,0 +1 @@
1
+ export declare function replacePlaceholders(dir: string, placeholders: Record<string, string>): Promise<void>;
@@ -0,0 +1,31 @@
1
+ import fs from "fs-extra";
2
+ import path from "path";
3
+ const EXTENSIONS = [".md", ".json", ".yaml", ".yml", ".ts", ".tsx", ".js", ".jsx", ".env", ".example"];
4
+ const IGNORE_DIRS = ["node_modules", ".git", "dist", ".next", ".turbo"];
5
+ export async function replacePlaceholders(dir, placeholders) {
6
+ const entries = await fs.readdir(dir, { withFileTypes: true });
7
+ for (const entry of entries) {
8
+ const fullPath = path.join(dir, entry.name);
9
+ if (entry.isDirectory()) {
10
+ if (!IGNORE_DIRS.includes(entry.name)) {
11
+ await replacePlaceholders(fullPath, placeholders);
12
+ }
13
+ }
14
+ else if (entry.isFile()) {
15
+ const ext = path.extname(entry.name);
16
+ if (EXTENSIONS.includes(ext) || entry.name.startsWith(".env")) {
17
+ let content = await fs.readFile(fullPath, "utf8");
18
+ let modified = false;
19
+ for (const [placeholder, value] of Object.entries(placeholders)) {
20
+ if (content.includes(placeholder)) {
21
+ content = content.split(placeholder).join(value);
22
+ modified = true;
23
+ }
24
+ }
25
+ if (modified) {
26
+ await fs.writeFile(fullPath, content);
27
+ }
28
+ }
29
+ }
30
+ }
31
+ }
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "create-nyoworks",
3
+ "version": "2.3.0",
4
+ "description": "Create a new NYOWORKS project",
5
+ "type": "module",
6
+ "bin": {
7
+ "create-nyoworks": "./dist/index.js"
8
+ },
9
+ "files": [
10
+ "dist"
11
+ ],
12
+ "scripts": {
13
+ "build": "tsc",
14
+ "dev": "tsc --watch",
15
+ "prepublishOnly": "npm run build"
16
+ },
17
+ "keywords": [
18
+ "create",
19
+ "nyoworks",
20
+ "boilerplate",
21
+ "monorepo",
22
+ "typescript",
23
+ "nextjs",
24
+ "hono",
25
+ "trpc"
26
+ ],
27
+ "author": "NYOWORKS",
28
+ "license": "MIT",
29
+ "dependencies": {
30
+ "prompts": "^2.4.2",
31
+ "picocolors": "^1.1.1",
32
+ "fs-extra": "^11.2.0",
33
+ "execa": "^9.5.2"
34
+ },
35
+ "devDependencies": {
36
+ "@types/fs-extra": "^11.0.4",
37
+ "@types/node": "^22.0.0",
38
+ "@types/prompts": "^2.4.9",
39
+ "typescript": "^5.7.0"
40
+ },
41
+ "engines": {
42
+ "node": ">=20"
43
+ }
44
+ }