@snapcommit/cli 2.1.0 → 2.2.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.
@@ -1,8 +1,41 @@
1
1
  "use strict";
2
2
  /**
3
3
  * Cursor-style natural language Git assistant
4
- * DEAD SIMPLE: Check status Show changes Commit → Push → Done!
4
+ * Fast, simple, auto-fixes errors, handles ALL Git + GitHub operations
5
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
+ })();
6
39
  var __importDefault = (this && this.__importDefault) || function (mod) {
7
40
  return (mod && mod.__esModule) ? mod : { "default": mod };
8
41
  };
@@ -12,67 +45,220 @@ const chalk_1 = __importDefault(require("chalk"));
12
45
  const child_process_1 = require("child_process");
13
46
  const git_1 = require("../utils/git");
14
47
  const auth_1 = require("../lib/auth");
48
+ const github_connect_1 = require("./github-connect");
49
+ const github = __importStar(require("../lib/github"));
15
50
  async function executeCursorStyle(userInput) {
16
51
  if (!(0, git_1.isGitRepo)()) {
17
52
  console.log(chalk_1.default.red('\n❌ Not a git repository\n'));
18
53
  return;
19
54
  }
20
- // Check if there are any changes first
55
+ const input = userInput.toLowerCase().trim();
56
+ // COMMIT/PUSH: Always use AI for commit messages (like Cursor!)
57
+ if (input.includes('commit') || input.includes('push') || input.includes('save')) {
58
+ await handleCommitAndPush();
59
+ return;
60
+ }
61
+ // OTHER OPERATIONS: Branch, merge, rebase, GitHub, etc.
62
+ await handleComplexCommand(userInput);
63
+ }
64
+ /**
65
+ * Commit and push - ALWAYS uses AI for commit messages (like Cursor!)
66
+ * Fast, clean, no prompts - just generates and commits
67
+ */
68
+ async function handleCommitAndPush() {
21
69
  const status = (0, git_1.getGitStatus)();
22
70
  const hasChanges = status.staged > 0 || status.unstaged > 0 || status.untracked > 0;
23
71
  if (!hasChanges) {
24
- console.log(chalk_1.default.gray('\n✓ Branch clean - no changes to commit\n'));
72
+ console.log(chalk_1.default.gray('\n✓ Branch clean\n'));
25
73
  return;
26
74
  }
27
- // Show what changed
28
- console.log(chalk_1.default.blue('\n📦 Changes detected:\n'));
29
- if (status.unstaged > 0)
30
- console.log(chalk_1.default.yellow(` • ${status.unstaged} modified`));
31
- if (status.untracked > 0)
32
- console.log(chalk_1.default.yellow(` • ${status.untracked} new`));
33
- if (status.staged > 0)
34
- console.log(chalk_1.default.green(` • ${status.staged} staged`));
35
- console.log();
36
- // Stage everything
75
+ // Stage all
37
76
  try {
38
77
  (0, git_1.stageAllChanges)();
39
78
  }
40
79
  catch (error) {
41
- console.log(chalk_1.default.red(`❌ Failed to stage changes: ${error.message}\n`));
80
+ console.log(chalk_1.default.red(`❌ ${error.message}\n`));
42
81
  return;
43
82
  }
44
- // Generate commit message
45
- console.log(chalk_1.default.blue('🤖 Generating commit message...'));
83
+ // Generate AI commit message (THE CORE FEATURE!)
46
84
  const diff = (0, git_1.getGitDiff)(true);
47
85
  const commitMessage = await generateCommitMessage(diff);
48
- console.log(chalk_1.default.green(`\n📝 ${commitMessage.split('\n')[0]}\n`));
49
- // Commit
86
+ // Commit with AI-generated message
50
87
  try {
51
88
  (0, child_process_1.execSync)(`git commit -m "${commitMessage.replace(/"/g, '\\"')}"`, { encoding: 'utf-8', stdio: 'pipe' });
52
- console.log(chalk_1.default.green('✓ Committed'));
89
+ console.log(chalk_1.default.green(`✓ ${commitMessage.split('\n')[0]}`));
53
90
  }
54
91
  catch (error) {
55
- console.log(chalk_1.default.red(`❌ Commit failed: ${error.message}\n`));
92
+ console.log(chalk_1.default.red(`❌ Commit failed\n`));
56
93
  return;
57
94
  }
58
- // Push
95
+ // Push to remote
59
96
  try {
60
- console.log(chalk_1.default.blue('⬆️ Pushing to remote...'));
61
97
  (0, child_process_1.execSync)('git push', { encoding: 'utf-8', stdio: 'pipe' });
62
98
  console.log(chalk_1.default.green('✓ Pushed\n'));
63
99
  }
64
100
  catch (error) {
65
- // If push fails (e.g., no remote), that's okay
66
101
  if (error.message.includes('no configured push destination')) {
67
- console.log(chalk_1.default.yellow('⚠️ No remote configured - changes committed locally\n'));
102
+ console.log(chalk_1.default.gray(' Committed locally (no remote)\n'));
68
103
  }
69
104
  else {
70
- console.log(chalk_1.default.yellow(`⚠️ Push failed: ${error.message}\n`));
105
+ console.log(chalk_1.default.yellow(`⚠️ ${error.message}\n`));
106
+ }
107
+ }
108
+ }
109
+ /**
110
+ * Complex path: Use AI to interpret and execute any Git/GitHub command
111
+ */
112
+ async function handleComplexCommand(userInput) {
113
+ const token = (0, auth_1.getToken)();
114
+ if (!token) {
115
+ console.log(chalk_1.default.red('❌ Not authenticated\n'));
116
+ return;
117
+ }
118
+ // Get AI interpretation
119
+ const intent = await getAIInterpretation(userInput, token);
120
+ if (!intent) {
121
+ console.log(chalk_1.default.red('❌ Could not understand command\n'));
122
+ return;
123
+ }
124
+ // Execute based on type
125
+ if (intent.type === 'git') {
126
+ await executeGitCommands(intent.gitCommands || []);
127
+ }
128
+ else if (intent.type === 'github') {
129
+ await executeGitHubCommand(intent);
130
+ }
131
+ console.log(chalk_1.default.green('✓ Done\n'));
132
+ }
133
+ /**
134
+ * Execute Git commands with auto-retry on errors
135
+ */
136
+ async function executeGitCommands(commands) {
137
+ for (const cmd of commands) {
138
+ try {
139
+ (0, child_process_1.execSync)(cmd, { encoding: 'utf-8', stdio: 'pipe' });
140
+ }
141
+ catch (error) {
142
+ // Try to auto-fix common errors
143
+ const fixed = await tryAutoFix(error, cmd);
144
+ if (!fixed) {
145
+ console.log(chalk_1.default.red(`❌ ${error.message}\n`));
146
+ return;
147
+ }
71
148
  }
72
149
  }
73
- console.log(chalk_1.default.green('✅ Done!\n'));
74
150
  }
