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}