@codebakers/cli 3.3.18 → 3.4.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.
- package/dist/commands/go.d.ts +1 -0
- package/dist/commands/go.js +75 -33
- package/dist/commands/install-precommit.d.ts +1 -0
- package/dist/commands/install-precommit.js +229 -0
- package/dist/commands/upgrade.js +284 -4
- package/dist/index.js +5 -0
- package/dist/mcp/server.js +325 -256
- package/package.json +1 -1
- package/src/commands/go.ts +91 -36
- package/src/commands/install-precommit.ts +234 -0
- package/src/commands/upgrade.ts +314 -5
- package/src/index.ts +6 -0
- package/src/mcp/server.ts +335 -266
package/package.json
CHANGED
package/src/commands/go.ts
CHANGED
|
@@ -9,10 +9,12 @@ import {
|
|
|
9
9
|
setTrialState,
|
|
10
10
|
getApiUrl,
|
|
11
11
|
getApiKey,
|
|
12
|
+
setApiKey,
|
|
12
13
|
isTrialExpired,
|
|
13
14
|
getTrialDaysRemaining,
|
|
14
15
|
type TrialState,
|
|
15
16
|
} from '../config.js';
|
|
17
|
+
import { validateApiKey } from '../lib/api.js';
|
|
16
18
|
import { getDeviceFingerprint } from '../lib/fingerprint.js';
|
|
17
19
|
|
|
18
20
|
function prompt(question: string): Promise<string> {
|
|
@@ -98,6 +100,7 @@ function log(message: string, options?: GoOptions): void {
|
|
|
98
100
|
|
|
99
101
|
/**
|
|
100
102
|
* Zero-friction entry point - start using CodeBakers instantly
|
|
103
|
+
* Single command for both trial and paid users
|
|
101
104
|
*/
|
|
102
105
|
export async function go(options: GoOptions = {}): Promise<void> {
|
|
103
106
|
log('Starting go command...', options);
|
|
@@ -107,21 +110,22 @@ export async function go(options: GoOptions = {}): Promise<void> {
|
|
|
107
110
|
console.log(chalk.blue(`
|
|
108
111
|
╔═══════════════════════════════════════════════════════════╗
|
|
109
112
|
║ ║
|
|
110
|
-
║ ${chalk.bold.white('CodeBakers -
|
|
113
|
+
║ ${chalk.bold.white('CodeBakers - Get Started')} ║
|
|
111
114
|
║ ║
|
|
112
115
|
╚═══════════════════════════════════════════════════════════╝
|
|
113
116
|
`));
|
|
114
117
|
|
|
115
118
|
// Check if user already has an API key (paid user)
|
|
116
119
|
log('Checking for existing API key...', options);
|
|
117
|
-
const
|
|
118
|
-
if (
|
|
119
|
-
log(`Found API key: ${
|
|
120
|
-
console.log(chalk.green(' ✓ You\'re already logged in
|
|
120
|
+
const existingApiKey = getApiKey();
|
|
121
|
+
if (existingApiKey) {
|
|
122
|
+
log(`Found API key: ${existingApiKey.substring(0, 8)}...`, options);
|
|
123
|
+
console.log(chalk.green(' ✓ You\'re already logged in!\n'));
|
|
121
124
|
|
|
122
|
-
//
|
|
123
|
-
await installPatternsWithApiKey(
|
|
125
|
+
// Install patterns if not already installed
|
|
126
|
+
await installPatternsWithApiKey(existingApiKey, options);
|
|
124
127
|
await configureMCP(options);
|
|
128
|
+
await showSuccessAndRestart();
|
|
125
129
|
return;
|
|
126
130
|
}
|
|
127
131
|
log('No API key found, checking trial state...', options);
|
|
@@ -140,8 +144,8 @@ export async function go(options: GoOptions = {}): Promise<void> {
|
|
|
140
144
|
|
|
141
145
|
// Install patterns if not already installed
|
|
142
146
|
await installPatterns(existingTrial.trialId, options);
|
|
143
|
-
|
|
144
147
|
await configureMCP(options);
|
|
148
|
+
await showSuccessAndRestart();
|
|
145
149
|
return;
|
|
146
150
|
}
|
|
147
151
|
|
|
@@ -149,15 +153,31 @@ export async function go(options: GoOptions = {}): Promise<void> {
|
|
|
149
153
|
if (existingTrial && isTrialExpired()) {
|
|
150
154
|
console.log(chalk.yellow(' ⚠️ Your trial has expired.\n'));
|
|
151
155
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
156
|
+
// Offer to login with API key or extend
|
|
157
|
+
console.log(chalk.white(' Options:\n'));
|
|
158
|
+
console.log(chalk.cyan(' [1] Login with API key') + chalk.gray(' (I have an account)'));
|
|
159
|
+
console.log(chalk.cyan(' [2] Extend trial') + chalk.gray(' (7 more days with GitHub)\n'));
|
|
160
|
+
|
|
161
|
+
const choice = await prompt(chalk.gray(' Enter 1 or 2: '));
|
|
162
|
+
|
|
163
|
+
if (choice === '1') {
|
|
164
|
+
await handleApiKeyLogin(options);
|
|
165
|
+
return;
|
|
157
166
|
} else {
|
|
158
|
-
console.log(chalk.
|
|
159
|
-
|
|
167
|
+
console.log(chalk.cyan('\n Run: codebakers extend\n'));
|
|
168
|
+
return;
|
|
160
169
|
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// New user - ask how they want to proceed
|
|
173
|
+
console.log(chalk.white(' How would you like to get started?\n'));
|
|
174
|
+
console.log(chalk.cyan(' [1] Start free 7-day trial') + chalk.gray(' (no signup required)'));
|
|
175
|
+
console.log(chalk.cyan(' [2] Login with API key') + chalk.gray(' (I have an account)\n'));
|
|
176
|
+
|
|
177
|
+
const choice = await prompt(chalk.gray(' Enter 1 or 2: '));
|
|
178
|
+
|
|
179
|
+
if (choice === '2') {
|
|
180
|
+
await handleApiKeyLogin(options);
|
|
161
181
|
return;
|
|
162
182
|
}
|
|
163
183
|
|
|
@@ -235,19 +255,8 @@ export async function go(options: GoOptions = {}): Promise<void> {
|
|
|
235
255
|
// Configure MCP
|
|
236
256
|
await configureMCP(options);
|
|
237
257
|
|
|
238
|
-
// Show success
|
|
239
|
-
|
|
240
|
-
╔═══════════════════════════════════════════════════════════╗
|
|
241
|
-
║ ✅ CodeBakers is ready! ║
|
|
242
|
-
║ ║
|
|
243
|
-
║ ${chalk.white('Your 7-day free trial has started.')} ║
|
|
244
|
-
║ ║
|
|
245
|
-
║ ${chalk.gray('Try: "Build me a todo app with authentication"')} ║
|
|
246
|
-
╚═══════════════════════════════════════════════════════════╝
|
|
247
|
-
`));
|
|
248
|
-
|
|
249
|
-
// Attempt auto-restart Claude Code
|
|
250
|
-
await attemptAutoRestart();
|
|
258
|
+
// Show success and restart
|
|
259
|
+
await showSuccessAndRestart();
|
|
251
260
|
|
|
252
261
|
} catch (error) {
|
|
253
262
|
spinner.fail('Failed to start trial');
|
|
@@ -289,10 +298,61 @@ async function configureMCP(options: GoOptions = {}): Promise<void> {
|
|
|
289
298
|
}
|
|
290
299
|
}
|
|
291
300
|
|
|
292
|
-
|
|
301
|
+
/**
|
|
302
|
+
* Handle API key login flow (for paid users)
|
|
303
|
+
*/
|
|
304
|
+
async function handleApiKeyLogin(options: GoOptions = {}): Promise<void> {
|
|
305
|
+
console.log(chalk.white('\n Enter your API key\n'));
|
|
306
|
+
console.log(chalk.gray(' Find it at: https://codebakers.ai/dashboard\n'));
|
|
307
|
+
|
|
308
|
+
const apiKey = await prompt(chalk.cyan(' API Key: '));
|
|
309
|
+
|
|
310
|
+
if (!apiKey) {
|
|
311
|
+
console.log(chalk.red('\n API key is required.\n'));
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
const spinner = ora('Validating API key...').start();
|
|
316
|
+
|
|
317
|
+
try {
|
|
318
|
+
await validateApiKey(apiKey);
|
|
319
|
+
spinner.succeed('API key validated');
|
|
320
|
+
|
|
321
|
+
// Save API key
|
|
322
|
+
setApiKey(apiKey);
|
|
323
|
+
console.log(chalk.green(' ✓ Logged in successfully!\n'));
|
|
324
|
+
|
|
325
|
+
// Install patterns
|
|
326
|
+
await installPatternsWithApiKey(apiKey, options);
|
|
327
|
+
|
|
328
|
+
// Configure MCP
|
|
329
|
+
await configureMCP(options);
|
|
330
|
+
|
|
331
|
+
// Show success
|
|
332
|
+
await showSuccessAndRestart();
|
|
333
|
+
|
|
334
|
+
} catch (error) {
|
|
335
|
+
spinner.fail('Invalid API key');
|
|
336
|
+
console.log(chalk.red('\n Could not validate API key.'));
|
|
337
|
+
console.log(chalk.gray(' Check your key at: https://codebakers.ai/dashboard\n'));
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Show success message and offer to restart
|
|
343
|
+
*/
|
|
344
|
+
async function showSuccessAndRestart(): Promise<void> {
|
|
293
345
|
const cwd = process.cwd();
|
|
294
346
|
|
|
295
|
-
console.log(chalk.
|
|
347
|
+
console.log(chalk.green(`
|
|
348
|
+
╔═══════════════════════════════════════════════════════════╗
|
|
349
|
+
║ ✅ CodeBakers is ready! ║
|
|
350
|
+
║ ║
|
|
351
|
+
║ ${chalk.gray('Try: "Build me a todo app with authentication"')} ║
|
|
352
|
+
╚═══════════════════════════════════════════════════════════╝
|
|
353
|
+
`));
|
|
354
|
+
|
|
355
|
+
console.log(chalk.yellow(' ⚠️ RESTART REQUIRED\n'));
|
|
296
356
|
console.log(chalk.gray(' Claude Code needs to restart to load CodeBakers.\n'));
|
|
297
357
|
|
|
298
358
|
const answer = await prompt(chalk.cyan(' Restart Claude Code now? (Y/n): '));
|
|
@@ -309,7 +369,6 @@ async function attemptAutoRestart(): Promise<void> {
|
|
|
309
369
|
const isWindows = process.platform === 'win32';
|
|
310
370
|
|
|
311
371
|
if (isWindows) {
|
|
312
|
-
// On Windows, spawn a new Claude process detached and exit
|
|
313
372
|
spawn('cmd', ['/c', 'start', 'claude'], {
|
|
314
373
|
cwd,
|
|
315
374
|
detached: true,
|
|
@@ -317,7 +376,6 @@ async function attemptAutoRestart(): Promise<void> {
|
|
|
317
376
|
shell: true,
|
|
318
377
|
}).unref();
|
|
319
378
|
} else {
|
|
320
|
-
// On Mac/Linux, spawn claude in new terminal
|
|
321
379
|
spawn('claude', [], {
|
|
322
380
|
cwd,
|
|
323
381
|
detached: true,
|
|
@@ -329,13 +387,10 @@ async function attemptAutoRestart(): Promise<void> {
|
|
|
329
387
|
console.log(chalk.green(' ✓ Claude Code is restarting...\n'));
|
|
330
388
|
console.log(chalk.gray(' This terminal will close. Claude Code will open in a new window.\n'));
|
|
331
389
|
|
|
332
|
-
// Give the spawn a moment to start
|
|
333
390
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
334
|
-
|
|
335
|
-
// Exit this process
|
|
336
391
|
process.exit(0);
|
|
337
392
|
|
|
338
|
-
} catch
|
|
393
|
+
} catch {
|
|
339
394
|
console.log(chalk.yellow(' Could not auto-restart. Please restart Claude Code manually.\n'));
|
|
340
395
|
}
|
|
341
396
|
}
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { existsSync, mkdirSync, writeFileSync, chmodSync, readFileSync } from 'fs';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
|
|
5
|
+
const PRE_COMMIT_SCRIPT = `#!/bin/sh
|
|
6
|
+
# CodeBakers Pre-Commit Hook - Session Enforcement
|
|
7
|
+
# Blocks commits unless AI called discover_patterns and validate_complete
|
|
8
|
+
|
|
9
|
+
# Run the validation script
|
|
10
|
+
node "$(dirname "$0")/validate-session.js"
|
|
11
|
+
exit $?
|
|
12
|
+
`;
|
|
13
|
+
|
|
14
|
+
const VALIDATE_SESSION_SCRIPT = `#!/usr/bin/env node
|
|
15
|
+
/**
|
|
16
|
+
* CodeBakers Pre-Commit Validation
|
|
17
|
+
* Blocks commits unless a valid session exists
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
const fs = require('fs');
|
|
21
|
+
const path = require('path');
|
|
22
|
+
|
|
23
|
+
const RED = '\\x1b[31m';
|
|
24
|
+
const GREEN = '\\x1b[32m';
|
|
25
|
+
const YELLOW = '\\x1b[33m';
|
|
26
|
+
const CYAN = '\\x1b[36m';
|
|
27
|
+
const RESET = '\\x1b[0m';
|
|
28
|
+
|
|
29
|
+
function log(color, message) {
|
|
30
|
+
console.log(color + message + RESET);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async function validateSession() {
|
|
34
|
+
const cwd = process.cwd();
|
|
35
|
+
const stateFile = path.join(cwd, '.codebakers.json');
|
|
36
|
+
|
|
37
|
+
// Check if this is a CodeBakers project
|
|
38
|
+
if (!fs.existsSync(stateFile)) {
|
|
39
|
+
return { valid: true, reason: 'not-codebakers-project' };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
let state;
|
|
43
|
+
try {
|
|
44
|
+
state = JSON.parse(fs.readFileSync(stateFile, 'utf-8'));
|
|
45
|
+
} catch (error) {
|
|
46
|
+
return { valid: false, reason: 'invalid-state-file' };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Check if v6.0 server-enforced mode
|
|
50
|
+
if (!state.serverEnforced) {
|
|
51
|
+
return { valid: true, reason: 'legacy-project' };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Check for session token (means discover_patterns was called)
|
|
55
|
+
const sessionToken = state.currentSessionToken;
|
|
56
|
+
if (!sessionToken) {
|
|
57
|
+
// Check if there's a recent passed validation
|
|
58
|
+
const lastValidation = state.lastValidation;
|
|
59
|
+
if (!lastValidation || !lastValidation.passed) {
|
|
60
|
+
return {
|
|
61
|
+
valid: false,
|
|
62
|
+
reason: 'no-session',
|
|
63
|
+
message: 'No active CodeBakers session.\\nAI must call discover_patterns before writing code.'
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Check session expiry
|
|
69
|
+
const sessionExpiry = state.sessionExpiresAt;
|
|
70
|
+
if (sessionExpiry && new Date(sessionExpiry) < new Date()) {
|
|
71
|
+
return {
|
|
72
|
+
valid: false,
|
|
73
|
+
reason: 'session-expired',
|
|
74
|
+
message: 'CodeBakers session has expired.\\nAI must call discover_patterns again.'
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Check if validation was completed
|
|
79
|
+
const lastValidation = state.lastValidation;
|
|
80
|
+
if (!lastValidation) {
|
|
81
|
+
return {
|
|
82
|
+
valid: false,
|
|
83
|
+
reason: 'no-validation',
|
|
84
|
+
message: 'No validation completed.\\nAI must call validate_complete before committing.'
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Check if validation passed
|
|
89
|
+
if (!lastValidation.passed) {
|
|
90
|
+
const issues = lastValidation.issues?.map(i => i.message || i).join(', ') || 'Unknown issues';
|
|
91
|
+
return {
|
|
92
|
+
valid: false,
|
|
93
|
+
reason: 'validation-failed',
|
|
94
|
+
message: 'Validation failed: ' + issues + '\\nAI must fix issues and call validate_complete again.'
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Check if validation is recent (within last 30 minutes)
|
|
99
|
+
const validationTime = new Date(lastValidation.timestamp);
|
|
100
|
+
const thirtyMinutesAgo = new Date(Date.now() - 30 * 60 * 1000);
|
|
101
|
+
if (validationTime < thirtyMinutesAgo) {
|
|
102
|
+
return {
|
|
103
|
+
valid: false,
|
|
104
|
+
reason: 'validation-stale',
|
|
105
|
+
message: 'Validation is stale (older than 30 minutes).\\nAI must call validate_complete again.'
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return { valid: true, reason: 'session-valid' };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async function main() {
|
|
113
|
+
console.log('');
|
|
114
|
+
log(CYAN, ' 🍪 CodeBakers Pre-Commit Validation');
|
|
115
|
+
console.log('');
|
|
116
|
+
|
|
117
|
+
const result = await validateSession();
|
|
118
|
+
|
|
119
|
+
if (result.valid) {
|
|
120
|
+
if (result.reason === 'not-codebakers-project') {
|
|
121
|
+
log(GREEN, ' ✓ Not a CodeBakers project - commit allowed');
|
|
122
|
+
} else if (result.reason === 'legacy-project') {
|
|
123
|
+
log(GREEN, ' ✓ Legacy project (pre-6.0) - commit allowed');
|
|
124
|
+
} else {
|
|
125
|
+
log(GREEN, ' ✓ Valid CodeBakers session - commit allowed');
|
|
126
|
+
}
|
|
127
|
+
console.log('');
|
|
128
|
+
process.exit(0);
|
|
129
|
+
} else {
|
|
130
|
+
log(RED, ' ✗ Commit blocked: ' + result.reason);
|
|
131
|
+
console.log('');
|
|
132
|
+
if (result.message) {
|
|
133
|
+
log(YELLOW, ' ' + result.message.split('\\n').join('\\n '));
|
|
134
|
+
}
|
|
135
|
+
console.log('');
|
|
136
|
+
log(CYAN, ' How to fix:');
|
|
137
|
+
log(RESET, ' 1. AI must call discover_patterns before writing code');
|
|
138
|
+
log(RESET, ' 2. AI must call validate_complete before saying "done"');
|
|
139
|
+
log(RESET, ' 3. Both tools must pass for commits to be allowed');
|
|
140
|
+
console.log('');
|
|
141
|
+
log(YELLOW, ' To bypass (not recommended): git commit --no-verify');
|
|
142
|
+
console.log('');
|
|
143
|
+
process.exit(1);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
main().catch(error => {
|
|
148
|
+
log(RED, ' Error: ' + error.message);
|
|
149
|
+
process.exit(1);
|
|
150
|
+
});
|
|
151
|
+
`;
|
|
152
|
+
|
|
153
|
+
export async function installPrecommit(): Promise<void> {
|
|
154
|
+
console.log(chalk.blue('\n CodeBakers Pre-Commit Hook Installation\n'));
|
|
155
|
+
|
|
156
|
+
const cwd = process.cwd();
|
|
157
|
+
|
|
158
|
+
// Check if this is a git repository
|
|
159
|
+
const gitDir = join(cwd, '.git');
|
|
160
|
+
if (!existsSync(gitDir)) {
|
|
161
|
+
console.log(chalk.red(' ✗ Not a git repository'));
|
|
162
|
+
console.log(chalk.gray(' Initialize git first: git init\n'));
|
|
163
|
+
process.exit(1);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Check if this is a CodeBakers project
|
|
167
|
+
const stateFile = join(cwd, '.codebakers.json');
|
|
168
|
+
if (!existsSync(stateFile)) {
|
|
169
|
+
console.log(chalk.yellow(' ⚠️ No .codebakers.json found'));
|
|
170
|
+
console.log(chalk.gray(' Run codebakers upgrade first to enable server enforcement\n'));
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Create hooks directory if it doesn't exist
|
|
174
|
+
const hooksDir = join(gitDir, 'hooks');
|
|
175
|
+
if (!existsSync(hooksDir)) {
|
|
176
|
+
mkdirSync(hooksDir, { recursive: true });
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Write the pre-commit hook
|
|
180
|
+
const preCommitPath = join(hooksDir, 'pre-commit');
|
|
181
|
+
writeFileSync(preCommitPath, PRE_COMMIT_SCRIPT);
|
|
182
|
+
|
|
183
|
+
// Make it executable (Unix only, Windows ignores this)
|
|
184
|
+
try {
|
|
185
|
+
chmodSync(preCommitPath, '755');
|
|
186
|
+
} catch {
|
|
187
|
+
// Windows doesn't support chmod
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
console.log(chalk.green(' ✓ Created pre-commit hook'));
|
|
191
|
+
|
|
192
|
+
// Write the validation script
|
|
193
|
+
const validatePath = join(hooksDir, 'validate-session.js');
|
|
194
|
+
writeFileSync(validatePath, VALIDATE_SESSION_SCRIPT);
|
|
195
|
+
|
|
196
|
+
console.log(chalk.green(' ✓ Created validation script'));
|
|
197
|
+
|
|
198
|
+
// Check if husky is being used
|
|
199
|
+
const huskyDir = join(cwd, '.husky');
|
|
200
|
+
if (existsSync(huskyDir)) {
|
|
201
|
+
// Also install in husky
|
|
202
|
+
const huskyPreCommit = join(huskyDir, 'pre-commit');
|
|
203
|
+
let huskyContent = '';
|
|
204
|
+
|
|
205
|
+
if (existsSync(huskyPreCommit)) {
|
|
206
|
+
huskyContent = readFileSync(huskyPreCommit, 'utf-8');
|
|
207
|
+
if (!huskyContent.includes('validate-session')) {
|
|
208
|
+
huskyContent += '\n# CodeBakers session enforcement\nnode .git/hooks/validate-session.js\n';
|
|
209
|
+
writeFileSync(huskyPreCommit, huskyContent);
|
|
210
|
+
console.log(chalk.green(' ✓ Added to existing husky pre-commit'));
|
|
211
|
+
} else {
|
|
212
|
+
console.log(chalk.gray(' ✓ Husky hook already configured'));
|
|
213
|
+
}
|
|
214
|
+
} else {
|
|
215
|
+
huskyContent = '#!/usr/bin/env sh\n. "$(dirname -- "$0")/_/husky.sh"\n\n# CodeBakers session enforcement\nnode .git/hooks/validate-session.js\n';
|
|
216
|
+
writeFileSync(huskyPreCommit, huskyContent);
|
|
217
|
+
try {
|
|
218
|
+
chmodSync(huskyPreCommit, '755');
|
|
219
|
+
} catch {
|
|
220
|
+
// Windows
|
|
221
|
+
}
|
|
222
|
+
console.log(chalk.green(' ✓ Created husky pre-commit hook'));
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
console.log(chalk.green('\n ✅ Pre-commit hook installed!\n'));
|
|
227
|
+
console.log(chalk.cyan(' What this does:'));
|
|
228
|
+
console.log(chalk.gray(' - Blocks commits unless AI called discover_patterns'));
|
|
229
|
+
console.log(chalk.gray(' - Blocks commits unless AI called validate_complete'));
|
|
230
|
+
console.log(chalk.gray(' - Requires validation to pass before committing'));
|
|
231
|
+
console.log(chalk.gray(' - Validation expires after 30 minutes\n'));
|
|
232
|
+
console.log(chalk.yellow(' To bypass (not recommended):'));
|
|
233
|
+
console.log(chalk.gray(' git commit --no-verify\n'));
|
|
234
|
+
}
|