From 22760cd6482ceae0a5f68c1428b4b81ab06ad08e Mon Sep 17 00:00:00 2001 From: Aanish Bhirud Date: Wed, 1 Jul 2026 14:44:16 +0530 Subject: [PATCH 1/2] chore(do): update codex/claude agent defaults for auto modes Use Codex auto-review approval flow and Claude auto permission mode in default agent suggestions. Co-authored-by: Cursor --- PRD.md | 8 ++++---- config/default.toml | 4 ++-- src/config.rs | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/PRD.md b/PRD.md index e37059d..09a7017 100644 --- a/PRD.md +++ b/PRD.md @@ -205,7 +205,7 @@ Natural language → action router. Classifies intent, generates a command or de | Tier | Example | Behavior | |---|---|---| | **Inline** | `qr do "run tests"`, `qr do "lint"` | Generates a shell command, shows preview, executes on approval | -| **Delegate** | `qr do "refactor auth to use JWT"`, `qr do "add pagination to the API"` | Recognizes multi-step coding work, suggests handing off to Codex (`codex exec`) or Claude Code (`claude --dangerously-skip-permissions`) | +| **Delegate** | `qr do "refactor auth to use JWT"`, `qr do "add pagination to the API"` | Recognizes multi-step coding work, suggests handing off to Codex (`codex exec`) or Claude Code (`claude --permission-mode auto -p`) | **How classification works:** - The AI receives the task + project context (from `qr learn`, see below) @@ -235,7 +235,7 @@ Run this command? [y/N] _ $ qr do "refactor the auth module to use JWT tokens" 🧠 This looks like a multi-step coding task. → Suggested: codex exec "refactor the auth module to use JWT tokens" - Or: claude --dangerously-skip-permissions -p "refactor the auth module to use JWT tokens" + Or: claude --permission-mode auto -p "refactor the auth module to use JWT tokens" Launch? [codex/claude/n] _ ``` @@ -243,8 +243,8 @@ Launch? [codex/claude/n] _ ```toml [do.agents] # Which coding agents are available for delegation -codex = "codex exec" -claude = "claude --dangerously-skip-permissions -p" +codex = "codex --sandbox workspace-write --ask-for-approval on-request -c approvals_reviewer=auto_review exec" +claude = "claude --permission-mode auto -p" default = "codex" # which one to highlight first ``` diff --git a/config/default.toml b/config/default.toml index b114742..e8a39de 100644 --- a/config/default.toml +++ b/config/default.toml @@ -25,5 +25,5 @@ enabled = false db_path = "__default__" [do.agents] -codex = "codex exec" -claude = "claude --dangerously-skip-permissions -p" +codex = "codex --sandbox workspace-write --ask-for-approval on-request -c approvals_reviewer=auto_review exec" +claude = "claude --permission-mode auto -p" diff --git a/src/config.rs b/src/config.rs index d587a86..c63d109 100644 --- a/src/config.rs +++ b/src/config.rs @@ -378,11 +378,11 @@ pub fn default_config_str() -> &'static str { } fn default_codex_agent() -> String { - "codex exec".into() + "codex --sandbox workspace-write --ask-for-approval on-request -c approvals_reviewer=auto_review exec".into() } fn default_claude_agent() -> String { - "claude --dangerously-skip-permissions -p".into() + "claude --permission-mode auto -p".into() } impl Default for AgentConfig { From 488088ffca2ab458fb629da79a3ae25792b1f7c4 Mon Sep 17 00:00:00 2001 From: Aanish Bhirud Date: Wed, 1 Jul 2026 19:50:32 +0530 Subject: [PATCH 2/2] fix(config): migrate legacy do.agents defaults on load Rewrite shipped codex/claude agent commands to the new auto-mode defaults when config.toml still contains the previous template values. Co-authored-by: Cursor --- src/config.rs | 167 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 166 insertions(+), 1 deletion(-) diff --git a/src/config.rs b/src/config.rs index c63d109..cf8845a 100644 --- a/src/config.rs +++ b/src/config.rs @@ -10,6 +10,8 @@ use serde::{Deserialize, Serialize}; use crate::ai::providers::{AiProtocol, ProviderConfig}; const DEFAULT_CONFIG: &str = include_str!("../config/default.toml"); +const LEGACY_CODEX_AGENT: &str = "codex exec"; +const LEGACY_CLAUDE_AGENT: &str = "claude --dangerously-skip-permissions -p"; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct AppConfig { @@ -92,7 +94,9 @@ impl AppConfig { let mut config = if path.exists() { let raw = fs::read_to_string(&path) .with_context(|| format!("Failed to read config file {}", path.display()))?; - Self::load_from_str(&raw)? + let mut config = Self::load_from_str(&raw)?; + migrate_legacy_agent_defaults(&path, &raw, &mut config)?; + config } else { Self::load_from_str(DEFAULT_CONFIG)? }; @@ -304,6 +308,85 @@ fn path_for_config(path: &Path) -> String { path.display().to_string() } +fn migrate_legacy_agent_defaults(path: &Path, raw: &str, config: &mut AppConfig) -> Result<()> { + let mut new_codex = None; + let mut new_claude = None; + + if config.do_config.agents.codex == LEGACY_CODEX_AGENT { + let value = default_codex_agent(); + config.do_config.agents.codex = value.clone(); + new_codex = Some(value); + } + if config.do_config.agents.claude == LEGACY_CLAUDE_AGENT { + let value = default_claude_agent(); + config.do_config.agents.claude = value.clone(); + new_claude = Some(value); + } + + if new_codex.is_none() && new_claude.is_none() { + return Ok(()); + } + + if let Some(updated) = + rewrite_agent_defaults_in_toml(raw, new_codex.as_deref(), new_claude.as_deref())? + { + fs::write(path, updated)?; + } + + Ok(()) +} + +fn rewrite_agent_defaults_in_toml( + raw: &str, + new_codex: Option<&str>, + new_claude: Option<&str>, +) -> Result> { + let mut in_agents = false; + let mut changed = false; + let mut lines: Vec = Vec::new(); + + for line in raw.lines() { + let trimmed = line.trim(); + if trimmed == "[do.agents]" { + in_agents = true; + lines.push(line.to_string()); + continue; + } + if in_agents && trimmed.starts_with('[') && trimmed.ends_with(']') { + in_agents = false; + } + if in_agents { + if let Some(value) = new_codex { + if trimmed.starts_with("codex") { + let indent: String = line.chars().take_while(|c| c.is_whitespace()).collect(); + lines.push(format!("{indent}codex = \"{value}\"")); + changed = true; + continue; + } + } + if let Some(value) = new_claude { + if trimmed.starts_with("claude") { + let indent: String = line.chars().take_while(|c| c.is_whitespace()).collect(); + lines.push(format!("{indent}claude = \"{value}\"")); + changed = true; + continue; + } + } + } + lines.push(line.to_string()); + } + + if !changed { + return Ok(None); + } + + let mut result = lines.join("\n"); + if raw.ends_with('\n') { + result.push('\n'); + } + Ok(Some(result)) +} + fn rewrite_stats_db_path_in_toml(raw: &str, new_value: &str) -> Result> { let mut in_stats = false; let mut changed = false; @@ -723,6 +806,88 @@ db_path = "{}" assert_eq!(config.stats.db_path, "__default__"); } + #[test] + fn migrate_legacy_agent_defaults_rewrites_shipped_defaults() { + let dir = tempfile::tempdir().unwrap(); + let path = dir.path().join("config.toml"); + fs::write( + &path, + r#" +[general] +default_run_mode = "output" +[projects] +roots = ["~/Development"] +scan_depth = 2 +scan_interval_hours = 1 +[ai] +protocol = "openai" +base_url = "https://api.openai.com/v1" +model = "gpt-4o" +api_key = "" +api_key_env = "OPENAI_API_KEY" +[stats] +enabled = false +db_path = "__default__" +[do.agents] +codex = "codex exec" +claude = "claude --dangerously-skip-permissions -p" +"#, + ) + .unwrap(); + + let config = AppConfig::load_from_env_with_path(path.clone()).unwrap(); + + assert_eq!(config.do_config.agents.codex, default_codex_agent()); + assert_eq!(config.do_config.agents.claude, default_claude_agent()); + + let updated = fs::read_to_string(&path).unwrap(); + assert!(updated.contains(&format!("codex = \"{}\"", default_codex_agent()))); + assert!(updated.contains(&format!("claude = \"{}\"", default_claude_agent()))); + assert!(!updated.contains(LEGACY_CODEX_AGENT)); + assert!(!updated.contains(LEGACY_CLAUDE_AGENT)); + } + + #[test] + fn migrate_legacy_agent_defaults_preserves_custom_agent_commands() { + let dir = tempfile::tempdir().unwrap(); + let path = dir.path().join("config.toml"); + let custom_codex = "codex exec --model gpt-5"; + fs::write( + &path, + format!( + r#" +[general] +default_run_mode = "output" +[projects] +roots = ["~/Development"] +scan_depth = 2 +scan_interval_hours = 1 +[ai] +protocol = "openai" +base_url = "https://api.openai.com/v1" +model = "gpt-4o" +api_key = "" +api_key_env = "OPENAI_API_KEY" +[stats] +enabled = false +db_path = "__default__" +[do.agents] +codex = "{custom_codex}" +claude = "claude --dangerously-skip-permissions -p" +"# + ), + ) + .unwrap(); + + let config = AppConfig::load_from_env_with_path(path.clone()).unwrap(); + + assert_eq!(config.do_config.agents.codex, custom_codex); + assert_eq!(config.do_config.agents.claude, default_claude_agent()); + + let updated = fs::read_to_string(&path).unwrap(); + assert!(updated.contains(&format!("codex = \"{custom_codex}\""))); + } + #[test] fn rewrite_stats_db_path_preserves_comments_and_formatting() { let root = tempfile::tempdir().unwrap();