@snapcommit/cli 2.6.0 → 3.0.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.
@@ -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,137 @@ 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'));
284
+ }
285
+ else {
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
+ try {
306
+ const ciStatus = await github.getCommitStatus();
307
+ const state = ciStatus.status.state;
308
+ const totalChecks = ciStatus.checks.length;
309
+ const failedChecks = ciStatus.checks.filter((c) => c.conclusion === 'failure').length;
310
+ if (state === 'success' || (totalChecks > 0 && failedChecks === 0)) {
311
+ console.log(chalk_1.default.green(`✓ All checks passed (${totalChecks} checks)\n`));
312
+ }
313
+ else if (state === 'pending') {
314
+ console.log(chalk_1.default.yellow(`⏳ Checks running (${totalChecks} checks)\n`));
315
+ }
316
+ else {
317
+ console.log(chalk_1.default.red(`❌ ${failedChecks}/${totalChecks} checks failed\n`));
318
+ }
319
+ }
320
+ catch (error) {
321
+ console.log(chalk_1.default.gray(' No CI checks found\n'));
322
+ }
323
+ break;
324
+ case 'issue_create':
325
+ console.log(chalk_1.default.blue('\n🔄 Creating issue...'));
326
+ const issue = await github.createIssue(intent.options || {});
327
+ console.log(chalk_1.default.green(`✓ Issue #${issue.number} created`));
328
+ console.log(chalk_1.default.cyan(` ${issue.html_url}\n`));
329
+ break;
330
+ case 'issue_list':
331
+ const issues = await github.listIssues({ state: 'open', limit: 10 });
332
+ if (issues.length === 0) {
333
+ console.log(chalk_1.default.gray('\n✓ No open issues\n'));
306
334
  }
307
335
  else {
308
- prs.forEach((p) => console.log(chalk_1.default.cyan(` #${p.number}: ${p.title}`)));
336
+ console.log(chalk_1.default.blue(`\n🐛 Open Issues (${issues.length}):`));
337
+ issues.forEach((i) => {
338
+ console.log(chalk_1.default.cyan(` #${i.number} `) + chalk_1.default.white(i.title));
339
+ });
340
+ console.log();
309
341
  }
310
342
  break;
311
343
  default:
312
- console.log(chalk_1.default.yellow(`⚠️ GitHub action not implemented: ${intent.action}\n`));
344
+ console.log(chalk_1.default.yellow(`\n⚠️ Action not supported: ${intent.action}\n`));
313
345
  }
314
346
  }
315
347
  catch (error) {
316
- console.log(chalk_1.default.red(`❌ ${error.message}\n`));
348
+ console.log(chalk_1.default.red(`\n❌ ${error.message}\n`));
317
349
  }
318
350
  }
319
351
  /**
320
- * Auto-fix common Git errors (like Cursor does)
352
+ * Auto-fix common Git errors (like Cursor does - silently when possible)
321
353
  */
