@gitset-dev/cli 1.2.1 → 2.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 (41) hide show
  1. package/{LICENSE.md → LICENSE} +1 -1
  2. package/README.md +80 -159
  3. package/index.js +230 -0
  4. package/lib/ai/capabilities.js +81 -0
  5. package/lib/ai/errors.js +115 -0
  6. package/lib/ai/index.js +98 -0
  7. package/lib/ai/providers/anthropic.js +62 -0
  8. package/lib/ai/providers/gemini.js +77 -0
  9. package/lib/ai/providers/mock.js +41 -0
  10. package/lib/ai/providers/openai-compatible.js +72 -0
  11. package/lib/ai/retry.js +31 -0
  12. package/lib/cli-commit.js +143 -0
  13. package/lib/cli-config.js +207 -0
  14. package/lib/cli-issue.js +144 -0
  15. package/lib/cli-pr.js +168 -0
  16. package/lib/cli-readme.js +153 -0
  17. package/lib/commit-local.js +67 -0
  18. package/lib/config.js +134 -0
  19. package/lib/generate-local.js +59 -0
  20. package/lib/labels-ai.js +41 -0
  21. package/lib/prompts/defaults.js +191 -0
  22. package/lib/prompts/index.js +79 -0
  23. package/lib/setup-wizard.js +115 -0
  24. package/npm-shrinkwrap.json +566 -0
  25. package/package.json +35 -35
  26. package/src/commands/dependabot-resolver.js +314 -0
  27. package/src/commands/gitignore.js +110 -0
  28. package/src/commands/init.js +38 -0
  29. package/src/commands/labelspack.js +94 -0
  30. package/src/commands/license.js +208 -0
  31. package/src/commands/release.js +180 -0
  32. package/src/commands/repo.js +176 -0
  33. package/src/commands/status.js +56 -0
  34. package/src/commands/template.js +92 -0
  35. package/src/commands/tree.js +77 -0
  36. package/src/utils/dependabot-analyzer.js +101 -0
  37. package/src/utils/labels.js +102 -0
  38. package/src/utils/theme.js +70 -0
  39. package/src/utils/ui.js +173 -0
  40. package/.github/workflows/npm_publish.yml +0 -38
  41. package/src/index.js +0 -505
