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