@snapcommit/cli 2.5.0 → 3.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.
@@ -57,25 +57,25 @@ async function executeCursorStyle(userInput) {
57
57
  await handleAICommand(userInput);
58
58
  }
59
59
  /**
60
- * AI-powered command handler - handles EVERYTHING
61
- * Pure AI interpretation for all operations (like Cursor!)
60
+ * AI-powered command handler - CURSOR-STYLE!
61
+ * Pure AI interpretation, minimal output, instant feel
62
62
  */
63
63
  async function handleAICommand(userInput) {
64
64
  const token = (0, auth_1.getToken)();
65
65
  if (!token) {
66
- console.log(chalk_1.default.red('❌ Not authenticated\n'));
66
+ console.log(chalk_1.default.red('\n❌ Not authenticated\n'));
67
67
  return;
68
68
  }
69
69
  // Get AI interpretation for EVERYTHING
70
70
  const intent = await getAIInterpretation(userInput, token);
71
71
  if (!intent) {
72
- console.log(chalk_1.default.red('❌ Could not understand command'));
73
- console.log(chalk_1.default.gray('Tip: Try "commit and push" or "show me changes"\n'));
72
+ console.log(chalk_1.default.red('\n❌ Could not understand'));
73
+ console.log(chalk_1.default.gray(' Try: "commit and push" or "show changes"\n'));
74
74
  return;
75
75
  }
76
- // Debug: Show what AI understood (for now)
76
+ // Debug mode (if needed)
77
77
  if (process.env.DEBUG) {
78
- console.log(chalk_1.default.gray(`[DEBUG] Intent: ${JSON.stringify(intent, null, 2)}`));
78
+ console.log(chalk_1.default.gray(`[DEBUG] ${JSON.stringify(intent, null, 2)}`));
79
79
  }
80
80
  // Execute based on type
81
81
  if (intent.type === 'git') {
@@ -84,30 +84,26 @@ async function handleAICommand(userInput) {
84
84
  await showStatus();
85
85
  return;
86
86
  }
87
- // For commits, generate AI message first
87
+ // Commit/push flow
88
88
  if (intent.action === 'commit' || intent.action === 'push' ||
89
89
  (intent.gitCommands && intent.gitCommands.some((cmd) => cmd.includes('commit')))) {
90
90
  await executeCommitWithAI(intent);
91
91
  return;
92
92
  }
93
- // Other git commands
93
+ // Other git commands (branch, merge, etc.)
94
94
  if (intent.gitCommands && intent.gitCommands.length > 0) {
95
95
  await executeGitCommands(intent.gitCommands);
96
- console.log(chalk_1.default.green('✓ Done\n'));
96
+ console.log(chalk_1.default.green('✓\n'));
97
97
  }
98
98
  else {
99
- // Debug: show what we got
100
- console.log(chalk_1.default.yellow('⚠️ No commands to execute'));
101
- console.log(chalk_1.default.gray(`Intent action: ${intent.action || 'none'}`));
102
- console.log(chalk_1.default.gray(`Git commands: ${intent.gitCommands?.length || 0}\n`));
99
+ console.log(chalk_1.default.yellow('\n⚠️ No action taken\n'));
103
100
  }
104
101
  }
105
102
  else if (intent.type === 'github') {
106
103
  await executeGitHubCommand(intent);
107
- console.log(chalk_1.default.green('✓ Done\n'));
108
104
  }
109
105
  else {
110
- console.log(chalk_1.default.yellow(`⚠️ Unknown intent type: ${intent.type}\n`));
106
+ console.log(chalk_1.default.yellow(`\n⚠️ Unknown action\n`));
111
107
  }
112
108
  }
113
109
  /**
@@ -132,8 +128,8 @@ async function showStatus() {
132
128
  console.log();
133
129
  }
134
130
  /**
135
- * Execute commit with AI-generated message
136
- * Interactive flow: select files preview message → edit → confirm → commit
131
+ * Execute commit - EXACTLY like Cursor!
132
+ * Minimal friction, one prompt, instant feel
137
133
  */
