@snapcommit/cli 1.0.0 → 1.0.2

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,188 +1,50 @@
1
1
  "use strict";
2
2
  /**
3
3
  * Natural Language Git Command Interpreter
4
- * Uses Claude to understand user intent and map to Git operations
4
+ * Uses backend API (Claude/Gemini) to understand user intent and map to Git 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
- })();
39
- var __importDefault = (this && this.__importDefault) || function (mod) {
40
- return (mod && mod.__esModule) ? mod : { "default": mod };
41
- };
42
6
  Object.defineProperty(exports, "__esModule", { value: true });
43
7
  exports.interpretGitCommand = interpretGitCommand;
44
8
  exports.suggestNextActions = suggestNextActions;
45
- const sdk_1 = __importDefault(require("@anthropic-ai/sdk"));
46
- const retry_1 = require("../utils/retry");
47
- const client = new sdk_1.default({
48
- apiKey: process.env.ANTHROPIC_API_KEY || '',
49
- });
9
+ const auth_1 = require("../lib/auth");
10
+ // API URL - defaults to production
11
+ const API_BASE_URL = process.env.SNAPCOMMIT_API_URL || 'https://snapcommit.dev';
12
+ /**
13
+ * Interpret natural language command using backend API
14
+ */
50
15
  async function interpretGitCommand(userInput, context) {
51
- const prompt = `You are a Git expert assistant. Interpret the user's natural language request into a structured Git operation.
52
-
53
- User request: "${userInput}"
54
-
55
- Current context:
56
- - Branch: ${context.currentBranch}
57
- - Uncommitted changes: ${context.hasUncommittedChanges}
58
- - Last commit: ${context.lastCommitHash || 'none'}
59
- - Remote: ${context.remoteBranch || 'none'}
60
-
61
- Respond with a JSON object (and ONLY JSON, no markdown):
62
- {
63
- "action": "commit|revert|merge|branch|reset|stash|push|pull|rebase|cherry-pick|etc",
64
- "target": "specific target if any (branch, commit, file)",
65
- "options": {"key": "value"},
66
- "confidence": 0.95,
67
- "explanation": "This will undo your last commit but keep all your changes staged",
68
- "gitCommands": ["git reset --soft HEAD~1", "git status"],
69
- "riskLevel": "safe|medium|high",
70
- "needsConfirmation": true,
71
- "educationalTip": "The --soft flag keeps your changes staged. Use --hard to discard changes entirely (dangerous!)"
72
- }
73
-
74
- Risk levels:
75
- - safe: Read-only, undo-able (status, log, diff)
76
- - medium: Changes state but recoverable (commit, stash, branch)
77
- - high: Destructive, affects remote, or hard to undo (push --force, reset --hard, delete branch)
78
-
79
- Common patterns:
80
- - "commit" / "save" / "commit this" → action: "commit"
81
- - "undo" / "go back" → action: "reset" (if uncommitted) or "revert" (if committed)
82
- - "push" / "send" / "upload" → action: "push"
83
- - "merge X" → action: "merge", target: "X"
84
- - "create branch X" → action: "branch", target: "X"
85
- - "switch to X" → action: "checkout", target: "X"
86
- - "delete branch X" → action: "delete_branch", target: "X"
87
-
88
- Be smart about context:
89
- - If user says "undo that" and last action was commit → revert
90
- - If user says "merge" without target → suggest merging current branch to main
91
- - If user says "fix conflicts" → action: "resolve_conflicts"
92
-
93
- Respond with ONLY the JSON object, no other text.`;
16
+ const token = (0, auth_1.getToken)();
17
+ if (!token) {
18
+ throw new Error('Authentication required. Please run: snapcommit login');
19
+ }
94
20
  try {
95
- const response = await (0, retry_1.retryWithBackoff)(() => client.messages.create({
96
- model: 'claude-sonnet-4-20250514',
97
- max_tokens: 1000,
98
- messages: [{ role: 'user', content: prompt }],
99
- }));
100
- const text = response.content[0];
101
- if (text.type !== 'text') {
102
- throw new Error('Unexpected response type');
103
- }
104
- // Parse JSON response
105
- const intent = JSON.parse(text.text.trim());
106
- // Validate
107
- if (!intent.action || !intent.explanation || !intent.gitCommands) {
108
- throw new Error('Invalid intent structure');
21
+ const response = await fetch(`${API_BASE_URL}/api/ai/interpret`, {
22
+ method: 'POST',
23
+ headers: { 'Content-Type': 'application/json' },
24
+ body: JSON.stringify({
25
+ userInput,
26
+ context,
27
+ token
28
+ }),
29
+ });
30
+ const data = await response.json();
31
+ if (!response.ok) {
32
+ if (response.status === 401) {
33
+ throw new Error('Authentication failed. Please log in again: snapcommit logout && snapcommit login');
34
+ }
35
+ if (response.status === 403) {
36
+ throw new Error(data.message || 'Subscription required. Visit https://snapcommit.dev/pricing');
37
+ }
38
+ throw new Error(data.error || 'Failed to interpret command');
109
39
  }
110
- return intent;
40
+ return data.intent;
111
41
  }
112
42
  catch (error) {
113
- // Try Gemini fallback
114
- try {
115
- const { interpretGitCommandGemini } = await Promise.resolve().then(() => __importStar(require('./gemini-client')));
116
- return await interpretGitCommandGemini(userInput, context);
43
+ if (error.message.includes('fetch')) {
44
+ throw new Error('Network error. Please check your internet connection.');
117
45
  }
118
- catch (geminiError) {
119
- // Ultimate fallback: simple pattern matching
120
- return getFallbackIntent(userInput, context);
121
- }
122
- }
123
- }
124
- /**
125
- * Fallback intent parser for common commands (if AI fails)
126
- */
127
- function getFallbackIntent(userInput, context) {
128
- const input = userInput.toLowerCase().trim();
129
- // Common patterns
130
- if (input.includes('commit') || input === 'save' || input === 'snap') {
131
- return {
132
- action: 'commit',
133
- confidence: 0.9,
134
- explanation: 'This will commit all your changes with an AI-generated message',
135
- gitCommands: ['git add -A', 'git commit -m "[AI generated]"'],
136
- riskLevel: 'medium',
137
- needsConfirmation: false,
138
- educationalTip: 'git add -A stages all changes (new, modified, deleted files)',
139
- };
140
- }
141
- if (input.includes('undo') || input.includes('go back')) {
142
- if (context.hasUncommittedChanges) {
143
- return {
144
- action: 'reset',
145
- confidence: 0.8,
146
- explanation: 'This will discard all uncommitted changes',
147
- gitCommands: ['git reset --hard HEAD'],
148
- riskLevel: 'high',
149
- needsConfirmation: true,
150
- educationalTip: '⚠️ --hard permanently deletes changes. Use --soft to keep changes.',
151
- };
152
- }
153
- else {
154
- return {
155
- action: 'revert',
156
- confidence: 0.8,
157
- explanation: 'This will revert your last commit',
158
- gitCommands: ['git revert HEAD'],
159
- riskLevel: 'medium',
160
- needsConfirmation: true,
161
- educationalTip: 'git revert creates a new commit that undoes changes (safe for pushed commits)',
162
- };
163
- }
164
- }
165
- if (input.includes('push') || input.includes('send')) {
166
- return {
167
- action: 'push',
168
- confidence: 0.9,
169
- explanation: `This will push your commits to remote (${context.currentBranch})`,
170
- gitCommands: [`git push origin ${context.currentBranch}`],
171
- riskLevel: 'medium',
172
- needsConfirmation: false,
173
- educationalTip: 'git push uploads your local commits to GitHub/remote',
174
- };
46
+ throw error;
175
47
  }
176
- // Default: show help
177
- return {
178
- action: 'help',
179
- confidence: 0.5,
180
- explanation: 'I\'m not sure what you want to do. Try being more specific.',
181
- gitCommands: ['git status'],
182
- riskLevel: 'safe',
183
- needsConfirmation: false,
184
- educationalTip: 'Try commands like: "commit", "push", "undo", "merge", "create branch"',
185
- };
186
48
  }
187
49
  /**
188
50
  * Generate suggestions for next actions
@@ -52,6 +52,7 @@ const database_1 = require("../db/database");
52
52
  const analytics_1 = require("../utils/analytics");
53
53
  const manager_1 = require("../license/manager");
54
54
  const smart_solver_1 = require("../ai/smart-solver");
55
+ const auth_1 = require("../lib/auth");
55
56
  function askQuestion(query) {
56
57
  const rl = readline_1.default.createInterface({
57
58
  input: process.stdin,
@@ -93,6 +94,12 @@ function executeGitCommands(commands, showCommands = true) {
93
94
  * Main natural language command handler
94
95
  */
95
96
  async function naturalCommand(userInput) {
97
+ // Ensure authentication first
98
+ const authConfig = await (0, auth_1.ensureAuth)();
99
+ if (!authConfig) {
100
+ console.log(chalk_1.default.red('\n❌ Authentication required to use SnapCommit\n'));
101
+ process.exit(1);
102
+ }
96
103
  // Check if in git repo
97
104
  if (!(0, git_1.isGitRepo)()) {
98
105
  console.log(chalk_1.default.red('\n❌ Not a git repository'));
package/dist/lib/auth.js CHANGED
@@ -96,18 +96,24 @@ async function verifyToken(token) {
96
96
  * Prompt user to authenticate
97
97
  */
98
98
  async function promptAuth() {
99
- console.log(chalk_1.default.bold.cyan('\n🔐 Welcome to SnapCommit!\n'));
100
- console.log(chalk_1.default.gray('To use SnapCommit, you need to authenticate.\n'));
101
- console.log(chalk_1.default.bold('Step 1:') + chalk_1.default.gray(' Sign up or log in at:'));
102
- console.log(chalk_1.default.cyan.underline(' 👉 https://snapcommit.dev/login\n'));
103
- console.log(chalk_1.default.bold('Step 2:') + chalk_1.default.gray(' Generate a token from your dashboard'));
104
- console.log(chalk_1.default.gray(' (You\'ll see a "Generate Token" button)\n'));
105
- const token = await askQuestion(chalk_1.default.yellow('Step 3: Paste your token here: '));
99
+ console.log(chalk_1.default.bold.cyan('\n╔════════════════════════════════════════╗'));
100
+ console.log(chalk_1.default.bold.cyan('║ 🔐 Authentication Required 🔐 ║'));
101
+ console.log(chalk_1.default.bold.cyan('╚════════════════════════════════════════╝\n'));
102
+ console.log(chalk_1.default.gray('SnapCommit needs authentication to provide AI-powered features.\n'));
103
+ console.log(chalk_1.default.yellow('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'));
104
+ console.log(chalk_1.default.bold.white('Step 1: ') + chalk_1.default.gray('Sign up or log in'));
105
+ console.log(chalk_1.default.cyan(' 👉 https://snapcommit.dev/login\n'));
106
+ console.log(chalk_1.default.bold.white('Step 2: ') + chalk_1.default.gray('Go to your dashboard and click "Generate Token"'));
107
+ console.log(chalk_1.default.cyan(' 👉 https://snapcommit.dev/dashboard\n'));
108
+ console.log(chalk_1.default.bold.white('Step 3: ') + chalk_1.default.gray('Copy the token and paste it below\n'));
109
+ console.log(chalk_1.default.yellow('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'));
110
+ const token = await askQuestion(chalk_1.default.yellow('🔑 Paste your authentication token: '));
106
111
  if (!token || token.trim().length === 0) {
107
112
  console.log(chalk_1.default.red('\n❌ No token provided. Authentication cancelled.\n'));
113
+ console.log(chalk_1.default.gray('Run "snap" again when you\'re ready to authenticate.\n'));
108
114
  return false;
109
115
  }
110
- console.log(chalk_1.default.gray('\n🔄 Verifying token...\n'));
116
+ console.log(chalk_1.default.gray('\n🔄 Verifying token with SnapCommit servers...\n'));
111
117
  try {
112
118
  const result = await verifyToken(token.trim());
113
119
  if (result.valid && result.user) {
@@ -118,20 +124,29 @@ async function promptAuth() {
118
124
  name: result.user.name || 'Developer',
119
125
  };
120
126
  saveAuthConfig(config);
121
- console.log(chalk_1.default.green(`✅ Authenticated successfully! Welcome, ${chalk_1.default.bold(config.name)}!\n`));
122
- console.log(chalk_1.default.gray('💎 SnapCommit Pro - Unlimited AI commits'));
123
- console.log(chalk_1.default.gray('🔥 Your coding journey starts now!\n'));
124
- console.log(chalk_1.default.gray('Try: snap quick (in any git repo)\n'));
127
+ console.log(chalk_1.default.green.bold('✅ AUTHENTICATION SUCCESSFUL! 🎉\n'));
128
+ console.log(chalk_1.default.white(` Welcome back, ${chalk_1.default.bold(config.name)}!`));
129
+ console.log(chalk_1.default.gray(` Email: ${config.email}`));
130
+ console.log(chalk_1.default.gray(` Plan: SnapCommit Pro (Active)\n`));
131
+ console.log(chalk_1.default.yellow('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'));
132
+ console.log(chalk_1.default.bold('🚀 Quick Start:\n'));
133
+ console.log(chalk_1.default.gray(' • ') + chalk_1.default.cyan('snap quick') + chalk_1.default.gray(' - AI commit in any repo'));
134
+ console.log(chalk_1.default.gray(' • ') + chalk_1.default.cyan('snap') + chalk_1.default.gray(' - Natural language Git commands'));
135
+ console.log(chalk_1.default.gray(' • ') + chalk_1.default.cyan('snap stats') + chalk_1.default.gray(' - View your coding stats\n'));
125
136
  return true;
126
137
  }
127
138
  else {
128
139
  console.log(chalk_1.default.red('\n❌ Invalid token. Please try again.\n'));
129
- console.log(chalk_1.default.gray('Make sure you copied the entire token from your dashboard.\n'));
140
+ console.log(chalk_1.default.yellow('Troubleshooting:'));
141
+ console.log(chalk_1.default.gray(' • Make sure you copied the ENTIRE token'));
142
+ console.log(chalk_1.default.gray(' • Generate a new token at: https://snapcommit.dev/dashboard'));
143
+ console.log(chalk_1.default.gray(' • Ensure you have an active SnapCommit subscription\n'));
130
144
  return false;
131
145
  }
132
146
  }
133
147
  catch (error) {
134
148
  console.log(chalk_1.default.red(`\n❌ ${error.message}\n`));
149
+ console.log(chalk_1.default.gray('Please check your internet connection and try again.\n'));
135
150
  return false;
136
151
  }
137
152
  }
package/dist/repl.js ADDED
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.startREPL = startREPL;
7
+ const readline_1 = __importDefault(require("readline"));
8
+ const chalk_1 = __importDefault(require("chalk"));
9
+ const auth_1 = require("./lib/auth");
10
+ const natural_1 = require("./commands/natural");
11
+ const quick_1 = require("./commands/quick");
12
+ const stats_1 = require("./commands/stats");
13
+ /**
14
+ * Start SnapCommit REPL (Read-Eval-Print-Loop)
15
+ * Interactive mode for natural language Git commands
16
+ */
17
+ async function startREPL() {
18
+ console.log(chalk_1.default.bold.cyan('\n╔════════════════════════════════════════╗'));
19
+ console.log(chalk_1.default.bold.cyan('║ Welcome to SnapCommit! 🚀 ║'));
20
+ console.log(chalk_1.default.bold.cyan('╚════════════════════════════════════════╝\n'));
21
+ console.log(chalk_1.default.gray(' The AI-powered Git assistant that speaks your language.\n'));
22
+ // Ensure user is authenticated before proceeding
23
+ const authConfig = await (0, auth_1.ensureAuth)();
24
+ if (!authConfig) {
25
+ console.log(chalk_1.default.red('❌ Authentication required to use SnapCommit.\n'));
26
+ console.log(chalk_1.default.gray('Please authenticate to continue.\n'));
27
+ process.exit(1);
28
+ }
29
+ console.log(chalk_1.default.green(`✅ Logged in as ${chalk_1.default.bold(authConfig.email)}\n`));
30
+ console.log(chalk_1.default.gray('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'));
31
+ console.log(chalk_1.default.bold('💡 What can SnapCommit do?\n'));
32
+ console.log(chalk_1.default.gray(' • ') + chalk_1.default.white('Natural language Git: ') + chalk_1.default.cyan('"undo my last commit"'));
33
+ console.log(chalk_1.default.gray(' • ') + chalk_1.default.white('Quick AI commits: ') + chalk_1.default.cyan('quick'));
34
+ console.log(chalk_1.default.gray(' • ') + chalk_1.default.white('Show your stats: ') + chalk_1.default.cyan('stats'));
35
+ console.log(chalk_1.default.gray(' • ') + chalk_1.default.white('Exit SnapCommit: ') + chalk_1.default.cyan('exit') + chalk_1.default.gray(' or ') + chalk_1.default.cyan('quit\n'));
36
+ console.log(chalk_1.default.gray('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'));
37
+ console.log(chalk_1.default.bold.yellow('🎯 Try it now! Type what you want to do:\n'));
38
+ const rl = readline_1.default.createInterface({
39
+ input: process.stdin,
40
+ output: process.stdout,
41
+ prompt: chalk_1.default.cyan('snap> '),
42
+ });
43
+ rl.prompt();
44
+ rl.on('line', async (input) => {
45
+ const line = input.trim();
46
+ if (!line) {
47
+ rl.prompt();
48
+ return;
49
+ }
50
+ // Exit commands
51
+ if (line === 'exit' || line === 'quit' || line === 'q') {
52
+ console.log(chalk_1.default.gray('\n👋 See you later! Happy coding!\n'));
53
+ rl.close();
54
+ process.exit(0);
55
+ }
56
+ // Built-in commands
57
+ if (line === 'quick' || line === 'q') {
58
+ await (0, quick_1.quickCommand)();
59
+ rl.prompt();
60
+ return;
61
+ }
62
+ if (line === 'stats' || line === 's') {
63
+ (0, stats_1.statsCommand)();
64
+ rl.prompt();
65
+ return;
66
+ }
67
+ if (line === 'help' || line === 'h') {
68
+ console.log(chalk_1.default.bold('\n💡 SnapCommit Commands:\n'));
69
+ console.log(chalk_1.default.gray(' • ') + chalk_1.default.cyan('quick') + chalk_1.default.gray(' - Quick AI commit (stage all + commit)'));
70
+ console.log(chalk_1.default.gray(' • ') + chalk_1.default.cyan('stats') + chalk_1.default.gray(' - Show your coding stats'));
71
+ console.log(chalk_1.default.gray(' • ') + chalk_1.default.cyan('exit/quit') + chalk_1.default.gray(' - Exit SnapCommit'));
72
+ console.log(chalk_1.default.gray(' • ') + chalk_1.default.white('Or just type what you want: ') + chalk_1.default.cyan('"create a new branch called feature-x"\n'));
73
+ rl.prompt();
74
+ return;
75
+ }
76
+ // Everything else is natural language
77
+ try {
78
+ await (0, natural_1.naturalCommand)(line);
79
+ }
80
+ catch (error) {
81
+ console.log(chalk_1.default.red(`\n❌ Error: ${error.message}\n`));
82
+ }
83
+ rl.prompt();
84
+ });
85
+ rl.on('close', () => {
86
+ console.log(chalk_1.default.gray('\n👋 See you later! Happy coding!\n'));
87
+ process.exit(0);
88
+ });
89
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@snapcommit/cli",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Instant AI commits. Beautiful progress tracking. Never write commit messages again.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -1,65 +0,0 @@
1
- "use strict";
2
- /**
3
- * Auth token storage utilities
4
- * Stores Supabase auth tokens securely in local database
5
- */
6
- var __importDefault = (this && this.__importDefault) || function (mod) {
7
- return (mod && mod.__esModule) ? mod : { "default": mod };
8
- };
9
- Object.defineProperty(exports, "__esModule", { value: true });
10
- exports.saveAuthToken = saveAuthToken;
11
- exports.getAuthToken = getAuthToken;
12
- exports.clearAuthToken = clearAuthToken;
13
- const database_1 = require("../db/database");
14
- const chalk_1 = __importDefault(require("chalk"));
15
- /**
16
- * Save auth tokens to local database
17
- */
18
- async function saveAuthToken(accessToken, refreshToken) {
19
- try {
20
- const db = (0, database_1.getDB)();
21
- db.prepare(`
22
- INSERT OR REPLACE INTO auth_tokens (id, access_token, refresh_token, updated_at)
23
- VALUES (1, ?, ?, ?)
24
- `).run(accessToken, refreshToken || null, Date.now());
25
- return true;
26
- }
27
- catch (error) {
28
- console.error(chalk_1.default.red('Failed to save auth token:'), error.message);
29
- return false;
30
- }
31
- }
32
- /**
33
- * Get auth tokens from local database
34
- */
35
- function getAuthToken() {
36
- try {
37
- const db = (0, database_1.getDB)();
38
- const row = db.prepare(`
39
- SELECT access_token, refresh_token FROM auth_tokens WHERE id = 1
40
- `).get();
41
- if (!row)
42
- return null;
43
- return {
44
- accessToken: row.access_token,
45
- refreshToken: row.refresh_token || undefined,
46
- };
47
- }
48
- catch (error) {
49
- return null;
50
- }
51
- }
52
- /**
53
- * Clear auth tokens from local database
54
- */
55
- async function clearAuthToken() {
56
- try {
57
- const db = (0, database_1.getDB)();
58
- db.prepare('DELETE FROM auth_tokens').run();
59
- return true;
60
- }
61
- catch (error) {
62
- console.error(chalk_1.default.red('Failed to clear auth token:'), error.message);
63
- return false;
64
- }
65
- }