code wiki / (root) / nx_shelltool.nx

nx_shelltool.nx

buildroot/runtime/nx_shelltool.nx

24597 B458 linesdepth 7pulls 7 transitivereach 0 importersview sourcekind tool
docsdependenciesstructsconstsfunctions

about

nx_shelltool.nx -- the INVOCABLE + MCP-READY surface for the shell-replacement tier (07-15 operator: "build our nishi grep and glob and search ... as full MCP API state of the art ... available to these workstreams ... anything else that uses wsl when we could just use nishi"). The S1-S9 organs (nx_grep etc.) are LIBS (functions, no main) -> not callable as tools + not on MCP. This is the ONE CLI that COMPOSES the canonical nx_grep (per NISHI_SHELL_REPLACEMENT_ROADMAP "consumers compose nx_grep", no proliferation) into filesystem verbs a workstream can invoke over MCP tools/call instead of the Grep/Glob/Bash builtins + WSL: nx_shelltool grep <pattern> <dir> [ext] -> recursive; prints path:lineno:line for matches nx_shelltool glob <glob-pat> <dir> -> recursive; prints paths whose NAME matches (* wildcard) nx_shelltool find <substr> <dir> -> recursive; prints paths whose NAME contains substr Composes nx_grep_any (canonical match). Read-only. Exit 0; 2 usage; 3 baddir; 5 budget-partial. SEQ1292 ROOT FIX (2026-07-30, incident 2026-07-29 sitewide outage): every scan now runs under a SCAN BUDGET -- an unbounded walk over a multi-GB store starved the whole hub (and the old code mmap'd 1MiB per file, never unmapped => RSS grew linearly with files scanned = the thrash vector). Budget source: shelltool_budget.conf in CWD (keys max_bytes= deadline_ms= max_files=), else derived defaults: max_bytes 512MiB (~1.4% of the 36GB hub, seconds of sequential NAS IO), deadline_ms 20000 (the proven WC_FETCH_BUDGET_MS wall-clock precedent), max_files 200000. Checked per file + per getdents batch so exceeding STOPS IO, prints partial counts + a structured NX-SHELLTOOL BUDGET-EXCEEDED line, exit 5 -- never a silent truncation, never a host wedge. Buffers: ONE reused 1MiB read buffer; per-level walk buffers munmap'd; overlong paths (>4095) skipped LOUDLY via skipped_toolong= in the summary. license_tier: ORIGINAL expect_exit: 0

dependencies 2 imports · 0 importers

nx_syscalls.nx nx_grep_rt.nx nx_shelltool.nx

imports: nx_syscalls.nxnx_grep_rt.nx

imported by: nobody (leaf or entry point)

call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown

main s_puts sys_write s_streq s_dir_ok sys_openat_rd sys_mmap nxa_die sys_write ↻ sys_exit nxa_lock_take nxa_lock_addr sys_write ↻ nxa_lock_give nxa_lock_addr ↻ nxa_report_overrun sys_write ↻ nxa_dump_printable sys_write ↻ nxa_dump_sizes sys_write ↻ sys_getdents64 sys_close sys_munmap sys_mmap ↻ s_read sys_openat_rd ↻ sys_read sys_close ↻ s_num_after s_slen sys_munmap ↻ sys_clock_now_us sys_now_us sys_mmap ↻ sys_clock_gettime_mono s_starts s_slen ↻ s_out_ok s_slen ↻

structs

none

consts

27const S_EXIT_BADDIR: i64 = 3 // <dir> not a readable directory (loud, distinct from usage=2)
28const S_EXIT_BUDGET: i64 = 5 // budget exceeded -> PARTIAL results (loud, distinct)
29const S_PROBE_CAP: i64 = 4096 // one-getdents probe buffer (dir-ness check)
30const S_DEF_MAX_BYTES: i64 = 536870912 // 512MiB derived: ~1.4% hub RAM, seconds of NAS IO (seq1292)
31const S_DEF_DEADLINE_MS: i64 = 20000 // proven wall-clock budget precedent (WC_FETCH_BUDGET_MS)
32const S_DEF_MAX_FILES: i64 = 200000 // > any legitimate single-tree scan observed to date
37const S_FD_STDOUT: i64 = 1
38const S_MODE_644: i64 = 420
39const S_EXIT_BADOUT: i64 = 6 // out= refused/unwritable (loud, distinct from usage/baddir/budget)
40const S_NUMBUF: i64 = 24
41const S_OUTKEY_LEN: i64 = 4 // len("out=")
42const S_OUT_MINLEN: i64 = 5 // at least one name char + ".out"
43const S_DOT: i64 = 46
44const S_SLASH: i64 = 47
45const S_CH_O: i64 = 111
46const S_CH_U: i64 = 117
47const S_CH_T: i64 = 116
48const S_BUD_OFD: i64 = 9 // bud[] already threads the scan context to every emit site --
49const S_BUD_OBYTES: i64 = 10 // reusing 2 spare slots avoids changing 3 function signatures
50const S_GBUF_CAP: i64 = 1048576 // the ONE shared read buffer; a CHUNK size now, not a coverage limit
51const S_BUD_LONGLINE: i64 = 11 // count of single lines longer than the whole buffer (declared, never silent)

functions

53func s_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
called by 1: main calls 1: sys_write
54func s_write(b: *u8, n: i64) -> i64 { sys_write(1, b, n); return 0 }
calls 1: sys_write
55func s_putn(v: i64) -> i64
called by 1: main calls 3: sys_writesys_mmapsys_munmap
66func s_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
67func s_starts(s: *u8, pre: *u8) -> i64 { var i: i64 = 0; while pre[i] != (0 as u8) { if s[i] != pre[i] { return 0 } i = i + 1 } return 1 }
called by 1: main
70func s_fputs(fd: i64, s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(fd, s, n); return n }
called by 2: s_grep_emits_walk calls 1: sys_write
71func s_fputn(fd: i64, v: i64) -> i64
87func s_out_ok(p: *u8) -> i64
called by 1: main calls 1: s_slen
99func s_streq(a: *u8, b: *u8) -> i64 { var i: i64 = 0; while a[i] != (0 as u8) { if a[i] != b[i] { return 0 } i = i + 1 } if b[i] != (0 as u8) { return 0 } return 1 }
called by 1: main
102func s_read(path: *u8, buf: *u8, cap: i64) -> i64
called by 1: main calls 3: sys_openat_rdsys_readsys_close
113func s_num_after(buf: *u8, n: i64, key: *u8) -> i64
called by 1: main calls 1: s_slen
140func s_ends_with(name: *u8, nn: i64, ext: *u8, en: i64) -> i64
called by 1: s_walk
149func s_glob(pat: *u8, pn: i64, str: *u8, sn: i64) -> i64
called by 1: s_walk
170func s_bud_ok(bud: *i64) -> i64
called by 2: s_grep_files_walk calls 1: sys_clock_now_us
180func s_grep_emit(path: *u8, ln: i64, gbuf: *u8, ls: i64, le: i64, bud: *i64) -> i64
called by 1: s_grep_file calls 3: s_fputssys_writes_fputn
210func s_grep_file(path: *u8, pat: *u8, pn: i64, gbuf: *u8, bud: *i64) -> i64
272func s_walk(dir: *u8, mode: i64, pat: *u8, pn: i64, ext: *u8, en: i64, gbuf: *u8, bud: *i64) -> i64
329func s_dir_ok(dir: *u8) -> i64
340func main(argc: i64, argv: *i64) -> i64