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}