devsplain 2.0.1 → 2.1.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/bin/cli.js +154 -16
- package/lib/config.js +11 -11
- package/lib/llm.js +8 -32
- package/package.json +1 -1
package/bin/cli.js
CHANGED
|
@@ -10,7 +10,7 @@ const { execSync } = require('child_process');
|
|
|
10
10
|
let rl;
|
|
11
11
|
let askQuestion;
|
|
12
12
|
|
|
13
|
-
/** Checks if the Git repository
|
|
13
|
+
/** Checks if the Git repository has uncommitted changes [ds] */
|
|
14
14
|
function isGitDirty() {
|
|
15
15
|
try {
|
|
16
16
|
const gitDir = execSync('git rev-parse --is-inside-work-tree', { stdio: ['ignore', 'pipe', 'ignore'], encoding: 'utf8' }).trim();
|
|
@@ -23,10 +23,13 @@ function isGitDirty() {
|
|
|
23
23
|
return false;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
/** Checks if a line of code is inside a string [ds] */
|
|
26
|
+
/** Checks if a line of code is inside a string literal [ds] */
|
|
27
27
|
function isLineInsideString(lines, targetLineIndex, ext = '') {
|
|
28
28
|
const isPython = ext.toLowerCase() === '.py';
|
|
29
29
|
const isHTML = ['.html', '.vue', '.svelte'].includes(ext.toLowerCase());
|
|
30
|
+
const isRustOrSwift = ['.rs', '.swift'].includes(ext.toLowerCase());
|
|
31
|
+
const isCpp = ['.cpp', '.cc', '.cxx', '.c', '.h', '.hpp'].includes(ext.toLowerCase());
|
|
32
|
+
const isJS = ['.js', '.jsx', '.ts', '.tsx'].includes(ext.toLowerCase());
|
|
30
33
|
let inBacktick = false;
|
|
31
34
|
let inTripleDouble = false;
|
|
32
35
|
let inTripleSingle = false;
|
|
@@ -34,19 +37,55 @@ function isLineInsideString(lines, targetLineIndex, ext = '') {
|
|
|
34
37
|
let inDouble = false;
|
|
35
38
|
let inBlockJS = false;
|
|
36
39
|
let inBlockHTML = false;
|
|
40
|
+
let blockDepthJS = 0;
|
|
41
|
+
let inCppRawString = false;
|
|
42
|
+
let cppRawDelimiter = '';
|
|
43
|
+
let inRegex = false;
|
|
37
44
|
for (let i = 0; i < targetLineIndex; i++) {
|
|
38
45
|
const line = lines[i];
|
|
39
46
|
let j = 0;
|
|
40
47
|
while (j < line.length) {
|
|
41
48
|
if (inBlockJS) {
|
|
49
|
+
if (line.slice(j, j + 2) === '/*') {
|
|
50
|
+
if (isRustOrSwift) blockDepthJS++;
|
|
51
|
+
j += 2;
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
42
54
|
if (line.slice(j, j + 2) === '*/') {
|
|
43
|
-
|
|
55
|
+
if (isRustOrSwift && blockDepthJS > 1) {
|
|
56
|
+
blockDepthJS--;
|
|
57
|
+
} else {
|
|
58
|
+
inBlockJS = false;
|
|
59
|
+
blockDepthJS = 0;
|
|
60
|
+
}
|
|
44
61
|
j += 2;
|
|
45
62
|
continue;
|
|
46
63
|
}
|
|
47
64
|
j++;
|
|
48
65
|
continue;
|
|
49
66
|
}
|
|
67
|
+
if (inCppRawString) {
|
|
68
|
+
if (line.slice(j, j + 2 + cppRawDelimiter.length) === ')' + cppRawDelimiter + '"') {
|
|
69
|
+
inCppRawString = false;
|
|
70
|
+
j += 2 + cppRawDelimiter.length;
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
j++;
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
if (inRegex) {
|
|
77
|
+
let escaped = false;
|
|
78
|
+
let k = j - 1;
|
|
79
|
+
while (k >= 0 && line[k] === '\\') {
|
|
80
|
+
escaped = !escaped;
|
|
81
|
+
k--;
|
|
82
|
+
}
|
|
83
|
+
if (line[j] === '/' && !escaped) {
|
|
84
|
+
inRegex = false;
|
|
85
|
+
}
|
|
86
|
+
j++;
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
50
89
|
if (inBlockHTML) {
|
|
51
90
|
if (line.slice(j, j + 3) === '-->') {
|
|
52
91
|
inBlockHTML = false;
|
|
@@ -65,7 +104,6 @@ function isLineInsideString(lines, targetLineIndex, ext = '') {
|
|
|
65
104
|
} else if (isHTML) {
|
|
66
105
|
if (line.slice(j, j + 4) === '<!--') {
|
|
67
106
|
inBlockHTML = true;
|
|
68
|
-
// Check if current character is a backtick [ds]
|
|
69
107
|
j += 4;
|
|
70
108
|
continue;
|
|
71
109
|
}
|
|
@@ -83,6 +121,7 @@ function isLineInsideString(lines, targetLineIndex, ext = '') {
|
|
|
83
121
|
}
|
|
84
122
|
if (line.slice(j, j + 2) === '/*') {
|
|
85
123
|
inBlockJS = true;
|
|
124
|
+
blockDepthJS = 1;
|
|
86
125
|
j += 2;
|
|
87
126
|
continue;
|
|
88
127
|
}
|
|
@@ -90,6 +129,38 @@ function isLineInsideString(lines, targetLineIndex, ext = '') {
|
|
|
90
129
|
if (isShellOrRuby && line[j] === '#') {
|
|
91
130
|
break; // Ignore rest of line
|
|
92
131
|
}
|
|
132
|
+
if (isCpp && line[j] === 'R' && line[j+1] === '"') {
|
|
133
|
+
const match = line.slice(j).match(/^R"([^()\\\s]{0,16})\(/);
|
|
134
|
+
if (match) {
|
|
135
|
+
cppRawDelimiter = match[1];
|
|
136
|
+
inCppRawString = true;
|
|
137
|
+
j += match[0].length;
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
if (isJS && line[j] === '/') {
|
|
142
|
+
let k = j - 1;
|
|
143
|
+
while (k >= 0 && /\s/.test(line[k])) k--;
|
|
144
|
+
let isRegex = false;
|
|
145
|
+
if (k < 0) {
|
|
146
|
+
isRegex = true;
|
|
147
|
+
} else {
|
|
148
|
+
const prevChar = line[k];
|
|
149
|
+
if (/[=({\[:,;!+*&|?<>-]/.test(prevChar)) {
|
|
150
|
+
isRegex = true;
|
|
151
|
+
} else {
|
|
152
|
+
const prefix = line.slice(0, k + 1);
|
|
153
|
+
if (/(?:return|typeof|yield|await|throw)\s*$/.test(prefix)) {
|
|
154
|
+
isRegex = true;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
if (isRegex) {
|
|
159
|
+
inRegex = true;
|
|
160
|
+
j++;
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
93
164
|
}
|
|
94
165
|
}
|
|
95
166
|
if (isPython) {
|
|
@@ -125,7 +196,6 @@ function isLineInsideString(lines, targetLineIndex, ext = '') {
|
|
|
125
196
|
}
|
|
126
197
|
}
|
|
127
198
|
if (!inBacktick) {
|
|
128
|
-
// Check if current character is a double quote [ds]
|
|
129
199
|
if (line[j] === '"' && !inSingle) {
|
|
130
200
|
let escaped = false;
|
|
131
201
|
let k = j - 1;
|
|
@@ -157,13 +227,16 @@ function isLineInsideString(lines, targetLineIndex, ext = '') {
|
|
|
157
227
|
inDouble = false;
|
|
158
228
|
}
|
|
159
229
|
}
|
|
160
|
-
return inBacktick || inTripleDouble || inTripleSingle || inSingle || inDouble;
|
|
230
|
+
return inBacktick || inTripleDouble || inTripleSingle || inSingle || inDouble || inCppRawString || inRegex;
|
|
161
231
|
}
|
|
162
232
|
|
|
163
|
-
/** Analyzes comments in a given
|
|
233
|
+
/** Analyzes the comments in a given set of code lines [ds] */
|
|
164
234
|
function analyzeComments(lines, ext = '') {
|
|
165
235
|
const isPython = ext.toLowerCase() === '.py';
|
|
166
236
|
const isHTML = ['.html', '.vue', '.svelte'].includes(ext.toLowerCase());
|
|
237
|
+
const isRustOrSwift = ['.rs', '.swift'].includes(ext.toLowerCase());
|
|
238
|
+
const isCpp = ['.cpp', '.cc', '.cxx', '.c', '.h', '.hpp'].includes(ext.toLowerCase());
|
|
239
|
+
const isJS = ['.js', '.jsx', '.ts', '.tsx'].includes(ext.toLowerCase());
|
|
167
240
|
const analysis = [];
|
|
168
241
|
let inBacktick = false;
|
|
169
242
|
let inTripleDouble = false;
|
|
@@ -172,6 +245,10 @@ function analyzeComments(lines, ext = '') {
|
|
|
172
245
|
let inDouble = false;
|
|
173
246
|
let inBlockJS = false;
|
|
174
247
|
let inBlockHTML = false;
|
|
248
|
+
let blockDepthJS = 0;
|
|
249
|
+
let inCppRawString = false;
|
|
250
|
+
let cppRawDelimiter = '';
|
|
251
|
+
let inRegex = false;
|
|
175
252
|
for (let i = 0; i < lines.length; i++) {
|
|
176
253
|
const line = lines[i];
|
|
177
254
|
let commentStartIndex = -1;
|
|
@@ -179,14 +256,46 @@ function analyzeComments(lines, ext = '') {
|
|
|
179
256
|
let j = 0;
|
|
180
257
|
while (j < line.length) {
|
|
181
258
|
if (inBlockJS) {
|
|
259
|
+
if (line.slice(j, j + 2) === '/*') {
|
|
260
|
+
if (isRustOrSwift) blockDepthJS++;
|
|
261
|
+
j += 2;
|
|
262
|
+
continue;
|
|
263
|
+
}
|
|
182
264
|
if (line.slice(j, j + 2) === '*/') {
|
|
183
|
-
|
|
265
|
+
if (isRustOrSwift && blockDepthJS > 1) {
|
|
266
|
+
blockDepthJS--;
|
|
267
|
+
} else {
|
|
268
|
+
inBlockJS = false;
|
|
269
|
+
blockDepthJS = 0;
|
|
270
|
+
}
|
|
184
271
|
j += 2;
|
|
185
272
|
continue;
|
|
186
273
|
}
|
|
187
274
|
j++;
|
|
188
275
|
continue;
|
|
189
276
|
}
|
|
277
|
+
if (inCppRawString) {
|
|
278
|
+
if (line.slice(j, j + 2 + cppRawDelimiter.length) === ')' + cppRawDelimiter + '"') {
|
|
279
|
+
inCppRawString = false;
|
|
280
|
+
j += 2 + cppRawDelimiter.length;
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
j++;
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
286
|
+
if (inRegex) {
|
|
287
|
+
let escaped = false;
|
|
288
|
+
let k = j - 1;
|
|
289
|
+
while (k >= 0 && line[k] === '\\') {
|
|
290
|
+
escaped = !escaped;
|
|
291
|
+
k--;
|
|
292
|
+
}
|
|
293
|
+
if (line[j] === '/' && !escaped) {
|
|
294
|
+
inRegex = false;
|
|
295
|
+
}
|
|
296
|
+
j++;
|
|
297
|
+
continue;
|
|
298
|
+
}
|
|
190
299
|
if (inBlockHTML) {
|
|
191
300
|
if (line.slice(j, j + 3) === '-->') {
|
|
192
301
|
inBlockHTML = false;
|
|
@@ -227,6 +336,7 @@ function analyzeComments(lines, ext = '') {
|
|
|
227
336
|
if (line.slice(j, j + 2) === '/*') {
|
|
228
337
|
commentStartIndex = j;
|
|
229
338
|
inBlockJS = true;
|
|
339
|
+
blockDepthJS = 1;
|
|
230
340
|
j += 2;
|
|
231
341
|
continue;
|
|
232
342
|
}
|
|
@@ -235,6 +345,38 @@ function analyzeComments(lines, ext = '') {
|
|
|
235
345
|
commentStartIndex = j;
|
|
236
346
|
break;
|
|
237
347
|
}
|
|
348
|
+
if (isCpp && line[j] === 'R' && line[j+1] === '"') {
|
|
349
|
+
const match = line.slice(j).match(/^R"([^()\\\s]{0,16})\(/);
|
|
350
|
+
if (match) {
|
|
351
|
+
cppRawDelimiter = match[1];
|
|
352
|
+
inCppRawString = true;
|
|
353
|
+
j += match[0].length;
|
|
354
|
+
continue;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
if (isJS && line[j] === '/') {
|
|
358
|
+
let k = j - 1;
|
|
359
|
+
while (k >= 0 && /\s/.test(line[k])) k--;
|
|
360
|
+
let isRegex = false;
|
|
361
|
+
if (k < 0) {
|
|
362
|
+
isRegex = true;
|
|
363
|
+
} else {
|
|
364
|
+
const prevChar = line[k];
|
|
365
|
+
if (/[=({\[:,;!+*&|?<>-]/.test(prevChar)) {
|
|
366
|
+
isRegex = true;
|
|
367
|
+
} else {
|
|
368
|
+
const prefix = line.slice(0, k + 1);
|
|
369
|
+
if (/(?:return|typeof|yield|await|throw)\s*$/.test(prefix)) {
|
|
370
|
+
isRegex = true;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
if (isRegex) {
|
|
375
|
+
inRegex = true;
|
|
376
|
+
j++;
|
|
377
|
+
continue;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
238
380
|
}
|
|
239
381
|
}
|
|
240
382
|
if (isPython) {
|
|
@@ -281,7 +423,6 @@ function analyzeComments(lines, ext = '') {
|
|
|
281
423
|
inDouble = !inDouble;
|
|
282
424
|
}
|
|
283
425
|
}
|
|
284
|
-
// Check if current character is a single quote [ds]
|
|
285
426
|
else if (line[j] === "'" && !inDouble) {
|
|
286
427
|
let escaped = false;
|
|
287
428
|
let k = j - 1;
|
|
@@ -323,7 +464,7 @@ function analyzeComments(lines, ext = '') {
|
|
|
323
464
|
return analysis;
|
|
324
465
|
}
|
|
325
466
|
|
|
326
|
-
/** Splices comments into the
|
|
467
|
+
/** Splices comments into the provided code data [ds] */
|
|
327
468
|
function spliceComments(data, comments, mode = 'default', ext = '') {
|
|
328
469
|
const hasCRLF = data.includes('\r\n');
|
|
329
470
|
const lineEnding = hasCRLF ? '\r\n' : '\n';
|
|
@@ -372,7 +513,6 @@ function spliceComments(data, comments, mode = 'default', ext = '') {
|
|
|
372
513
|
continue;
|
|
373
514
|
}
|
|
374
515
|
|
|
375
|
-
// Prune mode: remove all comments from the line [ds]
|
|
376
516
|
if (mode === 'prune') {
|
|
377
517
|
if (lineAnalysis.isPureComment) {
|
|
378
518
|
finalDeletions.add(lineNum);
|
|
@@ -447,7 +587,6 @@ function spliceComments(data, comments, mode = 'default', ext = '') {
|
|
|
447
587
|
const indentation = indentMatch ? indentMatch[1] : '';
|
|
448
588
|
|
|
449
589
|
const commentLines = c.comment.split(/\r?\n/).map((line, idx) => {
|
|
450
|
-
// Add the comment to the list of comments [ds]
|
|
451
590
|
let trimmed = line.trimStart();
|
|
452
591
|
if (!trimmed) return '';
|
|
453
592
|
|
|
@@ -479,7 +618,6 @@ function spliceComments(data, comments, mode = 'default', ext = '') {
|
|
|
479
618
|
const filteredText = filtered.map(line => line.text);
|
|
480
619
|
const filteredIndices = filtered.map(line => line.originalIndex);
|
|
481
620
|
|
|
482
|
-
// Check if the filtered text is equal to the original text [ds]
|
|
483
621
|
const textEqual = filteredText.every((text, idx) => {
|
|
484
622
|
const origIdx = filteredIndices[idx];
|
|
485
623
|
const originalLine = originalLines[origIdx];
|
|
@@ -518,7 +656,7 @@ function spliceComments(data, comments, mode = 'default', ext = '') {
|
|
|
518
656
|
return annotated.map(line => line.text).join(lineEnding);
|
|
519
657
|
}
|
|
520
658
|
|
|
521
|
-
/** Runs the CLI
|
|
659
|
+
/** Runs the CLI application [ds] */
|
|
522
660
|
async function runCLI() {
|
|
523
661
|
rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
524
662
|
askQuestion = (query) => new Promise((resolve) => rl.question(query, resolve));
|
|
@@ -662,7 +800,7 @@ Options:
|
|
|
662
800
|
];
|
|
663
801
|
const allIgnored = [...defaultIgnoredFolders, ...userIgnorePatterns];
|
|
664
802
|
|
|
665
|
-
/** Checks if a
|
|
803
|
+
/** Checks if a path is ignored based on the ignore patterns [ds] */
|
|
666
804
|
function isPathIgnored(targetPath) {
|
|
667
805
|
const filename = path.basename(targetPath);
|
|
668
806
|
for (const pattern of allIgnored) {
|
|
@@ -673,7 +811,7 @@ Options:
|
|
|
673
811
|
return false;
|
|
674
812
|
}
|
|
675
813
|
|
|
676
|
-
/** Processes a given path [ds] */
|
|
814
|
+
/** Processes a given path, which can be a file or directory [ds] */
|
|
677
815
|
async function processPath(targetPath) {
|
|
678
816
|
const stats = fs.statSync(targetPath);
|
|
679
817
|
|
package/lib/config.js
CHANGED
|
@@ -4,7 +4,7 @@ const os = require('os');
|
|
|
4
4
|
const readline = require('readline');
|
|
5
5
|
const configPath = path.join(os.homedir(), '.devsplainrc');
|
|
6
6
|
|
|
7
|
-
/**
|
|
7
|
+
/** Asks the user for a secret input (e.g., an API key) without echoing the input to the console. [ds] */
|
|
8
8
|
function askSecret(query) {
|
|
9
9
|
return new Promise((resolve) => {
|
|
10
10
|
const rl = readline.createInterface({
|
|
@@ -33,7 +33,7 @@ function askSecret(query) {
|
|
|
33
33
|
});
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
/**
|
|
36
|
+
/** Attempts to fetch the configuration settings, either from environment variables or by prompting the user. [ds] */
|
|
37
37
|
async function getConfig(forceWizard = false) {
|
|
38
38
|
if (process.env.DEVSPLAIN_API_KEY || process.env.DEVSPLAIN_PROVIDER) {
|
|
39
39
|
const provider = process.env.DEVSPLAIN_PROVIDER || 'gemini';
|
|
@@ -46,8 +46,8 @@ async function getConfig(forceWizard = false) {
|
|
|
46
46
|
baseUrl
|
|
47
47
|
};
|
|
48
48
|
}
|
|
49
|
+
// If the configuration file doesn't exist, or if we're forcing the wizard, let's prompt the user for configuration. [ds]
|
|
49
50
|
|
|
50
|
-
// If the configuration file does not exist or forceWizard is true, prompt the user to configure [ds]
|
|
51
51
|
if (!fs.existsSync(configPath) || forceWizard) {
|
|
52
52
|
let rl = readline.createInterface({
|
|
53
53
|
input: process.stdin,
|
|
@@ -58,13 +58,13 @@ async function getConfig(forceWizard = false) {
|
|
|
58
58
|
let config = null;
|
|
59
59
|
let confirmed = false;
|
|
60
60
|
|
|
61
|
-
//
|
|
61
|
+
// Keep prompting the user until they confirm their configuration settings. [ds]
|
|
62
62
|
while (!confirmed) {
|
|
63
63
|
let baseUrl = "";
|
|
64
64
|
let model = "";
|
|
65
65
|
let provider = "";
|
|
66
66
|
|
|
67
|
-
// Display the list of available AI providers [ds]
|
|
67
|
+
// Display the list of available AI providers to the user. [ds]
|
|
68
68
|
console.log("\nWhich AI Provider Do You want to use?");
|
|
69
69
|
console.log("1. Groq (Free, Fast, Llama-3)");
|
|
70
70
|
console.log("2. Gemini (Free Tier)");
|
|
@@ -115,7 +115,7 @@ async function getConfig(forceWizard = false) {
|
|
|
115
115
|
continue;
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
//
|
|
118
|
+
// Get the API key from the user. [ds]
|
|
119
119
|
let apiKey = '';
|
|
120
120
|
while (true) {
|
|
121
121
|
const promptMsg = provider === 'custom'
|
|
@@ -142,7 +142,7 @@ async function getConfig(forceWizard = false) {
|
|
|
142
142
|
console.log(`API key is required for provider '${provider}'.`);
|
|
143
143
|
}
|
|
144
144
|
|
|
145
|
-
// Ask the user if they want to enable auto-pruning of existing comments [ds]
|
|
145
|
+
// Ask the user if they want to enable auto-pruning of existing human/AI comments. [ds]
|
|
146
146
|
let autoPrune = false;
|
|
147
147
|
while (true) {
|
|
148
148
|
const pruneAns = (await askQuestion("Do you want devsplain to aggressively prune (overwrite) existing human/AI comments? (y/n, default: n): ")).trim().toLowerCase();
|
|
@@ -156,7 +156,7 @@ async function getConfig(forceWizard = false) {
|
|
|
156
156
|
console.log("Invalid choice. Please enter 'y' or 'n'.");
|
|
157
157
|
}
|
|
158
158
|
|
|
159
|
-
// Display a summary of the configuration [ds]
|
|
159
|
+
// Display a summary of the user's configuration settings. [ds]
|
|
160
160
|
console.log("\n--- Configuration Summary ---");
|
|
161
161
|
console.log(`Provider: ${provider}`);
|
|
162
162
|
console.log(`Model: ${model}`);
|
|
@@ -165,7 +165,7 @@ async function getConfig(forceWizard = false) {
|
|
|
165
165
|
console.log(`Auto-Prune: ${autoPrune ? 'Yes' : 'No'}`);
|
|
166
166
|
console.log("-----------------------------\n");
|
|
167
167
|
|
|
168
|
-
// Confirm the configuration with the user [ds]
|
|
168
|
+
// Confirm the configuration settings with the user. [ds]
|
|
169
169
|
while (true) {
|
|
170
170
|
const confirm = (await askQuestion("Does this look correct? (y/n, default: y): ")).trim().toLowerCase();
|
|
171
171
|
if (confirm === '' || confirm === 'y' || confirm === 'yes') {
|
|
@@ -187,10 +187,9 @@ async function getConfig(forceWizard = false) {
|
|
|
187
187
|
|
|
188
188
|
rl.close();
|
|
189
189
|
|
|
190
|
-
// Write the configuration to the config file [ds]
|
|
190
|
+
// Write the configuration settings to the config file. [ds]
|
|
191
191
|
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
192
192
|
try {
|
|
193
|
-
// Set the permissions of the config file to prevent other users from reading it [ds]
|
|
194
193
|
if (process.platform !== 'win32') {
|
|
195
194
|
fs.chmodSync(configPath, 0o600);
|
|
196
195
|
}
|
|
@@ -204,4 +203,5 @@ async function getConfig(forceWizard = false) {
|
|
|
204
203
|
}
|
|
205
204
|
}
|
|
206
205
|
|
|
206
|
+
// Export the getConfig function for use in other modules. [ds]
|
|
207
207
|
module.exports = { getConfig };
|
package/lib/llm.js
CHANGED
|
@@ -41,37 +41,14 @@ async function getComments(code, language, config, mode = 'default') {
|
|
|
41
41
|
const lines = code.split(/\r?\n/);
|
|
42
42
|
const numberedCode = lines.map((line, index) => `${index + 1}: ${line}`).join('\n');
|
|
43
43
|
|
|
44
|
-
let
|
|
45
|
-
if (mode === '
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
CRITICAL RULES:
|
|
51
|
-
1. You MUST respond with ONLY a raw, valid JSON array of objects. NO markdown formatting, NO backticks, NO explanations, NO text before or after the JSON.
|
|
52
|
-
2. Each object must have exactly two properties: "line" (the integer line number of the comment line to delete) and "action" (which must be the string "delete").
|
|
53
|
-
3. Do NOT include the original code in your response.
|
|
54
|
-
4. If no comments are found, return an empty array: [].
|
|
55
|
-
5. For block or JSDoc comments (e.g., starting with /* and ending with */), you MUST identify and return the line numbers of ALL lines in that block, including the opening /*, all intermediate lines, and the closing */. Do NOT leave trailing comment delimiters behind.
|
|
56
|
-
|
|
57
|
-
Example Output:
|
|
58
|
-
[
|
|
59
|
-
{ "line": 4, "action": "delete" },
|
|
60
|
-
{ "line": 5, "action": "delete" }
|
|
61
|
-
]
|
|
62
|
-
|
|
63
|
-
Here is the source code:
|
|
64
|
-
${numberedCode}
|
|
65
|
-
`.trim();
|
|
66
|
-
} else {
|
|
67
|
-
let instruction = "Provide JSDoc/docstrings block comments above functions and sparse inline comments for complex logic.";
|
|
68
|
-
if (mode === 'light') {
|
|
69
|
-
instruction = "Provide ONLY JSDoc/docstrings above functions. Keep it minimal.";
|
|
70
|
-
} else if (mode === 'full') {
|
|
71
|
-
instruction = "Provide highly detailed JSDoc/docstrings above functions, and exhaustive step-by-step inline comments (using standard comment syntax like // or #) explaining every conditional branch, loop, variable assignment, and logical block inside function bodies. Do not be sparse; explain the code's execution flow in detail.";
|
|
72
|
-
}
|
|
44
|
+
let instruction = "Provide JSDoc/docstrings block comments above functions and sparse inline comments for complex logic.";
|
|
45
|
+
if (mode === 'light') {
|
|
46
|
+
instruction = "Provide ONLY JSDoc/docstrings above functions. Keep it minimal.";
|
|
47
|
+
} else if (mode === 'full') {
|
|
48
|
+
instruction = "Provide highly detailed JSDoc/docstrings above functions, and exhaustive step-by-step inline comments (using standard comment syntax like // or #) explaining every conditional branch, loop, variable assignment, and logical block inside function bodies. Do not be sparse; explain the code's execution flow in detail.";
|
|
49
|
+
}
|
|
73
50
|
|
|
74
|
-
|
|
51
|
+
let prompt = `
|
|
75
52
|
You are a code documentation engine. Analyze the following ${language} code which has line numbers prepended to it.
|
|
76
53
|
${instruction}
|
|
77
54
|
|
|
@@ -89,8 +66,7 @@ Example Output:
|
|
|
89
66
|
|
|
90
67
|
Here is the source code:
|
|
91
68
|
${numberedCode}
|
|
92
|
-
|
|
93
|
-
}
|
|
69
|
+
`.trim();
|
|
94
70
|
|
|
95
71
|
// Initialize text response [ds]
|
|
96
72
|
let textResponse = "";
|
package/package.json
CHANGED