commitect 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.
- package/.github/workflows/publish.yml +39 -0
- package/LICENSE +21 -0
- package/README.md +176 -0
- package/dist/commands/analyze.d.ts +2 -0
- package/dist/commands/analyze.d.ts.map +1 -0
- package/dist/commands/analyze.js +40 -0
- package/dist/commands/analyze.js.map +1 -0
- package/dist/commands/clear-cache.d.ts +2 -0
- package/dist/commands/clear-cache.d.ts.map +1 -0
- package/dist/commands/clear-cache.js +23 -0
- package/dist/commands/clear-cache.js.map +1 -0
- package/dist/commands/commit.d.ts +2 -0
- package/dist/commands/commit.d.ts.map +1 -0
- package/dist/commands/commit.js +42 -0
- package/dist/commands/commit.js.map +1 -0
- package/dist/commands/copy.d.ts +2 -0
- package/dist/commands/copy.d.ts.map +1 -0
- package/dist/commands/copy.js +42 -0
- package/dist/commands/copy.js.map +1 -0
- package/dist/commands/help.d.ts +2 -0
- package/dist/commands/help.d.ts.map +1 -0
- package/dist/commands/help.js +129 -0
- package/dist/commands/help.js.map +1 -0
- package/dist/commands/history.d.ts +2 -0
- package/dist/commands/history.d.ts.map +1 -0
- package/dist/commands/history.js +58 -0
- package/dist/commands/history.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +44 -0
- package/dist/index.js.map +1 -0
- package/dist/services/llm.d.ts +6 -0
- package/dist/services/llm.d.ts.map +1 -0
- package/dist/services/llm.js +94 -0
- package/dist/services/llm.js.map +1 -0
- package/dist/utils/cache.d.ts +61 -0
- package/dist/utils/cache.d.ts.map +1 -0
- package/dist/utils/cache.js +141 -0
- package/dist/utils/cache.js.map +1 -0
- package/dist/utils/git.d.ts +5 -0
- package/dist/utils/git.d.ts.map +1 -0
- package/dist/utils/git.js +69 -0
- package/dist/utils/git.js.map +1 -0
- package/package.json +38 -0
- package/src/commands/analyze.ts +44 -0
- package/src/commands/clear-cache.ts +23 -0
- package/src/commands/commit.ts +48 -0
- package/src/commands/copy.ts +48 -0
- package/src/commands/help.ts +143 -0
- package/src/commands/history.ts +62 -0
- package/src/index.ts +53 -0
- package/src/services/llm.ts +123 -0
- package/src/utils/cache.ts +170 -0
- package/src/utils/git.ts +74 -0
- package/tsconfig.json +20 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { commitCache } from '../utils/cache.js';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
export function historyCommand() {
|
|
4
|
+
try {
|
|
5
|
+
const history = commitCache.getHistory();
|
|
6
|
+
if (history.length === 0) {
|
|
7
|
+
console.log(chalk.yellow('ℹ No commit history found'));
|
|
8
|
+
console.log(chalk.gray(' Generate some commits first using commitect analyze/copy/commit'));
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
console.log('');
|
|
12
|
+
console.log(chalk.bold.cyan('📜 COMMIT HISTORY'));
|
|
13
|
+
console.log(chalk.gray('─'.repeat(70)));
|
|
14
|
+
console.log('');
|
|
15
|
+
history.forEach((entry, index) => {
|
|
16
|
+
const date = new Date(entry.timestamp);
|
|
17
|
+
const timeAgo = getTimeAgo(entry.timestamp);
|
|
18
|
+
// Format: [1] Feature: Add user authentication
|
|
19
|
+
console.log(chalk.bold.white(`[${index + 1}]`) + ' ' + chalk.green(`${entry.intent}: ${entry.message}`));
|
|
20
|
+
console.log(chalk.gray(` 📁 ${entry.folder}`));
|
|
21
|
+
console.log(chalk.gray(` 🕒 ${date.toLocaleString()} (${timeAgo})`));
|
|
22
|
+
console.log('');
|
|
23
|
+
});
|
|
24
|
+
console.log(chalk.gray('─'.repeat(70)));
|
|
25
|
+
console.log(chalk.gray(`Total: ${history.length} cached commit message${history.length !== 1 ? 's' : ''}`));
|
|
26
|
+
console.log('');
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
if (error instanceof Error) {
|
|
30
|
+
console.error(chalk.red('❌ ' + error.message));
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
console.error(chalk.red('❌ Failed to load history'));
|
|
34
|
+
}
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function getTimeAgo(timestamp) {
|
|
39
|
+
const now = Date.now();
|
|
40
|
+
const diff = now - timestamp;
|
|
41
|
+
const seconds = Math.floor(diff / 1000);
|
|
42
|
+
const minutes = Math.floor(seconds / 60);
|
|
43
|
+
const hours = Math.floor(minutes / 60);
|
|
44
|
+
const days = Math.floor(hours / 24);
|
|
45
|
+
if (days > 0) {
|
|
46
|
+
return `${days} day${days !== 1 ? 's' : ''} ago`;
|
|
47
|
+
}
|
|
48
|
+
else if (hours > 0) {
|
|
49
|
+
return `${hours} hour${hours !== 1 ? 's' : ''} ago`;
|
|
50
|
+
}
|
|
51
|
+
else if (minutes > 0) {
|
|
52
|
+
return `${minutes} minute${minutes !== 1 ? 's' : ''} ago`;
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
return 'just now';
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=history.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"history.js","sourceRoot":"","sources":["../../src/commands/history.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import { analyzeCommand } from './commands/analyze.js';
|
|
4
|
+
import { copyCommand } from './commands/copy.js';
|
|
5
|
+
import { commitCommand } from './commands/commit.js';
|
|
6
|
+
import { clearCacheCommand } from './commands/clear-cache.js';
|
|
7
|
+
import { historyCommand } from './commands/history.js';
|
|
8
|
+
import { helpCommand } from './commands/help.js';
|
|
9
|
+
const program = new Command();
|
|
10
|
+
program
|
|
11
|
+
.name('commitect')
|
|
12
|
+
.description('Zero-config Git Commit Assistant')
|
|
13
|
+
.version('1.0.0');
|
|
14
|
+
program
|
|
15
|
+
.command('analyze')
|
|
16
|
+
.description('Analyze changes and suggest a commit message')
|
|
17
|
+
.action(analyzeCommand);
|
|
18
|
+
program
|
|
19
|
+
.command('copy')
|
|
20
|
+
.description('Generate commit message and copy to clipboard')
|
|
21
|
+
.action(copyCommand);
|
|
22
|
+
program
|
|
23
|
+
.command('commit')
|
|
24
|
+
.description('Generate and execute git commit')
|
|
25
|
+
.action(commitCommand);
|
|
26
|
+
program
|
|
27
|
+
.command('history')
|
|
28
|
+
.description('Show cached commit message history')
|
|
29
|
+
.action(historyCommand);
|
|
30
|
+
program
|
|
31
|
+
.command('clear-cache')
|
|
32
|
+
.description('Clear the commit message cache')
|
|
33
|
+
.action(clearCacheCommand);
|
|
34
|
+
program
|
|
35
|
+
.command('help')
|
|
36
|
+
.description('Show detailed help and examples')
|
|
37
|
+
.action(helpCommand);
|
|
38
|
+
// Show help by default if no command is provided
|
|
39
|
+
if (process.argv.length === 2) {
|
|
40
|
+
helpCommand();
|
|
41
|
+
process.exit(0);
|
|
42
|
+
}
|
|
43
|
+
program.parse();
|
|
44
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEjD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,WAAW,CAAC;KACjB,WAAW,CAAC,kCAAkC,CAAC;KAC/C,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpB,OAAO;KACJ,OAAO,CAAC,SAAS,CAAC;KAClB,WAAW,CAAC,8CAA8C,CAAC;KAC3D,MAAM,CAAC,cAAc,CAAC,CAAC;AAE1B,OAAO;KACJ,OAAO,CAAC,MAAM,CAAC;KACf,WAAW,CAAC,+CAA+C,CAAC;KAC5D,MAAM,CAAC,WAAW,CAAC,CAAC;AAEvB,OAAO;KACJ,OAAO,CAAC,QAAQ,CAAC;KACjB,WAAW,CAAC,iCAAiC,CAAC;KAC9C,MAAM,CAAC,aAAa,CAAC,CAAC;AAEzB,OAAO;KACJ,OAAO,CAAC,SAAS,CAAC;KAClB,WAAW,CAAC,oCAAoC,CAAC;KACjD,MAAM,CAAC,cAAc,CAAC,CAAC;AAE1B,OAAO;KACJ,OAAO,CAAC,aAAa,CAAC;KACtB,WAAW,CAAC,gCAAgC,CAAC;KAC7C,MAAM,CAAC,iBAAiB,CAAC,CAAC;AAE7B,OAAO;KACJ,OAAO,CAAC,MAAM,CAAC;KACf,WAAW,CAAC,iCAAiC,CAAC;KAC9C,MAAM,CAAC,WAAW,CAAC,CAAC;AAEvB,iDAAiD;AACjD,IAAI,OAAO,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;IAC9B,WAAW,EAAE,CAAC;IACd,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,OAAO,CAAC,KAAK,EAAE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"llm.d.ts","sourceRoot":"","sources":["../../src/services/llm.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB;AAID,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAmFnF"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import { commitCache } from '../utils/cache.js';
|
|
3
|
+
const API_ENDPOINT = 'http://commitintentdetector.runasp.net/api/Commit/analyze';
|
|
4
|
+
export async function generateCommitMessage(diff) {
|
|
5
|
+
// Check cache first
|
|
6
|
+
const cached = commitCache.get(diff);
|
|
7
|
+
if (cached) {
|
|
8
|
+
return cached;
|
|
9
|
+
}
|
|
10
|
+
const maxRetries = 3;
|
|
11
|
+
let lastError = null;
|
|
12
|
+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
13
|
+
try {
|
|
14
|
+
const response = await axios.post(API_ENDPOINT, { diff }, {
|
|
15
|
+
headers: {
|
|
16
|
+
'Content-Type': 'application/json'
|
|
17
|
+
},
|
|
18
|
+
timeout: 30000 // 30 second timeout
|
|
19
|
+
});
|
|
20
|
+
// API returns: { intent: "Intent: Feature\nMessage: Add subtraction support function" }
|
|
21
|
+
const data = response.data;
|
|
22
|
+
if (!data || !data.intent) {
|
|
23
|
+
throw new Error('Invalid response format from API');
|
|
24
|
+
}
|
|
25
|
+
const result = parseResponse(data.intent);
|
|
26
|
+
// Cache the result
|
|
27
|
+
commitCache.set(diff, result.intent, result.message);
|
|
28
|
+
return result;
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
lastError = error;
|
|
32
|
+
if (axios.isAxiosError(error)) {
|
|
33
|
+
const axiosError = error;
|
|
34
|
+
// Handle rate limiting (429)
|
|
35
|
+
if (axiosError.response?.status === 429) {
|
|
36
|
+
if (attempt < maxRetries) {
|
|
37
|
+
await sleep(1000 * attempt); // Exponential backoff
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
throw new Error('API rate limit reached. Please try again later.');
|
|
41
|
+
}
|
|
42
|
+
// Handle server errors (5xx) - retry
|
|
43
|
+
if (axiosError.response?.status && axiosError.response.status >= 500) {
|
|
44
|
+
if (attempt < maxRetries) {
|
|
45
|
+
await sleep(1000 * attempt);
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
// Handle network errors - retry
|
|
50
|
+
if (axiosError.code === 'ECONNREFUSED' || axiosError.code === 'ETIMEDOUT') {
|
|
51
|
+
if (attempt < maxRetries) {
|
|
52
|
+
await sleep(1000 * attempt);
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
throw new Error('Unable to connect to API. Please check your network connection.');
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// Don't retry on client errors (4xx) except 429
|
|
59
|
+
if (axios.isAxiosError(error) && error.response?.status && error.response.status < 500 && error.response.status !== 429) {
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
// Retry on other errors
|
|
63
|
+
if (attempt < maxRetries) {
|
|
64
|
+
await sleep(1000 * attempt);
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
throw new Error('Failed to generate commit message. Please check your API configuration.');
|
|
70
|
+
}
|
|
71
|
+
function parseResponse(response) {
|
|
72
|
+
const lines = response.trim().split('\n');
|
|
73
|
+
let intent = '';
|
|
74
|
+
let message = '';
|
|
75
|
+
for (const line of lines) {
|
|
76
|
+
if (line.startsWith('Intent:')) {
|
|
77
|
+
intent = line.replace('Intent:', '').trim();
|
|
78
|
+
}
|
|
79
|
+
else if (line.startsWith('Message:')) {
|
|
80
|
+
message = line.replace('Message:', '').trim();
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (!intent || !message) {
|
|
84
|
+
throw new Error('Invalid response format from LLM');
|
|
85
|
+
}
|
|
86
|
+
if (message.length > 70) {
|
|
87
|
+
message = message.substring(0, 67) + '...';
|
|
88
|
+
}
|
|
89
|
+
return { intent, message };
|
|
90
|
+
}
|
|
91
|
+
function sleep(ms) {
|
|
92
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=llm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"llm.js","sourceRoot":"","sources":["../../src/services/llm.ts"],"names":[],"mappings":"AAAA,OAAO,KAAqB,MAAM,OAAO,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAOhD,MAAM,YAAY,GAAG,2DAA2D,CAAC;AAEjF,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,IAAY;IACtD,oBAAoB;IACpB,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACrC,IAAI,MAAM,EAAE,CAAC;QACX,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,MAAM,UAAU,GAAG,CAAC,CAAC;IACrB,IAAI,SAAS,GAAiB,IAAI,CAAC;IAEnC,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,UAAU,EAAE,OAAO,EAAE,EAAE,CAAC;QACvD,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,IAAI,CAC/B,YAAY,EACZ,EAAE,IAAI,EAAE,EACR;gBACE,OAAO,EAAE;oBACP,cAAc,EAAE,kBAAkB;iBACnC;gBACD,OAAO,EAAE,KAAK,CAAC,oBAAoB;aACpC,CACF,CAAC;YAEF,wFAAwF;YACxF,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC;YAE3B,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;gBAC1B,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;YACtD,CAAC;YAED,MAAM,MAAM,GAAG,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAE1C,mBAAmB;YACnB,WAAW,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;YAErD,OAAO,MAAM,CAAC;QAEhB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,SAAS,GAAG,KAAc,CAAC;YAE3B,IAAI,KAAK,CAAC,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC9B,MAAM,UAAU,GAAG,KAAmB,CAAC;gBAEvC,6BAA6B;gBAC7B,IAAI,UAAU,CAAC,QAAQ,EAAE,MAAM,KAAK,GAAG,EAAE,CAAC;oBACxC,IAAI,OAAO,GAAG,UAAU,EAAE,CAAC;wBACzB,MAAM,KAAK,CAAC,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,sBAAsB;wBACnD,SAAS;oBACX,CAAC;oBACD,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;gBACrE,CAAC;gBAED,qCAAqC;gBACrC,IAAI,UAAU,CAAC,QAAQ,EAAE,MAAM,IAAI,UAAU,CAAC,QAAQ,CAAC,MAAM,IAAI,GAAG,EAAE,CAAC;oBACrE,IAAI,OAAO,GAAG,UAAU,EAAE,CAAC;wBACzB,MAAM,KAAK,CAAC,IAAI,GAAG,OAAO,CAAC,CAAC;wBAC5B,SAAS;oBACX,CAAC;gBACH,CAAC;gBAED,gCAAgC;gBAChC,IAAI,UAAU,CAAC,IAAI,KAAK,cAAc,IAAI,UAAU,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;oBAC1E,IAAI,OAAO,GAAG,UAAU,EAAE,CAAC;wBACzB,MAAM,KAAK,CAAC,IAAI,GAAG,OAAO,CAAC,CAAC;wBAC5B,SAAS;oBACX,CAAC;oBACD,MAAM,IAAI,KAAK,CAAC,iEAAiE,CAAC,CAAC;gBACrF,CAAC;YACH,CAAC;YAED,gDAAgD;YAChD,IAAI,KAAK,CAAC,YAAY,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,QAAQ,EAAE,MAAM,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,GAAG,GAAG,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;gBACxH,MAAM;YACR,CAAC;YAED,wBAAwB;YACxB,IAAI,OAAO,GAAG,UAAU,EAAE,CAAC;gBACzB,MAAM,KAAK,CAAC,IAAI,GAAG,OAAO,CAAC,CAAC;gBAC5B,SAAS;YACX,CAAC;QACH,CAAC;IACH,CAAC;IAED,MAAM,IAAI,KAAK,CAAC,yEAAyE,CAAC,CAAC;AAC7F,CAAC;AAED,SAAS,aAAa,CAAC,QAAgB;IACrC,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAE1C,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,IAAI,OAAO,GAAG,EAAE,CAAC;IAEjB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YAC/B,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QAC9C,CAAC;aAAM,IAAI,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;YACvC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QAChD,CAAC;IACH,CAAC;IAED,IAAI,CAAC,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;IACtD,CAAC;IAED,IAAI,OAAO,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;QACxB,OAAO,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,KAAK,CAAC;IAC7C,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;AAC7B,CAAC;AAED,SAAS,KAAK,CAAC,EAAU;IACvB,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;AACzD,CAAC"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
interface CacheEntry {
|
|
2
|
+
hash: string;
|
|
3
|
+
intent: string;
|
|
4
|
+
message: string;
|
|
5
|
+
timestamp: number;
|
|
6
|
+
folder: string;
|
|
7
|
+
}
|
|
8
|
+
declare class CommitCache {
|
|
9
|
+
private cache;
|
|
10
|
+
constructor();
|
|
11
|
+
/**
|
|
12
|
+
* Generate a hash from the git diff
|
|
13
|
+
*/
|
|
14
|
+
private hashDiff;
|
|
15
|
+
/**
|
|
16
|
+
* Get current folder name
|
|
17
|
+
*/
|
|
18
|
+
private getCurrentFolder;
|
|
19
|
+
/**
|
|
20
|
+
* Load cache from disk
|
|
21
|
+
*/
|
|
22
|
+
private loadCache;
|
|
23
|
+
/**
|
|
24
|
+
* Save cache to disk
|
|
25
|
+
*/
|
|
26
|
+
private saveCache;
|
|
27
|
+
/**
|
|
28
|
+
* Get cached commit message for a diff
|
|
29
|
+
*/
|
|
30
|
+
get(diff: string): {
|
|
31
|
+
intent: string;
|
|
32
|
+
message: string;
|
|
33
|
+
folder: string;
|
|
34
|
+
} | null;
|
|
35
|
+
/**
|
|
36
|
+
* Store a commit message in cache
|
|
37
|
+
*/
|
|
38
|
+
set(diff: string, intent: string, message: string): void;
|
|
39
|
+
/**
|
|
40
|
+
* Clear all cache
|
|
41
|
+
*/
|
|
42
|
+
clear(): void;
|
|
43
|
+
/**
|
|
44
|
+
* Get cache statistics
|
|
45
|
+
*/
|
|
46
|
+
getStats(): {
|
|
47
|
+
size: number;
|
|
48
|
+
oldestEntry: number | null;
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Get all cache entries sorted by timestamp (newest first)
|
|
52
|
+
*/
|
|
53
|
+
getHistory(): CacheEntry[];
|
|
54
|
+
/**
|
|
55
|
+
* Get cache entries for a specific folder
|
|
56
|
+
*/
|
|
57
|
+
getHistoryByFolder(folder: string): CacheEntry[];
|
|
58
|
+
}
|
|
59
|
+
export declare const commitCache: CommitCache;
|
|
60
|
+
export {};
|
|
61
|
+
//# sourceMappingURL=cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../src/utils/cache.ts"],"names":[],"mappings":"AAKA,UAAU,UAAU;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAChB;AAMD,cAAM,WAAW;IACf,OAAO,CAAC,KAAK,CAA0B;;IAOvC;;OAEG;IACH,OAAO,CAAC,QAAQ;IAIhB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAIxB;;OAEG;IACH,OAAO,CAAC,SAAS;IAwBjB;;OAEG;IACH,OAAO,CAAC,SAAS;IAcjB;;OAEG;IACH,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IAuB7E;;OAEG;IACH,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;IAcxD;;OAEG;IACH,KAAK,IAAI,IAAI;IAKb;;OAEG;IACH,QAAQ,IAAI;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE;IAYxD;;OAEG;IACH,UAAU,IAAI,UAAU,EAAE;IAK1B;;OAEG;IACH,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,EAAE;CAMjD;AAGD,eAAO,MAAM,WAAW,aAAoB,CAAC"}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { createHash } from 'crypto';
|
|
2
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
|
|
3
|
+
import { homedir } from 'os';
|
|
4
|
+
import { join, basename } from 'path';
|
|
5
|
+
const CACHE_DIR = join(homedir(), '.commitect');
|
|
6
|
+
const CACHE_FILE = join(CACHE_DIR, 'cache.json');
|
|
7
|
+
const CACHE_MAX_AGE = 30 * 24 * 60 * 60 * 1000; // 30 days in milliseconds
|
|
8
|
+
class CommitCache {
|
|
9
|
+
constructor() {
|
|
10
|
+
this.cache = new Map();
|
|
11
|
+
this.loadCache();
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Generate a hash from the git diff
|
|
15
|
+
*/
|
|
16
|
+
hashDiff(diff) {
|
|
17
|
+
return createHash('sha256').update(diff.trim()).digest('hex');
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Get current folder name
|
|
21
|
+
*/
|
|
22
|
+
getCurrentFolder() {
|
|
23
|
+
return basename(process.cwd());
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Load cache from disk
|
|
27
|
+
*/
|
|
28
|
+
loadCache() {
|
|
29
|
+
try {
|
|
30
|
+
if (!existsSync(CACHE_DIR)) {
|
|
31
|
+
mkdirSync(CACHE_DIR, { recursive: true });
|
|
32
|
+
}
|
|
33
|
+
if (existsSync(CACHE_FILE)) {
|
|
34
|
+
const data = readFileSync(CACHE_FILE, 'utf-8');
|
|
35
|
+
const entries = JSON.parse(data);
|
|
36
|
+
// Load valid entries (not expired)
|
|
37
|
+
const now = Date.now();
|
|
38
|
+
entries.forEach(entry => {
|
|
39
|
+
if (now - entry.timestamp < CACHE_MAX_AGE) {
|
|
40
|
+
this.cache.set(entry.hash, entry);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
// If cache is corrupted, start fresh
|
|
47
|
+
this.cache.clear();
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Save cache to disk
|
|
52
|
+
*/
|
|
53
|
+
saveCache() {
|
|
54
|
+
try {
|
|
55
|
+
if (!existsSync(CACHE_DIR)) {
|
|
56
|
+
mkdirSync(CACHE_DIR, { recursive: true });
|
|
57
|
+
}
|
|
58
|
+
const entries = Array.from(this.cache.values());
|
|
59
|
+
writeFileSync(CACHE_FILE, JSON.stringify(entries, null, 2), 'utf-8');
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
// Silently fail - caching is optional
|
|
63
|
+
console.warn('Warning: Failed to save cache');
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Get cached commit message for a diff
|
|
68
|
+
*/
|
|
69
|
+
get(diff) {
|
|
70
|
+
const hash = this.hashDiff(diff);
|
|
71
|
+
const entry = this.cache.get(hash);
|
|
72
|
+
if (!entry) {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
// Check if entry is still valid
|
|
76
|
+
const now = Date.now();
|
|
77
|
+
if (now - entry.timestamp > CACHE_MAX_AGE) {
|
|
78
|
+
this.cache.delete(hash);
|
|
79
|
+
this.saveCache();
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
intent: entry.intent,
|
|
84
|
+
message: entry.message,
|
|
85
|
+
folder: entry.folder
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Store a commit message in cache
|
|
90
|
+
*/
|
|
91
|
+
set(diff, intent, message) {
|
|
92
|
+
const hash = this.hashDiff(diff);
|
|
93
|
+
this.cache.set(hash, {
|
|
94
|
+
hash,
|
|
95
|
+
intent,
|
|
96
|
+
message,
|
|
97
|
+
timestamp: Date.now(),
|
|
98
|
+
folder: this.getCurrentFolder()
|
|
99
|
+
});
|
|
100
|
+
this.saveCache();
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Clear all cache
|
|
104
|
+
*/
|
|
105
|
+
clear() {
|
|
106
|
+
this.cache.clear();
|
|
107
|
+
this.saveCache();
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Get cache statistics
|
|
111
|
+
*/
|
|
112
|
+
getStats() {
|
|
113
|
+
const entries = Array.from(this.cache.values());
|
|
114
|
+
const oldestEntry = entries.length > 0
|
|
115
|
+
? Math.min(...entries.map(e => e.timestamp))
|
|
116
|
+
: null;
|
|
117
|
+
return {
|
|
118
|
+
size: this.cache.size,
|
|
119
|
+
oldestEntry
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Get all cache entries sorted by timestamp (newest first)
|
|
124
|
+
*/
|
|
125
|
+
getHistory() {
|
|
126
|
+
const entries = Array.from(this.cache.values());
|
|
127
|
+
return entries.sort((a, b) => b.timestamp - a.timestamp);
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Get cache entries for a specific folder
|
|
131
|
+
*/
|
|
132
|
+
getHistoryByFolder(folder) {
|
|
133
|
+
const entries = Array.from(this.cache.values());
|
|
134
|
+
return entries
|
|
135
|
+
.filter(entry => entry.folder === folder)
|
|
136
|
+
.sort((a, b) => b.timestamp - a.timestamp);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
// Singleton instance
|
|
140
|
+
export const commitCache = new CommitCache();
|
|
141
|
+
//# sourceMappingURL=cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../../src/utils/cache.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../../src/utils/git.ts"],"names":[],"mappings":"AAWA,wBAAgB,eAAe,IAAI,OAAO,CAOzC;AAED,wBAAgB,UAAU,IAAI,MAAM,CAYnC;AAED,wBAAgB,UAAU,IAAI,OAAO,CASpC;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAQnD"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { execSync } from 'child_process';
|
|
2
|
+
const IGNORED_PATHS = [
|
|
3
|
+
'node_modules/',
|
|
4
|
+
'bin/',
|
|
5
|
+
'obj/',
|
|
6
|
+
'dist/',
|
|
7
|
+
'build/',
|
|
8
|
+
'.git/'
|
|
9
|
+
];
|
|
10
|
+
export function isGitRepository() {
|
|
11
|
+
try {
|
|
12
|
+
execSync('git rev-parse --git-dir', { stdio: 'pipe' });
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export function getGitDiff() {
|
|
20
|
+
try {
|
|
21
|
+
// Get both staged and unstaged changes
|
|
22
|
+
const diff = execSync('git diff HEAD', {
|
|
23
|
+
encoding: 'utf-8',
|
|
24
|
+
maxBuffer: 10 * 1024 * 1024 // 10MB buffer
|
|
25
|
+
});
|
|
26
|
+
return filterIgnoredPaths(diff);
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
throw new Error('Failed to read git diff');
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
export function hasChanges() {
|
|
33
|
+
try {
|
|
34
|
+
const status = execSync('git status --porcelain', {
|
|
35
|
+
encoding: 'utf-8'
|
|
36
|
+
});
|
|
37
|
+
return status.trim().length > 0;
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
export function executeCommit(message) {
|
|
44
|
+
try {
|
|
45
|
+
execSync(`git commit -m "${message.replace(/"/g, '\\"')}"`, {
|
|
46
|
+
stdio: 'inherit'
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
throw new Error('Git commit failed');
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function filterIgnoredPaths(diff) {
|
|
54
|
+
const lines = diff.split('\n');
|
|
55
|
+
const filteredLines = [];
|
|
56
|
+
let skipFile = false;
|
|
57
|
+
for (const line of lines) {
|
|
58
|
+
// Check if this is a file header
|
|
59
|
+
if (line.startsWith('diff --git')) {
|
|
60
|
+
const path = line.split(' b/')[1];
|
|
61
|
+
skipFile = IGNORED_PATHS.some(ignored => path?.startsWith(ignored));
|
|
62
|
+
}
|
|
63
|
+
if (!skipFile) {
|
|
64
|
+
filteredLines.push(line);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return filteredLines.join('\n');
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=git.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git.js","sourceRoot":"","sources":["../../src/utils/git.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAEzC,MAAM,aAAa,GAAG;IACpB,eAAe;IACf,MAAM;IACN,MAAM;IACN,OAAO;IACP,QAAQ;IACR,OAAO;CACR,CAAC;AAEF,MAAM,UAAU,eAAe;IAC7B,IAAI,CAAC;QACH,QAAQ,CAAC,yBAAyB,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;QACvD,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,MAAM,UAAU,UAAU;IACxB,IAAI,CAAC;QACH,uCAAuC;QACvC,MAAM,IAAI,GAAG,QAAQ,CAAC,eAAe,EAAE;YACrC,QAAQ,EAAE,OAAO;YACjB,SAAS,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC,cAAc;SAC3C,CAAC,CAAC;QAEH,OAAO,kBAAkB,CAAC,IAAI,CAAC,CAAC;IAClC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAC7C,CAAC;AACH,CAAC;AAED,MAAM,UAAU,UAAU;IACxB,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,QAAQ,CAAC,wBAAwB,EAAE;YAChD,QAAQ,EAAE,OAAO;SAClB,CAAC,CAAC;QACH,OAAO,MAAM,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC;IAClC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,OAAe;IAC3C,IAAI,CAAC;QACH,QAAQ,CAAC,kBAAkB,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,GAAG,EAAE;YAC1D,KAAK,EAAE,SAAS;SACjB,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC;IACvC,CAAC;AACH,CAAC;AAED,SAAS,kBAAkB,CAAC,IAAY;IACtC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC/B,MAAM,aAAa,GAAa,EAAE,CAAC;IACnC,IAAI,QAAQ,GAAG,KAAK,CAAC;IAErB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,iCAAiC;QACjC,IAAI,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;YAClC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;YAClC,QAAQ,GAAG,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC;QACtE,CAAC;QAED,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC3B,CAAC;IACH,CAAC;IAED,OAAO,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAClC,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "commitect",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Zero-config Git Commit Assistant CLI",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"commitect": "./dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"dev": "ts-node src/index.ts",
|
|
13
|
+
"prepublishOnly": "npm run build"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"git",
|
|
17
|
+
"commit",
|
|
18
|
+
"cli",
|
|
19
|
+
"ai",
|
|
20
|
+
"assistant"
|
|
21
|
+
],
|
|
22
|
+
"author": "",
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"commander": "^11.1.0",
|
|
26
|
+
"chalk": "^4.1.2",
|
|
27
|
+
"clipboardy": "^3.0.0",
|
|
28
|
+
"axios": "^1.6.2"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@types/node": "^20.10.5",
|
|
32
|
+
"typescript": "^5.3.3",
|
|
33
|
+
"ts-node": "^10.9.2"
|
|
34
|
+
},
|
|
35
|
+
"engines": {
|
|
36
|
+
"node": ">=16.0.0"
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { isGitRepository, getGitDiff, hasChanges } from '../utils/git.js';
|
|
2
|
+
import { generateCommitMessage } from '../services/llm.js';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
|
|
5
|
+
export async function analyzeCommand(): Promise<void> {
|
|
6
|
+
try {
|
|
7
|
+
// Validate git repository
|
|
8
|
+
if (!isGitRepository()) {
|
|
9
|
+
console.error(chalk.red('❌ Not a git repository'));
|
|
10
|
+
process.exit(1);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// Check for changes
|
|
14
|
+
if (!hasChanges()) {
|
|
15
|
+
console.log(chalk.yellow('⚠ No changes detected'));
|
|
16
|
+
process.exit(0);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Get diff
|
|
20
|
+
const diff = getGitDiff();
|
|
21
|
+
|
|
22
|
+
if (!diff.trim()) {
|
|
23
|
+
console.log(chalk.yellow('⚠ No changes to analyze'));
|
|
24
|
+
process.exit(0);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Generate commit message
|
|
28
|
+
console.log(chalk.blue('🔍 Analyzing changes...'));
|
|
29
|
+
const suggestion = await generateCommitMessage(diff);
|
|
30
|
+
|
|
31
|
+
// Print result
|
|
32
|
+
console.log('');
|
|
33
|
+
console.log(chalk.green(`${suggestion.intent}: ${suggestion.message}`));
|
|
34
|
+
console.log('');
|
|
35
|
+
|
|
36
|
+
} catch (error) {
|
|
37
|
+
if (error instanceof Error) {
|
|
38
|
+
console.error(chalk.red('❌ ' + error.message));
|
|
39
|
+
} else {
|
|
40
|
+
console.error(chalk.red('❌ An unexpected error occurred'));
|
|
41
|
+
}
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { commitCache } from '../utils/cache.js';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
|
|
4
|
+
export function clearCacheCommand(): void {
|
|
5
|
+
try {
|
|
6
|
+
const stats = commitCache.getStats();
|
|
7
|
+
|
|
8
|
+
if (stats.size === 0) {
|
|
9
|
+
console.log(chalk.yellow('ℹ Cache is already empty'));
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
commitCache.clear();
|
|
14
|
+
console.log(chalk.green(`✔ Cache cleared (${stats.size} entries removed)`));
|
|
15
|
+
} catch (error) {
|
|
16
|
+
if (error instanceof Error) {
|
|
17
|
+
console.error(chalk.red('❌ ' + error.message));
|
|
18
|
+
} else {
|
|
19
|
+
console.error(chalk.red('❌ Failed to clear cache'));
|
|
20
|
+
}
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
}
|