@snapcommit/cli 2.6.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
- * Natural language interactive flow - like talking to Cursor!
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,11 +138,14 @@ async function executeCommitWithAI(intent) {
142
138
  console.log(chalk_1.default.gray('\n✓ Branch clean\n'));
143
139
  return;
144
140
  }
145
- // Show ALL changed files with details
146
- console.log(chalk_1.default.blue('\n📦 Files changed:\n'));
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'}):`));
147
145
  try {
148
146
  const output = (0, child_process_1.execSync)('git status --short', { encoding: 'utf-8' });
149
- output.split('\n').filter(line => line.trim()).forEach(line => {
147
+ const lines = output.split('\n').filter(line => line.trim()).slice(0, 10); // Show max 10
148
+ lines.forEach(line => {
150
149
  const status = line.substring(0, 2);
151
150
  const file = line.substring(3);
152
151
  if (status.includes('M')) {
@@ -159,6 +158,9 @@ async function executeCommitWithAI(intent) {
159
158
  console.log(chalk_1.default.red(` - ${file}`));
160
159
  }
161
160
  });
161
+ if (fileCount > 10) {
162
+ console.log(chalk_1.default.gray(` ... and ${fileCount - 10} more`));
163
+ }
162
164
  }
163
165
  catch {
164
166
  if (status.unstaged > 0)
@@ -167,79 +169,40 @@ async function executeCommitWithAI(intent) {
167
169
  console.log(chalk_1.default.green(` • ${status.untracked} new`));
168
170
  }
169
171
  console.log();
170
- // Natural language file selection
171
- const readline = await Promise.resolve().then(() => __importStar(require('readline')));
172
- const rl = readline.createInterface({
173
- input: process.stdin,
174
- output: process.stdout,
175
- });
176
- const fileChoice = await new Promise((resolve) => {
177
- rl.question(chalk_1.default.cyan('What do you want to do? ') + chalk_1.default.gray('(commit all / select files / cancel): '), (ans) => {
178
- rl.close();
179
- resolve(ans.toLowerCase().trim());
180
- });
181
- });
182
- if (fileChoice.includes('cancel') || fileChoice === 'n' || fileChoice === 'no') {
183
- console.log(chalk_1.default.gray('\n✗ Cancelled\n'));
184
- return;
185
- }
186
- if (fileChoice.includes('select')) {
187
- console.log(chalk_1.default.yellow('\n💡 Use: ') + chalk_1.default.cyan('git add <files>') + chalk_1.default.yellow(' to stage specific files'));
188
- console.log(chalk_1.default.gray(' Then run: ') + chalk_1.default.cyan('snap') + chalk_1.default.gray(' and try again\n'));
189
- return;
190
- }
191
- // Stage all (default for "commit all" or just pressing Enter)
172
+ // Stage all
192
173
  try {
193
174
  (0, git_1.stageAllChanges)();
194
175
  }
195
176
  catch (error) {
196
- console.log(chalk_1.default.red(`\n❌ ${error.message}\n`));
177
+ console.log(chalk_1.default.red(`❌ ${error.message}\n`));
197
178
  return;
198
179
  }
199
- // Generate AI commit message
200
- console.log(chalk_1.default.blue('🤖 Generating commit message...\n'));
180
+ // Generate AI commit message (like Cursor does instantly)
201
181
  const diff = (0, git_1.getGitDiff)(true);
202
182
  let commitMessage = await generateCommitMessage(diff);
203
- // Show message with natural language prompt
204
- console.log(chalk_1.default.cyan('📝 AI generated this commit message:'));
205
- console.log(chalk_1.default.white.bold(` "${commitMessage.split('\n')[0]}"\n`));
206
- 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({
207
189
  input: process.stdin,
208
190
  output: process.stdout,
209
191
  });
210
- const messageAction = await new Promise((resolve) => {
211
- rl2.question(chalk_1.default.cyan('What next? ') + chalk_1.default.gray('(looks good / edit / type new message / cancel): '), (ans) => {
212
- rl2.close();
213
- resolve(ans.toLowerCase().trim());
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());
214
196
  });
215
197
  });
216
- // Handle response naturally
217
- if (messageAction.includes('cancel') || messageAction === 'no' || messageAction === 'n') {
218
- console.log(chalk_1.default.gray('\n✗ Cancelled\n'));
219
- return;
198
+ // If they typed something, use it. Otherwise use AI message.
199
+ if (response) {
200
+ commitMessage = response;
220
201
  }
221
- if (messageAction.includes('edit') || messageAction.includes('change')) {
222
- const rl3 = readline.createInterface({
223
- input: process.stdin,
224
- output: process.stdout,
225
- });
226
- commitMessage = await new Promise((resolve) => {
227
- rl3.question(chalk_1.default.cyan('Your message: '), (ans) => {
228
- rl3.close();
229
- resolve(ans.trim() || commitMessage);
230
- });
231
- });
232
- console.log(chalk_1.default.green('\n✓ Message updated\n'));
233
- }
234
- else if (messageAction && !messageAction.includes('good') && !messageAction.includes('yes') && !messageAction.includes('ok')) {
235
- // They typed a custom message directly
236
- commitMessage = messageAction;
237
- console.log(chalk_1.default.green('\n✓ Using your message\n'));
238
- }
239
- // Commit
202
+ // Commit (like Cursor's instant commit)
240
203
  try {
241
204
  (0, child_process_1.execSync)(`git commit -m "${commitMessage.replace(/"/g, '\\"')}"`, { encoding: 'utf-8', stdio: 'pipe' });
242
- console.log(chalk_1.default.green(`✓ ${commitMessage.split('\n')[0]}`));
205
+ console.log(chalk_1.default.green(`\n✓ Committed`));
243
206
  }
244
207
  catch (error) {
245
208
  console.log(chalk_1.default.red(`\n❌ Commit failed: ${error.message}\n`));
@@ -266,69 +229,121 @@ async function executeCommitWithAI(intent) {
266
229
  }
267
230
  }
268
231
  /**
269
- * Execute Git commands with auto-retry on errors
232
+ * Execute Git commands - Cursor-style (clean, fast, auto-fix)
270
233
  */
271
234
  async function executeGitCommands(commands) {
272
235
  for (const cmd of commands) {
273
236
  try {
274
- (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
+ }
275
252
  }
276
253
  catch (error) {
277
254
  // Try to auto-fix common errors
278
255
  const fixed = await tryAutoFix(error, cmd);
279
256
  if (!fixed) {
280
- console.log(chalk_1.default.red(`❌ ${error.message}\n`));
257
+ console.log(chalk_1.default.red(`\n❌ ${error.message}\n`));
281
258
  return;
282
259
  }
283
260
  }
284
261
  }
285
262
  }
286
263
  /**
287
- * Execute GitHub operations (PRs, CI, issues)
264
+ * Execute GitHub operations - Cursor-style streamlined!
288
265
  */
289
266
  async function executeGitHubCommand(intent) {
290
267
  if (!(0, github_connect_1.isGitHubConnected)()) {
291
- 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'));
292
270
  return;
293
271
  }
294
272
  try {
295
273
  switch (intent.action) {
296
274
  case 'pr_create':
297
- console.log(chalk_1.default.blue('Creating PR...'));
275
+ console.log(chalk_1.default.blue('\n🔄 Creating PR...'));
298
276
  const pr = await github.createPullRequest(intent.options || {});
299
- 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`));
300
279
  break;
301
280
  case 'pr_list':
302
- console.log(chalk_1.default.blue('Listing PRs...'));
303
281
  const prs = await github.listPullRequests({ state: 'open', limit: 10 });
304
282
  if (prs.length === 0) {
305
- console.log(chalk_1.default.gray('No open PRs'));
283
+ console.log(chalk_1.default.gray('\n✓ No open PRs\n'));
306
284
  }
307
285
  else {
308
- 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();
309
325
  }
310
326
  break;
311
327
  default:
312
- 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`));
313
329
  }
314
330
  }
315
331
  catch (error) {
316
- console.log(chalk_1.default.red(`❌ ${error.message}\n`));
332
+ console.log(chalk_1.default.red(`\n❌ ${error.message}\n`));
317
333
  }
318
334
  }
319
335
  /**
320
- * Auto-fix common Git errors (like Cursor does)
336
+ * Auto-fix common Git errors (like Cursor does - silently when possible)
321
337
  */
322
338
  async function tryAutoFix(error, command) {
323
339
  const errorMsg = error.message?.toLowerCase() || '';
324
340
  // Merge conflict
325
341
  if (errorMsg.includes('conflict')) {
326
- console.log(chalk_1.default.yellow('⚠️ Merge conflict - attempting auto-resolve...'));
342
+ console.log(chalk_1.default.yellow('\n⚠️ Conflict detected - auto-resolving...'));
327
343
  try {
328
- // Try to accept current changes
329
344
  (0, child_process_1.execSync)('git add .', { encoding: 'utf-8', stdio: 'pipe' });
330
345
  (0, child_process_1.execSync)('git commit --no-edit', { encoding: 'utf-8', stdio: 'pipe' });
331
- console.log(chalk_1.default.green('✓ Auto-resolved'));
346
+ console.log(chalk_1.default.green('✓ Resolved\n'));
332
347
  return true;
333
348
  }
334
349
  catch {
@@ -337,11 +352,11 @@ async function tryAutoFix(error, command) {
337
352
  }
338
353
  // Diverged branches
339
354
  if (errorMsg.includes('diverged') || errorMsg.includes('non-fast-forward')) {
340
- console.log(chalk_1.default.yellow('⚠️ Branches diverged - pulling and retrying...'));
355
+ console.log(chalk_1.default.yellow('\n⚠️ Syncing with remote...'));
341
356
  try {
342
357
  (0, child_process_1.execSync)('git pull --rebase', { encoding: 'utf-8', stdio: 'pipe' });
343
358
  (0, child_process_1.execSync)(command, { encoding: 'utf-8', stdio: 'pipe' });
344
- console.log(chalk_1.default.green('✓ Fixed'));
359
+ console.log(chalk_1.default.green('✓ Synced\n'));
345
360
  return true;
346
361
  }
347
362
  catch {
@@ -350,12 +365,12 @@ async function tryAutoFix(error, command) {
350
365
  }
351
366
  // Unstaged changes
352
367
  if (errorMsg.includes('unstaged') || errorMsg.includes('uncommitted')) {
353
- console.log(chalk_1.default.yellow('⚠️ Unstaged changes - stashing and retrying...'));
368
+ console.log(chalk_1.default.yellow('\n⚠️ Stashing changes...'));
354
369
  try {
355
370
  (0, child_process_1.execSync)('git stash', { encoding: 'utf-8', stdio: 'pipe' });
356
371
  (0, child_process_1.execSync)(command, { encoding: 'utf-8', stdio: 'pipe' });
357
372
  (0, child_process_1.execSync)('git stash pop', { encoding: 'utf-8', stdio: 'pipe' });
358
- console.log(chalk_1.default.green('✓ Fixed'));
373
+ console.log(chalk_1.default.green('✓ Restored\n'));
359
374
  return true;
360
375
  }
361
376
  catch {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@snapcommit/cli",
3
- "version": "2.6.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": {