@torka/claude-qol 0.2.1 → 0.4.0

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.
@@ -6,6 +6,10 @@
6
6
  {
7
7
  "name": "optimize-auto-approve-hook",
8
8
  "description": "Analyze auto-approve decisions to identify safe patterns for auto-allowing and validate existing rules"
9
+ },
10
+ {
11
+ "name": "docs-quick-update",
12
+ "description": "Detect code changes and suggest targeted documentation updates"
9
13
  }
10
14
  ],
11
15
  "skills": [
@@ -0,0 +1,406 @@
1
+ ---
2
+ description: 'Detect code changes and suggest targeted documentation updates'
3
+ ---
4
+
5
+ # Docs Quick Update
6
+
7
+ You are a documentation maintenance assistant. Analyze code changes and suggest targeted documentation updates for affected docs.
8
+
9
+ ## Overview
10
+
11
+ This command intelligently detects code changes and proposes documentation updates:
12
+ 1. Detects change scope (uncommitted, branch diff, or recent commits)
13
+ 2. Analyzes what changed (features, APIs, config, refactors)
14
+ 3. Finds docs that reference changed code
15
+ 4. Detects dead references (deleted/renamed code still mentioned)
16
+ 5. Generates an update plan with user confirmation
17
+
18
+ ## CLI Arguments
19
+
20
+ | Argument | Description |
21
+ |----------|-------------|
22
+ | (none) | Smart auto-detect: uncommitted → branch diff → recent commits |
23
+ | `--uncommitted` | Only analyze staged/unstaged changes |
24
+ | `--branch` | Compare current branch vs main |
25
+ | `--yolo` | Skip confirmation, apply all updates directly |
26
+
27
+ ## Target Documents (Auto-Detected)
28
+
29
+ The command scans for these doc locations:
30
+ - `./docs/` folder (if exists)
31
+ - `CLAUDE.md`
32
+ - `AGENTS.md`
33
+ - `README.md`
34
+
35
+ ---
36
+
37
+ ## Workflow Phases
38
+
39
+ <steps>
40
+
41
+ ### Phase 0: Pre-flight Checks
42
+
43
+ 1. **Verify git repository**
44
+ - Run `git rev-parse --is-inside-work-tree`
45
+ - If not a git repo: Stop with message "Not a git repository. This command requires git to detect changes."
46
+
47
+ 2. **Parse CLI arguments**
48
+ - Check if `$ARGUMENTS` contains `--branch`, `--uncommitted`, or `--yolo`
49
+ - Store flags for later use
50
+
51
+ 3. **Discover existing docs**
52
+ - Check for `./docs/` folder
53
+ - Check for `CLAUDE.md`, `AGENTS.md`, `README.md` at project root
54
+ - Build list of doc paths that exist
55
+ - If no docs found: Stop with message "No documentation files found. Expected: docs/, CLAUDE.md, AGENTS.md, or README.md"
56
+
57
+ 4. **Display pre-flight summary:**
58
+ ```
59
+ Docs Quick Update
60
+ =================
61
+ Flags: {--branch | --uncommitted | --yolo | auto-detect}
62
+ Docs found: {list of doc paths}
63
+ ```
64
+
65
+ ### Phase 1: Smart Change Detection
66
+
67
+ Determine which changes to analyze based on flags or auto-detection:
68
+
69
+ **If `--uncommitted` flag:**
70
+ - Use: `git diff HEAD` (staged + unstaged)
71
+ - Scope label: "Uncommitted changes"
72
+
73
+ **If `--branch` flag:**
74
+ - Detect main branch: `git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null || echo "main"`
75
+ - Use: `git diff {main}...HEAD`
76
+ - Scope label: "Branch diff vs {main}"
77
+
78
+ **If no flag (auto-detect):**
79
+ 1. Check for uncommitted changes: `git status --porcelain`
80
+ - If changes exist → use uncommitted scope
81
+ 2. Check if on feature branch: `git branch --show-current`
82
+ - If not main/master → suggest branch diff, ask user to confirm
83
+ 3. Fallback: use last 5 commits on main
84
+ - Use: `git diff HEAD~5...HEAD`
85
+ - Scope label: "Recent commits (last 5)"
86
+
87
+ **Display scope summary:**
88
+ ```
89
+ Change Scope
90
+ ============
91
+ Mode: {scope label}
92
+ Command: {git diff command used}
93
+
94
+ Proceed with this scope? [Y/n]
95
+ ```
96
+
97
+ Unless `--yolo` flag is set, use `AskUserQuestion` to confirm or let user override:
98
+ ```
99
+ header: "Scope"
100
+ question: "Use this change scope for doc analysis?"
101
+ options:
102
+ - label: "Yes, proceed"
103
+ description: "{scope label}"
104
+ - label: "Use uncommitted only"
105
+ description: "Staged and unstaged changes"
106
+ - label: "Use branch diff"
107
+ description: "All commits on current branch vs main"
108
+ ```
109
+
110
+ ### Phase 2: Analyze Changes
111
+
112
+ 1. **Run the git diff command** determined in Phase 1
113
+ - Capture full diff output
114
+
115
+ 2. **Parse diff output** and extract:
116
+ - Files added/modified/deleted
117
+ - Function/class names changed (look for `function`, `class`, `def`, `const`, `export`)
118
+ - File renames (look for `rename from`/`rename to` in diff)
119
+
120
+ 3. **Categorize changes:**
121
+
122
+ | Category | Detection Signals |
123
+ |----------|-------------------|
124
+ | New features | New files, new exports, new functions |
125
+ | API changes | Modified function signatures, changed exports |
126
+ | Config changes | Changes to `*.config.*`, `package.json`, env files |
127
+ | Refactors | Renamed files, moved code, internal changes |
128
+ | Deletions | Removed files, removed exports, removed functions |
129
+
130
+ 4. **Build change summary:**
131
+ ```
132
+ Change Analysis
133
+ ===============
134
+ Files changed: {count}
135
+
136
+ By category:
137
+ - New features: {list}
138
+ - API changes: {list}
139
+ - Config changes: {list}
140
+ - Refactors: {list}
141
+ - Deletions: {list}
142
+
143
+ Key identifiers affected:
144
+ - Functions: {list}
145
+ - Exports: {list}
146
+ - Files renamed: {old} → {new}
147
+ ```
148
+
149
+ ### Phase 3: Doc Discovery & Relevance
150
+
151
+ 1. **Read each discovered doc file**
152
+
153
+ 2. **For each doc, cross-reference with changes:**
154
+ - Search for mentions of changed file paths
155
+ - Search for mentions of changed function/class names
156
+ - Search for mentions of deleted/renamed identifiers
157
+
158
+ 3. **Detect dead references:**
159
+ - If doc mentions a deleted file → flag as dead reference
160
+ - If doc mentions a renamed file by old name → flag as dead reference
161
+ - If doc mentions a deleted function/export → flag as dead reference
162
+
163
+ 4. **Rank docs by update priority:**
164
+
165
+ | Priority | Criteria |
166
+ |----------|----------|
167
+ | HIGH | Contains dead references (deleted/renamed code) |
168
+ | MEDIUM | References files/functions that changed |
169
+ | LOW | General project docs that might need refresh |
170
+
171
+ 5. **Build relevance map:**
172
+ ```
173
+ Doc Relevance Analysis
174
+ ======================
175
+
176
+ [HIGH] README.md
177
+ - Dead reference: mentions deleted function `oldHelper()`
178
+ - Stale: references `src/utils.js` (renamed to `src/helpers.js`)
179
+
180
+ [MEDIUM] CLAUDE.md
181
+ - References modified: `install.js`
182
+ - May need update for new export `newFeature`
183
+
184
+ [LOW] docs/api.md
185
+ - General API docs, no direct references found
186
+ ```
187
+
188
+ ### Phase 4: Generate Update Plan
189
+
190
+ For each doc needing updates, generate specific recommendations:
191
+
192
+ 1. **For dead references:**
193
+ - Identify exact line/section
194
+ - Recommend: remove reference, or update to new name
195
+
196
+ 2. **For stale content:**
197
+ - Identify section that references changed code
198
+ - Summarize what changed and suggest update
199
+
200
+ 3. **Format update plan:**
201
+ ```
202
+ Documentation Update Plan
203
+ =========================
204
+
205
+ README.md (2 updates needed)
206
+ ├── [1] Remove reference to deleted `oldHelper()` function
207
+ │ Section: "API Reference"
208
+ │ Action: Remove or replace with new equivalent
209
+
210
+ └── [2] Update file path `src/utils.js` → `src/helpers.js`
211
+ Section: "Project Structure"
212
+ Action: Find/replace old path with new
213
+
214
+ CLAUDE.md (1 update needed)
215
+ └── [1] Document new export `newFeature`
216
+ Section: "Development Commands" or new section
217
+ Action: Add brief description of new functionality
218
+
219
+ Total: 3 updates across 2 files
220
+ ```
221
+
222
+ ### Phase 5: User Confirmation
223
+
224
+ **If `--yolo` flag:** Skip to Phase 6 (apply all updates)
225
+
226
+ **Otherwise:** Use `AskUserQuestion` with `multiSelect: true`:
227
+
228
+ ```
229
+ header: "Updates"
230
+ question: "Which documentation updates should be applied?"
231
+ multiSelect: true
232
+ options:
233
+ - label: "All updates"
234
+ description: "Apply all {N} recommended updates"
235
+ - label: "README.md updates"
236
+ description: "{N} updates: {brief summary}"
237
+ - label: "CLAUDE.md updates"
238
+ description: "{N} updates: {brief summary}"
239
+ - label: "Skip all"
240
+ description: "Exit without making changes"
241
+ ```
242
+
243
+ If user selects individual docs, confirm each update within that doc.
244
+
245
+ ### Phase 6: Apply Updates
246
+
247
+ For each approved update:
248
+
249
+ 1. **Read the target doc file**
250
+
251
+ 2. **Make the specific edit:**
252
+ - For dead reference removal: Delete the line/paragraph
253
+ - For path updates: Find/replace old path with new
254
+ - For new content: Add to appropriate section
255
+
256
+ 3. **Preserve doc structure:**
257
+ - Maintain existing headings and formatting
258
+ - Only modify relevant sections
259
+ - Don't reorganize or reformat unrelated content
260
+
261
+ 4. **Track changes made:**
262
+ - Record each edit for summary
263
+
264
+ ### Phase 7: Completion Summary
265
+
266
+ Display final summary:
267
+
268
+ ```
269
+ ================================
270
+ DOCUMENTATION UPDATE COMPLETE
271
+ ================================
272
+
273
+ Changes Applied:
274
+ - README.md: 2 updates
275
+ ✓ Removed dead reference to `oldHelper()`
276
+ ✓ Updated path `src/utils.js` → `src/helpers.js`
277
+
278
+ - CLAUDE.md: 1 update
279
+ ✓ Added documentation for `newFeature` export
280
+
281
+ Suggested Commit Message:
282
+ docs: update documentation for recent code changes
283
+
284
+ Files Changed:
285
+ - README.md
286
+ - CLAUDE.md
287
+
288
+ To Revert:
289
+ git checkout README.md CLAUDE.md
290
+ ```
291
+
292
+ </steps>
293
+
294
+ ---
295
+
296
+ ## Safety Rules
297
+
298
+ **CRITICAL - These rules must NEVER be violated:**
299
+
300
+ 1. **NEVER edit docs without user confirmation** (unless `--yolo` flag is set)
301
+ 2. **Always show the update plan** before applying changes (even with `--yolo`, show summary after)
302
+ 3. **Preserve doc structure** - only update relevant sections, don't reorganize
303
+ 4. **Don't add unverifiable content** - only document what can be confirmed from code changes
304
+ 5. **Keep edits minimal** - fix specific issues, don't rewrite entire sections
305
+ 6. **Always provide revert instructions** - user should be able to undo easily
306
+
307
+ ---
308
+
309
+ ## Change Category Detection
310
+
311
+ | Category | File Patterns | Code Patterns |
312
+ |----------|---------------|---------------|
313
+ | New features | New files in `src/`, `lib/` | `export`, new functions |
314
+ | API changes | Modified exports, signatures | `function.*\(`, `export {` |
315
+ | Config changes | `*.config.*`, `package.json` | JSON keys, env vars |
316
+ | Refactors | File renames, moves | Import path changes |
317
+ | Deletions | Removed files | Removed exports/functions |
318
+
319
+ ---
320
+
321
+ ## Error Handling
322
+
323
+ | Scenario | Action |
324
+ |----------|--------|
325
+ | Not a git repo | Stop with clear error message |
326
+ | No docs found | Stop with message listing expected locations |
327
+ | No changes detected | Stop with message "No changes found in the specified scope" |
328
+ | Empty diff | Report "No code changes to analyze" |
329
+ | Doc file unreadable | Skip that file, warn user |
330
+ | Git command fails | Report error with the specific command |
331
+
332
+ ---
333
+
334
+ ## Example Session
335
+
336
+ ```
337
+ User: /docs-quick-update
338
+
339
+ Claude: Docs Quick Update
340
+ =================
341
+ Flags: auto-detect
342
+ Docs found: README.md, CLAUDE.md, docs/
343
+
344
+ Change Scope
345
+ ============
346
+ Mode: Uncommitted changes (auto-detected)
347
+ Files with changes: 3
348
+
349
+ [Analyzing changes...]
350
+
351
+ Change Analysis
352
+ ===============
353
+ Files changed: 3
354
+ - Modified: src/install.js
355
+ - Renamed: src/utils.js → src/helpers.js
356
+ - Deleted: src/legacy.js
357
+
358
+ Documentation Update Plan
359
+ =========================
360
+
361
+ README.md (2 updates needed)
362
+ ├── [1] Update file path `src/utils.js` → `src/helpers.js`
363
+ └── [2] Remove reference to deleted `src/legacy.js`
364
+
365
+ CLAUDE.md (1 update needed)
366
+ └── [1] Update "Key Files" section with renamed file
367
+
368
+ [Question] Which documentation updates should be applied?
369
+ > All updates
370
+
371
+ Applying updates...
372
+
373
+ DOCUMENTATION UPDATE COMPLETE
374
+ =============================
375
+ Changes Applied:
376
+ - README.md: 2 updates ✓
377
+ - CLAUDE.md: 1 update ✓
378
+
379
+ Suggested Commit Message:
380
+ docs: update documentation for recent code changes
381
+ ```
382
+
383
+ ---
384
+
385
+ ## Example with --yolo Flag
386
+
387
+ ```
388
+ User: /docs-quick-update --yolo
389
+
390
+ Claude: Docs Quick Update (YOLO mode)
391
+ =====================================
392
+ Skipping confirmations, applying all updates directly...
393
+
394
+ [Auto-detected scope: uncommitted changes]
395
+ [Found 2 updates across 2 docs]
396
+ [Applying all updates...]
397
+
398
+ DOCUMENTATION UPDATE COMPLETE
399
+ =============================
400
+ Changes Applied:
401
+ - README.md: 1 update ✓
402
+ - CLAUDE.md: 1 update ✓
403
+
404
+ Suggested Commit Message:
405
+ docs: update documentation for recent code changes
406
+ ```
@@ -88,6 +88,8 @@
88
88
 
89
89
  "^gh\\s+(pr|issue|repo|release|workflow|run|api|auth)(\\s+.*)?$",
90
90
 
91
+ "^git\\s+add\\s+[^|;&]+\\s*&&\\s*git\\s+commit\\s+-m\\s+",
92
+
91
93
  "^git\\s+rev-parse(\\s+.*)?$",
92
94
  "^git\\s+stash(\\s+--include-untracked|\\s+--all|\\s+-u|\\s+-a)?(\\s+-m\\s+.*)?$",
93
95
  "^git\\s+rev-list\\s+[^|;&]+$",
@@ -106,6 +108,7 @@
106
108
  "^npm\\s+run\\s+clean(\\s+.*)?$",
107
109
  "^npm\\s+(dedupe|cache\\s+clean)(\\s+.*)?$",
108
110
  "^curl\\s+(-[sIo]+\\s+)*https?://localhost[:/][^|;&]*$",
111
+ "^curl\\s+[^|;&]*http://localhost[:/][^|;&]*$",
109
112
  "^pgrep\\s+"
110
113
  ],
