summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--config.example.yaml1
-rw-r--r--src/config/mod.rs9
-rw-r--r--src/main.rs5
-rw-r--r--src/utils/command.rs73
-rw-r--r--src/utils/mod.rs7
5 files changed, 88 insertions, 7 deletions
diff --git a/config.example.yaml b/config.example.yaml
index f46cddd..e50b7ed 100644
--- a/config.example.yaml
+++ b/config.example.yaml
@@ -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:
diff --git a/src/config/mod.rs b/src/config/mod.rs
index 39c63a3..e0e1efa 100644
--- a/src/config/mod.rs
+++ b/src/config/mod.rs
@@ -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>,
@@ -206,6 +207,7 @@ impl Default for Config {
serve_addr: None,
user_agent: None,
+ append_command_to_history_file: true,
clients: vec![],
@@ -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() {
@@ -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")
@@ -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<()> {
diff --git a/src/main.rs b/src/main.rs
index d8f66f8..2cefaf3 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -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()?;
diff --git a/src/utils/command.rs b/src/utils/command.rs
index 9ec929c..32e470d 100644
--- a/src/utils/command.rs
+++ b/src/utils/command.rs
@@ -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();
@@ -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,
+ }
+}
diff --git a/src/utils/mod.rs b/src/utils/mod.rs
index 873c5c8..5271571 100644
--- a/src/utils/mod.rs
+++ b/src/utils/mod.rs
@@ -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 {