Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
8a4c6ee
feat(tb-session): scaffold crate with workspace integration
dafilipaj Mar 23, 2026
ace62db
feat(tb-session): add index schema with FTS5, ensure_fresh skeleton
dafilipaj Mar 23, 2026
abbef5f
feat(tb-session): add config module with load/save/defaults
dafilipaj Mar 23, 2026
f1bb813
feat(tb-session): implement JSONL file scanner with sessions-index.js…
dafilipaj Mar 23, 2026
b28721a
feat(tb-session): implement JSONL parser for user/assistant message e…
dafilipaj Mar 23, 2026
ab727b1
feat(tb-session): implement index builder with FTS5 message insertion
dafilipaj Mar 23, 2026
fe13a54
feat(tb-session): add list command with metadata-only pagination
dafilipaj Mar 23, 2026
92a244b
feat(tb-session): implement search command with FTS5 and metadata fil…
dafilipaj Mar 23, 2026
679b8c1
feat(tb-session): add show and resume commands
dafilipaj Mar 23, 2026
8ef1f4b
feat(tb-session): add index, doctor, and cache-clear utility commands
dafilipaj Mar 23, 2026
da93f0b
feat(tb-session): add prime command and SKILL.md for Claude integration
dafilipaj Mar 23, 2026
492f065
fix(tb-session): polish and fix issues from code review
dafilipaj Mar 23, 2026
26e9979
chore: add tb-session to distribution scripts
dafilipaj Mar 23, 2026
b87a561
fix(tb-session): apply clippy auto-fixes
dafilipaj Mar 23, 2026
6bfd20b
fix(tb-session): fix FTS5 snippet with GROUP BY, deduplicate search r…
dafilipaj Mar 23, 2026
34523ba
fix(tb-session): use neutral label for claude binary check in doctor
dafilipaj Mar 23, 2026
066b096
fix(tb-session): handle versioned sessions-index.json format
dafilipaj Mar 23, 2026
6954639
feat(tb-session): worktree-aware session scoping for list and search
dafilipaj Mar 23, 2026
feb800d
feat(tb-session): resume accepts names and search terms, not just UUIDs
dafilipaj Mar 23, 2026
45f80d7
fix(tb-session): sanitize FTS5 search queries to handle URLs and spec…
dafilipaj Mar 24, 2026
93eb5dc
fix(tb-session): try UUID prefix match before name search in resume
dafilipaj Mar 24, 2026
c00e48e
fix(tb-session): address PR review feedback — 4 issues
dafilipaj Mar 24, 2026
b38f9ed
feat(tb-session): add --pr filter to search command
dafilipaj Mar 24, 2026
94014c9
fix(tb-session): address PR review batch 2 — 3 issues
dafilipaj Mar 24, 2026
fbc179b
test(tb-session): add 16 tests for uncovered pure functions and edge …
dafilipaj Mar 24, 2026
c9cb0a0
docs(tb-session): update SKILL.md with new features
dafilipaj Mar 24, 2026
e0686ce
fix(tb-session): always cd in new terminal tab when resuming
dafilipaj Mar 24, 2026
926e723
Merge remote-tracking branch 'origin/main' into feature/tb-session
dafilipaj Mar 24, 2026
4fecc99
style(tb-session): apply cargo fmt
dafilipaj Mar 24, 2026
39b33cc
fix(tb-session): always single-quote in shell_escape to prevent injec…
dafilipaj Mar 30, 2026
a63963e
Merge remote-tracking branch 'origin/main' into fix/shell-escape-secu…
dafilipaj Mar 30, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 110 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ indicatif = "0.18"
inquire = "0.7"

# Database
rusqlite = { version = "0.38", features = ["bundled"] }
rusqlite = { version = "0.38", features = ["bundled", "modern_sqlite"] }

# Error handling
thiserror = "2.0"
Expand All @@ -45,6 +45,7 @@ tb-sem = { path = "crates/tb-sem" }
tb-prod = { path = "crates/tb-prod" }
tb-bug = { path = "crates/tb-bug" }
tb-devctl = { path = "crates/tb-devctl" }
tb-session = { path = "crates/tb-session" }

# Dev/test (also in workspace.dependencies so crates can inherit them)
assert_cmd = "2"
Expand Down
32 changes: 32 additions & 0 deletions crates/tb-session/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
[package]
name = "tb-session"
version = "0.1.0"
edition = "2024"
description = "Claude Code session search CLI"
authors.workspace = true
license.workspace = true
repository.workspace = true

