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