feat(claudbg-x45o): sessions list scopes to current project by default

Add --scope=project|user flag to `sessions list` (default: project).
When project scope is active, only sessions whose project_path matches
the current working directory are shown. Use --scope=user to see all.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
main
Elijah Voigt 2 months ago
parent 0c6ecf1165
commit f45a0dfd87

@ -1,10 +1,18 @@
---
# claudbg-x45o
title: 'sessions list: scope to current project by default with --scope flag'
status: todo
status: completed
type: feature
priority: normal
created_at: 2026-03-31T23:44:50Z
updated_at: 2026-03-31T23:44:50Z
updated_at: 2026-04-01T05:58:09Z
---
When run inside a project directory, `claudbg sessions` should only list sessions for that project (similar to how `claude /continue` lists sessions for that project). Override with `--scope=[user|project|local]` — default is 'project'.
## Summary of Changes
Added `SessionScope` enum (`Project` | `User`) to `src/cli.rs`.
Added `--scope` flag to `SessionsCmd::List` with default `project`.
Updated `sessions::list()` signature to accept `scope: SessionScope`.
When scope is `Project`, resolves the current working directory and filters raw rows to only include sessions whose `project_path` exactly matches the CWD. Scope `User` shows all sessions (previous behavior).

@ -91,6 +91,16 @@ impl Limit {
}
}
/// Controls which sessions are shown in `sessions list`.
#[derive(Debug, Clone, Default, clap::ValueEnum)]
pub enum SessionScope {
/// Show only sessions for the current project directory (default).
#[default]
Project,
/// Show all sessions for the current user.
User,
}
/// Top-level CLI entry point for claudbg.
#[derive(Debug, clap::Parser)]
#[command(name = "claudbg", about = "Claude Code session inspector")]
@ -189,6 +199,9 @@ pub enum SessionsCmd {
/// Example: --filter 'model:haiku' --filter 'agents>0'
#[arg(long, action = clap::ArgAction::Append)]
filter: Vec<String>,
/// Scope of sessions to show: 'project' (current dir only, default) or 'user' (all).
#[arg(long, default_value = "project")]
scope: SessionScope,
},
/// Dump raw messages from a session.
Dump {

@ -222,6 +222,7 @@ impl crate::filter::SessionRow for RawSessionRow {
pub async fn list(
limit: crate::cli::Limit,
filters: Vec<String>,
scope: crate::cli::SessionScope,
opts: &crate::cli::GlobalOpts,
) -> Result<()> {
// Parse all filter expressions up front so we can report errors immediately.
@ -230,6 +231,14 @@ pub async fn list(
.map(|s| crate::filter::Filter::parse(s))
.collect::<Result<Vec<_>>>()?;
// Resolve project scope: current working directory when scope == Project.
let project_cwd: Option<String> = match scope {
crate::cli::SessionScope::Project => std::env::current_dir()
.ok()
.map(|p| p.to_string_lossy().into_owned()),
crate::cli::SessionScope::User => None,
};
let db_path = crate::db::connection::default_db_path();
let db = crate::db::connection::open_db(&db_path, false).await?;
@ -290,6 +299,16 @@ pub async fn list(
});
}
// Apply project scope: only show sessions whose project_path matches CWD.
let raw_rows: Vec<RawSessionRow> = if let Some(ref cwd) = project_cwd {
raw_rows
.into_iter()
.filter(|r| &r.project_path == cwd)
.collect()
} else {
raw_rows
};
// Apply filters (AND semantics: all filters must match).
let raw_rows: Vec<RawSessionRow> = raw_rows
.into_iter()
@ -783,7 +802,13 @@ mod tests {
// SAFETY: single-threaded test context; no other threads read HOME concurrently.
unsafe { std::env::set_var("HOME", dir.path()) };
let opts = default_opts();
let result = list(crate::cli::Limit::default(), vec![], &opts).await;
let result = list(
crate::cli::Limit::default(),
vec![],
crate::cli::SessionScope::User,
&opts,
)
.await;
assert!(result.is_ok(), "list failed: {:?}", result.err());
}
@ -799,7 +824,13 @@ mod tests {
output: OutputFormat::Json,
..default_opts()
};
let result = list(crate::cli::Limit::default(), vec![], &opts).await;
let result = list(
crate::cli::Limit::default(),
vec![],
crate::cli::SessionScope::User,
&opts,
)
.await;
assert!(result.is_ok(), "list json failed: {:?}", result.err());
}
@ -815,7 +846,13 @@ mod tests {
output: OutputFormat::Xml,
..default_opts()
};
let result = list(crate::cli::Limit::default(), vec![], &opts).await;
let result = list(
crate::cli::Limit::default(),
vec![],
crate::cli::SessionScope::User,
&opts,
)
.await;
assert!(result.is_ok(), "list xml failed: {:?}", result.err());
}

@ -1,15 +1,23 @@
//! claudbg binary entry point.
use clap::Parser;
use claudbg::cli::{AgentsCmd, Cli, Commands, SessionsCmd};
use claudbg::cli::{AgentsCmd, Cli, Commands, SessionScope, SessionsCmd};
use claudbg::error::Result;
#[tokio::main]
async fn main() -> Result<()> {
let cli = Cli::parse();
match cli.command {
Commands::Sessions { cmd } => match cmd.unwrap_or(SessionsCmd::List { limit: Default::default(), filter: vec![] }) {
SessionsCmd::List { limit, filter } => claudbg::commands::sessions::list(limit, filter, &cli.global).await?,
Commands::Sessions { cmd } => match cmd.unwrap_or(SessionsCmd::List {
limit: Default::default(),
filter: vec![],
scope: SessionScope::Project,
}) {
SessionsCmd::List {
limit,
filter,
scope,
} => claudbg::commands::sessions::list(limit, filter, scope, &cli.global).await?,
SessionsCmd::Dump { id, follow } => {
claudbg::commands::sessions::dump(&id, follow, &cli.global).await?
}
@ -18,9 +26,11 @@ async fn main() -> Result<()> {
}
},
Commands::Agents { cmd } => match cmd {
AgentsCmd::List { session_id, limit, filter } => {
claudbg::commands::agents::list(&session_id, limit, filter, &cli.global).await?
}
AgentsCmd::List {
session_id,
limit,
filter,
} => claudbg::commands::agents::list(&session_id, limit, filter, &cli.global).await?,
AgentsCmd::Dump {
session_id,
agent_id,

Loading…
Cancel
Save