create-nene 0.1.2 → 0.2.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.
Files changed (52) hide show
  1. package/dist/index.js +87 -52
  2. package/dist/index.js.map +1 -1
  3. package/package.json +1 -1
  4. package/templates/default/.cursor/rules/backend-patterns.mdc +68 -0
  5. package/templates/default/.cursor/rules/frontend-patterns.mdc +53 -0
  6. package/templates/default/.cursor/rules/nene-architecture.mdc +65 -0
  7. package/templates/default/.cursor/rules/shared-package.mdc +68 -0
  8. package/templates/default/.cursor/rules/task-management.mdc +60 -0
  9. package/templates/default/README.md +42 -25
  10. package/templates/default/_gitignore +10 -17
  11. package/templates/default/apps/api/nest-cli.json +8 -0
  12. package/templates/default/apps/api/package.json +36 -0
  13. package/templates/default/apps/api/src/app.controller.ts +12 -0
  14. package/templates/default/apps/api/src/app.module.ts +19 -0
  15. package/templates/default/apps/api/src/app.service.ts +8 -0
  16. package/templates/default/apps/api/src/config/configuration.ts +6 -0
  17. package/templates/default/apps/api/src/health/health.controller.ts +12 -0
  18. package/templates/default/apps/api/src/health/health.module.ts +9 -0
  19. package/templates/default/apps/api/src/health/health.service.ts +12 -0
  20. package/templates/default/apps/api/src/main.ts +32 -0
  21. package/templates/default/apps/api/tsconfig.json +26 -0
  22. package/templates/default/apps/web/next.config.ts +7 -0
  23. package/templates/default/apps/web/package.json +28 -0
  24. package/templates/default/apps/web/postcss.config.mjs +9 -0
  25. package/templates/default/apps/web/src/app/globals.css +20 -0
  26. package/templates/default/apps/web/src/app/layout.tsx +19 -0
  27. package/templates/default/apps/web/src/app/page.tsx +75 -0
  28. package/templates/default/apps/web/tailwind.config.ts +15 -0
  29. package/templates/default/{tsconfig.json → apps/web/tsconfig.json} +11 -8
  30. package/templates/default/docs/API.md +55 -0
  31. package/templates/default/docs/ARCHITECTURE.md +56 -0
  32. package/templates/default/docs/PROGRESS.md +40 -0
  33. package/templates/default/package.json +12 -17
  34. package/templates/default/packages/shared/package.json +29 -0
  35. package/templates/default/packages/shared/src/constants/index.ts +13 -0
  36. package/templates/default/packages/shared/src/index.ts +3 -0
  37. package/templates/default/packages/shared/src/types/index.ts +21 -0
  38. package/templates/default/packages/shared/tsconfig.json +17 -0
  39. package/templates/default/packages/shared/tsup.config.ts +10 -0
  40. package/templates/default/pnpm-workspace.yaml +3 -0
  41. package/templates/default/turbo.json +14 -0
  42. package/templates/default/eslint.config.mjs +0 -10
  43. package/templates/default/src/app/layout.tsx +0 -14
  44. package/templates/default/src/app/page.tsx +0 -29
  45. package/templates/default/src/server/api/hello.ts +0 -14
  46. package/templates/default/src/server/index.ts +0 -3
  47. package/templates/minimal/README.md +0 -11
  48. package/templates/minimal/_gitignore +0 -6
  49. package/templates/minimal/package.json +0 -22
  50. package/templates/minimal/src/app/layout.tsx +0 -9
  51. package/templates/minimal/src/app/page.tsx +0 -7
  52. package/templates/minimal/tsconfig.json +0 -19
package/dist/index.js CHANGED
@@ -12,14 +12,6 @@ function validateProjectName(name) {
12
12
  const validNameRegex = /^(?:@[a-z0-9-*~][a-z0-9-*._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/;
13
13
  return validNameRegex.test(name);
14
14
  }
15
- function detectPackageManager() {
16
- const userAgent = process.env.npm_config_user_agent;
17
- if (userAgent) {
18
- if (userAgent.startsWith("yarn")) return "yarn";
19
- if (userAgent.startsWith("pnpm")) return "pnpm";
20
- }
21
- return "npm";
22
- }
23
15
  function copyDir(src, dest) {
24
16
  fs.mkdirSync(dest, { recursive: true });
25
17
  const entries = fs.readdirSync(src, { withFileTypes: true });
@@ -33,12 +25,12 @@ function copyDir(src, dest) {
33
25
  }
34
26
  }
35
27
  }
36
- function getTemplateDir(template) {
37
- const devPath = path.resolve(__dirname, "..", "templates", template);
38
- const prodPath = path.resolve(__dirname, "..", "..", "templates", template);
28
+ function getTemplateDir() {
29
+ const devPath = path.resolve(__dirname, "..", "templates", "default");
30
+ const prodPath = path.resolve(__dirname, "..", "..", "templates", "default");
39
31
  if (fs.existsSync(devPath)) return devPath;
40
32
  if (fs.existsSync(prodPath)) return prodPath;
41
- throw new Error(`Template "${template}" not found`);
33
+ throw new Error("Template not found");
42
34
  }
43
35
  function updatePackageJson(projectPath, projectName) {
44
36
  const pkgPath = path.join(projectPath, "package.json");
@@ -46,6 +38,57 @@ function updatePackageJson(projectPath, projectName) {
46
38
  pkg.name = projectName;
47
39
  fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
48
40
  }
41
+ function updateSubPackageNames(projectPath, projectName) {
42
+ const webPkgPath = path.join(projectPath, "apps", "web", "package.json");
43
+ if (fs.existsSync(webPkgPath)) {
44
+ const pkg = JSON.parse(fs.readFileSync(webPkgPath, "utf-8"));
45
+ pkg.name = `@${projectName}/web`;
46
+ if (pkg.dependencies?.["@app/shared"]) {
47
+ pkg.dependencies[`@${projectName}/shared`] = pkg.dependencies["@app/shared"];
48
+ delete pkg.dependencies["@app/shared"];
49
+ }
50
+ fs.writeFileSync(webPkgPath, JSON.stringify(pkg, null, 2) + "\n");
51
+ }
52
+ const apiPkgPath = path.join(projectPath, "apps", "api", "package.json");
53
+ if (fs.existsSync(apiPkgPath)) {
54
+ const pkg = JSON.parse(fs.readFileSync(apiPkgPath, "utf-8"));
55
+ pkg.name = `@${projectName}/api`;
56
+ if (pkg.dependencies?.["@app/shared"]) {
57
+ pkg.dependencies[`@${projectName}/shared`] = pkg.dependencies["@app/shared"];
58
+ delete pkg.dependencies["@app/shared"];
59
+ }
60
+ fs.writeFileSync(apiPkgPath, JSON.stringify(pkg, null, 2) + "\n");
61
+ }
62
+ const sharedPkgPath = path.join(projectPath, "packages", "shared", "package.json");
63
+ if (fs.existsSync(sharedPkgPath)) {
64
+ const pkg = JSON.parse(fs.readFileSync(sharedPkgPath, "utf-8"));
65
+ pkg.name = `@${projectName}/shared`;
66
+ fs.writeFileSync(sharedPkgPath, JSON.stringify(pkg, null, 2) + "\n");
67
+ }
68
+ const pagePath = path.join(projectPath, "apps", "web", "src", "app", "page.tsx");
69
+ if (fs.existsSync(pagePath)) {
70
+ let content = fs.readFileSync(pagePath, "utf-8");
71
+ content = content.replace(/@app\/shared/g, `@${projectName}/shared`);
72
+ fs.writeFileSync(pagePath, content);
73
+ }
74
+ const cursorRulesDir = path.join(projectPath, ".cursor", "rules");
75
+ if (fs.existsSync(cursorRulesDir)) {
76
+ const ruleFiles = fs.readdirSync(cursorRulesDir);
77
+ for (const file of ruleFiles) {
78
+ const filePath = path.join(cursorRulesDir, file);
79
+ let content = fs.readFileSync(filePath, "utf-8");
80
+ content = content.replace(/@app\/shared/g, `@${projectName}/shared`);
81
+ fs.writeFileSync(filePath, content);
82
+ }
83
+ }
84
+ const rootPkgPath = path.join(projectPath, "package.json");
85
+ if (fs.existsSync(rootPkgPath)) {
86
+ let content = fs.readFileSync(rootPkgPath, "utf-8");
87
+ content = content.replace(/@app\/web/g, `@${projectName}/web`);
88
+ content = content.replace(/@app\/api/g, `@${projectName}/api`);
89
+ fs.writeFileSync(rootPkgPath, content);
90
+ }
91
+ }
49
92
  function renameGitignore(projectPath) {
50
93
  const gitignorePath = path.join(projectPath, "_gitignore");
51
94
  const targetPath = path.join(projectPath, ".gitignore");
@@ -70,46 +113,16 @@ async function promptForOptions(projectName) {
70
113
  return true;
71
114
  }
72
115
  },
73
- {
74
- type: "select",
75
- name: "template",
76
- message: "Select a template:",
77
- choices: [
78
- {
79
- title: "Default",
80
- description: "Full-stack with unified frontend and backend",
81
- value: "default"
82
- },
83
- {
84
- title: "Minimal",
85
- description: "Minimal setup for quick prototyping",
86
- value: "minimal"
87
- }
88
- ],
89
- initial: 0
90
- },
91
- {
92
- type: "confirm",
93
- name: "typescript",
94
- message: "Use TypeScript?",
95
- initial: true
96
- },
97
- {
98
- type: "confirm",
99
- name: "eslint",
100
- message: "Use ESLint?",
101
- initial: true
102
- },
103
116
  {
104
117
  type: "select",
105
118
  name: "packageManager",
106
119
  message: "Select a package manager:",
107
120
  choices: [
121
+ { title: "pnpm (recommended)", value: "pnpm" },
108
122
  { title: "npm", value: "npm" },
109
- { title: "yarn", value: "yarn" },
110
- { title: "pnpm", value: "pnpm" }
123
+ { title: "yarn", value: "yarn" }
111
124
  ],
112
- initial: ["npm", "yarn", "pnpm"].indexOf(detectPackageManager())
125
+ initial: 0
113
126
  }
114
127
  ],
115
128
  {
@@ -121,14 +134,11 @@ async function promptForOptions(projectName) {
121
134
  );
122
135
  return {
123
136
  projectName: projectName || response.projectName,
124
- template: response.template,
125
- typescript: response.typescript,
126
- eslint: response.eslint,
127
137
  packageManager: response.packageManager
128
138
  };
129
139
  }
130
140
  async function createProject(options) {
131
- const { projectName, template, packageManager } = options;
141
+ const { projectName, packageManager } = options;
132
142
  const projectPath = path.resolve(process.cwd(), projectName);
133
143
  if (fs.existsSync(projectPath)) {
134
144
  const { overwrite } = await prompts({
@@ -145,13 +155,14 @@ async function createProject(options) {
145
155
  }
146
156
  console.log();
147
157
  console.log(
148
- pc.cyan(`Creating a new nene.js project in ${pc.bold(projectPath)}`)
158
+ pc.cyan(`Creating a new nene.js monorepo in ${pc.bold(projectPath)}`)
149
159
  );
150
160
  console.log();
151
- const templateDir = getTemplateDir(template);
161
+ const templateDir = getTemplateDir();
152
162
  copyDir(templateDir, projectPath);
153
163
  renameGitignore(projectPath);
154
164
  updatePackageJson(projectPath, projectName);
165
+ updateSubPackageNames(projectPath, projectName);
155
166
  console.log(pc.cyan("Installing dependencies..."));
156
167
  console.log();
157
168
  const installCmd = {
@@ -172,10 +183,31 @@ async function createProject(options) {
172
183
  );
173
184
  }
174
185
  console.log();
186
+ console.log(pc.cyan("Building shared package..."));
187
+ try {
188
+ const buildCmd = packageManager === "npm" ? "npm run build" : `${packageManager} build`;
189
+ execSync(buildCmd, {
190
+ cwd: path.join(projectPath, "packages", "shared"),
191
+ stdio: "inherit"
192
+ });
193
+ } catch {
194
+ console.log(
195
+ pc.yellow("\nFailed to build shared package. Run 'pnpm build' in packages/shared manually.")
196
+ );
197
+ }
198
+ console.log();
175
199
  console.log(
176
200
  pc.green("Success!") + ` Created ${pc.bold(projectName)} at ${projectPath}`
177
201
  );
178
202
  console.log();
203
+ console.log("Project structure:");
204
+ console.log();
205
+ console.log(` ${pc.cyan("apps/web")} - Next.js frontend (port 3000)`);
206
+ console.log(` ${pc.cyan("apps/api")} - NestJS backend (port 4000)`);
207
+ console.log(` ${pc.cyan("packages/shared")} - Shared types and constants`);
208
+ console.log(` ${pc.cyan("docs/")} - Project documentation`);
209
+ console.log(` ${pc.cyan(".cursor/rules/")} - Cursor AI agent rules`);
210
+ console.log();
179
211
  console.log("Next steps:");
180
212
  console.log();
181
213
  console.log(` ${pc.cyan("cd")} ${projectName}`);
@@ -183,15 +215,18 @@ async function createProject(options) {
183
215
  ` ${pc.cyan(packageManager === "npm" ? "npm run" : packageManager)} dev`
184
216
  );
185
217
  console.log();
218
+ console.log("This will start both the frontend (port 3000) and backend (port 4000).");
219
+ console.log();
186
220
  console.log("Happy coding!");
187
221
  console.log();
188
222
  }
189
223
  async function main() {
190
- program.name("create-nene").description("Create a new nene.js project").version("0.1.0").argument("[project-name]", "Name of the project").option("-t, --template <template>", "Template to use (default, minimal)").option("--typescript", "Use TypeScript (default: true)").option("--no-typescript", "Do not use TypeScript").option("--eslint", "Use ESLint (default: true)").option("--no-eslint", "Do not use ESLint").action(async (projectName) => {
224
+ program.name("create-nene").description("Create a new nene.js monorepo with Next.js and NestJS").version("0.2.0").argument("[project-name]", "Name of the project").action(async (projectName) => {
191
225
  console.log();
192
226
  console.log(
193
227
  pc.bold(pc.cyan(" nene.js ") + "- The AI-native full-stack framework")
194
228
  );
229
+ console.log(pc.dim(" Next.js + NestJS monorepo for AI-assisted development"));
195
230
  console.log();
196
231
  const options = await promptForOptions(projectName);
197
232
  await createProject(options);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { program } from \"commander\";\nimport prompts from \"prompts\";\nimport pc from \"picocolors\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { execSync } from \"node:child_process\";\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\ninterface ProjectOptions {\n projectName: string;\n template: \"default\" | \"minimal\";\n typescript: boolean;\n eslint: boolean;\n packageManager: \"npm\" | \"yarn\" | \"pnpm\";\n}\n\nfunction validateProjectName(name: string): boolean {\n const validNameRegex =\n /^(?:@[a-z0-9-*~][a-z0-9-*._~]*\\/)?[a-z0-9-~][a-z0-9-._~]*$/;\n return validNameRegex.test(name);\n}\n\nfunction detectPackageManager(): \"npm\" | \"yarn\" | \"pnpm\" {\n const userAgent = process.env.npm_config_user_agent;\n if (userAgent) {\n if (userAgent.startsWith(\"yarn\")) return \"yarn\";\n if (userAgent.startsWith(\"pnpm\")) return \"pnpm\";\n }\n return \"npm\";\n}\n\nfunction copyDir(src: string, dest: string): void {\n fs.mkdirSync(dest, { recursive: true });\n const entries = fs.readdirSync(src, { withFileTypes: true });\n\n for (const entry of entries) {\n const srcPath = path.join(src, entry.name);\n const destPath = path.join(dest, entry.name);\n\n if (entry.isDirectory()) {\n copyDir(srcPath, destPath);\n } else {\n fs.copyFileSync(srcPath, destPath);\n }\n }\n}\n\nfunction getTemplateDir(template: string): string {\n // In development, templates are relative to src\n // In production (dist), templates are at the package root\n const devPath = path.resolve(__dirname, \"..\", \"templates\", template);\n const prodPath = path.resolve(__dirname, \"..\", \"..\", \"templates\", template);\n\n if (fs.existsSync(devPath)) return devPath;\n if (fs.existsSync(prodPath)) return prodPath;\n\n throw new Error(`Template \"${template}\" not found`);\n}\n\nfunction updatePackageJson(projectPath: string, projectName: string): void {\n const pkgPath = path.join(projectPath, \"package.json\");\n const pkg = JSON.parse(fs.readFileSync(pkgPath, \"utf-8\"));\n pkg.name = projectName;\n fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + \"\\n\");\n}\n\nfunction renameGitignore(projectPath: string): void {\n const gitignorePath = path.join(projectPath, \"_gitignore\");\n const targetPath = path.join(projectPath, \".gitignore\");\n if (fs.existsSync(gitignorePath)) {\n fs.renameSync(gitignorePath, targetPath);\n }\n}\n\nasync function promptForOptions(projectName?: string): Promise<ProjectOptions> {\n const defaultProjectName = projectName || \"my-nene-app\";\n\n const response = await prompts(\n [\n {\n type: projectName ? null : \"text\",\n name: \"projectName\",\n message: \"Project name:\",\n initial: defaultProjectName,\n validate: (value: string) => {\n if (!value) return \"Project name is required\";\n if (!validateProjectName(value)) {\n return \"Invalid project name. Use lowercase letters, numbers, and hyphens only.\";\n }\n return true;\n },\n },\n {\n type: \"select\",\n name: \"template\",\n message: \"Select a template:\",\n choices: [\n {\n title: \"Default\",\n description: \"Full-stack with unified frontend and backend\",\n value: \"default\",\n },\n {\n title: \"Minimal\",\n description: \"Minimal setup for quick prototyping\",\n value: \"minimal\",\n },\n ],\n initial: 0,\n },\n {\n type: \"confirm\",\n name: \"typescript\",\n message: \"Use TypeScript?\",\n initial: true,\n },\n {\n type: \"confirm\",\n name: \"eslint\",\n message: \"Use ESLint?\",\n initial: true,\n },\n {\n type: \"select\",\n name: \"packageManager\",\n message: \"Select a package manager:\",\n choices: [\n { title: \"npm\", value: \"npm\" },\n { title: \"yarn\", value: \"yarn\" },\n { title: \"pnpm\", value: \"pnpm\" },\n ],\n initial: [\"npm\", \"yarn\", \"pnpm\"].indexOf(detectPackageManager()),\n },\n ],\n {\n onCancel: () => {\n console.log(pc.red(\"\\nOperation cancelled.\"));\n process.exit(0);\n },\n }\n );\n\n return {\n projectName: projectName || response.projectName,\n template: response.template,\n typescript: response.typescript,\n eslint: response.eslint,\n packageManager: response.packageManager,\n };\n}\n\nasync function createProject(options: ProjectOptions): Promise<void> {\n const { projectName, template, packageManager } = options;\n const projectPath = path.resolve(process.cwd(), projectName);\n\n // Check if directory exists\n if (fs.existsSync(projectPath)) {\n const { overwrite } = await prompts({\n type: \"confirm\",\n name: \"overwrite\",\n message: `Directory \"${projectName}\" already exists. Overwrite?`,\n initial: false,\n });\n\n if (!overwrite) {\n console.log(pc.red(\"Operation cancelled.\"));\n process.exit(0);\n }\n\n fs.rmSync(projectPath, { recursive: true, force: true });\n }\n\n console.log();\n console.log(\n pc.cyan(`Creating a new nene.js project in ${pc.bold(projectPath)}`)\n );\n console.log();\n\n // Copy template\n const templateDir = getTemplateDir(template);\n copyDir(templateDir, projectPath);\n\n // Rename _gitignore to .gitignore\n renameGitignore(projectPath);\n\n // Update package.json with project name\n updatePackageJson(projectPath, projectName);\n\n // Install dependencies\n console.log(pc.cyan(\"Installing dependencies...\"));\n console.log();\n\n const installCmd = {\n npm: \"npm install\",\n yarn: \"yarn\",\n pnpm: \"pnpm install\",\n }[packageManager];\n\n try {\n execSync(installCmd, {\n cwd: projectPath,\n stdio: \"inherit\",\n });\n } catch {\n console.log(\n pc.yellow(\n \"\\nFailed to install dependencies. You can install them manually.\"\n )\n );\n }\n\n // Success message\n console.log();\n console.log(\n pc.green(\"Success!\") + ` Created ${pc.bold(projectName)} at ${projectPath}`\n );\n console.log();\n console.log(\"Next steps:\");\n console.log();\n console.log(` ${pc.cyan(\"cd\")} ${projectName}`);\n console.log(\n ` ${pc.cyan(packageManager === \"npm\" ? \"npm run\" : packageManager)} dev`\n );\n console.log();\n console.log(\"Happy coding!\");\n console.log();\n}\n\nexport async function main(): Promise<void> {\n program\n .name(\"create-nene\")\n .description(\"Create a new nene.js project\")\n .version(\"0.1.0\")\n .argument(\"[project-name]\", \"Name of the project\")\n .option(\"-t, --template <template>\", \"Template to use (default, minimal)\")\n .option(\"--typescript\", \"Use TypeScript (default: true)\")\n .option(\"--no-typescript\", \"Do not use TypeScript\")\n .option(\"--eslint\", \"Use ESLint (default: true)\")\n .option(\"--no-eslint\", \"Do not use ESLint\")\n .action(async (projectName: string | undefined) => {\n console.log();\n console.log(\n pc.bold(pc.cyan(\" nene.js \") + \"- The AI-native full-stack framework\")\n );\n console.log();\n\n const options = await promptForOptions(projectName);\n await createProject(options);\n });\n\n await program.parseAsync(process.argv);\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { program } from \"commander\";\nimport prompts from \"prompts\";\nimport pc from \"picocolors\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { execSync } from \"node:child_process\";\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\ninterface ProjectOptions {\n projectName: string;\n packageManager: \"npm\" | \"yarn\" | \"pnpm\";\n}\n\nfunction validateProjectName(name: string): boolean {\n const validNameRegex =\n /^(?:@[a-z0-9-*~][a-z0-9-*._~]*\\/)?[a-z0-9-~][a-z0-9-._~]*$/;\n return validNameRegex.test(name);\n}\n\nfunction detectPackageManager(): \"npm\" | \"yarn\" | \"pnpm\" {\n const userAgent = process.env.npm_config_user_agent;\n if (userAgent) {\n if (userAgent.startsWith(\"yarn\")) return \"yarn\";\n if (userAgent.startsWith(\"pnpm\")) return \"pnpm\";\n }\n return \"pnpm\"; // Default to pnpm for monorepo\n}\n\nfunction copyDir(src: string, dest: string): void {\n fs.mkdirSync(dest, { recursive: true });\n const entries = fs.readdirSync(src, { withFileTypes: true });\n\n for (const entry of entries) {\n const srcPath = path.join(src, entry.name);\n const destPath = path.join(dest, entry.name);\n\n if (entry.isDirectory()) {\n copyDir(srcPath, destPath);\n } else {\n fs.copyFileSync(srcPath, destPath);\n }\n }\n}\n\nfunction getTemplateDir(): string {\n // In development, templates are relative to src\n // In production (dist), templates are at the package root\n const devPath = path.resolve(__dirname, \"..\", \"templates\", \"default\");\n const prodPath = path.resolve(__dirname, \"..\", \"..\", \"templates\", \"default\");\n\n if (fs.existsSync(devPath)) return devPath;\n if (fs.existsSync(prodPath)) return prodPath;\n\n throw new Error(\"Template not found\");\n}\n\nfunction updatePackageJson(projectPath: string, projectName: string): void {\n const pkgPath = path.join(projectPath, \"package.json\");\n const pkg = JSON.parse(fs.readFileSync(pkgPath, \"utf-8\"));\n pkg.name = projectName;\n fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + \"\\n\");\n}\n\nfunction updateSubPackageNames(projectPath: string, projectName: string): void {\n // Update apps/web package.json\n const webPkgPath = path.join(projectPath, \"apps\", \"web\", \"package.json\");\n if (fs.existsSync(webPkgPath)) {\n const pkg = JSON.parse(fs.readFileSync(webPkgPath, \"utf-8\"));\n pkg.name = `@${projectName}/web`;\n // Update shared package reference\n if (pkg.dependencies?.[\"@app/shared\"]) {\n pkg.dependencies[`@${projectName}/shared`] = pkg.dependencies[\"@app/shared\"];\n delete pkg.dependencies[\"@app/shared\"];\n }\n fs.writeFileSync(webPkgPath, JSON.stringify(pkg, null, 2) + \"\\n\");\n }\n\n // Update apps/api package.json\n const apiPkgPath = path.join(projectPath, \"apps\", \"api\", \"package.json\");\n if (fs.existsSync(apiPkgPath)) {\n const pkg = JSON.parse(fs.readFileSync(apiPkgPath, \"utf-8\"));\n pkg.name = `@${projectName}/api`;\n // Update shared package reference\n if (pkg.dependencies?.[\"@app/shared\"]) {\n pkg.dependencies[`@${projectName}/shared`] = pkg.dependencies[\"@app/shared\"];\n delete pkg.dependencies[\"@app/shared\"];\n }\n fs.writeFileSync(apiPkgPath, JSON.stringify(pkg, null, 2) + \"\\n\");\n }\n\n // Update packages/shared package.json\n const sharedPkgPath = path.join(projectPath, \"packages\", \"shared\", \"package.json\");\n if (fs.existsSync(sharedPkgPath)) {\n const pkg = JSON.parse(fs.readFileSync(sharedPkgPath, \"utf-8\"));\n pkg.name = `@${projectName}/shared`;\n fs.writeFileSync(sharedPkgPath, JSON.stringify(pkg, null, 2) + \"\\n\");\n }\n\n // Update import statements in apps/web/src/app/page.tsx\n const pagePath = path.join(projectPath, \"apps\", \"web\", \"src\", \"app\", \"page.tsx\");\n if (fs.existsSync(pagePath)) {\n let content = fs.readFileSync(pagePath, \"utf-8\");\n content = content.replace(/@app\\/shared/g, `@${projectName}/shared`);\n fs.writeFileSync(pagePath, content);\n }\n\n // Update cursor rules to use correct package names\n const cursorRulesDir = path.join(projectPath, \".cursor\", \"rules\");\n if (fs.existsSync(cursorRulesDir)) {\n const ruleFiles = fs.readdirSync(cursorRulesDir);\n for (const file of ruleFiles) {\n const filePath = path.join(cursorRulesDir, file);\n let content = fs.readFileSync(filePath, \"utf-8\");\n content = content.replace(/@app\\/shared/g, `@${projectName}/shared`);\n fs.writeFileSync(filePath, content);\n }\n }\n\n // Update turbo.json filter names\n const rootPkgPath = path.join(projectPath, \"package.json\");\n if (fs.existsSync(rootPkgPath)) {\n let content = fs.readFileSync(rootPkgPath, \"utf-8\");\n content = content.replace(/@app\\/web/g, `@${projectName}/web`);\n content = content.replace(/@app\\/api/g, `@${projectName}/api`);\n fs.writeFileSync(rootPkgPath, content);\n }\n}\n\nfunction renameGitignore(projectPath: string): void {\n const gitignorePath = path.join(projectPath, \"_gitignore\");\n const targetPath = path.join(projectPath, \".gitignore\");\n if (fs.existsSync(gitignorePath)) {\n fs.renameSync(gitignorePath, targetPath);\n }\n}\n\nasync function promptForOptions(projectName?: string): Promise<ProjectOptions> {\n const defaultProjectName = projectName || \"my-nene-app\";\n\n const response = await prompts(\n [\n {\n type: projectName ? null : \"text\",\n name: \"projectName\",\n message: \"Project name:\",\n initial: defaultProjectName,\n validate: (value: string) => {\n if (!value) return \"Project name is required\";\n if (!validateProjectName(value)) {\n return \"Invalid project name. Use lowercase letters, numbers, and hyphens only.\";\n }\n return true;\n },\n },\n {\n type: \"select\",\n name: \"packageManager\",\n message: \"Select a package manager:\",\n choices: [\n { title: \"pnpm (recommended)\", value: \"pnpm\" },\n { title: \"npm\", value: \"npm\" },\n { title: \"yarn\", value: \"yarn\" },\n ],\n initial: 0,\n },\n ],\n {\n onCancel: () => {\n console.log(pc.red(\"\\nOperation cancelled.\"));\n process.exit(0);\n },\n }\n );\n\n return {\n projectName: projectName || response.projectName,\n packageManager: response.packageManager,\n };\n}\n\nasync function createProject(options: ProjectOptions): Promise<void> {\n const { projectName, packageManager } = options;\n const projectPath = path.resolve(process.cwd(), projectName);\n\n // Check if directory exists\n if (fs.existsSync(projectPath)) {\n const { overwrite } = await prompts({\n type: \"confirm\",\n name: \"overwrite\",\n message: `Directory \"${projectName}\" already exists. Overwrite?`,\n initial: false,\n });\n\n if (!overwrite) {\n console.log(pc.red(\"Operation cancelled.\"));\n process.exit(0);\n }\n\n fs.rmSync(projectPath, { recursive: true, force: true });\n }\n\n console.log();\n console.log(\n pc.cyan(`Creating a new nene.js monorepo in ${pc.bold(projectPath)}`)\n );\n console.log();\n\n // Copy template\n const templateDir = getTemplateDir();\n copyDir(templateDir, projectPath);\n\n // Rename _gitignore to .gitignore\n renameGitignore(projectPath);\n\n // Update package.json with project name\n updatePackageJson(projectPath, projectName);\n\n // Update sub-package names\n updateSubPackageNames(projectPath, projectName);\n\n // Install dependencies\n console.log(pc.cyan(\"Installing dependencies...\"));\n console.log();\n\n const installCmd = {\n npm: \"npm install\",\n yarn: \"yarn\",\n pnpm: \"pnpm install\",\n }[packageManager];\n\n try {\n execSync(installCmd, {\n cwd: projectPath,\n stdio: \"inherit\",\n });\n } catch {\n console.log(\n pc.yellow(\n \"\\nFailed to install dependencies. You can install them manually.\"\n )\n );\n }\n\n // Build shared package first\n console.log();\n console.log(pc.cyan(\"Building shared package...\"));\n \n try {\n const buildCmd = packageManager === \"npm\" ? \"npm run build\" : `${packageManager} build`;\n execSync(buildCmd, {\n cwd: path.join(projectPath, \"packages\", \"shared\"),\n stdio: \"inherit\",\n });\n } catch {\n console.log(\n pc.yellow(\"\\nFailed to build shared package. Run 'pnpm build' in packages/shared manually.\")\n );\n }\n\n // Success message\n console.log();\n console.log(\n pc.green(\"Success!\") + ` Created ${pc.bold(projectName)} at ${projectPath}`\n );\n console.log();\n console.log(\"Project structure:\");\n console.log();\n console.log(` ${pc.cyan(\"apps/web\")} - Next.js frontend (port 3000)`);\n console.log(` ${pc.cyan(\"apps/api\")} - NestJS backend (port 4000)`);\n console.log(` ${pc.cyan(\"packages/shared\")} - Shared types and constants`);\n console.log(` ${pc.cyan(\"docs/\")} - Project documentation`);\n console.log(` ${pc.cyan(\".cursor/rules/\")} - Cursor AI agent rules`);\n console.log();\n console.log(\"Next steps:\");\n console.log();\n console.log(` ${pc.cyan(\"cd\")} ${projectName}`);\n console.log(\n ` ${pc.cyan(packageManager === \"npm\" ? \"npm run\" : packageManager)} dev`\n );\n console.log();\n console.log(\"This will start both the frontend (port 3000) and backend (port 4000).\");\n console.log();\n console.log(\"Happy coding!\");\n console.log();\n}\n\nexport async function main(): Promise<void> {\n program\n .name(\"create-nene\")\n .description(\"Create a new nene.js monorepo with Next.js and NestJS\")\n .version(\"0.2.0\")\n .argument(\"[project-name]\", \"Name of the project\")\n .action(async (projectName: string | undefined) => {\n console.log();\n console.log(\n pc.bold(pc.cyan(\" nene.js \") + \"- The AI-native full-stack framework\")\n );\n console.log(pc.dim(\" Next.js + NestJS monorepo for AI-assisted development\"));\n console.log();\n\n const options = await promptForOptions(projectName);\n await createProject(options);\n });\n\n await program.parseAsync(process.argv);\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-nene",
3
- "version": "0.1.2",
3
+ "version": "0.2.0",
4
4
  "description": "Create a new nene.js project with a single command",
5
5
  "type": "module",
6
6
  "bin": {
@@ -0,0 +1,68 @@
1
+ ---
2
+ description: NestJS backend development patterns
3
+ globs: apps/api/**/*.ts
4
+ alwaysApply: false
5
+ ---
6
+
7
+ # Backend Patterns
8
+
9
+ ## Module Structure
10
+
11
+ ```
12
+ apps/api/src/
13
+ ├── main.ts # Application entry
14
+ ├── app.module.ts # Root module
15
+ ├── config/ # Configuration
16
+ └── {feature}/ # Feature modules
17
+ ├── {feature}.module.ts
18
+ ├── {feature}.controller.ts
19
+ ├── {feature}.service.ts
20
+ └── dto/ # Feature-specific DTOs (if not shared)
21
+ ```
22
+
23
+ ## Controller Pattern
24
+
25
+ ```typescript
26
+ import { Controller, Get, Post, Body } from '@nestjs/common';
27
+ import { CreateUserDto, User } from '@app/shared';
28
+
29
+ @Controller('users')
30
+ export class UsersController {
31
+ constructor(private readonly usersService: UsersService) {}
32
+
33
+ @Get()
34
+ findAll(): Promise<User[]> {
35
+ return this.usersService.findAll();
36
+ }
37
+
38
+ @Post()
39
+ create(@Body() dto: CreateUserDto): Promise<User> {
40
+ return this.usersService.create(dto);
41
+ }
42
+ }
43
+ ```
44
+
45
+ ## Validation
46
+
47
+ Use class-validator with shared DTOs:
48
+
49
+ ```typescript
50
+ import { CreateUserDto } from '@app/shared';
51
+
52
+ @Post()
53
+ create(@Body() dto: CreateUserDto) {
54
+ // Automatically validated by ValidationPipe
55
+ }
56
+ ```
57
+
58
+ ## Configuration
59
+
60
+ Use @nestjs/config for environment variables:
61
+
62
+ ```typescript
63
+ import { ConfigService } from '@nestjs/config';
64
+
65
+ constructor(private config: ConfigService) {
66
+ const port = this.config.get<number>('PORT');
67
+ }
68
+ ```
@@ -0,0 +1,53 @@
1
+ ---
2
+ description: Next.js frontend development patterns
3
+ globs: apps/web/**/*.{ts,tsx}
4
+ alwaysApply: false
5
+ ---
6
+
7
+ # Frontend Patterns
8
+
9
+ ## Component Structure
10
+
11
+ ```
12
+ apps/web/src/
13
+ ├── app/ # App Router pages
14
+ ├── components/ # React components
15
+ │ ├── ui/ # Reusable UI components
16
+ │ └── features/ # Feature-specific components
17
+ ├── hooks/ # Custom React hooks
18
+ └── lib/ # Utilities and helpers
19
+ ```
20
+
21
+ ## Server vs Client Components
22
+
23
+ ```typescript
24
+ // Default: Server Component (no directive needed)
25
+ export default function Page() {
26
+ return <div>Server rendered</div>;
27
+ }
28
+
29
+ // Client Component: add 'use client' at top
30
+ 'use client';
31
+ export function InteractiveComponent() {
32
+ const [state, setState] = useState();
33
+ return <button onClick={() => setState(...)}>Click</button>;
34
+ }
35
+ ```
36
+
37
+ ## API Calls to Backend
38
+
39
+ ```typescript
40
+ // Use environment variable for API URL
41
+ const API_URL = process.env.NEXT_PUBLIC_API_URL;
42
+
43
+ // Import routes from shared
44
+ import { API_ROUTES } from '@app/shared';
45
+
46
+ const response = await fetch(`${API_URL}${API_ROUTES.USERS}`);
47
+ ```
48
+
49
+ ## Styling
50
+
51
+ - Use Tailwind CSS for styling
52
+ - Follow mobile-first responsive design
53
+ - Use CSS variables for theming
@@ -0,0 +1,65 @@
1
+ ---
2
+ description: Nene monorepo architecture and key patterns
3
+ alwaysApply: true
4
+ ---
5
+
6
+ # Nene Architecture Rules
7
+
8
+ ## Project Structure
9
+
10
+ This is a full-stack monorepo:
11
+ - `apps/web/` - Next.js 15 frontend (port 3000)
12
+ - `apps/api/` - NestJS 11 backend (port 4000)
13
+ - `packages/shared/` - Shared types, DTOs, constants
14
+
15
+ ## Key Patterns
16
+
17
+ ### Type Sharing (Critical)
18
+
19
+ **NEVER duplicate types.** Always use `@app/shared`:
20
+
21
+ ```typescript
22
+ // ✅ GOOD - import from shared
23
+ import { User, CreateUserDto } from '@app/shared';
24
+
25
+ // ❌ BAD - duplicating types
26
+ interface User { id: string; ... }
27
+ ```
28
+
29
+ ### API Routes
30
+
31
+ Use shared constants for API routes:
32
+
33
+ ```typescript
34
+ // packages/shared/src/constants/index.ts
35
+ export const API_ROUTES = {
36
+ USERS: '/api/users',
37
+ };
38
+
39
+ // Use in both apps
40
+ import { API_ROUTES } from '@app/shared';
41
+ ```
42
+
43
+ ### Validation
44
+
45
+ DTOs with class-validator are shared:
46
+
47
+ ```typescript
48
+ // packages/shared/src/types/index.ts
49
+ export class CreateUserDto {
50
+ @IsEmail()
51
+ email: string;
52
+ }
53
+ ```
54
+
55
+ ## Before Making Changes
56
+
57
+ 1. Check `docs/ARCHITECTURE.md` for current patterns
58
+ 2. Check `docs/API.md` for existing endpoints
59
+ 3. Check `docs/PROGRESS.md` for project status
60
+
61
+ ## After Making Changes
62
+
63
+ 1. Update `docs/API.md` when adding/modifying endpoints
64
+ 2. Update `docs/PROGRESS.md` to reflect progress
65
+ 3. Run `pnpm build` to verify shared package changes work
@@ -0,0 +1,68 @@
1
+ ---
2
+ description: Shared package development rules
3
+ globs: packages/shared/**/*.ts
4
+ alwaysApply: false
5
+ ---
6
+
7
+ # Shared Package Rules
8
+
9
+ ## Purpose
10
+
11
+ The `@app/shared` package is the single source of truth for:
12
+ - TypeScript types and interfaces
13
+ - DTOs with validation decorators
14
+ - API route constants
15
+ - Shared utilities
16
+
17
+ ## File Structure
18
+
19
+ ```
20
+ packages/shared/src/
21
+ ├── index.ts # Main exports
22
+ ├── types/
23
+ │ └── index.ts # Interfaces and types
24
+ ├── constants/
25
+ │ └── index.ts # API routes, constants
26
+ └── utils/ # Shared utilities (optional)
27
+ ```
28
+
29
+ ## Adding New Types
30
+
31
+ 1. Add to `packages/shared/src/types/index.ts`
32
+ 2. Export from `packages/shared/src/index.ts`
33
+ 3. Run `pnpm build` in shared package
34
+
35
+ ```typescript
36
+ // types/index.ts
37
+ export interface NewType {
38
+ id: string;
39
+ name: string;
40
+ }
41
+
42
+ // index.ts
43
+ export * from './types';
44
+ ```
45
+
46
+ ## Adding DTOs
47
+
48
+ ```typescript
49
+ import { IsString, IsEmail } from 'class-validator';
50
+
51
+ export class CreateUserDto {
52
+ @IsEmail()
53
+ email: string;
54
+
55
+ @IsString()
56
+ name: string;
57
+ }
58
+ ```
59
+
60
+ ## After Changes
61
+
62
+ Always rebuild shared package after changes:
63
+
64
+ ```bash
65
+ pnpm --filter @app/shared build
66
+ # or from root
67
+ pnpm build
68
+ ```
@@ -0,0 +1,60 @@
1
+ ---
2
+ description: Task management workflow for docs/plan kanban board
3
+ globs: docs/plan/**/*.md
4
+ alwaysApply: false
5
+ ---
6
+
7
+ # Task Management Rules
8
+
9
+ Rules for the project's kanban board (`docs/plan/`) task management.
10
+
11
+ ## When Starting a Task
12
+
13
+ 1. Check the Priority Order in `docs/plan/README.md`
14
+ 2. Select the highest-priority TODO file
15
+ 3. Review the file contents before starting work
16
+
17
+ ## When Completing a Task (Required)
18
+
19
+ When completing a task, you **must** do the following:
20
+
21
+ ### 1. Move the File
22
+
23
+ ```bash
24
+ mv docs/plan/TODO/{task-file}.md docs/plan/DONE/
25
+ ```
26
+
27
+ ### 2. Update File Contents
28
+
29
+ ```markdown
30
+ # Status change
31
+
32
+ - **Status**: 📋 Todo → - **Status**: ✅ Done
33
+
34
+ # Complete checklist items
35
+
36
+ - [ ] item → - [x] item
37
+ ```
38
+
39
+ ### 3. Update README.md Counts
40
+
41
+ ```markdown
42
+ | ✅ DONE | N | # +1 increase
43
+ | 📋 TODO | M | # -1 decrease
44
+ ```
45
+
46
+ ## Task Status
47
+
48
+ | Status | Emoji | Folder |
49
+ | ------ | ----- | -------- |
50
+ | Todo | 📋 | `TODO/` |
51
+ | Doing | 🔄 | `DOING/` |
52
+ | Done | ✅ | `DONE/` |
53
+
54
+ ## Priority
55
+
56
+ | Priority | Emoji |
57
+ | -------- | ----- |
58
+ | High | 🔥 |
59
+ | Medium | 🟠 |
60
+ | Low | 🔵 |