devsplain 1.5.4 → 1.5.6
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 +15 -14
- 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');
|
|
@@ -9,7 +10,7 @@ const { execSync } = require('child_process');
|
|
|
9
10
|
let rl;
|
|
10
11
|
let askQuestion;
|
|
11
12
|
|
|
12
|
-
/** Checks if the current Git repository
|
|
13
|
+
/** Checks if the current Git repository is dirty by inspecting status. */
|
|
13
14
|
function isGitDirty() {
|
|
14
15
|
try {
|
|
15
16
|
const gitDir = execSync('git rev-parse --is-inside-work-tree', { stdio: ['ignore', 'pipe', 'ignore'], encoding: 'utf8' }).trim();
|
|
@@ -22,7 +23,7 @@ function isGitDirty() {
|
|
|
22
23
|
return false;
|
|
23
24
|
}
|
|
24
25
|
|
|
25
|
-
/** Determines if a specific line index
|
|
26
|
+
/** Determines if a specific line index is within a string literal (handling quotes/backticks). */
|
|
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
|
+
/** Parses a file to identify pure comments and block structures. */
|
|
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
|
-
/** Splices
|
|
251
|
+
/** Splices comments into code or cleans existing ones, with safety checks. */
|
|
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,12 +259,14 @@ 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();
|
|
269
265
|
for (let i = 0; i < originalLines.length; i++) {
|
|
270
266
|
const lineNum = i + 1;
|
|
267
|
+
if (originalLines[i].trim().startsWith('#!')) {
|
|
268
|
+
continue;
|
|
269
|
+
}
|
|
271
270
|
if (analysis[i].isPureComment) {
|
|
272
271
|
finalDeletions.add(lineNum);
|
|
273
272
|
} else if (analysis[i].commentStartIndex !== -1) {
|
|
@@ -290,6 +289,10 @@ function spliceComments(data, comments, mode = 'default', ext = '') {
|
|
|
290
289
|
const trimmedLine = targetLine.trim();
|
|
291
290
|
|
|
292
291
|
const lineAnalysis = analysis[lineNum - 1];
|
|
292
|
+
if (trimmedLine.startsWith('#!')) {
|
|
293
|
+
continue;
|
|
294
|
+
}
|
|
295
|
+
|
|
293
296
|
const isCommentLine =
|
|
294
297
|
lineAnalysis.isInsideBlock ||
|
|
295
298
|
lineAnalysis.isPureComment ||
|
|
@@ -311,7 +314,6 @@ function spliceComments(data, comments, mode = 'default', ext = '') {
|
|
|
311
314
|
annotated.splice(lineNum - 1, 1);
|
|
312
315
|
}
|
|
313
316
|
} else {
|
|
314
|
-
// 'default'/'light'/'full' mode: Inject AI-generated comments
|
|
315
317
|
for (const c of validComments) {
|
|
316
318
|
if (isLineInsideString(originalLines, c.line - 1, ext)) {
|
|
317
319
|
console.warn(`[devsplain] Skipping comment insertion at line ${c.line} to avoid string literal corruption.`);
|
|
@@ -374,7 +376,7 @@ function spliceComments(data, comments, mode = 'default', ext = '') {
|
|
|
374
376
|
return annotated.map(line => line.text).join(lineEnding);
|
|
375
377
|
}
|
|
376
378
|
|
|
377
|
-
/** Main entry point for the CLI tool
|
|
379
|
+
/** Main entry point for the CLI tool. */
|
|
378
380
|
async function runCLI() {
|
|
379
381
|
rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
380
382
|
askQuestion = (query) => new Promise((resolve) => rl.question(query, resolve));
|
|
@@ -491,7 +493,7 @@ Options:
|
|
|
491
493
|
let successCount = 0;
|
|
492
494
|
let failCount = 0;
|
|
493
495
|
|
|
494
|
-
/** Recursively
|
|
496
|
+
/** Recursively processes files or directories to apply AI-generated comments. */
|
|
495
497
|
async function processPath(targetPath) {
|
|
496
498
|
const stats = fs.statSync(targetPath);
|
|
497
499
|
|
|
@@ -536,7 +538,6 @@ Options:
|
|
|
536
538
|
|
|
537
539
|
console.log(` Analyzing ${filename} in ${mode} mode...`);
|
|
538
540
|
try {
|
|
539
|
-
// Logic to either clean existing comments or replace/insert new ones
|
|
540
541
|
let comments = [];
|
|
541
542
|
let commentedCode;
|
|
542
543
|
if (mode !== 'clean') {
|
|
@@ -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
|
+
// Check if the script is run directly vs required as a module
|
|
593
594
|
if (require.main === module) {
|
|
594
595
|
runCLI().catch(err => {
|
|
595
596
|
console.error(err);
|
package/package.json
CHANGED