create-nene 0.1.2 → 0.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.
Files changed (57) hide show
  1. package/dist/index.js +185 -62
  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 +135 -0
  6. package/templates/default/.cursor/rules/nene-architecture.mdc +95 -0
  7. package/templates/default/.cursor/rules/shared-package.mdc +68 -0
  8. package/templates/default/.cursor/rules/task-management.mdc +71 -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 +63 -0
  26. package/templates/default/apps/web/src/app/layout.tsx +36 -0
  27. package/templates/default/apps/web/src/app/page.tsx +375 -0
  28. package/templates/default/apps/web/tailwind.config.ts +26 -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/kanban/DOING/_gitkeep +0 -0
  32. package/templates/default/docs/kanban/DONE/01-project-setup.md +24 -0
  33. package/templates/default/docs/kanban/README.md +60 -0
  34. package/templates/default/docs/kanban/TODO/01-database-integration.md +24 -0
  35. package/templates/default/docs/overview/ARCHITECTURE.md +59 -0
  36. package/templates/default/docs/pages/README.md +50 -0
  37. package/templates/default/package.json +16 -17
  38. package/templates/default/packages/shared/package.json +29 -0
  39. package/templates/default/packages/shared/src/constants/index.ts +13 -0
  40. package/templates/default/packages/shared/src/index.ts +3 -0
  41. package/templates/default/packages/shared/src/types/index.ts +21 -0
  42. package/templates/default/packages/shared/tsconfig.json +17 -0
  43. package/templates/default/packages/shared/tsup.config.ts +10 -0
  44. package/templates/default/pnpm-workspace.yaml +3 -0
  45. package/templates/default/scripts/dev.sh +59 -0
  46. package/templates/default/turbo.json +14 -0
  47. package/templates/default/eslint.config.mjs +0 -10
  48. package/templates/default/src/app/layout.tsx +0 -14
  49. package/templates/default/src/app/page.tsx +0 -29
  50. package/templates/default/src/server/api/hello.ts +0 -14
  51. package/templates/default/src/server/index.ts +0 -3
  52. package/templates/minimal/README.md +0 -11
  53. package/templates/minimal/_gitignore +0 -6
  54. package/templates/minimal/package.json +0 -22
  55. package/templates/minimal/src/app/layout.tsx +0 -9
  56. package/templates/minimal/src/app/page.tsx +0 -7
  57. package/templates/minimal/tsconfig.json +0 -19
package/dist/index.js CHANGED
@@ -12,13 +12,68 @@ 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";
15
+ function isCommandAvailable(command) {
16
+ try {
17
+ execSync(`${command} --version`, { stdio: "ignore" });
18
+ return true;
19
+ } catch {
20
+ return false;
21
+ }
22
+ }
23
+ function ensurePackageManager(packageManager) {
24
+ if (isCommandAvailable(packageManager)) {
25
+ return true;
26
+ }
27
+ console.log(
28
+ pc.yellow(`
29
+ ${packageManager} is not installed. Attempting to install...`)
30
+ );
31
+ if (isCommandAvailable("corepack")) {
32
+ try {
33
+ console.log(pc.dim(` Using corepack to enable ${packageManager}...`));
34
+ execSync(`corepack enable ${packageManager}`, { stdio: "inherit" });
35
+ if (isCommandAvailable(packageManager)) {
36
+ console.log(pc.green(` \u2713 ${packageManager} installed via corepack
37
+ `));
38
+ return true;
39
+ }
40
+ } catch {
41
+ }
42
+ }
43
+ if (packageManager !== "npm" && isCommandAvailable("npm")) {
44
+ try {
45
+ console.log(pc.dim(` Installing ${packageManager} globally via npm...`));
46
+ execSync(`npm install -g ${packageManager}`, { stdio: "inherit" });
47
+ if (isCommandAvailable(packageManager)) {
48
+ console.log(pc.green(`
49
+ \u2713 ${packageManager} installed via npm
50
+ `));
51
+ return true;
52
+ }
53
+ } catch {
54
+ }
55
+ }
56
+ console.log(
57
+ pc.red(`
58
+ \u2717 Failed to install ${packageManager} automatically.`)
59
+ );
60
+ console.log(
61
+ pc.yellow(` Please install it manually:
62
+ `)
63
+ );
64
+ if (packageManager === "pnpm") {
65
+ console.log(pc.dim(" npm install -g pnpm"));
66
+ console.log(pc.dim(" # or"));
67
+ console.log(pc.dim(" corepack enable pnpm"));
68
+ console.log(pc.dim(" # or"));
69
+ console.log(pc.dim(" curl -fsSL https://get.pnpm.io/install.sh | sh -"));
70
+ } else if (packageManager === "yarn") {
71
+ console.log(pc.dim(" npm install -g yarn"));
72
+ console.log(pc.dim(" # or"));
73
+ console.log(pc.dim(" corepack enable yarn"));
20
74
  }
21
- return "npm";
75
+ console.log();
76
+ return false;
22
77
  }
