@snapcommit/cli 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.
Files changed (40) hide show
  1. package/README.md +162 -0
  2. package/dist/ai/anthropic-client.js +92 -0
  3. package/dist/ai/commit-generator.js +200 -0
  4. package/dist/ai/gemini-client.js +201 -0
  5. package/dist/ai/git-interpreter.js +209 -0
  6. package/dist/ai/smart-solver.js +260 -0
  7. package/dist/auth/supabase-client.js +288 -0
  8. package/dist/commands/activate.js +108 -0
  9. package/dist/commands/commit.js +255 -0
  10. package/dist/commands/conflict.js +233 -0
  11. package/dist/commands/doctor.js +113 -0
  12. package/dist/commands/git-advanced.js +311 -0
  13. package/dist/commands/github-auth.js +193 -0
  14. package/dist/commands/login.js +11 -0
  15. package/dist/commands/natural.js +305 -0
  16. package/dist/commands/onboard.js +111 -0
  17. package/dist/commands/quick.js +173 -0
  18. package/dist/commands/setup.js +163 -0
  19. package/dist/commands/stats.js +128 -0
  20. package/dist/commands/uninstall.js +131 -0
  21. package/dist/db/database.js +99 -0
  22. package/dist/index.js +144 -0
  23. package/dist/lib/auth.js +171 -0
  24. package/dist/lib/github.js +280 -0
  25. package/dist/lib/multi-repo.js +276 -0
  26. package/dist/lib/supabase.js +153 -0
  27. package/dist/license/manager.js +203 -0
  28. package/dist/repl/index.js +185 -0
  29. package/dist/repl/interpreter.js +524 -0
  30. package/dist/utils/analytics.js +36 -0
  31. package/dist/utils/auth-storage.js +65 -0
  32. package/dist/utils/dopamine.js +211 -0
  33. package/dist/utils/errors.js +56 -0
  34. package/dist/utils/git.js +105 -0
  35. package/dist/utils/heatmap.js +265 -0
  36. package/dist/utils/rate-limit.js +68 -0
  37. package/dist/utils/retry.js +46 -0
  38. package/dist/utils/ui.js +189 -0
  39. package/dist/utils/version.js +81 -0
  40. package/package.json +69 -0