322
354
  async function tryAutoFix(error, command) {
323
355
  const errorMsg = error.message?.toLowerCase() || '';
324
356
  // Merge conflict
325
357
  if (errorMsg.includes('conflict')) {
326
- console.log(chalk_1.default.yellow('⚠️ Merge conflict - attempting auto-resolve...'));
358
+ console.log(chalk_1.default.yellow('\n⚠️ Conflict detected - auto-resolving...'));
327
359
  try {
328
- // Try to accept current changes
329
360
  (0, child_process_1.execSync)('git add .', { encoding: 'utf-8', stdio: 'pipe' });
330
361
  (0, child_process_1.execSync)('git commit --no-edit', { encoding: 'utf-8', stdio: 'pipe' });
331
- console.log(chalk_1.default.green('✓ Auto-resolved'));
362
+ console.log(chalk_1.default.green('✓ Resolved\n'));
332
363
  return true;
333
364
  }
334
365
  catch {
@@ -337,11 +368,11 @@ async function tryAutoFix(error, command) {
337
368
  }
338
369
  // Diverged branches
339
370
  if (errorMsg.includes('diverged') || errorMsg.includes('non-fast-forward')) {
340
- console.log(chalk_1.default.yellow('⚠️ Branches diverged - pulling and retrying...'));
371
+ console.log(chalk_1.default.yellow('\n⚠️ Syncing with remote...'));
341
372
  try {
342
373
  (0, child_process_1.execSync)('git pull --rebase', { encoding: 'utf-8', stdio: 'pipe' });
343
374
  (0, child_process_1.execSync)(command, { encoding: 'utf-8', stdio: 'pipe' });
344
- console.log(chalk_1.default.green('✓ Fixed'));
375
+ console.log(chalk_1.default.green('✓ Synced\n'));
345
376
  return true;
346
377
  }
347
378
  catch {
@@ -350,12 +381,12 @@ async function tryAutoFix(error, command) {
350
381
  }
351
382
  // Unstaged changes
352
383
  if (errorMsg.includes('unstaged') || errorMsg.includes('uncommitted')) {
353
- console.log(chalk_1.default.yellow('⚠️ Unstaged changes - stashing and retrying...'));
384
+ console.log(chalk_1.default.yellow('\n⚠️ Stashing changes...'));
354
385
  try {
355
386
  (0, child_process_1.execSync)('git stash', { encoding: 'utf-8', stdio: 'pipe' });
356
387
  (0, child_process_1.execSync)(command, { encoding: 'utf-8', stdio: 'pipe' });
357
388
  (0, child_process_1.execSync)('git stash pop', { encoding: 'utf-8', stdio: 'pipe' });
358
- console.log(chalk_1.default.green('✓ Fixed'));
389
+ console.log(chalk_1.default.green('✓ Restored\n'));
359
390
  return true;
360
391
  }
361
392
  catch {
@@ -1,7 +1,4 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
2
  Object.defineProperty(exports, "__esModule", { value: true });
6
3
  exports.getCurrentRepo = getCurrentRepo;
7
4
  exports.getCurrentBranch = getCurrentBranch;
@@ -18,12 +15,8 @@ exports.closeIssue = closeIssue;
18
15
  exports.createRelease = createRelease;
19
16
  exports.getRepoInfo = getRepoInfo;
20
17
  const child_process_1 = require("child_process");
21
- const chalk_1 = __importDefault(require("chalk"));
22
- const GITHUB_TOKEN = process.env.GITHUB_TOKEN;
18
+ const github_connect_1 = require("../commands/github-connect");
23
19
  const GITHUB_API = 'https://api.github.com';
24
- if (!GITHUB_TOKEN) {
25
- console.warn(chalk_1.default.yellow('⚠️ GitHub token not found. GitHub features will be disabled.'));
26
- }
27
20
  /**
28
21
  * Get current repository info from git remote
29
22
  */
@@ -64,15 +57,16 @@ function getCurrentBranch() {
64
57
  }
65
58
  }
66
59
  /**
67
- * GitHub API request helper
60
+ * GitHub API request helper - uses locally stored token
68
61
  */
69
62
  async function githubRequest(endpoint, options = {}) {
70
- if (!GITHUB_TOKEN) {
71
- throw new Error('GitHub token not configured. Please set GITHUB_TOKEN in your .env file.');
63
+ const token = (0, github_connect_1.getGitHubToken)();
64
+ if (!token) {
65
+ throw new Error('GitHub not connected. Run: snap github connect');
72
66
  }
73
67
  const url = `${GITHUB_API}${endpoint}`;
74
68
  const headers = {
75
- Authorization: `Bearer ${GITHUB_TOKEN}`,
69
+ Authorization: `Bearer ${token}`,
76
70
  Accept: 'application/vnd.github+json',
77
71
  'X-GitHub-Api-Version': '2022-11-28',
78
72
  'Content-Type': 'application/json',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@snapcommit/cli",
3
- "version": "2.6.0",
3
+ "version": "3.0.1",
4
4
  "description": "Instant AI commits. Beautiful progress tracking. Never write commit messages again.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {