Skip to main content

codex_cli/agent/
mod.rs

1use std::io::{self, BufRead, Write};
2
3pub mod commit;
4pub mod exec;
5
6pub fn prompt(prompt_args: &[String]) -> i32 {
7    prompt_with_options(prompt_args, exec::ExecOptions::default())
8}
9
10pub fn prompt_with_options(prompt_args: &[String], exec_options: exec::ExecOptions) -> i32 {
11    let stdin = io::stdin();
12    let mut stdin = stdin.lock();
13    let stdout = io::stdout();
14    let mut stdout = stdout.lock();
15    let stderr = io::stderr();
16    let mut stderr = stderr.lock();
17    prompt_with_io(
18        prompt_args,
19        exec_options,
20        &mut stdin,
21        &mut stdout,
22        &mut stderr,
23    )
24}
25
26pub fn prompt_with_io<R: BufRead, WOut: Write, WErr: Write>(
27    prompt_args: &[String],
28    exec_options: exec::ExecOptions,
29    stdin: &mut R,
30    stdout: &mut WOut,
31    stderr: &mut WErr,
32) -> i32 {
33    let mut user_prompt = prompt_args.join(" ");
34
35    if user_prompt.is_empty() {
36        if write!(stdout, "Prompt: ").is_err() {
37            return 1;
38        }
39        let _ = stdout.flush();
40
41        user_prompt.clear();
42        if stdin
43            .read_line(&mut user_prompt)
44            .ok()
45            .filter(|n| *n > 0)
46            .is_none()
47        {
48            return 1;
49        }
50        user_prompt = user_prompt.trim_end_matches(&['\n', '\r'][..]).to_string();
51    }
52
53    if user_prompt.is_empty() {
54        let _ = writeln!(stderr, "codex-tools: missing prompt");
55        return 1;
56    }
57
58    exec::exec_dangerous_with_options(&user_prompt, "codex-tools:prompt", stderr, exec_options)
59}
60
61pub fn advice(question_args: &[String]) -> i32 {
62    advice_with_options(question_args, exec::ExecOptions::default())
63}
64
65pub fn advice_with_options(question_args: &[String], exec_options: exec::ExecOptions) -> i32 {
66    let stdin = io::stdin();
67    let mut stdin = stdin.lock();
68    let stdout = io::stdout();
69    let mut stdout = stdout.lock();
70    let stderr = io::stderr();
71    let mut stderr = stderr.lock();
72    run_template_with_io(
73        "actionable-advice",
74        question_args,
75        exec_options,
76        &mut stdin,
77        &mut stdout,
78        &mut stderr,
79    )
80}
81
82pub fn knowledge(concept_args: &[String]) -> i32 {
83    knowledge_with_options(concept_args, exec::ExecOptions::default())
84}
85
86pub fn knowledge_with_options(concept_args: &[String], exec_options: exec::ExecOptions) -> i32 {
87    let stdin = io::stdin();
88    let mut stdin = stdin.lock();
89    let stdout = io::stdout();
90    let mut stdout = stdout.lock();
91    let stderr = io::stderr();
92    let mut stderr = stderr.lock();
93    run_template_with_io(
94        "actionable-knowledge",
95        concept_args,
96        exec_options,
97        &mut stdin,
98        &mut stdout,
99        &mut stderr,
100    )
101}
102
103fn run_template_with_io<R: BufRead, WOut: Write, WErr: Write>(
104    template_name: &str,
105    args: &[String],
106    exec_options: exec::ExecOptions,
107    stdin: &mut R,
108    stdout: &mut WOut,
109    stderr: &mut WErr,
110) -> i32 {
111    let mut user_query = args.join(" ");
112    if user_query.trim().is_empty() {
113        if write!(stdout, "Question: ").is_err() {
114            return 1;
115        }
116        let _ = stdout.flush();
117
118        user_query.clear();
119        if stdin
120            .read_line(&mut user_query)
121            .ok()
122            .filter(|n| *n > 0)
123            .is_none()
124        {
125            return 1;
126        }
127        user_query = user_query.trim_end_matches(&['\n', '\r'][..]).to_string();
128    }
129
130    if user_query.trim().is_empty() {
131        let _ = writeln!(stderr, "codex-tools: missing question");
132        return 1;
133    }
134
135    let template_content = match crate::prompts::read_template(template_name) {
136        Ok((_path, content)) => content,
137        Err(crate::prompts::PromptTemplateError::TemplateMissing { path }) => {
138            let _ = writeln!(
139                stderr,
140                "codex-tools: prompt template not found: {}",
141                path.to_string_lossy()
142            );
143            return 1;
144        }
145        Err(crate::prompts::PromptTemplateError::ReadFailed { path }) => {
146            let _ = writeln!(
147                stderr,
148                "codex-tools: failed to read prompt template: {}",
149                path.to_string_lossy()
150            );
151            return 1;
152        }
153        Err(crate::prompts::PromptTemplateError::PromptsDirNotFound) => return 1,
154    };
155
156    let final_prompt = template_content.replace("$ARGUMENTS", &user_query);
157    exec::exec_dangerous_with_options(
158        &final_prompt,
159        &format!("codex-tools:{template_name}"),
160        stderr,
161        exec_options,
162    )
163}