Axe 3 (#52) : templates embarqués dans le binaire (web, python, rust, cli) - am init --template <stack> génère une config pré-remplie : groupe dev, profil par défaut, hooks projet (npm install / uv sync / cargo fetch) - substitution {project} {dir} au rendu ; --template list pour lister - fallback détection automatique de stack quand aucun template - config générée validée (parse YAML + validate) par les tests Axe 4 (#53) : am sessions export + rétention configurable - am sessions --export <id> [--output <fichier>] : JSON complet et reproductible (métadonnées + commandes history/<id>.jsonl + extrait de log 4 Ko + rendu lisible 'readable'), compatible archivage manuel - settings.sessions_retention_days (défaut 90) : purge au démarrage (silencieuse) et am sessions --retention <jours> - purge limitée à l'index dérivé sessions.json — le journal d'événements primaire n'est jamais touché ; EventKind::Prune journalisé (visibilité am audit) - sessions en cours (running) jamais purgées ; erreur claire si id inconnu REPL : sessions --export/--retention/--output + init --template ; help specs, tip cheat sheet, config.yaml, README, man pages régénérées. Version 0.4.7, ROADMAP cochée, 276 tests verts.
This commit is contained in:
@@ -4,6 +4,7 @@ use super::*;
|
||||
use crate::output::{print_json, status_style, Table};
|
||||
use crate::sessions::{self, SessionRecord};
|
||||
use serde::Serialize;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct SessionRow {
|
||||
@@ -45,7 +46,16 @@ pub fn run(
|
||||
status: Option<&str>,
|
||||
show: Option<&str>,
|
||||
resume: Option<&str>,
|
||||
export: Option<&str>,
|
||||
output: Option<&str>,
|
||||
retention: Option<u64>,
|
||||
) -> Result<i32> {
|
||||
if let Some(id) = export {
|
||||
return export_one(app, id, output);
|
||||
}
|
||||
if let Some(days) = retention {
|
||||
return purge(app, days);
|
||||
}
|
||||
if let Some(id) = show {
|
||||
return show_one(app, id);
|
||||
}
|
||||
@@ -181,6 +191,57 @@ fn short_id(id: &str) -> String {
|
||||
id.chars().take(15).collect()
|
||||
}
|
||||
|
||||
/// Issue #53: 'am sessions export <id> [--output <file>]' — write a
|
||||
/// complete, self-contained JSON export (metadata, commands, log excerpt)
|
||||
/// plus a human-readable rendering. Default destination:
|
||||
/// ./am-session-<id>.json in the current directory.
|
||||
fn export_one(app: &App, id: &str, output: Option<&str>) -> Result<i32> {
|
||||
let export = sessions::export_session(app, id)?;
|
||||
let dest = match output {
|
||||
Some(o) => PathBuf::from(o),
|
||||
None => std::env::current_dir()
|
||||
.unwrap_or_else(|_| PathBuf::from("."))
|
||||
.join(format!("am-session-{}.json", export.session.id)),
|
||||
};
|
||||
let text = serde_json::to_string_pretty(&export)?;
|
||||
std::fs::write(&dest, text).map_err(|e| {
|
||||
anyhow::anyhow!("cannot write export {}: {e}", dest.display())
|
||||
})?;
|
||||
app.log.success(&format!(
|
||||
"exported session {} ({} command(s), {} log bytes) to {}",
|
||||
export.session.id,
|
||||
export.commands.len(),
|
||||
export.log_excerpt.len(),
|
||||
dest.display()
|
||||
));
|
||||
app.log.info(&format!(
|
||||
"JSON complet + rendu lisible ('readable') — compatible archivage manuel / am import"
|
||||
));
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
/// Issue #53: 'am sessions --retention <days>' — purge finished sessions
|
||||
/// older than the threshold. The primary event journal is untouched; the
|
||||
/// purge is journaled as EventKind::Prune.
|
||||
fn purge(app: &App, days: u64) -> Result<i32> {
|
||||
if days == 0 {
|
||||
bail!("retention must be at least 1 day");
|
||||
}
|
||||
if app.dry_run() {
|
||||
app.log
|
||||
.dry(format!("would purge sessions finished more than {days} days ago"));
|
||||
return Ok(0);
|
||||
}
|
||||
let purged = sessions::purge_expired(app, days)?;
|
||||
match purged {
|
||||
0 => app.log.info(&format!(
|
||||
"no session finished more than {days} days ago — nothing to purge"
|
||||
)),
|
||||
n => app.log.success(&format!("purged {n} session(s) older than {days} days")),
|
||||
}
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn format_duration(secs: u64) -> String {
|
||||
if secs < 60 {
|
||||
format!("{secs}s")
|
||||
|
||||
Reference in New Issue
Block a user