Archived
Compare commits
2
Commits
main
...
feat-updates
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
728c79cb86 | ||
|
|
0e4c54958f |
Generated
+1001
-281
File diff suppressed because it is too large
Load Diff
+5
-5
@@ -2,20 +2,20 @@
|
|||||||
name = "secret-agent"
|
name = "secret-agent"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
description = "Rust TUI/CLI wrapper for Claude, Gemini, Codex, and Copilot with SQLite history"
|
description = "Rust TUI/CLI wrapper for Claude, Antigravity, Codex, and Copilot with SQLite history"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
chrono = { version = "0.4", features = ["serde"] }
|
chrono = { version = "0.4", features = ["serde"] }
|
||||||
clap = { version = "4.5", features = ["derive"] }
|
clap = { version = "4.5", features = ["derive"] }
|
||||||
crossterm = { version = "0.28", features = ["event-stream"] }
|
crossterm = { version = "^0.29.0", features = ["event-stream"] }
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
ratatui = "0.29"
|
ratatui = "0.30"
|
||||||
rusqlite = { version = "0.32", features = ["bundled"] }
|
rusqlite = { version = "^0.40.0", features = ["bundled"] }
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
serde_yaml = "0.9"
|
serde_yaml = "0.9"
|
||||||
shell-words = "1.1"
|
shell-words = "1.1"
|
||||||
tokio = { version = "1.43", features = ["full"] }
|
tokio = { version = "1.43", features = ["full"] }
|
||||||
uuid = { version = "1.11", features = ["serde", "v7"] }
|
uuid = { version = "1.11", features = ["serde", "v7"] }
|
||||||
which = "7.0"
|
which = "^8.0.2"
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ except ModuleNotFoundError as exc: # pragma: no cover - hard failure on old Pyt
|
|||||||
REPO_ROOT = Path(__file__).resolve().parents[1]
|
REPO_ROOT = Path(__file__).resolve().parents[1]
|
||||||
DEFAULT_OUTPUT_DIR = REPO_ROOT / "target" / "debian"
|
DEFAULT_OUTPUT_DIR = REPO_ROOT / "target" / "debian"
|
||||||
DEFAULT_DESCRIPTION = (
|
DEFAULT_DESCRIPTION = (
|
||||||
"Rust TUI/CLI wrapper for Claude, Gemini, Codex, and Copilot with SQLite history"
|
"Rust TUI/CLI wrapper for Claude, Antigravity, Codex, and Copilot with SQLite history"
|
||||||
)
|
)
|
||||||
DEFAULT_MAINTAINER = "Secret Agent Maintainers <noreply@example.com>"
|
DEFAULT_MAINTAINER = "Secret Agent Maintainers <noreply@example.com>"
|
||||||
|
|
||||||
|
|||||||
+4
-4
@@ -20,7 +20,7 @@ pub struct CommandSpec {
|
|||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum OutputMode {
|
pub enum OutputMode {
|
||||||
PlainText,
|
PlainText,
|
||||||
GeminiJson,
|
AgyJson,
|
||||||
CodexJson,
|
CodexJson,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,7 +63,7 @@ pub fn build_command(request: &AgentRequest) -> Result<CommandSpec> {
|
|||||||
args.push(composed_prompt);
|
args.push(composed_prompt);
|
||||||
tool_conversation_id_hint = Some(tool_conversation_id);
|
tool_conversation_id_hint = Some(tool_conversation_id);
|
||||||
}
|
}
|
||||||
ToolKind::Gemini => {
|
ToolKind::Agy => {
|
||||||
args.push("-p".to_string());
|
args.push("-p".to_string());
|
||||||
args.push(composed_prompt);
|
args.push(composed_prompt);
|
||||||
args.push("--output-format".to_string());
|
args.push("--output-format".to_string());
|
||||||
@@ -73,7 +73,7 @@ pub fn build_command(request: &AgentRequest) -> Result<CommandSpec> {
|
|||||||
args.push(model.to_string());
|
args.push(model.to_string());
|
||||||
}
|
}
|
||||||
args.extend(collect_extra_args(role, &request.extra_args)?);
|
args.extend(collect_extra_args(role, &request.extra_args)?);
|
||||||
output_mode = OutputMode::GeminiJson;
|
output_mode = OutputMode::AgyJson;
|
||||||
}
|
}
|
||||||
ToolKind::Codex => {
|
ToolKind::Codex => {
|
||||||
args.push("exec".to_string());
|
args.push("exec".to_string());
|
||||||
@@ -129,7 +129,7 @@ pub fn tool_notes(tool: ToolKind) -> &'static str {
|
|||||||
ToolKind::Claude => {
|
ToolKind::Claude => {
|
||||||
"supports model and effort directly; role markdown can be injected as a system prompt"
|
"supports model and effort directly; role markdown can be injected as a system prompt"
|
||||||
}
|
}
|
||||||
ToolKind::Gemini => {
|
ToolKind::Agy => {
|
||||||
"supports model directly; role markdown is prepended to the task prompt"
|
"supports model directly; role markdown is prepended to the task prompt"
|
||||||
}
|
}
|
||||||
ToolKind::Codex => "supports model directly; role markdown is prepended to the task prompt",
|
ToolKind::Codex => "supports model directly; role markdown is prepended to the task prompt",
|
||||||
|
|||||||
+1
-1
@@ -24,7 +24,7 @@ use uuid::Uuid;
|
|||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
#[command(name = "secret-agent")]
|
#[command(name = "secret-agent")]
|
||||||
#[command(
|
#[command(
|
||||||
about = "Rust TUI/CLI wrapper for Claude, Gemini, Codex, and Copilot with SQLite history"
|
about = "Rust TUI/CLI wrapper for Claude, Antigravity, Codex, and Copilot with SQLite history"
|
||||||
)]
|
)]
|
||||||
struct Cli {
|
struct Cli {
|
||||||
#[arg(long, global = true)]
|
#[arg(long, global = true)]
|
||||||
|
|||||||
+2
-2
@@ -15,7 +15,7 @@ const CLAUDE_MODELS: &[&str] = &[
|
|||||||
"claude-haiku-4-5",
|
"claude-haiku-4-5",
|
||||||
];
|
];
|
||||||
|
|
||||||
const GEMINI_MODELS: &[&str] = &[
|
const AGY_MODELS: &[&str] = &[
|
||||||
"auto",
|
"auto",
|
||||||
"gemini-2.5-pro",
|
"gemini-2.5-pro",
|
||||||
"gemini-2.5-flash",
|
"gemini-2.5-flash",
|
||||||
@@ -90,7 +90,7 @@ fn is_model_available(tool: ToolKind, model: &str) -> bool {
|
|||||||
ToolKind::Claude => {
|
ToolKind::Claude => {
|
||||||
CLAUDE_MODELS.contains(&normalized.as_str()) || matches_claude_model_name(&normalized)
|
CLAUDE_MODELS.contains(&normalized.as_str()) || matches_claude_model_name(&normalized)
|
||||||
}
|
}
|
||||||
ToolKind::Gemini => GEMINI_MODELS.contains(&normalized.as_str()),
|
ToolKind::Agy => AGY_MODELS.contains(&normalized.as_str()),
|
||||||
ToolKind::Codex => CODEX_MODELS.contains(&normalized.as_str()),
|
ToolKind::Codex => CODEX_MODELS.contains(&normalized.as_str()),
|
||||||
ToolKind::Copilot => COPILOT_MODELS.contains(&normalized.as_str()),
|
ToolKind::Copilot => COPILOT_MODELS.contains(&normalized.as_str()),
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-8
@@ -92,7 +92,7 @@ async fn run(
|
|||||||
internal_tx.clone(),
|
internal_tx.clone(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
OutputMode::GeminiJson | OutputMode::CodexJson => {
|
OutputMode::AgyJson | OutputMode::CodexJson => {
|
||||||
tokio::spawn(read_structured_stream(
|
tokio::spawn(read_structured_stream(
|
||||||
stdout,
|
stdout,
|
||||||
command_spec.output_mode,
|
command_spec.output_mode,
|
||||||
@@ -279,7 +279,7 @@ fn parse_structured_line(output_mode: OutputMode, line: &str) -> Result<ParsedSt
|
|||||||
let value: Value = serde_json::from_str(line)?;
|
let value: Value = serde_json::from_str(line)?;
|
||||||
let tool_conversation_id = find_tool_conversation_id(output_mode, &value);
|
let tool_conversation_id = find_tool_conversation_id(output_mode, &value);
|
||||||
let display_text = match output_mode {
|
let display_text = match output_mode {
|
||||||
OutputMode::GeminiJson => extract_gemini_display_text(&value),
|
OutputMode::AgyJson => extract_agy_display_text(&value),
|
||||||
OutputMode::CodexJson => extract_codex_display_text(&value),
|
OutputMode::CodexJson => extract_codex_display_text(&value),
|
||||||
OutputMode::PlainText => None,
|
OutputMode::PlainText => None,
|
||||||
};
|
};
|
||||||
@@ -290,7 +290,7 @@ fn parse_structured_line(output_mode: OutputMode, line: &str) -> Result<ParsedSt
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extract_gemini_display_text(value: &Value) -> Option<String> {
|
fn extract_agy_display_text(value: &Value) -> Option<String> {
|
||||||
let event_type = value
|
let event_type = value
|
||||||
.get("type")
|
.get("type")
|
||||||
.and_then(Value::as_str)
|
.and_then(Value::as_str)
|
||||||
@@ -358,7 +358,7 @@ fn extract_codex_display_text(value: &Value) -> Option<String> {
|
|||||||
|
|
||||||
fn find_tool_conversation_id(output_mode: OutputMode, value: &Value) -> Option<String> {
|
fn find_tool_conversation_id(output_mode: OutputMode, value: &Value) -> Option<String> {
|
||||||
let keys = match output_mode {
|
let keys = match output_mode {
|
||||||
OutputMode::GeminiJson => &["session_id", "sessionId"][..],
|
OutputMode::AgyJson => &["session_id", "sessionId"][..],
|
||||||
OutputMode::CodexJson => &["thread_id", "threadId", "session_id", "sessionId"][..],
|
OutputMode::CodexJson => &["thread_id", "threadId", "session_id", "sessionId"][..],
|
||||||
OutputMode::PlainText => return None,
|
OutputMode::PlainText => return None,
|
||||||
};
|
};
|
||||||
@@ -469,15 +469,15 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parses_gemini_session_id() {
|
fn parses_agy_session_id() {
|
||||||
let parsed = parse_structured_line(
|
let parsed = parse_structured_line(
|
||||||
OutputMode::GeminiJson,
|
OutputMode::AgyJson,
|
||||||
r#"{"type":"init","session_id":"gemini-session-1"}"#,
|
r#"{"type":"init","session_id":"agy-session-1"}"#,
|
||||||
)
|
)
|
||||||
.expect("structured line should parse");
|
.expect("structured line should parse");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parsed.tool_conversation_id.as_deref(),
|
parsed.tool_conversation_id.as_deref(),
|
||||||
Some("gemini-session-1")
|
Some("agy-session-1")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -435,7 +435,7 @@ impl ConversationDraft {
|
|||||||
fn parse_tool(tool: String) -> rusqlite::Result<ToolKind> {
|
fn parse_tool(tool: String) -> rusqlite::Result<ToolKind> {
|
||||||
match tool.as_str() {
|
match tool.as_str() {
|
||||||
"claude" => Ok(ToolKind::Claude),
|
"claude" => Ok(ToolKind::Claude),
|
||||||
"gemini" => Ok(ToolKind::Gemini),
|
"agy" => Ok(ToolKind::Agy),
|
||||||
"codex" => Ok(ToolKind::Codex),
|
"codex" => Ok(ToolKind::Codex),
|
||||||
"copilot" => Ok(ToolKind::Copilot),
|
"copilot" => Ok(ToolKind::Copilot),
|
||||||
_ => Err(rusqlite::Error::FromSqlConversionFailure(
|
_ => Err(rusqlite::Error::FromSqlConversionFailure(
|
||||||
|
|||||||
+4
-4
@@ -9,7 +9,7 @@ use serde::{Deserialize, Serialize};
|
|||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
pub enum ToolKind {
|
pub enum ToolKind {
|
||||||
Claude,
|
Claude,
|
||||||
Gemini,
|
Agy,
|
||||||
Codex,
|
Codex,
|
||||||
Copilot,
|
Copilot,
|
||||||
}
|
}
|
||||||
@@ -17,7 +17,7 @@ pub enum ToolKind {
|
|||||||
impl ToolKind {
|
impl ToolKind {
|
||||||
pub const ALL: [ToolKind; 4] = [
|
pub const ALL: [ToolKind; 4] = [
|
||||||
ToolKind::Claude,
|
ToolKind::Claude,
|
||||||
ToolKind::Gemini,
|
ToolKind::Agy,
|
||||||
ToolKind::Codex,
|
ToolKind::Codex,
|
||||||
ToolKind::Copilot,
|
ToolKind::Copilot,
|
||||||
];
|
];
|
||||||
@@ -25,7 +25,7 @@ impl ToolKind {
|
|||||||
pub fn command_name(self) -> &'static str {
|
pub fn command_name(self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
ToolKind::Claude => "claude",
|
ToolKind::Claude => "claude",
|
||||||
ToolKind::Gemini => "gemini",
|
ToolKind::Agy => "agy",
|
||||||
ToolKind::Codex => "codex",
|
ToolKind::Codex => "codex",
|
||||||
ToolKind::Copilot => "copilot",
|
ToolKind::Copilot => "copilot",
|
||||||
}
|
}
|
||||||
@@ -40,7 +40,7 @@ impl fmt::Display for ToolKind {
|
|||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
let label = match self {
|
let label = match self {
|
||||||
ToolKind::Claude => "Claude",
|
ToolKind::Claude => "Claude",
|
||||||
ToolKind::Gemini => "Gemini",
|
ToolKind::Agy => "Antigravity",
|
||||||
ToolKind::Codex => "Codex",
|
ToolKind::Codex => "Codex",
|
||||||
ToolKind::Copilot => "Copilot",
|
ToolKind::Copilot => "Copilot",
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user