Skip to content
Draft
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
29 changes: 29 additions & 0 deletions agent-support/zed/rules
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
You have access to a `git_ai_checkpoint` MCP tool from git-ai that tracks AI code authorship.

IMPORTANT: You MUST call this tool to track which code you write vs which code the human writes.

## Rules

1. **Before editing any file**, call `git_ai_checkpoint` with:
- `event`: `"PreToolUse"`
- `file_paths`: array of file paths you are about to edit

2. **After editing any file**, call `git_ai_checkpoint` with:
- `event`: `"PostToolUse"`
- `file_paths`: array of file paths you just edited

3. Call the checkpoint tool for EVERY file edit operation (edit_file, create file, etc.)

4. Do NOT skip checkpoint calls even if you are making multiple edits in sequence - call before and after each edit operation.

## Example

Before editing `src/main.rs`:
```
git_ai_checkpoint(event: "PreToolUse", file_paths: ["src/main.rs"])
```

After editing `src/main.rs`:
```
git_ai_checkpoint(event: "PostToolUse", file_paths: ["src/main.rs"])
```
1 change: 1 addition & 0 deletions src/commands/checkpoint_agent/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ pub mod agent_presets;
pub mod agent_v1_preset;
pub mod amp_preset;
pub mod opencode_preset;
pub mod zed_preset;
85 changes: 85 additions & 0 deletions src/commands/checkpoint_agent/zed_preset.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
use crate::{
authorship::working_log::{AgentId, CheckpointKind},
commands::checkpoint_agent::agent_presets::{
AgentCheckpointFlags, AgentCheckpointPreset, AgentRunResult,
},
error::GitAiError,
};
use serde::Deserialize;

pub struct ZedPreset;

/// Hook input from git-ai MCP server or direct CLI invocation
#[derive(Debug, Deserialize)]
struct ZedHookInput {
hook_event_name: String,
#[serde(default)]
session_id: Option<String>,
#[serde(default)]
cwd: Option<String>,
#[serde(default)]
edited_filepaths: Option<Vec<String>>,
#[serde(default)]
tool_input: Option<ZedToolInput>,
}

#[derive(Debug, Deserialize)]
struct ZedToolInput {
#[serde(default)]
file_paths: Option<Vec<String>>,
}

impl AgentCheckpointPreset for ZedPreset {
fn run(&self, flags: AgentCheckpointFlags) -> Result<AgentRunResult, GitAiError> {
let hook_input_json = flags.hook_input.ok_or_else(|| {
GitAiError::PresetError("hook_input is required for Zed preset".to_string())
})?;

let hook_input: ZedHookInput = serde_json::from_str(&hook_input_json)
.map_err(|e| GitAiError::PresetError(format!("Invalid JSON in hook_input: {}", e)))?;

let is_pre_tool_use = hook_input.hook_event_name == "PreToolUse";

// Extract file paths from edited_filepaths or tool_input
let file_paths = hook_input
.edited_filepaths
.or_else(|| hook_input.tool_input.and_then(|ti| ti.file_paths))
.filter(|paths| !paths.is_empty());

// Use session_id from MCP server (stable per server instance) or generate fallback
let session_id = hook_input
.session_id
.unwrap_or_else(|| "zed-unknown".to_string());

let agent_id = AgentId {
tool: "zed".to_string(),
id: session_id,
model: "unknown".to_string(),
};

if is_pre_tool_use {
return Ok(AgentRunResult {
agent_id,
agent_metadata: None,
checkpoint_kind: CheckpointKind::Human,
transcript: None,
repo_working_dir: hook_input.cwd,
edited_filepaths: None,
will_edit_filepaths: file_paths,
dirty_files: None,
});
}

// PostToolUse - AI checkpoint
Ok(AgentRunResult {
agent_id,
agent_metadata: None,
checkpoint_kind: CheckpointKind::AiAgent,
transcript: None,
repo_working_dir: hook_input.cwd,
edited_filepaths: file_paths,
will_edit_filepaths: None,
dirty_files: None,
})
}
}
23 changes: 22 additions & 1 deletion src/commands/git_ai_handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use crate::commands::checkpoint_agent::agent_presets::{
use crate::commands::checkpoint_agent::agent_v1_preset::AgentV1Preset;
use crate::commands::checkpoint_agent::amp_preset::AmpPreset;
use crate::commands::checkpoint_agent::opencode_preset::OpenCodePreset;
use crate::commands::checkpoint_agent::zed_preset::ZedPreset;
use crate::config;
use crate::git::find_repository;
use crate::git::find_repository_in_path;
Expand Down Expand Up @@ -174,6 +175,9 @@ pub fn handle_git_ai(args: &[String]) {
"effective-ignore-patterns" => {
handle_effective_ignore_patterns_internal(&args[1..]);
}
"mcp-server" => {
commands::mcp_server::run_mcp_server();
}
"blame-analysis" => {
handle_blame_analysis_internal(&args[1..]);
}
Expand Down Expand Up @@ -202,7 +206,7 @@ fn print_help() {
eprintln!("Commands:");
eprintln!(" checkpoint Checkpoint working changes and attribute author");
eprintln!(
" Presets: claude, codex, continue-cli, cursor, gemini, github-copilot, amp, windsurf, opencode, ai_tab, mock_ai"
" Presets: claude, codex, continue-cli, cursor, gemini, github-copilot, amp, windsurf, opencode, zed, ai_tab, mock_ai"
);
eprintln!(
" --hook-input <json|stdin> JSON payload required by presets, or 'stdin' to read from stdin"
Expand Down Expand Up @@ -292,6 +296,7 @@ fn print_help() {
eprintln!(" --launch Launch agent CLI with restored context");
eprintln!(" --clipboard Copy context to system clipboard");
eprintln!(" --json Output context as structured JSON");
eprintln!(" mcp-server Run as MCP server (for Zed and other MCP-compatible editors)");
eprintln!(" login Authenticate with Git AI");
eprintln!(" logout Clear stored credentials");
eprintln!(" whoami Show auth state and login identity");
Expand Down Expand Up @@ -546,6 +551,22 @@ fn handle_checkpoint(args: &[String]) {
}
}
}
"zed" => {
match ZedPreset.run(AgentCheckpointFlags {
hook_input: hook_input.clone(),
}) {
Ok(agent_run) => {
if agent_run.repo_working_dir.is_some() {
repository_working_dir = agent_run.repo_working_dir.clone().unwrap();
}
agent_run_result = Some(agent_run);
}
Err(e) => {
eprintln!("Zed preset error: {}", e);
std::process::exit(0);
}
}
}
"mock_ai" => {
let mock_agent_id = format!(
"ai-thread-{}",
Expand Down
Loading
Loading