claude-code-tools 1.4.1__py3-none-any.whl → 1.4.6__py3-none-any.whl
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.
- claude_code_tools/__init__.py +1 -1
- claude_code_tools/export_session.py +85 -6
- claude_code_tools/search_index.py +36 -6
- {claude_code_tools-1.4.1.dist-info → claude_code_tools-1.4.6.dist-info}/METADATA +3 -4
- {claude_code_tools-1.4.1.dist-info → claude_code_tools-1.4.6.dist-info}/RECORD +9 -8
- docs/linked-in-20260102.md +32 -0
- {claude_code_tools-1.4.1.dist-info → claude_code_tools-1.4.6.dist-info}/WHEEL +0 -0
- {claude_code_tools-1.4.1.dist-info → claude_code_tools-1.4.6.dist-info}/entry_points.txt +0 -0
- {claude_code_tools-1.4.1.dist-info → claude_code_tools-1.4.6.dist-info}/licenses/LICENSE +0 -0
claude_code_tools/__init__.py
CHANGED
|
@@ -2,11 +2,39 @@
|
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
4
|
import os
|
|
5
|
+
import re
|
|
5
6
|
from datetime import datetime
|
|
6
7
|
from io import StringIO
|
|
7
8
|
from pathlib import Path
|
|
8
9
|
from typing import Any, Optional
|
|
9
10
|
|
|
11
|
+
# Known system-injected XML tags that appear at the start of messages.
|
|
12
|
+
# Using a whitelist of specific tags avoids filtering legitimate user
|
|
13
|
+
# messages that start with HTML/XML like <div> or <svg>.
|
|
14
|
+
NON_GENUINE_XML_TAGS = {
|
|
15
|
+
# Claude system tags (local command execution)
|
|
16
|
+
"command-name",
|
|
17
|
+
"command-message",
|
|
18
|
+
"command-args",
|
|
19
|
+
"local-command-stdout",
|
|
20
|
+
"bash-input",
|
|
21
|
+
"bash-stdout",
|
|
22
|
+
"bash-stderr",
|
|
23
|
+
"bash-notification",
|
|
24
|
+
# Codex system tags (environment/context injection)
|
|
25
|
+
"environment_context",
|
|
26
|
+
"user_instructions",
|
|
27
|
+
"user_shell_command",
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
# Regex patterns for non-genuine user messages (system-injected content).
|
|
31
|
+
# Messages matching any of these patterns are filtered out when finding
|
|
32
|
+
# the first real user message. Used for both Claude and Codex sessions.
|
|
33
|
+
NON_GENUINE_MSG_PATTERNS = [
|
|
34
|
+
re.compile(r"^Caveat:", re.IGNORECASE), # Caveat warnings about local commands
|
|
35
|
+
re.compile(r"^\s*\[SESSION LINEAGE\]", re.IGNORECASE), # Session continuation context
|
|
36
|
+
]
|
|
37
|
+
|
|
10
38
|
# Lazy import yaml to allow module to load even if not installed
|
|
11
39
|
try:
|
|
12
40
|
import yaml
|
|
@@ -139,22 +167,60 @@ def _extract_codex_message_text(data: dict) -> Optional[str]:
|
|
|
139
167
|
return None
|
|
140
168
|
|
|
141
169
|
|
|
170
|
+
def _is_meta_user_message(data: dict, text: str) -> bool:
|
|
171
|
+
"""
|
|
172
|
+
Check if a user message is a meta/system-injected message.
|
|
173
|
+
|
|
174
|
+
These include local command injections that Claude Code records
|
|
175
|
+
in the session file but aren't actual user queries.
|
|
176
|
+
|
|
177
|
+
Args:
|
|
178
|
+
data: The parsed JSON data for the message
|
|
179
|
+
text: The extracted text content
|
|
180
|
+
|
|
181
|
+
Returns:
|
|
182
|
+
True if this is a meta message that should be skipped
|
|
183
|
+
"""
|
|
184
|
+
# Check isMeta flag
|
|
185
|
+
if data.get("isMeta") is True:
|
|
186
|
+
return True
|
|
187
|
+
|
|
188
|
+
# Check against regex patterns (Caveat, SESSION LINEAGE, etc.)
|
|
189
|
+
for pattern in NON_GENUINE_MSG_PATTERNS:
|
|
190
|
+
if pattern.search(text):
|
|
191
|
+
return True
|
|
192
|
+
|
|
193
|
+
# Check if message starts with a known system-injected XML tag
|
|
194
|
+
text_stripped = text.strip()
|
|
195
|
+
match = re.match(r"^<([a-z][a-z0-9_-]*)>", text_stripped)
|
|
196
|
+
if match and match.group(1) in NON_GENUINE_XML_TAGS:
|
|
197
|
+
return True
|
|
198
|
+
|
|
199
|
+
return False
|
|
200
|
+
|
|
201
|
+
|
|
142
202
|
def extract_first_last_messages(
|
|
143
203
|
session_file: Path, agent: str
|
|
144
|
-
) -> tuple[
|
|
204
|
+
) -> tuple[
|
|
205
|
+
Optional[dict[str, str]],
|
|
206
|
+
Optional[dict[str, str]],
|
|
207
|
+
Optional[dict[str, str]],
|
|
208
|
+
]:
|
|
145
209
|
"""
|
|
146
|
-
Extract first and
|
|
210
|
+
Extract first/last messages and the first real user message from a session.
|
|
147
211
|
|
|
148
212
|
Args:
|
|
149
213
|
session_file: Path to session JSONL file
|
|
150
214
|
agent: Agent type ('claude' or 'codex')
|
|
151
215
|
|
|
152
216
|
Returns:
|
|
153
|
-
Tuple of (first_msg, last_msg) where each is a dict
|
|
154
|
-
'content' keys, or None if not found
|
|
217
|
+
Tuple of (first_msg, last_msg, first_user_msg) where each is a dict
|
|
218
|
+
with 'role' and 'content' keys, or None if not found.
|
|
219
|
+
first_user_msg skips meta messages (local command injections).
|
|
155
220
|
"""
|
|
156
221
|
first_msg: Optional[dict[str, str]] = None
|
|
157
222
|
last_msg: Optional[dict[str, str]] = None
|
|
223
|
+
first_user_msg: Optional[dict[str, str]] = None
|
|
158
224
|
|
|
159
225
|
try:
|
|
160
226
|
with open(session_file, "r", encoding="utf-8") as f:
|
|
@@ -190,13 +256,20 @@ def extract_first_last_messages(
|
|
|
190
256
|
}
|
|
191
257
|
if first_msg is None:
|
|
192
258
|
first_msg = msg_dict
|
|
259
|
+
# Track first real user message (skip meta messages)
|
|
260
|
+
if (
|
|
261
|
+
role == "user"
|
|
262
|
+
and first_user_msg is None
|
|
263
|
+
and not _is_meta_user_message(data, text)
|
|
264
|
+
):
|
|
265
|
+
first_user_msg = msg_dict
|
|
193
266
|
# Always update last_msg to get the last one
|
|
194
267
|
last_msg = msg_dict
|
|
195
268
|
|
|
196
269
|
except (OSError, IOError):
|
|
197
270
|
pass
|
|
198
271
|
|
|
199
|
-
return first_msg, last_msg
|
|
272
|
+
return first_msg, last_msg, first_user_msg
|
|
200
273
|
|
|
201
274
|
|
|
202
275
|
def extract_session_metadata(session_file: Path, agent: str) -> dict[str, Any]:
|
|
@@ -236,6 +309,7 @@ def extract_session_metadata(session_file: Path, agent: str) -> dict[str, Any]:
|
|
|
236
309
|
"trim_stats": None,
|
|
237
310
|
"first_msg": None,
|
|
238
311
|
"last_msg": None,
|
|
312
|
+
"first_user_msg": None,
|
|
239
313
|
}
|
|
240
314
|
|
|
241
315
|
# Track session start timestamp from JSON metadata
|
|
@@ -371,9 +445,12 @@ def extract_session_metadata(session_file: Path, agent: str) -> dict[str, Any]:
|
|
|
371
445
|
metadata["project"] = Path(metadata["cwd"]).name
|
|
372
446
|
|
|
373
447
|
# Extract first and last messages
|
|
374
|
-
first_msg, last_msg = extract_first_last_messages(
|
|
448
|
+
first_msg, last_msg, first_user_msg = extract_first_last_messages(
|
|
449
|
+
session_file, agent
|
|
450
|
+
)
|
|
375
451
|
metadata["first_msg"] = first_msg
|
|
376
452
|
metadata["last_msg"] = last_msg
|
|
453
|
+
metadata["first_user_msg"] = first_user_msg
|
|
377
454
|
|
|
378
455
|
return metadata
|
|
379
456
|
|
|
@@ -458,6 +535,8 @@ def generate_yaml_frontmatter(metadata: dict[str, Any]) -> str:
|
|
|
458
535
|
yaml_data["first_msg"] = metadata["first_msg"]
|
|
459
536
|
if metadata.get("last_msg"):
|
|
460
537
|
yaml_data["last_msg"] = metadata["last_msg"]
|
|
538
|
+
if metadata.get("first_user_msg"):
|
|
539
|
+
yaml_data["first_user_msg"] = metadata["first_user_msg"]
|
|
461
540
|
|
|
462
541
|
# Trim stats (only for trimmed sessions)
|
|
463
542
|
if metadata.get("trim_stats"):
|
|
@@ -10,6 +10,7 @@ from importlib.metadata import version as get_pkg_version
|
|
|
10
10
|
from pathlib import Path
|
|
11
11
|
from typing import Any, Optional
|
|
12
12
|
|
|
13
|
+
from claude_code_tools.export_session import _is_meta_user_message
|
|
13
14
|
from claude_code_tools.session_utils import is_valid_session
|
|
14
15
|
|
|
15
16
|
|
|
@@ -177,6 +178,7 @@ class SessionIndex:
|
|
|
177
178
|
self.schema_builder.add_text_field("first_msg_content", stored=True)
|
|
178
179
|
self.schema_builder.add_text_field("last_msg_role", stored=True)
|
|
179
180
|
self.schema_builder.add_text_field("last_msg_content", stored=True)
|
|
181
|
+
self.schema_builder.add_text_field("first_user_msg_content", stored=True)
|
|
180
182
|
|
|
181
183
|
# Session type fields (for filtering in TUI)
|
|
182
184
|
self.schema_builder.add_text_field("derivation_type", stored=True)
|
|
@@ -321,10 +323,12 @@ class SessionIndex:
|
|
|
321
323
|
# First and last message fields
|
|
322
324
|
first_msg = metadata.get("first_msg", {}) or {}
|
|
323
325
|
last_msg = metadata.get("last_msg", {}) or {}
|
|
326
|
+
first_user_msg = metadata.get("first_user_msg", {}) or {}
|
|
324
327
|
doc.add_text("first_msg_role", first_msg.get("role", ""))
|
|
325
328
|
doc.add_text("first_msg_content", first_msg.get("content", ""))
|
|
326
329
|
doc.add_text("last_msg_role", last_msg.get("role", ""))
|
|
327
330
|
doc.add_text("last_msg_content", last_msg.get("content", ""))
|
|
331
|
+
doc.add_text("first_user_msg_content", first_user_msg.get("content", ""))
|
|
328
332
|
|
|
329
333
|
# Session type fields
|
|
330
334
|
doc.add_text("derivation_type", metadata.get("derivation_type", "") or "")
|
|
@@ -405,10 +409,12 @@ class SessionIndex:
|
|
|
405
409
|
# First and last message fields
|
|
406
410
|
first_msg = metadata.get("first_msg", {}) or {}
|
|
407
411
|
last_msg = metadata.get("last_msg", {}) or {}
|
|
412
|
+
first_user_msg = metadata.get("first_user_msg", {}) or {}
|
|
408
413
|
doc.add_text("first_msg_role", first_msg.get("role", ""))
|
|
409
414
|
doc.add_text("first_msg_content", first_msg.get("content", ""))
|
|
410
415
|
doc.add_text("last_msg_role", last_msg.get("role", ""))
|
|
411
416
|
doc.add_text("last_msg_content", last_msg.get("content", ""))
|
|
417
|
+
doc.add_text("first_user_msg_content", first_user_msg.get("content", ""))
|
|
412
418
|
|
|
413
419
|
# Session type fields
|
|
414
420
|
doc.add_text(
|
|
@@ -525,9 +531,10 @@ class SessionIndex:
|
|
|
525
531
|
message = data.get("message", {})
|
|
526
532
|
content = message.get("content")
|
|
527
533
|
|
|
528
|
-
# Count user messages, but exclude
|
|
529
|
-
# Tool results
|
|
530
|
-
#
|
|
534
|
+
# Count user messages, but exclude:
|
|
535
|
+
# - Tool results (content is list with {"type": "tool_result"})
|
|
536
|
+
# - Meta messages (isMeta: true, e.g., "Caveat:" warnings)
|
|
537
|
+
# - Local command messages (<command-name>, <local-command-stdout>)
|
|
531
538
|
if role == "user":
|
|
532
539
|
is_tool_result = (
|
|
533
540
|
isinstance(content, list)
|
|
@@ -535,7 +542,12 @@ class SessionIndex:
|
|
|
535
542
|
and isinstance(content[0], dict)
|
|
536
543
|
and content[0].get("type") == "tool_result"
|
|
537
544
|
)
|
|
538
|
-
|
|
545
|
+
# Check for meta/non-genuine messages
|
|
546
|
+
text_content = content if isinstance(content, str) else ""
|
|
547
|
+
if (
|
|
548
|
+
not is_tool_result
|
|
549
|
+
and not _is_meta_user_message(data, text_content)
|
|
550
|
+
):
|
|
539
551
|
user_count += 1
|
|
540
552
|
|
|
541
553
|
if not content:
|
|
@@ -575,13 +587,23 @@ class SessionIndex:
|
|
|
575
587
|
continue
|
|
576
588
|
|
|
577
589
|
role = payload.get("role")
|
|
578
|
-
if role == "user":
|
|
579
|
-
user_count += 1
|
|
580
590
|
content = payload.get("content", [])
|
|
581
591
|
|
|
582
592
|
if not isinstance(content, list):
|
|
583
593
|
continue
|
|
584
594
|
|
|
595
|
+
# Extract text first to check for meta patterns
|
|
596
|
+
codex_text = ""
|
|
597
|
+
for block in content:
|
|
598
|
+
if isinstance(block, dict):
|
|
599
|
+
if block.get("type") in ("input_text", "output_text"):
|
|
600
|
+
codex_text += block.get("text", "")
|
|
601
|
+
|
|
602
|
+
# Count user messages, but exclude non-genuine messages
|
|
603
|
+
if role == "user":
|
|
604
|
+
if not _is_meta_user_message({}, codex_text):
|
|
605
|
+
user_count += 1
|
|
606
|
+
|
|
585
607
|
for block in content:
|
|
586
608
|
if not isinstance(block, dict):
|
|
587
609
|
continue
|
|
@@ -635,6 +657,7 @@ class SessionIndex:
|
|
|
635
657
|
# Map metadata fields to expected format
|
|
636
658
|
first_msg = metadata.get("first_msg") or {"role": "", "content": ""}
|
|
637
659
|
last_msg = metadata.get("last_msg") or {"role": "", "content": ""}
|
|
660
|
+
first_user_msg = metadata.get("first_user_msg") or {"role": "", "content": ""}
|
|
638
661
|
|
|
639
662
|
# Always use filename-derived session_id (the canonical identifier)
|
|
640
663
|
# Internal sessionId field can be stale in forked sessions
|
|
@@ -660,6 +683,7 @@ class SessionIndex:
|
|
|
660
683
|
"content": content,
|
|
661
684
|
"first_msg": first_msg,
|
|
662
685
|
"last_msg": last_msg,
|
|
686
|
+
"first_user_msg": first_user_msg,
|
|
663
687
|
"lines": msg_count,
|
|
664
688
|
"file_path": str(jsonl_path),
|
|
665
689
|
}
|
|
@@ -742,6 +766,7 @@ class SessionIndex:
|
|
|
742
766
|
metadata = parsed["metadata"]
|
|
743
767
|
first_msg = parsed["first_msg"]
|
|
744
768
|
last_msg = parsed["last_msg"]
|
|
769
|
+
first_user_msg = parsed.get("first_user_msg", {}) or {}
|
|
745
770
|
|
|
746
771
|
# Skip helper sessions (SDK/headless sessions used for analysis)
|
|
747
772
|
if metadata.get("session_type") == "helper":
|
|
@@ -785,6 +810,7 @@ class SessionIndex:
|
|
|
785
810
|
doc.add_text("first_msg_content", first_msg.get("content", ""))
|
|
786
811
|
doc.add_text("last_msg_role", last_msg.get("role", ""))
|
|
787
812
|
doc.add_text("last_msg_content", last_msg.get("content", ""))
|
|
813
|
+
doc.add_text("first_user_msg_content", first_user_msg.get("content", ""))
|
|
788
814
|
|
|
789
815
|
# Session type fields
|
|
790
816
|
doc.add_text(
|
|
@@ -1007,6 +1033,7 @@ class SessionIndex:
|
|
|
1007
1033
|
first_msg_content = doc.get_first("first_msg_content") or ""
|
|
1008
1034
|
last_msg_role = doc.get_first("last_msg_role") or ""
|
|
1009
1035
|
last_msg_content = doc.get_first("last_msg_content") or ""
|
|
1036
|
+
first_user_msg_content = doc.get_first("first_user_msg_content") or ""
|
|
1010
1037
|
|
|
1011
1038
|
results.append({
|
|
1012
1039
|
"session_id": session_id,
|
|
@@ -1024,6 +1051,7 @@ class SessionIndex:
|
|
|
1024
1051
|
"first_msg_content": first_msg_content,
|
|
1025
1052
|
"last_msg_role": last_msg_role,
|
|
1026
1053
|
"last_msg_content": last_msg_content,
|
|
1054
|
+
"first_user_msg_content": first_user_msg_content,
|
|
1027
1055
|
})
|
|
1028
1056
|
|
|
1029
1057
|
if len(results) >= limit:
|
|
@@ -1087,6 +1115,7 @@ class SessionIndex:
|
|
|
1087
1115
|
"first_msg_content": doc.get_first("first_msg_content") or "",
|
|
1088
1116
|
"last_msg_role": doc.get_first("last_msg_role") or "",
|
|
1089
1117
|
"last_msg_content": doc.get_first("last_msg_content") or "",
|
|
1118
|
+
"first_user_msg_content": doc.get_first("first_user_msg_content") or "",
|
|
1090
1119
|
"claude_home": doc.get_first("claude_home") or "",
|
|
1091
1120
|
})
|
|
1092
1121
|
|
|
@@ -1186,6 +1215,7 @@ class SessionIndex:
|
|
|
1186
1215
|
"first_msg_content": doc.get_first("first_msg_content") or "",
|
|
1187
1216
|
"last_msg_role": doc.get_first("last_msg_role") or "",
|
|
1188
1217
|
"last_msg_content": doc.get_first("last_msg_content") or "",
|
|
1218
|
+
"first_user_msg_content": doc.get_first("first_user_msg_content") or "",
|
|
1189
1219
|
"derivation_type": doc.get_first("derivation_type") or "",
|
|
1190
1220
|
"is_sidechain": doc.get_first("is_sidechain") or "false",
|
|
1191
1221
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: claude-code-tools
|
|
3
|
-
Version: 1.4.
|
|
3
|
+
Version: 1.4.6
|
|
4
4
|
Summary: Collection of tools for working with Claude Code
|
|
5
5
|
License-File: LICENSE
|
|
6
6
|
Requires-Python: >=3.11
|
|
@@ -26,9 +26,8 @@ Description-Content-Type: text/markdown
|
|
|
26
26
|
[](https://pypi.org/project/claude-code-tools/)
|
|
27
27
|
[](https://github.com/pchalasani/claude-code-tools/releases?q=rust)
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
CLI commands, skills, agents, hooks, plugins.
|
|
29
|
+
Productivity tools for Claude Code, Codex-CLI, and similar CLI coding agents:
|
|
30
|
+
CLI commands, skills, agents, hooks, plugins. Click on a card below to navigate.
|
|
32
31
|
|
|
33
32
|
|
|
34
33
|
<div align="center">
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
claude_code_tools/__init__.py,sha256=
|
|
1
|
+
claude_code_tools/__init__.py,sha256=lJ_ty7aXHz-OaAsz_ikznt_x0M6lgCfbFZqflfmM_-U,89
|
|
2
2
|
claude_code_tools/action_rpc.py,sha256=6NfWUjt22qqkDKq7ftNH2V9B8VSQycbFx_jDA0UrlJQ,17016
|
|
3
3
|
claude_code_tools/aichat.py,sha256=s8pfRTmXR55J4yNElJdztXnfjFifaJbjrBtVo6NUe-s,95346
|
|
4
4
|
claude_code_tools/claude_continue.py,sha256=GwgKGbTpj4ES615yCadjz0Q6wOb69T76rf_-wPnHix8,11727
|
|
@@ -10,7 +10,7 @@ claude_code_tools/env_safe.py,sha256=TSSkOjEpzBwNgbeSR-0tR1-pAW_qmbZNmn3fiAsHJ4w
|
|
|
10
10
|
claude_code_tools/export_all.py,sha256=GOWj_5IZrrngeRUsDxbE48cOOZIxo7drZJWZh9QiuHg,9848
|
|
11
11
|
claude_code_tools/export_claude_session.py,sha256=rEJLMcaCMuWbWxs1rfd0LuT6gSmjEsej6nueGrH1ujo,16256
|
|
12
12
|
claude_code_tools/export_codex_session.py,sha256=V2deRcI6FMCEWYAEvvL74XXuW798B1esgTs6PH3_-7E,15650
|
|
13
|
-
claude_code_tools/export_session.py,sha256=
|
|
13
|
+
claude_code_tools/export_session.py,sha256=I2ncN3lbbrfc8M-3URQVimyM2fAbcu4BXITtCdNfL6E,21860
|
|
14
14
|
claude_code_tools/find_claude_session.py,sha256=QRv6u4T5X9c9QLj-1X8-uYj3wul5YsbFI5LgUUTFMW0,70559
|
|
15
15
|
claude_code_tools/find_codex_session.py,sha256=dpZVek3cJ-se4JMwzGEDkZ50_XvtK6dfP36mo8KDHnI,48177
|
|
16
16
|
claude_code_tools/find_original_session.py,sha256=JlHeati0X1KkPkmz4knvdfCqRHjuJRLfRRcn3ZsuG8o,4120
|
|
@@ -19,7 +19,7 @@ claude_code_tools/find_trimmed_sessions.py,sha256=JvMSetHD4DgXzKDFaZlAndBT_dYaw_
|
|
|
19
19
|
claude_code_tools/gdoc2md.py,sha256=J83CZJomHquOBIl15fISqtDyGsmkqqMuRY-nN7-7K1I,6346
|
|
20
20
|
claude_code_tools/md2gdoc.py,sha256=sA6gU2QsWanJpAwfSC6HnSPQuSywv0xUopXSvbRUX_o,17945
|
|
21
21
|
claude_code_tools/node_menu_ui.py,sha256=CQ6PxxNQ5jbLRLYESJ-klLSxSIIuLegU8s-Sj5yRl8Q,12621
|
|
22
|
-
claude_code_tools/search_index.py,sha256=
|
|
22
|
+
claude_code_tools/search_index.py,sha256=_ORSD2E6PF-Gjtzrnvp03KyfGueO5FA3WCzTbg7n208,50557
|
|
23
23
|
claude_code_tools/session_lineage.py,sha256=BYKpAolPGLJUv97-xMXvNFMzgauUVNAsRx8Shw0X_hk,8430
|
|
24
24
|
claude_code_tools/session_menu.py,sha256=5M1AlqhmCWly3r3P1u-GhxWB0_rbGKsKSlIPEgTaN9w,6095
|
|
25
25
|
claude_code_tools/session_menu_cli.py,sha256=SnCdm1xyJQAC0ogZ5-PRc8SkAZVKHXYu6mtc0Lp_las,15426
|
|
@@ -36,6 +36,7 @@ docs/claude-code-chutes.md,sha256=jCnYAAHZm32NGHE0CzGGl3vpO_zlF_xdmr23YxuCjPg,80
|
|
|
36
36
|
docs/claude-code-tmux-tutorials.md,sha256=S-9U3a1AaPEBPo3oKpWuyOfKK7yPFOIu21P_LDfGUJk,7558
|
|
37
37
|
docs/dot-zshrc.md,sha256=DC2fOiGrUlIzol6N_47CW53a4BsnMEvCnhlRRVxFCTc,7160
|
|
38
38
|
docs/find-claude-session.md,sha256=fACbQP0Bj5jqIpNWk0lGDOQQaji-K9Va3gUv2RA47VQ,4284
|
|
39
|
+
docs/linked-in-20260102.md,sha256=wCihbQGGqS-GpQ7z9-q6UObiJBJ8_VfbUufXTvqB6hY,1159
|
|
39
40
|
docs/lmsh.md,sha256=Kf5tKt1lh7eDV-B6mrMi2hsjUMZv1EGfkrsNS29HYBA,2226
|
|
40
41
|
docs/local-llm-setup.md,sha256=JnMF4m1e0s8DZxfB-8S3Y20W74KBMm2RXwBjTK0o27U,7596
|
|
41
42
|
docs/reddit-aichat-resume-v2.md,sha256=Rpq4E-tMDpgjWiSfb-jS50AeUxgdnOJIwDHs7rdLTZw,2980
|
|
@@ -1806,8 +1807,8 @@ node_ui/node_modules/yoga-wasm-web/dist/wrapAsm-f766f97f.js,sha256=-82_XGQhP7kkD
|
|
|
1806
1807
|
node_ui/node_modules/yoga-wasm-web/dist/wrapAsm.d.ts,sha256=2l7bSIMruV8KTC2I4XKJBDQx8nsgwVR43q9rvkClpUE,4877
|
|
1807
1808
|
node_ui/node_modules/yoga-wasm-web/dist/yoga.wasm,sha256=R_tPgdJ0kyGEzRnHXtNPkC0T8FGTAVkHiaN_cHeXfic,88658
|
|
1808
1809
|
node_ui/node_modules/yoga-wasm-web/dist/generated/YGEnums.d.ts,sha256=kE3_7yS8iqNd5sMfXtD9B3Tq_JcJkVOQkdwxhch1pI4,8893
|
|
1809
|
-
claude_code_tools-1.4.
|
|
1810
|
-
claude_code_tools-1.4.
|
|
1811
|
-
claude_code_tools-1.4.
|
|
1812
|
-
claude_code_tools-1.4.
|
|
1813
|
-
claude_code_tools-1.4.
|
|
1810
|
+
claude_code_tools-1.4.6.dist-info/METADATA,sha256=O2zrPX_UYG6uRdKknR5S0P9ACZJEvOlzyxaCuy89_NU,42998
|
|
1811
|
+
claude_code_tools-1.4.6.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
1812
|
+
claude_code_tools-1.4.6.dist-info/entry_points.txt,sha256=-hVowB6m8tgqV_dCyzCLbt7vthEDiBxodGMqMvD4F2M,280
|
|
1813
|
+
claude_code_tools-1.4.6.dist-info/licenses/LICENSE,sha256=BBQdOBLdFB3CEPmb3pqxeOThaFCIdsiLzmDANsCHhoM,1073
|
|
1814
|
+
claude_code_tools-1.4.6.dist-info/RECORD,,
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
The top pain for users of Claude Code and similar CLI agents is...
|
|
2
|
+
|
|
3
|
+
Sesesion Continuity: What do you do when you've filled your context window?
|
|
4
|
+
|
|
5
|
+
Compaction? you lose valuable detail that you have to explain all over again.
|
|
6
|
+
|
|
7
|
+
Here's what I do instead, to recover the precise, full context I need, to continue my work:
|
|
8
|
+
|
|
9
|
+
In my Claude Code session, I type ">resume" -- This triggers a hook that copies the current session ID to the clipboard.
|
|
10
|
+
|
|
11
|
+
Then I run:
|
|
12
|
+
|
|
13
|
+
aichat resume <paste-session-id>
|
|
14
|
+
|
|
15
|
+
This launches a TUI that shows a few ways to continue my work: I select
|
|
16
|
+
the "rollover" option: it creates a new session and injects the session log file
|
|
17
|
+
path into the first user message.
|
|
18
|
+
|
|
19
|
+
Then I prompt it to retrieve the exact context I need, or use a slash command /aichat:recover-context
|
|
20
|
+
|
|
21
|
+
This works with Codex-CLI as well, and you can even do cross-agent handoff: start in
|
|
22
|
+
Claude-Code, continue with Codex-CLI or vice versa.
|
|
23
|
+
|
|
24
|
+
The aichat command is one of several productivity tools in my claude-code-tools repo:
|
|
25
|
+
If you'd like to try them out, see the repo for instructions on how to install the suite of tools
|
|
26
|
+
|
|
27
|
+
https://github.com/pchalasani/claude-code-tools
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
File without changes
|
|
File without changes
|
|
File without changes
|