Skip to content

Commit

Permalink
feat: append shell execute command to history file (#1026)
Browse files Browse the repository at this point in the history
  • Loading branch information
sigoden authored Dec 1, 2024
1 parent efdec34 commit 7e8e2a6
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 7 deletions.
1 change: 1 addition & 0 deletions config.example.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ right_prompt:
# ---- misc ----
serve_addr: 127.0.0.1:8000 # Default serve listening address
user_agent: null # Set User-Agent HTTP header, use `auto` for aichat/<current-version>
append_command_to_history_file: true # Weather to append shell execute command to the history file

# ---- clients ----
clients:
Expand Down
9 changes: 7 additions & 2 deletions src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ pub struct Config {

pub serve_addr: Option<String>,
pub user_agent: Option<String>,
pub append_command_to_history_file: bool,

pub clients: Vec<ClientConfig>,

Expand Down Expand Up @@ -206,6 +207,7 @@ impl Default for Config {

serve_addr: None,
user_agent: None,
append_command_to_history_file: true,

clients: vec![],

Expand Down Expand Up @@ -1950,7 +1952,7 @@ impl Config {
if output.is_empty() || !self.save {
return Ok(());
}
let timestamp = now();
let now = now();
let summary = input.summary();
let raw_input = input.raw();
let scope = if self.agent.is_none() {
Expand Down Expand Up @@ -1983,7 +1985,7 @@ impl Config {
None => String::new(),
};
let output = format!(
"# CHAT: {summary} [{timestamp}]{scope}\n{raw_input}\n--------\n{tool_calls}{output}\n--------\n\n",
"# CHAT: {summary} [{now}]{scope}\n{raw_input}\n--------\n{tool_calls}{output}\n--------\n\n",
);
file.write_all(output.as_bytes())
.with_context(|| "Failed to save message")
Expand Down Expand Up @@ -2225,6 +2227,9 @@ impl Config {
if let Some(v) = read_env_value::<String>(&get_env_name("user_agent")) {
self.user_agent = v;
}
if let Some(Some(v)) = read_env_bool(&get_env_name("append_command_to_history_file")) {
self.append_command_to_history_file = v;
}
}

fn load_functions(&mut self) -> Result<()> {
Expand Down
5 changes: 3 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -259,9 +259,10 @@ async fn shell_execute(
"e" => {
debug!("{} {:?}", shell.cmd, &[&shell.arg, &eval_str]);
let code = run_command(&shell.cmd, &[&shell.arg, &eval_str], None)?;
if code != 0 {
process::exit(code);
if code == 0 && config.read().append_command_to_history_file {
let _ = append_to_shell_history(&shell.name, &eval_str, code);
}
process::exit(code);
}
"r" => {
let revision = Text::new("Enter your revision:").prompt()?;
Expand Down
73 changes: 72 additions & 1 deletion src/utils/command.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
use super::*;

use std::{collections::HashMap, env, ffi::OsStr, path::Path, process::Command};
use std::{
collections::HashMap,
env,
ffi::OsStr,
fs::OpenOptions,
io::{self, Write},
path::{Path, PathBuf},
process::Command,
};

use anyhow::{anyhow, bail, Context, Result};
use dirs::home_dir;

lazy_static::lazy_static! {
pub static ref SHELL: Shell = detect_shell();
Expand Down Expand Up @@ -152,3 +161,65 @@ pub fn edit_file(editor: &str, path: &Path) -> Result<()> {
child.wait()?;
Ok(())
}

pub fn append_to_shell_history(shell: &str, command: &str, exit_code: i32) -> io::Result<()> {
if let Some(history_file) = get_history_file(shell) {
let command = command.replace('\n', " ");
let now = now_timestamp();
let history_txt = if shell == "fish" {
format!("- cmd: {command}\n when: {now}")
} else if shell == "zsh" {
format!(": {now}:{exit_code};{command}",)
} else {
command
};
let mut file = OpenOptions::new()
.create(true)
.append(true)
.open(&history_file)?;
writeln!(file, "{}", history_txt)?;
}
Ok(())
}

fn get_history_file(shell: &str) -> Option<PathBuf> {
match shell {
"bash" | "sh" => Some(home_dir()?.join(".bash_history")),
"zsh" => Some(home_dir()?.join(".zsh_history")),
"nushell" => Some(dirs::config_dir()?.join("nushell").join("history.txt")),
"fish" => Some(
home_dir()?
.join(".local")
.join("share")
.join("fish")
.join("fish_history"),
),
"powershell" | "pwsh" => {
#[cfg(not(windows))]
{
Some(
home_dir()?
.join(".local")
.join("share")
.join("powershell")
.join("PSReadLine")
.join("ConsoleHost_history.txt"),
)
}
#[cfg(windows)]
{
Some(
dirs::data_dir()?
.join("Microsoft")
.join("Windows")
.join("PowerShell")
.join("PSReadLine")
.join("ConsoleHost_history.txt"),
)
}
}
"ksh" => Some(home_dir()?.join(".ksh_history")),
"tcsh" => Some(home_dir()?.join(".history")),
_ => None,
}
}
7 changes: 5 additions & 2 deletions src/utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,11 @@ lazy_static::lazy_static! {
}

pub fn now() -> String {
let now = chrono::Local::now();
now.to_rfc3339_opts(chrono::SecondsFormat::Secs, false)
chrono::Local::now().to_rfc3339_opts(chrono::SecondsFormat::Secs, false)
}

pub fn now_timestamp() -> i64 {
chrono::Local::now().timestamp()
}

pub fn get_env_name(key: &str) -> String {
Expand Down

0 comments on commit 7e8e2a6

Please sign in to comment.