devsplain 2.2.1 → 2.2.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
@@ -10,7 +10,7 @@ const { execSync } = require('child_process');
10
10
  let rl;
11
11
  let askQuestion;
12
12
 
13
- // Determines whether the current Git repository has uncommitted changes [ds]
13
+ // Checks if the Git repository is dirty [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,7 +23,7 @@ function isGitDirty() {
23
23
  return false;
24
24
  }
25
25
 
26
- // Checks whether a given line of code is inside a string literal [ds]
26
+ // Determines if a line 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());
@@ -236,7 +236,7 @@ function isLineInsideString(lines, targetLineIndex, ext = '') {
236
236
  return inBacktick || inTripleDouble || inTripleSingle || inSingle || inDouble || inCppRawString || inRegex;
237
237
  }
238
238
 
239
- // Analyzes comments within a given list of code lines [ds]
239
+ // Analyzes comments in the given lines of code [ds]
240
240
  function analyzeComments(lines, ext = '') {
241
241
  const isPython = ext.toLowerCase() === '.py';
242
242
  const isHTML = ['.html', '.vue', '.svelte'].includes(ext.toLowerCase());
@@ -477,7 +477,7 @@ function analyzeComments(lines, ext = '') {
477
477
  return analysis;
478
478
  }
479
479
 
480
- // Inserts or removes comments from code based on the specified mode [ds]
480
+ // Splices comments into the provided data [ds]
481
481
  function spliceComments(data, comments, mode = 'default', ext = '') {
482
482
  const hasCRLF = data.includes('\r\n');
483
483
  const lineEnding = hasCRLF ? '\r\n' : '\n';
@@ -717,7 +717,7 @@ function spliceComments(data, comments, mode = 'default', ext = '') {
717
717
  return annotated.map(line => line.text).join(lineEnding);
718
718
  }
719
719
 
720
- // Runs the command-line interface for the application [ds]
720
+ // Runs the CLI interface for the application [ds]
721
721
  async function runCLI() {
722
722
  rl = readline.createInterface({ input: process.stdin, output: process.stdout });
723
723
  askQuestion = (query) => new Promise((resolve) => rl.question(query, resolve));
@@ -744,6 +744,7 @@ Options:
744
744
  --base-url <url> Override base URL for custom APIs
745
745
  --config Force run the configuration setup wizard
746
746
  --setup-hook Install Git pre-commit and post-commit hooks in repository
747
+ --remove-hook Remove devsplain-installed Git hooks from repository
747
748
  --help, -h Show this help message
748
749
  --version, -v Show version information
749
750
  `);
@@ -767,11 +768,18 @@ Options:
767
768
 
768
769
  if (args.includes('--setup-hook')) {
769
770
  rl.close();
770
- const installHooks = require('./setup-hook.js');
771
+ const { installHooks } = require('./setup-hook.js');
771
772
  await installHooks();
772
773
  return;
773
774
  }
774
775
 
776
+ if (args.includes('--remove-hook')) {
777
+ rl.close();
778
+ const { removeHooks } = require('./setup-hook.js');
779
+ await removeHooks();
780
+ return;
781
+ }
782
+
775
783
  const getArgValue = (flag) => {
776
784
  const index = args.indexOf(flag);
777
785
  if (index !== -1 && index + 1 < args.length) {
package/bin/setup-hook.js CHANGED
@@ -3,7 +3,7 @@ const path = require('path');
3
3
  const { execSync } = require('child_process');
4
4
  const readline = require('readline');
5
5
 
6
- /** Installs Git hooks and configures commit settings [ds] */
6
+ // Installs Git hooks for automatic comment generation [ds]
7
7
  async function installHooks() {
8
8
  try {
9
9
  const gitDir = execSync('git rev-parse --git-dir', { encoding: 'utf8' }).trim();
@@ -14,7 +14,7 @@ async function installHooks() {
14
14
  }
15
15
 
16
16
  let modeChoice = '1';
17
- // Check if process is run in a TTY environment [ds]
17
+ // Prompt user for commenting mode if running in a TTY [ds]
18
18
  if (process.stdout.isTTY) {
19
19
  const rl = readline.createInterface({
20
20
  input: process.stdin,
@@ -22,13 +22,13 @@ async function installHooks() {
22
22
  });
23
23
  const askQuestion = (query) => new Promise((resolve) => rl.question(query, resolve));
24
24
 
25
- // Prompt user to select the default commenting mode [ds]
25
+ // Display commenting mode options to the user [ds]
26
26
  console.log('\nSelect default commenting mode for Git commits:');
27
27
  console.log('1. Balanced (mix of JSDoc and sparse inline comments)');
28
28
  console.log('2. Light (JSDoc block comments above functions only)');
29
29
  console.log('3. Full (aggressive inline commenting)');
30
30
 
31
- // Validate user input for commenting mode [ds]
31
+ // Loop until a valid mode choice is selected [ds]
32
32
  while (true) {
33
33
  const answer = (await askQuestion('Select (1-3, default: 1): ')).trim();
34
34
  if (answer === '' || ['1', '2', '3'].includes(answer)) {
@@ -40,7 +40,7 @@ async function installHooks() {
40
40
  rl.close();
41
41
  }
42
42
 
43
- // Determine mode arguments based on user choice [ds]
43
+ // Determine mode arguments based on user selection [ds]
44
44
  let modeArgs = '';
45
45
  if (modeChoice === '2') {
46
46
  modeArgs = ' --light';
@@ -48,7 +48,7 @@ async function installHooks() {
48
48
  modeArgs = ' --full';
49
49
  }
50
50
 
51
- // Define the pre-commit hook script [ds]
51
+ // Create pre-commit hook script [ds]
52
52
  const preCommitHookPath = path.join(hooksDir, 'pre-commit');
53
53
  const preCommitContent = `#!/bin/sh
54
54
  # devsplain native pre-commit hook
@@ -57,63 +57,126 @@ if [ -f package.json ] && grep -q '"test"' package.json 2>/dev/null; then
57
57
  npm test || exit 1
58
58
  fi
59
59
  `;
60
- // Write the pre-commit hook to the Git hooks directory [ds]
60
+ // Write pre-commit hook to file [ds]
61
61
  fs.writeFileSync(preCommitHookPath, preCommitContent);
62
62
  try {
63
63
  fs.chmodSync(preCommitHookPath, 0o755);
64
64
  } catch (err) {}
65
65
 
66
- // Define the post-commit script path [ds]
66
+ // Path to post-commit script [ds]
67
67
  const postCommitScript = path.join(__dirname, 'post-commit.js').replace(/\\/g, '/');
68
68
 
69
- // Define the post-commit hook script [ds]
69
+ // Create post-commit hook script [ds]
70
70
  const postCommitHookPath = path.join(hooksDir, 'post-commit');
71
71
  const postCommitContent = `#!/bin/sh
72
72
  # devsplain native post-commit hook
73
73
  echo "Auto-generating comments for files in the last commit..."
74
74
  node "${postCommitScript}"${modeArgs} || exit 1
75
75
  `;
76
+ // Write post-commit hook to file [ds]
76
77
  fs.writeFileSync(postCommitHookPath, postCommitContent);
77
78
  try {
78
79
  fs.chmodSync(postCommitHookPath, 0o755);
79
80
  } catch (err) {}
80
81
 
81
- // Inform the user about the successful installation of the post-commit hook [ds]
82
+ // Log successful installation of post-commit hook [ds]
82
83
  console.log(`[devsplain] Git post-commit hook successfully installed at: ${postCommitHookPath}`);
83
84
 
84
- // Check if a .devsplainignore file exists in the Git root directory [ds]
85
+ // Path to devsplain ignore file [ds]
85
86
  const ignorePath = path.join(gitRoot, '.devsplainignore');
87
+ const defaultIgnoreLines = [
88
+ 'node_modules/', '.git/', 'dist/', 'build/', 'out/',
89
+ '.next/', '.nuxt/', '.svelte-kit/',
90
+ 'venv/', 'env/', '.venv/',
91
+ '.vscode/', '.idea/', 'coverage/',
92
+ 'tests/', '__tests__/', 'fixtures/'
93
+ ];
94
+ // List of default patterns to ignore [ds]
95
+
96
+ const gitignorePath = path.join(gitRoot, '.gitignore');
97
+ let gitignoreLines = [];
98
+ if (fs.existsSync(gitignorePath)) {
99
+ const gitignoreContent = fs.readFileSync(gitignorePath, 'utf8');
100
+ gitignoreLines = gitignoreContent.split(/\r?\n/)
101
+ .map(l => l.trim())
102
+ .filter(l => l && !l.startsWith('#'));
103
+ }
104
+
105
+ // Check if .gitignore file exists [ds]
86
106
  if (!fs.existsSync(ignorePath)) {
87
- const defaultIgnore = `node_modules/
88
- .git/
89
- dist/
90
- build/
91
- out/
92
- .next/
93
- .nuxt/
94
- .svelte-kit/
95
- venv/
96
- env/
97
- .venv/
98
- .vscode/
99
- .idea/
100
- coverage/
101
- tests/
102
- __tests__/
103
- fixtures/
104
- `;
105
- fs.writeFileSync(ignorePath, defaultIgnore);
106
- console.log(`[devsplain] Created default .devsplainignore file at: ${ignorePath}`);
107
+ const gitignoreOnly = gitignoreLines.filter(p => !defaultIgnoreLines.includes(p));
108
+ let content = defaultIgnoreLines.join('\n') + '\n';
109
+ if (gitignoreOnly.length > 0) {
110
+ content += '\n# From .gitignore\n' + gitignoreOnly.join('\n') + '\n';
111
+ }
112
+ fs.writeFileSync(ignorePath, content);
113
+ console.log(`[devsplain] Created .devsplainignore at: ${ignorePath}`);
114
+ if (gitignoreOnly.length > 0) {
115
+ console.log(`[devsplain] Merged ${gitignoreOnly.length} pattern(s) from .gitignore into .devsplainignore.`);
116
+ }
117
+ } else {
118
+ const existingContent = fs.readFileSync(ignorePath, 'utf8');
119
+ const existingLines = existingContent.split(/\r?\n/).map(l => l.trim()).filter(l => l && !l.startsWith('#'));
120
+ const newPatterns = gitignoreLines.filter(p => !existingLines.includes(p));
121
+ if (newPatterns.length > 0) {
122
+ const appendContent = '\n# From .gitignore\n' + newPatterns.join('\n') + '\n';
123
+ fs.appendFileSync(ignorePath, appendContent);
124
+ console.log(`[devsplain] Merged ${newPatterns.length} new pattern(s) from .gitignore into .devsplainignore.`);
125
+ } else if (gitignoreLines.length > 0) {
126
+ console.log('[devsplain] .devsplainignore is already up-to-date with .gitignore patterns.');
127
+ }
128
+ // Handle any errors during hook installation [ds]
107
129
  }
130
+
108
131
  } catch (e) {
109
132
  console.warn('Warning: Could not set up Git hooks (not inside a git repository or git command missing).');
110
133
  console.warn(e.message);
111
134
  }
112
135
  }
113
136
 
114
- // Check if the script is run as the main module [ds]
137
+ // Removes Git hooks installed by devsplain [ds]
138
+ async function removeHooks() {
139
+ try {
140
+ const gitDir = execSync('git rev-parse --git-dir', { encoding: 'utf8' }).trim();
141
+ const hooksDir = path.join(gitDir, 'hooks');
142
+ const hookSignatures = {
143
+ 'pre-commit': '# devsplain native pre-commit hook',
144
+ 'post-commit': '# devsplain native post-commit hook'
145
+ };
146
+
147
+ let removed = 0;
148
+ // Iterate through hook signatures to remove [ds]
149
+ for (const [hookName, signature] of Object.entries(hookSignatures)) {
150
+ const hookPath = path.join(hooksDir, hookName);
151
+ if (fs.existsSync(hookPath)) {
152
+ const content = fs.readFileSync(hookPath, 'utf8');
153
+ if (content.includes(signature)) {
154
+ fs.unlinkSync(hookPath);
155
+ console.log(`[devsplain] Removed ${hookName} hook at: ${hookPath}`);
156
+ removed++;
157
+ } else {
158
+ console.log(`[devsplain] Skipping ${hookName}: not installed by devsplain.`);
159
+ }
160
+ } else {
161
+ console.log(`[devsplain] No ${hookName} hook found at: ${hookPath}`);
162
+ }
163
+ }
164
+
165
+ // Log removal of hooks [ds]
166
+ if (removed > 0) {
167
+ console.log(`[devsplain] Successfully removed ${removed} hook(s).`);
168
+ } else {
169
+ console.log('[devsplain] No devsplain hooks were found to remove.');
170
+ }
171
+ } catch (e) {
172
+ console.warn('Warning: Could not remove Git hooks (not inside a git repository or git command missing).');
173
+ console.warn(e.message);
174
+ }
175
+ }
176
+
177
+ // Run installHooks if this script is executed directly [ds]
115
178
  if (require.main === module) {
116
179
  installHooks();
117
180
  }
118
- // Export the installHooks function for external use [ds]
119
- module.exports = installHooks;
181
+ // Export installHooks and removeHooks functions [ds]
182
+ module.exports = { installHooks, removeHooks };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "devsplain",
3
- "version": "2.2.1",
3
+ "version": "2.2.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",