claude-code-autoconfig 1.0.141 → 1.0.142

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.
@@ -1,5 +1,5 @@
1
1
  <!-- @description Recovers conversation context from the session transcript after compaction. -->
2
- <!-- @version 3 -->
2
+ <!-- @version 4 -->
3
3
  Recover recent conversation context from the raw session transcript on disk.
4
4
 
5
5
  Usage:
@@ -16,109 +16,166 @@ The arguments are: $ARGUMENTS
16
16
  - Strip the leading `-` from the number and treat it as the number of minutes to look back
17
17
  - Check if `--show` flag is present
18
18
 
19
- ## Step 2: Find the transcript file
19
+ ## Step 2: List candidate transcript files
20
20
 
21
- Find the current session's transcript by looking for the most recently modified `.jsonl` file in the Claude projects directory:
21
+ List all `.jsonl` transcript files sorted by most recently modified:
22
22
 
23
23
  ```bash
24
- ls -t ~/.claude/projects/*/*.jsonl 2>/dev/null | head -1
24
+ ls -t ~/.claude/projects/*/*.jsonl 2>/dev/null | head -20
25
25
  ```
26
26
 
27
- If no transcript is found, tell the user and stop.
27
+ If no transcripts are found, tell the user and stop. Store the list as `$TRANSCRIPT_FILES` (one path per line).
28
28
 
29
- ## Step 3: Extract conversation context
29
+ ## Step 3: Identify which files to parse (lazy probing)
30
30
 
31
- 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:
31
+ For each file in `$TRANSCRIPT_FILES` (starting from most recent), probe its time range by reading only the **first and last timestamp** — do NOT parse the full file yet. Run this script, substituting `$MINUTES` and `$TRANSCRIPT_FILES`:
32
+
33
+ ```bash
34
+ python3 -c "
35
+ import json, sys
36
+ from datetime import datetime, timezone, timedelta
37
+
38
+ minutes = int('$MINUTES')
39
+ cutoff = datetime.now(timezone.utc) - timedelta(minutes=minutes)
40
+
41
+ files = '''$TRANSCRIPT_FILES'''.strip().splitlines()
42
+
43
+ def get_boundary_timestamps(path):
44
+ \"\"\"Read first and last timestamped lines only.\"\"\"
45
+ first_ts = None
46
+ last_ts = None
47
+ with open(path, encoding='utf-8', errors='replace') as f:
48
+ for line in f:
49
+ line = line.strip()
50
+ if not line:
51
+ continue
52
+ try:
53
+ obj = json.loads(line)
54
+ except:
55
+ continue
56
+ ts = obj.get('timestamp')
57
+ if not ts:
58
+ continue
59
+ parsed = datetime.fromisoformat(ts.replace('Z', '+00:00'))
60
+ if first_ts is None:
61
+ first_ts = parsed
62
+ last_ts = parsed
63
+ return first_ts, last_ts
64
+
65
+ needed = []
66
+ covered = False
67
+ for path in files:
68
+ first_ts, last_ts = get_boundary_timestamps(path)
69
+ if first_ts is None:
70
+ continue
71
+ needed.append(path)
72
+ # If this file's earliest timestamp is before our cutoff, we have enough files
73
+ if first_ts <= cutoff:
74
+ covered = True
75
+ break
76
+
77
+ for p in needed:
78
+ print(p)
79
+ "
80
+ ```
81
+
82
+ Store the output as `$FILES_TO_PARSE` — these are the only files that need full parsing.
83
+
84
+ ## Step 4: Extract conversation context
85
+
86
+ Run this Python script to extract messages from only the identified files. Substitute `$MINUTES` and `$FILES_TO_PARSE`:
32
87
 