[[bin]]
name = "tb-session"
path = "src/main.rs"

[lib]
doctest = false

[dependencies]
toolbox-core = { workspace = true, features = ["version-check"] }
clap.workspace = true
serde.workspace = true
serde_json.workspace = true
chrono.workspace = true
thiserror.workspace = true
colored.workspace = true
rusqlite.workspace = true
toml.workspace = true
dirs = "6"

[dev-dependencies]
assert_cmd.workspace = true
predicates.workspace = true
tempfile.workspace = true
61 changes: 61 additions & 0 deletions crates/tb-session/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
---
name: tb-session
description: Search and manage Claude Code sessions. Use when the user references past sessions, wants to find prior work, or needs to resume a specific conversation.
---

# tb-session

Claude Code session search CLI. Full-text search across session history with metadata filtering. Built for AI agent consumption but works for humans too.

## Capabilities

- **Search** — full-text search across messages with BM25 ranking, `--pr` filter, metadata filters
- **List** — browse sessions by metadata (branch, date, project), worktree-aware
- **Show** — session detail with conversation preview
- **Resume** — resume a past session in a **new terminal tab** (accepts UUIDs, prefixes, or name search)

## When to use

- User references prior work: "remember when we...", "that session where..."
- User asks about a specific PR: use `--pr` to find sessions mentioning it
- User asks to find or resume a past session
- Before starting work: check if a prior session already started the same task
- Use `--json` for programmatic access when processing results

## Quick reference

```bash
# Search by content
tb-session search "authentication middleware"
tb-session search "budget calculation" --all-projects

# Search by PR (number or URL)
tb-session search --pr 557
tb-session search --pr https://github.com/org/repo/pull/123

# List recent sessions
tb-session list
tb-session list --all-projects --limit 20

# Show session details (prefix match works)
tb-session show bcb7ff

# Resume by UUID prefix or name
tb-session resume bcb7ffed
tb-session resume "auth refactor"
```

## Important

- **Resume opens a new terminal tab** — Claude sessions can't nest inside each other. When called from within Claude Code (non-TTY), `resume` opens a new iTerm/Terminal.app tab, cd's into the original project, and runs `claude --resume`. This is expected — not an error.
- **Worktree-aware by default** — `list` and `search` include sessions from all git worktrees of the same repo. Use `--all-projects` for everything.
- **Resume accepts names** — `resume "auth refactor"` searches summary/first prompt and resumes the most recent match. UUID prefixes of any length also work.
- **URLs work as search queries** — special characters are sanitized automatically.

## Getting started

Run `tb-session prime` for available commands and index status.

## Live context

!`tb-session prime`
27 changes: 27 additions & 0 deletions crates/tb-session/src/commands/cache_clear.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
use crate::config::Config;
use crate::error::Result;

pub fn run() -> Result<()> {
let config = Config::load()?;
let db_path = config.db_path()?;

if db_path.exists() {
std::fs::remove_file(&db_path)?;

// Also remove SQLite WAL and shared-memory sidecar files if present.
let wal = db_path.with_extension("db-wal");
if wal.exists() {
std::fs::remove_file(&wal)?;
}
let shm = db_path.with_extension("db-shm");
if shm.exists() {
std::fs::remove_file(&shm)?;
}

println!("Index cleared: {}", db_path.display());
} else {
println!("No index to clear.");
}

Ok(())
}
34 changes: 34 additions & 0 deletions crates/tb-session/src/commands/config_cmd.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
use crate::config::Config;
use crate::error::Result;

/// Write default config to disk and print its path.
pub fn init() -> Result<()> {
let config = Config::default();
config.save()?;
let path = Config::config_path()?;
println!("Config initialized at: {}", path.display());
Ok(())
}

/// Load and print all config values and resolved paths.
pub fn show() -> Result<()> {
let config = Config::load()?;
let path = Config::config_path()?;

println!("Config file: {}", path.display());
println!("claude_home: {}", config.claude_home);
println!(
"claude_home resolved: {}",
config.claude_home_path().display()
);
println!("projects_dir: {}", config.projects_dir().display());
match config.db_path() {
Ok(p) => println!("db_path: {}", p.display()),
Err(e) => println!("db_path: (error: {})", e),
}
println!("ttl_minutes: {}", config.ttl_minutes);
println!("ttl: {}s", config.ttl().as_secs());
println!("default_limit: {}", config.default_limit);

Ok(())
}
Loading