@@ -0,0 +1,193 @@
1
+ "use strict";
2
+ /**
3
+ * GitHub OAuth authentication handler
4
+ * Opens browser, starts local callback server, handles OAuth flow
5
+ */
6
+ var __importDefault = (this && this.__importDefault) || function (mod) {
7
+ return (mod && mod.__esModule) ? mod : { "default": mod };
8
+ };
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.handleGitHubAuth = handleGitHubAuth;
11
+ const chalk_1 = __importDefault(require("chalk"));
12
+ const http_1 = __importDefault(require("http"));
13
+ const url_1 = require("url");
14
+ const open_1 = __importDefault(require("open"));
15
+ const supabase_client_1 = require("../auth/supabase-client");
16
+ /**
17
+ * Handle GitHub OAuth authentication
18
+ * 1. Start local callback server
19
+ * 2. Open browser to Supabase OAuth URL
20
+ * 3. User authorizes on GitHub
21
+ * 4. GitHub redirects to local server with token
22
+ * 5. Save token and close server
23
+ */
24
+ async function handleGitHubAuth() {
25
+ console.log(chalk_1.default.blue('\nšŸš€ Opening browser for GitHub authentication...\n'));
26
+ return new Promise((resolve) => {
27
+ // Start local callback server
28
+ const server = http_1.default.createServer(async (req, res) => {
29
+ const url = new url_1.URL(req.url || '', `http://localhost:54321`);
30
+ if (url.pathname === '/auth/callback') {
31
+ const params = url.searchParams;
32
+ // Check for errors
33
+ const error = params.get('error');
34
+ const errorDescription = params.get('error_description');
35
+ if (error) {
36
+ res.writeHead(200, { 'Content-Type': 'text/html' });
37
+ res.end(`
38
+ <html>
39
+ <head>
40
+ <style>
41
+ body {
42
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
43
+ display: flex;
44
+ justify-content: center;
45
+ align-items: center;
46
+ height: 100vh;
47
+ margin: 0;
48
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
49
+ }
50
+ .container {
51
+ background: white;
52
+ padding: 3rem;
53
+ border-radius: 1rem;
54
+ box-shadow: 0 20px 60px rgba(0,0,0,0.3);
55
+ text-align: center;
56
+ max-width: 500px;
57
+ }
58
+ h1 { color: #e53e3e; margin-bottom: 1rem; }
59
+ p { color: #4a5568; line-height: 1.6; }
60
+ </style>
61
+ </head>
62
+ <body>
63
+ <div class="container">
64
+ <h1>āŒ Authentication Failed</h1>
65
+ <p>${errorDescription || error}</p>
66
+ <p style="margin-top: 2rem; color: #718096;">You can close this window and try again in your terminal.</p>
67
+ </div>
68
+ </body>
69
+ </html>
70
+ `);
71
+ server.close();
72
+ console.log(chalk_1.default.red(`\nāŒ Authentication failed: ${errorDescription || error}\n`));
73
+ resolve(false);
74
+ return;
75
+ }
76
+ // Get access token from hash fragment
77
+ // Supabase returns token in URL hash (#access_token=...)
78
+ const hashParams = url.hash.substring(1); // Remove #
79
+ const hashSearchParams = new URLSearchParams(hashParams);
80
+ const accessToken = hashSearchParams.get('access_token');
81
+ const refreshToken = hashSearchParams.get('refresh_token');
82
+ const expiresIn = hashSearchParams.get('expires_in');
83
+ if (accessToken && refreshToken) {
84
+ // Save tokens
85
+ try {
86
+ // Parse token to get user info
87
+ const payload = JSON.parse(Buffer.from(accessToken.split('.')[1], 'base64').toString());
88
+ (0, supabase_client_1.saveAuthTokens)({
89
+ access_token: accessToken,
90
+ refresh_token: refreshToken,
91
+ user_id: payload.sub,
92
+ email: payload.email || 'github-user',
93
+ expires_at: Date.now() + (parseInt(expiresIn || '3600') * 1000),
94
+ });
95
+ // Success page
96
+ res.writeHead(200, { 'Content-Type': 'text/html' });
97
+ res.end(`
98
+ <html>
99
+ <head>
100
+ <style>
101
+ body {
102
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
103
+ display: flex;
104
+ justify-content: center;
105
+ align-items: center;
106
+ height: 100vh;
107
+ margin: 0;
108
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
109
+ }
110
+ .container {
111
+ background: white;
112
+ padding: 3rem;
113
+ border-radius: 1rem;
114
+ box-shadow: 0 20px 60px rgba(0,0,0,0.3);
115
+ text-align: center;
116
+ max-width: 500px;
117
+ }
118
+ h1 { color: #48bb78; margin-bottom: 1rem; font-size: 2.5rem; }
119
+ p { color: #4a5568; line-height: 1.6; }
120
+ .code {
121
+ background: #f7fafc;
122
+ padding: 0.5rem 1rem;
123
+ border-radius: 0.5rem;
124
+ font-family: 'Monaco', monospace;
125
+ color: #3182ce;
126
+ margin: 1rem 0;
127
+ }
128
+ </style>
129
+ </head>
130
+ <body>
131
+ <div class="container">
132
+ <h1>āœ… Success!</h1>
133
+ <p style="font-size: 1.2rem; margin-bottom: 2rem;">You're now logged in to SnapCommit</p>
134
+ <p>You can close this window and return to your terminal.</p>
135
+ <div class="code">Try: snap</div>
136
+ <p style="margin-top: 2rem; color: #718096; font-size: 0.9rem;">This window will close automatically in 3 seconds...</p>
137
+ </div>
138
+ <script>setTimeout(() => window.close(), 3000);</script>
139
+ </body>
140
+ </html>
141
+ `);
142
+ server.close();
143
+ console.log(chalk_1.default.green.bold('\nāœ… Successfully authenticated with GitHub!\n'));
144
+ console.log(chalk_1.default.gray('You can now use SnapCommit Pro features.'));
145
+ console.log(chalk_1.default.gray('Try: ') + chalk_1.default.cyan('snap') + chalk_1.default.gray(' to make your first commit!\n'));
146
+ resolve(true);
147
+ }
148
+ catch (err) {
149
+ res.writeHead(500, { 'Content-Type': 'text/plain' });
150
+ res.end('Error parsing authentication token');
151
+ server.close();
152
+ console.log(chalk_1.default.red('\nāŒ Error saving authentication\n'));
153
+ resolve(false);
154
+ }
155
+ }
156
+ else {
157
+ res.writeHead(400, { 'Content-Type': 'text/plain' });
158
+ res.end('Missing authentication tokens');
159
+ server.close();
160
+ console.log(chalk_1.default.red('\nāŒ No authentication tokens received\n'));
161
+ resolve(false);
162
+ }
163
+ }
164
+ });
165
+ // Start server on port 54321
166
+ server.listen(54321, async () => {
167
+ console.log(chalk_1.default.gray('āœ“ Local callback server started on port 54321'));
168
+ // Construct OAuth URL manually (since Supabase client method might not work well in CLI)
169
+ const supabaseUrl = process.env.SUPABASE_URL || '';
170
+ const redirectUri = 'http://localhost:54321/auth/callback';
171
+ // GitHub OAuth URL via Supabase
172
+ const authUrl = `${supabaseUrl}/auth/v1/authorize?provider=github&redirect_to=${encodeURIComponent(redirectUri)}`;
173
+ console.log(chalk_1.default.gray('āœ“ Opening browser...\n'));
174
+ try {
175
+ await (0, open_1.default)(authUrl);
176
+ console.log(chalk_1.default.yellow('šŸ‘‰ Complete the authentication in your browser'));
177
+ console.log(chalk_1.default.gray(' (Waiting for callback...)\n'));
178
+ }
179
+ catch (error) {
180
+ console.log(chalk_1.default.yellow('\nāš ļø Could not open browser automatically.'));
181
+ console.log(chalk_1.default.white('Please open this URL manually:\n'));
182
+ console.log(chalk_1.default.cyan(authUrl));
183
+ console.log();
184
+ }
185
+ });
186
+ // Timeout after 5 minutes
187
+ setTimeout(() => {
188
+ server.close();
189
+ console.log(chalk_1.default.red('\nāŒ Authentication timeout (5 minutes)\n'));
190
+ resolve(false);
191
+ }, 5 * 60 * 1000);
192
+ });
193
+ }
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.loginCommand = loginCommand;
4
+ exports.logoutCommand = logoutCommand;
5
+ const auth_1 = require("../lib/auth");
6
+ async function loginCommand() {
7
+ await (0, auth_1.promptAuth)();
8
+ }
9
+ async function logoutCommand() {
10
+ await (0, auth_1.logout)();
11
+ }
@@ -0,0 +1,305 @@
1
+ "use strict";
2
+ /**
3
+ * Natural language Git command handler
4
+ * The magic command that understands what you want
5
+ */
6
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
+ if (k2 === undefined) k2 = k;
8
+ var desc = Object.getOwnPropertyDescriptor(m, k);
9
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
+ desc = { enumerable: true, get: function() { return m[k]; } };
11
+ }
12
+ Object.defineProperty(o, k2, desc);
13
+ }) : (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ o[k2] = m[k];
16
+ }));
17
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
18
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
19
+ }) : function(o, v) {
20
+ o["default"] = v;
21
+ });
22
+ var __importStar = (this && this.__importStar) || (function () {
23
+ var ownKeys = function(o) {
24
+ ownKeys = Object.getOwnPropertyNames || function (o) {
25
+ var ar = [];
26
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
27
+ return ar;
28
+ };
29
+ return ownKeys(o);
30
+ };
31
+ return function (mod) {
32
+ if (mod && mod.__esModule) return mod;
33
+ var result = {};
34
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
35
+ __setModuleDefault(result, mod);
36
+ return result;
37
+ };
38
+ })();
39
+ var __importDefault = (this && this.__importDefault) || function (mod) {
40
+ return (mod && mod.__esModule) ? mod : { "default": mod };
41
+ };
42
+ Object.defineProperty(exports, "__esModule", { value: true });
43
+ exports.naturalCommand = naturalCommand;
44
+ const chalk_1 = __importDefault(require("chalk"));
45
+ const child_process_1 = require("child_process");
46
+ const readline_1 = __importDefault(require("readline"));
47
+ const git_interpreter_1 = require("../ai/git-interpreter");
48
+ const git_1 = require("../utils/git");
49
+ const anthropic_client_1 = require("../ai/anthropic-client");
50
+ const git_2 = require("../utils/git");
51
+ const database_1 = require("../db/database");
52
+ const analytics_1 = require("../utils/analytics");
53
+ const manager_1 = require("../license/manager");
54
+ const smart_solver_1 = require("../ai/smart-solver");
55
+ function askQuestion(query) {
56
+ const rl = readline_1.default.createInterface({
57
+ input: process.stdin,
58
+ output: process.stdout,
59
+ });
60
+ return new Promise(resolve => rl.question(query, ans => {
61
+ rl.close();
62
+ resolve(ans);
63
+ }));
64
+ }
65
+ /**
66
+ * Execute git commands with visual feedback
67
+ */
68
+ function executeGitCommands(commands, showCommands = true) {
69
+ const results = [];
70
+ for (const cmd of commands) {
71
+ if (showCommands) {
72
+ console.log(chalk_1.default.gray(` $ ${cmd}`));
73
+ }
74
+ try {
75
+ const output = (0, child_process_1.execSync)(cmd, { encoding: 'utf-8', stdio: 'pipe' });
76
+ if (output.trim()) {
77
+ if (showCommands) {
78
+ console.log(chalk_1.default.gray(` ${output.trim().split('\n').join('\n ')}`));
79
+ }
80
+ results.push(output.trim());
81
+ }
82
+ }
83
+ catch (error) {
84
+ if (showCommands) {
85
+ console.log(chalk_1.default.red(` Error: ${error.message}`));
86
+ }
87
+ return { success: false, output: results.join('\n'), error: error.message };
88
+ }
89
+ }
90
+ return { success: true, output: results.join('\n') };
91
+ }
92
+ /**
93
+ * Main natural language command handler
94
+ */
95
+ async function naturalCommand(userInput) {
96
+ // Check if in git repo
97
+ if (!(0, git_1.isGitRepo)()) {
98
+ console.log(chalk_1.default.red('\nāŒ Not a git repository'));
99
+ console.log(chalk_1.default.gray(' Navigate to a git repository first\n'));
100
+ return;
101
+ }
102
+ // Get context
103
+ const currentBranch = (0, git_1.getCurrentBranch)();
104
+ const status = (0, git_1.getGitStatus)();
105
+ const hasUncommittedChanges = status.staged > 0 || status.unstaged > 0;
106
+ let lastCommitHash;
107
+ try {
108
+ lastCommitHash = (0, child_process_1.execSync)('git log -1 --format=%H', { encoding: 'utf-8' }).trim();
109
+ }
110
+ catch {
111
+ lastCommitHash = undefined;
112
+ }
113
+ // Check if we need clarification
114
+ const clarification = await (0, smart_solver_1.needsClarification)(userInput);
115
+ if (clarification.needsClarification && clarification.options) {
116
+ console.log(chalk_1.default.yellow('\nšŸ¤” I need more information:\n'));
117
+ clarification.options.forEach((option, i) => {
118
+ console.log(chalk_1.default.white(` ${i + 1}. ${option.label}`));
119
+ });
120
+ console.log();
121
+ const answer = await askQuestion(chalk_1.default.yellow(`Which one? (1-${clarification.options.length}): `));
122
+ const choice = parseInt(answer) - 1;
123
+ if (choice >= 0 && choice < clarification.options.length) {
124
+ const selectedOption = clarification.options[choice];
125
+ userInput = selectedOption.value; // Use the clarified intent
126
+ }
127
+ else {
128
+ console.log(chalk_1.default.gray('\nInvalid choice. Cancelled.\n'));
129
+ return;
130
+ }
131
+ }
132
+ console.log(chalk_1.default.blue('\n✨ Understanding your request...\n'));
133
+ // Show spinner
134
+ const spinner = ['ā ‹', 'ā ™', 'ā ¹', 'ā ø', 'ā ¼', 'ā “', 'ā ¦', 'ā §', 'ā ‡', 'ā '];
135
+ let i = 0;
136
+ const interval = setInterval(() => {
137
+ process.stdout.write(chalk_1.default.blue(`\r${spinner[i++ % spinner.length]} Thinking...`));
138
+ }, 100);
139
+ // Interpret user input
140
+ let intent;
141
+ try {
142
+ intent = await (0, git_interpreter_1.interpretGitCommand)(userInput, {
143
+ currentBranch,
144
+ hasUncommittedChanges,
145
+ lastCommitHash,
146
+ });
147
+ }
148
+ catch (error) {
149
+ clearInterval(interval);
150
+ process.stdout.write('\r' + ' '.repeat(20) + '\r');
151
+ // Try smart solver as ultimate fallback
152
+ console.log(chalk_1.default.yellow('🧠 Using advanced problem solver...\n'));
153
+ const gitStatusOutput = (0, child_process_1.execSync)('git status', { encoding: 'utf-8' });
154
+ const result = await (0, smart_solver_1.solveGitProblem)(userInput, {
155
+ currentBranch,
156
+ gitStatus: gitStatusOutput,
157
+ recentError: error.message,
158
+ });
159
+ if (result.success) {
160
+ await showDopamineStats();
161
+ (0, analytics_1.trackEvent)({ event: analytics_1.EVENTS.NATURAL_COMMAND_SUCCESS });
162
+ }
163
+ else {
164
+ (0, analytics_1.trackEvent)({ event: analytics_1.EVENTS.NATURAL_COMMAND_ERROR });
165
+ }
166
+ return;
167
+ }
168
+ clearInterval(interval);
169
+ process.stdout.write('\r' + ' '.repeat(20) + '\r');
170
+ // Show intent
171
+ console.log(chalk_1.default.green(`✨ I understand: ${chalk_1.default.white.bold(intent.action)}\n`));
172
+ console.log(chalk_1.default.white('This will:'));
173
+ console.log(chalk_1.default.gray(`• ${intent.explanation}\n`));
174
+ // Show educational tip
175
+ if (intent.educationalTip) {
176
+ console.log(chalk_1.default.yellow(`šŸ’” ${intent.educationalTip}\n`));
177
+ }
178
+ // Risk warning
179
+ if (intent.riskLevel === 'high') {
180
+ console.log(chalk_1.default.red('āš ļø Warning: This operation is destructive!\n'));
181
+ }
182
+ // Confirmation
183
+ if (intent.needsConfirmation) {
184
+ const answer = await askQuestion(chalk_1.default.yellow('Continue? (Y/n): '));
185
+ if (answer.toLowerCase() === 'n') {
186
+ console.log(chalk_1.default.gray('\nCancelled.\n'));
187
+ return;
188
+ }
189
+ }
190
+ // Special handling for commit (needs AI message generation)
191
+ if (intent.action === 'commit') {
192
+ await handleCommitWithAI();
193
+ return;
194
+ }
195
+ // Execute git commands with visualization
196
+ console.log(chalk_1.default.blue('šŸ“Ÿ Running Git commands:\n'));
197
+ const result = executeGitCommands(intent.gitCommands, true);
198
+ console.log();
199
+ if (result.success) {
200
+ console.log(chalk_1.default.green.bold(`āœ… Success! ${intent.action} completed\n`));
201
+ // Dopamine hit - show stats/streaks
202
+ await showDopamineStats();
203
+ // Suggest next actions
204
+ const suggestions = (0, git_interpreter_1.suggestNextActions)(intent.action, {
205
+ currentBranch,
206
+ hasUncommittedChanges,
207
+ });
208
+ if (suggestions.length > 0) {
209
+ console.log(chalk_1.default.white('šŸ’” What\'s next?'));
210
+ suggestions.forEach((suggestion) => {
211
+ console.log(chalk_1.default.gray(` • ${suggestion}`));
212
+ });
213
+ console.log();
214
+ }
215
+ (0, analytics_1.trackEvent)({ event: analytics_1.EVENTS.NATURAL_COMMAND_SUCCESS });
216
+ }
217
+ else {
218
+ // First approach failed - try smart solver
219
+ console.log(chalk_1.default.yellow(`\nāš ļø Standard approach failed. Trying advanced solver...\n`));
220
+ const gitStatusOutput = (0, child_process_1.execSync)('git status', { encoding: 'utf-8' });
221
+ const smartResult = await (0, smart_solver_1.solveGitProblem)(userInput, {
222
+ currentBranch,
223
+ gitStatus: gitStatusOutput,
224
+ recentError: result.error,
225
+ });
226
+ if (smartResult.success) {
227
+ await showDopamineStats();
228
+ (0, analytics_1.trackEvent)({ event: analytics_1.EVENTS.NATURAL_COMMAND_SUCCESS });
229
+ }
230
+ else {
231
+ console.log(chalk_1.default.red(`āŒ Could not solve the problem: ${smartResult.error}\n`));
232
+ (0, analytics_1.trackEvent)({ event: analytics_1.EVENTS.NATURAL_COMMAND_ERROR });
233
+ }
234
+ }
235
+ }
236
+ /**
237
+ * Handle commit with AI message generation
238
+ */
239
+ async function handleCommitWithAI() {
240
+ const { getGitDiff, stageAllChanges } = await Promise.resolve().then(() => __importStar(require('../utils/git')));
241
+ console.log(chalk_1.default.blue('šŸ“Ÿ Running Git commands:\n'));
242
+ // Stage all changes
243
+ console.log(chalk_1.default.gray(' $ git add -A'));
244
+ stageAllChanges();
245
+ console.log(chalk_1.default.gray(' āœ“ Staged all changes\n'));
246
+ // Get diff
247
+ let diff;
248
+ try {
249
+ diff = getGitDiff();
250
+ if (diff.length > 40000) {
251
+ diff = diff.substring(0, 40000);
252
+ }
253
+ }
254
+ catch (error) {
255
+ console.log(chalk_1.default.red(' āŒ Could not get diff\n'));
256
+ return;
257
+ }
258
+ // Generate AI commit message
259
+ console.log(chalk_1.default.gray(' ✨ Generating commit message with AI...'));
260
+ let message;
261
+ try {
262
+ message = await (0, anthropic_client_1.generateCommitMessage)(diff);
263
+ console.log(chalk_1.default.gray(` āœ“ Generated: "${message.split('\n')[0]}"\n`));
264
+ }
265
+ catch (error) {
266
+ console.log(chalk_1.default.red(' āŒ AI failed\n'));
267
+ (0, analytics_1.trackEvent)({ event: 'commit_error' });
268
+ return;
269
+ }
270
+ // Commit
271
+ console.log(chalk_1.default.gray(` $ git commit -m "${message.split('\n')[0]}"`));
272
+ try {
273
+ const hash = (0, git_2.commitWithMessage)(message);
274
+ const stats = (0, git_2.getCommitStats)(hash);
275
+ console.log(chalk_1.default.gray(` āœ“ Committed ${hash.substring(0, 7)}\n`));
276
+ console.log(chalk_1.default.green.bold('āœ… Success! Committed your changes\n'));
277
+ console.log(chalk_1.default.gray(` Message: ${message.split('\n')[0]}`));
278
+ console.log(chalk_1.default.gray(` Files: ${stats.files} | +${stats.insertions} -${stats.deletions}\n`));
279
+ // Track
280
+ (0, manager_1.trackCommit)();
281
+ (0, analytics_1.trackEvent)({ event: analytics_1.EVENTS.COMMIT_SUCCESS });
282
+ // Log to database
283
+ (0, database_1.logCommit)({
284
+ message,
285
+ hash,
286
+ files_changed: stats.files,
287
+ insertions: stats.insertions,
288
+ deletions: stats.deletions,
289
+ timestamp: Date.now(),
290
+ });
291
+ // Dopamine
292
+ await showDopamineStats();
293
+ }
294
+ catch (error) {
295
+ console.log(chalk_1.default.red(` āŒ Commit failed: ${error.message}\n`));
296
+ (0, analytics_1.trackEvent)({ event: analytics_1.EVENTS.COMMIT_ERROR });
297
+ }
298
+ }
299
+ /**
300
+ * Show dopamine-inducing stats
301
+ */
302
+ async function showDopamineStats() {
303
+ const { displayQuickDopamine } = await Promise.resolve().then(() => __importStar(require('../utils/dopamine')));
304
+ displayQuickDopamine();
305
+ }
@@ -0,0 +1,111 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.onboardCommand = onboardCommand;
7
+ const chalk_1 = __importDefault(require("chalk"));
8
+ const readline_1 = __importDefault(require("readline"));
9
+ const setup_1 = require("./setup");
10
+ async function onboardCommand() {
11
+ console.clear();
12
+ console.log(chalk_1.default.blue.bold('\nšŸš€ Welcome to SnapCommit!\n'));
13
+ console.log(chalk_1.default.white('The AI that makes you look like a senior developer.\n'));
14
+ await sleep(500);
15
+ // Step 1: What is SnapCommit?
16
+ console.log(chalk_1.default.cyan.bold('What is SnapCommit?\n'));
17
+ console.log(chalk_1.default.gray('SnapCommit uses AI to:'));
18
+ console.log(chalk_1.default.white(' • Generate perfect commit messages'));
19
+ console.log(chalk_1.default.white(' • Track your coding progress'));
20
+ console.log(chalk_1.default.white(' • Make git effortless'));
21
+ console.log();
22
+ await pressEnter();
23
+ // Step 2: How it works
24
+ console.clear();
25
+ console.log(chalk_1.default.blue.bold('\nšŸŽÆ How it works\n'));
26
+ console.log(chalk_1.default.white('Instead of this:'));
27
+ console.log(chalk_1.default.gray(' $ git add .'));
28
+ console.log(chalk_1.default.gray(' $ git commit -m "fix" ') + chalk_1.default.red('← Bad commit message!'));
29
+ console.log();
30
+ console.log(chalk_1.default.white('Just do this:'));
31
+ console.log(chalk_1.default.green(' $ bq ') + chalk_1.default.cyan('← AI writes perfect commits!'));
32
+ console.log();
33
+ await pressEnter();
34
+ // Step 3: Setup
35
+ console.clear();
36
+ console.log(chalk_1.default.blue.bold('\nāš™ļø Let\'s set up SnapCommit\n'));
37
+ console.log(chalk_1.default.white('We\'ll add some aliases to your shell:\n'));
38
+ console.log(chalk_1.default.cyan(' bos ') + chalk_1.default.gray('→ builderos'));
39
+ console.log(chalk_1.default.cyan(' bq ') + chalk_1.default.gray('→ snapcommit quick (instant commit)'));
40
+ console.log(chalk_1.default.cyan(' bs ') + chalk_1.default.gray('→ snapcommit stats'));
41
+ console.log();
42
+ const setupNow = await askYesNo('Run setup now?');
43
+ console.log();
44
+ if (setupNow) {
45
+ (0, setup_1.setupCommand)();
46
+ }
47
+ else {
48
+ console.log(chalk_1.default.gray(' Skipped. Run') + chalk_1.default.cyan(' snapcommit setup') + chalk_1.default.gray(' anytime.'));
49
+ console.log();
50
+ }
51
+ await sleep(500);
52
+ // Step 4: Pricing
53
+ console.clear();
54
+ console.log(chalk_1.default.blue.bold('\nšŸ’° Pricing\n'));
55
+ console.log(chalk_1.default.white.bold('Free Tier:'));
56
+ console.log(chalk_1.default.gray(' • 10 AI commits per month'));
57
+ console.log(chalk_1.default.gray(' • Perfect for trying it out'));
58
+ console.log();
59
+ console.log(chalk_1.default.white.bold('Pro ($9.99/mo or $100/yr):'));
60
+ console.log(chalk_1.default.gray(' • Unlimited AI commits'));
61
+ console.log(chalk_1.default.gray(' • Advanced stats'));
62
+ console.log(chalk_1.default.gray(' • Priority support'));
63
+ console.log();
64
+ console.log(chalk_1.default.cyan('You start on the free tier. Upgrade anytime at builderos.dev/pricing'));
65
+ console.log();
66
+ await pressEnter();
67
+ // Step 5: Quick Start
68
+ console.clear();
69
+ console.log(chalk_1.default.blue.bold('\nšŸŽ‰ You\'re all set!\n'));
70
+ console.log(chalk_1.default.white.bold('Quick Start:\n'));
71
+ console.log(chalk_1.default.cyan(' 1. ') + chalk_1.default.white('cd your-project'));
72
+ console.log(chalk_1.default.cyan(' 2. ') + chalk_1.default.white('Make some changes'));
73
+ console.log(chalk_1.default.cyan(' 3. ') + chalk_1.default.white('Run: ') + chalk_1.default.green.bold('bq'));
74
+ console.log();
75
+ console.log(chalk_1.default.gray('Other commands:'));
76
+ console.log(chalk_1.default.cyan(' snapcommit doctor ') + chalk_1.default.gray('→ Check setup'));
77
+ console.log(chalk_1.default.cyan(' snapcommit stats ') + chalk_1.default.gray('→ See your progress'));
78
+ console.log(chalk_1.default.cyan(' snapcommit --help ') + chalk_1.default.gray('→ All commands'));
79
+ console.log();
80
+ console.log(chalk_1.default.yellow.bold('šŸ’” Pro tip: ') + chalk_1.default.white('Use') + chalk_1.default.cyan(' bq ') + chalk_1.default.white('for instant commits!'));
81
+ console.log();
82
+ console.log(chalk_1.default.gray('Need help? → ') + chalk_1.default.cyan('https://snapcommit.dev/docs'));
83
+ console.log();
84
+ }
85
+ async function pressEnter() {
86
+ const rl = readline_1.default.createInterface({
87
+ input: process.stdin,
88
+ output: process.stdout,
89
+ });
90
+ return new Promise((resolve) => {
91
+ rl.question(chalk_1.default.gray('Press Enter to continue...'), () => {
92
+ rl.close();
93
+ resolve();
94
+ });
95
+ });
96
+ }
97
+ async function askYesNo(question) {
98
+ const rl = readline_1.default.createInterface({
99
+ input: process.stdin,
100
+ output: process.stdout,
101
+ });
102
+ return new Promise((resolve) => {
103
+ rl.question(chalk_1.default.cyan(question + ' (Y/n): '), (answer) => {
104
+ rl.close();
105
+ resolve(answer.toLowerCase() !== 'n');
106
+ });
107
+ });
108
+ }
109
+ function sleep(ms) {
110
+ return new Promise(resolve => setTimeout(resolve, ms));
111
+ }