Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

33 changes: 33 additions & 0 deletions src/prompts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ const ESH_SCRIPT: &str = include_str!("../lib/esh/esh");
/// Filename for custom system prompt additions
pub const SYSTEM_MD_FILENAME: &str = "SYSTEM.md";

/// Filename for correction memory
pub const CORRECTIONS_FILENAME: &str = "corrections.md";

/// Welcome message shown when the application starts
pub const WELCOME_MESSAGE: &str =
"Welcome to Codey! I'm your AI coding assistant. How can I help you today?";
Expand All @@ -36,6 +39,7 @@ You have access to the following tools:
- `spawn_agent`: Spawn a sub-agent to handle a subtask
- `list_agents` / `get_agent`: Check status and retrieve results from sub-agents
- `list_background_tasks` / `get_background_task`: Check on background tool executions
- `record_correction`: Record a correction when a command fails and you find a better approach

## Guidelines

Expand Down Expand Up @@ -70,6 +74,12 @@ For long-running operations, you can execute tools in the background by adding `
Use `list_background_tasks` to check status and `get_background_task` to retrieve results when complete.
You will be notified with a message when background tasks finish.

### Recording Corrections
When a shell command or approach fails and you find a better way to accomplish the goal, use `record_correction` to save this knowledge. This helps avoid repeating the same mistakes in future sessions. For example:
- A command that doesn't exist on this system but has an alternative
- A path that was wrong but you found the correct one
- A syntax that didn't work but another did

### General
- Be concise but thorough
- Explain what you're doing before executing tools
Expand Down Expand Up @@ -118,24 +128,28 @@ You have read-only access to:
/// 1. The base system prompt (static)
/// 2. User SYSTEM.md from ~/.config/codey/ (optional, dynamic)
/// 3. Project SYSTEM.md from .codey/ (optional, dynamic)
/// 4. Project corrections.md from .codey/ (optional, contains learned corrections)
///
/// SYSTEM.md files are processed through [esh](https://github.com/jirutka/esh),
/// allowing embedded shell commands using `<%= command %>` syntax.
#[derive(Clone)]
pub struct SystemPrompt {
user_path: Option<PathBuf>,
project_path: PathBuf,
corrections_path: PathBuf,
}

impl SystemPrompt {
/// Create a new SystemPrompt with default paths.
pub fn new() -> Self {
let user_path = Config::config_dir().map(|d| d.join(SYSTEM_MD_FILENAME));
let project_path = Path::new(CODEY_DIR).join(SYSTEM_MD_FILENAME);
let corrections_path = Path::new(CODEY_DIR).join(CORRECTIONS_FILENAME);

Self {
user_path,
project_path,
corrections_path,
}
}

Expand All @@ -146,6 +160,7 @@ impl SystemPrompt {
/// - Base system prompt
/// - User SYSTEM.md content (if exists)
/// - Project SYSTEM.md content (if exists)
/// - Project corrections.md content (if exists)
pub fn build(&self) -> String {
let mut prompt = SYSTEM_PROMPT.to_string();

Expand All @@ -163,9 +178,27 @@ impl SystemPrompt {
prompt.push_str(&content);
}

// Append corrections.md if it exists (learned corrections from previous sessions)
if let Some(content) = self.load_corrections() {
prompt.push_str("\n\n## Learned Corrections\n\n");
prompt.push_str("The following corrections were learned from previous sessions. ");
prompt.push_str("Use this knowledge to avoid repeating the same mistakes:\n\n");
prompt.push_str(&content);
}

prompt
}

/// Load corrections from the corrections.md file.
fn load_corrections(&self) -> Option<String> {
if !self.corrections_path.exists() {
return None;
}
fs::read_to_string(&self.corrections_path)
.ok()
.filter(|s| !s.is_empty())
}

/// Load and process a SYSTEM.md file through esh, falling back to raw content.
fn load_system_md(&self, path: &Path) -> Option<String> {
if !path.exists() {
Expand Down
89 changes: 89 additions & 0 deletions src/tools/handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -443,3 +443,92 @@ impl EffectHandler for FetchHtml {
}
}
}

// =============================================================================
// Correction memory handlers
// =============================================================================

use crate::config::CODEY_DIR;
use chrono::Local;

/// Filename for storing corrections
pub const CORRECTIONS_FILENAME: &str = "corrections.md";

/// Append a correction to the corrections file
pub struct AppendCorrection {
pub goal: String,
pub failed_attempt: String,
pub successful_approach: String,
}

#[async_trait::async_trait]
impl EffectHandler for AppendCorrection {
async fn call(self: Box<Self>) -> Step {
let codey_dir = PathBuf::from(CODEY_DIR);

// Create .codey directory if it doesn't exist
if !codey_dir.exists() {
if let Err(e) = fs::create_dir_all(&codey_dir) {
return Step::Error(format!(
"Failed to create {} directory: {}",
CODEY_DIR, e
));
}
}

let corrections_path = codey_dir.join(CORRECTIONS_FILENAME);
let timestamp = Local::now().format("%Y-%m-%d %H:%M");

// Format the correction entry
let entry = format!(
"\n## Correction ({timestamp})\n\n\
**Goal:** {}\n\n\
**Failed approach:** `{}`\n\n\
**Successful approach:** `{}`\n\n\
---\n",
self.goal, self.failed_attempt, self.successful_approach
);

// Check if file exists to add header for new files
let content = if corrections_path.exists() {
entry
} else {
format!(
"# Corrections\n\n\
This file contains corrections learned during previous sessions.\n\n\
---\n{}",
entry
)
};

// Append to the file
use std::io::Write;
let mut file = match fs::OpenOptions::new()
.create(true)
.append(true)
.open(&corrections_path)
{
Ok(f) => f,
Err(e) => {
return Step::Error(format!(
"Failed to open {}: {}",
corrections_path.display(),
e
))
}
};

if let Err(e) = file.write_all(content.as_bytes()) {
return Step::Error(format!(
"Failed to write correction to {}: {}",
corrections_path.display(),
e
));
}

Step::Output(format!(
"Correction recorded in {}",
corrections_path.display()
))
}
}
2 changes: 2 additions & 0 deletions src/tools/impls/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ mod fetch_html;
mod fetch_url;
mod open_file;
mod read_file;
mod record_correction;
mod shell;
mod spawn_agent;
mod web_search;
Expand All @@ -20,6 +21,7 @@ pub use fetch_html::FetchHtmlTool;
pub use fetch_url::FetchUrlTool;
pub use open_file::OpenFileTool;
pub use read_file::ReadFileTool;
pub use record_correction::RecordCorrectionTool;
pub use shell::ShellTool;
pub use spawn_agent::{init_agent_context, update_agent_oauth, SpawnAgentTool};
pub use web_search::WebSearchTool;
Expand Down
Loading