create-nyoworks 2.3.0 → 2.4.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.
@@ -0,0 +1,2 @@
1
+ export declare function checkDependencies(): Promise<void>;
2
+ export declare function showClaudeMaxWarning(): void;
package/dist/checks.js ADDED
@@ -0,0 +1,114 @@
1
+ import pc from "picocolors";
2
+ import { execa } from "execa";
3
+ const DEPENDENCIES = [
4
+ {
5
+ name: "Node.js",
6
+ cmd: "node",
7
+ args: ["-v"],
8
+ required: true,
9
+ minVersion: "20.0.0",
10
+ url: "https://nodejs.org",
11
+ },
12
+ {
13
+ name: "pnpm",
14
+ cmd: "pnpm",
15
+ args: ["-v"],
16
+ required: true,
17
+ install: "npm i -g pnpm",
18
+ },
19
+ {
20
+ name: "Docker",
21
+ cmd: "docker",
22
+ args: ["-v"],
23
+ required: true,
24
+ url: "https://docker.com/download",
25
+ },
26
+ {
27
+ name: "Claude CLI",
28
+ cmd: "claude",
29
+ args: ["-v"],
30
+ required: false,
31
+ install: "npm i -g @anthropic-ai/claude-code",
32
+ },
33
+ {
34
+ name: "VS Code",
35
+ cmd: "code",
36
+ args: ["-v"],
37
+ required: false,
38
+ url: "https://code.visualstudio.com",
39
+ },
40
+ ];
41
+ async function checkCommand(cmd, args = []) {
42
+ try {
43
+ const result = await execa(cmd, args);
44
+ return result.stdout.trim().replace(/^v/, "");
45
+ }
46
+ catch {
47
+ return null;
48
+ }
49
+ }
50
+ function compareVersions(current, minimum) {
51
+ const curr = current.split(".").map(Number);
52
+ const min = minimum.split(".").map(Number);
53
+ for (let i = 0; i < 3; i++) {
54
+ if ((curr[i] || 0) > (min[i] || 0))
55
+ return true;
56
+ if ((curr[i] || 0) < (min[i] || 0))
57
+ return false;
58
+ }
59
+ return true;
60
+ }
61
+ export async function checkDependencies() {
62
+ console.log();
63
+ console.log(pc.cyan("Checking dependencies..."));
64
+ let hasError = false;
65
+ for (const dep of DEPENDENCIES) {
66
+ const version = await checkCommand(dep.cmd, dep.args);
67
+ if (!version) {
68
+ if (dep.required) {
69
+ console.log(pc.red(` ✗ ${dep.name} is required`));
70
+ hasError = true;
71
+ }
72
+ else {
73
+ console.log(pc.yellow(` ⚠ ${dep.name} (recommended)`));
74
+ }
75
+ if (dep.install) {
76
+ console.log(pc.dim(` Install: ${dep.install}`));
77
+ }
78
+ if (dep.url) {
79
+ console.log(pc.dim(` Download: ${dep.url}`));
80
+ }
81
+ }
82
+ else {
83
+ if (dep.minVersion && !compareVersions(version, dep.minVersion)) {
84
+ console.log(pc.yellow(` ⚠ ${dep.name} ${version} (minimum: ${dep.minVersion})`));
85
+ }
86
+ else {
87
+ console.log(pc.green(` ✓ ${dep.name} ${version}`));
88
+ }
89
+ }
90
+ }
91
+ if (hasError) {
92
+ console.log();
93
+ console.log(pc.red("Please install the required dependencies before continuing."));
94
+ }
95
+ }
96
+ export function showClaudeMaxWarning() {
97
+ console.log();
98
+ console.log(pc.yellow("╔═══════════════════════════════════════════════════════════════════════╗"));
99
+ console.log(pc.yellow("║") + pc.bold(" ⚠️ MCP Server requires Claude Max subscription ") + pc.yellow("║"));
100
+ console.log(pc.yellow("╠═══════════════════════════════════════════════════════════════════════╣"));
101
+ console.log(pc.yellow("║") + " " + pc.yellow("║"));
102
+ console.log(pc.yellow("║") + " The AI agents in this framework use Model Context Protocol (MCP). " + pc.yellow("║"));
103
+ console.log(pc.yellow("║") + " MCP requires Claude Max subscription. " + pc.yellow("║"));
104
+ console.log(pc.yellow("║") + " " + pc.yellow("║"));
105
+ console.log(pc.yellow("║") + pc.bold(" Pricing: $100/month") + " (5x usage vs Pro) " + pc.yellow("║"));
106
+ console.log(pc.yellow("║") + " Subscribe: " + pc.cyan("https://claude.ai/settings/billing") + " " + pc.yellow("║"));
107
+ console.log(pc.yellow("║") + " " + pc.yellow("║"));
108
+ console.log(pc.yellow("║") + " Without Claude Max: " + pc.yellow("║"));
109
+ console.log(pc.yellow("║") + pc.green(" ✓ You can still use the codebase manually") + " " + pc.yellow("║"));
110
+ console.log(pc.yellow("║") + pc.red(" ✗ AI agents (/lead, /backend, /frontend, etc.) won't work") + " " + pc.yellow("║"));
111
+ console.log(pc.yellow("║") + pc.red(" ✗ MCP tools will not be available") + " " + pc.yellow("║"));
112
+ console.log(pc.yellow("║") + " " + pc.yellow("║"));
113
+ console.log(pc.yellow("╚═══════════════════════════════════════════════════════════════════════╝"));
114
+ }
package/dist/init.js CHANGED
@@ -2,8 +2,10 @@ import prompts from "prompts";
2
2
  import pc from "picocolors";
