refactor: migrate handoff, notes, mcp, subagent, recall, anchors to .codewhale

- HANDOFF_RELATIVE_PATH → .codewhale/handoff.md with .deepseek fallback
- load_handoff_block reads both paths, prefers .codewhale
- ToolContext notes_path and mcp_config_path use resolve_project_state_dir
- Sub-agent state path prefers .codewhale/state/
- Cycle archive (recall_archive) uses resolve_state_dir for sessions
- Compaction anchors path prefers .codewhale/anchors.md
- Updated marker constants and comments

Part of #2231.
This commit is contained in:
Hunter Bown
2026-05-26 14:15:57 -05:00
parent a1a7b5709a
commit 4925be4dda
5 changed files with 31 additions and 13 deletions
+7 -1
View File
@@ -1032,7 +1032,13 @@ fn read_workspace_anchors(workspace: Option<&Path>) -> Vec<String> {
return Vec::new();
};
let anchors_path = ws.join(".deepseek").join("anchors.md");
// Prefer .codewhale, fall back to .deepseek
let primary = ws.join(".codewhale").join("anchors.md");
let anchors_path = if primary.exists() {
primary
} else {
ws.join(".deepseek").join("anchors.md")
};
let Ok(content) = std::fs::read_to_string(anchors_path) else {
return Vec::new();
};
+12 -5
View File
@@ -53,7 +53,9 @@ impl Default for PromptSessionContext<'_> {
/// A previous session writes it on exit / `/compact`; the next session reads
/// it back on startup and prepends it to the system prompt so a fresh agent
/// doesn't have to re-discover open blockers from scratch.
pub const HANDOFF_RELATIVE_PATH: &str = ".deepseek/handoff.md";
pub const HANDOFF_RELATIVE_PATH: &str = ".codewhale/handoff.md";
/// Legacy handoff path for reading from existing installs.
const LEGACY_HANDOFF_RELATIVE_PATH: &str = ".deepseek/handoff.md";
/// Per-file size cap for `instructions = [...]` entries (#454). Mirrors
/// the existing project-context cap in `project_context::load_context_file`
@@ -180,7 +182,12 @@ fn render_instructions_block(paths: &[PathBuf]) -> Option<String> {
/// system-prompt block. Returns `None` when the file is absent or empty so
/// callers can keep the default-uncluttered prompt for fresh workspaces.
fn load_handoff_block(workspace: &Path) -> Option<String> {
let path = workspace.join(HANDOFF_RELATIVE_PATH);
let primary = workspace.join(HANDOFF_RELATIVE_PATH);
let path = if primary.exists() {
primary
} else {
workspace.join(LEGACY_HANDOFF_RELATIVE_PATH)
};
let raw = std::fs::read_to_string(&path).ok()?;
let trimmed = raw.trim();
if trimmed.is_empty() {
@@ -373,7 +380,7 @@ pub const SUGGEST_APPROVAL: &str = include_str!("prompts/approvals/suggest.md");
pub const NEVER_APPROVAL: &str = include_str!("prompts/approvals/never.md");
/// Compaction relay template — written into the system prompt so the
/// model knows the format to use when writing `.deepseek/handoff.md`.
/// model knows the format to use when writing `.codewhale/handoff.md`.
pub const COMPACT_TEMPLATE: &str = include_str!("prompts/compact.md");
/// Memory hygiene guidance — appended to the system prompt only when the
@@ -741,7 +748,7 @@ pub fn system_prompt_for_mode_with_context_skills_session_and_approval(
}
// 5. Compaction relay template — so the model knows the format to use
// when writing `.deepseek/handoff.md` on exit / `/compact`.
// when writing `.codewhale/handoff.md` on exit / `/compact`.
full_prompt.push_str("\n\n");
full_prompt.push_str(COMPACT_TEMPLATE);
@@ -832,7 +839,7 @@ mod tests {
/// Discriminator unique to the injected relay block (not present in the
/// agent prompt's own discussion of the convention).
const HANDOFF_BLOCK_MARKER: &str = "left a relay artifact at `.deepseek/handoff.md`";
const HANDOFF_BLOCK_MARKER: &str = "left a relay artifact at `.codewhale/handoff.md`";
fn contains_cjk(text: &str) -> bool {
text.chars().any(|ch| {
+4 -5
View File
@@ -162,11 +162,10 @@ fn archive_root(session_id: &str) -> Result<PathBuf, std::io::Error> {
"Could not resolve home directory for cycle archive root",
)
})?;
Ok(home
.join(".deepseek")
.join("sessions")
.join(session_id)
.join("cycles"))
// Use resolved sessions dir (prefers ~/.codewhale/sessions)
let sessions = codewhale_config::resolve_state_dir("sessions")
.unwrap_or_else(|_| home.join(".deepseek").join("sessions"));
Ok(sessions.join(session_id).join("cycles"))
}
/// Enumerate all archive files for a session, sorted by cycle number ascending.
+3 -2
View File
@@ -183,8 +183,9 @@ impl ToolContext {
pub fn new(workspace: impl Into<PathBuf>) -> Self {
let workspace = workspace.into();
let shell_manager = new_shared_shell_manager(workspace.clone());
let notes_path = workspace.join(".deepseek").join("notes.md");
let mcp_config_path = workspace.join(".deepseek").join("mcp.json");
// Prefer .codewhale, fall back to .deepseek for project-local state
let notes_path = codewhale_config::resolve_project_state_dir(&workspace, "notes.md").1;
let mcp_config_path = codewhale_config::resolve_project_state_dir(&workspace, "mcp.json").1;
Self {
workspace,
shell_manager,
+5
View File
@@ -1845,6 +1845,11 @@ async fn subagent_session_projection(
}
fn default_state_path(workspace: &Path) -> PathBuf {
// Prefer .codewhale, fall back to .deepseek for project-local state
let primary = workspace.join(".codewhale").join("state");
if primary.exists() {
return primary.join(SUBAGENT_STATE_FILE);
}
workspace
.join(".deepseek")
.join("state")