@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.
- package/.claude-plugin/plugin.json +4 -0
- package/commands/docs-quick-update.md +406 -0
- package/hooks/auto_approve_safe.rules.json +3 -0
- package/install.js +88 -20
- package/package.json +1 -1
- package/uninstall.js +1 -0
|
@@ -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
|
|
43
|
+
function logUpdate(message) {
|
|
38
44
|
log(` ↻ ${message}`, 'blue');
|
|
39
45
|
}
|
|
40
46
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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
|
|
114
|
-
if (
|
|
115
|
-
stats.
|
|
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 + '.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
180
|
-
log(`
|
|
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