3
3
  import fs from "fs-extra";
4
4
  import path from "path";
5
+ import os from "os";
5
6
  import { execa } from "execa";
6
7
  import { replacePlaceholders } from "./replace.js";
8
+ import { checkDependencies, showClaudeMaxWarning } from "./checks.js";
7
9
  const REPO = "naimozcan/nyoworks-framework";
8
10
  const BRANCH = "main";
9
11
  const AVAILABLE_FEATURES = [
@@ -39,22 +41,14 @@ function generateDatabaseName(name) {
39
41
  .replace(/[^a-z0-9]+/g, "_")
40
42
  .replace(/^_|_$/g, "") + "_dev";
41
43
  }
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");
44
+ async function downloadRepo(repo, branch) {
45
+ const url = `https://github.com/${repo}/archive/refs/heads/${branch}.tar.gz`;
46
+ const tempDir = path.join(os.tmpdir(), `nyoworks-${Date.now()}`);
47
+ const tarFile = path.join(tempDir, "repo.tar.gz");
45
48
  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
- }
49
+ await execa("curl", ["-L", "-o", tarFile, url]);
50
+ await execa("tar", ["-xzf", tarFile, "-C", tempDir]);
51
+ return path.join(tempDir, `nyoworks-framework-${branch}`);
58
52
  }
