@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.
- package/README.md +162 -0
- package/dist/ai/anthropic-client.js +92 -0
- package/dist/ai/commit-generator.js +200 -0
- package/dist/ai/gemini-client.js +201 -0
- package/dist/ai/git-interpreter.js +209 -0
- package/dist/ai/smart-solver.js +260 -0
- package/dist/auth/supabase-client.js +288 -0
- package/dist/commands/activate.js +108 -0
- package/dist/commands/commit.js +255 -0
- package/dist/commands/conflict.js +233 -0
- package/dist/commands/doctor.js +113 -0
- package/dist/commands/git-advanced.js +311 -0
- package/dist/commands/github-auth.js +193 -0
- package/dist/commands/login.js +11 -0
- package/dist/commands/natural.js +305 -0
- package/dist/commands/onboard.js +111 -0
- package/dist/commands/quick.js +173 -0
- package/dist/commands/setup.js +163 -0
- package/dist/commands/stats.js +128 -0
- package/dist/commands/uninstall.js +131 -0
- package/dist/db/database.js +99 -0
- package/dist/index.js +144 -0
- package/dist/lib/auth.js +171 -0
- package/dist/lib/github.js +280 -0
- package/dist/lib/multi-repo.js +276 -0
- package/dist/lib/supabase.js +153 -0
- package/dist/license/manager.js +203 -0
- package/dist/repl/index.js +185 -0
- package/dist/repl/interpreter.js +524 -0
- package/dist/utils/analytics.js +36 -0
- package/dist/utils/auth-storage.js +65 -0
- package/dist/utils/dopamine.js +211 -0
- package/dist/utils/errors.js +56 -0
- package/dist/utils/git.js +105 -0
- package/dist/utils/heatmap.js +265 -0
- package/dist/utils/rate-limit.js +68 -0
- package/dist/utils/retry.js +46 -0
- package/dist/utils/ui.js +189 -0
- package/dist/utils/version.js +81 -0
- 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
|
+
}
|