devsplain 1.5.1 → 1.5.2

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 CHANGED
@@ -22,7 +22,7 @@ function isGitDirty() {
22
22
  return false;
23
23
  }
24
24
 
25
- /** Determines if a specific line index is inside a string or multiline literal */
25
+ /** Determines if a specific line index falls within a multi-line string or block */
26
26
  function isLineInsideString(lines, targetLineIndex, ext = '') {
27
27
  const isPython = ext.toLowerCase() === '.py';
28
28
  let inBacktick = false;
@@ -96,7 +96,7 @@ function isLineInsideString(lines, targetLineIndex, ext = '') {
96
96
  return inBacktick || inTripleDouble || inTripleSingle || inSingle || inDouble;
97
97
  }
98
98
 
99
- /** Analyzes code to identify pure comment lines and block boundaries */
99
+ /** Analyzes code to determine which lines are purely comments */
100
100
  function analyzeComments(lines, ext = '') {
101
101
  const isPython = ext.toLowerCase() === '.py';
102
102
  const isHTML = ['.html', '.vue', '.svelte'].includes(ext.toLowerCase());
@@ -247,7 +247,7 @@ function analyzeComments(lines, ext = '') {
247
247
  return analysis;
248
248
  }
249
249
 
250
- /** Splicers or removes comments from source data based on the requested mode */
250
+ /** Splices AI-generated comments into source code or cleans existing ones */
251
251
  function spliceComments(data, comments, mode = 'default', ext = '') {
252
252
  const hasCRLF = data.includes('\r\n');
253
253
  const lineEnding = hasCRLF ? '\r\n' : '\n';
@@ -255,9 +255,11 @@ function spliceComments(data, comments, mode = 'default', ext = '') {
255
255
  const sortedComments = [...comments].sort((a, b) => b.line - a.line);
256
256
  const validComments = sortedComments.filter(c => c.line >= 1 && c.line <= originalLines.length + 1);
257
257
 
258
+ // Map lines to objects to track original positioning after splicing
258
259
  const annotated = originalLines.map((text, index) => ({ text, originalIndex: index }));
259
260
  let analysis = null;
260
261
 
262
+ // Logic for removing existing comments
261
263
  if (mode === 'clean') {
262
264
  analysis = analyzeComments(originalLines, ext);
263
265
  const finalDeletions = new Set();
@@ -279,6 +281,7 @@ function spliceComments(data, comments, mode = 'default', ext = '') {
279
281
 
280
282
  const linesToDelete = Array.from(finalDeletions).sort((a, b) => b - a);
281
283
 
284
+ // Process deletions in reverse to maintain line integrity
282
285
  for (const lineNum of linesToDelete) {
283
286
  const targetLine = originalLines[lineNum - 1];
284
287
  if (!targetLine) continue;
@@ -313,6 +316,7 @@ function spliceComments(data, comments, mode = 'default', ext = '') {
313
316
  }
314
317
 
315
318
  const targetLine = originalLines[c.line - 1] || '';
319
+ // Determine indentation level for new comment blocks
316
320
  const indentMatch = targetLine.match(/^([ \t]*)/);
317
321
  const indentation = indentMatch ? indentMatch[1] : '';
318
322
 
@@ -330,6 +334,7 @@ function spliceComments(data, comments, mode = 'default', ext = '') {
330
334
  }
331
335
  }
332
336
 
337
+ // Verify that the result matches expected output before committing to disk
333
338
  const filtered = annotated.filter(line => line.originalIndex !== -1);
334
339
  const filteredText = filtered.map(line => line.text);
335
340
  const filteredIndices = filtered.map(line => line.originalIndex);
@@ -368,7 +373,7 @@ function spliceComments(data, comments, mode = 'default', ext = '') {
368
373
  return annotated.map(line => line.text).join(lineEnding);
369
374
  }
370
375
 
371
- /** Main CLI entry point handler */
376
+ /** Main CLI execution loop */
372
377
  async function runCLI() {
373
378
  rl = readline.createInterface({ input: process.stdin, output: process.stdout });
374
379
  askQuestion = (query) => new Promise((resolve) => rl.question(query, resolve));
@@ -416,7 +421,8 @@ Options:
416
421
 
417
422
  if (args.includes('--setup-hook')) {
418
423
  rl.close();
419
- require('./setup-hook.js');
424
+ const installHooks = require('./setup-hook.js');
425
+ await installHooks();
420
426
  return;
421
427
  }
422
428
 
@@ -484,7 +490,7 @@ Options:
484
490
  let successCount = 0;
485
491
  let failCount = 0;
486
492
 
487
- /** Recursively traverses directories to process files */
493
+ /** Recursively process files or directories */
488
494
  async function processPath(targetPath) {
489
495
  const stats = fs.statSync(targetPath);
490
496
 
@@ -528,6 +534,7 @@ Options:
528
534
  }
529
535
 
530
536
  console.log(` Analyzing ${filename} in ${mode} mode...`);
537
+ // Perform comment generation if not in 'clean' mode
531
538
  try {
532
539
  let comments = [];
533
540
  let commentedCode;
@@ -544,6 +551,7 @@ Options:
544
551
  console.log(`---------------------------------------\n`);
545
552
  const answer = await askQuestion("Type 'write' to save to file, or press any key to discard: ");
546
553
  if (answer.toLowerCase() === 'write') {
554
+ // Atomic write: write to temp file then rename
547
555
  const tempPath = targetPath + '.tmp';
548
556
  fs.writeFileSync(tempPath, commentedCode, 'utf8');
549
557
  fs.renameSync(tempPath, targetPath);
@@ -581,7 +589,7 @@ Options:
581
589
  rl.close();
582
590
  }
583
591
 
584
- // If running as a standalone script, start the CLI; otherwise export helpers
592
+ // Execute main if run directly, otherwise export utility functions
585
593
  if (require.main === module) {
586
594
  runCLI().catch(err => {
587
595
  console.error(err);
package/bin/setup-hook.js CHANGED
@@ -4,12 +4,12 @@ const { execSync } = require('child_process');
4
4
  const readline = require('readline');
5
5
 
6
6
  /**
7
- * Configures and installs git pre-commit and post-commit hooks.
8
- * Detects the local git repository and prompts the user for comment mode preferences.
7
+ * Initializes and installs Git pre-commit and post-commit hooks.
8
+ * Prompts the user for documentation mode configuration.
9
9
  */
10
10
  async function installHooks() {
11
11
  try {
12
- // Resolve the actual .git directory path
12
+ // Resolve the root .git directory path
13
13
  const gitDir = execSync('git rev-parse --git-dir', { encoding: 'utf8' }).trim();
14
14
  const hooksDir = path.join(gitDir, 'hooks');
15
15
  if (!fs.existsSync(hooksDir)) {
@@ -17,13 +17,13 @@ async function installHooks() {
17
17
  }
18
18
 
19
19
  let modeChoice = '1';
20
- // Check if running in an interactive terminal to prompt for preferences
20
+ // Interactive mode requires a TTY terminal
21
21
  if (process.stdout.isTTY) {
22
22
  const rl = readline.createInterface({
23
23
  input: process.stdin,
24
24
  output: process.stdout
25
25
  });
26
- // Helper to wrap readline as a Promise for async/await control flow
26
+ // Promisify the readline interface for async/await flow
27
27
  const askQuestion = (query) => new Promise((resolve) => rl.question(query, resolve));
28
28
 
29
29
  console.log('\nSelect default commenting mode for Git commits:');
@@ -35,7 +35,7 @@ async function installHooks() {
35
35
  rl.close();
36
36
  }
37
37
 
38
- // Map user selection to CLI argument flags for the post-commit handler
38
+ // Determine documentation style flags based on user input
39
39
  let modeArgs = '';
40
40
  if (modeChoice === '2') {
41
41
  modeArgs = ' --light';
@@ -43,7 +43,7 @@ async function installHooks() {
43
43
  modeArgs = ' --full';
44
44
  }
45
45
 
46
- // Define and write the pre-commit shell script
46
+ // Write the pre-commit script to the git hooks directory
47
47
  const preCommitHookPath = path.join(hooksDir, 'pre-commit');
48
48
  const preCommitContent = `#!/bin/sh
49
49
  # devsplain native pre-commit hook
@@ -51,12 +51,12 @@ echo "Running pre-commit tests..."
51
51
  npm test || exit 1
52
52
  `;
53
53
  fs.writeFileSync(preCommitHookPath, preCommitContent);
54
- // Ensure the shell script is executable by the system
55
54
  try {
55
+ // Ensure the hook file is executable
56
56
  fs.chmodSync(preCommitHookPath, 0o755);
57
57
  } catch (err) {}
58
58
 
59
- // Define and write the post-commit shell script
59
+ // Write the post-commit script that triggers documentation generation
60
60
  const postCommitHookPath = path.join(hooksDir, 'post-commit');
61
61
  const postCommitContent = `#!/bin/sh
62
62
  # devsplain native post-commit hook
@@ -64,7 +64,6 @@ echo "Auto-generating comments for files in the last commit..."
64
64
  node bin/post-commit.js${modeArgs} || exit 1
65
65
  `;
66
66
  fs.writeFileSync(postCommitHookPath, postCommitContent);
67
- // Ensure the post-commit shell script is executable
68
67
  try {
69
68
  fs.chmodSync(postCommitHookPath, 0o755);
70
69
  } catch (err) {}
@@ -75,5 +74,8 @@ node bin/post-commit.js${modeArgs} || exit 1
75
74
  }
76
75
  }
77
76
 
78
- // Execute the hook installation sequence
79
- installHooks();
77
+ // Execute the function automatically if the file is run directly
78
+ if (require.main === module) {
79
+ installHooks();
80
+ }
81
+ module.exports = installHooks;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "devsplain",
3
- "version": "1.5.1",
3
+ "version": "1.5.2",
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",