claude-code-tools 0.2.5__tar.gz → 0.2.6__tar.gz

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.

Files changed (23) hide show
  1. {claude_code_tools-0.2.5 → claude_code_tools-0.2.6}/PKG-INFO +83 -1
  2. {claude_code_tools-0.2.5 → claude_code_tools-0.2.6}/README.md +82 -0
  3. {claude_code_tools-0.2.5 → claude_code_tools-0.2.6}/claude_code_tools/__init__.py +1 -1
  4. claude_code_tools-0.2.6/claude_code_tools/find_session.py +438 -0
  5. {claude_code_tools-0.2.5 → claude_code_tools-0.2.6}/pyproject.toml +4 -2
  6. {claude_code_tools-0.2.5 → claude_code_tools-0.2.6}/.gitignore +0 -0
  7. {claude_code_tools-0.2.5 → claude_code_tools-0.2.6}/LICENSE +0 -0
  8. {claude_code_tools-0.2.5 → claude_code_tools-0.2.6}/claude_code_tools/codex_bridge_mcp.py +0 -0
  9. {claude_code_tools-0.2.5 → claude_code_tools-0.2.6}/claude_code_tools/dotenv_vault.py +0 -0
  10. {claude_code_tools-0.2.5 → claude_code_tools-0.2.6}/claude_code_tools/env_safe.py +0 -0
  11. {claude_code_tools-0.2.5 → claude_code_tools-0.2.6}/claude_code_tools/find_claude_session.py +0 -0
  12. {claude_code_tools-0.2.5 → claude_code_tools-0.2.6}/claude_code_tools/find_codex_session.py +0 -0
  13. {claude_code_tools-0.2.5 → claude_code_tools-0.2.6}/claude_code_tools/tmux_cli_controller.py +0 -0
  14. {claude_code_tools-0.2.5 → claude_code_tools-0.2.6}/claude_code_tools/tmux_remote_controller.py +0 -0
  15. {claude_code_tools-0.2.5 → claude_code_tools-0.2.6}/docs/cc-codex-instructions.md +0 -0
  16. {claude_code_tools-0.2.5 → claude_code_tools-0.2.6}/docs/claude-code-chutes.md +0 -0
  17. {claude_code_tools-0.2.5 → claude_code_tools-0.2.6}/docs/claude-code-tmux-tutorials.md +0 -0
  18. {claude_code_tools-0.2.5 → claude_code_tools-0.2.6}/docs/dot-zshrc.md +0 -0
  19. {claude_code_tools-0.2.5 → claude_code_tools-0.2.6}/docs/find-claude-session.md +0 -0
  20. {claude_code_tools-0.2.5 → claude_code_tools-0.2.6}/docs/lmsh.md +0 -0
  21. {claude_code_tools-0.2.5 → claude_code_tools-0.2.6}/docs/reddit-post.md +0 -0
  22. {claude_code_tools-0.2.5 → claude_code_tools-0.2.6}/docs/tmux-cli-instructions.md +0 -0
  23. {claude_code_tools-0.2.5 → claude_code_tools-0.2.6}/docs/vault-documentation.md +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: claude-code-tools
3
- Version: 0.2.5
3
+ Version: 0.2.6
4
4
  Summary: Collection of tools for working with Claude Code
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.11
@@ -23,6 +23,7 @@ and other CLI coding agents.
23
23
  - [🚀 Quick Start](#quick-start)
24
24
  - [🎮 tmux-cli Deep Dive](#tmux-cli-deep-dive)
25
25
  - [🚀 lmsh (Experimental) — natural language to shell commands](#lmsh-experimental)
26
+ - [🔍 find-session — unified search across Claude & Codex sessions](#find-session)
26
27
  - [🔍 find-claude-session — search and resume Claude sessions](#find-claude-session)
27
28
  - [🔍 find-codex-session — search and resume Codex sessions](#find-codex-session)
28
29
  - [🔐 vault — encrypted .env backup & sync](#vault)
@@ -81,6 +82,7 @@ uv tool install git+https://github.com/pchalasani/claude-code-tools
81
82
 
82
83
  This gives you:
83
84
  - `tmux-cli` - The interactive CLI controller we just covered
85
+ - `find-session` - Unified search across Claude Code and Codex sessions
84
86
  - `find-claude-session` - Search and resume Claude Code sessions by keywords
85
87
  - `find-codex-session` - Search and resume Codex sessions by keywords
86
88
  - `vault` - Encrypted backup for your .env files
@@ -175,6 +177,86 @@ cp target/release/lmsh ~/.cargo/bin/
175
177
 
176
178
  See [docs/lmsh.md](docs/lmsh.md) for details.
177
179
 
180
+ <a id="find-session"></a>
181
+ ## 🔍 find-session
182
+
183
+ **Unified session finder** - Search across both Claude Code and Codex sessions simultaneously.
184
+
185
+ > **⚠️ Note about the `fs` command**: For convenience, this tool is available as both `find-session` and `fs`. The short `fs` alias may conflict with existing commands or aliases in your shell. If you have a conflict, you can:
186
+ > - Use the full `find-session` command instead
187
+ > - Create your own alias: `alias mysearch='find-session'`
188
+ > - Uninstall and reinstall without the `fs` entry point (edit `pyproject.toml`)
189
+
190
+ ### Usage
191
+
192
+ ```bash
193
+ # Search all agents in current project
194
+ fs "keyword1,keyword2"
195
+
196
+ # Show all sessions across all agents in current project
197
+ fs
198
+
199
+ # Search across all projects (Claude + Codex)
200
+ fs "keywords" -g
201
+
202
+ # Show all sessions across all projects
203
+ fs -g
204
+
205
+ # Search only specific agent(s)
206
+ fs "bug,fix" --agents claude
207
+ fs "error" --agents codex
208
+
209
+ # Limit number of results
210
+ fs "keywords" -n 15
211
+ ```
212
+
213
+ ### Features
214
+
215
+ - **Multi-agent search**: Searches both Claude Code and Codex sessions simultaneously
216
+ - **Unified display**: Single table showing sessions from all agents with agent column
217
+ - **Smart resume**: Automatically uses correct CLI tool (`claude` or `codex`) based on selected session
218
+ - **Optional keyword search**: Keywords are optional—omit them to show all sessions
219
+ - **Action menu** after session selection:
220
+ - Resume session (default)
221
+ - Show session file path
222
+ - Copy session file to file (*.jsonl) or directory
223
+ - **Project filtering**: Search current project only (default) or all projects with `-g`
224
+ - **Agent filtering**: Use `--agents claude codex` to search specific agents only
225
+ - **Configurable**: Optional config file at `~/.config/find-session/config.json` for customizing agents
226
+ - Interactive session selection with Rich table display
227
+ - Shows agent, project, git branch, date, line count, and preview
228
+ - Reverse chronological ordering (most recent first)
229
+ - Press Enter to cancel (no need for Ctrl+C)
230
+
231
+ ### Configuration (Optional)
232
+
233
+ Create `~/.config/find-session/config.json` to customize agent settings:
234
+
235
+ ```json
236
+ {
237
+ "agents": [
238
+ {
239
+ "name": "claude",
240
+ "display_name": "Claude",
241
+ "home_dir": "~/.claude",
242
+ "enabled": true
243
+ },
244
+ {
245
+ "name": "codex",
246
+ "display_name": "Codex",
247
+ "home_dir": "~/.codex",
248
+ "enabled": true
249
+ }
250
+ ]
251
+ }
252
+ ```
253
+
254
+ This allows you to:
255
+ - Enable/disable specific agents
256
+ - Override default home directories
257
+ - Customize display names
258
+ - Prepare for future agent additions
259
+
178
260
  <a id="find-claude-session"></a>
179
261
  ## 🔍 find-claude-session
180
262
 
@@ -9,6 +9,7 @@ and other CLI coding agents.
9
9
  - [🚀 Quick Start](#quick-start)
10
10
  - [🎮 tmux-cli Deep Dive](#tmux-cli-deep-dive)
11
11
  - [🚀 lmsh (Experimental) — natural language to shell commands](#lmsh-experimental)
12
+ - [🔍 find-session — unified search across Claude & Codex sessions](#find-session)
12
13
  - [🔍 find-claude-session — search and resume Claude sessions](#find-claude-session)
13
14
  - [🔍 find-codex-session — search and resume Codex sessions](#find-codex-session)
14
15
  - [🔐 vault — encrypted .env backup & sync](#vault)
@@ -67,6 +68,7 @@ uv tool install git+https://github.com/pchalasani/claude-code-tools
67
68
 
68
69
  This gives you:
69
70
  - `tmux-cli` - The interactive CLI controller we just covered
71
+ - `find-session` - Unified search across Claude Code and Codex sessions
70
72
  - `find-claude-session` - Search and resume Claude Code sessions by keywords
71
73
  - `find-codex-session` - Search and resume Codex sessions by keywords
72
74
  - `vault` - Encrypted backup for your .env files
@@ -161,6 +163,86 @@ cp target/release/lmsh ~/.cargo/bin/
161
163
 
162
164
  See [docs/lmsh.md](docs/lmsh.md) for details.
163
165
 
166
+ <a id="find-session"></a>
167
+ ## 🔍 find-session
168
+
169
+ **Unified session finder** - Search across both Claude Code and Codex sessions simultaneously.
170
+
171
+ > **⚠️ Note about the `fs` command**: For convenience, this tool is available as both `find-session` and `fs`. The short `fs` alias may conflict with existing commands or aliases in your shell. If you have a conflict, you can:
172
+ > - Use the full `find-session` command instead
173
+ > - Create your own alias: `alias mysearch='find-session'`
174
+ > - Uninstall and reinstall without the `fs` entry point (edit `pyproject.toml`)
175
+
176
+ ### Usage
177
+
178
+ ```bash
179
+ # Search all agents in current project
180
+ fs "keyword1,keyword2"
181
+
182
+ # Show all sessions across all agents in current project
183
+ fs
184
+
185
+ # Search across all projects (Claude + Codex)
186
+ fs "keywords" -g
187
+
188
+ # Show all sessions across all projects
189
+ fs -g
190
+
191
+ # Search only specific agent(s)
192
+ fs "bug,fix" --agents claude
193
+ fs "error" --agents codex
194
+
195
+ # Limit number of results
196
+ fs "keywords" -n 15
197
+ ```
198
+
199
+ ### Features
200
+
201
+ - **Multi-agent search**: Searches both Claude Code and Codex sessions simultaneously
202
+ - **Unified display**: Single table showing sessions from all agents with agent column
203
+ - **Smart resume**: Automatically uses correct CLI tool (`claude` or `codex`) based on selected session
204
+ - **Optional keyword search**: Keywords are optional—omit them to show all sessions
205
+ - **Action menu** after session selection:
206
+ - Resume session (default)
207
+ - Show session file path
208
+ - Copy session file to file (*.jsonl) or directory
209
+ - **Project filtering**: Search current project only (default) or all projects with `-g`
210
+ - **Agent filtering**: Use `--agents claude codex` to search specific agents only
211
+ - **Configurable**: Optional config file at `~/.config/find-session/config.json` for customizing agents
212
+ - Interactive session selection with Rich table display
213
+ - Shows agent, project, git branch, date, line count, and preview
214
+ - Reverse chronological ordering (most recent first)
215
+ - Press Enter to cancel (no need for Ctrl+C)
216
+
217
+ ### Configuration (Optional)
218
+
219
+ Create `~/.config/find-session/config.json` to customize agent settings:
220
+
221
+ ```json
222
+ {
223
+ "agents": [
224
+ {
225
+ "name": "claude",
226
+ "display_name": "Claude",
227
+ "home_dir": "~/.claude",
228
+ "enabled": true
229
+ },
230
+ {
231
+ "name": "codex",
232
+ "display_name": "Codex",
233
+ "home_dir": "~/.codex",
234
+ "enabled": true
235
+ }
236
+ ]
237
+ }
238
+ ```
239
+
240
+ This allows you to:
241
+ - Enable/disable specific agents
242
+ - Override default home directories
243
+ - Customize display names
244
+ - Prepare for future agent additions
245
+
164
246
  <a id="find-claude-session"></a>
165
247
  ## 🔍 find-claude-session
166
248
 
@@ -1,3 +1,3 @@
1
1
  """Claude Code Tools - Collection of utilities for Claude Code."""
2
2
 
3
- __version__ = "0.2.5"
3
+ __version__ = "0.2.6"
@@ -0,0 +1,438 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Unified session finder - search across multiple coding agents (Claude Code, Codex, etc.)
4
+
5
+ Usage:
6
+ find-session [keywords] [OPTIONS]
7
+ fs [keywords] [OPTIONS] # via shell wrapper
8
+
9
+ Examples:
10
+ find-session "langroid,MCP" # Search all agents in current project
11
+ find-session -g # Show all sessions across all projects
12
+ find-session "bug" --agents claude # Search only Claude sessions
13
+ """
14
+
15
+ import argparse
16
+ import json
17
+ import os
18
+ import sys
19
+ from dataclasses import dataclass
20
+ from pathlib import Path
21
+ from typing import List, Optional
22
+
23
+ # Import search functions from existing tools
24
+ from claude_code_tools.find_claude_session import (
25
+ find_sessions as find_claude_sessions,
26
+ resume_session as resume_claude_session,
27
+ get_session_file_path as get_claude_session_file_path,
28
+ copy_session_file as copy_claude_session_file,
29
+ )
30
+ from claude_code_tools.find_codex_session import (
31
+ find_sessions as find_codex_sessions,
32
+ resume_session as resume_codex_session,
33
+ get_codex_home,
34
+ copy_session_file as copy_codex_session_file,
35
+ )
36
+
37
+ try:
38
+ from rich.console import Console
39
+ from rich.table import Table
40
+ from rich import box
41
+
42
+ RICH_AVAILABLE = True
43
+ except ImportError:
44
+ RICH_AVAILABLE = False
45
+
46
+
47
+ @dataclass
48
+ class AgentConfig:
49
+ """Configuration for a coding agent."""
50
+
51
+ name: str # Internal name (e.g., "claude", "codex")
52
+ display_name: str # Display name (e.g., "Claude", "Codex")
53
+ home_dir: Optional[str] # Custom home directory (None = default)
54
+ enabled: bool = True
55
+
56
+
57
+ def get_default_agents() -> List[AgentConfig]:
58
+ """Get default agent configurations."""
59
+ return [
60
+ AgentConfig(name="claude", display_name="Claude", home_dir=None),
61
+ AgentConfig(name="codex", display_name="Codex", home_dir=None),
62
+ ]
63
+
64
+
65
+ def load_config() -> List[AgentConfig]:
66
+ """Load agent configuration from config file or use defaults."""
67
+ config_path = Path.home() / ".config" / "find-session" / "config.json"
68
+
69
+ if config_path.exists():
70
+ try:
71
+ with open(config_path, "r") as f:
72
+ data = json.load(f)
73
+ agents = []
74
+ for agent_data in data.get("agents", []):
75
+ agents.append(
76
+ AgentConfig(
77
+ name=agent_data["name"],
78
+ display_name=agent_data.get(
79
+ "display_name", agent_data["name"].title()
80
+ ),
81
+ home_dir=agent_data.get("home_dir"),
82
+ enabled=agent_data.get("enabled", True),
83
+ )
84
+ )
85
+ return agents
86
+ except (json.JSONDecodeError, KeyError, IOError):
87
+ pass
88
+
89
+ # Return defaults if config doesn't exist or is invalid
90
+ return get_default_agents()
91
+
92
+
93
+ def search_all_agents(
94
+ keywords: List[str],
95
+ global_search: bool = False,
96
+ num_matches: int = 10,
97
+ agents: Optional[List[str]] = None,
98
+ claude_home: Optional[str] = None,
99
+ codex_home: Optional[str] = None,
100
+ ) -> List[dict]:
101
+ """
102
+ Search sessions across all enabled agents.
103
+
104
+ Returns list of dicts with agent metadata added.
105
+ """
106
+ agent_configs = load_config()
107
+
108
+ # Filter by requested agents if specified
109
+ if agents:
110
+ agent_configs = [a for a in agent_configs if a.name in agents]
111
+
112
+ # Filter by enabled agents
113
+ agent_configs = [a for a in agent_configs if a.enabled]
114
+
115
+ all_sessions = []
116
+
117
+ for agent_config in agent_configs:
118
+ if agent_config.name == "claude":
119
+ # Search Claude sessions
120
+ home = claude_home or agent_config.home_dir
121
+ sessions = find_claude_sessions(
122
+ keywords, global_search=global_search, claude_home=home
123
+ )
124
+
125
+ # Add agent metadata to each session
126
+ for session in sessions:
127
+ session_dict = {
128
+ "agent": "claude",
129
+ "agent_display": agent_config.display_name,
130
+ "session_id": session[0],
131
+ "mod_time": session[1],
132
+ "create_time": session[2],
133
+ "lines": session[3],
134
+ "project": session[4],
135
+ "preview": session[5],
136
+ "cwd": session[6],
137
+ "branch": session[7] if len(session) > 7 else "",
138
+ "claude_home": home,
139
+ }
140
+ all_sessions.append(session_dict)
141
+
142
+ elif agent_config.name == "codex":
143
+ # Search Codex sessions
144
+ home = codex_home or agent_config.home_dir
145
+ codex_home_path = get_codex_home(home)
146
+
147
+ if codex_home_path.exists():
148
+ sessions = find_codex_sessions(
149
+ codex_home_path,
150
+ keywords,
151
+ num_matches=num_matches * 2, # Get more for merging
152
+ global_search=global_search,
153
+ )
154
+
155
+ # Add agent metadata to each session
156
+ for session in sessions:
157
+ session_dict = {
158
+ "agent": "codex",
159
+ "agent_display": agent_config.display_name,
160
+ "session_id": session["session_id"],
161
+ "mod_time": session["mod_time"],
162
+ "create_time": session.get("mod_time"), # Codex doesn't separate these
163
+ "lines": session["lines"],
164
+ "project": session["project"],
165
+ "preview": session["preview"],
166
+ "cwd": session["cwd"],
167
+ "branch": session.get("branch", ""),
168
+ "file_path": session.get("file_path", ""),
169
+ }
170
+ all_sessions.append(session_dict)
171
+
172
+ # Sort by modification time (newest first) and limit
173
+ all_sessions.sort(key=lambda x: x["mod_time"], reverse=True)
174
+ return all_sessions[:num_matches]
175
+
176
+
177
+ def display_interactive_ui(
178
+ sessions: List[dict], keywords: List[str], stderr_mode: bool = False, num_matches: int = 10
179
+ ) -> Optional[dict]:
180
+ """Display unified session selection UI."""
181
+ if not RICH_AVAILABLE:
182
+ return None
183
+
184
+ # Use stderr console if in stderr mode
185
+ ui_console = Console(file=sys.stderr) if stderr_mode else Console()
186
+
187
+ # Limit to specified number of sessions
188
+ display_sessions = sessions[:num_matches]
189
+
190
+ if not display_sessions:
191
+ ui_console.print("[red]No sessions found[/red]")
192
+ return None
193
+
194
+ # Create table
195
+ title = (
196
+ f"Sessions matching: {', '.join(keywords)}" if keywords else "All sessions"
197
+ )
198
+ table = Table(
199
+ title=title, box=box.ROUNDED, show_header=True, header_style="bold cyan"
200
+ )
201
+
202
+ table.add_column("#", style="bold yellow", width=3)
203
+ table.add_column("Agent", style="magenta", width=6)
204
+ table.add_column("Session ID", style="dim", width=10)
205
+ table.add_column("Project", style="green")
206
+ table.add_column("Branch", style="cyan")
207
+ table.add_column("Date", style="blue")
208
+ table.add_column("Lines", style="cyan", justify="right", width=6)
209
+ table.add_column("Last User Message", style="white", max_width=50, overflow="fold")
210
+
211
+ for idx, session in enumerate(display_sessions, 1):
212
+ # Format date from mod_time
213
+ from datetime import datetime
214
+
215
+ mod_time = session["mod_time"]
216
+ date_str = datetime.fromtimestamp(mod_time).strftime("%m/%d %H:%M")
217
+
218
+ branch_display = session.get("branch", "") or "N/A"
219
+
220
+ table.add_row(
221
+ str(idx),
222
+ session["agent_display"],
223
+ session["session_id"][:8] + "...",
224
+ session["project"],
225
+ branch_display,
226
+ date_str,
227
+ str(session["lines"]),
228
+ session["preview"],
229
+ )
230
+
231
+ ui_console.print(table)
232
+ ui_console.print("\n[bold]Select a session:[/bold]")
233
+ ui_console.print(f" • Enter number (1-{len(display_sessions)}) to select")
234
+ ui_console.print(" • Press Enter to cancel\n")
235
+
236
+ while True:
237
+ try:
238
+ from rich.prompt import Prompt
239
+
240
+ choice = Prompt.ask(
241
+ "Your choice", default="", show_default=False, console=ui_console
242
+ )
243
+
244
+ # Handle empty input - cancel
245
+ if not choice or not choice.strip():
246
+ ui_console.print("[yellow]Cancelled[/yellow]")
247
+ return None
248
+
249
+ idx = int(choice) - 1
250
+ if 0 <= idx < len(display_sessions):
251
+ return display_sessions[idx]
252
+ else:
253
+ ui_console.print("[red]Invalid choice. Please try again.[/red]")
254
+
255
+ except KeyboardInterrupt:
256
+ ui_console.print("\n[yellow]Cancelled[/yellow]")
257
+ return None
258
+ except EOFError:
259
+ ui_console.print("\n[yellow]Cancelled (EOF)[/yellow]")
260
+ return None
261
+ except ValueError:
262
+ ui_console.print("[red]Invalid choice. Please try again.[/red]")
263
+
264
+
265
+ def show_action_menu(session: dict) -> Optional[str]:
266
+ """Show action menu for selected session."""
267
+ print(f"\n=== Session: {session['session_id'][:16]}... ===")
268
+ print(f"Agent: {session['agent_display']}")
269
+ print(f"Project: {session['project']}")
270
+ if session.get("branch"):
271
+ print(f"Branch: {session['branch']}")
272
+ print(f"\nWhat would you like to do?")
273
+ print("1. Resume session (default)")
274
+ print("2. Show session file path")
275
+ print("3. Copy session file to file (*.jsonl) or directory")
276
+ print()
277
+
278
+ try:
279
+ choice = input("Enter choice [1-3] (or Enter for 1): ").strip()
280
+ if not choice or choice == "1":
281
+ return "resume"
282
+ elif choice == "2":
283
+ return "path"
284
+ elif choice == "3":
285
+ return "copy"
286
+ else:
287
+ print("Invalid choice.")
288
+ return None
289
+ except KeyboardInterrupt:
290
+ print("\nCancelled.")
291
+ return None
292
+
293
+
294
+ def handle_action(session: dict, action: str, shell_mode: bool = False) -> None:
295
+ """Handle the selected action based on agent type."""
296
+ agent = session["agent"]
297
+
298
+ if action == "resume":
299
+ if agent == "claude":
300
+ resume_claude_session(
301
+ session["session_id"],
302
+ session["cwd"],
303
+ shell_mode=shell_mode,
304
+ claude_home=session.get("claude_home"),
305
+ )
306
+ elif agent == "codex":
307
+ resume_codex_session(
308
+ session["session_id"], session["cwd"], shell_mode=shell_mode
309
+ )
310
+
311
+ elif action == "path":
312
+ if agent == "claude":
313
+ file_path = get_claude_session_file_path(
314
+ session["session_id"],
315
+ session["cwd"],
316
+ claude_home=session.get("claude_home"),
317
+ )
318
+ print(f"\nSession file path:")
319
+ print(file_path)
320
+ elif agent == "codex":
321
+ print(f"\nSession file path:")
322
+ print(session.get("file_path", "Unknown"))
323
+
324
+ elif action == "copy":
325
+ if agent == "claude":
326
+ file_path = get_claude_session_file_path(
327
+ session["session_id"],
328
+ session["cwd"],
329
+ claude_home=session.get("claude_home"),
330
+ )
331
+ copy_claude_session_file(file_path)
332
+ elif agent == "codex":
333
+ copy_codex_session_file(session.get("file_path", ""))
334
+
335
+
336
+ def main():
337
+ parser = argparse.ArgumentParser(
338
+ description="Unified session finder - search across multiple coding agents",
339
+ formatter_class=argparse.RawDescriptionHelpFormatter,
340
+ epilog="""
341
+ Examples:
342
+ find-session "langroid,MCP" # Search all agents in current project
343
+ find-session -g # Show all sessions across all projects
344
+ find-session "bug" --agents claude # Search only Claude sessions
345
+ find-session "error" --agents codex # Search only Codex sessions
346
+ """,
347
+ )
348
+ parser.add_argument(
349
+ "keywords",
350
+ nargs="?",
351
+ default="",
352
+ help="Comma-separated keywords to search (AND logic). If omitted, shows all sessions.",
353
+ )
354
+ parser.add_argument(
355
+ "-g",
356
+ "--global",
357
+ dest="global_search",
358
+ action="store_true",
359
+ help="Search across all projects, not just the current one",
360
+ )
361
+ parser.add_argument(
362
+ "-n",
363
+ "--num-matches",
364
+ type=int,
365
+ default=10,
366
+ help="Number of matching sessions to display (default: 10)",
367
+ )
368
+ parser.add_argument(
369
+ "--agents",
370
+ nargs="+",
371
+ choices=["claude", "codex"],
372
+ help="Limit search to specific agents (default: all)",
373
+ )
374
+ parser.add_argument(
375
+ "--shell",
376
+ action="store_true",
377
+ help="Output shell commands for evaluation (for use with shell function)",
378
+ )
379
+ parser.add_argument(
380
+ "--claude-home", type=str, help="Path to Claude home directory (default: ~/.claude)"
381
+ )
382
+ parser.add_argument(
383
+ "--codex-home", type=str, help="Path to Codex home directory (default: ~/.codex)"
384
+ )
385
+
386
+ args = parser.parse_args()
387
+
388
+ # Parse keywords
389
+ keywords = (
390
+ [k.strip() for k in args.keywords.split(",") if k.strip()]
391
+ if args.keywords
392
+ else []
393
+ )
394
+
395
+ # Search all agents
396
+ matching_sessions = search_all_agents(
397
+ keywords,
398
+ global_search=args.global_search,
399
+ num_matches=args.num_matches,
400
+ agents=args.agents,
401
+ claude_home=args.claude_home,
402
+ codex_home=args.codex_home,
403
+ )
404
+
405
+ if not matching_sessions:
406
+ scope = "all projects" if args.global_search else "current project"
407
+ keyword_msg = (
408
+ f" containing all keywords: {', '.join(keywords)}" if keywords else ""
409
+ )
410
+ if RICH_AVAILABLE:
411
+ console = Console()
412
+ console.print(f"[yellow]No sessions found{keyword_msg} in {scope}[/yellow]")
413
+ else:
414
+ print(f"No sessions found{keyword_msg} in {scope}", file=sys.stderr)
415
+ sys.exit(0)
416
+
417
+ # Display interactive UI
418
+ if RICH_AVAILABLE:
419
+ selected_session = display_interactive_ui(
420
+ matching_sessions, keywords, stderr_mode=args.shell, num_matches=args.num_matches
421
+ )
422
+ if selected_session:
423
+ # Show action menu
424
+ action = show_action_menu(selected_session)
425
+ if action:
426
+ handle_action(selected_session, action, shell_mode=args.shell)
427
+ else:
428
+ # Fallback without rich
429
+ print("\nMatching sessions:")
430
+ for idx, session in enumerate(matching_sessions[: args.num_matches], 1):
431
+ print(
432
+ f"{idx}. [{session['agent_display']}] {session['session_id'][:16]}... | "
433
+ f"{session['project']} | {session.get('branch', 'N/A')}"
434
+ )
435
+
436
+
437
+ if __name__ == "__main__":
438
+ main()
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "claude-code-tools"
3
- version = "0.2.5"
3
+ version = "0.2.6"
4
4
  description = "Collection of tools for working with Claude Code"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -17,6 +17,8 @@ dev = ["commitizen>=3.0.0"]
17
17
  [project.scripts]
18
18
  find-claude-session = "claude_code_tools.find_claude_session:main"
19
19
  find-codex-session = "claude_code_tools.find_codex_session:main"
20
+ find-session = "claude_code_tools.find_session:main"
21
+ fs = "claude_code_tools.find_session:main"
20
22
  vault = "claude_code_tools.dotenv_vault:main"
21
23
  tmux-cli = "claude_code_tools.tmux_cli_controller:main"
22
24
  env-safe = "claude_code_tools.env_safe:main"
@@ -43,7 +45,7 @@ exclude = [
43
45
 
44
46
  [tool.commitizen]
45
47
  name = "cz_conventional_commits"
46
- version = "0.2.5"
48
+ version = "0.2.6"
47
49
  tag_format = "v$version"
48
50
  version_files = [
49
51
  "pyproject.toml:version",