1use crate::history::History;
4use anyhow::Result;
5use rustyline::completion::{Completer, Pair};
6use rustyline::error::ReadlineError;
7use rustyline::highlight::Highlighter;
8use rustyline::hint::Hinter;
9use rustyline::history::DefaultHistory;
10use rustyline::validate::Validator;
11use rustyline::{CompletionType, Config, Editor, Helper};
12use std::borrow::Cow;
13use std::time::Instant;
14
15struct MielinHelper {
17 commands: Vec<String>,
18}
19
20impl Helper for MielinHelper {}
21
22impl MielinHelper {
23 fn new() -> Self {
24 Self {
25 commands: vec![
26 "node".to_string(),
27 "node list".to_string(),
28 "node info".to_string(),
29 "node start".to_string(),
30 "node stop".to_string(),
31 "node create".to_string(),
32 "node join".to_string(),
33 "node leave".to_string(),
34 "node config".to_string(),
35 "agent".to_string(),
36 "agent list".to_string(),
37 "agent deploy".to_string(),
38 "agent migrate".to_string(),
39 "agent stop".to_string(),
40 "agent create".to_string(),
41 "agent inspect".to_string(),
42 "agent logs".to_string(),
43 "agent exec".to_string(),
44 "mesh".to_string(),
45 "mesh status".to_string(),
46 "mesh peers".to_string(),
47 "cluster".to_string(),
48 "cluster init".to_string(),
49 "cluster status".to_string(),
50 "cluster health".to_string(),
51 "cluster upgrade".to_string(),
52 "migrate".to_string(),
53 "migrate status".to_string(),
54 "migrate cancel".to_string(),
55 "migrate history".to_string(),
56 "registry".to_string(),
57 "registry list".to_string(),
58 "registry query".to_string(),
59 "registry stats".to_string(),
60 "gossip".to_string(),
61 "gossip status".to_string(),
62 "gossip members".to_string(),
63 "gossip sync".to_string(),
64 "wasm".to_string(),
65 "wasm build".to_string(),
66 "wasm validate".to_string(),
67 "wasm test".to_string(),
68 "wasm optimize".to_string(),
69 "debug".to_string(),
70 "debug attach".to_string(),
71 "debug trace".to_string(),
72 "debug dump".to_string(),
73 "debug profile".to_string(),
74 "history".to_string(),
75 "history show".to_string(),
76 "history search".to_string(),
77 "history stats".to_string(),
78 "history clear".to_string(),
79 "history export".to_string(),
80 "version".to_string(),
81 "help".to_string(),
82 "exit".to_string(),
83 "quit".to_string(),
84 ],
85 }
86 }
87}
88
89impl Completer for MielinHelper {
90 type Candidate = Pair;
91
92 fn complete(
93 &self,
94 line: &str,
95 pos: usize,
96 _ctx: &rustyline::Context<'_>,
97 ) -> Result<(usize, Vec<Pair>), ReadlineError> {
98 let mut matches = Vec::new();
99
100 for cmd in &self.commands {
101 if cmd.starts_with(&line[..pos]) {
102 matches.push(Pair {
103 display: cmd.clone(),
104 replacement: cmd.clone(),
105 });
106 }
107 }
108
109 Ok((0, matches))
110 }
111}
112
113impl Hinter for MielinHelper {
114 type Hint = String;
115
116 fn hint(&self, line: &str, pos: usize, _ctx: &rustyline::Context<'_>) -> Option<String> {
117 if line.is_empty() || pos < line.len() {
118 return None;
119 }
120
121 for cmd in &self.commands {
122 if cmd.starts_with(line) && cmd != line {
123 return Some(cmd[line.len()..].to_string());
124 }
125 }
126
127 None
128 }
129}
130
131impl Highlighter for MielinHelper {
132 fn highlight<'l>(&self, line: &'l str, _pos: usize) -> Cow<'l, str> {
133 Cow::Borrowed(line)
136 }
137}
138
139impl Validator for MielinHelper {}
140
141pub struct Repl {
143 editor: Editor<MielinHelper, DefaultHistory>,
144 history: History,
145}
146
147impl Repl {
148 pub fn new() -> Result<Self> {
150 let config = Config::builder()
151 .completion_type(CompletionType::List)
152 .build();
153
154 let helper = MielinHelper::new();
155 let mut editor = Editor::with_config(config)?;
156 editor.set_helper(Some(helper));
157
158 let history = History::load()?;
160
161 Ok(Self { editor, history })
162 }
163
164 pub async fn run(&mut self) -> Result<()> {
166 println!("MielinOS CLI - Interactive Mode");
167 println!("Type 'help' for commands, 'exit' or 'quit' to exit");
168 println!();
169
170 loop {
171 let readline = self.editor.readline("mielin> ");
172
173 match readline {
174 Ok(line) => {
175 let line = line.trim();
176
177 if line.is_empty() {
178 continue;
179 }
180
181 let _ = self.editor.add_history_entry(line);
183
184 if line == "exit" || line == "quit" {
186 println!("Goodbye!");
187 break;
188 }
189
190 if line == "help" {
192 self.show_help();
193 continue;
194 }
195
196 let start = Instant::now();
198 let result = self.execute_command(line).await;
199 let duration = start.elapsed();
200
201 let exit_code = if result.is_ok() { 0 } else { 1 };
202
203 self.history
205 .add(line.to_string(), exit_code, duration.as_millis() as u64);
206
207 if let Err(e) = result {
208 eprintln!("Error: {}", crate::format_error(&e));
209 }
210 }
211 Err(ReadlineError::Interrupted) => {
212 println!("Interrupted (Ctrl+C). Type 'exit' or 'quit' to exit.");
213 }
214 Err(ReadlineError::Eof) => {
215 println!("EOF (Ctrl+D). Exiting...");
216 break;
217 }
218 Err(err) => {
219 eprintln!("Error: {:?}", err);
220 break;
221 }
222 }
223 }
224
225 self.history.save()?;
227
228 Ok(())
229 }
230
231 async fn execute_command(&self, line: &str) -> Result<()> {
233 let parts: Vec<&str> = line.split_whitespace().collect();
238 if parts.is_empty() {
239 return Ok(());
240 }
241
242 println!("Would execute: {}", line);
244 println!("(Command execution in REPL mode is a stub - full integration pending)");
245
246 Ok(())
247 }
248
249 fn show_help(&self) {
251 println!("Available commands:");
252 println!();
253 println!(" Node Management:");
254 println!(" node list - List all nodes");
255 println!(" node info <id> - Show node details");
256 println!(" node start <id> - Start a node");
257 println!(" node stop <id> - Stop a node");
258 println!();
259 println!(" Agent Management:");
260 println!(" agent list - List agents");
261 println!(" agent deploy <wasm> - Deploy WASM agent");
262 println!(" agent migrate <id> <node> - Migrate agent");
263 println!(" agent stop <id> - Stop agent");
264 println!();
265 println!(" Mesh Network:");
266 println!(" mesh status - Mesh status");
267 println!(" mesh peers - List peers");
268 println!();
269 println!(" Cluster:");
270 println!(" cluster init - Initialize cluster");
271 println!(" cluster status - Cluster status");
272 println!(" cluster health - Health check");
273 println!();
274 println!(" Other:");
275 println!(" history show - Show command history");
276 println!(" history stats - History statistics");
277 println!(" version - Show version");
278 println!(" help - Show this help");
279 println!(" exit, quit - Exit interactive mode");
280 println!();
281 }
282}
283
284impl Default for Repl {
285 fn default() -> Self {
286 Self::new().expect("Failed to create REPL")
287 }
288}