Skip to main content

codex_cli/agent/
mod.rs

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