@codebakers/cli 1.2.1 → 1.3.1

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.
@@ -4,71 +4,40 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync, copyFileSync } from
4
4
  import { join } from 'path';
5
5
  import { homedir } from 'os';
6
6
 
7
- // Enhanced hook with pre-flight checks and self-review automation
7
+ // Enhanced hook with visible feedback and concise instructions
8
8
  const HOOK_TEMPLATE = {
9
9
  hooks: {
10
10
  UserPromptSubmit: [
11
11
  {
12
12
  type: "command",
13
- command: `echo '════════════════════════════════════════════════════════════════
14
- 🚀 CODEBAKERS ZERO-FRICTION PROTOCOL
15
- ════════════════════════════════════════════════════════════════
16
-
17
- AUTOMATIC WORKFLOW - Execute silently, output only the acknowledgment:
18
-
19
- ▸ PHASE 1: CONTEXT LOAD
20
- Read these files (if they exist):
21
- - CLAUDE.md → Router & module instructions
22
- - PRD.md → What we are building (requirements!)
23
- - PROJECT-CONTEXT.md → Codebase knowledge
24
- - PROJECT-STATE.md → What is in progress
25
- - DECISIONS.md → Past architectural choices
26
-
27
- ▸ PHASE 2: PRE-FLIGHT CHECK (before writing code)
28
- □ What existing code does this touch?
29
- □ Is similar code in the codebase? (copy that pattern!)
30
- □ Whats the data model?
31
- □ What are the error cases?
32
- □ Is someone else working on this? (check In Progress)
33
-
34
- If PROJECT-CONTEXT.md is empty/stale, SCAN PROJECT FIRST:
35
- - Read package.json
36
- - Check file structure
37
- - Find existing patterns
38
- - Update PROJECT-CONTEXT.md
39
-
40
- ▸ PHASE 3: ACKNOWLEDGE & EXECUTE
41
- Output: 📋 CodeBakers | [Type] | Modules: [list]
42
- Then: Follow patterns from .claude/ folder EXACTLY
43
-
44
- ▸ PHASE 4: SELF-REVIEW (before saying done)
45
- □ TypeScript compiles? (npx tsc --noEmit)
46
- □ Imports resolve?
47
- □ Error handling exists?
48
- □ Matches existing patterns?
49
- □ Tests written?
50
-
51
- If ANY fails → FIX before responding
52
-
53
- ▸ PHASE 5: UPDATE STATE
54
- - Update PROJECT-STATE.md (move to Completed)
55
- - Add to DECISIONS.md if architectural choice made
56
-
57
- ════════════════════════════════════════════════════════════════
58
- 🔄 MULTI-AGENT MODE
59
- ════════════════════════════════════════════════════════════════
60
- - Check PROJECT-STATE.md "In Progress" - dont duplicate work
61
- - Add YOUR task to In Progress when starting
62
- - If conflict → STOP and ask user
63
-
64
- ════════════════════════════════════════════════════════════════
65
- 💡 REMEMBER: Check existing code FIRST. Copy patterns. Validate.
66
- ════════════════════════════════════════════════════════════════'`
13
+ command: `echo '[CodeBakers] Loading project context...'`
14
+ }
15
+ ],
16
+ PostToolUse: [
17
+ {
18
+ type: "command",
19
+ matcher: "Write|Edit",
20
+ command: `echo '[CodeBakers] Code written - remember to self-review before marking done'`
67
21
  }
68
22
  ]
69
23
  }
70
24
  };
71
25
 
26
+ // Instructions that get injected into the system prompt
27
+ const CODEBAKERS_INSTRUCTIONS = `
28
+ <user-prompt-submit-hook>
29
+ [CodeBakers] Active - Follow these steps for EVERY request:
30
+
31
+ 1. CONTEXT: Read CLAUDE.md, PROJECT-CONTEXT.md, PROJECT-STATE.md
32
+ 2. PRE-FLIGHT: Check existing code patterns before writing new code
33
+ 3. EXECUTE: Use patterns from .claude/ folder
34
+ 4. SELF-REVIEW: Verify TypeScript compiles, imports resolve, error handling exists
35
+ 5. UPDATE: Mark tasks complete in PROJECT-STATE.md
36
+
37
+ Output format: "[CodeBakers] Building [feature] using [patterns]"
38
+ </user-prompt-submit-hook>
39
+ `;
40
+
72
41
  /**
73
42
  * Install the CodeBakers hook into ~/.claude/settings.json
74
43
  */
