@gitset-dev/cli 1.2.0 → 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.
- package/{LICENSE.md → LICENSE} +1 -1
- package/README.md +80 -159
- package/index.js +230 -0
- package/lib/ai/capabilities.js +81 -0
- package/lib/ai/errors.js +115 -0
- package/lib/ai/index.js +98 -0
- package/lib/ai/providers/anthropic.js +62 -0
- package/lib/ai/providers/gemini.js +77 -0
- package/lib/ai/providers/mock.js +41 -0
- package/lib/ai/providers/openai-compatible.js +72 -0
- package/lib/ai/retry.js +31 -0
- package/lib/cli-commit.js +143 -0
- package/lib/cli-config.js +207 -0
- package/lib/cli-issue.js +144 -0
- package/lib/cli-pr.js +168 -0
- package/lib/cli-readme.js +153 -0
- package/lib/commit-local.js +67 -0
- package/lib/config.js +134 -0
- package/lib/generate-local.js +59 -0
- package/lib/labels-ai.js +41 -0
- package/lib/prompts/defaults.js +191 -0
- package/lib/prompts/index.js +79 -0
- package/lib/setup-wizard.js +115 -0
- package/npm-shrinkwrap.json +566 -0
- package/package.json +35 -35
- package/src/commands/dependabot-resolver.js +314 -0
- package/src/commands/gitignore.js +110 -0
- package/src/commands/init.js +38 -0
- package/src/commands/labelspack.js +94 -0
- package/src/commands/license.js +208 -0
- package/src/commands/release.js +180 -0
- package/src/commands/repo.js +176 -0
- package/src/commands/status.js +56 -0
- package/src/commands/template.js +92 -0
- package/src/commands/tree.js +77 -0
- package/src/utils/dependabot-analyzer.js +101 -0
- package/src/utils/labels.js +102 -0
- package/src/utils/theme.js +70 -0
- package/src/utils/ui.js +173 -0
- package/.github/workflows/npm_publish.yml +0 -38
- 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();
|