devsplain 1.7.0 → 1.7.1
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/README.md +9 -1
- package/bin/post-commit.js +23 -11
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
# devsplain
|
|
2
|
+
An agent-agnostic CLI tool that adds JSDoc and inline comments using state-of-the-art LLMs while preserving non-comment source lines byte-for-byte through deterministic verification.
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+

|
|
4
5
|
|
|
6
|
+
devsplain never rewrites executable code.
|
|
7
|
+
If the original source cannot be reproduced exactly after comment insertion, the operation aborts.
|
|
5
8
|
---
|
|
6
9
|
|
|
7
10
|
## Key Features
|
|
@@ -35,6 +38,11 @@ Many AI code formatters rewrite your code entirely, exposing you to logic regres
|
|
|
35
38
|
### String Literal Guardrails
|
|
36
39
|
The engine tracks lexical state across template strings, single quotes, double quotes, and multi-line docstrings (such as Python triple-quotes). Comment insertion is blocked if the target line resides within a string literal, preventing broken syntax.
|
|
37
40
|
|
|
41
|
+
### The `[ds]` AI Tag Guarantee
|
|
42
|
+
Every single comment generated by the LLM is forcibly prefixed with a `[ds]` tag (e.g., `// [ds] This function handles...`).
|
|
43
|
+
This guarantees that the local `devsplain` lexer can mathematically differentiate between your human-written manual comments and the AI-generated comments.
|
|
44
|
+
When you run the `--clean` command, the lexer looks specifically for the `[ds]` prefix and surgically removes only the AI-generated comments, safely preserving 100% of your manual documentation.
|
|
45
|
+
|
|
38
46
|
### Why Not AST Verification?
|
|
39
47
|
|
|
40
48
|
AST verification would require language-specific parser dependencies for every supported language.
|
package/bin/post-commit.js
CHANGED
|
@@ -3,43 +3,54 @@ const fs = require('fs');
|
|
|
3
3
|
const path = require('path');
|
|
4
4
|
const { spliceComments } = require('./cli');
|
|
5
5
|
|
|
6
|
-
/**
|
|
6
|
+
/** Main execution block [ds] */
|
|
7
7
|
try {
|
|
8
|
-
// Get the
|
|
8
|
+
// Get the current git directory [ds]
|
|
9
|
+
const gitDir = execSync('git rev-parse --git-dir', { encoding: 'utf8' }).trim();
|
|
10
|
+
const isRebasing = fs.existsSync(path.join(gitDir, 'rebase-merge')) || fs.existsSync(path.join(gitDir, 'rebase-apply'));
|
|
11
|
+
const isMerging = fs.existsSync(path.join(gitDir, 'MERGE_HEAD'));
|
|
12
|
+
const isCherryPicking = fs.existsSync(path.join(gitDir, 'CHERRY_PICK_HEAD'));
|
|
13
|
+
|
|
14
|
+
if (isRebasing || isMerging || isCherryPicking) {
|
|
15
|
+
console.log('[devsplain] Skipping AI comment generation during git rebase/merge/cherry-pick to avoid history conflicts.');
|
|
16
|
+
process.exit(0);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Check if the current operation is a rebase, merge or cherry-pick [ds] */
|
|
9
20
|
const lastCommitMsg = execSync('git log -1 --format=%s', { encoding: 'utf8' }).trim();
|
|
10
21
|
if (lastCommitMsg === 'docs: auto-generated comments by devsplain') {
|
|
11
22
|
process.exit(0);
|
|
12
23
|
}
|
|
13
24
|
|
|
14
|
-
// Get the
|
|
25
|
+
// Get the last commit message [ds]
|
|
15
26
|
const changedFilesStr = execSync('git diff-tree --no-commit-id --name-only -r HEAD', { encoding: 'utf8' }).trim();
|
|
16
27
|
if (!changedFilesStr) {
|
|
17
28
|
process.exit(0);
|
|
18
29
|
}
|
|
19
30
|
const changedFiles = changedFilesStr.split(/\r?\n/);
|
|
20
31
|
|
|
21
|
-
/**
|
|
32
|
+
/** List of valid file extensions for commenting [ds] */
|
|
22
33
|
const validExtensions = [
|
|
23
34
|
'.js', '.jsx', '.ts', '.tsx', '.html', '.css', '.scss', '.vue', '.svelte',
|
|
24
35
|
'.py', '.java', '.c', '.cpp', '.cs', '.go', '.rb', '.php', '.rs',
|
|
25
36
|
'.swift', '.kt', '.dart', '.sh'
|
|
26
37
|
];
|
|
27
38
|
|
|
28
|
-
/**
|
|
39
|
+
/** Filter files to be commented [ds] */
|
|
29
40
|
const filesToComment = changedFiles.filter(file => {
|
|
30
41
|
const ext = path.extname(file).toLowerCase();
|
|
31
42
|
const isIgnored = file.includes('node_modules/') || file.includes('tests/') || file.includes('__tests__/') || file.includes('fixtures/');
|
|
32
43
|
return validExtensions.includes(ext) && fs.existsSync(file) && !isIgnored;
|
|
33
44
|
});
|
|
34
45
|
|
|
46
|
+
// Check if there are any files to comment [ds]
|
|
35
47
|
if (filesToComment.length === 0) {
|
|
36
48
|
process.exit(0);
|
|
37
49
|
}
|
|
38
50
|
|
|
39
|
-
// Log the number of files to be commented [ds]
|
|
40
51
|
console.log(`[devsplain] Found ${filesToComment.length} file(s) in the last commit to auto-comment.`);
|
|
41
52
|
|
|
42
|
-
// Parse command
|
|
53
|
+
// Parse command line arguments for mode flag [ds]
|
|
43
54
|
const args = process.argv.slice(2);
|
|
44
55
|
let modeFlag = '';
|
|
45
56
|
if (args.includes('--light')) modeFlag = ' --light';
|
|
@@ -47,7 +58,7 @@ try {
|
|
|
47
58
|
|
|
48
59
|
let commentedAny = false;
|
|
49
60
|
|
|
50
|
-
/**
|
|
61
|
+
/** Iterate over files to comment [ds] */
|
|
51
62
|
for (const file of filesToComment) {
|
|
52
63
|
try {
|
|
53
64
|
const ext = path.extname(file).toLowerCase();
|
|
@@ -61,7 +72,7 @@ try {
|
|
|
61
72
|
} catch (prevErr) {
|
|
62
73
|
}
|
|
63
74
|
|
|
64
|
-
//
|
|
75
|
+
// Compare file content before and after the last commit [ds]
|
|
65
76
|
if (contentPrev) {
|
|
66
77
|
const cleanHead = spliceComments(contentHead, [], 'prune', ext);
|
|
67
78
|
const cleanPrev = spliceComments(contentPrev, [], 'prune', ext);
|
|
@@ -73,7 +84,7 @@ try {
|
|
|
73
84
|
} catch (cleanErr) {
|
|
74
85
|
}
|
|
75
86
|
|
|
76
|
-
//
|
|
87
|
+
// Comment the file using the CLI [ds]
|
|
77
88
|
console.log(`[devsplain] Automatically commenting file: ${file}`);
|
|
78
89
|
try {
|
|
79
90
|
const cliPath = path.join(__dirname, 'cli.js');
|
|
@@ -84,7 +95,7 @@ try {
|
|
|
84
95
|
}
|
|
85
96
|
}
|
|
86
97
|
|
|
87
|
-
/**
|
|
98
|
+
/** Stage and commit auto-generated comments if any [ds] */
|
|
88
99
|
if (commentedAny) {
|
|
89
100
|
const status = execSync('git diff --name-only', { encoding: 'utf8' }).trim();
|
|
90
101
|
if (status.length > 0) {
|
|
@@ -93,6 +104,7 @@ try {
|
|
|
93
104
|
console.log('[devsplain] Comments committed successfully! Rollback via: git reset --hard HEAD~1');
|
|
94
105
|
}
|
|
95
106
|
}
|
|
107
|
+
/** Catch and log any errors [ds] */
|
|
96
108
|
} catch (e) {
|
|
97
109
|
console.warn(`[devsplain] Warning: post-commit hook script failed: ${e.message}`);
|
|
98
110
|
}
|
package/package.json
CHANGED