use std::path::PathBuf;
use std::str::FromStr;
use argh::FromArgs;
use crate::engine::{Client, memory};
mod run;
pub(crate) use run::run;
#[derive(Debug, Clone)]
struct Target {
provider: Client,
id: String,
}
impl Target {
fn qualified(&self) -> String {
format!("{}:{}", self.provider.as_str(), self.id)
}
}
impl FromStr for Target {
type Err = String;
fn from_str(value: &str) -> Result<Self, Self::Err> {
let (provider, id) = value
.split_once(':')
.ok_or_else(|| "session must be qualified as provider:id".to_string())?;
if id.is_empty() {
return Err("session id must not be empty".to_string());
}
Ok(Self {
provider: provider.parse::<Client>().map_err(|e| e.to_string())?,
id: id.to_string(),
})
}
}
#[derive(Debug, Clone)]
enum MemoryTarget {
Id(String),
Session(Target),
}
impl FromStr for MemoryTarget {
type Err = String;
fn from_str(value: &str) -> Result<Self, Self::Err> {
if value.starts_with("mem_") {
return Ok(Self::Id(value.to_string()));
}
Target::from_str(value)
.map(Self::Session)
.map_err(|_| "memory target must be mem_<id> or provider:id".to_string())
}
}
#[derive(FromArgs)]
#[argh(description = "browse, search, compact, and learn from coding-agent sessions")]
struct GoosedumpArgs {
#[argh(subcommand)]
command: GoosedumpCommand,
}
#[derive(FromArgs)]
#[argh(subcommand)]
enum GoosedumpCommand {
Session(SessionArgs),
Memory(MemoryArgs),
#[cfg(feature = "bench")]
BenchExpertCache(BenchExpertCacheArgs),
#[cfg(feature = "bench")]
BenchInference(BenchInferenceArgs),
#[cfg(feature = "bench")]
BenchMemory(BenchMemoryArgs),
}
#[cfg(feature = "bench")]
#[derive(FromArgs)]
#[argh(
subcommand,
name = "bench-expert-cache",
description = "run synthetic routed-expert cache checks"
)]
struct BenchExpertCacheArgs {}
#[cfg(feature = "bench")]
#[derive(FromArgs)]
#[argh(
subcommand,
name = "bench-inference",
description = "run the fixed local-inference benchmark"
)]
struct BenchInferenceArgs {
#[argh(switch, description = "evict the text model from the Linux page cache")]
cold: bool,
#[argh(option, default = "8", description = "benchmark prompt repetitions")]
prompt_repetitions: usize,
#[argh(option, default = "4", description = "maximum generated tokens")]
max_tokens: usize,
}
#[cfg(feature = "bench")]
#[derive(FromArgs)]
#[argh(
subcommand,
name = "bench-memory",
description = "run the fixed sequential-memory benchmark"
)]
struct BenchMemoryArgs {}
#[derive(FromArgs)]
#[argh(
subcommand,
name = "session",
description = "list, show, search, compact, copy, or remove sessions"
)]
struct SessionArgs {
#[argh(subcommand)]
command: SessionCommand,
}
#[derive(FromArgs)]
#[argh(subcommand)]
enum SessionCommand {
List(ListSessionsArgs),
Show(ShowSessionArgs),
Find(FindSessionArgs),
Search(SearchSessionArgs),
Compact(CompactSessionArgs),
Copy(CopySessionArgs),
Remove(RemoveSessionArgs),
}
#[derive(FromArgs)]
#[argh(
subcommand,
name = "list",
description = "list discovered sessions as provider:id targets"
)]
struct ListSessionsArgs {
#[argh(option, short = 'q', description = "JSON query filter")]
query: Option<String>,
#[argh(switch, description = "output in JSON format")]
json: bool,
}
#[derive(FromArgs)]
#[argh(subcommand, name = "show", description = "print a session transcript")]
struct ShowSessionArgs {
#[argh(positional)]
target: Target,
#[argh(option, short = 'e', long = "entry", description = "entry ID")]
entries: Vec<String>,
#[argh(switch, description = "all branches")]
all: bool,
#[argh(option, description = "first entry ID")]
from: Option<String>,
#[argh(option, description = "exclusive final entry ID")]
before: Option<String>,
#[argh(option, long = "as", description = "render provider")]
render_as: Option<Client>,
}
#[derive(FromArgs)]
#[argh(
subcommand,
name = "find",
description = "find session messages by shell-style glob"
)]
struct FindSessionArgs {
#[argh(positional)]
target: Target,
#[argh(positional)]
pattern: String,
#[argh(option, short = 'e', long = "entry", description = "entry ID")]
entries: Vec<String>,
#[argh(switch, description = "all branches")]
all: bool,
#[argh(option, description = "first entry ID")]
from: Option<String>,
#[argh(option, description = "exclusive final entry ID")]
before: Option<String>,
#[argh(option, long = "as", description = "render provider")]
render_as: Option<Client>,
}
#[derive(FromArgs)]
#[argh(
subcommand,
name = "search",
description = "rank session messages by relevance"
)]
struct SearchSessionArgs {
#[argh(positional)]
target: Target,
#[argh(positional)]
query: String,
#[argh(option, short = 'e', long = "entry", description = "entry ID")]
entries: Vec<String>,
#[argh(switch, description = "all branches")]
all: bool,
#[argh(option, description = "first entry ID")]
from: Option<String>,
#[argh(option, description = "exclusive final entry ID")]
before: Option<String>,
#[argh(option, short = 'p', description = "result page")]
page: Option<usize>,
#[argh(option, long = "as", description = "render provider")]
render_as: Option<Client>,
}
#[derive(FromArgs)]
#[argh(
subcommand,
name = "compact",
description = "create a compaction summary for a session"
)]
struct CompactSessionArgs {
#[argh(positional)]
target: Target,
#[argh(option, short = 'e', long = "entry", description = "entry ID")]
entries: Vec<String>,
#[argh(switch, description = "all branches")]
all: bool,
#[argh(option, description = "first entry ID")]
from: Option<String>,
#[argh(option, description = "exclusive final entry ID")]
before: Option<String>,
#[argh(option, description = "prior summary")]
previous_summary: Option<String>,
#[argh(
option,
default = "4096",
description = "maximum estimated summary tokens; 0 disables the limit"
)]
summary_max_tokens: usize,
#[argh(option, long = "as", description = "render provider")]
render_as: Option<Client>,
#[argh(switch, description = "screen-readable output")]
plain: bool,
}
#[derive(FromArgs)]
#[argh(
subcommand,
name = "copy",
description = "preview or write a cross-provider session copy"
)]
struct CopySessionArgs {
#[argh(positional)]
target: Target,
#[argh(option, description = "destination provider")]
to: Client,
#[argh(switch, description = "perform the copy")]
yes: bool,
}
#[derive(FromArgs)]
#[argh(
subcommand,
name = "remove",
description = "preview or remove a session and its descendants"
)]
struct RemoveSessionArgs {
#[argh(positional)]
target: Target,
#[argh(switch, description = "remove the session")]
yes: bool,
}
#[derive(FromArgs)]
#[argh(
subcommand,
name = "memory",
description = "learn, recall, list, show, forget, or status durable memory"
)]
struct MemoryArgs {
#[argh(subcommand)]
command: MemoryCommand,
}
#[derive(FromArgs)]
#[argh(subcommand)]
enum MemoryCommand {
Learn(MemoryLearnArgs),
Recall(MemoryRecallArgs),
List(MemoryListArgs),
Show(MemoryShowArgs),
Forget(MemoryForgetArgs),
Status(MemoryStatusArgs),
}
#[derive(FromArgs)]
#[argh(
subcommand,
name = "learn",
description = "learn durable claims from new session evidence"
)]
struct MemoryLearnArgs {
#[argh(positional)]
target: Target,
#[argh(switch, description = "all branches")]
all: bool,
#[argh(switch, description = "output in JSON format")]
json: bool,
}
#[derive(FromArgs)]
#[argh(
subcommand,
name = "recall",
description = "search durable claims by lexical, entity, and semantic match"
)]
struct MemoryRecallArgs {
#[argh(positional)]
query: String,
#[argh(option, description = "project working directory")]
project: Option<PathBuf>,
#[argh(switch, description = "search every project")]
all_projects: bool,
#[argh(option, long = "type", description = "memory type")]
memory_type: Option<memory::MemoryType>,
#[argh(option, default = "10", description = "result limit")]
limit: usize,
#[argh(
option,
default = "2048",
description = "maximum estimated result tokens"
)]
max_tokens: usize,
#[argh(switch, description = "include superseded historical memories")]
history: bool,
#[argh(switch, description = "output in JSON format")]
json: bool,
}
#[derive(FromArgs)]
#[argh(subcommand, name = "list", description = "list recent durable claims")]
struct MemoryListArgs {
#[argh(option, description = "project working directory")]
project: Option<PathBuf>,
#[argh(switch, description = "list every project")]
all_projects: bool,
#[argh(option, long = "type", description = "memory type")]
memory_type: Option<memory::MemoryType>,
#[argh(option, default = "20", description = "result limit")]
limit: usize,
#[argh(switch, description = "include superseded historical memories")]
history: bool,
#[argh(switch, description = "output in JSON format")]
json: bool,
}
#[derive(FromArgs)]
#[argh(
subcommand,
name = "show",
description = "show one claim with cited evidence"
)]
struct MemoryShowArgs {
#[argh(positional)]
id: String,
#[argh(switch, description = "output in JSON format")]
json: bool,
}
#[derive(FromArgs)]
#[argh(
subcommand,
name = "forget",
description = "tombstone a claim or a session's retained evidence"
)]
struct MemoryForgetArgs {
#[argh(positional)]
target: MemoryTarget,
#[argh(switch, description = "perform the forget operation")]
yes: bool,
#[argh(switch, description = "output in JSON format")]
json: bool,
}
#[derive(FromArgs)]
#[argh(
subcommand,
name = "status",
description = "show memory storage and indexing status"
)]
struct MemoryStatusArgs {
#[argh(switch, description = "output in JSON format")]
json: bool,
}