vibe-now 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/PLAN.md +54 -0
- package/README.md +94 -0
- package/index.js +18 -0
- package/lib/packages.js +195 -0
- package/package.json +24 -0
- package/plopfile.js +190 -0
- package/templates/AGENTS.md.hbs +45 -0
- package/templates/README.md.hbs +28 -0
package/PLAN.md
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Custom CLI Wizard Plan (Plop.js + Next.js Stack)
|
|
2
|
+
|
|
3
|
+
This project has been successfully implemented and released as **Vibe Now**.
|
|
4
|
+
|
|
5
|
+
## 1. Project Overview
|
|
6
|
+
A custom CLI (`vibe-now`) that:
|
|
7
|
+
- Prompts for project name with validation.
|
|
8
|
+
- Dynamically generates interactive prompts group by category.
|
|
9
|
+
- Orchestrates project creation and dependency installation using a configuration-driven approach.
|
|
10
|
+
- Generates dynamic documentation (AGENTS.md and README.md).
|
|
11
|
+
|
|
12
|
+
## 2. Core Dependencies
|
|
13
|
+
- **plop**: Generator framework.
|
|
14
|
+
- **inquirer**: Interactive prompts.
|
|
15
|
+
- **execa**: Process execution.
|
|
16
|
+
- **ora**: Premium progress spinners.
|
|
17
|
+
- **handlebars**: Template engine for file generation.
|
|
18
|
+
|
|
19
|
+
## 3. Configuration-Driven Architecture (DRY & Scalable)
|
|
20
|
+
The CLI uses a `PACKAGE_GROUPS` array in `lib/packages.js`. Adding a new package or category is fully decoupled from the core logic.
|
|
21
|
+
|
|
22
|
+
## 4. Implementation Details & Commands
|
|
23
|
+
|
|
24
|
+
### Base Next.js Setup
|
|
25
|
+
- **Command**: `npx create-next-app@latest`
|
|
26
|
+
|
|
27
|
+
### Features Implemented
|
|
28
|
+
- **AI SDK**: Vercel AI SDK & OpenRouter.
|
|
29
|
+
- **Linters**: ESLint/Prettier and Biome selection.
|
|
30
|
+
- **UI & Helpers**: shadcn/ui, nuqs, hook-form, dayjs, lodash.
|
|
31
|
+
- **Database**: Supabase & Drizzle ORM.
|
|
32
|
+
- **Payments**: Stripe & Polar.sh.
|
|
33
|
+
- **Authentication**: Better Auth.
|
|
34
|
+
|
|
35
|
+
## 5. Implementation Status
|
|
36
|
+
|
|
37
|
+
- [x] **Phase 1: Environment Setup** - Completed with ESM support and binary linking.
|
|
38
|
+
- [x] **Phase 2: Configuration Hub** - Scalable group-based configuration implemented.
|
|
39
|
+
- [x] **Phase 3: CLI Entry Point** - `index.js` and `plopfile.js` logic finalized.
|
|
40
|
+
- [x] **Phase 4: Premium UI** - **Ora** spinners integrated for all major steps.
|
|
41
|
+
- [x] **Phase 5: Smart Validation** - URL-safe project names and empty directory checks.
|
|
42
|
+
- [x] **Phase 6: Dynamic Documentation** - Handlebars-based `README.md` and `AGENTS.md` generation.
|
|
43
|
+
|
|
44
|
+
## 6. Project Structure
|
|
45
|
+
```text
|
|
46
|
+
vibe-cli/
|
|
47
|
+
├── lib/
|
|
48
|
+
│ └── packages.js # Centralized configuration
|
|
49
|
+
├── templates/ # HBS templates (README, AGENTS)
|
|
50
|
+
├── index.js # CLI Entry point
|
|
51
|
+
├── plopfile.js # Plop logic & Action orchestrator
|
|
52
|
+
├── package.json
|
|
53
|
+
└── README.md
|
|
54
|
+
```
|
package/README.md
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# Vibe Now 🌌
|
|
2
|
+
|
|
3
|
+
A premium, interactive CLI wizard for scaffolding modern Next.js applications with a perfectly curated stack. Stop running manual `npm install` commands and start building within seconds.
|
|
4
|
+
|
|
5
|
+
## ✨ Features
|
|
6
|
+
|
|
7
|
+
- **Interactive Wizard**: A beautiful CLI experience powered by Plop.js and Inquirer.
|
|
8
|
+
- **World-Class Feedback**: Engaged progress tracking with **Ora** spinners for a premium feel.
|
|
9
|
+
- **Smart Scaffolding**: Automatically orchestrates `create-next-app` and library-specific initializations (like `shadcn init` and `biome init`).
|
|
10
|
+
- **Dynamic Documentation**: Automatically generates a project-specific `README.md` and a comprehensive `AGENTS.md` to guide AI assistants (Cursor, Claude) on your stack and standards.
|
|
11
|
+
- **Template System**: Powered by Handlebars templates in the `templates/` directory for highly customized project initialization.
|
|
12
|
+
- **Safety First**: Project name validation and directory check to prevent accidental overwrites.
|
|
13
|
+
|
|
14
|
+
## 🛠️ The Curated Stack
|
|
15
|
+
|
|
16
|
+
### Base
|
|
17
|
+
- **Framework**: Next.js (App Router, TypeScript, Tailwind CSS v4)
|
|
18
|
+
|
|
19
|
+
### Optional Libraries
|
|
20
|
+
- **State**: Zustand
|
|
21
|
+
- **Validation**: Zod
|
|
22
|
+
- **Data Fetching**: TanStack React Query
|
|
23
|
+
- **UI & Components**: shadcn/ui (Initializes & adds all components automatically)
|
|
24
|
+
- **Database & ORM**: Supabase JS client, Drizzle ORM (Postgres + Drizzle Kit)
|
|
25
|
+
- **Authentication**: Better Auth
|
|
26
|
+
- **Email**: Resend
|
|
27
|
+
- **Payments**: Mutually exclusive selection between **Stripe** and **Polar.sh**
|
|
28
|
+
- **AI SDK**: Vercel AI SDK & OpenRouter Provider support
|
|
29
|
+
- **Linting & Formatting**: Choose between **ESLint + Prettier** or **Biome** (High speed)
|
|
30
|
+
- **UI Helpers**: nuqs, React Hook Form, Day.js, Lodash
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## 🚀 Quick Start
|
|
35
|
+
|
|
36
|
+
Launch the wizard without installation:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npx vibe-now
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## 🛠️ Local Development
|
|
45
|
+
|
|
46
|
+
Clone the repo to customize the logic or add your own favorite packages.
|
|
47
|
+
|
|
48
|
+
### 1. Installation
|
|
49
|
+
```bash
|
|
50
|
+
git clone https://github.com/mosster/vibe-now.git
|
|
51
|
+
cd vibe-now
|
|
52
|
+
npm install
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### 2. Linking for Development
|
|
56
|
+
To use the `vibe-now` command globally on your machine while developing:
|
|
57
|
+
```bash
|
|
58
|
+
npm link
|
|
59
|
+
```
|
|
60
|
+
Now you can run `vibe-now` from any directory!
|
|
61
|
+
|
|
62
|
+
### 3. Adding New Packages
|
|
63
|
+
The CLI uses a group-based configuration for easy maintenance. To add a new library:
|
|
64
|
+
1. Open `lib/packages.js`.
|
|
65
|
+
2. Find the relevant `PACKAGE_GROUPS` entry or add a new one.
|
|
66
|
+
|
|
67
|
+
```javascript
|
|
68
|
+
{
|
|
69
|
+
category: 'My New Category',
|
|
70
|
+
items: [
|
|
71
|
+
{
|
|
72
|
+
id: 'myPackage',
|
|
73
|
+
name: 'Cool Library',
|
|
74
|
+
install: ['cool-lib-package'],
|
|
75
|
+
default: false
|
|
76
|
+
}
|
|
77
|
+
]
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## 📦 Publishing
|
|
84
|
+
|
|
85
|
+
When you're ready to release a new version:
|
|
86
|
+
|
|
87
|
+
1. Update version: `npm version patch` (or minor/major)
|
|
88
|
+
2. Login: `npm login`
|
|
89
|
+
3. Publish: `npm publish --access public`
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## 📜 License
|
|
94
|
+
MIT © [Ed Moss](https://github.com/mosster)
|
package/index.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import minimist from 'minimist';
|
|
6
|
+
import { Plop, run } from 'plop';
|
|
7
|
+
|
|
8
|
+
const args = process.argv.slice(2);
|
|
9
|
+
const argv = minimist(args);
|
|
10
|
+
|
|
11
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
12
|
+
|
|
13
|
+
Plop.prepare({
|
|
14
|
+
cwd: argv.cwd,
|
|
15
|
+
configPath: path.join(__dirname, 'plopfile.js'),
|
|
16
|
+
preload: argv.preload || [],
|
|
17
|
+
completion: argv.completion,
|
|
18
|
+
}, env => Plop.execute(env, run));
|
package/lib/packages.js
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration for all optional packages and their install/init commands.
|
|
3
|
+
* Grouped by category for better UX.
|
|
4
|
+
*/
|
|
5
|
+
export const PACKAGE_GROUPS = [
|
|
6
|
+
{
|
|
7
|
+
category: 'State Management',
|
|
8
|
+
items: [
|
|
9
|
+
{
|
|
10
|
+
id: 'zustand',
|
|
11
|
+
name: 'Zustand',
|
|
12
|
+
install: ['zustand'],
|
|
13
|
+
default: true,
|
|
14
|
+
},
|
|
15
|
+
],
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
category: 'Validation',
|
|
19
|
+
items: [
|
|
20
|
+
{
|
|
21
|
+
id: 'zod',
|
|
22
|
+
name: 'Zod',
|
|
23
|
+
install: ['zod'],
|
|
24
|
+
default: true,
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
category: 'Data Fetching',
|
|
30
|
+
items: [
|
|
31
|
+
{
|
|
32
|
+
id: 'reactQuery',
|
|
33
|
+
name: 'TanStack React Query',
|
|
34
|
+
install: ['@tanstack/react-query'],
|
|
35
|
+
default: true,
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
category: 'UI & Components',
|
|
41
|
+
items: [
|
|
42
|
+
{
|
|
43
|
+
id: 'shadcn',
|
|
44
|
+
name: 'shadcn/ui (Initializes & adds all components)',
|
|
45
|
+
install: [],
|
|
46
|
+
commands: [
|
|
47
|
+
['npx', 'shadcn@latest', 'init', '-y'],
|
|
48
|
+
['npx', 'shadcn@latest', 'add', '--all', '-y'],
|
|
49
|
+
],
|
|
50
|
+
default: true,
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
category: 'Authentication',
|
|
56
|
+
items: [
|
|
57
|
+
{
|
|
58
|
+
id: 'betterAuth',
|
|
59
|
+
name: 'Better Auth',
|
|
60
|
+
install: ['better-auth'],
|
|
61
|
+
default: false,
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
category: 'Database & ORM',
|
|
67
|
+
items: [
|
|
68
|
+
{
|
|
69
|
+
id: 'supabase',
|
|
70
|
+
name: 'Supabase JS client',
|
|
71
|
+
install: ['@supabase/supabase-js'],
|
|
72
|
+
default: true,
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
id: 'drizzle',
|
|
76
|
+
name: 'Drizzle ORM (with Postgres & Kit)',
|
|
77
|
+
install: ['drizzle-orm', 'pg'],
|
|
78
|
+
devInstall: ['drizzle-kit'],
|
|
79
|
+
default: true,
|
|
80
|
+
},
|
|
81
|
+
],
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
category: 'Email',
|
|
85
|
+
items: [
|
|
86
|
+
{
|
|
87
|
+
id: 'resend',
|
|
88
|
+
name: 'Resend',
|
|
89
|
+
install: ['resend'],
|
|
90
|
+
default: false,
|
|
91
|
+
},
|
|
92
|
+
],
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
category: 'Payments (Select One)',
|
|
96
|
+
type: 'list',
|
|
97
|
+
id: 'paymentProvider',
|
|
98
|
+
choices: [
|
|
99
|
+
{ name: 'None', value: 'none' },
|
|
100
|
+
{ name: 'Stripe (Server + Client SDKs)', value: 'stripe' },
|
|
101
|
+
{ name: 'Polar.sh (SDK + Next.js integration)', value: 'polar' },
|
|
102
|
+
],
|
|
103
|
+
default: 'none',
|
|
104
|
+
// Mapping provider value to actual package config
|
|
105
|
+
providerConfig: {
|
|
106
|
+
stripe: {
|
|
107
|
+
name: 'Stripe',
|
|
108
|
+
install: ['stripe', '@stripe/stripe-js', '@stripe/react-stripe-js'],
|
|
109
|
+
},
|
|
110
|
+
polar: {
|
|
111
|
+
name: 'Polar.sh',
|
|
112
|
+
install: ['@polar-sh/sdk', '@polar-sh/nextjs'],
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
category: 'AI SDK',
|
|
118
|
+
items: [
|
|
119
|
+
{
|
|
120
|
+
id: 'aiSdk',
|
|
121
|
+
name: 'Vercel AI SDK',
|
|
122
|
+
install: ['ai'],
|
|
123
|
+
default: false,
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
id: 'openRouter',
|
|
127
|
+
name: 'OpenRouter AI SDK Provider',
|
|
128
|
+
install: ['@openrouter/ai-sdk-provider'],
|
|
129
|
+
default: false,
|
|
130
|
+
},
|
|
131
|
+
],
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
category: 'Linting & Formatting',
|
|
135
|
+
type: 'list',
|
|
136
|
+
id: 'linter',
|
|
137
|
+
choices: [
|
|
138
|
+
{ name: 'None', value: 'none' },
|
|
139
|
+
{ name: 'ESLint + Prettier (Standard)', value: 'eslint' },
|
|
140
|
+
{ name: 'Biome (Fast, All-in-one)', value: 'biome' },
|
|
141
|
+
],
|
|
142
|
+
default: 'eslint',
|
|
143
|
+
providerConfig: {
|
|
144
|
+
eslint: {
|
|
145
|
+
name: 'ESLint + Prettier',
|
|
146
|
+
devInstall: [
|
|
147
|
+
'eslint',
|
|
148
|
+
'eslint-config-next',
|
|
149
|
+
'prettier',
|
|
150
|
+
'eslint-config-prettier',
|
|
151
|
+
'eslint-plugin-prettier'
|
|
152
|
+
],
|
|
153
|
+
},
|
|
154
|
+
biome: {
|
|
155
|
+
name: 'Biome',
|
|
156
|
+
devInstall: ['@biomejs/biome'],
|
|
157
|
+
commands: [
|
|
158
|
+
['npx', '@biomejs/biome', 'init']
|
|
159
|
+
],
|
|
160
|
+
},
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
category: 'UI Helpers',
|
|
165
|
+
items: [
|
|
166
|
+
{
|
|
167
|
+
id: 'nuqs',
|
|
168
|
+
name: 'nuqs (Type-safe query params)',
|
|
169
|
+
install: ['nuqs'],
|
|
170
|
+
default: false,
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
id: 'hookForm',
|
|
174
|
+
name: 'React Hook Form',
|
|
175
|
+
install: ['react-hook-form'],
|
|
176
|
+
default: true,
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
id: 'dayjs',
|
|
180
|
+
name: 'Day.js (Date handling)',
|
|
181
|
+
install: ['dayjs'],
|
|
182
|
+
default: false,
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
id: 'lodash',
|
|
186
|
+
name: 'Lodash (Utilities)',
|
|
187
|
+
install: ['lodash'],
|
|
188
|
+
default: false,
|
|
189
|
+
},
|
|
190
|
+
],
|
|
191
|
+
},
|
|
192
|
+
];
|
|
193
|
+
|
|
194
|
+
// Helper to get a flat list of all potential packages for the prompt validation or internal logic if needed
|
|
195
|
+
export const ALL_ITEMS = PACKAGE_GROUPS.flatMap(group => group.items || []);
|
package/package.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "vibe-now",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"main": "index.js",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"vibe-now": "./index.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [],
|
|
13
|
+
"author": "Ed Moss <ed@mossified.com>",
|
|
14
|
+
"license": "ISC",
|
|
15
|
+
"description": "Quickly scaffold a Next.js app with a curated stack",
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"execa": "^8.0.1",
|
|
18
|
+
"fs-extra": "^11.3.3",
|
|
19
|
+
"inquirer": "^9.2.16",
|
|
20
|
+
"minimist": "^1.2.8",
|
|
21
|
+
"ora": "^9.0.0",
|
|
22
|
+
"plop": "^4.0.4"
|
|
23
|
+
}
|
|
24
|
+
}
|
package/plopfile.js
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { execa } from 'execa';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import ora from 'ora';
|
|
6
|
+
import { PACKAGE_GROUPS, ALL_ITEMS } from './lib/packages.js';
|
|
7
|
+
|
|
8
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
|
|
10
|
+
export default function (plop) {
|
|
11
|
+
plop.setGenerator('next-app', {
|
|
12
|
+
description: 'Scaffold a custom Next.js app',
|
|
13
|
+
prompts: [
|
|
14
|
+
{
|
|
15
|
+
type: 'input',
|
|
16
|
+
name: 'projectName',
|
|
17
|
+
message: 'What is your project name?',
|
|
18
|
+
default: 'my-vibe-now',
|
|
19
|
+
validate: (input) => {
|
|
20
|
+
const validName = /^[a-z0-9-_.]+$/.test(input);
|
|
21
|
+
if (!validName) {
|
|
22
|
+
return 'Project name must be url-safe (a-z, 0-9, -, _, .)';
|
|
23
|
+
}
|
|
24
|
+
if (input === '.' && fs.readdirSync(process.cwd()).length > 0) {
|
|
25
|
+
return 'Current directory is not empty. Please provide a different project name.';
|
|
26
|
+
}
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
...PACKAGE_GROUPS.flatMap((group) => {
|
|
31
|
+
if (group.type === 'list') {
|
|
32
|
+
return [{
|
|
33
|
+
type: 'list',
|
|
34
|
+
name: group.id,
|
|
35
|
+
message: `${group.category}:`,
|
|
36
|
+
choices: group.choices,
|
|
37
|
+
default: group.default,
|
|
38
|
+
}];
|
|
39
|
+
}
|
|
40
|
+
return group.items.map(item => ({
|
|
41
|
+
type: 'confirm',
|
|
42
|
+
name: item.id,
|
|
43
|
+
message: `${group.category}: Include ${item.name}?`,
|
|
44
|
+
default: item.default,
|
|
45
|
+
}));
|
|
46
|
+
}),
|
|
47
|
+
],
|
|
48
|
+
actions: (data) => {
|
|
49
|
+
const actions = [];
|
|
50
|
+
const projectPath = path.join(process.cwd(), data.projectName);
|
|
51
|
+
|
|
52
|
+
// 1. Create Base Next.js App
|
|
53
|
+
actions.push({
|
|
54
|
+
type: 'customSync',
|
|
55
|
+
async action(answers) {
|
|
56
|
+
const spinner = ora({
|
|
57
|
+
text: `Creating base Next.js app in ${answers.projectName}...`,
|
|
58
|
+
color: 'cyan',
|
|
59
|
+
}).start();
|
|
60
|
+
|
|
61
|
+
try {
|
|
62
|
+
await execa('npx', [
|
|
63
|
+
'create-next-app@latest',
|
|
64
|
+
answers.projectName,
|
|
65
|
+
'--ts',
|
|
66
|
+
'--tailwind',
|
|
67
|
+
'--app',
|
|
68
|
+
'--eslint',
|
|
69
|
+
'--import-alias',
|
|
70
|
+
'@/*',
|
|
71
|
+
'--yes',
|
|
72
|
+
]);
|
|
73
|
+
spinner.succeed('Base Next.js app created!');
|
|
74
|
+
return 'Base Next.js app created';
|
|
75
|
+
} catch (error) {
|
|
76
|
+
spinner.fail('Failed to create Next.js app');
|
|
77
|
+
throw error;
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// 2. Install selected packages & run commands
|
|
83
|
+
actions.push({
|
|
84
|
+
type: 'customSync',
|
|
85
|
+
async action(answers) {
|
|
86
|
+
// Collect standard selections
|
|
87
|
+
const selectedPackages = ALL_ITEMS.filter(item => answers[item.id]);
|
|
88
|
+
|
|
89
|
+
// Collect list-based selections (e.g. Payments)
|
|
90
|
+
PACKAGE_GROUPS.filter(g => g.type === 'list').forEach(group => {
|
|
91
|
+
const choice = answers[group.id];
|
|
92
|
+
if (choice && choice !== 'none' && group.providerConfig[choice]) {
|
|
93
|
+
selectedPackages.push({
|
|
94
|
+
...group.providerConfig[choice],
|
|
95
|
+
id: `${group.id}_${choice}` // unique internal id
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
if (selectedPackages.length === 0) return 'No additional packages selected';
|
|
101
|
+
|
|
102
|
+
const pkgNames = selectedPackages.map(p => p.name).join(', ');
|
|
103
|
+
const installSpinner = ora({
|
|
104
|
+
text: `Installing: ${pkgNames}...`,
|
|
105
|
+
color: 'magenta',
|
|
106
|
+
}).start();
|
|
107
|
+
|
|
108
|
+
const installCmds = selectedPackages.flatMap(pkg => pkg.install || []);
|
|
109
|
+
const devInstallCmds = selectedPackages.flatMap(pkg => pkg.devInstall || []);
|
|
110
|
+
|
|
111
|
+
try {
|
|
112
|
+
// Standard Installs
|
|
113
|
+
if (installCmds.length > 0) {
|
|
114
|
+
await execa('npm', ['install', ...installCmds], { cwd: projectPath });
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Dev Installs
|
|
118
|
+
if (devInstallCmds.length > 0) {
|
|
119
|
+
installSpinner.text = 'Installing devDependencies...';
|
|
120
|
+
await execa('npm', ['install', '-D', ...devInstallCmds], { cwd: projectPath });
|
|
121
|
+
}
|
|
122
|
+
installSpinner.succeed('Packages installed successfully!');
|
|
123
|
+
} catch (error) {
|
|
124
|
+
installSpinner.fail('Package installation failed');
|
|
125
|
+
throw error;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Run initialization commands (like shadcn init)
|
|
129
|
+
for (const pkg of selectedPackages) {
|
|
130
|
+
if (pkg.commands && pkg.commands.length > 0) {
|
|
131
|
+
console.log(`\n✨ Finalizing ${pkg.name}...`);
|
|
132
|
+
|
|
133
|
+
try {
|
|
134
|
+
for (const cmd of pkg.commands) {
|
|
135
|
+
const [command, ...args] = cmd;
|
|
136
|
+
// Use 'inherit' so user can see/interact with shadow-style commands
|
|
137
|
+
await execa(command, args, {
|
|
138
|
+
cwd: projectPath,
|
|
139
|
+
stdio: 'inherit'
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
} catch (error) {
|
|
143
|
+
console.error(`❌ Failed to initialize ${pkg.name}`);
|
|
144
|
+
throw error;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// 3. Generate README and AGENTS files
|
|
150
|
+
const docSpinner = ora({
|
|
151
|
+
text: 'Generating custom documentation and agent rules...',
|
|
152
|
+
color: 'blue',
|
|
153
|
+
}).start();
|
|
154
|
+
|
|
155
|
+
try {
|
|
156
|
+
const templateData = {
|
|
157
|
+
projectName: answers.projectName === '.' ? path.basename(projectPath) : answers.projectName,
|
|
158
|
+
selectedPackages
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
const readmeTmpl = fs.readFileSync(path.join(__dirname, 'templates/README.md.hbs'), 'utf8');
|
|
162
|
+
const agentsTmpl = fs.readFileSync(path.join(__dirname, 'templates/AGENTS.md.hbs'), 'utf8');
|
|
163
|
+
|
|
164
|
+
const renderedReadme = plop.renderString(readmeTmpl, templateData);
|
|
165
|
+
const renderedAgents = plop.renderString(agentsTmpl, templateData);
|
|
166
|
+
|
|
167
|
+
fs.writeFileSync(path.join(projectPath, 'README.md'), renderedReadme);
|
|
168
|
+
fs.writeFileSync(path.join(projectPath, 'AGENTS.md'), renderedAgents);
|
|
169
|
+
|
|
170
|
+
docSpinner.succeed('Documentation and AGENTS.md generated!');
|
|
171
|
+
} catch (error) {
|
|
172
|
+
docSpinner.fail('Failed to generate documentation files');
|
|
173
|
+
console.error(error);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return 'All selected packages installed and initialized';
|
|
177
|
+
},
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
actions.push('\n🌌 Vibe Check: COMPLETE. Your stack is ready.');
|
|
181
|
+
actions.push(`🚀 Next steps:\n cd ${data.projectName}\n npm run dev\n`);
|
|
182
|
+
|
|
183
|
+
return actions;
|
|
184
|
+
},
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
plop.setActionType('customSync', async (answers, config) => {
|
|
188
|
+
return await config.action(answers);
|
|
189
|
+
});
|
|
190
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Agent Guidance & Project Standards
|
|
2
|
+
|
|
3
|
+
This document serves as a reference for AI Coding Assistants (Cursor, Claude, etc.) to ensure consistency and
|
|
4
|
+
high-quality engineering standards.
|
|
5
|
+
|
|
6
|
+
## 🏗️ Architecture Overview
|
|
7
|
+
- **Framework**: Next.js 15+ (App Router)
|
|
8
|
+
- **Language**: TypeScript (Strict mode)
|
|
9
|
+
- **Styling**: Tailwind CSS (Utility-first)
|
|
10
|
+
- **Components**: shadcn/ui (Radix UI primitives)
|
|
11
|
+
|
|
12
|
+
## 🛠️ Tech Stack Consistency
|
|
13
|
+
{{#each selectedPackages}}
|
|
14
|
+
- **{{this.name}}**: Use standard patterns as defined in official documentation.
|
|
15
|
+
{{/each}}
|
|
16
|
+
|
|
17
|
+
## 🤖 AI Coding Rules
|
|
18
|
+
|
|
19
|
+
### 1. General Principles
|
|
20
|
+
- **Conciseness**: Write clean, modular code. Avoid bloated components.
|
|
21
|
+
- **Type Safety**: Avoid `any`. Use Zod for runtime validation and TypeScript for compile-time safety.
|
|
22
|
+
- **Modern Syntax**: Prefer functional components, hooks, and async/await.
|
|
23
|
+
|
|
24
|
+
### 2. Component Standards
|
|
25
|
+
- Small, focused components in `components/`.
|
|
26
|
+
- UI primitives in `components/ui/`.
|
|
27
|
+
- Use `cn()` utility for conditional classes.
|
|
28
|
+
- Favor Server Components by default; use `'use client'` sparingly and only at the leaf nodes.
|
|
29
|
+
|
|
30
|
+
### 3. State & Data
|
|
31
|
+
- **Server State**: Use TanStack React Query or Server Actions.
|
|
32
|
+
- **Client State**: Use Zustand for global state only if necessary.
|
|
33
|
+
- **Forms**: Use React Hook Form with Zod resolvers.
|
|
34
|
+
|
|
35
|
+
### 4. Performance & SEO
|
|
36
|
+
- Use Next.js `
|
|
37
|
+
<Image />` for optimized assets.
|
|
38
|
+
- Ensure proper semantic HTML and ARIA labels.
|
|
39
|
+
- Implement metadata for every page.
|
|
40
|
+
|
|
41
|
+
## 📝 Best Practices
|
|
42
|
+
- Follow the "Rule of Three": If you repeat logic three times, abstract it.
|
|
43
|
+
- Keep business logic separate from UI logic where possible.
|
|
44
|
+
- Use meaningful variable and function names.
|
|
45
|
+
- Always include error handling for data fetching and mutations.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# {{projectName}}
|
|
2
|
+
|
|
3
|
+
This project was bootstrapped with [vibe-now](https://github.com/mosster/vibe-now).
|
|
4
|
+
|
|
5
|
+
## 🚀 Installed Stack
|
|
6
|
+
|
|
7
|
+
### Core
|
|
8
|
+
- **Next.js** (App Router)
|
|
9
|
+
- **TypeScript**
|
|
10
|
+
- **Tailwind CSS** (v4)
|
|
11
|
+
|
|
12
|
+
### Selected Features
|
|
13
|
+
{{#each selectedPackages}}
|
|
14
|
+
- **{{this.name}}**
|
|
15
|
+
{{/each}}
|
|
16
|
+
|
|
17
|
+
## 🛠️ Getting Started
|
|
18
|
+
|
|
19
|
+
First, run the development server:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm run dev
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
Generated by quick-vibe
|