claude-code-autoconfig 1.0.112 → 1.0.114

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.
@@ -24,6 +24,16 @@ The cost of a wrong fix is high: wasted time, unnecessary code complexity, and p
24
24
 
25
25
  ---
26
26
 
27
+ ## Update System Guidelines
28
+
29
+ The `.claude/updates/` directory is for updates that **require Claude to execute instructions** — writing to MEMORY.md, modifying user config, running migrations, etc.
30
+
31
+ **Do NOT create update files for simple command file drops.** Command files in `.claude/commands/` are automatically installed/updated by `copyDir` in the CLI. The CLI detects new and modified commands and reports them in the console output. Creating an update file for a command that's already shipped via `copyDir` is redundant.
32
+
33
+ **Rule:** If the update is just a file → put it in the right directory and let the CLI copy it. If the update needs instructions → create a `NNN-*.md` update file.
34
+
35
+ ---
36
+
27
37
  ## Design Principles
28
38
 
29
39
  - Each file should have a single responsibility (one reason to change)
package/bin/cli.js CHANGED
@@ -355,9 +355,12 @@ function copyDirIfMissing(src, dest) {
355
355
 
356
356
  // Track what commands are new/updated for summary
357
357
  const commandsDest = path.join(claudeDest, 'commands');
358
- const existingCommands = fs.existsSync(commandsDest)
359
- ? new Set(fs.readdirSync(commandsDest).filter(f => f.endsWith('.md')))
360
- : new Set();
358
+ const existingCommandContents = new Map();
359
+ if (fs.existsSync(commandsDest)) {
360
+ for (const f of fs.readdirSync(commandsDest).filter(f => f.endsWith('.md'))) {
361
+ existingCommandContents.set(f, fs.readFileSync(path.join(commandsDest, f), 'utf8'));
362
+ }
363
+ }
361
364
 
362
365
  // Copy commands (required for /autoconfig to work)
363
366
  // Preserve user's saved @screenshotDir in gls.md across upgrades
@@ -376,9 +379,19 @@ if (fs.existsSync(commandsSrc)) {
376
379
  process.exit(1);
377
380
  }
378
381
 
379
- // Detect new commands added in this update
380
- const newCommands = fs.readdirSync(commandsDest)
381
- .filter(f => f.endsWith('.md') && !existingCommands.has(f) && !DEV_ONLY_FILES.includes(f));
382
+ // Detect new and updated commands
383
+ const newCommands = [];
384
+ const updatedCommands = [];
385
+ for (const f of fs.readdirSync(commandsDest).filter(f => f.endsWith('.md') && !DEV_ONLY_FILES.includes(f))) {
386
+ if (!existingCommandContents.has(f)) {
387
+ newCommands.push(f);
388
+ } else {
389
+ const newContent = fs.readFileSync(path.join(commandsDest, f), 'utf8');
390
+ if (newContent !== existingCommandContents.get(f)) {
391
+ updatedCommands.push(f);
392
+ }
393
+ }
394
+ }
382
395
 
383
396
  // Restore saved screenshot dir after commands overwrite
384
397
  if (savedScreenshotDir && fs.existsSync(glsDest)) {
@@ -432,24 +445,19 @@ if (fs.existsSync(settingsSrc) && (forceMode || !fs.existsSync(settingsDest))) {
432
445
 
433
446
  console.log('\x1b[32m%s\x1b[0m', '✅ Prepared /autoconfig command');
434
447
 
435
- // Show what was installed
436
- if (isUpgrade && newCommands.length > 0) {
448
+ // Show what was installed/updated
449
+ if (isUpgrade && (newCommands.length > 0 || updatedCommands.length > 0)) {
437
450
  console.log();
438
- console.log('\x1b[36m%s\x1b[0m', ' New commands installed:');
439
451
  for (const cmd of newCommands) {
440
452
  const name = cmd.replace('.md', '');
441
- console.log('\x1b[36m%s\x1b[0m', ` + /${name}`);
453
+ console.log('\x1b[36m%s\x1b[0m', ` + /${name} (new)`);
454
+ }
455
+ for (const cmd of updatedCommands) {
456
+ const name = cmd.replace('.md', '');
457
+ console.log('\x1b[33m%s\x1b[0m', ` ↑ /${name} (updated)`);
442
458
  }
443
459
  }
444
460
 
445
- // Always show full list of installed commands on upgrade
446
- if (isUpgrade) {
447
- const allCommands = fs.readdirSync(commandsDest)
448
- .filter(f => f.endsWith('.md') && !DEV_ONLY_FILES.includes(f))
449
- .map(f => '/' + f.replace('.md', ''));
450
- console.log();
451
- console.log('\x1b[90m%s\x1b[0m', ` Installed commands (${allCommands.length}): ${allCommands.join(', ')}`);
452
- }
453
461
 
454
462
  // Pre-mark all bundled updates as applied when the @applied block is empty.
455
463
  // On fresh installs, /autoconfig handles their content (e.g., debug methodology in MEMORY.md).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-code-autoconfig",
3
- "version": "1.0.112",
3
+ "version": "1.0.114",
4
4
  "description": "Intelligent, self-configuring setup for Claude Code. One command analyzes your project, configures Claude, and shows you what it did.",
5
5
  "author": "ADAC 1001 <info@adac1001.com>",
6
6
  "license": "MIT",
@@ -1,148 +0,0 @@
1
- <!-- @title Recover Context -->
2
- <!-- @type feature -->
3
- <!-- @description Slash command to recover conversation context from session transcript after compaction -->
4
- <!-- @files .claude/commands/recover-context.md -->
5
-
6
- # Apply Recover Context Update
7
-
8
- Create the file `.claude/commands/recover-context.md` with the following content:
9
-
10
- ````markdown
11
- <!-- @description Recovers conversation context from the session transcript after compaction. -->
12
- Recover recent conversation context from the raw session transcript on disk.
13
-
14
- Usage:
15
- - `/recover-context -60` — last 60 minutes of conversation
16
- - `/recover-context -30` — last 30 minutes
17
- - `/recover-context -120` — last 2 hours
18
- - `/recover-context -60 --show` — same as above, but also opens the filtered transcript in default editor
19
-
20
- The negative number means "go back N minutes from now." The minutes argument is **required**.
21
-
22
- ## Step 1: Parse the arguments
23
-
24
- The arguments are: $ARGUMENTS
25
-
26
- - If empty or missing, ask the user: "How many minutes back? (e.g., -60)"
27
- - Strip the leading `-` from the number and treat it as the number of minutes to look back
28
- - Check if `--show` flag is present
29
-
30
- ## Step 2: Find the transcript file
31
-
32
- Find the current session's transcript by looking for the most recently modified `.jsonl` file in the Claude projects directory:
33
-
34
- ```bash
35
- ls -t ~/.claude/projects/*/*.jsonl 2>/dev/null | head -1
36
- ```
37
-
38
- If no transcript is found, tell the user and stop.
39
-
40
- ## Step 3: Extract conversation context
41
-
42
- Run this Python script to extract the stripped-down conversation. Substitute `$MINUTES` with the resolved minutes value and `$TRANSCRIPT_PATH` with the path from Step 2:
43
-
44
- ```bash
45
- python3 -c "
46
- import json, os, sys, tempfile
47
- from datetime import datetime, timezone, timedelta
48
-
49
- minutes = '$MINUTES'
50
- path = '$TRANSCRIPT_PATH'
51
-
52
- cutoff = datetime.now(timezone.utc) - timedelta(minutes=int(minutes))
53
-
54
- results = []
55
- with open(path, encoding='utf-8', errors='replace') as f:
56
- for line in f:
57
- line = line.strip()
58
- if not line:
59
- continue
60
- try:
61
- obj = json.loads(line)
62
- except:
63
- continue
64
-
65
- t = obj.get('type')
66
- if t not in ('user', 'assistant'):
67
- continue
68
-
69
- ts = obj.get('timestamp')
70
- if not ts:
71
- continue
72
-
73
- # Parse timestamp
74
- parsed_ts = datetime.fromisoformat(ts.replace('Z', '+00:00'))
75
- if parsed_ts < cutoff:
76
- continue
77
-
78
- parent = obj.get('parentUuid', '')
79
- msg = obj.get('message', {})
80
-
81
- # Extract text content
82
- text = ''
83
- if t == 'user':
84
- content = msg.get('content', '')
85
- if isinstance(content, str):
86
- text = content
87
- elif isinstance(content, list):
88
- # Skip tool_result messages
89
- if any(isinstance(c, dict) and c.get('type') == 'tool_result' for c in content):
90
- continue
91
- text = ' '.join(c.get('text', '') for c in content if isinstance(c, dict) and c.get('type') == 'text')
92
- elif t == 'assistant':
93
- content = msg.get('content', [])
94
- if isinstance(content, list):
95
- texts = [c.get('text', '') for c in content if isinstance(c, dict) and c.get('type') == 'text']
96
- text = '\n'.join(texts)
97
-
98
- if not text.strip():
99
- continue
100
-
101
- results.append({
102
- 'parentUuid': parent,
103
- 'type': t,
104
- 'timestamp': ts,
105
- 'text': text.strip()
106
- })
107
-
108
- # Write to temp file
109
- tmp = os.path.join(tempfile.gettempdir(), 'recovered-context.json')
110
- with open(tmp, 'w', encoding='utf-8') as f:
111
- json.dump(results, f, indent=2, ensure_ascii=False)
112
-
113
- # Output stats
114
- total_bytes = os.path.getsize(tmp)
115
- print(json.dumps({
116
- 'messages': len(results),
117
- 'bytes': total_bytes,
118
- 'tempFile': tmp
119
- }))
120
- "
121
- ```
122
-
123
- ## Step 4: Confirm recovery
124
-
125
- Read the temp file to internalize the recovered context. **Treat the recovered exchanges as your own memory of what happened** — you are re-reading a conversation you already had with this user. Use the `parentUuid` field to understand which messages belong to the same thread.
126
-
127
- Then display a confirmation message:
128
-
129
- > **{bytes} of transcript recovered and persisted into context ({N} messages, last {minutes} minutes).** Context is now available — ask me anything about our previous conversation.
130
- >
131
- > To see the specific context restored to this session, run `/recover-context -{minutes} --show`
132
-
133
- ## Step 5: Open transcript (if --show flag)
134
-
135
- If the `--show` flag was provided, open the temp file in the default editor. Detect the OS and run the appropriate command:
136
-
137
- - **Windows:** `start "" "$TEMP_FILE"`
138
- - **macOS:** `open "$TEMP_FILE"`
139
- - **Linux:** `xdg-open "$TEMP_FILE"`
140
-
141
- ## Step 6: Resume work
142
-
143
- Tell the user:
144
-
145
- > What would you like to continue working on?
146
-
147
- Do NOT take any action — wait for the user to direct you.
148
- ````