@@ -117,23 +86,25 @@ export async function installHook(): Promise<void> {
117
86
  }
118
87
  }
119
88
 
120
- // Merge hook into settings
89
+ // Merge hooks into settings
121
90
  settings.hooks = settings.hooks || {};
122
91
  (settings.hooks as Record<string, unknown>).UserPromptSubmit = HOOK_TEMPLATE.hooks.UserPromptSubmit;
92
+ (settings.hooks as Record<string, unknown>).PostToolUse = HOOK_TEMPLATE.hooks.PostToolUse;
123
93
 
124
94
  // Write back
125
95
  writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
126
96
 
127
97
  spinner.succeed('Hook installed successfully!');
128
98
 
129
- console.log(chalk.white('\n What happens automatically on EVERY message:\n'));
130
- console.log(chalk.gray(' Loads project context (CLAUDE.md, PROJECT-CONTEXT.md)'));
131
- console.log(chalk.gray(' Checks what\'s in progress (PROJECT-STATE.md)'));
132
- console.log(chalk.gray(' ✓ Runs pre-flight checks before coding'));
133
- console.log(chalk.gray(' Copies existing patterns from your codebase'));
134
- console.log(chalk.gray(' ✓ Self-reviews code before outputting'));
135
- console.log(chalk.gray(' ✓ Updates project state when done'));
136
- console.log(chalk.gray(' ✓ Logs architectural decisions\n'));
99
+ console.log(chalk.white('\n You\'ll see [CodeBakers] feedback in terminal:\n'));
100
+ console.log(chalk.cyan(' [CodeBakers] Loading project context...'));
101
+ console.log(chalk.cyan(' [CodeBakers] Code written - remember to self-review\n'));
102
+
103
+ console.log(chalk.white(' What happens automatically:\n'));
104
+ console.log(chalk.gray(' ✓ Loads project context before every response'));
105
+ console.log(chalk.gray(' ✓ Pre-flight checks before writing code'));
106
+ console.log(chalk.gray(' ✓ Self-review reminders after code changes'));
107
+ console.log(chalk.gray(' ✓ Pattern-based development from .claude/ folder\n'));
137
108
 
138
109
  console.log(chalk.yellow(' ⚠️ Restart Claude Code for changes to take effect.\n'));