23
78
  function copyDir(src, dest) {
24
79
  fs.mkdirSync(dest, { recursive: true });
@@ -33,12 +88,12 @@ function copyDir(src, dest) {
33
88
  }
34
89
  }
35
90
  }
36
- function getTemplateDir(template) {
37
- const devPath = path.resolve(__dirname, "..", "templates", template);
38
- const prodPath = path.resolve(__dirname, "..", "..", "templates", template);
91
+ function getTemplateDir() {
92
+ const devPath = path.resolve(__dirname, "..", "templates", "default");
93
+ const prodPath = path.resolve(__dirname, "..", "..", "templates", "default");
39
94
  if (fs.existsSync(devPath)) return devPath;
40
95
  if (fs.existsSync(prodPath)) return prodPath;
41
- throw new Error(`Template "${template}" not found`);
96
+ throw new Error("Template not found");
42
97
  }
43
98
  function updatePackageJson(projectPath, projectName) {
44
99
  const pkgPath = path.join(projectPath, "package.json");
@@ -46,6 +101,57 @@ function updatePackageJson(projectPath, projectName) {
46
101
  pkg.name = projectName;
47
102
  fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
48
103
  }
104
+ function updateSubPackageNames(projectPath, projectName) {
105
+ const webPkgPath = path.join(projectPath, "apps", "web", "package.json");
106
+ if (fs.existsSync(webPkgPath)) {
107
+ const pkg = JSON.parse(fs.readFileSync(webPkgPath, "utf-8"));
108
+ pkg.name = `@${projectName}/web`;
109
+ if (pkg.dependencies?.["@app/shared"]) {
110
+ pkg.dependencies[`@${projectName}/shared`] = pkg.dependencies["@app/shared"];
111
+ delete pkg.dependencies["@app/shared"];
112
+ }
113
+ fs.writeFileSync(webPkgPath, JSON.stringify(pkg, null, 2) + "\n");
114
+ }
115
+ const apiPkgPath = path.join(projectPath, "apps", "api", "package.json");
116
+ if (fs.existsSync(apiPkgPath)) {
117
+ const pkg = JSON.parse(fs.readFileSync(apiPkgPath, "utf-8"));
118
+ pkg.name = `@${projectName}/api`;
119
+ if (pkg.dependencies?.["@app/shared"]) {
120
+ pkg.dependencies[`@${projectName}/shared`] = pkg.dependencies["@app/shared"];
121
+ delete pkg.dependencies["@app/shared"];
122
+ }
123
+ fs.writeFileSync(apiPkgPath, JSON.stringify(pkg, null, 2) + "\n");
124
+ }
125
+ const sharedPkgPath = path.join(projectPath, "packages", "shared", "package.json");
126
+ if (fs.existsSync(sharedPkgPath)) {
127
+ const pkg = JSON.parse(fs.readFileSync(sharedPkgPath, "utf-8"));
128
+ pkg.name = `@${projectName}/shared`;
129
+ fs.writeFileSync(sharedPkgPath, JSON.stringify(pkg, null, 2) + "\n");
130
+ }
131
+ const pagePath = path.join(projectPath, "apps", "web", "src", "app", "page.tsx");
132
+ if (fs.existsSync(pagePath)) {
133
+ let content = fs.readFileSync(pagePath, "utf-8");
134
+ content = content.replace(/@app\/shared/g, `@${projectName}/shared`);
135
+ fs.writeFileSync(pagePath, content);
136
+ }
137
+ const cursorRulesDir = path.join(projectPath, ".cursor", "rules");
138
+ if (fs.existsSync(cursorRulesDir)) {
139
+ const ruleFiles = fs.readdirSync(cursorRulesDir);
140
+ for (const file of ruleFiles) {
141
+ const filePath = path.join(cursorRulesDir, file);
142
+ let content = fs.readFileSync(filePath, "utf-8");
143
+ content = content.replace(/@app\/shared/g, `@${projectName}/shared`);
144
+ fs.writeFileSync(filePath, content);
145
+ }
146
+ }
147
+ const rootPkgPath = path.join(projectPath, "package.json");
148
+ if (fs.existsSync(rootPkgPath)) {
149
+ let content = fs.readFileSync(rootPkgPath, "utf-8");
150
+ content = content.replace(/@app\/web/g, `@${projectName}/web`);
151
+ content = content.replace(/@app\/api/g, `@${projectName}/api`);
152
+ fs.writeFileSync(rootPkgPath, content);
153
+ }
154
+ }
49
155
  function renameGitignore(projectPath) {
50
156
  const gitignorePath = path.join(projectPath, "_gitignore");
51
157
  const targetPath = path.join(projectPath, ".gitignore");
@@ -72,44 +178,23 @@ async function promptForOptions(projectName) {
72
178
  },
73
179
  {
74
180
  type: "select",
75
- name: "template",
76
- message: "Select a template:",
181
+ name: "packageManager",
182
+ message: "Select a package manager:",
77
183
  choices: [
78
184
  {
79
- title: "Default",
80
- description: "Full-stack with unified frontend and backend",
81
- value: "default"
185
+ title: isCommandAvailable("pnpm") ? "pnpm (recommended)" : "pnpm (recommended, will be installed)",
186
+ value: "pnpm"
187
+ },
188
+ {
189
+ title: isCommandAvailable("npm") ? "npm" : "npm (not found)",
190
+ value: "npm"
82
191
  },
83
192
  {
84
- title: "Minimal",
85
- description: "Minimal setup for quick prototyping",
86
- value: "minimal"
193
+ title: isCommandAvailable("yarn") ? "yarn" : "yarn (will be installed)",
194
+ value: "yarn"
87
195
  }
88
196
  ],
89
197
  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
- {
104
- type: "select",
105
- name: "packageManager",
106
- message: "Select a package manager:",
107
- choices: [
108
- { title: "npm", value: "npm" },
109
- { title: "yarn", value: "yarn" },
110
- { title: "pnpm", value: "pnpm" }
111
- ],
112
- initial: ["npm", "yarn", "pnpm"].indexOf(detectPackageManager())
113
198
  }
114
199
  ],
115
200
  {
@@ -121,14 +206,11 @@ async function promptForOptions(projectName) {
121
206
  );
122
207
  return {
123
208
  projectName: projectName || response.projectName,
124
- template: response.template,
125
- typescript: response.typescript,
126
- eslint: response.eslint,
127
209
  packageManager: response.packageManager
128
210
  };
129
211
  }
130
212
  async function createProject(options) {
131
- const { projectName, template, packageManager } = options;
213
+ const { projectName, packageManager } = options;
132
214
  const projectPath = path.resolve(process.cwd(), projectName);
133
215
  if (fs.existsSync(projectPath)) {
134
216
  const { overwrite } = await prompts({
@@ -145,37 +227,75 @@ async function createProject(options) {
145
227
  }
146
228
  console.log();
147
229
  console.log(
148
- pc.cyan(`Creating a new nene.js project in ${pc.bold(projectPath)}`)
230
+ pc.cyan(`Creating a new nene.js monorepo in ${pc.bold(projectPath)}`)
149
231
  );
150
232
  console.log();
151
- const templateDir = getTemplateDir(template);
233
+ const templateDir = getTemplateDir();
152
234
  copyDir(templateDir, projectPath);
153
235
  renameGitignore(projectPath);
154
236
  updatePackageJson(projectPath, projectName);
155
- console.log(pc.cyan("Installing dependencies..."));
156
- console.log();
157
- const installCmd = {
158
- npm: "npm install",
159
- yarn: "yarn",
160
- pnpm: "pnpm install"
161
- }[packageManager];
162
- try {
163
- execSync(installCmd, {
164
- cwd: projectPath,
165
- stdio: "inherit"
166
- });
167
- } catch {
237
+ updateSubPackageNames(projectPath, projectName);
238
+ const pmReady = ensurePackageManager(packageManager);
239
+ if (!pmReady) {
168
240
  console.log(
169
241
  pc.yellow(
170
- "\nFailed to install dependencies. You can install them manually."
242
+ `Skipping dependency installation. After installing ${packageManager}, run:`
171
243
  )
172
244
  );
245
+ console.log(pc.dim(` cd ${projectName}`));
246
+ console.log(pc.dim(` ${packageManager === "npm" ? "npm install" : packageManager === "yarn" ? "yarn" : "pnpm install"}`));
247
+ console.log(pc.dim(` cd packages/shared && ${packageManager === "npm" ? "npm run build" : `${packageManager} build`}`));
248
+ console.log();
249
+ } else {
250
+ console.log(pc.cyan("Installing dependencies..."));
251
+ console.log();
252
+ const installCmd = {
253
+ npm: "npm install",
254
+ yarn: "yarn",
255
+ pnpm: "pnpm install"
256
+ }[packageManager];
257
+ try {
258
+ execSync(installCmd, {
259
+ cwd: projectPath,
260
+ stdio: "inherit"
261
+ });
262
+ } catch {
263
+ console.log(
264
+ pc.yellow(
265
+ "\nFailed to install dependencies. You can install them manually."
266
+ )
267
+ );
268
+ }
269
+ console.log();
270
+ console.log(pc.cyan("Building shared package..."));
271
+ try {
272
+ const buildCmd = packageManager === "npm" ? "npm run build" : `${packageManager} build`;
273
+ execSync(buildCmd, {
274
+ cwd: path.join(projectPath, "packages", "shared"),
275
+ stdio: "inherit"
276
+ });
277
+ } catch {
278
+ console.log(
279
+ pc.yellow(
280
+ `
281
+ Failed to build shared package. Run '${packageManager === "npm" ? "npm run build" : `${packageManager} build`}' in packages/shared manually.`
282
+ )
283
+ );
284
+ }
173
285
  }
174
286
  console.log();
175
287
  console.log(
176
288
  pc.green("Success!") + ` Created ${pc.bold(projectName)} at ${projectPath}`
177
289
  );
178
290
  console.log();
291
+ console.log("Project structure:");
292
+ console.log();
293
+ console.log(` ${pc.cyan("apps/web")} - Next.js frontend (port 3000)`);
294
+ console.log(` ${pc.cyan("apps/api")} - NestJS backend (port 4000)`);
295
+ console.log(` ${pc.cyan("packages/shared")} - Shared types and constants`);
296
+ console.log(` ${pc.cyan("docs/")} - Project documentation`);
297
+ console.log(` ${pc.cyan(".cursor/rules/")} - Cursor AI agent rules`);
298
+ console.log();
179
299
  console.log("Next steps:");
180
300
  console.log();
181
301
  console.log(` ${pc.cyan("cd")} ${projectName}`);
@@ -183,15 +303,18 @@ async function createProject(options) {
183
303
  ` ${pc.cyan(packageManager === "npm" ? "npm run" : packageManager)} dev`
184
304
  );
185
305
  console.log();
306
+ console.log("This will start both the frontend (port 3000) and backend (port 4000).");
307
+ console.log();
186
308
  console.log("Happy coding!");
187
309
  console.log();
188
310
  }
189
311
  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) => {
312
+ 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
313
  console.log();
192
314
  console.log(
193
315
  pc.bold(pc.cyan(" nene.js ") + "- The AI-native full-stack framework")
194
316
  );
317
+ console.log(pc.dim(" Next.js + NestJS monorepo for AI-assisted development"));
195
318
  console.log();
196
319
  const options = await promptForOptions(projectName);
197
320
  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 isCommandAvailable(command: string): boolean {\n try {\n execSync(`${command} --version`, { stdio: \"ignore\" });\n return true;\n } catch {\n return false;\n }\n}\n\nfunction ensurePackageManager(packageManager: \"npm\" | \"yarn\" | \"pnpm\"): boolean {\n if (isCommandAvailable(packageManager)) {\n return true;\n }\n\n console.log(\n pc.yellow(`\\n${packageManager} is not installed. Attempting to install...`)\n );\n\n // Try corepack first (built-in to Node.js 16.9+)\n if (isCommandAvailable(\"corepack\")) {\n try {\n console.log(pc.dim(` Using corepack to enable ${packageManager}...`));\n execSync(`corepack enable ${packageManager}`, { stdio: \"inherit\" });\n if (isCommandAvailable(packageManager)) {\n console.log(pc.green(` ✓ ${packageManager} installed via corepack\\n`));\n return true;\n }\n } catch {\n // corepack failed, try next method\n }\n }\n\n // Fallback: install via npm\n if (packageManager !== \"npm\" && isCommandAvailable(\"npm\")) {\n try {\n console.log(pc.dim(` Installing ${packageManager} globally via npm...`));\n execSync(`npm install -g ${packageManager}`, { stdio: \"inherit\" });\n if (isCommandAvailable(packageManager)) {\n console.log(pc.green(`\\n ✓ ${packageManager} installed via npm\\n`));\n return true;\n }\n } catch {\n // npm global install failed (possibly permission issue)\n }\n }\n\n // All methods failed\n console.log(\n pc.red(`\\n ✗ Failed to install ${packageManager} automatically.`)\n );\n console.log(\n pc.yellow(` Please install it manually:\\n`)\n );\n\n if (packageManager === \"pnpm\") {\n console.log(pc.dim(\" npm install -g pnpm\"));\n console.log(pc.dim(\" # or\"));\n console.log(pc.dim(\" corepack enable pnpm\"));\n console.log(pc.dim(\" # or\"));\n console.log(pc.dim(\" curl -fsSL https://get.pnpm.io/install.sh | sh -\"));\n } else if (packageManager === \"yarn\") {\n console.log(pc.dim(\" npm install -g yarn\"));\n console.log(pc.dim(\" # or\"));\n console.log(pc.dim(\" corepack enable yarn\"));\n }\n console.log();\n\n return false;\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 {\n title: isCommandAvailable(\"pnpm\")\n ? \"pnpm (recommended)\"\n : \"pnpm (recommended, will be installed)\",\n value: \"pnpm\",\n },\n {\n title: isCommandAvailable(\"npm\")\n ? \"npm\"\n : \"npm (not found)\",\n value: \"npm\",\n },\n {\n title: isCommandAvailable(\"yarn\")\n ? \"yarn\"\n : \"yarn (will be installed)\",\n value: \"yarn\",\n },\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 // Ensure package manager is available\n const pmReady = ensurePackageManager(packageManager);\n\n if (!pmReady) {\n console.log(\n pc.yellow(\n `Skipping dependency installation. After installing ${packageManager}, run:`\n )\n );\n console.log(pc.dim(` cd ${projectName}`));\n console.log(pc.dim(` ${packageManager === \"npm\" ? \"npm install\" : packageManager === \"yarn\" ? \"yarn\" : \"pnpm install\"}`));\n console.log(pc.dim(` cd packages/shared && ${packageManager === \"npm\" ? \"npm run build\" : `${packageManager} build`}`));\n console.log();\n } else {\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(\n `\\nFailed to build shared package. Run '${packageManager === \"npm\" ? \"npm run build\" : `${packageManager} build`}' in packages/shared manually.`\n )\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(\"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.3.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,135 @@
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
54
+
55
+ ## Post-Edit Verification (REQUIRED)
56
+
57
+ **After every web file edit (`apps/web/**`), you MUST verify the app still works.**
58
+
59
+ This is the most important rule in this file. Skipping verification leads to broken builds that the user discovers later.
60
+
61
+ ### Step 1: Check if dev server is running
62
+
63
+ ```bash
64
+ # Check if the Next.js dev server is running
65
+ curl -s -o /dev/null -w "%{http_code}" http://localhost:3000
66
+ ```
67
+
68
+ - If `000` → dev server is not running. Start it with `pnpm dev` (or `pnpm dev:web`).
69
+ - If `200` → proceed to Step 2.
70
+ - If `500` → there's an error. Proceed to Step 2 for details.
71
+
72
+ ### Step 2: Fetch the page and check for errors
73
+
74
+ ```bash
75
+ # Fetch the page HTML and check for runtime errors
76
+ curl -s http://localhost:3000 2>&1 | head -100
77
+ ```
78
+
79
+ **What to look for:**
80
+ - `Cannot find module './XXX.js'` → **Stale webpack cache** (see Troubleshooting below)
81
+ - `Error: ` or `error` in the output → Code error, inspect and fix
82
+ - `Internal Server Error` or `500` → Server-side rendering error
83
+ - Normal HTML with `<div>`, `<html>` → Page is working correctly
84
+
85
+ ### Step 3: Auto-fix based on error type
86
+
87
+ | Error Pattern | Cause | Fix |
88
+ |---|---|---|
89
+ | `Cannot find module './NNN.js'` | Stale `.next/` cache | `rm -rf apps/web/.next` then restart dev server |
90
+ | `Module not found: Can't resolve 'X'` | Missing import / dependency | Check import paths, install missing packages |
91
+ | `SyntaxError` or `TypeError` | Code bug in your edit | Review and fix the code you just changed |
92
+ | `Invalid hook call` | Hooks in server component | Add `'use client'` directive or restructure |
93
+ | `500 Internal Server Error` | Server-side rendering crash | Check terminal logs for the full stack trace |
94
+
95
+ ### Step 4: Re-verify after fix
96
+
97
+ After applying any fix, **repeat Steps 1-2** to confirm the error is resolved. Do not move on to other tasks until the page loads successfully.
98
+
99
+ ### Quick Verification Command (copy-paste ready)
100
+
101
+ ```bash
102
+ # One-liner: check if the web app is healthy
103
+ curl -s -o /dev/null -w "Web status: %{http_code}\n" http://localhost:3000 && curl -s http://localhost:3000 | grep -c "<!DOCTYPE\|<html" > /dev/null && echo "✓ Page OK" || echo "✗ Page has issues - check output above"
104
+ ```
105
+
106
+ ## Troubleshooting
107
+
108
+ ### "Cannot find module './XXX.js'" Runtime Error
109
+
110
+ This error occurs when the Next.js webpack cache (`.next/`) becomes stale — typically after files are modified while the dev server is running.
111
+
112
+ **How to fix:**
113
+
114
+ 1. Stop the dev server (or it will auto-restart on file change)
115
+ 2. Delete the cache: `rm -rf apps/web/.next`
116
+ 3. Restart: `pnpm dev` (or `pnpm dev:clean` which does steps 2+3 together)
117
+
118
+ **When you encounter this error as an AI agent:**
119
+
120
+ - Do NOT try to debug the missing module — the `.js` file number is a webpack chunk hash, not your code.
121
+ - Run `rm -rf apps/web/.next` and restart the dev server. This resolves the issue 100% of the time.
122
+ - If the error persists after cache clear, THEN look for actual code issues.
123
+
124
+ ### Next.js dev server port conflict
125
+
126
+ If port 3000 is already in use, Next.js will auto-select the next available port and display it in the terminal output. This is normal behavior — check the terminal for the actual port number.
127
+
128
+ ### Build verification (for production)
129
+
130
+ ```bash
131
+ # Full build check (catches more issues than dev mode)
132
+ pnpm build 2>&1 | tail -20
133
+ ```
134
+
135
+ If the build fails, fix all errors before considering the task complete.