claude-code-tools 0.1.13__py3-none-any.whl → 0.1.14__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.
Potentially problematic release.
This version of claude-code-tools might be problematic. Click here for more details.
- claude_code_tools/__init__.py +1 -1
- claude_code_tools/find_claude_session.py +50 -21
- {claude_code_tools-0.1.13.dist-info → claude_code_tools-0.1.14.dist-info}/METADATA +1 -1
- {claude_code_tools-0.1.13.dist-info → claude_code_tools-0.1.14.dist-info}/RECORD +6 -6
- {claude_code_tools-0.1.13.dist-info → claude_code_tools-0.1.14.dist-info}/WHEEL +0 -0
- {claude_code_tools-0.1.13.dist-info → claude_code_tools-0.1.14.dist-info}/entry_points.txt +0 -0
claude_code_tools/__init__.py
CHANGED
|
@@ -121,39 +121,51 @@ def extract_project_name(original_path: str) -> str:
|
|
|
121
121
|
return parts[-1] if parts else "unknown"
|
|
122
122
|
|
|
123
123
|
|
|
124
|
-
def search_keywords_in_file(filepath: Path, keywords: List[str]) -> tuple[bool, int]:
|
|
124
|
+
def search_keywords_in_file(filepath: Path, keywords: List[str]) -> tuple[bool, int, Optional[str]]:
|
|
125
125
|
"""
|
|
126
|
-
Check if all keywords are present in the JSONL file and
|
|
126
|
+
Check if all keywords are present in the JSONL file, count lines, and extract git branch.
|
|
127
127
|
|
|
128
128
|
Args:
|
|
129
129
|
filepath: Path to the JSONL file
|
|
130
130
|
keywords: List of keywords to search for (case-insensitive)
|
|
131
131
|
|
|
132
132
|
Returns:
|
|
133
|
-
Tuple of (matches: bool, line_count: int)
|
|
133
|
+
Tuple of (matches: bool, line_count: int, git_branch: Optional[str])
|
|
134
134
|
- matches: True if ALL keywords are found in the file
|
|
135
135
|
- line_count: Total number of lines in the file
|
|
136
|
+
- git_branch: Git branch name from the first message that has it, or None
|
|
136
137
|
"""
|
|
137
138
|
# Convert keywords to lowercase for case-insensitive search
|
|
138
139
|
keywords_lower = [k.lower() for k in keywords]
|
|
139
140
|
found_keywords = set()
|
|
140
141
|
line_count = 0
|
|
142
|
+
git_branch = None
|
|
141
143
|
|
|
142
144
|
try:
|
|
143
145
|
with open(filepath, 'r', encoding='utf-8') as f:
|
|
144
146
|
for line in f:
|
|
145
147
|
line_count += 1
|
|
146
148
|
line_lower = line.lower()
|
|
149
|
+
|
|
150
|
+
# Extract git branch from JSON if not already found
|
|
151
|
+
if git_branch is None:
|
|
152
|
+
try:
|
|
153
|
+
data = json.loads(line.strip())
|
|
154
|
+
if 'gitBranch' in data and data['gitBranch']:
|
|
155
|
+
git_branch = data['gitBranch']
|
|
156
|
+
except (json.JSONDecodeError, KeyError):
|
|
157
|
+
pass
|
|
158
|
+
|
|
147
159
|
# Check which keywords are in this line
|
|
148
160
|
for keyword in keywords_lower:
|
|
149
161
|
if keyword in line_lower:
|
|
150
162
|
found_keywords.add(keyword)
|
|
151
163
|
except Exception:
|
|
152
164
|
# Skip files that can't be read
|
|
153
|
-
return False, 0
|
|
165
|
+
return False, 0, None
|
|
154
166
|
|
|
155
167
|
matches = len(found_keywords) == len(keywords_lower)
|
|
156
|
-
return matches, line_count
|
|
168
|
+
return matches, line_count, git_branch
|
|
157
169
|
|
|
158
170
|
|
|
159
171
|
def get_session_preview(filepath: Path) -> str:
|
|
@@ -187,7 +199,7 @@ def get_session_preview(filepath: Path) -> str:
|
|
|
187
199
|
return "No preview available"
|
|
188
200
|
|
|
189
201
|
|
|
190
|
-
def find_sessions(keywords: List[str], global_search: bool = False, claude_home: Optional[str] = None) -> List[Tuple[str, float, int, str, str, str]]:
|
|
202
|
+
def find_sessions(keywords: List[str], global_search: bool = False, claude_home: Optional[str] = None) -> List[Tuple[str, float, int, str, str, str, Optional[str]]]:
|
|
191
203
|
"""
|
|
192
204
|
Find all Claude Code sessions containing the specified keywords.
|
|
193
205
|
|
|
@@ -197,7 +209,7 @@ def find_sessions(keywords: List[str], global_search: bool = False, claude_home:
|
|
|
197
209
|
claude_home: Optional custom Claude home directory (defaults to ~/.claude)
|
|
198
210
|
|
|
199
211
|
Returns:
|
|
200
|
-
List of tuples (session_id, modification_time, line_count, project_name, preview, project_path) sorted by modification time
|
|
212
|
+
List of tuples (session_id, modification_time, line_count, project_name, preview, project_path, git_branch) sorted by modification time
|
|
201
213
|
"""
|
|
202
214
|
matching_sessions = []
|
|
203
215
|
|
|
@@ -220,12 +232,12 @@ def find_sessions(keywords: List[str], global_search: bool = False, claude_home:
|
|
|
220
232
|
|
|
221
233
|
# Search all JSONL files in this project directory
|
|
222
234
|
for jsonl_file in project_dir.glob("*.jsonl"):
|
|
223
|
-
matches, line_count = search_keywords_in_file(jsonl_file, keywords)
|
|
235
|
+
matches, line_count, git_branch = search_keywords_in_file(jsonl_file, keywords)
|
|
224
236
|
if matches:
|
|
225
237
|
session_id = jsonl_file.stem
|
|
226
238
|
mod_time = jsonl_file.stat().st_mtime
|
|
227
239
|
preview = get_session_preview(jsonl_file)
|
|
228
|
-
matching_sessions.append((session_id, mod_time, line_count, project_name, preview, original_path))
|
|
240
|
+
matching_sessions.append((session_id, mod_time, line_count, project_name, preview, original_path, git_branch))
|
|
229
241
|
|
|
230
242
|
progress.advance(task)
|
|
231
243
|
else:
|
|
@@ -234,12 +246,12 @@ def find_sessions(keywords: List[str], global_search: bool = False, claude_home:
|
|
|
234
246
|
project_name = extract_project_name(original_path)
|
|
235
247
|
|
|
236
248
|
for jsonl_file in project_dir.glob("*.jsonl"):
|
|
237
|
-
matches, line_count = search_keywords_in_file(jsonl_file, keywords)
|
|
249
|
+
matches, line_count, git_branch = search_keywords_in_file(jsonl_file, keywords)
|
|
238
250
|
if matches:
|
|
239
251
|
session_id = jsonl_file.stem
|
|
240
252
|
mod_time = jsonl_file.stat().st_mtime
|
|
241
253
|
preview = get_session_preview(jsonl_file)
|
|
242
|
-
matching_sessions.append((session_id, mod_time, line_count, project_name, preview, original_path))
|
|
254
|
+
matching_sessions.append((session_id, mod_time, line_count, project_name, preview, original_path, git_branch))
|
|
243
255
|
else:
|
|
244
256
|
# Search current project only
|
|
245
257
|
claude_dir = get_claude_project_dir(claude_home)
|
|
@@ -251,12 +263,12 @@ def find_sessions(keywords: List[str], global_search: bool = False, claude_home:
|
|
|
251
263
|
|
|
252
264
|
# Search all JSONL files in the directory
|
|
253
265
|
for jsonl_file in claude_dir.glob("*.jsonl"):
|
|
254
|
-
matches, line_count = search_keywords_in_file(jsonl_file, keywords)
|
|
266
|
+
matches, line_count, git_branch = search_keywords_in_file(jsonl_file, keywords)
|
|
255
267
|
if matches:
|
|
256
268
|
session_id = jsonl_file.stem
|
|
257
269
|
mod_time = jsonl_file.stat().st_mtime
|
|
258
270
|
preview = get_session_preview(jsonl_file)
|
|
259
|
-
matching_sessions.append((session_id, mod_time, line_count, project_name, preview, os.getcwd()))
|
|
271
|
+
matching_sessions.append((session_id, mod_time, line_count, project_name, preview, os.getcwd(), git_branch))
|
|
260
272
|
|
|
261
273
|
# Sort by modification time (newest first)
|
|
262
274
|
matching_sessions.sort(key=lambda x: x[1], reverse=True)
|
|
@@ -264,7 +276,7 @@ def find_sessions(keywords: List[str], global_search: bool = False, claude_home:
|
|
|
264
276
|
return matching_sessions
|
|
265
277
|
|
|
266
278
|
|
|
267
|
-
def display_interactive_ui(sessions: List[Tuple[str, float, int, str, str, str]], keywords: List[str], stderr_mode: bool = False, num_matches: int = 10) -> Optional[Tuple[str, str]]:
|
|
279
|
+
def display_interactive_ui(sessions: List[Tuple[str, float, int, str, str, str, Optional[str]]], keywords: List[str], stderr_mode: bool = False, num_matches: int = 10) -> Optional[Tuple[str, str]]:
|
|
268
280
|
"""Display interactive UI for session selection."""
|
|
269
281
|
if not RICH_AVAILABLE:
|
|
270
282
|
return None
|
|
@@ -292,16 +304,19 @@ def display_interactive_ui(sessions: List[Tuple[str, float, int, str, str, str]]
|
|
|
292
304
|
table.add_column("#", style="bold yellow", width=3)
|
|
293
305
|
table.add_column("Session ID", style="dim")
|
|
294
306
|
table.add_column("Project", style="green")
|
|
307
|
+
table.add_column("Branch", style="magenta")
|
|
295
308
|
table.add_column("Date", style="blue")
|
|
296
309
|
table.add_column("Lines", style="cyan", justify="right")
|
|
297
310
|
table.add_column("Preview", style="white", overflow="fold")
|
|
298
311
|
|
|
299
|
-
for idx, (session_id, mod_time, line_count, project_name, preview, _) in enumerate(display_sessions, 1):
|
|
312
|
+
for idx, (session_id, mod_time, line_count, project_name, preview, _, git_branch) in enumerate(display_sessions, 1):
|
|
300
313
|
mod_date = datetime.fromtimestamp(mod_time).strftime('%Y-%m-%d %H:%M')
|
|
314
|
+
branch_display = git_branch if git_branch else "N/A"
|
|
301
315
|
table.add_row(
|
|
302
316
|
str(idx),
|
|
303
317
|
session_id[:8] + "...",
|
|
304
318
|
project_name,
|
|
319
|
+
branch_display,
|
|
305
320
|
mod_date,
|
|
306
321
|
str(line_count),
|
|
307
322
|
preview
|
|
@@ -327,6 +342,11 @@ def display_interactive_ui(sessions: List[Tuple[str, float, int, str, str, str]]
|
|
|
327
342
|
console=ui_console
|
|
328
343
|
)
|
|
329
344
|
|
|
345
|
+
# Handle empty input
|
|
346
|
+
if not choice or not choice.strip():
|
|
347
|
+
ui_console.print("[red]Invalid choice. Please try again.[/red]")
|
|
348
|
+
continue
|
|
349
|
+
|
|
330
350
|
# Restore stdout
|
|
331
351
|
if stderr_mode:
|
|
332
352
|
sys.stdout.close()
|
|
@@ -340,7 +360,10 @@ def display_interactive_ui(sessions: List[Tuple[str, float, int, str, str, str]]
|
|
|
340
360
|
except KeyboardInterrupt:
|
|
341
361
|
ui_console.print("\n[yellow]Cancelled[/yellow]")
|
|
342
362
|
return None
|
|
343
|
-
except
|
|
363
|
+
except EOFError:
|
|
364
|
+
ui_console.print("\n[yellow]Cancelled (EOF)[/yellow]")
|
|
365
|
+
return None
|
|
366
|
+
except ValueError:
|
|
344
367
|
ui_console.print("[red]Invalid choice. Please try again.[/red]")
|
|
345
368
|
|
|
346
369
|
|
|
@@ -496,12 +519,13 @@ To persist directory changes when resuming sessions:
|
|
|
496
519
|
# Fallback: print session IDs as before
|
|
497
520
|
if not args.shell:
|
|
498
521
|
print("\nMatching sessions:")
|
|
499
|
-
for idx, (session_id, mod_time, line_count, project_name, preview, project_path) in enumerate(matching_sessions[:args.num_matches], 1):
|
|
522
|
+
for idx, (session_id, mod_time, line_count, project_name, preview, project_path, git_branch) in enumerate(matching_sessions[:args.num_matches], 1):
|
|
500
523
|
mod_date = datetime.fromtimestamp(mod_time).strftime('%Y-%m-%d %H:%M:%S')
|
|
524
|
+
branch_display = git_branch if git_branch else "N/A"
|
|
501
525
|
if getattr(args, 'global'):
|
|
502
|
-
print(f"{idx}. {session_id} | {project_name} | {mod_date} | {line_count} lines", file=sys.stderr if args.shell else sys.stdout)
|
|
526
|
+
print(f"{idx}. {session_id} | {project_name} | {branch_display} | {mod_date} | {line_count} lines", file=sys.stderr if args.shell else sys.stdout)
|
|
503
527
|
else:
|
|
504
|
-
print(f"{idx}. {session_id} | {mod_date} | {line_count} lines", file=sys.stderr if args.shell else sys.stdout)
|
|
528
|
+
print(f"{idx}. {session_id} | {branch_display} | {mod_date} | {line_count} lines", file=sys.stderr if args.shell else sys.stdout)
|
|
505
529
|
|
|
506
530
|
if len(matching_sessions) > args.num_matches:
|
|
507
531
|
print(f"\n... and {len(matching_sessions) - args.num_matches} more sessions", file=sys.stderr if args.shell else sys.stdout)
|
|
@@ -510,7 +534,7 @@ To persist directory changes when resuming sessions:
|
|
|
510
534
|
if len(matching_sessions) == 1:
|
|
511
535
|
if not args.shell:
|
|
512
536
|
print("\nOnly one match found. Resuming automatically...")
|
|
513
|
-
session_id, _, _, _, _, project_path = matching_sessions[0]
|
|
537
|
+
session_id, _, _, _, _, project_path, _ = matching_sessions[0]
|
|
514
538
|
resume_session(session_id, project_path, shell_mode=args.shell)
|
|
515
539
|
else:
|
|
516
540
|
try:
|
|
@@ -521,10 +545,15 @@ To persist directory changes when resuming sessions:
|
|
|
521
545
|
choice = sys.stdin.readline().strip()
|
|
522
546
|
else:
|
|
523
547
|
choice = input("\nEnter number to resume session (or Ctrl+C to cancel): ")
|
|
548
|
+
|
|
549
|
+
# Handle empty input or EOF
|
|
550
|
+
if not choice:
|
|
551
|
+
print("Cancelled (EOF)", file=sys.stderr)
|
|
552
|
+
sys.exit(0)
|
|
524
553
|
|
|
525
554
|
idx = int(choice) - 1
|
|
526
555
|
if 0 <= idx < min(args.num_matches, len(matching_sessions)):
|
|
527
|
-
session_id, _, _, _, _, project_path = matching_sessions[idx]
|
|
556
|
+
session_id, _, _, _, _, project_path, _ = matching_sessions[idx]
|
|
528
557
|
resume_session(session_id, project_path, shell_mode=args.shell)
|
|
529
558
|
else:
|
|
530
559
|
print("Invalid choice", file=sys.stderr)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
claude_code_tools/__init__.py,sha256=
|
|
1
|
+
claude_code_tools/__init__.py,sha256=_-QQFZ18-bFEW2GEAgme8FhI4Ejyk7Fxq8o0zPNtT9U,90
|
|
2
2
|
claude_code_tools/dotenv_vault.py,sha256=KPI9NDFu5HE6FfhQUYw6RhdR-miN0ScJHsBg0OVG61k,9617
|
|
3
|
-
claude_code_tools/find_claude_session.py,sha256=
|
|
3
|
+
claude_code_tools/find_claude_session.py,sha256=TfQWW2zMDJAnfLREt_P23BB6e9Qb-XS22SSEU80K-4Y,23524
|
|
4
4
|
claude_code_tools/tmux_cli_controller.py,sha256=0PqWBhITw74TtOKAfiUJMyHEFVOlLp4sEYmSlDHyznc,27482
|
|
5
5
|
claude_code_tools/tmux_remote_controller.py,sha256=uK9lJKrNz7_NeV1_V3BM-q0r6sRVmYfOR8H3zo5hfH8,3220
|
|
6
6
|
docs/claude-code-chutes.md,sha256=jCnYAAHZm32NGHE0CzGGl3vpO_zlF_xdmr23YxuCjPg,8098
|
|
@@ -9,7 +9,7 @@ docs/find-claude-session.md,sha256=fACbQP0Bj5jqIpNWk0lGDOQQaji-K9Va3gUv2RA47VQ,4
|
|
|
9
9
|
docs/reddit-post.md,sha256=ZA7kPoJNi06t6F9JQMBiIOv039ADC9lM8YXFt8UA_Jg,2345
|
|
10
10
|
docs/tmux-cli-instructions.md,sha256=lQqKTI-uhH-EdU9P4To4GC10WJjj3VllAW4cxd8jfj8,4167
|
|
11
11
|
docs/vault-documentation.md,sha256=5XzNpHyhGU38JU2hKEWEL1gdPq3rC2zBg8yotK4eNF4,3600
|
|
12
|
-
claude_code_tools-0.1.
|
|
13
|
-
claude_code_tools-0.1.
|
|
14
|
-
claude_code_tools-0.1.
|
|
15
|
-
claude_code_tools-0.1.
|
|
12
|
+
claude_code_tools-0.1.14.dist-info/METADATA,sha256=mkXcHoHrAhUHp-4rYvAKD9qDA94C5hsPj9OSs1j2i9I,10313
|
|
13
|
+
claude_code_tools-0.1.14.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
14
|
+
claude_code_tools-0.1.14.dist-info/entry_points.txt,sha256=yUTZlZ2jteoUZ9bGPvZKI9tjmohPDhRk1fovu5pZACM,181
|
|
15
|
+
claude_code_tools-0.1.14.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|