korvet-intellijs 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/README.md ADDED
@@ -0,0 +1,3 @@
1
+ # Korvet IntelliJS
2
+
3
+ Self-debugging and self-optimizing JavaScript execution framework.
package/TODO.md ADDED
@@ -0,0 +1,21 @@
1
+ # TODO — Korvet IntelliJS
2
+
3
+ ## Step 1 — Repo alignment (done)
4
+ - Confirm repo is ESM (`type: module`) and fixes are applied via `issue.fix` + `src/fixer.js` called from `bin/kijs.js`.
5
+ - Confirm `src/engine.js` currently runs offline AST rules only.
6
+
7
+ ## Step 2 — Port Step 9 AST self-heal into ESM pipeline (in progress)
8
+ - Update `src/engine.js` to generate additional **fixable issues** from AST:
9
+ - `var` -> `const` on the declaration line
10
+ - `==` -> `===` and `!=` -> `!==` on the binary expression line
11
+ - Remove `console.log/warn/info/debug` lines via `fix: ''`
12
+ - (Optional) bare `.then()` / `.catch()` heuristic (skip if risky)
13
+ - Ensure `fix` strings are full-line replacements (or empty string for removal) compatible with existing `src/fixer.js`.
14
+
15
+
16
+ ## Step 3 — Verify fixes
17
+ - Run:
18
+ - `npm test`
19
+ - `node bin/kijs.js debug --fix ./test-fixtures/buggy-file.js`
20
+ - `node bin/kijs.js debug ./test-fixtures/buggy-file.js`
21
+
package/bin/index.js ADDED
@@ -0,0 +1,379 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { Command } from 'commander';
4
+ import fs from 'fs-extra';
5
+ import path from 'path';
6
+ import { glob } from 'glob';
7
+ import chalk from 'chalk';
8
+
9
+ import { loadConfig } from '../src/config.js';
10
+ import { analyzeFile } from '../src/analyzer.js';
11
+ import { debugCode } from '../src/debugger.js';
12
+ import { optimizeCode, applyAutoFixes } from '../src/optimizer.js';
13
+ import { analyzeMemory } from '../src/memory.js';
14
+ import * as reporter from '../src/reporter.js';
15
+
16
+ const program = new Command();
17
+
18
+ program
19
+ .name('kijs')
20
+ .description('Korvet IntelliJS: AI-powered and AST-driven code intelligence CLI')
21
+ .version('1.0.0');
22
+
23
+ // Shared config option
24
+ program.option('-c, --config <path>', 'custom path to korvet.config.js configuration file');
25
+
26
+ /**
27
+ * Common helper to gather files to process based on configurations and pattern overrides.
28
+ */
29
+ async function resolveFiles(config, overridePath = null) {
30
+ if (overridePath) {
31
+ const resolved = path.resolve(process.cwd(), overridePath);
32
+ if (await fs.pathExists(resolved)) {
33
+ return [path.normalize(resolved)];
34
+ }
35
+
36
+ // If it is a dynamic path pattern (glob) passed as an override
37
+ try {
38
+ const matched = await glob(overridePath, {
39
+ absolute: true,
40
+ nodir: true
41
+ });
42
+ return matched.map(f => path.normalize(f));
43
+ } catch (err) {
44
+ return [];
45
+ }
46
+ }
47
+
48
+ const include = config.include;
49
+ const exclude = config.exclude;
50
+
51
+ const filesSet = new Set();
52
+ for (const pattern of include) {
53
+ try {
54
+ const matched = await glob(pattern, {
55
+ ignore: exclude,
56
+ absolute: true,
57
+ nodir: true
58
+ });
59
+ matched.forEach(f => filesSet.add(path.normalize(f)));
60
+ } catch (err) {
61
+ console.error(chalk.red(`[Error] Failed resolving file pattern "${pattern}": ${err.message}`));
62
+ }
63
+ }
64
+ return Array.from(filesSet);
65
+ }
66
+
67
+ /**
68
+ * CLI COMMAND: init
69
+ * Scaffolds a default configuration file if not already present.
70
+ */
71
+ program
72
+ .command('init')
73
+ .description('Initialize default korvet.config.js template in the current directory')
74
+ .action(async () => {
75
+ reporter.printHeader();
76
+ const targetPath = path.resolve(process.cwd(), 'korvet.config.js');
77
+
78
+ if (await fs.pathExists(targetPath)) {
79
+ console.log(chalk.yellow(` ⚠ Configuration file already exists at ${targetPath}`));
80
+ return;
81
+ }
82
+
83
+ const template = `export default {
84
+ // Gemini API key. If empty, falls back to GEMINI_API_KEY environment variable.
85
+ apiKey: "",
86
+
87
+ // File paths to include in analysis
88
+ include: ["src/**/*.js", "*.js"],
89
+
90
+ // File paths to exclude from analysis
91
+ exclude: [
92
+ "node_modules/**",
93
+ "dist/**",
94
+ "build/**",
95
+ "test-fixtures/**",
96
+ "coverage/**"
97
+ ],
98
+
99
+ // Control module execution
100
+ modules: {
101
+ analyzer: true, // Gemini AI Engine
102
+ debugger: true, // Predictive Control-Flow Debugger
103
+ optimizer: true, // AST Performance Optimizer
104
+ memory: true // AST Memory Leak Check
105
+ },
106
+
107
+ // Minimum reporting severity: "info", "warning", "error"
108
+ severity: "info"
109
+ };
110
+ `;
111
+
112
+ try {
113
+ await fs.writeFile(targetPath, template, 'utf-8');
114
+ console.log(chalk.green.bold(` ✔ Successfully created korvet.config.js default template!`));
115
+ console.log(chalk.gray(` Get started by setting your Gemini API key inside it or as an environment variable.`));
116
+ } catch (err) {
117
+ console.error(chalk.red(` ✘ Failed to create configuration template: ${err.message}`));
118
+ }
119
+ });
120
+
121
+ /**
122
+ * CLI COMMAND: analyze
123
+ * Runs AI-powered Gemini analysis.
124
+ */
125
+ program
126
+ .command('analyze [path]')
127
+ .description('Execute AI-powered semantic analysis using the Gemini Engine')
128
+ .action(async (overridePath) => {
129
+ reporter.printHeader();
130
+ const options = program.opts();
131
+ const config = await loadConfig(options.config);
132
+
133
+ if (!config.apiKey) {
134
+ console.log(chalk.red.bold(` ✘ API Key Missing! Cannot run AI-powered analysis.`));
135
+ console.log(chalk.gray(` Define your "apiKey" in korvet.config.js or set a GEMINI_API_KEY env variable.`));
136
+ process.exit(1);
137
+ }
138
+
139
+ const files = await resolveFiles(config, overridePath);
140
+ if (files.length === 0) {
141
+ console.log(chalk.yellow(` ⚠ No matching files found to analyze.`));
142
+ return;
143
+ }
144
+
145
+ console.log(chalk.cyan(` Initiating Gemini Engine for ${files.length} file(s)...`));
146
+
147
+ let totalIssues = [];
148
+ for (const file of files) {
149
+ const relative = path.relative(process.cwd(), file);
150
+ console.log(chalk.gray(` Scanning ${relative}...`));
151
+
152
+ try {
153
+ const content = await fs.readFile(file, 'utf-8');
154
+ const fileIssues = await analyzeFile(relative, content, config.apiKey);
155
+
156
+ // Filter by minimum configured severity
157
+ const filtered = filterIssues(fileIssues, config.severity);
158
+ totalIssues.push(...filtered);
159
+
160
+ reporter.reportFileIssues(relative, content, filtered);
161
+ } catch (err) {
162
+ console.error(chalk.red(` ✘ Failed to analyze file ${relative}: ${err.message}`));
163
+ }
164
+ }
165
+
166
+ reporter.printSummaryDashboard(files.length, totalIssues);
167
+ });
168
+
169
+ /**
170
+ * CLI COMMAND: debug
171
+ * Runs AST-based control flow predictive debugger.
172
+ */
173
+ program
174
+ .command('debug [path]')
175
+ .description('Perform predictive control-flow and logical bug diagnostics via AST')
176
+ .action(async (overridePath) => {
177
+ reporter.printHeader();
178
+ const options = program.opts();
179
+ const config = await loadConfig(options.config);
180
+
181
+ const files = await resolveFiles(config, overridePath);
182
+ if (files.length === 0) {
183
+ console.log(chalk.yellow(` ⚠ No matching files found to check.`));
184
+ return;
185
+ }
186
+
187
+ let totalIssues = [];
188
+ for (const file of files) {
189
+ const relative = path.relative(process.cwd(), file);
190
+ try {
191
+ const content = await fs.readFile(file, 'utf-8');
192
+ const fileIssues = debugCode(relative, content);
193
+ const filtered = filterIssues(fileIssues, config.severity);
194
+ totalIssues.push(...filtered);
195
+ reporter.reportFileIssues(relative, content, filtered);
196
+ } catch (err) {
197
+ console.error(chalk.red(` ✘ Failed to debug file ${relative}: ${err.message}`));
198
+ }
199
+ }
200
+
201
+ reporter.printSummaryDashboard(files.length, totalIssues);
202
+ });
203
+
204
+ /**
205
+ * CLI COMMAND: optimize
206
+ * Runs performance analyzer with optional --fix flag.
207
+ */
208
+ program
209
+ .command('optimize [path]')
210
+ .description('Identify and fix performance bottlenecks, redundant declarations, and unused code')
211
+ .option('--fix', 'automatically apply clean optimization and variables removals')
212
+ .action(async (overridePath, cmdOpts) => {
213
+ reporter.printHeader();
214
+ const options = program.opts();
215
+ const config = await loadConfig(options.config);
216
+
217
+ const files = await resolveFiles(config, overridePath);
218
+ if (files.length === 0) {
219
+ console.log(chalk.yellow(` ⚠ No matching JS files matched optimization scopes.`));
220
+ return;
221
+ }
222
+
223
+ let totalIssues = [];
224
+ for (const file of files) {
225
+ const relative = path.relative(process.cwd(), file);
226
+ try {
227
+ let content = await fs.readFile(file, 'utf-8');
228
+ const fileIssues = optimizeCode(relative, content);
229
+ const filtered = filterIssues(fileIssues, config.severity);
230
+
231
+ totalIssues.push(...filtered);
232
+ reporter.reportFileIssues(relative, content, filtered);
233
+
234
+ if (cmdOpts.fix && filtered.length > 0) {
235
+ console.log(chalk.green(` Applying auto-fixes for ${relative}...`));
236
+ await applyAutoFixes(file, content, filtered);
237
+ }
238
+ } catch (err) {
239
+ console.error(chalk.red(` ✘ Optimization run failed on ${relative}: ${err.message}`));
240
+ }
241
+ }
242
+
243
+ reporter.printSummaryDashboard(files.length, totalIssues);
244
+ });
245
+
246
+ /**
247
+ * CLI COMMAND: leak-check
248
+ * Runs memory analyzer.
249
+ */
250
+ program
251
+ .command('leak-check [path]')
252
+ .description('Scan source code to identify memory leaks, uncleaned intervals, and dynamic globals')
253
+ .action(async (overridePath) => {
254
+ reporter.printHeader();
255
+ const options = program.opts();
256
+ const config = await loadConfig(options.config);
257
+
258
+ const files = await resolveFiles(config, overridePath);
259
+ if (files.length === 0) {
260
+ console.log(chalk.yellow(` ⚠ No matching files found to evaluate leaks.`));
261
+ return;
262
+ }
263
+
264
+ let totalIssues = [];
265
+ for (const file of files) {
266
+ const relative = path.relative(process.cwd(), file);
267
+ try {
268
+ const content = await fs.readFile(file, 'utf-8');
269
+ const fileIssues = analyzeMemory(relative, content);
270
+ const filtered = filterIssues(fileIssues, config.severity);
271
+ totalIssues.push(...filtered);
272
+ reporter.reportFileIssues(relative, content, filtered);
273
+ } catch (err) {
274
+ console.error(chalk.red(` ✘ Leak-check execution failed on ${relative}: ${err.message}`));
275
+ }
276
+ }
277
+
278
+ reporter.printSummaryDashboard(files.length, totalIssues);
279
+ });
280
+
281
+ /**
282
+ * CLI COMMAND: scan
283
+ * Runs ALL modules sequentially.
284
+ */
285
+ program
286
+ .command('scan [path]')
287
+ .description('Run full analysis suite (AI + AST debugging, optimizations, and leak audits)')
288
+ .option('--fix', 'auto-fix performance issues, unused codes, and clearable patterns')
289
+ .action(async (overridePath, cmdOpts) => {
290
+ reporter.printHeader();
291
+ const options = program.opts();
292
+ const config = await loadConfig(options.config);
293
+
294
+ const files = await resolveFiles(config, overridePath);
295
+ if (files.length === 0) {
296
+ console.log(chalk.yellow(` ⚠ No matching files found to scan.`));
297
+ return;
298
+ }
299
+
300
+ // Friendly API Key check to prevent analyzer failures while letting other modules proceed
301
+ let runAnalyzer = config.modules.analyzer;
302
+ if (runAnalyzer && !config.apiKey) {
303
+ console.log(chalk.yellow(` ⚠ [Config Warning] Gemini API key not found. Skipping AI-powered semantic analysis.\n` +
304
+ ` Set GEMINI_API_KEY inside korvet.config.js or environment to enable AI rules.`));
305
+ runAnalyzer = false;
306
+ }
307
+
308
+ let totalIssues = [];
309
+ for (const file of files) {
310
+ const relative = path.relative(process.cwd(), file);
311
+ console.log(chalk.cyan(`\n Scanning ${relative}...`));
312
+
313
+ try {
314
+ let content = await fs.readFile(file, 'utf-8');
315
+ let fileIssues = [];
316
+
317
+ // 1. Gemini Engine
318
+ if (runAnalyzer) {
319
+ try {
320
+ console.log(chalk.gray(` - Querying Gemini AI Analyzer...`));
321
+ const aiIssues = await analyzeFile(relative, content, config.apiKey);
322
+ fileIssues.push(...aiIssues);
323
+ } catch (aiErr) {
324
+ console.warn(chalk.yellow(` ⚠ AI Analyzer failed on this file: ${aiErr.message}`));
325
+ }
326
+ }
327
+
328
+ // 2. Control Flow Debugger
329
+ if (config.modules.debugger) {
330
+ const debugIssues = debugCode(relative, content);
331
+ fileIssues.push(...debugIssues);
332
+ }
333
+
334
+ // 3. Performance Optimizer
335
+ if (config.modules.optimizer) {
336
+ const optIssues = optimizeCode(relative, content);
337
+ fileIssues.push(...optIssues);
338
+ }
339
+
340
+ // 4. Memory Leak Auditor
341
+ if (config.modules.memory) {
342
+ const memIssues = analyzeMemory(relative, content);
343
+ fileIssues.push(...memIssues);
344
+ }
345
+
346
+ // Filter and display
347
+ const filtered = filterIssues(fileIssues, config.severity);
348
+ totalIssues.push(...filtered);
349
+ reporter.reportFileIssues(relative, content, filtered);
350
+
351
+ // Apply auto-fixes if requested and applicable
352
+ if (cmdOpts.fix && filtered.length > 0) {
353
+ console.log(chalk.green(` Applying auto-fixes for ${relative}...`));
354
+ const fixedContent = await applyAutoFixes(file, content, filtered);
355
+ content = fixedContent; // Refresh contents
356
+ }
357
+ } catch (err) {
358
+ console.error(chalk.red(` ✘ Processing failed on ${relative}: ${err.message}`));
359
+ }
360
+ }
361
+
362
+ reporter.printSummaryDashboard(files.length, totalIssues);
363
+ });
364
+
365
+ /**
366
+ * Filters issues array based on the configured minimum severity.
367
+ */
368
+ function filterIssues(issues, minSeverity = 'info') {
369
+ const levels = { info: 1, warning: 2, error: 3 };
370
+ const minVal = levels[minSeverity.toLowerCase()] || 1;
371
+
372
+ return issues.filter(issue => {
373
+ const issueVal = levels[(issue.severity || 'info').toLowerCase()] || 1;
374
+ return issueVal >= minVal;
375
+ });
376
+ }
377
+
378
+ // Start processing CLI instructions
379
+ program.parse(process.argv);