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.
- package/.claude/commands/recover-context.md +125 -68
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<!-- @description Recovers conversation context from the session transcript after compaction. -->
|
|
2
|
-
<!-- @version
|
|
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:
|
|
19
|
+
## Step 2: List candidate transcript files
|
|
20
20
|
|
|
21
|
-
|
|
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 -
|
|
24
|
+
ls -t ~/.claude/projects/*/*.jsonl 2>/dev/null | head -20
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
-
If no
|
|
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:
|
|
29
|
+
## Step 3: Identify which files to parse (lazy probing)
|
|
30
30
|
|
|
31
|
-
|
|
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
|
-
|
|
93
|
+
minutes = int('$MINUTES')
|
|
94
|
+
cutoff = datetime.now(timezone.utc) - timedelta(minutes=minutes)
|
|
40
95
|
|
|
41
|
-
|
|
96
|
+
files = '''$FILES_TO_PARSE'''.strip().splitlines()
|
|
42
97
|
|
|
43
98
|
results = []
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
line
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
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",
|