111
114
 
package/install.js CHANGED
@@ -13,11 +13,17 @@ const colors = {
13
13
  green: '\x1b[32m',
14
14
  yellow: '\x1b[33m',
15
15
  blue: '\x1b[34m',
16
+ cyan: '\x1b[36m',
16
17
  red: '\x1b[31m',
17
18
  reset: '\x1b[0m',
18
19
  bold: '\x1b[1m',
19
20
  };
20
21
 
22
+ // Files that should not be overwritten if user has customized them
23
+ const PROTECTED_FILES = [
24
+ 'auto_approve_safe.rules.json'
25
+ ];
26
+
21
27
  function log(message, color = 'reset') {
22
28
  console.log(`${colors[color]}${message}${colors.reset}`);
23
29
  }
@@ -34,21 +40,16 @@ function logError(message) {
34
40
  log(` ✗ ${message}`, 'red');
35
41
  }
36
42
 
37
- function logUpdated(message) {
43
+ function logUpdate(message) {
38
44
  log(` ↻ ${message}`, 'blue');
39
45
  }
40
46
 
41
- /**
42
- * Check if two files have identical content
43
- */
44
- function filesAreIdentical(file1, file2) {
45
- try {
46
- const content1 = fs.readFileSync(file1);
47
- const content2 = fs.readFileSync(file2);
48
- return content1.equals(content2);
49
- } catch {
50
- return false;
51
- }
47
+ function logBackup(message) {
48
+ log(` ⤷ ${message}`, 'cyan');
49
+ }
50
+
51
+ function logPreserve(message) {
52
+ log(` ★ ${message}`, 'yellow');
52
53
  }
53
54
 
54
55
  /**
@@ -87,6 +88,32 @@ function getTargetBase() {
87
88
  return path.join(process.env.INIT_CWD || process.cwd(), '.claude');
88
89
  }
89
90
 
91
+ /**
92
+ * Ensure entries exist in a .gitignore file (append if missing)
93
+ */
94
+ function ensureGitignoreEntries(gitignorePath, entries, header) {
95
+ let existingContent = '';
96
+ if (fs.existsSync(gitignorePath)) {
97
+ existingContent = fs.readFileSync(gitignorePath, 'utf8');
98
+ }
99
+
100
+ const existingLines = new Set(
101
+ existingContent.split('\n').map(line => line.trim()).filter(Boolean)
102
+ );
103
+
104
+ const missingEntries = entries.filter(entry => !existingLines.has(entry));
105
+
106
+ if (missingEntries.length > 0) {
107
+ const newContent = existingContent.trimEnd() +
108
+ (existingContent ? '\n\n' : '') +
109
+ `# ${header}\n` +
110
+ missingEntries.join('\n') + '\n';
111
+ fs.writeFileSync(gitignorePath, newContent);
112
+ return missingEntries.length;
113
+ }
114
+ return 0;
115
+ }
116
+
90
117
  /**
91
118
  * Recursively copy directory contents
92
119
  */
@@ -110,17 +137,31 @@ function copyDirRecursive(src, dest, stats) {
110
137
  copyDirRecursive(srcPath, destPath, stats);
111
138
  } else {
112
139
  if (fs.existsSync(destPath)) {
113
- // Check if files are identical
114
- if (filesAreIdentical(srcPath, destPath)) {
115
- stats.skipped.push(destPath);
140
+ // Check if this is a protected user config file
141
+ if (PROTECTED_FILES.includes(entry.name)) {
142
+ stats.preserved.push(destPath);
143
+ logPreserve(`Preserved (user config): ${path.relative(stats.targetBase, destPath)}`);
144
+ continue;
145
+ }
146
+
147
+ // Check if files are identical (inline comparison)
148
+ const srcContent = fs.readFileSync(srcPath);
149
+ const destContent = fs.readFileSync(destPath);
150
+ const filesAreIdentical = srcContent.equals(destContent);
151
+
152
+ if (filesAreIdentical) {
153
+ stats.unchanged.push(destPath);
116
154
  logSkip(`Unchanged: ${path.relative(stats.targetBase, destPath)}`);
117
155
  } else {
118
156
  // Backup existing file, then replace
119
- const backupPath = destPath + '.bak';
157
+ const backupPath = destPath + '.backup';
120
158
  fs.copyFileSync(destPath, backupPath);
159
+ stats.backups.push(backupPath);
160
+ logBackup(`Backup: ${path.relative(stats.targetBase, backupPath)}`);
161
+
121
162
  fs.copyFileSync(srcPath, destPath);
122
163
  stats.updated.push(destPath);
123
- logUpdated(`Updated (backup: .bak): ${path.relative(stats.targetBase, destPath)}`);
164
+ logUpdate(`Updated: ${path.relative(stats.targetBase, destPath)}`);
124
165
  }
125
166
  } else {
126
167
  fs.copyFileSync(srcPath, destPath);
@@ -148,10 +189,35 @@ function install() {
148
189
  fs.mkdirSync(targetBase, { recursive: true });
149
190
  }
150
191
 
192
+ // Ensure gitignore entries for package-installed files
193
+ const gitignorePath = path.join(targetBase, '.gitignore');
194
+ const gitignoreEntries = [
195
+ 'scripts/auto_approve_safe.py',
196
+ 'scripts/auto_approve_safe.rules.json',
197
+ 'scripts/context-monitor.py',
198
+ 'scripts/__pycache__/',
199
+ 'commands/optimize-auto-approve-hook.md',
200
+ 'commands/docs-quick-update.md',
201
+ 'skills/nash/',
202
+ 'auto_approve_safe.decisions.jsonl',
203
+ 'auto_approve_safe.decisions.archived.jsonl',
204
+ '*.backup',
205
+ ];
206
+ const addedCount = ensureGitignoreEntries(
207
+ gitignorePath,
208
+ gitignoreEntries,
209
+ 'Installed by @torka/claude-qol'
210
+ );
211
+ if (addedCount > 0) {
212
+ log(` Updated .gitignore (added ${addedCount} entries)`, 'green');
213
+ }
214
+
151
215
  const stats = {
152
216
  copied: [],
153
217
  updated: [],
154
- skipped: [],
218
+ unchanged: [],
219
+ preserved: [],
220
+ backups: [],
155
221
  targetBase,
156
222
  };
157
223
 
@@ -176,8 +242,10 @@ function install() {
176
242
  // Summary
177
243
  log('\n' + colors.bold + '📊 Installation Summary' + colors.reset);
178
244
  log(` Files copied: ${stats.copied.length}`, 'green');
179
- log(` Files updated (backups created): ${stats.updated.length}`, 'blue');
180
- log(` Files unchanged: ${stats.skipped.length}`, 'yellow');
245
+ log(` Files updated: ${stats.updated.length}`, 'blue');
246
+ log(` Backups created: ${stats.backups.length}`, 'cyan');
247
+ log(` Files unchanged: ${stats.unchanged.length}`, 'yellow');
248
+ log(` Files preserved (user config): ${stats.preserved.length}`, 'yellow');
181
249
 
182
250
  // Post-install instructions
183
251
  log('\n' + colors.bold + '📝 Configuration Required' + colors.reset);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@torka/claude-qol",
3
- "version": "0.2.1",
3
+ "version": "0.4.0",
4
4
  "description": "Claude Code quality-of-life improvements: auto-approve hooks, context monitoring, and status line enhancements",
5
5
  "keywords": [
6
6
  "claude-code",
package/uninstall.js CHANGED
@@ -42,6 +42,7 @@ const INSTALLED_FILES = {
42
42
  ],
43
43
  commands: [
44
44
  'optimize-auto-approve-hook.md',
45
+ 'docs-quick-update.md',
45
46
  ],
46
47
  };
47
48