33
88
  ```bash
34
89
  python3 -c "
35
90
  import json, os, sys, tempfile
36
91
  from datetime import datetime, timezone, timedelta
37
92
 
38
- minutes = '$MINUTES'
39
- path = '$TRANSCRIPT_PATH'
93
+ minutes = int('$MINUTES')
94
+ cutoff = datetime.now(timezone.utc) - timedelta(minutes=minutes)
40
95
 
41
- cutoff = datetime.now(timezone.utc) - timedelta(minutes=int(minutes))
96
+ files = '''$FILES_TO_PARSE'''.strip().splitlines()
42
97
 
43
98
  results = []
44
- with open(path, encoding='utf-8', errors='replace') as f:
45
- for line in f:
46
- line = line.strip()
47
- if not line:
48
- continue
49
- try:
50
- obj = json.loads(line)
51
- except:
52
- continue
53
-
54
- t = obj.get('type')
55
- if t not in ('user', 'assistant'):
56
- continue
57
-
58
- ts = obj.get('timestamp')
59
- if not ts:
60
- continue
61
-
62
- # Parse timestamp
63
- parsed_ts = datetime.fromisoformat(ts.replace('Z', '+00:00'))
64
- if parsed_ts < cutoff:
65
- continue
66
-
67
- parent = obj.get('parentUuid', '')
68
- msg = obj.get('message', {})
69
-
70
- # Extract text content
71
- text = ''
72
- if t == 'user':
73
- content = msg.get('content', '')
74
- if isinstance(content, str):
75
- text = content
76
- elif isinstance(content, list):
77
- # Skip tool_result messages
78
- if any(isinstance(c, dict) and c.get('type') == 'tool_result' for c in content):
79
- continue
80
- text = ' '.join(c.get('text', '') for c in content if isinstance(c, dict) and c.get('type') == 'text')
81
- elif t == 'assistant':
82
- content = msg.get('content', [])
83
- if isinstance(content, list):
84
- texts = [c.get('text', '') for c in content if isinstance(c, dict) and c.get('type') == 'text']
85
- text = '\n'.join(texts)
86
-
87
- if not text.strip():
88
- continue
89
-
90
- results.append({
91
- 'parentUuid': parent,
92
- 'type': t,
93
- 'timestamp': ts,
94
- 'text': text.strip()
95
- })
99
+ for path in files:
100
+ with open(path, encoding='utf-8', errors='replace') as f:
101
+ for line in f:
102
+ line = line.strip()
103
+ if not line:
104
+ continue
105
+ try:
106
+ obj = json.loads(line)
107
+ except:
108
+ continue
109
+
110
+ t = obj.get('type')
111
+ if t not in ('user', 'assistant'):
112
+ continue
113
+
114
+ ts = obj.get('timestamp')
115
+ if not ts:
116
+ continue
117
+
118
+ parsed_ts = datetime.fromisoformat(ts.replace('Z', '+00:00'))
119
+ if parsed_ts < cutoff:
120
+ continue
121
+
122
+ parent = obj.get('parentUuid', '')
123
+ msg = obj.get('message', {})
124
+
125
+ text = ''
126
+ if t == 'user':
127
+ content = msg.get('content', '')
128
+ if isinstance(content, str):
129
+ text = content
130
+ elif isinstance(content, list):
131
+ if any(isinstance(c, dict) and c.get('type') == 'tool_result' for c in content):
132
+ continue
133
+ text = ' '.join(c.get('text', '') for c in content if isinstance(c, dict) and c.get('type') == 'text')
134
+ elif t == 'assistant':
135
+ content = msg.get('content', [])
136
+ if isinstance(content, list):
137
+ texts = [c.get('text', '') for c in content if isinstance(c, dict) and c.get('type') == 'text']
138
+ text = '\n'.join(texts)
139
+
140
+ if not text.strip():
141
+ continue
142
+
143
+ results.append({
144
+ 'parentUuid': parent,
145
+ 'type': t,
146
+ 'timestamp': ts,
147
+ 'text': text.strip()
148
+ })
149
+
150
+ # Sort by timestamp across all files
151
+ results.sort(key=lambda r: r['timestamp'])
96
152
 
97
153
  # Write to temp file
98
154
  tmp = os.path.join(tempfile.gettempdir(), 'recovered-context.json')
99
155
  with open(tmp, 'w', encoding='utf-8') as f:
100
156
  json.dump(results, f, indent=2, ensure_ascii=False)
101
157
 
102
- # Output stats
103
158
  total_chars = sum(len(r['text']) for r in results)
104
- est_tokens = total_chars // 4 # ~4 chars per token for English
159
+ est_tokens = total_chars // 4
160
+ sessions = len(files)
105
161
  print(json.dumps({
106
162
  'messages': len(results),
107
163
  'tokens': est_tokens,
164
+ 'sessions': sessions,
108
165
  'tempFile': tmp
109
166
  }))
110
167
  "
111
168
  ```
112
169
 
113
- ## Step 4: Confirm recovery
170
+ ## Step 5: Confirm recovery
114
171
 
115
172
  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.
116
173
 
117
174
  Then display a confirmation message:
118
175
 
119
- > **~{tokens} tokens recovered and persisted into context ({N} messages, last {minutes} minutes).**
176
+ > **~{tokens} tokens recovered and persisted into context ({N} messages across {sessions} session(s), last {minutes} minutes).**
120
177
 
121
- ## Step 5: Open transcript (if --show flag)
178
+ ## Step 6: Open transcript (if --show flag)
122
179
 
123
180
  If the `--show` flag was provided, open the temp file in the default editor. Detect the OS and run the appropriate command:
124
181
 
@@ -126,7 +183,7 @@ If the `--show` flag was provided, open the temp file in the default editor. Det
126
183
  - **macOS:** `open "$TEMP_FILE"`
127
184
  - **Linux:** `xdg-open "$TEMP_FILE"`
128
185
 
129
- ## Step 6: Resume work
186
+ ## Step 7: Resume work
130
187
 
131
188
  Tell the user:
132
189
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-code-autoconfig",
3
- "version": "1.0.141",
3
+ "version": "1.0.142",
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",