kaish_kernel/tools/builtin/
mod.rs1mod alias;
6mod assert;
7mod awk;
8mod base64_tool;
9mod basename;
10#[cfg(feature = "subprocess")]
11mod bg;
12mod cat;
13mod cd;
14mod checksum;
15mod cmp;
16mod cp;
17mod cut;
18mod date;
19mod dd;
20mod diff;
21mod dirname;
22mod patch;
23mod echo;
24mod env;
25#[cfg(feature = "subprocess")]
26mod exec;
27#[cfg(feature = "subprocess")]
28mod spawn;
29#[cfg(feature = "subprocess")]
30pub use spawn::{resolve_in_path, virtual_cwd_error};
31mod export;
32#[cfg(feature = "subprocess")]
33mod fg;
34mod file;
35mod fromjson;
36mod fromjsonl;
37mod glob;
38mod find;
39pub(crate) mod format_string;
40mod gather;
41mod grep;
42mod grep_engine;
43mod regex_dialect;
44mod head;
45mod ignore;
46mod help;
47#[cfg(feature = "host")]
50mod hostname;
51mod introspect;
52mod jobs;
53mod jq_native;
54mod kaish_ast;
55mod kaish_clear;
56mod kaish_last;
57mod kaish_trash;
58mod kaish_status;
59mod kaish_version;
60mod kaish_vfs;
61mod keys;
62mod kill;
63mod ln;
64mod ls;
65mod mkdir;
66mod mktemp;
67mod mv;
68mod output_limit;
69mod plan;
70mod printf;
71mod push;
72mod pwd;
73mod random;
74mod read;
75mod readlink;
76mod realpath;
77mod rm;
78mod scatter;
79mod sed;
80mod seq;
81mod split;
82mod set;
83mod sleep;
84mod sort;
85mod stat;
86mod tac;
87mod tail;
88mod tee;
89mod test;
90mod timeout;
91mod tojson;
92mod tojsonl;
93#[cfg(feature = "tokens")]
94mod tokens;
95mod touch;
96mod tr;
97mod tree;
98mod true_false;
99mod type_of;
103mod uname;
104mod uniq;
105mod unset;
106mod validate;
107mod values;
108mod vars;
109mod wait;
110mod wc;
111#[cfg(feature = "subprocess")]
112mod which;
113mod write;
114mod xxd;
115
116use super::ToolRegistry;
117use crate::validator::{IssueCode, ValidationIssue};
118
119pub(crate) fn mixed_script_issue(name: &str) -> Option<ValidationIssue> {
132 if name == "<dynamic>" {
133 return None;
134 }
135 let mixed = crate::name::mixed_script(name)?;
136 Some(
137 ValidationIssue::warning(IssueCode::MixedScriptName, mixed.to_string())
138 .with_suggestion(mixed.suggestion()),
139 )
140}
141
142pub(crate) fn read_repeatable_strings(
157 args: &super::ToolArgs,
158 key: &str,
159) -> Result<Vec<String>, String> {
160 use crate::ast::Value;
161 match args.named.get(key) {
162 Some(Value::Json(serde_json::Value::Array(items))) => {
163 let mut out = Vec::with_capacity(items.len());
164 for v in items {
165 match v.as_str() {
166 Some(s) => out.push(s.to_string()),
167 None => {
168 return Err(format!(
169 "a repeatable flag value must be a string, got `{v}`"
170 ))
171 }
172 }
173 }
174 Ok(out)
175 }
176 Some(Value::String(s)) => Ok(vec![s.clone()]),
177 Some(other) => {
178 match crate::interpreter::value_to_text_sink_named(other, "a repeatable flag value") {
179 Ok(s) => Ok(vec![s]),
180 Err(e) => Err(e.to_string()),
181 }
182 }
183 None => Ok(Vec::new()),
184 }
185}
186
187pub(crate) fn get_path_string(
200 args: &super::ToolArgs,
201 name: &str,
202 positional_index: usize,
203) -> Result<Option<String>, String> {
204 use crate::ast::Value;
205 match args.get(name, positional_index) {
206 Some(v @ Value::Bytes(_)) => match crate::interpreter::value_to_text_sink_named(v, "a path") {
207 Ok(s) => Ok(Some(s)),
208 Err(e) => Err(e.to_string()),
209 },
210 _ => Ok(args.get_string(name, positional_index)),
211 }
212}
213
214pub(crate) fn guard_no_newline_names(
235 builtin: &str,
236 output: &crate::interpreter::OutputData,
237) -> Result<(), String> {
238 fn first_newline_name(nodes: &[crate::interpreter::OutputNode]) -> Option<&str> {
239 for node in nodes {
240 let name = node.display_name();
241 if name.contains('\n') {
242 return Some(name);
243 }
244 if let Some(found) = first_newline_name(&node.children) {
245 return Some(found);
246 }
247 }
248 None
249 }
250
251 if let Some(name) = first_newline_name(&output.root) {
252 let escaped = name.replace('\n', "\\n");
253 return Err(format!(
254 "{builtin}: '{escaped}': a newline in a filename cannot be reported as \
255 text — the line split would report two files that do not exist. \
256 Use `{builtin} --json` to read it losslessly, or rename the file."
257 ));
258 }
259 Ok(())
260}
261
262pub fn register_builtins(registry: &mut ToolRegistry) {
264 registry.register(alias::Alias);
265 registry.register(alias::Unalias);
266 registry.register(assert::Assert);
267 registry.register(awk::Awk);
268 registry.register(base64_tool::Base64Tool);
269 registry.register(basename::Basename);
270 #[cfg(feature = "subprocess")]
271 registry.register(bg::Bg);
272 registry.register(cat::Cat);
273 registry.register(cd::Cd);
274 registry.register(checksum::Checksum);
275 registry.register(cmp::Cmp);
276 registry.register(cp::Cp);
277 registry.register(cut::Cut);
278 registry.register(date::Date::new());
279 registry.register(dd::Dd);
280 registry.register(diff::Diff);
281 registry.register(dirname::Dirname);
282 registry.register(echo::Echo);
283 registry.register(env::Env);
284 #[cfg(feature = "subprocess")]
285 registry.register(exec::Exec);
286 #[cfg(feature = "subprocess")]
287 registry.register(spawn::Spawn);
288 registry.register(export::Export);
289 #[cfg(feature = "subprocess")]
290 registry.register(fg::Fg);
291 registry.register(file::File);
292 registry.register(fromjson::FromJson);
293 registry.register(fromjsonl::FromJsonl);
294 registry.register(glob::Glob);
295 registry.register(find::Find);
296 registry.register(gather::Gather);
297 registry.register(grep::Grep);
298 registry.register(head::Head);
299 registry.register(help::Help);
300 registry.register(ignore::KaishIgnore);
301 #[cfg(feature = "host")]
302 registry.register(hostname::Hostname);
303 registry.register(introspect::Mounts);
304 registry.register(introspect::Tools);
305 registry.register(jobs::Jobs);
306 registry.register(jq_native::JqNative);
307 registry.register(kaish_ast::KaishAst);
308 registry.register(kaish_clear::KaishClear);
309 registry.register(kaish_last::KaishLast);
310 registry.register(kaish_trash::KaishTrash);
311 registry.register(kaish_status::KaishStatus);
312 registry.register(kaish_version::KaishVersion);
313 registry.register(kaish_vfs::KaishVfs);
314 registry.register(keys::Keys);
315 registry.register(kill::Kill);
316 registry.register(ln::Ln);
317 registry.register(ls::Ls);
318 registry.register(mkdir::Mkdir);
319 registry.register(mktemp::Mktemp);
320 registry.register(mv::Mv);
321 registry.register(output_limit::KaishOutputLimit);
322 registry.register(patch::Patch);
323 registry.register(plan::PlanTool);
324 registry.register(printf::Printf);
325 registry.register(push::Push);
326 #[cfg(all(target_os = "linux", feature = "host"))]
327 registry.register(kaish_tools_host::Ps);
328 registry.register(pwd::Pwd);
329 registry.register(random::Random);
330 registry.register(read::Read);
331 registry.register(readlink::Readlink);
332 registry.register(realpath::Realpath);
333 registry.register(rm::Rm);
334 registry.register(scatter::Scatter);
335 registry.register(sed::Sed);
336 registry.register(seq::Seq);
337 registry.register(set::Set);
338 registry.register(split::Split);
339 registry.register(sleep::Sleep);
340 registry.register(sort::Sort);
341 registry.register(stat::Stat);
342 registry.register(tac::Tac);
343 registry.register(tail::Tail);
344 registry.register(tee::Tee);
345 registry.register(test::Test);
346 registry.register(timeout::Timeout);
347 #[cfg(feature = "tokens")]
348 registry.register(tokens::Tokens);
349 registry.register(tojson::ToJson);
350 registry.register(tojsonl::ToJsonl);
351 registry.register(touch::Touch);
352 registry.register(tr::Tr);
353 registry.register(tree::Tree);
354 registry.register(true_false::True);
355 registry.register(true_false::False);
356 registry.register(true_false::Colon);
357 registry.register(type_of::TypeOf);
358 registry.register(uname::Uname);
359 registry.register(uniq::Uniq);
360 registry.register(unset::Unset);
361 registry.register(validate::Validate);
362 registry.register(values::Values);
363 registry.register(vars::Vars);
364 registry.register(wait::Wait);
365 registry.register(wc::Wc);
366 #[cfg(feature = "subprocess")]
367 registry.register(which::Which);
368 registry.register(write::Write);
369 registry.register(xxd::Xxd);
370}