history : historique structure par session + am history (issue #7) - migration one-shot de history.txt, filtres kind/cwd/search/failed/session, enregistrement REPL avec duree et exit
This commit is contained in:
+40
-5
@@ -63,6 +63,7 @@ const COMMAND_DESCRIPTIONS: &[(&str, &str)] = &[
|
||||
("sessions", "list agent sessions"),
|
||||
("stats", "usage statistics"),
|
||||
("log", "read the event journal"),
|
||||
("history", "search command history"),
|
||||
("search", "search the catalog"),
|
||||
("info", "agent details"),
|
||||
("install", "install an agent"),
|
||||
@@ -139,7 +140,7 @@ impl AmCompleter {
|
||||
Self {
|
||||
theme: std::cell::Cell::new(crate::theme::default_theme()),
|
||||
commands: vec![
|
||||
"list", "status", "sessions", "stats", "log", "search", "info", "install", "uninstall", "update",
|
||||
"list", "status", "sessions", "stats", "log", "history", "search", "info", "install", "uninstall", "update",
|
||||
"start", "stop", "restart", "run", "doctor", "config", "completion",
|
||||
"self-update", "self-uninstall", "export", "import", "shell", "theme",
|
||||
"ls", "dir", "cd", "ps", "where", "get", "help", "version", "exit",
|
||||
@@ -894,21 +895,24 @@ pub fn run(app: &App) -> Result<i32> {
|
||||
.with_cwd(Some(current_dir_string())),
|
||||
);
|
||||
let _ = crate::sessions::start_repl(app, &sid, Some(current_dir_string()));
|
||||
if let Err(e) = crate::history::migrate(app) {
|
||||
app.log.verbose(&format!("history migration skipped: {e:#}"));
|
||||
}
|
||||
print!("{}", banner(app, &session, &sid));
|
||||
let result = match run_with_editor(app, &mut session) {
|
||||
let result = match run_with_editor(app, &mut session, &sid) {
|
||||
Ok(code) => Ok(code),
|
||||
Err(e) => {
|
||||
app.log.verbose(&format!(
|
||||
"line editor unavailable ({e:#}); falling back to plain input"
|
||||
));
|
||||
run_plain(app, &mut session)
|
||||
run_plain(app, &mut session, &sid)
|
||||
}
|
||||
};
|
||||
let _ = crate::sessions::finish_repl(app, &sid);
|
||||
result
|
||||
}
|
||||
|
||||
fn run_with_editor(app: &App, session: &mut ShellSession) -> Result<i32> {
|
||||
fn run_with_editor(app: &App, session: &mut ShellSession, sid: &str) -> Result<i32> {
|
||||
let mut rl: rustyline::Editor<AmCompleter, rustyline::history::FileHistory> =
|
||||
rustyline::Editor::new()?;
|
||||
rl.set_helper(Some(AmCompleter::new(app)));
|
||||
@@ -936,6 +940,7 @@ fn run_with_editor(app: &App, session: &mut ShellSession) -> Result<i32> {
|
||||
let t = Instant::now();
|
||||
let result = handle_input(app, session, &mut last, &trimmed);
|
||||
last_duration = Some(t.elapsed());
|
||||
record_history(app, sid, &trimmed, &result, t.elapsed());
|
||||
// 'theme <name>' may have switched the app theme: repoint the
|
||||
// completion menu at the new palette.
|
||||
if let Some(h) = rl.helper_mut() {
|
||||
@@ -959,8 +964,33 @@ fn run_with_editor(app: &App, session: &mut ShellSession) -> Result<i32> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
/// Append one structured history entry for a REPL command.
|
||||
fn record_history(
|
||||
app: &App,
|
||||
sid: &str,
|
||||
line: &str,
|
||||
result: &Result<bool>,
|
||||
elapsed: std::time::Duration,
|
||||
) {
|
||||
let kind = if is_am_command(line.split_whitespace().next().unwrap_or("")) {
|
||||
"am"
|
||||
} else {
|
||||
"shell"
|
||||
};
|
||||
let exit_code = if result.is_err() { Some(1) } else { None };
|
||||
let _ = crate::history::record(
|
||||
app,
|
||||
sid,
|
||||
&crate::history::HistoryEntry::now(line.to_string())
|
||||
.with_kind(kind.to_string())
|
||||
.with_cwd(Some(current_dir_string()))
|
||||
.with_exit_code(exit_code)
|
||||
.with_duration_ms(elapsed.as_millis() as u64),
|
||||
);
|
||||
}
|
||||
|
||||
/// Plain fallback when no terminal/line editor is available.
|
||||
fn run_plain(app: &App, session: &mut ShellSession) -> Result<i32> {
|
||||
fn run_plain(app: &App, session: &mut ShellSession, sid: &str) -> Result<i32> {
|
||||
let started = Instant::now();
|
||||
let mut last: Option<DataTable> = None;
|
||||
let mut last_duration: Option<Duration> = None;
|
||||
@@ -979,6 +1009,7 @@ fn run_plain(app: &App, session: &mut ShellSession) -> Result<i32> {
|
||||
let t = Instant::now();
|
||||
let result = handle_input(app, session, &mut last, &line);
|
||||
last_duration = Some(t.elapsed());
|
||||
record_history(app, sid, &line, &result, t.elapsed());
|
||||
match result {
|
||||
Ok(true) => break,
|
||||
Ok(false) => {}
|
||||
@@ -1273,6 +1304,10 @@ fn is_am_command(word: &str) -> bool {
|
||||
word,
|
||||
"list"
|
||||
| "status"
|
||||
| "sessions"
|
||||
| "stats"
|
||||
| "log"
|
||||
| "history"
|
||||
| "search"
|
||||
| "info"
|
||||
| "install"
|
||||
|
||||
Reference in New Issue
Block a user