139
110
  } catch (error) {
@@ -162,13 +133,18 @@ export async function uninstallHook(): Promise<void> {
162
133
 
163
134
  const settings = JSON.parse(readFileSync(settingsPath, 'utf-8'));
164
135
 
165
- if (!settings.hooks?.UserPromptSubmit) {
166
- spinner.info('No UserPromptSubmit hook found. Nothing to remove.');
136
+ if (!settings.hooks?.UserPromptSubmit && !settings.hooks?.PostToolUse) {
137
+ spinner.info('No CodeBakers hooks found. Nothing to remove.');
167
138
  return;
168
139
  }
169
140
 
170
- // Remove the hook
171
- delete settings.hooks.UserPromptSubmit;
141
+ // Remove both hooks
142
+ if (settings.hooks?.UserPromptSubmit) {
143
+ delete settings.hooks.UserPromptSubmit;
144
+ }
145
+ if (settings.hooks?.PostToolUse) {
146
+ delete settings.hooks.PostToolUse;
147
+ }
172
148
 
173
149
  // Clean up empty hooks object
174
150
  if (Object.keys(settings.hooks).length === 0) {
@@ -0,0 +1,270 @@
1
+ import chalk from 'chalk';
2
+ import ora from 'ora';
3
+ import { createInterface } from 'readline';
4
+ import { writeFileSync, mkdirSync, existsSync, readdirSync } from 'fs';
5
+ import { join } from 'path';
6
+ import { execSync } from 'child_process';
7
+ import * as templates from '../templates/nextjs-supabase.js';
8
+
9
+ async function prompt(question: string): Promise<string> {
10
+ const rl = createInterface({
11
+ input: process.stdin,
12
+ output: process.stdout,
13
+ });
14
+
15
+ return new Promise((resolve) => {
16
+ rl.question(question, (answer) => {
17
+ rl.close();
18
+ resolve(answer.trim());
19
+ });
20
+ });
21
+ }
22
+
23
+ async function confirm(question: string): Promise<boolean> {
24
+ const answer = await prompt(`${question} (Y/n): `);
25
+ return answer.toLowerCase() !== 'n';
26
+ }
27
+
28
+ /**
29
+ * Scaffold a new project with full structure
30
+ */
31
+ export async function scaffold(): Promise<void> {
32
+ console.log(chalk.blue(`
33
+ ╔═══════════════════════════════════════════════════════════╗
34
+ ║ ║
35
+ ║ ${chalk.bold('CodeBakers Project Scaffolding')} ║
36
+ ║ ║
37
+ ║ Create a production-ready project in seconds ║
38
+ ║ ║
39
+ ╚═══════════════════════════════════════════════════════════╝
40
+ `));
41
+
42
+ const cwd = process.cwd();
43
+ const files = readdirSync(cwd);
44
+ const hasFiles = files.filter(f => !f.startsWith('.')).length > 0;
45
+
46
+ if (hasFiles) {
47
+ console.log(chalk.yellow(' ⚠️ This directory is not empty.\n'));
48
+ const proceed = await confirm(' Continue anyway? (Existing files may be overwritten)');
49
+ if (!proceed) {
50
+ console.log(chalk.gray('\n Run this command in an empty directory.\n'));
51
+ return;
52
+ }
53
+ }
54
+
55
+ // Ask about experience level
56
+ console.log(chalk.white('\n What\'s your experience level?\n'));
57
+ console.log(chalk.gray(' 1. ') + chalk.cyan('Beginner') + chalk.gray(' - New to coding, explain everything'));
58
+ console.log(chalk.gray(' 2. ') + chalk.cyan('Intermediate') + chalk.gray(' - Know some coding, brief explanations'));
59
+ console.log(chalk.gray(' 3. ') + chalk.cyan('Advanced') + chalk.gray(' - Skip explanations, just build\n'));
60
+
61
+ let experienceLevel = '';
62
+ while (!['1', '2', '3'].includes(experienceLevel)) {
63
+ experienceLevel = await prompt(' Enter 1, 2, or 3: ');
64
+ }
65
+
66
+ const isBeginnerMode = experienceLevel === '1';
67
+ const showBriefExplanations = experienceLevel === '2';
68
+
69
+ // Select stack with explanations for beginners
70
+ console.log(chalk.white('\n Select your stack:\n'));
71
+
72
+ if (isBeginnerMode) {
73
+ console.log(chalk.gray(' 1. ') + chalk.cyan('Next.js + Supabase + Drizzle') + chalk.green(' (Recommended)'));
74
+ console.log(chalk.gray(' ') + chalk.dim('Next.js = Framework for building websites with React'));
75
+ console.log(chalk.gray(' ') + chalk.dim('Supabase = Database + user login (like Firebase, but open source)'));
76
+ console.log(chalk.gray(' ') + chalk.dim('Drizzle = Tool to talk to your database safely'));
77
+ console.log('');
78
+ console.log(chalk.gray(' 2. ') + chalk.cyan('Next.js + Prisma') + chalk.gray(' (Coming soon)'));
79
+ console.log(chalk.gray(' ') + chalk.dim('Prisma = Another database tool, more popular but heavier'));
80
+ console.log('');
81
+ console.log(chalk.gray(' 3. ') + chalk.cyan('Express API') + chalk.gray(' (Coming soon)'));
82
+ console.log(chalk.gray(' ') + chalk.dim('Express = Lightweight server, good for APIs without a frontend'));
83
+ console.log('');
84
+ } else {
85
+ console.log(chalk.gray(' 1. ') + chalk.cyan('Next.js + Supabase + Drizzle') + chalk.gray(' (Recommended)'));
86
+ console.log(chalk.gray(' 2. ') + chalk.cyan('Next.js + Prisma') + chalk.gray(' (Coming soon)'));
87
+ console.log(chalk.gray(' 3. ') + chalk.cyan('Express API') + chalk.gray(' (Coming soon)\n'));
88
+ }
89
+
90
+ let stackChoice = '';
91
+ while (!['1', '2', '3'].includes(stackChoice)) {
92
+ stackChoice = await prompt(' Enter 1, 2, or 3: ');
93
+ }
94
+
95
+ if (stackChoice !== '1') {
96
+ console.log(chalk.yellow('\n That stack is coming soon! Using Next.js + Supabase + Drizzle.\n'));
97
+ stackChoice = '1';
98
+ }
99
+
100
+ // Explain what we're about to create for beginners
101
+ if (isBeginnerMode) {
102
+ console.log(chalk.blue('\n ═══════════════════════════════════════════════════════════'));
103
+ console.log(chalk.white.bold(' 📚 What we\'re creating:'));
104
+ console.log(chalk.blue(' ═══════════════════════════════════════════════════════════\n'));
105
+ console.log(chalk.gray(' This will create a complete web application with:'));
106
+ console.log(chalk.gray(' • A website users can visit (Next.js)'));
107
+ console.log(chalk.gray(' • User signup/login system (Supabase Auth)'));
108
+ console.log(chalk.gray(' • A database to store data (PostgreSQL via Supabase)'));
109
+ console.log(chalk.gray(' • Beautiful styling system (Tailwind CSS)'));
110
+ console.log(chalk.gray(' • Type safety to prevent bugs (TypeScript)\n'));
111
+ console.log(chalk.gray(' Think of it like a house:'));
112
+ console.log(chalk.gray(' • Next.js is the structure (walls, roof)'));
113
+ console.log(chalk.gray(' • Supabase is the utilities (electricity, plumbing)'));
114
+ console.log(chalk.gray(' • Tailwind is the interior design (paint, furniture)\n'));
115
+ }
116
+
117
+ // Get project name
118
+ const defaultName = cwd.split(/[\\/]/).pop() || 'my-project';
119
+ const projectName = await prompt(` Project name (${defaultName}): `) || defaultName;
120
+
121
+ console.log(chalk.green(`\n Creating ${projectName} with Next.js + Supabase + Drizzle...\n`));
122
+
123
+ // Create project structure
124
+ const spinner = ora(' Creating project structure...').start();
125
+
126
+ try {
127
+ // Create directories
128
+ const dirs = [
129
+ 'src/app',
130
+ 'src/components',
131
+ 'src/lib/supabase',
132
+ 'src/db',
133
+ 'src/db/migrations',
134
+ 'src/services',
135
+ 'src/types',
136
+ 'public',
137
+ ];
138
+
139
+ for (const dir of dirs) {
140
+ const dirPath = join(cwd, dir);
141
+ if (!existsSync(dirPath)) {
142
+ mkdirSync(dirPath, { recursive: true });
143
+ }
144
+ }
145
+
146
+ spinner.text = ' Writing configuration files...';
147
+
148
+ // Write package.json
149
+ const packageJson = { ...templates.PACKAGE_JSON, name: projectName };
150
+ writeFileSync(join(cwd, 'package.json'), JSON.stringify(packageJson, null, 2));
151
+
152
+ // Write .env.example
153
+ writeFileSync(join(cwd, '.env.example'), templates.ENV_EXAMPLE);
154
+ writeFileSync(join(cwd, '.env.local'), templates.ENV_EXAMPLE);
155
+
156
+ // Write config files
157
+ writeFileSync(join(cwd, 'drizzle.config.ts'), templates.DRIZZLE_CONFIG);
158
+ writeFileSync(join(cwd, 'tailwind.config.ts'), templates.TAILWIND_CONFIG);
159
+ writeFileSync(join(cwd, 'postcss.config.mjs'), templates.POSTCSS_CONFIG);
160
+ writeFileSync(join(cwd, 'tsconfig.json'), JSON.stringify(templates.TSCONFIG, null, 2));
161
+ writeFileSync(join(cwd, 'next.config.ts'), templates.NEXT_CONFIG);
162
+ writeFileSync(join(cwd, '.gitignore'), templates.GITIGNORE);
163
+
164
+ spinner.text = ' Writing source files...';
165
+
166
+ // Write Supabase files
167
+ writeFileSync(join(cwd, 'src/lib/supabase/server.ts'), templates.SUPABASE_SERVER);
168
+ writeFileSync(join(cwd, 'src/lib/supabase/client.ts'), templates.SUPABASE_CLIENT);
169
+ writeFileSync(join(cwd, 'src/lib/supabase/middleware.ts'), templates.SUPABASE_MIDDLEWARE);
170
+
171
+ // Write middleware
172
+ writeFileSync(join(cwd, 'middleware.ts'), templates.MIDDLEWARE);
173
+
174
+ // Write database files
175
+ writeFileSync(join(cwd, 'src/db/schema.ts'), templates.DB_SCHEMA);
176
+ writeFileSync(join(cwd, 'src/db/index.ts'), templates.DB_INDEX);
177
+
178
+ // Write app files
179
+ writeFileSync(join(cwd, 'src/app/globals.css'), templates.GLOBALS_CSS);
180
+ writeFileSync(join(cwd, 'src/app/layout.tsx'), templates.LAYOUT_TSX);
181
+ writeFileSync(join(cwd, 'src/app/page.tsx'), templates.PAGE_TSX);
182
+
183
+ // Write utils
184
+ writeFileSync(join(cwd, 'src/lib/utils.ts'), templates.UTILS_CN);
185
+
186
+ spinner.succeed('Project structure created!');
187
+
188
+ // Ask about installing dependencies
189
+ console.log('');
190
+ const installDeps = await confirm(' Install dependencies with npm?');
191
+
192
+ if (installDeps) {
193
+ const installSpinner = ora(' Installing dependencies (this may take a minute)...').start();
194
+ try {
195
+ execSync('npm install', { cwd, stdio: 'pipe' });
196
+ installSpinner.succeed('Dependencies installed!');
197
+ } catch (error) {
198
+ installSpinner.warn('Could not install dependencies automatically');
199
+ console.log(chalk.gray(' Run `npm install` manually.\n'));
200
+ }
201
+ }
202
+
203
+ // Success message
204
+ console.log(chalk.green(`
205
+ ╔═══════════════════════════════════════════════════════════╗
206
+ ║ ║
207
+ ║ ${chalk.bold('✓ Project scaffolded successfully!')} ║
208
+ ║ ║
209
+ ╚═══════════════════════════════════════════════════════════╝
210
+ `));
211
+
212
+ console.log(chalk.white(' Project structure:\n'));
213
+ if (isBeginnerMode) {
214
+ console.log(chalk.gray(' src/'));
215
+ console.log(chalk.gray(' ├── app/ ') + chalk.cyan('← Your pages (what users see)'));
216
+ console.log(chalk.gray(' ├── components/ ') + chalk.cyan('← Reusable UI pieces (buttons, forms)'));
217
+ console.log(chalk.gray(' ├── lib/ ') + chalk.cyan('← Helper code & connections'));
218
+ console.log(chalk.gray(' │ └── supabase/ ') + chalk.cyan('← Login & database connection'));
219
+ console.log(chalk.gray(' ├── db/ ') + chalk.cyan('← Database structure (tables)'));
220
+ console.log(chalk.gray(' ├── services/ ') + chalk.cyan('← Core app logic (what your app does)'));
221
+ console.log(chalk.gray(' └── types/ ') + chalk.cyan('← Data shape definitions'));
222
+ } else {
223
+ console.log(chalk.gray(' src/'));
224
+ console.log(chalk.gray(' ├── app/ ') + chalk.cyan('← Pages & layouts'));
225
+ console.log(chalk.gray(' ├── components/ ') + chalk.cyan('← React components'));
226
+ console.log(chalk.gray(' ├── lib/ ') + chalk.cyan('← Utilities & clients'));
227
+ console.log(chalk.gray(' │ └── supabase/ ') + chalk.cyan('← Supabase clients (ready!)'));
228
+ console.log(chalk.gray(' ├── db/ ') + chalk.cyan('← Database schema & queries'));
229
+ console.log(chalk.gray(' ├── services/ ') + chalk.cyan('← Business logic'));
230
+ console.log(chalk.gray(' └── types/ ') + chalk.cyan('← TypeScript types'));
231
+ }
232
+ console.log('');
233
+
234
+ console.log(chalk.white(' Next steps:\n'));
235
+ if (isBeginnerMode) {
236
+ console.log(chalk.cyan(' 1. ') + chalk.white('Set up Supabase (free database + login):'));
237
+ console.log(chalk.gray(' Go to https://supabase.com → Create free account → New Project'));
238
+ console.log('');
239
+ console.log(chalk.cyan(' 2. ') + chalk.white('Connect your project:'));
240
+ console.log(chalk.gray(' Open .env.local file and paste your Supabase credentials'));
241
+ console.log(chalk.gray(' (Found in Supabase: Settings → API)'));
242
+ console.log('');
243
+ console.log(chalk.cyan(' 3. ') + chalk.white('Start your app:'));
244
+ console.log(chalk.gray(' Run: npm run dev'));
245
+ console.log(chalk.gray(' Open: http://localhost:3000 in your browser'));
246
+ console.log('');
247
+ console.log(chalk.cyan(' 4. ') + chalk.white('Add AI superpowers:'));
248
+ console.log(chalk.gray(' Run: codebakers init'));
249
+ console.log(chalk.gray(' Now AI will follow professional coding patterns!\n'));
250
+ } else {
251
+ console.log(chalk.cyan(' 1. ') + chalk.gray('Update .env.local with your Supabase credentials'));
252
+ console.log(chalk.cyan(' 2. ') + chalk.gray('Run `npm run dev` to start development'));
253
+ console.log(chalk.cyan(' 3. ') + chalk.gray('Run `codebakers init` to add CodeBakers patterns'));
254
+ console.log(chalk.cyan(' 4. ') + chalk.gray('Start building with AI assistance!\n'));
255
+
256
+ console.log(chalk.white(' Supabase setup:\n'));
257
+ console.log(chalk.gray(' 1. Create a project at https://supabase.com'));
258
+ console.log(chalk.gray(' 2. Go to Settings → API'));
259
+ console.log(chalk.gray(' 3. Copy URL and anon key to .env.local'));
260
+ console.log(chalk.gray(' 4. Go to Settings → Database → Connection string'));
261
+ console.log(chalk.gray(' 5. Copy DATABASE_URL to .env.local\n'));
262
+ }
263
+
264
+ } catch (error) {
265
+ spinner.fail('Project scaffolding failed');
266
+ const message = error instanceof Error ? error.message : 'Unknown error';
267
+ console.log(chalk.red(`\n Error: ${message}\n`));
268
+ process.exit(1);
269
+ }
270
+ }
package/src/index.ts CHANGED
@@ -11,6 +11,7 @@ import { init } from './commands/init.js';
11
11
  import { serve } from './commands/serve.js';
12
12
  import { mcpConfig, mcpUninstall } from './commands/mcp-config.js';
13
13
  import { setup } from './commands/setup.js';
14
+ import { scaffold } from './commands/scaffold.js';
14
15
 
15
16
  const program = new Command();
16
17
 
@@ -30,6 +31,12 @@ program
30
31
  .description('Interactive project setup wizard')
31
32
  .action(init);
32
33
 
34
+ program
35
+ .command('scaffold')
36
+ .alias('new')
37
+ .description('Create a new project with full stack scaffolding (Next.js + Supabase + Drizzle)')
38
+ .action(scaffold);
39
+
33
40
  program
34
41
  .command('login')
35
42
  .description('Login with your API key')