138
134
  async function executeCommitWithAI(intent) {
139
135
  const status = (0, git_1.getGitStatus)();
@@ -142,77 +138,71 @@ async function executeCommitWithAI(intent) {
142
138
  console.log(chalk_1.default.gray('\n✓ Branch clean\n'));
143
139
  return;
144
140
  }
145
- // Show what changed
146
- console.log(chalk_1.default.blue('\n📦 Changes detected:'));
147
- if (status.unstaged > 0)
148
- console.log(chalk_1.default.yellow(` • ${status.unstaged} modified`));
149
- if (status.untracked > 0)
150
- console.log(chalk_1.default.yellow(` • ${status.untracked} new`));
151
- console.log();
152
- // Ask if they want to commit all files
153
- const readline = await Promise.resolve().then(() => __importStar(require('readline')));
154
- const rl = readline.createInterface({
155
- input: process.stdin,
156
- output: process.stdout,
157
- });
158
- const commitAll = await new Promise((resolve) => {
159
- rl.question(chalk_1.default.cyan('Commit all files? (Y/n): '), (ans) => {
160
- rl.close();
161
- resolve(ans);
141
+ // Count files
142
+ let fileCount = status.staged + status.unstaged + status.untracked;
143
+ // Show changed files (like Cursor sidebar)
144
+ console.log(chalk_1.default.blue(`\n📦 Changes (${fileCount} ${fileCount === 1 ? 'file' : 'files'}):`));
145
+ try {
146
+ const output = (0, child_process_1.execSync)('git status --short', { encoding: 'utf-8' });
147
+ const lines = output.split('\n').filter(line => line.trim()).slice(0, 10); // Show max 10
148
+ lines.forEach(line => {
149
+ const status = line.substring(0, 2);
150
+ const file = line.substring(3);
151
+ if (status.includes('M')) {
152
+ console.log(chalk_1.default.yellow(` ✎ ${file}`));
153
+ }
154
+ else if (status.includes('?')) {
155
+ console.log(chalk_1.default.green(` + ${file}`));
156
+ }
157
+ else if (status.includes('D')) {
158
+ console.log(chalk_1.default.red(` - ${file}`));
159
+ }
162
160
  });
163
- });
164
- if (commitAll.toLowerCase() === 'n') {
165
- console.log(chalk_1.default.yellow('\n💡 Tip: Use "git add <files>" to stage specific files, then try again\n'));
166
- return;
161
+ if (fileCount > 10) {
162
+ console.log(chalk_1.default.gray(` ... and ${fileCount - 10} more`));
163
+ }
167
164
  }
165
+ catch {
166
+ if (status.unstaged > 0)
167
+ console.log(chalk_1.default.yellow(` • ${status.unstaged} modified`));
168
+ if (status.untracked > 0)
169
+ console.log(chalk_1.default.green(` • ${status.untracked} new`));
170
+ }
171
+ console.log();
168
172
  // Stage all
169
173
  try {
170
174
  (0, git_1.stageAllChanges)();
171
175
  }
172
176
  catch (error) {
173
- console.log(chalk_1.default.red(`\n❌ ${error.message}\n`));
177
+ console.log(chalk_1.default.red(`❌ ${error.message}\n`));
174
178
  return;
175
179
  }
176
- // Generate AI commit message
177
- console.log(chalk_1.default.blue('🤖 Generating commit message...\n'));
180
+ // Generate AI commit message (like Cursor does instantly)
178
181
  const diff = (0, git_1.getGitDiff)(true);
179
182
  let commitMessage = await generateCommitMessage(diff);
180
- // Show message
181
- console.log(chalk_1.default.cyan('📝 Commit message:'));
182
- console.log(chalk_1.default.white.bold(` ${commitMessage.split('\n')[0]}\n`));
183
- // Ask if message is good
184
- const rl2 = readline.createInterface({
183
+ // Show message like Cursor - clean and ready to use
184
+ console.log(chalk_1.default.cyan('🤖 ') + chalk_1.default.white.bold(commitMessage.split('\n')[0]));
185
+ console.log();
186
+ // ONE PROMPT - like Cursor's commit button
187
+ const readline = await Promise.resolve().then(() => __importStar(require('readline')));
188
+ const rl = readline.createInterface({
185
189
  input: process.stdin,
186
190
  output: process.stdout,
187
191
  });
188
- const messageOk = await new Promise((resolve) => {
189
- rl2.question(chalk_1.default.cyan('Is this message okay? ') + chalk_1.default.gray('(Y/n/edit): '), (ans) => {
190
- rl2.close();
191
- resolve(ans);
192
+ const response = await new Promise((resolve) => {
193
+ rl.question(chalk_1.default.gray(' Press Enter to commit, or edit message: '), (ans) => {
194
+ rl.close();
195
+ resolve(ans.trim());
192
196
  });
193
197
  });
194
- // Handle response
195
- if (messageOk.toLowerCase() === 'n') {
196
- console.log(chalk_1.default.gray('\n✗ Cancelled\n'));
197
- return;
198
- }
199
- if (messageOk.toLowerCase() === 'edit' || messageOk.toLowerCase() === 'e') {
200
- const rl3 = readline.createInterface({
201
- input: process.stdin,
202
- output: process.stdout,
203
- });
204
- commitMessage = await new Promise((resolve) => {
205
- rl3.question(chalk_1.default.cyan('New message: '), (ans) => {
206
- rl3.close();
207
- resolve(ans || commitMessage);
208
- });
209
- });
210
- console.log(chalk_1.default.green('\n✓ Message updated\n'));
198
+ // If they typed something, use it. Otherwise use AI message.
199
+ if (response) {
200
+ commitMessage = response;
211
201
  }
212
- // Commit
202
+ // Commit (like Cursor's instant commit)
213
203
  try {
214
204
  (0, child_process_1.execSync)(`git commit -m "${commitMessage.replace(/"/g, '\\"')}"`, { encoding: 'utf-8', stdio: 'pipe' });
215
- console.log(chalk_1.default.green(`✓ Committed: ${commitMessage.split('\n')[0]}`));
205
+ console.log(chalk_1.default.green(`\n✓ Committed`));
216
206
  }
217
207
  catch (error) {
218
208
  console.log(chalk_1.default.red(`\n❌ Commit failed: ${error.message}\n`));
@@ -230,7 +220,7 @@ async function executeCommitWithAI(intent) {
230
220
  console.log(chalk_1.default.gray('✓ Committed locally (no remote)\n'));
231
221
  }
232
222
  else {
233
- console.log(chalk_1.default.yellow(`\n⚠️ Push failed: ${error.message}\n`));
223
+ console.log(chalk_1.default.yellow(`⚠️ Push failed: ${error.message}\n`));
234
224
  }
235
225
  }
236
226
  }
@@ -239,69 +229,121 @@ async function executeCommitWithAI(intent) {
239
229
  }
240
230
  }
241
231
  /**
242
- * Execute Git commands with auto-retry on errors
232
+ * Execute Git commands - Cursor-style (clean, fast, auto-fix)
243
233
  */
244
234
  async function executeGitCommands(commands) {
245
235
  for (const cmd of commands) {
246
236
  try {
247
- (0, child_process_1.execSync)(cmd, { encoding: 'utf-8', stdio: 'pipe' });
237
+ const output = (0, child_process_1.execSync)(cmd, { encoding: 'utf-8', stdio: 'pipe' });
238
+ // Show meaningful output for certain commands
239
+ if (cmd.includes('git branch') && !cmd.includes('-D')) {
240
+ if (output.trim()) {
241
+ console.log(chalk_1.default.cyan('\n' + output.trim() + '\n'));
242
+ }
243
+ }
244
+ else if (cmd.includes('git log')) {
245
+ console.log(chalk_1.default.cyan('\n' + output.trim() + '\n'));
246
+ }
247
+ else if (cmd.includes('git diff') && !cmd.includes('--name-only')) {
248
+ if (output.trim()) {
249
+ console.log(chalk_1.default.gray('\n' + output.trim().substring(0, 500) + (output.length > 500 ? '...' : '') + '\n'));
250
+ }
251
+ }
248
252
  }
249
253
  catch (error) {
250
254
  // Try to auto-fix common errors
251
255
  const fixed = await tryAutoFix(error, cmd);
252
256
  if (!fixed) {
253
- console.log(chalk_1.default.red(`❌ ${error.message}\n`));
257
+ console.log(chalk_1.default.red(`\n❌ ${error.message}\n`));
254
258
  return;
255
259
  }
256
260
  }
257
261
  }
258
262
  }
259
263
  /**
260
- * Execute GitHub operations (PRs, CI, issues)
264
+ * Execute GitHub operations - Cursor-style streamlined!
261
265
  */
262
266
  async function executeGitHubCommand(intent) {
263
267
  if (!(0, github_connect_1.isGitHubConnected)()) {
264
- console.log(chalk_1.default.yellow('⚠️ GitHub not connected. Run: snap github connect\n'));
268
+ console.log(chalk_1.default.yellow('\n⚠️ GitHub not connected'));
269
+ console.log(chalk_1.default.gray(' Run: ') + chalk_1.default.cyan('snap github connect\n'));
265
270
  return;
266
271
  }
267
272
  try {
268
273
  switch (intent.action) {
269
274
  case 'pr_create':
270
- console.log(chalk_1.default.blue('Creating PR...'));
275
+ console.log(chalk_1.default.blue('\n🔄 Creating PR...'));
271
276
  const pr = await github.createPullRequest(intent.options || {});
272
- console.log(chalk_1.default.green(`✓ PR created: ${pr.html_url}`));
277
+ console.log(chalk_1.default.green(`✓ PR #${pr.number} created`));
278
+ console.log(chalk_1.default.cyan(` ${pr.html_url}\n`));
273
279
  break;
274
280
  case 'pr_list':
275
- console.log(chalk_1.default.blue('Listing PRs...'));
276
281
  const prs = await github.listPullRequests({ state: 'open', limit: 10 });
277
282
  if (prs.length === 0) {
278
- console.log(chalk_1.default.gray('No open PRs'));
283
+ console.log(chalk_1.default.gray('\n✓ No open PRs\n'));
279
284
  }
280
285
  else {
281
- prs.forEach((p) => console.log(chalk_1.default.cyan(` #${p.number}: ${p.title}`)));
286
+ console.log(chalk_1.default.blue(`\n📋 Open PRs (${prs.length}):`));
287
+ prs.forEach((p) => {
288
+ console.log(chalk_1.default.cyan(` #${p.number} `) + chalk_1.default.white(p.title));
289
+ });
290
+ console.log();
291
+ }
292
+ break;
293
+ case 'pr_merge':
294
+ const prNumber = intent.target || intent.options?.number;
295
+ if (!prNumber) {
296
+ console.log(chalk_1.default.red('\n❌ PR number required\n'));
297
+ return;
298
+ }
299
+ console.log(chalk_1.default.blue(`\n🔄 Merging PR #${prNumber}...`));
300
+ await github.mergePullRequest(prNumber);
301
+ console.log(chalk_1.default.green(`✓ PR #${prNumber} merged\n`));
302
+ break;
303
+ case 'ci_check':
304
+ console.log(chalk_1.default.blue('\n🔍 Checking CI status...'));
305
+ // TODO: Implement CI status check
306
+ console.log(chalk_1.default.gray(' CI checks (coming soon)\n'));
307
+ break;
308
+ case 'issue_create':
309
+ console.log(chalk_1.default.blue('\n🔄 Creating issue...'));
310
+ const issue = await github.createIssue(intent.options || {});
311
+ console.log(chalk_1.default.green(`✓ Issue #${issue.number} created`));
312
+ console.log(chalk_1.default.cyan(` ${issue.html_url}\n`));
313
+ break;
314
+ case 'issue_list':
315
+ const issues = await github.listIssues({ state: 'open', limit: 10 });
316
+ if (issues.length === 0) {
317
+ console.log(chalk_1.default.gray('\n✓ No open issues\n'));
318
+ }
319
+ else {
320
+ console.log(chalk_1.default.blue(`\n🐛 Open Issues (${issues.length}):`));
321
+ issues.forEach((i) => {
322
+ console.log(chalk_1.default.cyan(` #${i.number} `) + chalk_1.default.white(i.title));
323
+ });
324
+ console.log();
282
325
  }
283
326
  break;
284
327
  default:
285
- console.log(chalk_1.default.yellow(`⚠️ GitHub action not implemented: ${intent.action}\n`));
328
+ console.log(chalk_1.default.yellow(`\n⚠️ Action not supported: ${intent.action}\n`));
286
329
  }
287
330
  }
288
331
  catch (error) {
289
- console.log(chalk_1.default.red(`❌ ${error.message}\n`));
332
+ console.log(chalk_1.default.red(`\n❌ ${error.message}\n`));
290
333
  }
291
334
  }
292
335
  /**
293
- * Auto-fix common Git errors (like Cursor does)
336
+ * Auto-fix common Git errors (like Cursor does - silently when possible)
294
337
  */
295
338
  async function tryAutoFix(error, command) {
296
339
  const errorMsg = error.message?.toLowerCase() || '';
297
340
  // Merge conflict
298
341
  if (errorMsg.includes('conflict')) {
299
- console.log(chalk_1.default.yellow('⚠️ Merge conflict - attempting auto-resolve...'));
342
+ console.log(chalk_1.default.yellow('\n⚠️ Conflict detected - auto-resolving...'));
300
343
  try {
301
- // Try to accept current changes
302
344
  (0, child_process_1.execSync)('git add .', { encoding: 'utf-8', stdio: 'pipe' });
303
345
  (0, child_process_1.execSync)('git commit --no-edit', { encoding: 'utf-8', stdio: 'pipe' });
304
- console.log(chalk_1.default.green('✓ Auto-resolved'));
346
+ console.log(chalk_1.default.green('✓ Resolved\n'));
305
347
  return true;
306
348
  }
307
349
  catch {
@@ -310,11 +352,11 @@ async function tryAutoFix(error, command) {
310
352
  }
311
353
  // Diverged branches
312
354
  if (errorMsg.includes('diverged') || errorMsg.includes('non-fast-forward')) {
313
- console.log(chalk_1.default.yellow('⚠️ Branches diverged - pulling and retrying...'));
355
+ console.log(chalk_1.default.yellow('\n⚠️ Syncing with remote...'));
314
356
  try {
315
357
  (0, child_process_1.execSync)('git pull --rebase', { encoding: 'utf-8', stdio: 'pipe' });
316
358
  (0, child_process_1.execSync)(command, { encoding: 'utf-8', stdio: 'pipe' });
317
- console.log(chalk_1.default.green('✓ Fixed'));
359
+ console.log(chalk_1.default.green('✓ Synced\n'));
318
360
  return true;
319
361
  }
320
362
  catch {
@@ -323,12 +365,12 @@ async function tryAutoFix(error, command) {
323
365
  }
324
366
  // Unstaged changes
325
367
  if (errorMsg.includes('unstaged') || errorMsg.includes('uncommitted')) {
326
- console.log(chalk_1.default.yellow('⚠️ Unstaged changes - stashing and retrying...'));
368
+ console.log(chalk_1.default.yellow('\n⚠️ Stashing changes...'));
327
369
  try {
328
370
  (0, child_process_1.execSync)('git stash', { encoding: 'utf-8', stdio: 'pipe' });
329
371
  (0, child_process_1.execSync)(command, { encoding: 'utf-8', stdio: 'pipe' });
330
372
  (0, child_process_1.execSync)('git stash pop', { encoding: 'utf-8', stdio: 'pipe' });
331
- console.log(chalk_1.default.green('✓ Fixed'));
373
+ console.log(chalk_1.default.green('✓ Restored\n'));
332
374
  return true;
333
375
  }
334
376
  catch {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@snapcommit/cli",
3
- "version": "2.5.0",
3
+ "version": "3.0.0",
4
4
  "description": "Instant AI commits. Beautiful progress tracking. Never write commit messages again.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {