@snapcommit/cli 1.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.
Files changed (40) hide show
  1. package/README.md +162 -0
  2. package/dist/ai/anthropic-client.js +92 -0
  3. package/dist/ai/commit-generator.js +200 -0
  4. package/dist/ai/gemini-client.js +201 -0
  5. package/dist/ai/git-interpreter.js +209 -0
  6. package/dist/ai/smart-solver.js +260 -0
  7. package/dist/auth/supabase-client.js +288 -0
  8. package/dist/commands/activate.js +108 -0
  9. package/dist/commands/commit.js +255 -0
  10. package/dist/commands/conflict.js +233 -0
  11. package/dist/commands/doctor.js +113 -0
  12. package/dist/commands/git-advanced.js +311 -0
  13. package/dist/commands/github-auth.js +193 -0
  14. package/dist/commands/login.js +11 -0
  15. package/dist/commands/natural.js +305 -0
  16. package/dist/commands/onboard.js +111 -0
  17. package/dist/commands/quick.js +173 -0
  18. package/dist/commands/setup.js +163 -0
  19. package/dist/commands/stats.js +128 -0
  20. package/dist/commands/uninstall.js +131 -0
  21. package/dist/db/database.js +99 -0
  22. package/dist/index.js +144 -0
  23. package/dist/lib/auth.js +171 -0
  24. package/dist/lib/github.js +280 -0
  25. package/dist/lib/multi-repo.js +276 -0
  26. package/dist/lib/supabase.js +153 -0
  27. package/dist/license/manager.js +203 -0
  28. package/dist/repl/index.js +185 -0
  29. package/dist/repl/interpreter.js +524 -0
  30. package/dist/utils/analytics.js +36 -0
  31. package/dist/utils/auth-storage.js +65 -0
  32. package/dist/utils/dopamine.js +211 -0
  33. package/dist/utils/errors.js +56 -0
  34. package/dist/utils/git.js +105 -0
  35. package/dist/utils/heatmap.js +265 -0
  36. package/dist/utils/rate-limit.js +68 -0
  37. package/dist/utils/retry.js +46 -0
  38. package/dist/utils/ui.js +189 -0
  39. package/dist/utils/version.js +81 -0
  40. package/package.json +69 -0
@@ -0,0 +1,276 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.loadRepos = loadRepos;
40
+ exports.getCurrentRepoPath = getCurrentRepoPath;
41
+ exports.getRepoName = getRepoName;
42
+ exports.saveCurrentRepo = saveCurrentRepo;
43
+ exports.listRepos = listRepos;
44
+ exports.switchToRepo = switchToRepo;
45
+ exports.getRepoContext = getRepoContext;
46
+ const child_process_1 = require("child_process");
47
+ const fs = __importStar(require("fs"));
48
+ const path = __importStar(require("path"));
49
+ const os = __importStar(require("os"));
50
+ const chalk_1 = __importDefault(require("chalk"));
51
+ const REPOS_CONFIG_PATH = path.join(os.homedir(), '.snapcommit', 'repos.json');
52
+ /**
53
+ * Load saved repositories
54
+ */
55
+ function loadRepos() {
56
+ try {
57
+ if (!fs.existsSync(REPOS_CONFIG_PATH)) {
58
+ return [];
59
+ }
60
+ const data = fs.readFileSync(REPOS_CONFIG_PATH, 'utf-8');
61
+ return JSON.parse(data);
62
+ }
63
+ catch (error) {
64
+ return [];
65
+ }
66
+ }
67
+ /**
68
+ * Save repositories
69
+ */
70
+ function saveRepos(repos) {
71
+ try {
72
+ const dir = path.dirname(REPOS_CONFIG_PATH);
73
+ if (!fs.existsSync(dir)) {
74
+ fs.mkdirSync(dir, { recursive: true });
75
+ }
76
+ fs.writeFileSync(REPOS_CONFIG_PATH, JSON.stringify(repos, null, 2));
77
+ }
78
+ catch (error) {
79
+ console.error(chalk_1.default.red('Failed to save repo config'));
80
+ }
81
+ }
82
+ /**
83
+ * Get current repository path
84
+ */
85
+ function getCurrentRepoPath() {
86
+ try {
87
+ const repoPath = (0, child_process_1.execSync)('git rev-parse --show-toplevel', {
88
+ encoding: 'utf-8',
89
+ stdio: ['pipe', 'pipe', 'ignore']
90
+ }).trim();
91
+ return repoPath;
92
+ }
93
+ catch (error) {
94
+ return null;
95
+ }
96
+ }
97
+ /**
98
+ * Get repository name from path
99
+ */
100
+ function getRepoName(repoPath) {
101
+ return path.basename(repoPath);
102
+ }
103
+ /**
104
+ * Add current repository to saved repos
105
+ */
106
+ function saveCurrentRepo() {
107
+ const repoPath = getCurrentRepoPath();
108
+ if (!repoPath) {
109
+ return;
110
+ }
111
+ const repos = loadRepos();
112
+ const name = getRepoName(repoPath);
113
+ // Get current branch
114
+ let branch;
115
+ try {
116
+ branch = (0, child_process_1.execSync)('git rev-parse --abbrev-ref HEAD', {
117
+ encoding: 'utf-8',
118
+ cwd: repoPath,
119
+ stdio: ['pipe', 'pipe', 'ignore']
120
+ }).trim();
121
+ }
122
+ catch (error) {
123
+ branch = 'unknown';
124
+ }
125
+ // Update or add repo
126
+ const existingIndex = repos.findIndex(r => r.path === repoPath);
127
+ if (existingIndex >= 0) {
128
+ repos[existingIndex].lastUsed = Date.now();
129
+ repos[existingIndex].branch = branch;
130
+ }
131
+ else {
132
+ repos.push({
133
+ name,
134
+ path: repoPath,
135
+ lastUsed: Date.now(),
136
+ branch,
137
+ });
138
+ }
139
+ saveRepos(repos);
140
+ }
141
+ /**
142
+ * List all saved repositories
143
+ */
144
+ function listRepos() {
145
+ const repos = loadRepos();
146
+ if (repos.length === 0) {
147
+ console.log(chalk_1.default.gray('\n No saved repositories\n'));
148
+ return;
149
+ }
150
+ // Sort by last used
151
+ repos.sort((a, b) => b.lastUsed - a.lastUsed);
152
+ console.log(chalk_1.default.bold('\n📁 Saved Repositories:\n'));
153
+ const currentPath = getCurrentRepoPath();
154
+ repos.forEach((repo, index) => {
155
+ const isCurrent = repo.path === currentPath;
156
+ const icon = isCurrent ? chalk_1.default.green('→') : ' ';
157
+ const nameColor = isCurrent ? chalk_1.default.green.bold : chalk_1.default.cyan;
158
+ console.log(` ${icon} ${nameColor(repo.name)}`);
159
+ console.log(chalk_1.default.gray(` ${repo.path}`));
160
+ if (repo.branch) {
161
+ console.log(chalk_1.default.gray(` Branch: ${repo.branch}`));
162
+ }
163
+ console.log('');
164
+ });
165
+ }
166
+ /**
167
+ * Switch to a repository by name or index
168
+ */
169
+ async function switchToRepo(nameOrIndex) {
170
+ const repos = loadRepos();
171
+ if (repos.length === 0) {
172
+ console.log(chalk_1.default.red('\n❌ No saved repositories\n'));
173
+ return false;
174
+ }
175
+ // Sort by last used
176
+ repos.sort((a, b) => b.lastUsed - a.lastUsed);
177
+ let targetRepo;
178
+ if (typeof nameOrIndex === 'number') {
179
+ targetRepo = repos[nameOrIndex];
180
+ }
181
+ else {
182
+ // Try to match by name (case-insensitive)
183
+ const search = nameOrIndex.toLowerCase();
184
+ targetRepo = repos.find(r => r.name.toLowerCase().includes(search) ||
185
+ r.path.toLowerCase().includes(search));
186
+ }
187
+ if (!targetRepo) {
188
+ console.log(chalk_1.default.red(`\n❌ Repository "${nameOrIndex}" not found\n`));
189
+ return false;
190
+ }
191
+ // Check if repo path still exists
192
+ if (!fs.existsSync(targetRepo.path)) {
193
+ console.log(chalk_1.default.red(`\n❌ Repository path no longer exists: ${targetRepo.path}\n`));
194
+ console.log(chalk_1.default.yellow(' Removing from saved repos...\n'));
195
+ // Remove from saved repos
196
+ const filtered = repos.filter(r => r.path !== targetRepo.path);
197
+ saveRepos(filtered);
198
+ return false;
199
+ }
200
+ // Check if it's a git repo
201
+ try {
202
+ (0, child_process_1.execSync)('git rev-parse --git-dir', {
203
+ cwd: targetRepo.path,
204
+ stdio: ['pipe', 'pipe', 'ignore']
205
+ });
206
+ }
207
+ catch (error) {
208
+ console.log(chalk_1.default.red(`\n❌ Not a valid git repository: ${targetRepo.path}\n`));
209
+ return false;
210
+ }
211
+ // Change to the directory
212
+ try {
213
+ process.chdir(targetRepo.path);
214
+ // Update last used
215
+ targetRepo.lastUsed = Date.now();
216
+ saveRepos(repos);
217
+ // Get current branch
218
+ let branch = 'unknown';
219
+ try {
220
+ branch = (0, child_process_1.execSync)('git rev-parse --abbrev-ref HEAD', {
221
+ encoding: 'utf-8',
222
+ stdio: ['pipe', 'pipe', 'ignore']
223
+ }).trim();
224
+ }
225
+ catch (error) {
226
+ // Ignore
227
+ }
228
+ // Get status
229
+ let statusLine = '';
230
+ try {
231
+ const status = (0, child_process_1.execSync)('git status --short', {
232
+ encoding: 'utf-8',
233
+ stdio: ['pipe', 'pipe', 'ignore']
234
+ }).trim();
235
+ const changedFiles = status.split('\n').filter(line => line.trim()).length;
236
+ if (changedFiles > 0) {
237
+ statusLine = chalk_1.default.yellow(` | ${changedFiles} file${changedFiles > 1 ? 's' : ''} changed`);
238
+ }
239
+ else {
240
+ statusLine = chalk_1.default.green(' | Clean');
241
+ }
242
+ }
243
+ catch (error) {
244
+ // Ignore
245
+ }
246
+ console.log(chalk_1.default.green(`\n✅ Switched to ${chalk_1.default.bold(targetRepo.name)}`));
247
+ console.log(chalk_1.default.gray(` Branch: ${branch}${statusLine}`));
248
+ console.log(chalk_1.default.gray(` Path: ${targetRepo.path}\n`));
249
+ return true;
250
+ }
251
+ catch (error) {
252
+ console.log(chalk_1.default.red(`\n❌ Failed to switch: ${error.message}\n`));
253
+ return false;
254
+ }
255
+ }
256
+ /**
257
+ * Get repo context for display
258
+ */
259
+ function getRepoContext() {
260
+ const repoPath = getCurrentRepoPath();
261
+ if (!repoPath) {
262
+ return chalk_1.default.gray('(not in a git repo)');
263
+ }
264
+ const name = getRepoName(repoPath);
265
+ let branch = 'unknown';
266
+ try {
267
+ branch = (0, child_process_1.execSync)('git rev-parse --abbrev-ref HEAD', {
268
+ encoding: 'utf-8',
269
+ stdio: ['pipe', 'pipe', 'ignore']
270
+ }).trim();
271
+ }
272
+ catch (error) {
273
+ // Ignore
274
+ }
275
+ return chalk_1.default.cyan(`${name}`) + chalk_1.default.gray(` (${branch})`);
276
+ }
@@ -0,0 +1,153 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.supabase = void 0;
37
+ exports.storeAuthTokens = storeAuthTokens;
38
+ exports.getStoredAuth = getStoredAuth;
39
+ exports.clearStoredAuth = clearStoredAuth;
40
+ exports.isAuthenticated = isAuthenticated;
41
+ exports.getCurrentUser = getCurrentUser;
42
+ exports.checkLicenseStatus = checkLicenseStatus;
43
+ exports.syncStatsToCloud = syncStatsToCloud;
44
+ const supabase_js_1 = require("@supabase/supabase-js");
45
+ const fs = __importStar(require("fs"));
46
+ const path = __importStar(require("path"));
47
+ const os = __importStar(require("os"));
48
+ const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
49
+ const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;
50
+ if (!supabaseUrl || !supabaseAnonKey) {
51
+ throw new Error('Missing Supabase environment variables. Please check your .env file.');
52
+ }
53
+ exports.supabase = (0, supabase_js_1.createClient)(supabaseUrl, supabaseAnonKey);
54
+ // Token storage path
55
+ const TOKEN_DIR = path.join(os.homedir(), '.snapcommit');
56
+ const TOKEN_FILE = path.join(TOKEN_DIR, 'auth_token.json');
57
+ /**
58
+ * Store authentication tokens locally
59
+ */
60
+ function storeAuthTokens(auth) {
61
+ if (!fs.existsSync(TOKEN_DIR)) {
62
+ fs.mkdirSync(TOKEN_DIR, { recursive: true });
63
+ }
64
+ fs.writeFileSync(TOKEN_FILE, JSON.stringify(auth, null, 2));
65
+ }
66
+ /**
67
+ * Get stored authentication tokens
68
+ */
69
+ function getStoredAuth() {
70
+ try {
71
+ if (!fs.existsSync(TOKEN_FILE)) {
72
+ return null;
73
+ }
74
+ const data = fs.readFileSync(TOKEN_FILE, 'utf-8');
75
+ return JSON.parse(data);
76
+ }
77
+ catch (error) {
78
+ return null;
79
+ }
80
+ }
81
+ /**
82
+ * Clear stored authentication tokens
83
+ */
84
+ function clearStoredAuth() {
85
+ try {
86
+ if (fs.existsSync(TOKEN_FILE)) {
87
+ fs.unlinkSync(TOKEN_FILE);
88
+ }
89
+ }
90
+ catch (error) {
91
+ // Silent fail
92
+ }
93
+ }
94
+ /**
95
+ * Check if user is authenticated
96
+ */
97
+ async function isAuthenticated() {
98
+ const stored = getStoredAuth();
99
+ if (!stored) {
100
+ return false;
101
+ }
102
+ // Check if token is expired
103
+ if (Date.now() >= stored.expires_at) {
104
+ clearStoredAuth();
105
+ return false;
106
+ }
107
+ return true;
108
+ }
109
+ /**
110
+ * Get current user
111
+ */
112
+ async function getCurrentUser() {
113
+ const stored = getStoredAuth();
114
+ if (!stored) {
115
+ return null;
116
+ }
117
+ const { data, error } = await exports.supabase.auth.getUser(stored.access_token);
118
+ if (error || !data.user) {
119
+ clearStoredAuth();
120
+ return null;
121
+ }
122
+ return data.user;
123
+ }
124
+ /**
125
+ * Check license status
126
+ */
127
+ async function checkLicenseStatus(userId) {
128
+ const { data, error } = await exports.supabase.rpc('check_license_status', {
129
+ user_id_param: userId,
130
+ });
131
+ if (error) {
132
+ console.error('Error checking license:', error);
133
+ return null;
134
+ }
135
+ return data && data.length > 0 ? data[0] : null;
136
+ }
137
+ /**
138
+ * Sync stats to cloud
139
+ */
140
+ async function syncStatsToCloud(userId, date, commits, commands, insertions, deletions, filesChanged) {
141
+ const { error } = await exports.supabase.rpc('update_daily_stats', {
142
+ user_id_param: userId,
143
+ date_param: date,
144
+ commits_delta: commits,
145
+ commands_delta: commands,
146
+ insertions_delta: insertions,
147
+ deletions_delta: deletions,
148
+ files_changed_delta: filesChanged,
149
+ });
150
+ if (error) {
151
+ console.error('Error syncing stats:', error);
152
+ }
153
+ }
@@ -0,0 +1,203 @@
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.getCurrentLicense = getCurrentLicense;
7
+ exports.activateLicense = activateLicense;
8
+ exports.activateFreeTier = activateFreeTier;
9
+ exports.deactivateLicense = deactivateLicense;
10
+ exports.isProUser = isProUser;
11
+ exports.isTrialActive = isTrialActive;
12
+ exports.canUseCommit = canUseCommit;
13
+ exports.trackCommit = trackCommit;
14
+ exports.getTrialStatus = getTrialStatus;
15
+ exports.getLicenseInfo = getLicenseInfo;
16
+ const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
17
+ const path_1 = __importDefault(require("path"));
18
+ const os_1 = __importDefault(require("os"));
19
+ const fs_1 = __importDefault(require("fs"));
20
+ const LICENSE_DIR = path_1.default.join(os_1.default.homedir(), '.builderos');
21
+ const LICENSE_DB = path_1.default.join(LICENSE_DIR, 'license.db');
22
+ // Ensure directory exists
23
+ if (!fs_1.default.existsSync(LICENSE_DIR)) {
24
+ fs_1.default.mkdirSync(LICENSE_DIR, { recursive: true });
25
+ }
26
+ const db = new better_sqlite3_1.default(LICENSE_DB);
27
+ // Initialize license database
28
+ db.exec(`
29
+ CREATE TABLE IF NOT EXISTS license (
30
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
31
+ key TEXT UNIQUE NOT NULL,
32
+ email TEXT,
33
+ plan TEXT NOT NULL,
34
+ status TEXT NOT NULL,
35
+ activated_at INTEGER NOT NULL,
36
+ expires_at INTEGER,
37
+ device_id TEXT NOT NULL,
38
+ last_validated INTEGER NOT NULL
39
+ );
40
+
41
+ CREATE TABLE IF NOT EXISTS usage (
42
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
43
+ month TEXT NOT NULL,
44
+ commits INTEGER DEFAULT 0,
45
+ UNIQUE(month)
46
+ );
47
+ `);
48
+ // Free trial: 7 days
49
+ const TRIAL_DAYS = 7;
50
+ const TRIAL_DURATION_MS = TRIAL_DAYS * 24 * 60 * 60 * 1000;
51
+ function getCurrentLicense() {
52
+ const result = db.prepare('SELECT * FROM license ORDER BY id DESC LIMIT 1').get();
53
+ if (!result) {
54
+ return null;
55
+ }
56
+ return {
57
+ key: result.key,
58
+ email: result.email,
59
+ plan: result.plan,
60
+ status: result.status,
61
+ activated_at: result.activated_at,
62
+ expires_at: result.expires_at,
63
+ device_id: result.device_id,
64
+ };
65
+ }
66
+ function activateLicense(licenseKey, email, plan = 'pro_monthly') {
67
+ const deviceId = getDeviceId();
68
+ const now = Date.now();
69
+ // Calculate expiration
70
+ let expires_at;
71
+ if (plan === 'pro_monthly') {
72
+ expires_at = now + 30 * 24 * 60 * 60 * 1000; // 30 days
73
+ }
74
+ else if (plan === 'pro_yearly') {
75
+ expires_at = now + 365 * 24 * 60 * 60 * 1000; // 365 days
76
+ }
77
+ // Deactivate any existing license
78
+ db.prepare('DELETE FROM license').run();
79
+ // Insert new license
80
+ db.prepare(`
81
+ INSERT INTO license (key, email, plan, status, activated_at, expires_at, device_id, last_validated)
82
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
83
+ `).run(licenseKey, email || null, plan, 'active', now, expires_at || null, deviceId, now);
84
+ }
85
+ function activateFreeTier() {
86
+ const deviceId = getDeviceId();
87
+ const now = Date.now();
88
+ const trialEnd = now + TRIAL_DURATION_MS;
89
+ const freeKey = `trial_${deviceId}_${now}`;
90
+ // Deactivate any existing license
91
+ db.prepare('DELETE FROM license').run();
92
+ // Insert trial (7 days)
93
+ db.prepare(`
94
+ INSERT INTO license (key, email, plan, status, activated_at, expires_at, device_id, last_validated)
95
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
96
+ `).run(freeKey, null, 'free', 'active', now, trialEnd, deviceId, now);
97
+ }
98
+ function deactivateLicense() {
99
+ db.prepare('DELETE FROM license').run();
100
+ }
101
+ function isProUser() {
102
+ const license = getCurrentLicense();
103
+ if (!license) {
104
+ return false;
105
+ }
106
+ // Check if expired
107
+ if (license.expires_at && Date.now() > license.expires_at) {
108
+ return false;
109
+ }
110
+ return license.status === 'active' && license.plan !== 'free';
111
+ }
112
+ function isTrialActive() {
113
+ const license = getCurrentLicense();
114
+ if (!license || license.plan !== 'free') {
115
+ return false;
116
+ }
117
+ // Check if trial expired
118
+ if (license.expires_at && Date.now() > license.expires_at) {
119
+ return false;
120
+ }
121
+ return true;
122
+ }
123
+ function canUseCommit() {
124
+ const license = getCurrentLicense();
125
+ // No license = start trial
126
+ if (!license) {
127
+ activateFreeTier();
128
+ return canUseCommit(); // Retry
129
+ }
130
+ // Pro users = unlimited
131
+ if (isProUser()) {
132
+ return { allowed: true };
133
+ }
134
+ // Trial users = check if expired
135
+ const usage = getTrialStatus();
136
+ if (usage.isExpired) {
137
+ return {
138
+ allowed: false,
139
+ reason: '7-day trial expired',
140
+ usage,
141
+ };
142
+ }
143
+ return { allowed: true, usage };
144
+ }
145
+ function trackCommit() {
146
+ const currentMonth = getCurrentMonth();
147
+ const existing = db.prepare('SELECT * FROM usage WHERE month = ?').get(currentMonth);
148
+ if (existing) {
149
+ db.prepare('UPDATE usage SET commits = commits + 1 WHERE month = ?').run(currentMonth);
150
+ }
151
+ else {
152
+ db.prepare('INSERT INTO usage (month, commits) VALUES (?, 1)').run(currentMonth);
153
+ }
154
+ }
155
+ function getTrialStatus() {
156
+ const license = getCurrentLicense();
157
+ if (!license || license.plan !== 'free' || !license.expires_at) {
158
+ return {
159
+ trialStarted: 0,
160
+ trialEndsAt: 0,
161
+ daysRemaining: 0,
162
+ isExpired: true,
163
+ };
164
+ }
165
+ const now = Date.now();
166
+ const daysRemaining = Math.max(0, Math.ceil((license.expires_at - now) / (24 * 60 * 60 * 1000)));
167
+ const isExpired = now > license.expires_at;
168
+ return {
169
+ trialStarted: license.activated_at,
170
+ trialEndsAt: license.expires_at,
171
+ daysRemaining,
172
+ isExpired,
173
+ };
174
+ }
175
+ function getCurrentMonth() {
176
+ const now = new Date();
177
+ return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`;
178
+ }
179
+ function getDeviceId() {
180
+ // Create a unique device ID based on hostname and username
181
+ const hostname = os_1.default.hostname();
182
+ const username = os_1.default.userInfo().username;
183
+ return Buffer.from(`${hostname}_${username}`).toString('base64').substring(0, 16);
184
+ }
185
+ function getLicenseInfo() {
186
+ const license = getCurrentLicense();
187
+ if (!license) {
188
+ return 'No license';
189
+ }
190
+ if (license.plan === 'free') {
191
+ const usage = getTrialStatus();
192
+ if (usage.isExpired) {
193
+ return 'Trial expired';
194
+ }
195
+ return `Trial (${usage.daysRemaining} day${usage.daysRemaining === 1 ? '' : 's'} remaining)`;
196
+ }
197
+ const planName = license.plan === 'pro_monthly' ? 'Pro Monthly' : 'Pro Yearly';
198
+ if (license.expires_at) {
199
+ const daysRemaining = Math.ceil((license.expires_at - Date.now()) / (24 * 60 * 60 * 1000));
200
+ return `${planName} (${daysRemaining} days remaining)`;
201
+ }
202
+ return planName;
203
+ }