devsplain 1.5.4 → 1.5.5
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 +14 -13
- package/package.json +1 -1
package/bin/cli.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
1
2
|
|
|
2
3
|
const { getComments } = require('../lib/llm.js');
|
|
3
4
|
const { getConfig } = require('../lib/config.js');
|
|
@@ -22,7 +23,7 @@ function isGitDirty() {
|
|
|
22
23
|
return false;
|
|
23
24
|
}
|
|
24
25
|
|
|
25
|
-
/** Determines if a
|
|
26
|
+
/** Determines if a line is within a string literal in the source code */
|
|
26
27
|
function isLineInsideString(lines, targetLineIndex, ext = '') {
|
|
27
28
|
const isPython = ext.toLowerCase() === '.py';
|
|
28
29
|
let inBacktick = false;
|
|
@@ -31,7 +32,6 @@ function isLineInsideString(lines, targetLineIndex, ext = '') {
|
|
|
31
32
|
let inSingle = false;
|
|
32
33
|
let inDouble = false;
|
|
33
34
|
|
|
34
|
-
// Iterate through lines prior to the target to track string/block state
|
|
35
35
|
for (let i = 0; i < targetLineIndex; i++) {
|
|
36
36
|
const line = lines[i];
|
|
37
37
|
let j = 0;
|
|
@@ -52,7 +52,6 @@ function isLineInsideString(lines, targetLineIndex, ext = '') {
|
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
54
|
} else {
|
|
55
|
-
// Check for unescaped backtick (JS template strings) or quotes
|
|
56
55
|
if (!inSingle && !inDouble && line[j] === '`') {
|
|
57
56
|
let escaped = false;
|
|
58
57
|
let k = j - 1;
|
|
@@ -98,7 +97,7 @@ function isLineInsideString(lines, targetLineIndex, ext = '') {
|
|
|
98
97
|
return inBacktick || inTripleDouble || inTripleSingle || inSingle || inDouble;
|
|
99
98
|
}
|
|
100
99
|
|
|
101
|
-
/**
|
|
100
|
+
/** Analyzes source code to identify comments and code blocks */
|
|
102
101
|
function analyzeComments(lines, ext = '') {
|
|
103
102
|
const isPython = ext.toLowerCase() === '.py';
|
|
104
103
|
const isHTML = ['.html', '.vue', '.svelte'].includes(ext.toLowerCase());
|
|
@@ -110,7 +109,6 @@ function analyzeComments(lines, ext = '') {
|
|
|
110
109
|
let inDouble = false;
|
|
111
110
|
let inBlockJS = false;
|
|
112
111
|
let inBlockHTML = false;
|
|
113
|
-
// Iterate through each line character by character to detect comment boundaries
|
|
114
112
|
for (let i = 0; i < lines.length; i++) {
|
|
115
113
|
const line = lines[i];
|
|
116
114
|
let commentStartIndex = -1;
|
|
@@ -250,9 +248,8 @@ function analyzeComments(lines, ext = '') {
|
|
|
250
248
|
return analysis;
|
|
251
249
|
}
|
|
252
250
|
|
|
253
|
-
/**
|
|
251
|
+
/** Applies or removes comments from source data based on a specified mode */
|
|
254
252
|
function spliceComments(data, comments, mode = 'default', ext = '') {
|
|
255
|
-
// Determine platform-specific line endings
|
|
256
253
|
const hasCRLF = data.includes('\r\n');
|
|
257
254
|
const lineEnding = hasCRLF ? '\r\n' : '\n';
|
|
258
255
|
const originalLines = data.split(/\r?\n/);
|
|
@@ -262,7 +259,6 @@ function spliceComments(data, comments, mode = 'default', ext = '') {
|
|
|
262
259
|
const annotated = originalLines.map((text, index) => ({ text, originalIndex: index }));
|
|
263
260
|
let analysis = null;
|
|
264
261
|
|
|
265
|
-
// 'clean' mode removes all existing comments/documentation
|
|
266
262
|
if (mode === 'clean') {
|
|
267
263
|
analysis = analyzeComments(originalLines, ext);
|
|
268
264
|
const finalDeletions = new Set();
|
|
@@ -290,6 +286,10 @@ function spliceComments(data, comments, mode = 'default', ext = '') {
|
|
|
290
286
|
const trimmedLine = targetLine.trim();
|
|
291
287
|
|
|
292
288
|
const lineAnalysis = analysis[lineNum - 1];
|
|
289
|
+
if (trimmedLine.startsWith('#!')) {
|
|
290
|
+
continue;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
293
|
const isCommentLine =
|
|
294
294
|
lineAnalysis.isInsideBlock ||
|
|
295
295
|
lineAnalysis.isPureComment ||
|
|
@@ -311,7 +311,6 @@ function spliceComments(data, comments, mode = 'default', ext = '') {
|
|
|
311
311
|
annotated.splice(lineNum - 1, 1);
|
|
312
312
|
}
|
|
313
313
|
} else {
|
|
314
|
-
// 'default'/'light'/'full' mode: Inject AI-generated comments
|
|
315
314
|
for (const c of validComments) {
|
|
316
315
|
if (isLineInsideString(originalLines, c.line - 1, ext)) {
|
|
317
316
|
console.warn(`[devsplain] Skipping comment insertion at line ${c.line} to avoid string literal corruption.`);
|
|
@@ -374,7 +373,7 @@ function spliceComments(data, comments, mode = 'default', ext = '') {
|
|
|
374
373
|
return annotated.map(line => line.text).join(lineEnding);
|
|
375
374
|
}
|
|
376
375
|
|
|
377
|
-
/** Main
|
|
376
|
+
/** Main CLI execution logic */
|
|
378
377
|
async function runCLI() {
|
|
379
378
|
rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
380
379
|
askQuestion = (query) => new Promise((resolve) => rl.question(query, resolve));
|
|
@@ -427,6 +426,7 @@ Options:
|
|
|
427
426
|
return;
|
|
428
427
|
}
|
|
429
428
|
|
|
429
|
+
// Helper to retrieve specific CLI argument values
|
|
430
430
|
const getArgValue = (flag) => {
|
|
431
431
|
const index = args.indexOf(flag);
|
|
432
432
|
if (index !== -1 && index + 1 < args.length) {
|
|
@@ -491,7 +491,7 @@ Options:
|
|
|
491
491
|
let successCount = 0;
|
|
492
492
|
let failCount = 0;
|
|
493
493
|
|
|
494
|
-
/** Recursively
|
|
494
|
+
/** Recursively processes files or directories to apply comments */
|
|
495
495
|
async function processPath(targetPath) {
|
|
496
496
|
const stats = fs.statSync(targetPath);
|
|
497
497
|
|
|
@@ -504,6 +504,7 @@ Options:
|
|
|
504
504
|
'.vscode', '.idea', 'coverage'
|
|
505
505
|
];
|
|
506
506
|
|
|
507
|
+
// Skip common dependency and configuration folders
|
|
507
508
|
if (ignoredFolders.includes(folderName)) {
|
|
508
509
|
return;
|
|
509
510
|
}
|
|
@@ -536,9 +537,9 @@ Options:
|
|
|
536
537
|
|
|
537
538
|
console.log(` Analyzing ${filename} in ${mode} mode...`);
|
|
538
539
|
try {
|
|
539
|
-
// Logic to either clean existing comments or replace/insert new ones
|
|
540
540
|
let comments = [];
|
|
541
541
|
let commentedCode;
|
|
542
|
+
// Perform comment processing: Clean existing, then inject new comments via LLM
|
|
542
543
|
if (mode !== 'clean') {
|
|
543
544
|
const cleanData = spliceComments(data, [], 'clean', ext);
|
|
544
545
|
comments = await getComments(cleanData, filename, config, mode);
|
|
@@ -552,7 +553,6 @@ Options:
|
|
|
552
553
|
console.log(`---------------------------------------\n`);
|
|
553
554
|
const answer = await askQuestion("Type 'write' to save to file, or press any key to discard: ");
|
|
554
555
|
if (answer.toLowerCase() === 'write') {
|
|
555
|
-
// Use temporary file for atomic write operations
|
|
556
556
|
const tempPath = targetPath + '.tmp';
|
|
557
557
|
fs.writeFileSync(tempPath, commentedCode, 'utf8');
|
|
558
558
|
fs.renameSync(tempPath, targetPath);
|
|
@@ -590,6 +590,7 @@ Options:
|
|
|
590
590
|
rl.close();
|
|
591
591
|
}
|
|
592
592
|
|
|
593
|
+
// Initialize the CLI application if executed as a script
|
|
593
594
|
if (require.main === module) {
|
|
594
595
|
runCLI().catch(err => {
|
|
595
596
|
console.error(err);
|
package/package.json
CHANGED