create-loadout 1.0.2 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -49,7 +49,7 @@ async function newProjectFlow() {
49
49
  }
50
50
  console.log(chalk.bold(' Next steps:'));
51
51
  console.log(chalk.gray(` 1. cd ${config.name}`));
52
- console.log(chalk.gray(' 2. Configure .env.local with your API keys'));
52
+ console.log(chalk.gray(' 2. Configure .env with your API keys'));
53
53
  console.log(chalk.gray(' 3. npm run dev'));
54
54
  console.log();
55
55
  if (config.integrations.includes('neon-drizzle')) {
@@ -101,7 +101,7 @@ async function addIntegrationFlow(projectPath) {
101
101
  });
102
102
  console.log();
103
103
  console.log(chalk.bold(' Next steps:'));
104
- console.log(chalk.gray(' 1. Update .env.local with new API keys'));
104
+ console.log(chalk.gray(' 1. Update .env with new API keys'));
105
105
  console.log(chalk.gray(' 2. npm run dev'));
106
106
  console.log();
107
107
  if (addConfig.integrations.includes('neon-drizzle')) {
package/dist/engine.js CHANGED
@@ -18,7 +18,7 @@ export async function createProject(config, onProgress) {
18
18
  await extendUtils(projectPath);
19
19
  onProgress?.('Installing base packages...');
20
20
  const { execa } = await import('execa');
21
- await execa('npm', ['install', 'zod@^3.24', 'zustand@^5', 'luxon@^3'], { cwd: projectPath });
21
+ await execa('npm', ['install', 'zod', 'zustand', 'luxon'], { cwd: projectPath });
22
22
  await execa('npm', ['install', '-D', '@types/luxon'], { cwd: projectPath });
23
23
  await fs.mkdir(path.join(projectPath, 'lib/stores'), { recursive: true });
24
24
  await fs.writeFile(path.join(projectPath, 'lib/stores/counter.store.ts'), zustandTemplates.exampleStore);
@@ -53,29 +53,29 @@ export async function addIntegrations(projectPath, config, onProgress) {
53
53
  async function extendUtils(projectPath) {
54
54
  const utilsPath = path.join(projectPath, 'lib/utils.ts');
55
55
  const existingContent = await fs.readFile(utilsPath, 'utf-8');
56
- const additionalUtils = `
57
- import { DateTime } from 'luxon';
58
-
59
- export function formatDate(date: Date | string, format = 'LLL d, yyyy'): string {
60
- const dt = typeof date === 'string' ? DateTime.fromISO(date) : DateTime.fromJSDate(date);
61
- return dt.toFormat(format);
62
- }
63
-
64
- export function formatRelative(date: Date | string): string {
65
- const dt = typeof date === 'string' ? DateTime.fromISO(date) : DateTime.fromJSDate(date);
66
- return dt.toRelative() ?? dt.toFormat('LLL d, yyyy');
67
- }
68
-
69
- export function debounce<P extends unknown[], R>(
70
- func: (...args: P) => R,
71
- wait: number
72
- ): (...args: P) => void {
73
- let timeout: ReturnType<typeof setTimeout>;
74
- return (...args: P) => {
75
- clearTimeout(timeout);
76
- timeout = setTimeout(() => func(...args), wait);
77
- };
78
- }
56
+ const additionalUtils = `
57
+ import { DateTime } from 'luxon';
58
+
59
+ export function formatDate(date: Date | string, format = 'LLL d, yyyy'): string {
60
+ const dt = typeof date === 'string' ? DateTime.fromISO(date) : DateTime.fromJSDate(date);
61
+ return dt.toFormat(format);
62
+ }
63
+
64
+ export function formatRelative(date: Date | string): string {
65
+ const dt = typeof date === 'string' ? DateTime.fromISO(date) : DateTime.fromJSDate(date);
66
+ return dt.toRelative() ?? dt.toFormat('LLL d, yyyy');
67
+ }
68
+
69
+ export function debounce<P extends unknown[], R>(
70
+ func: (...args: P) => R,
71
+ wait: number
72
+ ): (...args: P) => void {
73
+ let timeout: ReturnType<typeof setTimeout>;
74
+ return (...args: P) => {
75
+ clearTimeout(timeout);
76
+ timeout = setTimeout(() => func(...args), wait);
77
+ };
78
+ }
79
79
  `;
80
80
  await fs.writeFile(utilsPath, existingContent + additionalUtils);
81
81
  }
package/dist/env.js CHANGED
@@ -138,37 +138,24 @@ export async function generateEnvFiles(projectPath, config) {
138
138
  envExample += '\n';
139
139
  }
140
140
  await fs.writeFile(path.join(projectPath, '.env.example'), envExample.trim() + '\n');
141
- // Generate .env.local with empty values
142
- let envLocal = '';
141
+ // Generate .env with empty values
142
+ let env = '';
143
143
  for (const id of selectedIds) {
144
144
  const section = getEnvSection(id, config.aiProvider);
145
- envLocal += generateEnvSection(section, false);
146
- envLocal += '\n';
147
- }
148
- await fs.writeFile(path.join(projectPath, '.env.local'), envLocal.trim() + '\n');
149
- // Update .gitignore to include .env.local
150
- const gitignorePath = path.join(projectPath, '.gitignore');
151
- try {
152
- let gitignore = await fs.readFile(gitignorePath, 'utf-8');
153
- if (!gitignore.includes('.env.local')) {
154
- gitignore += '\n# Environment variables\n.env.local\n.env*.local\n';
155
- await fs.writeFile(gitignorePath, gitignore);
156
- }
157
- }
158
- catch {
159
- // .gitignore doesn't exist, create it
160
- await fs.writeFile(gitignorePath, '# Environment variables\n.env.local\n.env*.local\n');
145
+ env += generateEnvSection(section, false);
146
+ env += '\n';
161
147
  }
148
+ await fs.writeFile(path.join(projectPath, '.env'), env.trim() + '\n');
162
149
  }
163
150
  export async function appendEnvFiles(projectPath, integrations, aiProvider) {
164
151
  const envExamplePath = path.join(projectPath, '.env.example');
165
- const envLocalPath = path.join(projectPath, '.env.local');
152
+ const envPath = path.join(projectPath, '.env');
166
153
  let envExampleContent = '';
167
- let envLocalContent = '';
154
+ let envContent = '';
168
155
  for (const id of integrations) {
169
156
  const section = getEnvSection(id, aiProvider);
170
157
  envExampleContent += '\n' + generateEnvSection(section, true);
171
- envLocalContent += '\n' + generateEnvSection(section, false);
158
+ envContent += '\n' + generateEnvSection(section, false);
172
159
  }
173
160
  if (envExampleContent) {
174
161
  try {
@@ -179,13 +166,13 @@ export async function appendEnvFiles(projectPath, integrations, aiProvider) {
179
166
  await fs.writeFile(envExamplePath, envExampleContent.trim() + '\n');
180
167
  }
181
168
  }
182
- if (envLocalContent) {
169
+ if (envContent) {
183
170
  try {
184
- const existing = await fs.readFile(envLocalPath, 'utf-8');
185
- await fs.writeFile(envLocalPath, existing.trimEnd() + '\n' + envLocalContent.trim() + '\n');
171
+ const existing = await fs.readFile(envPath, 'utf-8');
172
+ await fs.writeFile(envPath, existing.trimEnd() + '\n' + envContent.trim() + '\n');
186
173
  }
187
174
  catch {
188
- await fs.writeFile(envLocalPath, envLocalContent.trim() + '\n');
175
+ await fs.writeFile(envPath, envContent.trim() + '\n');
189
176
  }
190
177
  }
191
178
  }
@@ -14,36 +14,36 @@ const integrationNames = {
14
14
  sentry: 'Sentry',
15
15
  };
16
16
  export async function generateReadme(projectPath, config) {
17
- let content = `# ${config.name}
18
-
19
- A Next.js application scaffolded with [create-loadout](https://github.com/your-org/create-loadout).
20
-
21
- ## Getting Started
22
-
23
- 1. Install dependencies:
24
- \`\`\`bash
25
- npm install
26
- \`\`\`
27
-
28
- 2. Copy the environment file and configure your API keys:
29
- \`\`\`bash
30
- cp .env.example .env.local
31
- \`\`\`
32
-
33
- 3. Start the development server:
34
- \`\`\`bash
35
- npm run dev
36
- \`\`\`
37
-
38
- 4. Open [http://localhost:3000](http://localhost:3000) in your browser.
39
-
40
- ## Tech Stack
41
-
42
- - [Next.js](https://nextjs.org/) - React framework
43
- - [TypeScript](https://www.typescriptlang.org/) - Type safety
44
- - [Tailwind CSS](https://tailwindcss.com/) - Styling
45
- - [shadcn/ui](https://ui.shadcn.com/) - UI components
46
- - [Zod](https://zod.dev/) - Schema validation
17
+ let content = `# ${config.name}
18
+
19
+ A Next.js application scaffolded with [create-loadout](https://github.com/your-org/create-loadout).
20
+
21
+ ## Getting Started
22
+
23
+ 1. Install dependencies:
24
+ \`\`\`bash
25
+ npm install
26
+ \`\`\`
27
+
28
+ 2. Copy the environment file and configure your API keys:
29
+ \`\`\`bash
30
+ cp .env.example .env
31
+ \`\`\`
32
+
33
+ 3. Start the development server:
34
+ \`\`\`bash
35
+ npm run dev
36
+ \`\`\`
37
+
38
+ 4. Open [http://localhost:3000](http://localhost:3000) in your browser.
39
+
40
+ ## Tech Stack
41
+
42
+ - [Next.js](https://nextjs.org/) - React framework
43
+ - [TypeScript](https://www.typescriptlang.org/) - Type safety
44
+ - [Tailwind CSS](https://tailwindcss.com/) - Styling
45
+ - [shadcn/ui](https://ui.shadcn.com/) - UI components
46
+ - [Zod](https://zod.dev/) - Schema validation
47
47
  `;
48
48
  if (config.integrations.length > 0) {
49
49
  content += `\n### Integrations\n\n`;
@@ -51,111 +51,114 @@ A Next.js application scaffolded with [create-loadout](https://github.com/your-o
51
51
  content += `- ${integrationNames[id]}\n`;
52
52
  }
53
53
  }
54
- content += `
55
- ## Scripts
56
-
57
- \`\`\`bash
58
- npm run dev # Start development server
59
- npm run build # Build for production
60
- npm run start # Start production server
61
- npm run lint # Run ESLint
62
- \`\`\`
54
+ content += `
55
+ ## Scripts
56
+
57
+ \`\`\`bash
58
+ npm run dev # Start development server
59
+ npm run build # Build for production
60
+ npm run start # Start production server
61
+ npm run lint # Run ESLint
62
+ \`\`\`
63
63
  `;
64
64
  if (config.integrations.includes('neon-drizzle')) {
65
- content += `
66
- ### Database
67
-
68
- \`\`\`bash
69
- npm run db:generate # Generate migrations
70
- npm run db:migrate # Run migrations
71
- npm run db:studio # Open Drizzle Studio
72
- \`\`\`
65
+ content += `
66
+ ### Database
67
+
68
+ \`\`\`bash
69
+ npm run db:generate # Generate migrations
70
+ npm run db:migrate # Run migrations
71
+ npm run db:studio # Open Drizzle Studio
72
+ \`\`\`
73
73
  `;
74
74
  }
75
75
  if (config.integrations.includes('inngest')) {
76
- content += `
77
- ### Background Jobs
78
-
79
- \`\`\`bash
80
- npm run inngest:dev # Start Inngest dev server
81
- \`\`\`
76
+ content += `
77
+ ### Background Jobs
78
+
79
+ \`\`\`bash
80
+ npm run inngest:dev # Start Inngest dev server
81
+ \`\`\`
82
82
  `;
83
83
  }
84
- content += `
85
- ## Project Structure
86
-
87
- \`\`\`
88
- ├── app/ # Next.js App Router
89
- ├── components/ # React components
90
- ├── lib/ # Utilities and clients
91
- ├── services/ # Business logic
84
+ content += `
85
+ ## Project Structure
86
+
87
+ \`\`\`
88
+ ├── app/ # Next.js App Router
89
+ ├── components/ # React components
90
+ ├── lib/ # Utilities and clients
91
+ ├── services/ # Business logic
92
92
  `;
93
93
  if (config.integrations.includes('resend') || config.integrations.includes('postmark')) {
94
94
  content += `├── emails/ # Email templates\n`;
95
95
  }
96
- content += `└── public/ # Static assets
97
- \`\`\`
98
-
99
- ## Environment Variables
100
-
101
- See \`.env.example\` for all required environment variables.
102
-
103
- ## Learn More
104
-
105
- - [Next.js Documentation](https://nextjs.org/docs)
106
- - [CLAUDE.md](./CLAUDE.md) - AI assistant context file
96
+ content += `└── public/ # Static assets
97
+ \`\`\`
98
+
99
+ ## Environment Variables
100
+
101
+ See \`.env.example\` for all required environment variables.
102
+
103
+ ## Learn More
104
+
105
+ - [Next.js Documentation](https://nextjs.org/docs)
106
+ - [CLAUDE.md](./CLAUDE.md) - AI assistant context file
107
107
  `;
108
108
  await fs.writeFile(path.join(projectPath, 'README.md'), content);
109
109
  }
110
110
  export async function generateGitignore(projectPath) {
111
- const content = `# Dependencies
112
- node_modules/
113
- .pnp/
114
- .pnp.js
115
-
116
- # Build
117
- .next/
118
- out/
119
- build/
120
- dist/
121
-
122
- # Environment
123
- .env
124
- .env.local
125
- .env.development.local
126
- .env.test.local
127
- .env.production.local
128
-
129
- # Testing
130
- coverage/
131
-
132
- # IDE
133
- .vscode/
134
- .idea/
135
- *.swp
136
- *.swo
137
-
138
- # OS
139
- .DS_Store
140
- Thumbs.db
141
-
142
- # Debug
143
- npm-debug.log*
144
- yarn-debug.log*
145
- yarn-error.log*
146
-
147
- # Vercel
148
- .vercel
149
-
150
- # TypeScript
151
- *.tsbuildinfo
152
- next-env.d.ts
153
-
154
- # Drizzle
155
- drizzle/
156
-
157
- # Sentry
158
- .sentryclirc
111
+ const content = `# Dependencies
112
+ node_modules/
113
+ .pnp/
114
+ .pnp.js
115
+
116
+ # Build
117
+ .next/
118
+ out/
119
+ build/
120
+ dist/
121
+
122
+ # Environment
123
+ .env
124
+ .env.local
125
+ .env.development.local
126
+ .env.test.local
127
+ .env.production.local
128
+
129
+ # Testing
130
+ coverage/
131
+
132
+ # IDE
133
+ .vscode/
134
+ .idea/
135
+ *.swp
136
+ *.swo
137
+
138
+ # Claude Code
139
+ .claude/
140
+
141
+ # OS
142
+ .DS_Store
143
+ Thumbs.db
144
+
145
+ # Debug
146
+ npm-debug.log*
147
+ yarn-debug.log*
148
+ yarn-error.log*
149
+
150
+ # Vercel
151
+ .vercel
152
+
153
+ # TypeScript
154
+ *.tsbuildinfo
155
+ next-env.d.ts
156
+
157
+ # Drizzle
158
+ drizzle/
159
+
160
+ # Sentry
161
+ .sentryclirc
159
162
  `;
160
163
  await fs.writeFile(path.join(projectPath, '.gitignore'), content);
161
164
  }
@@ -3,6 +3,10 @@ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
3
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
4
  import { z } from 'zod';
5
5
  import path from 'path';
6
+ const nodeBinDir = path.dirname(process.execPath);
7
+ if (!process.env.PATH?.includes(nodeBinDir)) {
8
+ process.env.PATH = `${nodeBinDir}:${process.env.PATH || ''}`;
9
+ }
6
10
  import { createProject, addIntegrations } from './engine.js';
7
11
  import { validateProjectConfig, validateIntegrationSelection, } from './validate.js';
8
12
  import { listIntegrations } from './metadata.js';