75
- // All removed - we're keeping it SUPER simple now!
151
+ /**
152
+ * Execute GitHub operations (PRs, CI, issues)
153
+ */
154
+ async function executeGitHubCommand(intent) {
155
+ if (!(0, github_connect_1.isGitHubConnected)()) {
156
+ console.log(chalk_1.default.yellow('⚠️ GitHub not connected. Run: snap github connect\n'));
157
+ return;
158
+ }
159
+ try {
160
+ switch (intent.action) {
161
+ case 'pr_create':
162
+ console.log(chalk_1.default.blue('Creating PR...'));
163
+ const pr = await github.createPullRequest(intent.options || {});
164
+ console.log(chalk_1.default.green(`✓ PR created: ${pr.html_url}`));
165
+ break;
166
+ case 'pr_list':
167
+ console.log(chalk_1.default.blue('Listing PRs...'));
168
+ const prs = await github.listPullRequests({ state: 'open', limit: 10 });
169
+ if (prs.length === 0) {
170
+ console.log(chalk_1.default.gray('No open PRs'));
171
+ }
172
+ else {
173
+ prs.forEach((p) => console.log(chalk_1.default.cyan(` #${p.number}: ${p.title}`)));
174
+ }
175
+ break;
176
+ default:
177
+ console.log(chalk_1.default.yellow(`⚠️ GitHub action not implemented: ${intent.action}\n`));
178
+ }
179
+ }
180
+ catch (error) {
181
+ console.log(chalk_1.default.red(`❌ ${error.message}\n`));
182
+ }
183
+ }
184
+ /**
185
+ * Auto-fix common Git errors (like Cursor does)
186
+ */
187
+ async function tryAutoFix(error, command) {
188
+ const errorMsg = error.message?.toLowerCase() || '';
189
+ // Merge conflict
190
+ if (errorMsg.includes('conflict')) {
191
+ console.log(chalk_1.default.yellow('⚠️ Merge conflict - attempting auto-resolve...'));
192
+ try {
193
+ // Try to accept current changes
194
+ (0, child_process_1.execSync)('git add .', { encoding: 'utf-8', stdio: 'pipe' });
195
+ (0, child_process_1.execSync)('git commit --no-edit', { encoding: 'utf-8', stdio: 'pipe' });
196
+ console.log(chalk_1.default.green('✓ Auto-resolved'));
197
+ return true;
198
+ }
199
+ catch {
200
+ return false;
201
+ }
202
+ }
203
+ // Diverged branches
204
+ if (errorMsg.includes('diverged') || errorMsg.includes('non-fast-forward')) {
205
+ console.log(chalk_1.default.yellow('⚠️ Branches diverged - pulling and retrying...'));
206
+ try {
207
+ (0, child_process_1.execSync)('git pull --rebase', { encoding: 'utf-8', stdio: 'pipe' });
208
+ (0, child_process_1.execSync)(command, { encoding: 'utf-8', stdio: 'pipe' });
209
+ console.log(chalk_1.default.green('✓ Fixed'));
210
+ return true;
211
+ }
212
+ catch {
213
+ return false;
214
+ }
215
+ }
216
+ // Unstaged changes
217
+ if (errorMsg.includes('unstaged') || errorMsg.includes('uncommitted')) {
218
+ console.log(chalk_1.default.yellow('⚠️ Unstaged changes - stashing and retrying...'));
219
+ try {
220
+ (0, child_process_1.execSync)('git stash', { encoding: 'utf-8', stdio: 'pipe' });
221
+ (0, child_process_1.execSync)(command, { encoding: 'utf-8', stdio: 'pipe' });
222
+ (0, child_process_1.execSync)('git stash pop', { encoding: 'utf-8', stdio: 'pipe' });
223
+ console.log(chalk_1.default.green('✓ Fixed'));
224
+ return true;
225
+ }
226
+ catch {
227
+ return false;
228
+ }
229
+ }
230
+ return false;
231
+ }
232
+ /**
233
+ * Get AI interpretation for complex commands
234
+ */
235
+ async function getAIInterpretation(userInput, token) {
236
+ try {
237
+ const currentBranch = (0, git_1.getCurrentBranch)();
238
+ const status = (0, git_1.getGitStatus)();
239
+ const response = await fetch('https://snapcommit.dev/api/ai/interpret', {
240
+ method: 'POST',
241
+ headers: { 'Content-Type': 'application/json' },
242
+ body: JSON.stringify({
243
+ userInput,
244
+ token,
245
+ context: {
246
+ currentBranch,
247
+ hasUncommittedChanges: status.unstaged > 0 || status.untracked > 0,
248
+ lastCommitHash: (0, child_process_1.execSync)('git log -1 --format=%H 2>/dev/null || echo ""', { encoding: 'utf-8' }).trim(),
249
+ remoteBranch: (0, child_process_1.execSync)('git rev-parse --abbrev-ref @{upstream} 2>/dev/null || echo ""', { encoding: 'utf-8' }).trim(),
250
+ },
251
+ }),
252
+ });
253
+ if (!response.ok)
254
+ return null;
255
+ const data = await response.json();
256
+ return data.intent;
257
+ }
258
+ catch {
259
+ return null;
260
+ }
261
+ }
76
262
  async function generateCommitMessage(diff) {
77
263
  const token = (0, auth_1.getToken)();
78
264
  if (!token) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@snapcommit/cli",
3
- "version": "2.1.0",
3
+ "version": "2.2.1",
4
4
  "description": "Instant AI commits. Beautiful progress tracking. Never write commit messages again.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {