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.
Files changed (2) hide show
  1. package/bin/cli.js +14 -13
  2. 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 specific line index falls within a string literal */
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
- /** Performs a lexical analysis to categorize code lines and comment blocks */
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
- /** Splices generated comments into the source data or removes existing ones */
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 entry point for the CLI tool logic */
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 traverses the file system to identify and process source files */
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "devsplain",
3
- "version": "1.5.4",
3
+ "version": "1.5.5",
4
4
  "description": "An agent-agnostic CLI tool that automatically adds JSDoc and inline comments to your code using free LLMs.",
5
5
  "author": "mwahaj36",
6
6
  "license": "MIT",