59
53
  export async function createProject(projectName) {
60
54
  console.log();
@@ -102,22 +96,47 @@ export async function createProject(projectName) {
102
96
  console.log(pc.red(`Directory ${slug} already exists.`));
103
97
  process.exit(1);
104
98
  }
105
- await fs.ensureDir(targetDir);
106
99
  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" },
100
+ process.stdout.write(pc.cyan("Downloading from GitHub..."));
101
+ let repoDir;
102
+ try {
103
+ repoDir = await downloadRepo(REPO, BRANCH);
104
+ console.log(pc.green(" done"));
105
+ }
106
+ catch (error) {
107
+ console.log(pc.red(" failed"));
108
+ console.error(pc.red("Failed to download from GitHub. Check your internet connection."));
109
+ process.exit(1);
110
+ }
111
+ process.stdout.write(pc.dim(" Copying files..."));
112
+ await fs.ensureDir(targetDir);
113
+ const corePaths = [
114
+ "packages/api",
115
+ "packages/api-client",
116
+ "packages/database",
117
+ "packages/validators",
118
+ "packages/shared",
119
+ "packages/ui",
120
+ "packages/assets",
121
+ "apps/server",
122
+ "docs",
123
+ "mcp-server",
124
+ ".claude",
120
125
  ];
126
+ for (const p of corePaths) {
127
+ const src = path.join(repoDir, p);
128
+ const dest = path.join(targetDir, p);
129
+ if (await fs.pathExists(src)) {
130
+ await fs.copy(src, dest);
131
+ }
132
+ }
133
+ for (const platform of platforms) {
134
+ const src = path.join(repoDir, `apps/${platform}`);
135
+ const dest = path.join(targetDir, `apps/${platform}`);
136
+ if (await fs.pathExists(src)) {
137
+ await fs.copy(src, dest);
138
+ }
139
+ }
121
140
  const rootFiles = [
122
141
  "package.json",
123
142
  "pnpm-workspace.yaml",
@@ -127,32 +146,15 @@ export async function createProject(projectName) {
127
146
  ".gitignore",
128
147
  "nyoworks.config.yaml",
129
148
  ];
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
+ for (const file of rootFiles) {
150
+ const src = path.join(repoDir, file);
151
+ const dest = path.join(targetDir, file);
152
+ if (await fs.pathExists(src)) {
153
+ await fs.copy(src, dest);
149
154
  }
150
155
  }
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
+ await fs.remove(path.dirname(repoDir));
157
+ console.log(pc.green(" done"));
156
158
  const placeholders = {
157
159
  "${PROJECT_NAME}": name,
158
160
  "${PROJECT_CODE}": code,
@@ -161,7 +163,7 @@ export async function createProject(projectName) {
161
163
  };
162
164
  process.stdout.write(pc.dim(" Replacing placeholders..."));
163
165
  await replacePlaceholders(targetDir, placeholders);
164
- console.log(pc.green(" "));
166
+ console.log(pc.green(" done"));
165
167
  for (const feature of features) {
166
168
  const featureDoc = path.join(targetDir, "docs", "bible", "features", `${feature}.md`);
167
169
  const content = `# Feature: ${feature.charAt(0).toUpperCase() + feature.slice(1)}
@@ -211,13 +213,20 @@ See \`docs/bible/data/schema.md\`
211
213
  }
212
214
  console.log();
213
215
  console.log(pc.green(pc.bold("Project created successfully!")));
216
+ await checkDependencies();
217
+ showClaudeMaxWarning();
214
218
  console.log();
215
- console.log(" Next steps:");
219
+ console.log(pc.bold(" Next steps:"));
216
220
  console.log();
217
221
  console.log(pc.cyan(` cd ${slug}`));
218
222
  console.log(pc.cyan(" pnpm install"));
223
+ console.log(pc.cyan(" docker compose up -d ") + pc.dim("# Start PostgreSQL & Redis"));
219
224
  console.log(pc.cyan(" pnpm dev"));
220
225
  console.log();
226
+ console.log(pc.dim(" Optional:"));
227
+ console.log(pc.dim(" code . # Open in VS Code"));
228
+ console.log(pc.dim(" claude # Start Claude Code CLI"));
229
+ console.log();
221
230
  console.log(pc.dim(" Configuration:"));
222
231
  console.log(pc.dim(` Name: ${name}`));
223
232
  console.log(pc.dim(` Code: ${code}`));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-nyoworks",
3
- "version": "2.3.0",
3
+ "version": "2.4.0",
4
4
  "description": "Create a new NYOWORKS project",
5
5
  "type": "module",
6
6
  "bin": {