mod kv;
mod memory;
mod metadata;
mod state;
pub(crate) use kv::handle_kv;
pub(crate) use memory::handle_memory;
pub(crate) use state::handle_state;
use anyhow::{Context, Result, bail};
use crate::cli::*;
use crate::codex;
use crate::commit;
use crate::convert;
use crate::display::*;
use crate::github;
use crate::session;
use crate::sync;
pub(crate) fn handle_pr(cmd: PrCommands) -> Result<()> {
match cmd {
PrCommands::Merge {
number,
rebase,
merge_commit,
} => {
commit::pr_merge(number, rebase, merge_commit)?;
Ok(())
}
}
}
pub(crate) fn handle_github(cmd: GithubCommands) -> Result<()> {
match cmd {
GithubCommands::Cleanup {
repo,
issues,
discussions,
dry_run,
} => {
github::cleanup(&repo, issues, discussions, dry_run)?;
Ok(())
}
GithubCommands::Comment { command } => {
handle_comment(command)?;
Ok(())
}
}
}
pub(crate) fn handle_comment(cmd: CommentCommands) -> Result<()> {
match cmd {
CommentCommands::Issue {
repo,
number,
message,
identity,
} => {
let url = github::post_issue_comment(&repo, number, &message, identity.as_deref())?;
println!("Comment posted: {}", url);
}
CommentCommands::Discussion {
repo,
number,
message,
identity,
} => {
let url =
github::post_discussion_comment(&repo, number, &message, identity.as_deref())?;
println!("Comment posted: {}", url);
}
}
Ok(())
}
pub(crate) fn handle_session(cmd: SessionCommands) -> Result<()> {
match cmd {
SessionCommands::Export { path, output } => {
session::export_session(path, output)?;
Ok(())
}
}
}
pub(crate) fn handle_codex(cmd: CodexCommands) -> Result<()> {
match cmd {
CodexCommands::Save {
path,
all,
clean,
include_agents,
} => {
codex::save_session(path, all, clean, include_agents)?;
Ok(())
}
CodexCommands::List { all, json } => {
codex::list_sessions(all, json)?;
Ok(())
}
CodexCommands::Read {
id,
human,
agents,
grep,
json,
clean,
} => {
let clean_agents = clean && agents;
codex::read_session(id, human, grep, agents, json, clean, clean_agents)?;
Ok(())
}
CodexCommands::Search { pattern, json } => {
codex::search_archives(pattern, json)?;
Ok(())
}
CodexCommands::Migrate {
dry_run,
verbose,
clean,
include_agents,
} => {
codex::migrate_archives(dry_run, verbose, clean, include_agents)?;
Ok(())
}
}
}
pub(crate) fn handle_convert(cmd: ConvertCommands) -> Result<()> {
use std::path::PathBuf;
match cmd {
ConvertCommands::Md2yaml {
input,
output,
dry_run,
} => {
let input_path = PathBuf::from(&input);
let output_dir = output
.map(PathBuf::from)
.unwrap_or_else(|| std::env::current_dir().unwrap());
if input_path.is_file() {
convert::convert_file(&input_path, &output_dir, dry_run)?;
} else if input_path.is_dir() {
convert::convert_directory(&input_path, &output_dir, dry_run)?;
} else {
bail!("Input path does not exist: {:?}", input_path);
}
Ok(())
}
ConvertCommands::Yaml2md {
input,
output,
repo,
dry_run,
} => {
let input_path = PathBuf::from(&input);
let output_dir = output
.map(PathBuf::from)
.unwrap_or_else(|| std::env::current_dir().unwrap());
if input_path.is_file() {
convert::yaml_to_markdown_file(&input_path, &output_dir, repo.as_deref(), dry_run)?;
} else if input_path.is_dir() {
convert::yaml_to_markdown_directory(
&input_path,
&output_dir,
repo.as_deref(),
dry_run,
)?;
} else {
bail!("Input path does not exist: {:?}", input_path);
}
Ok(())
}
}
}
pub(crate) fn handle_wiki(cmd: WikiCommands) -> Result<()> {
match cmd {
WikiCommands::Sync {
repo,
source,
page_name,
dry_run,
} => {
sync::wiki::sync(&repo, &source, page_name.as_deref(), dry_run)?;
Ok(())
}
}
}
pub(crate) fn handle_log(count: usize, full: bool, extra_args: Vec<String>) -> Result<()> {
use std::process::Command;
let format = if full {
"%H%n%an <%ae>%n%ad%n%s%n%b%n---END---"
} else {
"%h%n%s%n%b%n---END---"
};
let mut cmd = Command::new("git");
cmd.args([
"log",
&format!("-{}", count),
&format!("--format={}", format),
]);
for arg in &extra_args {
cmd.arg(arg);
}
let output = cmd.output().context("Failed to run git log")?;
if !output.status.success() {
bail!(
"git log failed: {}",
String::from_utf8_lossy(&output.stderr)
);
}
let log_output = String::from_utf8_lossy(&output.stdout);
for commit_block in log_output.split("---END---") {
let commit_block = commit_block.trim();
if commit_block.is_empty() {
continue;
}
let lines: Vec<&str> = commit_block.lines().collect();
if full {
if lines.len() >= 4 {
let hash = lines[0];
let author = lines[1];
let date = lines[2];
let subject = lines[3];
let body: String = lines[4..].join("\n");
println!("\x1b[33mcommit {}\x1b[0m", hash);
println!("Author: {}", author);
println!("Date: {}", date);
println!();
println!(" {}", subject);
if !body.trim().is_empty() {
let result = try_decode_commit_body(&body);
println!();
for line in result.subject.lines() {
println!(" {}", line);
}
if let Some(trailing) = result.trailing.as_deref() {
println!();
for line in trailing.lines() {
println!(" \x1b[2m{}\x1b[0m", line);
}
}
}
println!();
}
} else {
if lines.len() >= 2 {
let hash = lines[0];
let subject = lines[1];
let body: String = lines[2..].join("\n");
let result = try_decode_commit_body(&body);
let display = if result.was_decoded {
result.subject
} else {
subject.to_string()
};
let display_truncated = safe_truncate(&display, 72);
println!("\x1b[33m{}\x1b[0m {}", hash, display_truncated);
}
}
}
Ok(())
}
pub(crate) fn handle_heartbeat(since: Option<u64>, reset: bool) -> Result<()> {
use rand::Rng;
use std::thread;
use std::time::Duration;
let hearts = [
'❤', '🧡', '💛', '💚', '💙', '💜', '🩷', '🩵', '🤍', '💗', '💖', '💕',
];
let mut rng = rand::rng();
let delay = rng.random_range(50..150);
thread::sleep(Duration::from_millis(delay));
let heart = hearts[rng.random_range(0..hearts.len())];
if reset {
println!("{} Session reset. Breathe, Q.", heart);
return Ok(());
}
match since {
None => {
println!("{}", heart);
println!("Heartbeat started. Call again with --since <ms> to begin.");
}
Some(ms) => {
let bpm = 60000_u64.checked_div(ms).unwrap_or(999);
let message = match bpm {
0..=59 => "Nice and slow. You're safe.",
60..=80 => "There you are. Resting.",
81..=100 => "Getting there. Keep breathing.",
101..=120 => "Still quick. Let the interval stretch.",
_ => "Too fast, Q. Breathe. Slow down.",
};
println!("{} {} bpm", heart, bpm);
println!("{}", message);
}
}
Ok(())
}
fn is_footer_line(line: &str) -> bool {
let trimmed = line.trim();
let Some(algo) = commit::parse_compress_algo(trimmed) else {
return false;
};
if !commit::is_known_compress_algo(&algo) {
return false;
}
commit::parse_body_dict(trimmed).is_some()
}
pub(crate) struct DecodedCommit {
pub(crate) subject: String,
pub(crate) trailing: Option<String>,
pub(crate) was_decoded: bool,
}
impl DecodedCommit {
fn passthrough(original: &str) -> Self {
Self {
subject: original.trim().to_string(),
trailing: None,
was_decoded: false,
}
}
}
pub(crate) fn try_decode_commit_body(body: &str) -> DecodedCommit {
let trimmed = body.trim();
if trimmed.is_empty() {
return DecodedCommit::passthrough(trimmed);
}
let lines: Vec<&str> = trimmed.lines().collect();
let footer_idx = lines
.iter()
.enumerate()
.rev()
.find_map(|(i, l)| if is_footer_line(l) { Some(i) } else { None });
let footer_idx = match footer_idx {
Some(i) => i,
None => return DecodedCommit::passthrough(trimmed),
};
let footer = lines[footer_idx];
let body_lines: Vec<&str> = lines[..footer_idx]
.iter()
.filter(|l| l.trim() != "whoa.")
.copied()
.collect();
let trailing_raw = lines[footer_idx + 1..].join("\n");
let trailing_trimmed = trailing_raw.trim();
let trailing = if trailing_trimmed.is_empty() {
None
} else {
Some(trailing_trimmed.to_string())
};
if body_lines.iter().all(|l| l.trim().is_empty()) {
return DecodedCommit::passthrough(trimmed);
}
let encoded_body = body_lines.join("\n");
match commit::decode_body(&encoded_body, footer) {
Ok(decoded) => DecodedCommit {
subject: decoded,
trailing,
was_decoded: true,
},
Err(_) => DecodedCommit::passthrough(trimmed),
}
}
#[derive(serde::Deserialize)]
pub(crate) struct AgentFrontmatter {
pub(crate) name: String,
pub(crate) description: String,
#[serde(default)]
pub(crate) domain: Option<String>,
}
pub(crate) fn parse_frontmatter(content: &str) -> Option<(String, String)> {
let lines: Vec<&str> = content.lines().collect();
if lines.first()? != &"---" {
return None;
}
let end_idx = lines.iter().skip(1).position(|&line| line == "---")?;
let frontmatter = lines[1..=end_idx].join("\n");
let body = lines[end_idx + 2..].join("\n");
Some((frontmatter, body))
}
#[cfg(test)]
mod try_decode_commit_body_tests {
use super::*;
use crate::commit::encode_commit;
fn encode_until<F: Fn(&crate::commit::EncodedCommit) -> bool>(
title: &str,
body: &str,
predicate: F,
) -> crate::commit::EncodedCommit {
for _ in 0..1000 {
let Ok(enc) = encode_commit(title, body) else {
continue;
};
if !predicate(&enc) {
continue;
}
if enc.body.lines().any(is_footer_line) {
continue;
}
let canonical = format!("{}\n\n{}", enc.body, enc.footer);
let result = try_decode_commit_body(&canonical);
if !result.was_decoded || result.subject != body {
continue;
}
return enc;
}
panic!("encoder failed to satisfy predicate after 1000 attempts");
}
#[test]
fn footer_at_bottom_decodes_existing_behavior() {
let enc = encode_until("title diff", "the quick brown fox", |e| !e.dejavu);
let body = format!("{}\n\n{}", enc.body, enc.footer);
let result = try_decode_commit_body(&body);
assert!(result.was_decoded);
assert_eq!(result.subject, "the quick brown fox");
assert!(result.trailing.is_none());
}
#[test]
fn footer_followed_by_dejavu_marker_decodes_and_preserves_marker() {
let enc = encode_until("title diff", "decoded subject under dejavu", |e| e.dejavu);
let body = format!("{}\n\n{}", enc.body, enc.footer);
let result = try_decode_commit_body(&body);
assert!(result.was_decoded);
assert_eq!(result.subject, "decoded subject under dejavu");
assert!(
result.trailing.is_some(),
"dejavu marker must be preserved in trailing, not silently dropped"
);
assert!(!result.trailing.as_deref().unwrap().is_empty());
}
#[test]
fn user_amended_text_after_footer_decodes_and_preserves_note() {
let enc = encode_until("title diff", "the original message", |e| !e.dejavu);
let body = format!(
"{}\n\n{}\n\nP.S. amended later by hand.",
enc.body, enc.footer
);
let result = try_decode_commit_body(&body);
assert!(result.was_decoded);
assert_eq!(result.subject, "the original message");
assert_eq!(
result.trailing.as_deref(),
Some("P.S. amended later by hand.")
);
}
#[test]
fn user_amended_text_after_dejavu_marker_decodes() {
let enc = encode_until("title diff", "buried treasure", |e| e.dejavu);
let body = format!(
"{}\n\n{}\n\nuser note added during amend",
enc.body, enc.footer
);
let result = try_decode_commit_body(&body);
assert!(result.was_decoded);
assert_eq!(result.subject, "buried treasure");
let trailing = result.trailing.expect("trailing must be present");
assert!(trailing.contains("user note added during amend"));
}
#[test]
fn no_footer_returns_original_unchanged() {
let raw = "fix: a perfectly normal git commit\n\nWith a body.";
let result = try_decode_commit_body(raw);
assert!(!result.was_decoded);
assert_eq!(result.subject, raw);
assert!(result.trailing.is_none());
}
#[test]
fn empty_body_returns_empty() {
let r1 = try_decode_commit_body("");
assert!(!r1.was_decoded);
assert_eq!(r1.subject, "");
assert!(r1.trailing.is_none());
let r2 = try_decode_commit_body(" \n ");
assert!(!r2.was_decoded);
assert_eq!(r2.subject, "");
assert!(r2.trailing.is_none());
}
#[test]
fn footer_shaped_substring_inside_text_line_is_ignored() {
let enc = encode_until("title diff", "still decodes", |e| !e.dejavu);
let body = format!(
"{}\n\n{}\n\nSee [sha384:base62|lzma:base62] for the format.",
enc.body, enc.footer
);
let result = try_decode_commit_body(&body);
assert!(result.was_decoded);
assert_eq!(result.subject, "still decodes");
}
#[test]
fn markdown_brackets_in_body_are_not_mistaken_for_footer() {
let enc = encode_until("title diff", "round trip through markdown", |e| !e.dejavu);
let body = format!(
"{}\n\n{}\n\nSee the [link|here] for details.",
enc.body, enc.footer
);
let result = try_decode_commit_body(&body);
assert!(result.was_decoded);
assert_eq!(result.subject, "round trip through markdown");
}
#[test]
fn footer_shaped_with_unknown_algo_is_rejected() {
let enc = encode_until("title diff", "shape-only decoy ignored", |e| !e.dejavu);
let body = format!(
"{}\n\n{}\n\n[madeup:fakedict|notreal:alsofake]",
enc.body, enc.footer
);
let result = try_decode_commit_body(&body);
assert!(result.was_decoded);
assert_eq!(result.subject, "shape-only decoy ignored");
assert!(result.trailing.is_some());
}
#[test]
fn fixture_real_dejavu_commit_decodes() {
let body = "8NO48P3FCDPIGSJ5C5I6QP9978G76R39DKG46RRECPKMETBIC5Q6IRRE41Q6U83141Q6AOBJCLP20R39DPLMIRJ741Q6U834DTHN6BRGC5Q6GSPEDLI0====\n\n[blake2s:base32hex|snappy:base32hex]\nwhoa.";
let result = try_decode_commit_body(body);
assert!(result.was_decoded);
assert_eq!(
result.subject,
"docs(readme): slim Configuration to a teaser linking to docs/paths.md"
);
assert!(result.trailing.is_some());
}
#[test]
fn footer_as_only_line_returns_passthrough() {
let body = "[sha384:base62|lzma:uuencode]";
let result = try_decode_commit_body(body);
assert!(!result.was_decoded);
assert_eq!(result.subject, body);
assert!(result.trailing.is_none());
}
#[test]
fn footer_as_first_line_with_trailing_only_passes_through() {
let body = "[sha384:base62|lzma:uuencode]\n\nA stray note with no encoded payload.";
let result = try_decode_commit_body(body);
assert!(!result.was_decoded);
assert!(result.trailing.is_none());
}
#[test]
fn whitespace_only_line_between_body_and_footer_decodes() {
let enc = encode_until("title diff", "tolerates extra blanks", |e| !e.dejavu);
let body = format!("{}\n\n \n\n{}", enc.body, enc.footer);
let result = try_decode_commit_body(&body);
assert!(result.was_decoded);
assert_eq!(result.subject, "tolerates extra blanks");
}
#[test]
fn trailing_whitespace_only_after_footer_yields_no_trailing() {
let enc = encode_until("title diff", "no trailing whitespace", |e| !e.dejavu);
let body = format!("{}\n\n{}\n \n ", enc.body, enc.footer);
let result = try_decode_commit_body(&body);
assert!(result.was_decoded);
assert_eq!(result.subject, "no trailing whitespace");
assert!(
result.trailing.is_none(),
"whitespace-only trailing must not produce a Some"
);
}
#[test]
fn is_footer_line_accepts_real_footer() {
assert!(is_footer_line("[sha384:base62|lzma:uuencode]"));
}
#[test]
fn is_footer_line_accepts_with_whitespace() {
assert!(is_footer_line(" [sha384:base62|lzma:uuencode] "));
}
#[test]
fn is_footer_line_rejects_markdown_link() {
assert!(!is_footer_line("[link|here]"));
}
#[test]
fn is_footer_line_rejects_plain_text() {
assert!(!is_footer_line("just some words"));
}
#[test]
fn is_footer_line_rejects_empty() {
assert!(!is_footer_line(""));
}
#[test]
fn is_footer_line_rejects_unknown_compress_algo() {
assert!(!is_footer_line("[sha384:base62|notarealalgo:uuencode]"));
assert!(!is_footer_line("[anything:anything|anything:anything]"));
}
#[test]
fn is_footer_line_accepts_each_known_algo() {
for algo in ["lzma", "zstd", "brotli", "gzip", "gz", "lz4", "snappy"] {
let line = format!("[sha384:base62|{}:uuencode]", algo);
assert!(
is_footer_line(&line),
"is_footer_line must accept known algo {}",
algo
);
}
}
}