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 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));
@@ -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