package/src/index.js DELETED
@@ -1,505 +0,0 @@
1
- #!/usr/bin/env node
2
- import { exec } from 'child_process';
3
- import { promisify } from 'util';
4
- import fetch from 'node-fetch';
5
- import { program } from 'commander';
6
- import path from 'path';
7
- import os from 'os';
8
- import fs from 'fs/promises';
9
- import crypto from 'crypto';
10
-
11
- const execAsync = promisify(exec);
12
- const API_URL = 'https://gitset-commit-messages.vercel.app';
13
-
14
- const CONFIG_LOCATIONS = {
15
- global: {
16
- dir: path.join(os.homedir(), '.config', 'gitset'),
17
- file: 'credentials.json'
18
- },
19
- local: {
20
- dir: path.join(process.cwd(), '.gitset'),
21
- file: 'credentials.json'
22
- },
23
- system: {
24
- darwin: path.join(os.homedir(), 'Library', 'Application Support', 'GitSet'),
25
- win32: path.join(process.env.APPDATA || '', 'GitSet'),
26
- linux: path.join(os.homedir(), '.local', 'share', 'gitset')
27
- }
28
- };
29
-
30
- const colors = {
31
- reset: '\x1b[0m',
32
- bright: '\x1b[1m',
33
- green: '\x1b[32m',
34
- yellow: '\x1b[33m',
35
- blue: '\x1b[34m',
36
- magenta: '\x1b[35m',
37
- cyan: '\x1b[36m',
38
- red: '\x1b[31m',
39
- customCyan: '\x1b[38;2;126;255;247m',
40
- darkCyan: '\x1b[38;2;75;208;214m'
41
- };
42
-
43
- function log(step, message, isError = false) {
44
- const timestamp = new Date().toLocaleTimeString();
45
- const prefix = isError ? colors.red + '❌' : colors.customCyan + '●';
46
- const stepColor = isError ? colors.red : colors.cyan;
47
- console.log(`${prefix} ${colors.reset}[${timestamp}] ${stepColor}${step}${colors.reset}: ${message}`);
48
- }
49
-
50
- function getConfigPath() {
51
- const localPath = path.join(CONFIG_LOCATIONS.local.dir, CONFIG_LOCATIONS.local.file);
52
- try {
53
- if (fs.existsSync(localPath)) {
54
- return localPath;
55
- }
56
- } catch (error) {}
57
-
58
- const globalPath = path.join(CONFIG_LOCATIONS.global.dir, CONFIG_LOCATIONS.global.file);
59
- try {
60
- if (fs.existsSync(globalPath)) {
61
- return globalPath;
62
- }
63
- } catch (error) {}
64
-
65
- const systemDir = CONFIG_LOCATIONS.system[process.platform] || CONFIG_LOCATIONS.system.linux;
66
- return path.join(systemDir, CONFIG_LOCATIONS.local.file);
67
- }
68
-
69
- async function ensureConfigDir() {
70
- const configPath = getConfigPath();
71
- const configDir = path.dirname(configPath);
72
- try {
73
- await fs.mkdir(configDir, { recursive: true, mode: 0o700 });
74
- } catch (error) {
75
- if (error.code !== 'EEXIST') {
76
- throw error;
77
- }
78
- }
79
- return configPath;
80
- }
81
-
82
- async function saveConfig(config) {
83
- const configPath = await ensureConfigDir();
84
- await fs.writeFile(configPath, JSON.stringify(config, null, 2), { mode: 0o600 });
85
- }
86
-
87
- async function loadConfig() {
88
- const storedConfig = {
89
- licenseKey: null,
90
- lastValidation: null,
91
- installationId: null
92
- };
93
-
94
- try {
95
- const configPath = getConfigPath();
96
- const configData = await fs.readFile(configPath, 'utf8');
97
- const parsedConfig = JSON.parse(configData);
98
-
99
- if (!parsedConfig.installationId) {
100
- parsedConfig.installationId = crypto.randomUUID();
101
- await saveConfig(parsedConfig);
102
- }
103
-
104
- return parsedConfig;
105
- } catch (error) {
106
- if (error.code === 'ENOENT') {
107
-
108
- storedConfig.installationId = crypto.randomUUID();
109
- await saveConfig(storedConfig);
110
- return storedConfig;
111
- }
112
- throw error;
113
- }
114
- }
115
-
116
- async function makeApiRequest(endpoint, data) {
117
- const config = await loadConfig();
118
- const headers = {
119
- 'Content-Type': 'application/json'
120
- };
121
-
122
- if (config.licenseKey) {
123
- headers['X-License-Key'] = config.licenseKey;
124
- }
125
-
126
- if (!config.installationId) {
127
- config.installationId = crypto.randomUUID();
128
- await saveConfig(config);
129
- }
130
-
131
- headers['X-Installation-Id'] = config.installationId;
132
-
133
- const response = await fetch(`${API_URL}${endpoint}`, {
134
- method: 'POST',
135
- headers,
136
- body: JSON.stringify(data)
137
- });
138
-
139
- if (!response.ok) {
140
- const errorData = await response.json();
141
-
142
- if (response.status === 429 && errorData.status === 'quota_exceeded') {
143
- log('Quota', 'Monthly request limit reached:', true);
144
- log('Info', errorData.message);
145
- log('Info', `Next reset date: ${new Date(errorData.next_reset_date).toLocaleDateString()}`);
146
- log('Info', `Remaining days: ${errorData.days_until_reset}`);
147
- log('Upgrade', 'To remove limits, activate a pro license:');
148
- log('Command', ' gitset activate <license-key>');
149
- log('Purchase', 'Visit https://gitset.dev/pricing to get a license');
150
- process.exit(1);
151
- }
152
-
153
- throw new Error(errorData.message || 'Failed to make request');
154
- }
155
-
156
- return response.json();
157
- }
158
-
159
- const IGNORED_FILES = [
160
- 'package-lock.json',
161
- 'yarn.lock',
162
- 'pnpm-lock.yaml',
163
- '.env',
164
- 'venv',
165
- 'node_modules',
166
- '.DS_Store',
167
- 'dist',
168
- 'build',
169
- '__pycache__',
170
- '.pytest_cache',
171
- 'coverage'
172
- ];
173
-
174
- function shouldIgnoreFile(filePath) {
175
- return IGNORED_FILES.some(ignored =>
176
- filePath === ignored ||
177
- filePath.startsWith(ignored + '/') ||
178
- filePath.includes('/node_modules/') ||
179
- filePath.includes('/__pycache__/') ||
180
- filePath.includes('/venv/')
181
- );
182
- }
183
-
184
- async function getLastCommits(count = 20) {
185
- try {
186
- log('History', `Fetching last ${count} commits...`);
187
- const { stdout } = await execAsync(`git log -${count} --pretty=format:"%s"`);
188
- const commits = stdout.split('\n');
189
- log('History', `✓ Retrieved ${commits.length} commits`);
190
- return commits;
191
- } catch (error) {
192
- log('History', `Error fetching commit history: ${error.message}`, true);
193
- return [];
194
- }
195
- }
196
-
197
- async function getGitDiff(file) {
198
- try {
199
- log('Diff', `Getting differences for ${file}...`);
200
- const { stdout: lastCommit } = await execAsync('git rev-parse HEAD');
201
-
202
- let previousContent = '';
203
- try {
204
- const { stdout } = await execAsync(`git show ${lastCommit.trim()}:${file}`);
205
- previousContent = stdout;
206
- log('Diff', `✓ Retrieved previous content`);
207
- } catch (e) {
208
- log('Diff', `New file detected: ${file}`);
209
- }
210
-
211
- const { stdout: currentContent } = await execAsync(`git show :${file}`);
212
- log('Diff', `✓ Retrieved current content`);
213
-
214
- return {
215
- previous: previousContent,
216
- current: currentContent
217
- };
218
- } catch (error) {
219
- log('Diff', `Error processing ${file}: ${error.message}`, true);
220
- return null;
221
- }
222
- }
223
-
224
- async function getStagedFiles() {
225
- try {
226
- log('Git', 'Retrieving staged files...');
227
- const { stdout } = await execAsync('git diff --cached --name-status');
228
- const files = stdout.split('\n')
229
- .filter(line => line)
230
- .map(line => {
231
- const [status, ...fileParts] = line.split('\t');
232
- const file = fileParts.join('\t');
233
- return { status, file };
234
- })
235
- .filter(({ file }) => !shouldIgnoreFile(file));
236
-
237
- log('Git', `✓ Found ${files.length} staged files`);
238
- return files;
239
- } catch (error) {
240
- log('Git', `Error: ${error.message}`, true);
241
- return [];
242
- }
243
- }
244
-
245
- async function getRepoInfo() {
246
- try {
247
- log('Repo', 'Retrieving repository information...');
248
- const { stdout: remoteUrl } = await execAsync('git config --get remote.origin.url');
249
- const repoPath = remoteUrl.trim()
250
- .replace('git@github.com:', '')
251
- .replace('https://github.com/', '')
252
- .replace('.git', '');
253
- log('Repo', `✓ Repository identified: ${repoPath}`);
254
- return repoPath;
255
- } catch (error) {
256
- log('Repo', `Error: ${error.message}`, true);
257
- return '';
258
- }
259
- }
260
-
261
- async function generateCommitMessage(options) {
262
- try {
263
- log('Start', 'Starting commit message generation process...');
264
- const { mode = 'semantic', commitCount = 20 } = options;
265
-
266
- const files = await getStagedFiles();
267
- if (files.length === 0) {
268
- log('Git', 'No files staged. Use `git add <file>` first.', true);
269
- return;
270
- }
271
-
272
- log('Files', 'Analyzing:');
273
- files.forEach(({ status, file }) => {
274
- const statusText = {
275
- 'A': `${colors.green}➕ Added`,
276
- 'M': `${colors.yellow}📝 Modified`,
277
- 'D': `${colors.red}❌ Deleted`
278
- }[status] || status;
279
- console.log(` ${statusText}${colors.reset}: ${file}`);
280
- });
281
-
282
- const fileChanges = [];
283
- for (const { status, file } of files) {
284
- const diffResult = await getGitDiff(file);
285
- if (!diffResult) continue;
286
-
287
- fileChanges.push({
288
- name: path.basename(file),
289
- path: file,
290
- changeType: status === 'A' ? 'added' : status === 'M' ? 'modified' : 'deleted',
291
- contentType: 'text',
292
- changes: {
293
- before: diffResult.previous,
294
- after: diffResult.current
295
- }
296
- });
297
- }
298
-
299
- const repoName = await getRepoInfo();
300
- const commitHistory = mode === 'custom' ? await getLastCommits(parseInt(commitCount)) : [];
301
-
302
- log('API', `Processing diffs with AI using ${mode} mode...`);
303
-
304
- const response = await makeApiRequest('/generate-commit-message', {
305
- repo_name: repoName,
306
- file_changes: fileChanges,
307
- mode: mode,
308
- commit_history: commitHistory
309
- });
310
-
311
- log('Success', '✨ Commit message generated\n');
312
-
313
- function formatCommitMessage(message) {
314
- const [title, ...descriptionParts] = message.split('\n');
315
- const description = descriptionParts.join('\n');
316
- return `${colors.bright}${colors.customCyan}${title}${colors.reset}${description ? `\n${colors.darkCyan}${description}${colors.reset}` : ''}`;
317
- }
318
-
319
- console.log(`${colors.bright}📝 Suggested message (${mode} mode):${colors.reset}`);
320
- console.log(`${colors.yellow}------------------${colors.reset}`);
321
- console.log(formatCommitMessage(response.commit_message));
322
-
323
- } catch (error) {
324
- log('Error', error.message, true);
325
- if (error.message.includes('permission denied')) {
326
- log('Help', 'Make sure you have the necessary permissions and are in a git repository.');
327
- }
328
- process.exit(1);
329
- }
330
- }
331
-
332
- program
333
- .name('gitset')
334
- .description('Smart AI Docs & Versioning for GitHub Repositories.')
335
- .version('1.1.0');
336
-
337
- program
338
- .command('activate')
339
- .description('Activate GitSet with a license key')
340
- .argument('[licenseKey]', 'Your GitSet license key')
341
- .action(async (licenseKey) => {
342
- try {
343
- if (!licenseKey) {
344
- const config = await loadConfig();
345
- if (config.licenseKey) {
346
- log('License', 'Current license status:');
347
- const status = await makeApiRequest('/validate-license', { licenseKey: config.licenseKey });
348
- log('Status', `Plan: ${status.data.product_name}`);
349
- log('Status', `Valid until: ${new Date(status.data.renews_at).toLocaleDateString()}`);
350
- } else {
351
- log('License', 'No license key configured', true);
352
- log('Info', 'You can use GitSet with limited features (10 requests/month)');
353
- log('Info', 'To unlock unlimited usage, activate a pro license:');
354
- log('Command', ' gitset activate <license-key>');
355
- log('Purchase', 'Visit https://gitset.dev/pricing to get a license');
356
- }
357
- return;
358
- }
359
-
360
- const response = await makeApiRequest('/validate-license', { licenseKey });
361
- const config = await loadConfig();
362
- await saveConfig({
363
- ...config, // Mantiene el installationId existente
364
- licenseKey,
365
- lastValidation: new Date().toISOString(),
366
- subscriptionData: response.data
367
- });
368
- log('Success', '✨ License activated successfully!');
369
- log('Plan', response.data.product_name);
370
- log('Info', 'You now have unlimited access to all features');
371
-
372
- } catch (error) {
373
- log('Error', error.message, true);
374
- process.exit(1);
375
- }
376
- });
377
-
378
- program
379
- .command('suggest')
380
- .description('Generate commit messages using AI-driven analysis of staged code changes.')
381
- .option('-m, --mode <mode>', 'Commit message mode (semantic or custom)', 'semantic')
382
- .option('-c, --commit-count <count>', 'Number of previous commits to analyze for custom mode', '20')
383
- .action(generateCommitMessage);
384
-
385
- program
386
- .command('status')
387
- .description('Check your GitSet license status and usage')
388
- .action(async () => {
389
- try {
390
- const config = await loadConfig();
391
-
392
- if (!config.licenseKey) {
393
- log('License', 'No license key configured');
394
- log('Info', 'You are using GitSet with limited features (10 requests/month)');
395
- log('Info', 'To unlock unlimited usage, activate a pro license:');
396
- log('Command', ' gitset activate <license-key>');
397
- log('Purchase', 'Visit https://gitset.dev/pricing to get a license');
398
- return;
399
- }
400
-
401
- const response = await makeApiRequest('/validate-license', { licenseKey: config.licenseKey });
402
-
403
- log('License', 'License is active and valid ✨');
404
- log('Plan', response.data.product_name);
405
- log('Status', response.data.status);
406
- if (response.data.renews_at) {
407
- log('Renewal', `Next renewal: ${new Date(response.data.renews_at).toLocaleDateString()}`);
408
- }
409
-
410
- if (!response.data.product_name.toLowerCase().includes('pro')) {
411
- log('Usage', 'Request limits (Basic Plan):');
412
- const usageResponse = await makeApiRequest('/usage-info', { licenseKey: config.licenseKey });
413
- log('Info', `Used ${usageResponse.current_usage} of ${usageResponse.limit} monthly requests`);
414
- log('Info', `Reset date: ${new Date(usageResponse.next_reset_date).toLocaleDateString()}`);
415
- } else {
416
- log('Usage', 'Unlimited requests (Pro Plan)');
417
- }
418
-
419
- } catch (error) {
420
- log('Error', error.message, true);
421
- if (error.message.includes('validation')) {
422
- log('Help', 'Your license might have expired or been deactivated');
423
- log('Info', 'You can check your subscription status at https://gitset.dev/account');
424
- }
425
- }
426
- });
427
-
428
- program
429
- .command('deactivate')
430
- .description('Deactivate and remove current license key')
431
- .action(async () => {
432
- try {
433
- const config = await loadConfig();
434
-
435
- if (!config.licenseKey) {
436
- log('Info', 'No active license found');
437
- return;
438
- }
439
-
440
- await saveConfig({
441
- ...config,
442
- licenseKey: null,
443
- lastValidation: null,
444
- subscriptionData: null,
445
- });
446
- log('Success', 'License deactivated successfully');
447
- log('Info', 'Switched to basic plan (10 requests/month)');
448
- log('Info', 'You can reactivate your license anytime with:');
449
- log('Command', ' gitset activate <license-key>');
450
-
451
- } catch (error) {
452
- log('Error', error.message, true);
453
- process.exit(1);
454
- }
455
- });
456
-
457
- program
458
- .command('help')
459
- .description('Show detailed help and usage information')
460
- .action(() => {
461
- console.log(`
462
- ${colors.bright}GitSet CLI - Smart AI Commit Messages${colors.reset}
463
-
464
- ${colors.cyan}Usage:${colors.reset}
465
- gitset suggest Generate a commit message for staged changes
466
- gitset activate <key> Activate GitSet with a license key
467
- gitset status Check license status and usage
468
- gitset deactivate Remove current license key
469
- gitset help Show this help message
470
-
471
- ${colors.cyan}Options for 'suggest':${colors.reset}
472
- -m, --mode <mode> Commit message mode (semantic or custom)
473
- -c, --commit-count <n> Number of commits to analyze in custom mode
474
-
475
- ${colors.cyan}Plans:${colors.reset}
476
- Basic (Free) 10 requests per month
477
- Pro Unlimited requests
478
-
479
- ${colors.cyan}Examples:${colors.reset}
480
- $ gitset suggest Generate semantic commit message
481
- $ gitset suggest -m custom Generate message matching your style
482
- $ gitset activate ABC123... Activate pro license
483
- $ gitset status Check current license status
484
-
485
- ${colors.cyan}Learn more:${colors.reset}
486
- Visit https://gitset.dev for documentation and pricing
487
- Report issues at https://github.com/gitset/cli/issues
488
- `);
489
- });
490
-
491
- program.exitOverride((err) => {
492
- if (err.code === 'commander.help') {
493
- process.exit(0);
494
- }
495
-
496
- log('Error', err.message, true);
497
-
498
- if (err.code === 'commander.unknownCommand') {
499
- log('Help', 'Run "gitset help" to see available commands');
500
- }
501
-
502
- process.exit(1);
503
- });
504
-
505
- program.parse();