@gilbert_oliveira/commit-wizard 2.12.3-canary.1 → 2.12.3-canary.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/commit-wizard.js +85 -65
- package/package.json +4 -2
- package/src/bin/commit-wizard.ts +12 -11
- package/src/cache/analysis.ts +210 -0
- package/src/commands/commit.ts +410 -0
- package/src/commands/init.ts +17 -0
- package/src/commands/version.ts +5 -0
- package/src/commitlint/index.ts +253 -0
- package/src/config/index.ts +25 -0
- package/src/core/cache.ts +13 -209
- package/src/core/index.ts +3 -385
- package/src/core/openai.ts +18 -465
- package/src/core/smart-split.ts +7 -722
- package/src/git/diff-filter.ts +118 -0
- package/src/pipeline/enforce.ts +152 -0
- package/src/pipeline/generate.ts +116 -0
- package/src/pipeline/split.ts +737 -0
- package/src/pipeline/types.ts +26 -0
- package/src/prompt/builder.ts +61 -0
- package/src/prompt/system.ts +69 -0
- package/src/prompt/templates.ts +172 -0
- package/src/prompt/types.ts +4 -0
- package/src/providers/factory.ts +18 -0
- package/src/providers/openai.ts +367 -0
- package/src/providers/types.ts +11 -0
- package/src/types/clack.d.ts +49 -4
- package/src/ui/format.ts +33 -0
- package/src/ui/index.ts +9 -5
- package/src/ui/smart-split.ts +1 -1
- package/src/ui/spinner.ts +23 -0
- package/src/ui/theme.ts +17 -0
- package/src/utils/args.ts +16 -8
- package/src/utils/hash.ts +9 -0
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import { existsSync, writeFileSync, readFileSync } from 'fs';
|
|
2
|
+
import { join, extname, basename } from 'path';
|
|
3
|
+
import { spawnSync } from 'child_process';
|
|
4
|
+
|
|
5
|
+
export interface CommitlintResult {
|
|
6
|
+
valid: boolean;
|
|
7
|
+
errors: string[];
|
|
8
|
+
warnings: string[];
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const COMMITLINT_CONFIG_FILES = [
|
|
12
|
+
'commitlint.config.js',
|
|
13
|
+
'commitlint.config.ts',
|
|
14
|
+
'commitlint.config.mjs',
|
|
15
|
+
'commitlint.config.cjs',
|
|
16
|
+
'.commitlintrc',
|
|
17
|
+
'.commitlintrc.js',
|
|
18
|
+
'.commitlintrc.json',
|
|
19
|
+
'.commitlintrc.yml',
|
|
20
|
+
'.commitlintrc.yaml',
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
export interface CommitlintRules {
|
|
24
|
+
typeEnum?: string[];
|
|
25
|
+
headerMaxLength?: number;
|
|
26
|
+
subjectCase?: { severity: number; condition: string; cases: string[] };
|
|
27
|
+
subjectFullStop?: { severity: number; condition: string; value: string };
|
|
28
|
+
bodyLeadingBlank?: boolean;
|
|
29
|
+
scopeEnum?: string[];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function extractRulesFromConfig(config: unknown): CommitlintRules {
|
|
33
|
+
const rules: CommitlintRules = {};
|
|
34
|
+
const rawRules =
|
|
35
|
+
(config as { rules?: Record<string, unknown[]> })?.rules ?? {};
|
|
36
|
+
|
|
37
|
+
const typeEnumRule = rawRules['type-enum'];
|
|
38
|
+
if (Array.isArray(typeEnumRule) && Array.isArray(typeEnumRule[2])) {
|
|
39
|
+
rules.typeEnum = typeEnumRule[2] as string[];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const headerMaxLengthRule = rawRules['header-max-length'];
|
|
43
|
+
if (
|
|
44
|
+
Array.isArray(headerMaxLengthRule) &&
|
|
45
|
+
typeof headerMaxLengthRule[2] === 'number'
|
|
46
|
+
) {
|
|
47
|
+
rules.headerMaxLength = headerMaxLengthRule[2];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const subjectCaseRule = rawRules['subject-case'];
|
|
51
|
+
if (Array.isArray(subjectCaseRule)) {
|
|
52
|
+
const [severity, condition, cases] = subjectCaseRule;
|
|
53
|
+
if (Array.isArray(cases)) {
|
|
54
|
+
rules.subjectCase = {
|
|
55
|
+
severity: severity as number,
|
|
56
|
+
condition: condition as string,
|
|
57
|
+
cases: cases as string[],
|
|
58
|
+
};
|
|
59
|
+
} else if (typeof cases === 'string') {
|
|
60
|
+
rules.subjectCase = {
|
|
61
|
+
severity: severity as number,
|
|
62
|
+
condition: condition as string,
|
|
63
|
+
cases: [cases],
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const subjectFullStopRule = rawRules['subject-full-stop'];
|
|
69
|
+
if (Array.isArray(subjectFullStopRule)) {
|
|
70
|
+
const [severity, condition, value] = subjectFullStopRule;
|
|
71
|
+
rules.subjectFullStop = {
|
|
72
|
+
severity: severity as number,
|
|
73
|
+
condition: condition as string,
|
|
74
|
+
value: (value as string) ?? '.',
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const bodyLeadingBlankRule = rawRules['body-leading-blank'];
|
|
79
|
+
if (Array.isArray(bodyLeadingBlankRule)) {
|
|
80
|
+
rules.bodyLeadingBlank = bodyLeadingBlankRule[1] === 'always';
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const scopeEnumRule = rawRules['scope-enum'];
|
|
84
|
+
if (Array.isArray(scopeEnumRule) && Array.isArray(scopeEnumRule[2])) {
|
|
85
|
+
rules.scopeEnum = scopeEnumRule[2] as string[];
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return rules;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Reads and parses a commitlint config file, returning the extracted rules.
|
|
93
|
+
* Returns null if the file cannot be parsed or the format is unsupported.
|
|
94
|
+
*/
|
|
95
|
+
export function parseCommitlintRules(configPath: string): CommitlintRules | null {
|
|
96
|
+
try {
|
|
97
|
+
const ext = extname(configPath).toLowerCase();
|
|
98
|
+
const base = basename(configPath);
|
|
99
|
+
|
|
100
|
+
// JSON format: .commitlintrc.json or .commitlintrc (extensionless, try JSON)
|
|
101
|
+
if (ext === '.json' || (ext === '' && base === '.commitlintrc')) {
|
|
102
|
+
try {
|
|
103
|
+
const content = readFileSync(configPath, 'utf-8');
|
|
104
|
+
const config = JSON.parse(content) as unknown;
|
|
105
|
+
return extractRulesFromConfig(config);
|
|
106
|
+
} catch {
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// JS / MJS / CJS: use a Node.js subprocess to evaluate the config
|
|
112
|
+
if (ext === '.js' || ext === '.mjs' || ext === '.cjs') {
|
|
113
|
+
// Try ESM dynamic import first (handles export default)
|
|
114
|
+
const esmScript = [
|
|
115
|
+
"import { pathToFileURL } from 'url';",
|
|
116
|
+
`const mod = await import(pathToFileURL(${JSON.stringify(configPath)}).href);`,
|
|
117
|
+
'const cfg = mod.default ?? mod;',
|
|
118
|
+
'process.stdout.write(JSON.stringify(cfg));',
|
|
119
|
+
].join('\n');
|
|
120
|
+
|
|
121
|
+
const esmResult = spawnSync(process.execPath, ['--input-type=module'], {
|
|
122
|
+
input: esmScript,
|
|
123
|
+
encoding: 'utf-8',
|
|
124
|
+
timeout: 5000,
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
if (esmResult.status === 0 && esmResult.stdout) {
|
|
128
|
+
try {
|
|
129
|
+
const config = JSON.parse(esmResult.stdout) as unknown;
|
|
130
|
+
return extractRulesFromConfig(config);
|
|
131
|
+
} catch {
|
|
132
|
+
// JSON parse error – fall through to CJS
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Fallback: try CommonJS require
|
|
137
|
+
const cjsScript = [
|
|
138
|
+
'try {',
|
|
139
|
+
` const c = require(${JSON.stringify(configPath)});`,
|
|
140
|
+
' process.stdout.write(JSON.stringify(c.default ?? c));',
|
|
141
|
+
'} catch (e) {',
|
|
142
|
+
' process.exit(1);',
|
|
143
|
+
'}',
|
|
144
|
+
].join('\n');
|
|
145
|
+
const cjsResult = spawnSync(process.execPath, ['-e', cjsScript], {
|
|
146
|
+
encoding: 'utf-8',
|
|
147
|
+
timeout: 5000,
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
if (cjsResult.status === 0 && cjsResult.stdout) {
|
|
151
|
+
try {
|
|
152
|
+
const config = JSON.parse(cjsResult.stdout) as unknown;
|
|
153
|
+
return extractRulesFromConfig(config);
|
|
154
|
+
} catch {
|
|
155
|
+
// JSON parse error
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Unsupported format (e.g. YAML, TypeScript): return null gracefully
|
|
161
|
+
return null;
|
|
162
|
+
} catch {
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export function findCommitlintConfig(cwd?: string): string | null {
|
|
168
|
+
const dir = cwd || process.cwd();
|
|
169
|
+
for (const file of COMMITLINT_CONFIG_FILES) {
|
|
170
|
+
const fullPath = join(dir, file);
|
|
171
|
+
if (existsSync(fullPath)) {
|
|
172
|
+
return fullPath;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export function isCommitlintInstalled(cwd?: string): boolean {
|
|
179
|
+
const dir = cwd || process.cwd();
|
|
180
|
+
const binPath = join(dir, 'node_modules', '.bin', 'commitlint');
|
|
181
|
+
return existsSync(binPath);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export function validateCommitMessage(
|
|
185
|
+
message: string,
|
|
186
|
+
cwd?: string
|
|
187
|
+
): CommitlintResult {
|
|
188
|
+
const dir = cwd || process.cwd();
|
|
189
|
+
const binPath = join(dir, 'node_modules', '.bin', 'commitlint');
|
|
190
|
+
|
|
191
|
+
const result = spawnSync(binPath, [], {
|
|
192
|
+
input: message,
|
|
193
|
+
encoding: 'utf-8',
|
|
194
|
+
cwd: dir,
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
if (result.status === 0) {
|
|
198
|
+
return { valid: true, errors: [], warnings: [] };
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const output = (result.stdout || '') + (result.stderr || '');
|
|
202
|
+
const lines = output.split('\n');
|
|
203
|
+
const errors: string[] = [];
|
|
204
|
+
const warnings: string[] = [];
|
|
205
|
+
|
|
206
|
+
for (const line of lines) {
|
|
207
|
+
const trimmed = line.trim();
|
|
208
|
+
if (!trimmed) continue;
|
|
209
|
+
if (trimmed.startsWith('✖') || trimmed.includes('[error]')) {
|
|
210
|
+
errors.push(trimmed);
|
|
211
|
+
} else if (trimmed.startsWith('⚠') || trimmed.includes('[warning]')) {
|
|
212
|
+
warnings.push(trimmed);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// If we couldn't parse individual errors, extract meaningful lines only
|
|
217
|
+
// (skip stack trace noise: file paths, `at ...` frames, `throw err;`, etc.)
|
|
218
|
+
if (errors.length === 0 && result.status !== 0) {
|
|
219
|
+
const rawErrors = lines
|
|
220
|
+
.map((l) => l.trim())
|
|
221
|
+
.filter((l) => {
|
|
222
|
+
if (!l.length) return false;
|
|
223
|
+
if (l.startsWith('⧗') || l.startsWith('✔')) return false;
|
|
224
|
+
// Skip Node.js stack trace lines
|
|
225
|
+
if (l.startsWith('at ')) return false;
|
|
226
|
+
if (l.startsWith('file:///') || l.match(/^[^:]+\.(?:js|ts|mjs|cjs):\d+$/)) return false;
|
|
227
|
+
if (l === '^' || l === 'throw err;') return false;
|
|
228
|
+
if (l.startsWith('Node.js v')) return false;
|
|
229
|
+
return true;
|
|
230
|
+
});
|
|
231
|
+
errors.push(...rawErrors);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return { valid: false, errors, warnings };
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
export function initCommitlintConfig(cwd?: string): void {
|
|
238
|
+
const dir = cwd || process.cwd();
|
|
239
|
+
const configPath = join(dir, 'commitlint.config.js');
|
|
240
|
+
|
|
241
|
+
if (existsSync(configPath)) {
|
|
242
|
+
throw new Error(
|
|
243
|
+
'commitlint.config.js já existe neste diretório. Remova-o antes de continuar.'
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const content = `export default {
|
|
248
|
+
extends: ['@commitlint/config-conventional'],
|
|
249
|
+
};
|
|
250
|
+
`;
|
|
251
|
+
|
|
252
|
+
writeFileSync(configPath, content, 'utf-8');
|
|
253
|
+
}
|
package/src/config/index.ts
CHANGED
|
@@ -5,7 +5,10 @@ import { join } from 'path';
|
|
|
5
5
|
|
|
6
6
|
// Removido: dotenv.config();
|
|
7
7
|
|
|
8
|
+
export type ProviderType = 'openai';
|
|
9
|
+
|
|
8
10
|
export interface Config {
|
|
11
|
+
provider: ProviderType;
|
|
9
12
|
openai: {
|
|
10
13
|
model: string;
|
|
11
14
|
maxTokens: number;
|
|
@@ -30,9 +33,13 @@ export interface Config {
|
|
|
30
33
|
ttl: number; // Time to live em minutos
|
|
31
34
|
maxSize: number; // Número máximo de entradas
|
|
32
35
|
};
|
|
36
|
+
commitlint: {
|
|
37
|
+
enabled: boolean;
|
|
38
|
+
};
|
|
33
39
|
}
|
|
34
40
|
|
|
35
41
|
const DEFAULT_CONFIG: Config = {
|
|
42
|
+
provider: 'openai',
|
|
36
43
|
openai: {
|
|
37
44
|
model: 'gpt-4o',
|
|
38
45
|
maxTokens: 150,
|
|
@@ -56,10 +63,14 @@ const DEFAULT_CONFIG: Config = {
|
|
|
56
63
|
ttl: 60, // 1 hora
|
|
57
64
|
maxSize: 100,
|
|
58
65
|
},
|
|
66
|
+
commitlint: {
|
|
67
|
+
enabled: true,
|
|
68
|
+
},
|
|
59
69
|
};
|
|
60
70
|
|
|
61
71
|
// Adicionar interface para userConfig
|
|
62
72
|
interface UserConfig {
|
|
73
|
+
provider?: ProviderType;
|
|
63
74
|
openai?: Partial<Config['openai']>;
|
|
64
75
|
language?: string;
|
|
65
76
|
commitStyle?: Config['commitStyle'];
|
|
@@ -68,6 +79,7 @@ interface UserConfig {
|
|
|
68
79
|
dryRun?: boolean;
|
|
69
80
|
smartSplit?: Partial<Config['smartSplit']>;
|
|
70
81
|
cache?: Partial<Config['cache']>;
|
|
82
|
+
commitlint?: Partial<Config['commitlint']>;
|
|
71
83
|
}
|
|
72
84
|
|
|
73
85
|
export function loadConfig(configPath?: string): Config {
|
|
@@ -142,12 +154,25 @@ function mergeConfig(defaultConfig: Config, userConfig: UserConfig): Config {
|
|
|
142
154
|
...defaultConfig.cache,
|
|
143
155
|
...userConfig.cache,
|
|
144
156
|
},
|
|
157
|
+
commitlint: {
|
|
158
|
+
...defaultConfig.commitlint,
|
|
159
|
+
...userConfig.commitlint,
|
|
160
|
+
},
|
|
145
161
|
};
|
|
146
162
|
}
|
|
147
163
|
|
|
164
|
+
const SUPPORTED_PROVIDERS: ProviderType[] = ['openai'];
|
|
165
|
+
|
|
148
166
|
export function validateConfig(config: Config): string[] {
|
|
149
167
|
const errors: string[] = [];
|
|
150
168
|
|
|
169
|
+
// Validação de provider
|
|
170
|
+
if (!SUPPORTED_PROVIDERS.includes(config.provider)) {
|
|
171
|
+
errors.push(
|
|
172
|
+
`provider deve ser um dos suportados: ${SUPPORTED_PROVIDERS.join(', ')}`
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
|
|
151
176
|
// Validação básica
|
|
152
177
|
if (!config.openai.apiKey) {
|
|
153
178
|
errors.push('OPENAI_API_KEY não encontrada nas variáveis de ambiente');
|
package/src/core/cache.ts
CHANGED
|
@@ -1,210 +1,14 @@
|
|
|
1
|
-
import type { Config } from '../config/index';
|
|
2
|
-
import type { FileGroup } from './smart-split';
|
|
3
|
-
import crypto from 'crypto';
|
|
4
|
-
|
|
5
|
-
export interface CacheEntry {
|
|
6
|
-
groups: FileGroup[];
|
|
7
|
-
timestamp: number;
|
|
8
|
-
hash: string;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export interface CacheResult {
|
|
12
|
-
hit: boolean;
|
|
13
|
-
groups?: FileGroup[];
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
class AnalysisCache {
|
|
17
|
-
private cache: Map<string, CacheEntry> = new Map();
|
|
18
|
-
private config: Config;
|
|
19
|
-
|
|
20
|
-
constructor(config: Config) {
|
|
21
|
-
this.config = config;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Gera hash do contexto para identificar análises similares
|
|
26
|
-
*/
|
|
27
|
-
private generateHash(files: string[], overallDiff: string): string {
|
|
28
|
-
const context = {
|
|
29
|
-
files: files.sort(), // Ordenar para consistência
|
|
30
|
-
diff: overallDiff.substring(0, 1000), // Limitar tamanho do diff
|
|
31
|
-
model: this.config.openai.model,
|
|
32
|
-
temperature: this.config.openai.temperature,
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
return crypto
|
|
36
|
-
.createHash('md5')
|
|
37
|
-
.update(JSON.stringify(context))
|
|
38
|
-
.digest('hex');
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Verifica se há cache válido para o contexto
|
|
43
|
-
*/
|
|
44
|
-
get(files: string[], overallDiff: string): CacheResult {
|
|
45
|
-
if (!this.config.cache.enabled) {
|
|
46
|
-
return { hit: false };
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const hash = this.generateHash(files, overallDiff);
|
|
50
|
-
const entry = this.cache.get(hash);
|
|
51
|
-
|
|
52
|
-
if (!entry) {
|
|
53
|
-
return { hit: false };
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// Verificar se o cache expirou
|
|
57
|
-
const now = Date.now();
|
|
58
|
-
const ttlMs = this.config.cache.ttl * 60 * 1000; // Converter minutos para ms
|
|
59
|
-
|
|
60
|
-
if (now - entry.timestamp > ttlMs) {
|
|
61
|
-
this.cache.delete(hash);
|
|
62
|
-
return { hit: false };
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
return { hit: true, groups: entry.groups };
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Armazena resultado no cache
|
|
70
|
-
*/
|
|
71
|
-
set(files: string[], overallDiff: string, groups: FileGroup[]): void {
|
|
72
|
-
if (!this.config.cache.enabled) {
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// Limpar cache se exceder tamanho máximo
|
|
77
|
-
if (this.cache.size >= this.config.cache.maxSize) {
|
|
78
|
-
this.cleanup();
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// Se ainda exceder após cleanup, não adicionar
|
|
82
|
-
if (this.cache.size >= this.config.cache.maxSize) {
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
const hash = this.generateHash(files, overallDiff);
|
|
87
|
-
const entry: CacheEntry = {
|
|
88
|
-
groups,
|
|
89
|
-
timestamp: Date.now(),
|
|
90
|
-
hash,
|
|
91
|
-
};
|
|
92
|
-
|
|
93
|
-
this.cache.set(hash, entry);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Limpa cache expirado e reduz tamanho se necessário
|
|
98
|
-
*/
|
|
99
|
-
private cleanup(): void {
|
|
100
|
-
const now = Date.now();
|
|
101
|
-
const ttlMs = this.config.cache.ttl * 60 * 1000;
|
|
102
|
-
|
|
103
|
-
// Remover entradas expiradas
|
|
104
|
-
for (const [hash, entry] of this.cache.entries()) {
|
|
105
|
-
if (now - entry.timestamp > ttlMs) {
|
|
106
|
-
this.cache.delete(hash);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// Se ainda exceder tamanho máximo, remover entradas mais antigas
|
|
111
|
-
if (this.cache.size >= this.config.cache.maxSize) {
|
|
112
|
-
const entries = Array.from(this.cache.entries()).sort(
|
|
113
|
-
(a, b) => a[1].timestamp - b[1].timestamp
|
|
114
|
-
);
|
|
115
|
-
|
|
116
|
-
// Remover 50% das entradas mais antigas para garantir espaço
|
|
117
|
-
const toRemove = entries.slice(
|
|
118
|
-
0,
|
|
119
|
-
Math.ceil(this.config.cache.maxSize * 0.5)
|
|
120
|
-
);
|
|
121
|
-
for (const [hash] of toRemove) {
|
|
122
|
-
this.cache.delete(hash);
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Limpa todo o cache
|
|
129
|
-
*/
|
|
130
|
-
clear(): void {
|
|
131
|
-
this.cache.clear();
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Retorna estatísticas do cache
|
|
136
|
-
*/
|
|
137
|
-
getStats(): { size: number; maxSize: number; enabled: boolean } {
|
|
138
|
-
return {
|
|
139
|
-
size: this.cache.size,
|
|
140
|
-
maxSize: this.config.cache.maxSize,
|
|
141
|
-
enabled: this.config.cache.enabled,
|
|
142
|
-
};
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// Instância global do cache
|
|
147
|
-
let globalCache: AnalysisCache | null = null;
|
|
148
|
-
|
|
149
1
|
/**
|
|
150
|
-
*
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* Verifica se há cache válido para o contexto
|
|
165
|
-
*/
|
|
166
|
-
export function getCachedAnalysis(
|
|
167
|
-
files: string[],
|
|
168
|
-
overallDiff: string
|
|
169
|
-
): CacheResult {
|
|
170
|
-
const cache = getCache();
|
|
171
|
-
return cache ? cache.get(files, overallDiff) : { hit: false };
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
/**
|
|
175
|
-
* Armazena resultado no cache
|
|
176
|
-
*/
|
|
177
|
-
export function setCachedAnalysis(
|
|
178
|
-
files: string[],
|
|
179
|
-
overallDiff: string,
|
|
180
|
-
groups: FileGroup[]
|
|
181
|
-
): void {
|
|
182
|
-
const cache = getCache();
|
|
183
|
-
if (cache) {
|
|
184
|
-
cache.set(files, overallDiff, groups);
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
/**
|
|
189
|
-
* Retorna estatísticas do cache
|
|
190
|
-
*/
|
|
191
|
-
export function getCacheStats(): {
|
|
192
|
-
size: number;
|
|
193
|
-
maxSize: number;
|
|
194
|
-
enabled: boolean;
|
|
195
|
-
} | null {
|
|
196
|
-
const cache = getCache();
|
|
197
|
-
return cache ? cache.getStats() : null;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
/**
|
|
201
|
-
* Limpa o cache global
|
|
202
|
-
*/
|
|
203
|
-
export function clearCache(): void {
|
|
204
|
-
const cache = getCache();
|
|
205
|
-
if (cache) {
|
|
206
|
-
cache.clear();
|
|
207
|
-
}
|
|
208
|
-
// Resetar a instância global
|
|
209
|
-
globalCache = null;
|
|
210
|
-
}
|
|
2
|
+
* Re-exports from src/cache/analysis.ts for backwards compatibility.
|
|
3
|
+
* This file is kept as a shim during the v3 migration.
|
|
4
|
+
* Consumers should migrate to importing directly from ../cache/analysis.
|
|
5
|
+
*/
|
|
6
|
+
export type { CacheEntry, CacheResult } from '../cache/analysis';
|
|
7
|
+
export {
|
|
8
|
+
initializeCache,
|
|
9
|
+
getCache,
|
|
10
|
+
getCachedAnalysis,
|
|
11
|
+
setCachedAnalysis,
|
|
12
|
+
getCacheStats,
|
|
13
|
+
clearCache,
|
|
14
|
+
} from '../cache/analysis';
|