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()?,
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 = "coding-agent session browser")]
struct GoosedumpArgs {
#[argh(subcommand)]
command: GoosedumpCommand,
}
#[derive(FromArgs)]
#[argh(subcommand)]
enum GoosedumpCommand {
Session(SessionArgs),
Memory(MemoryArgs),
Model(ModelArgs),
}
#[derive(FromArgs)]
#[argh(subcommand, name = "session", description = "session commands")]
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 sessions")]
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 = "show a session")]
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")]
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 = "search session messages")]
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, description = "result page")]
page: Option<usize>,
#[argh(option, long = "as", description = "render provider")]
render_as: Option<Client>,
}
#[derive(FromArgs)]
#[argh(subcommand, name = "compact", description = "compact 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 = "copy a session")]
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 = "remove a session")]
struct RemoveSessionArgs {
#[argh(positional)]
target: Target,
#[argh(switch, description = "remove the session")]
yes: bool,
}
#[derive(FromArgs)]
#[argh(subcommand, name = "memory", description = "memory commands")]
struct MemoryArgs {
#[argh(subcommand)]
command: MemoryCommand,
}
#[derive(FromArgs)]
#[argh(subcommand)]
enum MemoryCommand {
Remember(MemoryRememberArgs),
Recall(MemoryRecallArgs),
List(MemoryListArgs),
Show(MemoryShowArgs),
Forget(MemoryForgetArgs),
Status(MemoryStatusArgs),
}
#[derive(FromArgs)]
#[argh(
subcommand,
name = "remember",
description = "learn durable memory from a session"
)]
struct MemoryRememberArgs {
#[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 = "recall durable memory")]
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 durable memories")]
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 durable memory")]
struct MemoryShowArgs {
#[argh(positional)]
id: String,
#[argh(switch, description = "output in JSON format")]
json: bool,
}
#[derive(FromArgs)]
#[argh(
subcommand,
name = "forget",
description = "forget memory or a remembered session"
)]
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 status")]
struct MemoryStatusArgs {
#[argh(switch, description = "output in JSON format")]
json: bool,
}
#[derive(FromArgs)]
#[argh(subcommand, name = "model", description = "model commands")]
struct ModelArgs {
#[argh(subcommand)]
command: ModelCommand,
}
#[derive(FromArgs)]
#[argh(subcommand)]
enum ModelCommand {
Pull(PullModelArgs),
}
#[derive(FromArgs)]
#[argh(
subcommand,
name = "pull",
description = "download local inference models"
)]
struct PullModelArgs {
#[argh(positional, description = "model kind: all, embedding, or text")]
kind: Option<ModelKind>,
}
#[derive(Clone, Copy)]
enum ModelKind {
All,
Embedding,
Text,
}
impl FromStr for ModelKind {
type Err = String;
fn from_str(value: &str) -> Result<Self, Self::Err> {
match value {
"all" => Ok(Self::All),
"embedding" => Ok(Self::Embedding),
"text" => Ok(Self::Text),
_ => Err("model kind must be one of: all, embedding, text".to_string()),
}
}
}