{"slug": "show-hn-ambits-agentic-grep-rg-tool-will-history-tracking", "title": "Show HN: Ambits – agentic grep/rg tool will history tracking", "summary": "A developer released Ambits, an open-source code-reading tool for AI coding agents that returns a symbol's definition as structured JSON and records every symbol an agent reads so the history can be restored after a context window compaction. Ambits ships as two front ends over one engine — `ambits rg` and `ambits grep` — with `ambits rg` matching ripgrep's flags and regex crate, and it is currently built and tested against Claude Code, installed via `cargo install ambits`. The tool addresses agents re-reading whole files and losing context on compaction by letting them request a symbol such as `App/process_compaction` instead of 2,000 lines of `app.rs`, and it can hand that read history back automatically after every compaction via `ambits hook install --project .`.", "body_md": "**A code-reading tool for AI agents, and a memory of what they have read.**\n\nCoding agents read whole files to find one function, re-read code they already know, and lose all of it the moment the context window compacts. ambits addresses both halves of that:\n\n- **Reading** —`ambits show` returns a symbol's definition as structured JSON, addressed by name or by content hash. The agent asks for`App/process_compaction` , not for 2,000 lines of`app.rs` .\n- **Remembering** — ambits records every symbol the agent reads, at what depth, throughout the session. After a compaction it can hand that history back, so the agent knows what it already understands instead of rediscovering it.\n\nBoth surfaces are plain text and JSON with no vendor coupling, so the record can be handed between agents, or between providers. Ingestion is currently built and tested against Claude Code.\n\nThere is also a live TUI, for when you want to watch what your agent is actually looking at.\n\n```\ncargo install ambits\n\n# Read a symbol instead of a file\nambits -p . show 'src/app.rs::App/process_compaction'\n\n# What has this session read so far?\nambits -p . restore-context\n\n# Hand that history back automatically after every compaction\nambits hook install --project .\n\n# Watch it live\nambits -p .\n```\n\nA grep whose every hit knows which symbol it landed in.\n\n```\nambits -p . rg 'is_binary'                 # every use and definition\nambits -p . rg 'fn enclosing' -t rust      # one file type\nambits -p . rg 'TODO' -g '!tests/**'       # globs; ! excludes\nambits -p . rg 'Journal::open' -A 3        # with trailing context\nambits -p . rg 'unwrap\\(\\)' -c             # matching lines per file\njs\nsrc/find.rs:67:7:[full BINARY_SNIFF_BYTES] const BINARY_SNIFF_BYTES: usize = 8 * 1024;\nsrc/find.rs:337:4:[full is_binary] fn is_binary(buf: &[u8]) -> bool {\nsrc/find.rs:338:21:[full is_binary]     buf.iter().take(BINARY_SNIFF_BYTES).any(|&b| b == 0)\nsrc/find.rs:442:8:[full search_file]     if is_binary(&buf) || !matcher.worth_searching(&buf) {\n```\n\n`file:line:column:` — the prefix every grep consumer already parses — then the\nsymbol the match sits in and how deeply this session has read it, then the line.\n`--no-symbol` drops that field for output byte-identical to ripgrep's.\n\n`grep(1)` and ripgrep assign **opposite meanings to the same short flags**, so\nno single command can be faithful to both:\n\n| Flag | GNU grep | ripgrep | \n|---|---|---|\n| `-L` | `--files-without-match` | `--follow` (symlinks) | \n| `-z` | `--null-data` | `--search-zip` | \n| `-r` | `--recursive` | `--replace` | \n| `-h` | `--no-filename` | help | \n\n`ambits rg` and `ambits grep` are two front ends over one engine — the same\nmatcher, the same symbol attribution, the same output contract — each faithful\nto the tool it is named after. `rg` is the one to reach for: Claude Code's own\n`Grep` tool is ripgrep-backed, so it is the dialect agents already speak.\n\n`ambits grep` keeps grep's defaults rather than ours: line numbers are opt-in,\nthere is no column, and `-h` is `--no-filename`. Three flags it cannot honour\nsay so rather than pretending — `-P` (no lookaround in this engine, so a PCRE\npattern would match something other than what it says) and `-z` (NUL-separated\ninput would change what a line is) are refused; `-r`/`-R` are accepted no-ops,\nsince the search is always recursive.\n\nThe flags **are** ripgrep's, down to the regex engine: `-i -w -x -F -v -U -e -g -t -A -B -C -n -N -o -l -c -m -M -q --hidden --no-ignore --heading --color --json`. That is not imitation for its own sake — Claude Code's `Grep` tool is\nripgrep-backed, so an agent reaching for this already knows the dialect, and the\nsame `regex` crate means patterns behave identically, including the shared\nabsence of backreferences and lookaround.\n\n| Form | Meaning | \n|---|---|\n| `[full name]` ,`[signature name]` , … | This session has read the symbol, at that depth | \n| `[— name]` | It has not | \n| `[name]` | No coverage journal: *unknown* , which is not the same as unread | \n| `[-]` | The match is not inside any symbol — a `use` line, or a file no parser handles | \n\nThe third row is the one that matters. An empty column would read as \"unread\"\nwhen the honest answer is \"nobody was watching\", and only one of those means go\nread it. In `--json`, a `coverage` object on the summary event is what\ndistinguishes them.\n\nEvery text file is searched, not only the parseable ones — a hit in a TOML file is a real hit, it simply has no symbol.\n\nA search prints source into an agent's context, so it records what it showed:\nevery symbol whose matching line was printed is journaled as read, at the hash\nit was searched at. Modes that print no source — `-q`, `-l`, `-c` — record\nnothing, and neither do matches cut past `--head-limit`. The journal is a record\nof what was *seen*, not of what the process computed.\n\nOutput is always sorted by path, line and column, because determinism is worth\nmore to an agent than the microseconds. `--head-limit` caps at 200 matches and\n`-M` clips lines at 300 columns, because this output lands in a context window\nrather than a terminal; `0` lifts either. `--column` is on by default, because\nit is what disambiguates two matches on one line. Exit codes are grep's: `0`\nmatched, `1` nothing matched, `2` error.\n\nEvery other command scans the project first: walk, parse every file, then\nanswer. A search inverts that — walk, read, reject on the raw bytes, and parse\nonly the survivors. Searching this repo for `classify` reads 61 files and parses\nthe 5 that matched, in about 0.01s; a pattern that matches nothing parses\nnothing and costs 0.00s, where a symbol-index search would pay for a full parse\nevery time.\n\n```\nambits -p . callers centered_rect\ncentered_rect — 2 call sites in 2 callers\n  src/ui/alignment.rs::render  (src/ui/alignment.rs:16)\n  src/ui/compaction.rs::render  (src/ui/compaction.rs:15)\n```\n\nCall sites come from the grammar's own tags query, so a mention in a comment or\ninside a string literal is never reported — the answer is a call node or it is\nnot there. Each site is attributed to the innermost symbol containing it, and\nthat id goes straight into `show`.\n\n**Matching is by name, not by resolution.** tree-sitter parses; it does not do\ntype inference, so a call to `new()` cannot be tied to one of the twelve\ndefinitions named `new`. On this repo 895 of 993 function names are unique, so\nmost answers are exact — but `callers new` returns every call to anything named\n`new`. `--format json` sets `name_matched_only: true` so a consumer cannot\nmistake this for a resolved call graph.\n\nReferences are extracted on demand rather than stored, so `rg`, `show`, and\nthe TUI pay nothing for this. It costs about 0.06s on this repo against under\n0.01s for a search — and unlike one, it reports call nodes only, so the\ndefinition and the doc comments mentioning it do not come back with them.\n\n```\nambits -p . show 'src/fmt.rs::tokens'\n{\"schema_version\":2,\"coverage\":{\"session_id\":\"30172621-…\",\"symbols_read\":1034},\n\"results\":[{\"query\":\"src/fmt.rs::tokens\",\"selector\":\"id\",\n\"matches\":[{\"id\":\"src/fmt.rs::tokens\",\"name\":\"tokens\",\"file\":\"src/fmt.rs\",\n\"lines\":[10,18],\"bytes\":[418,641],\"content_hash\":\"b3:46d7bd8b…\",\"label\":\"fn\",\n\"estimated_tokens\":88,\"definition\":\"pub fn tokens(n: u64) -> String {\\n …\",\n\"read_depth\":\"full\"}]}]}\n```\n\nA selector is either a **symbol id** — `<path>::<name-path>`, exactly what `restore-context` prints — or a **content hash**, full or an 8-character prefix. Several resolve per invocation, so a batch of lookups costs one process:\n\n```\nambits -p . show b3:5a60f75c 'src/digest.rs::grouped' 'src/app.rs::App/handle_key'\n```\n\n`definition` is the exact source span, sliced by byte offset rather than reconstructed from line numbers. `--no-body` returns location metadata only; `--max-bytes N` caps each definition and flags it `\"truncated\": true`, since a cut definition is no longer valid source.\n\n**Ambiguity is reported, not resolved.** `matches` is an array, because ids are\nnot guaranteed unique — Rust allows a type several inherent impl blocks in one\nfile, and nothing in the name distinguishes them. A content hash always names\nexactly one symbol. Empty `matches` means no such symbol; `\"selector\": \"unrecognized\"` means the query was neither an id nor a hash. The command exits `0` either way: \"nothing matches\" is an answer, not a failure.\n\nIn practice this is a large saving. `src/filter.rs` is 479 lines — roughly 9,300 tokens to read whole. The five `PathFilter` methods a caller actually needs come to about 630.\n\nEvery read is tracked per symbol and per agent, at the depth the tool implies — a `Read` gives full body, a grep match gives overview, a glob gives name only. `restore-context` reports that history:\n\n```\nambits -p . restore-context\n### src/app.rs — 100 symbols (~49.2k tok)\nApp/switch_session:342-345, App/process_compaction:364-412,\nApp/rebuild_tree_rows:415-482, App/handle_key:484-557, …\n\n### src/digest.rs — 47 symbols (~13.7k tok)\ngrouped:64-84, symbol_label:104-119, fit_names:126-141, …\n```\n\nEntries are `name:first-last`. Line numbers come from a fresh scan at print time rather than from storage, so they stay correct in files edited since the read — the agent can read that range directly instead of pulling the file.\n\nA symbol that moved between files is annotated `(was <old path>)`. ambits identifies symbols by content as well as by path, so hoisting a helper into a shared module does not lose it.\n\n`--max-tokens N` fits a budget (default 3000). `--format json` gives the same data structurally, including each symbol's `content_hash` for exact `show` lookups.\n\n```\nambits hook install --project .\n```\n\nRegisters a `SessionStart` hook with `matcher: \"compact\"` in `.claude/settings.json`, so Claude Code runs `restore-context` and injects the result the moment a compaction completes. It merges into existing settings, is safe to re-run, and emits nothing when there is nothing to restore.\n\nambits parses `show` invocations out of the session log and credits the symbols they name, so reading efficiently costs nothing in coverage versus a plain `Read`. (`--no-body` credits name-level only — the agent learned where a symbol is, not what it says.)\n\nCredit is best-effort: it is reconstructed from the logged command text, so a selector passed through a shell variable or command substitution is not visible. It fails toward under-reporting, never over.\n\nNothing in the record is tied to a vendor or a machine:\n\n| Piece | Form | \n|---|---|\n| Symbol ids | `<project-relative-path>::<name-path>` | \n| Content hashes | BLAKE3 over whitespace-normalized source | \n| Digest | Markdown, or schema-versioned JSON | \n| `show` output | Schema-versioned JSON | \n| Journal | NDJSON, one record per read | \n\nThe same digest means the same thing on another checkout, another machine, or in front of another model. Any agent that can run a command and read text can consume it — no MCP server, no SDK, no wire protocol.\n\nWhat *is* provider-shaped, and where the seams are:\n\n- **Session ingestion** is Claude Code's JSONL format today.`SessionIngester` is the extension point — \"implement this to add support for a new LLM session format.\"\n- **Tool mappings** are data, not code. Another provider's tool names are taught in`.ambit/tools.toml` rather than patched in;`ToolCallMapper` exists to \"plug in alternative tool-name conventions.\"\n- **`--format hook`** emits Claude Code's` SessionStart` envelope specifically.`--format markdown` and`--format json` carry the same content with no envelope.\n\nClaude Code is what this is built and tested against. The formats are deliberately boring so that need not stay true.\n\n```\nambits -p .\n```\n\nTails the session log and updates live. Three panels — symbol tree, coverage stats, activity feed — cycled with `Tab`.\n\nIt watches your source files too, re-parsing one when it changes so the tree and\nthe coverage numbers follow your edits without a restart. The watcher honours\n`.gitignore`, so generated code stays out of the tree — without that, a build\ntool writing into `target/` (rust-analyzer running `cargo check`, say) pushes\nrows in for files you never wrote. Deleted files leave the tree rather than\nlingering until you quit.\n\n- **Depth-aware coloring** — every symbol shaded by how deeply it was read\n- **Per-file counts** —`seen/total` on each file header, so partial coverage shows without expanding\n- **Sortable tree** — alphabetical, or grouped by coverage to surface half-read files first\n- **Search** —`/` to jump to a symbol by name\n- **Compaction history** —`C` for this session's compaction boundaries\n- **Sub-agent alignment** —`d` compares two agents file by file: where they read the same code, and where only one looked\n\nSymbols carried over from before a compaction render dimmed — the read happened, but it is no longer in the agent's live context.\n\n| Key | Action | \n|---|---|\n| `j` /`k` | Navigate up/down (tree or agent list, depending on focus) | \n| `h` /`l` | Collapse / expand tree nodes | \n| `Enter` | Expand node, or select agent when Stats is focused | \n| `Tab` | Cycle panel focus (Tree / Stats / Activity) | \n| `Shift+Tab` | Cycle agent filter backward | \n| `/` | Search symbols | \n| `s` | Toggle sort (alphabetical / coverage) | \n| `a` /`A` | Cycle agent filter forward / backward | \n| `d` | Sub-agent alignment view | \n| `C` | Compaction history ( `[` /`]` to page) | \n| `g` /`G` | Jump to first / last | \n| `PgUp` /`PgDn` | Scroll by page | \n| `Esc` | Close the alignment view, or cancel a search | \n| `q` | Quit | \n\n**Symbols**, by read depth:\n\n| Color | Meaning | \n|---|---|\n| Dark gray | Unseen | \n| Light gray | Name only (appeared in a glob or listing) | \n| Pale blue | Overview (grep match, symbol listing) | \n| Blue | Signature seen | \n| Green | Full body read | \n\n**File headers**, by coverage:\n\n| Color | Meaning | \n|---|---|\n| White | Nothing seen | \n| Amber | Partially covered | \n| Yellow-green | All symbols seen, not all at full depth | \n| Green | Every symbol read in full | \n\n```\nambits -p . --coverage\nCoverage Report (session: 30172621-…)\n─────────────────────────────────────────────────────────────────────────────\nFile                                      Symbols    Seen    Full   Seen%   Full%\n─────────────────────────────────────────────────────────────────────────────\nsrc/events.rs                                   3       3       3    100%    100%\nsrc/parser/mod.rs                              15       2       2     13%     13%\nsrc/app.rs                                    100     100     100    100%    100%\n…\n─────────────────────────────────────────────────────────────────────────────\nTOTAL                                        1307     309     309     24%     24%\n```\n\n- **Seen%** — symbols the agent has any awareness of\n- **Full%** — symbols read completely\n\n```\nambits -p . --coverage --format json | jq '.totals.full_percent'\n```\n\nWhen a session spawns sub-agents with the Task tool, ambits tracks each independently:\n\n```\nAgents: 5\n  ▶ [All]              Seen: 95%\n  ├─ 7842313b          35%\n  │  ├─ a38e68c        20%\n  │  ├─ a9fe23c        41%\n  │  └─ a845182        15%\n  └─ compact-0aff      10%\n```\n\n`Tab` to the Stats panel, `j`/` k` to move, `Enter` to filter — tree, activity feed, and depth breakdown all follow. `a` cycles agents from any panel. `d` opens the alignment view, which scores each pair of agents file by file — useful for spotting sub-agents that duplicated each other's exploration.\n\nOutside the TUI:\n\n```\nambits -p . --coverage --agent a9fe23c\nambits -p . --coverage --agent a9fe        # prefix match\n```\n\nA prefix matching no agent, or several, warns rather than guessing.\n\nClaude Code writes a session log as it works. The TUI tails that log, turns tool\ncalls into per-symbol reads, and diffs them into a durable journal. When a\ncompaction wipes the context, a `SessionStart` hook reads the journal back and\ninjects what the session had already read.\n\n```\nflowchart LR\n  CC[\"Claude Code<br/>session\"]\n  LOG[(\"session JSONL<br/>+ subagents/*.jsonl\")]\n  TUI[\"ambits TUI\"]\n  LED[\"ContextLedger<br/>symbol → depth, per agent\"]\n  JRN[(\".ambit/coverage/<br/>&lt;session&gt;.ndjson\")]\n  HOOK[\"SessionStart hook<br/>matcher: compact\"]\n  DIG[\"restore-context<br/>→ digest\"]\n\n  CC -->|writes| LOG\n  LOG -->|tails byte offsets| TUI\n  TUI --> LED\n  LED -->|diff on interval| JRN\n  CC -.->|compaction wipes context| HOOK\n  HOOK --> DIG\n  JRN --> DIG\n  DIG -->|injects what was read| CC\n```\n\nThe journal is written only by the TUI, which is what removes concurrent-writer concerns rather than managing them. Everything downstream reads it.\n\nTwo independent pipelines. The **parse** side turns source into a symbol tree;\nthe **ingest** side turns a session log into per-symbol reads. They meet in only\ntwo places: `classify`, which compares journaled reads against the current tree,\nand the coverage annotation on search results.\n\n``` php\nflowchart TB\n  SRC[\"source files\"]\n  SRC -->|\"ignore::WalkBuilder\"| PATHS[\"collect paths<br/><i>walk is ~free</i>\"]\n  PATHS -->|\"parse in parallel<br/>(tree-sitter)\"| TREE[\"ProjectTree<br/>symbols · spans · BLAKE3 hashes\"]\n\n  LOG[(\"session JSONL\")] --> PJL[\"parse_jsonl_line\"]\n  PJL --> PL{\"ParsedLine\"}\n  PL -->|Events| MAP[\"ToolCallMapper<br/><i>.ambit/tools.toml</i>\"]\n  PL -->|Compacted| MARK[\"mark restored\"]\n  PL -->|SessionCleared| RST[\"reset\"]\n  MAP -->|\"tool → read depth\"| LED[\"ContextLedger\"]\n  MARK --> LED\n  RST --> LED\n  LED -->|\"diff on interval\"| JRN[(\"journal\")]\n\n  TREE --> FIND[\"find · show\"]\n  TREE --> CLS[\"classify\"]\n  JRN --> CLS\n  CLS --> DIG[\"digest<br/>markdown · json · hook\"]\n  JRN -.->|\"read depth\"| FIND\n\n  SRC ==>|\"re-read<br/><i>skip files lacking the name</i>\"| CQ[\"<b>second parse</b><br/>tags query + supplement<br/>@reference.call\"]\n  CQ ==>|\"macro token trees<br/>re-parsed, bounded worklist\"| CQ\n  CQ ==> ATTR[\"attribute to enclosing symbol<br/><i>by byte range</i>\"]\n  TREE -.->|\"symbol spans only\"| ATTR\n  ATTR ==> CALLERS[\"callers\"]\n```\n\nThe thick path is `callers`, and it is deliberately separate. A reference query\nneeds syntax nodes, which the scan discards once it has extracted symbols — so\n`callers` re-reads and re-parses, skipping any file whose text does not contain\nthe name at all. It borrows exactly one thing from the main pipeline: symbol\nbyte ranges, which is what turns `app.rs:959` into\n`src/app.rs::mark_selected_symbols`.\n\nThe self-edge is macro handling. tree-sitter does not parse macro bodies —\narguments arrive as an unparsed token tree — so those are re-parsed as source,\nand since macros nest it runs as a bounded worklist that feeds itself. Without\nit, anything called from inside `println!` or `assert_eq!` is invisible.\n\nThe TUI's file watcher enters this pipeline at `SRC`, and so has to agree with\nthe walk about what counts as a project file. `ProjectScope` (`src/filter.rs`)\nis that shared answer. It is deliberately the narrower of the two — it reads the\nroot `.gitignore` and `.git/info/exclude`, not `.gitignore` files nested in\nsubdirectories, which `WalkBuilder` discovers as it descends. The asymmetry\npoints that way on purpose: too permissive merely admits a file the scan would\nhave skipped, while too strict would silently stop live updates for a file the\nscan included, which is both worse and harder to notice.\n\nWhile the TUI runs it maintains an append-only NDJSON record at `.ambit/coverage/<session>.ndjson` — one entry per `(symbol, agent)` read. The TUI is the only writer: a search or `show` run with no TUI attached to the session earns no coverage credit, a deliberate trade for never needing two processes to reason about writing the same journal. This is what lets `restore-context` answer after the fact, and it survives restarts.\n\n```\nambits -p . cache status              # sessions, symbols, size on disk\nambits -p . cache clear --session <id>\nambits -p . cache clear --all\n```\n\nSize is bounded by what an agent can read in one session, not by repository size — a full day of heavy work on this repo runs to a few hundred KB. Nothing is pruned automatically, and `cache clear` requires naming a target, because a journal is the only record of what a past session read.\n\nDisable with `--no-journal`; tune the write interval with `--flush-interval-ms`.\n\n```\nambits -p . --filter src/parser              # by path component\nambits -p . --filter-regex '^src/.*\\.rs$'    # by regex\n```\n\n`--filter` matches whole path components, so `src/parser` matches `src/parser/rust.rs` but not `src/parser_extra.rs`.\n\nHow a tool call becomes a symbol read is data. Drop a `.ambit/tools.toml` into your project to teach ambits a tool it does not know, or to change the depth an existing one grants:\n\n```\nversion = 1\n\n[[tool]]\nnames         = [\"MyCustomReader\"]\npath_keys     = [\"path\"]\ndepth         = { type = \"fixed\", value = \"FullBody\" }\ndescription   = \"MyCustomReader {path}\"\n```\n\nProject config merges over the built-in defaults; a user-global config is picked up automatically. `--tools-config` points at a specific file.\n\n| Backend | Languages | \n|---|---|\n| Tree-sitter (default) | Rust, Python, TypeScript | \n| Serena MCP | Any language [Serena](https://github.com/oraios/serena) supports | \n\n```\nambits -p . --serena\nambits skill install --global      # all projects\nambits skill install               # current project\nambits skill install --project /path/to/project\n```\n\nInstalls a [skill](https://code.claude.com/docs/en/skills) that teaches the agent when to check its own coverage and how to fetch definitions. Global installs go to `~/.claude/skills/ambit/`, project installs to `.claude/skills/ambit/`.\n\n| Command | Description | \n|---|---|\n| `ambits -p <path>` | Launch the TUI | \n| `ambits … rg <pattern> [path…]` | Grep file contents, ripgrep's flags; every hit names its symbol | \n| `ambits … grep <pattern> [path…]` | The same search, GNU grep's flags | \n| `ambits … callers <name>…` | List call sites and their enclosing symbol | \n| `ambits … show <selector>…` | Print symbol definitions as JSON | \n| `ambits … restore-context` | Print this session's read history | \n| `ambits … --coverage` | Print a coverage report and exit | \n| `ambits … --dump` | Print the symbol tree and exit | \n| `ambits … cache status\\|clear` | Inspect or remove read journals | \n| `ambits hook install` | Register the post-compaction hook | \n| `ambits skill install` | Install the Claude Code skill | \n\n| Flag | Description | \n|---|---|\n| `--project` ,`-p` | Project root (required) | \n| `--session` ,`-s` | Session ID (auto-detects latest) | \n| `--agent` ,`-a` | Filter to one agent ID (prefix matching) | \n| `--filter` /`--filter-regex` | Restrict analysis to a subpath or regex | \n| `--format` | `table` (default) or`json` | \n| `--serena` | Use Serena's LSP symbol cache | \n| `--tools-config` | Custom tool-mapping TOML | \n| `--no-journal` | Disable the read journal | \n| `--flush-interval-ms` | Journal write interval | \n| `--log-dir` | Claude Code log directory (auto-derived) | \n| `--log-output` | Write processed events to a directory | \n\nRequires Rust 1.82+ (declared as `rust-version` in `Cargo.toml`).\n\n```\ncargo build --release\ncargo test\n```\n\n", "url": "https://wpnews.pro/news/show-hn-ambits-agentic-grep-rg-tool-will-history-tracking", "canonical_source": "https://github.com/joshLong145/ambits", "published_at": "2026-09-21 02:37:25+00:00", "updated_at": "2026-09-21 02:53:37.214501+00:00", "lang": "en", "topics": ["ai-agents", "ai-tools", "developer-tools", "ai-products"], "entities": ["Ambits", "Claude Code", "ripgrep", "GNU grep", "regex"], "alternates": {"html": "https://wpnews.pro/news/show-hn-ambits-agentic-grep-rg-tool-will-history-tracking", "markdown": "https://wpnews.pro/news/show-hn-ambits-agentic-grep-rg-tool-will-history-tracking.md", "text": "https://wpnews.pro/news/show-hn-ambits-agentic-grep-rg-tool-will-history-tracking.txt", "jsonld": "https://wpnews.pro/news/show-hn-ambits-agentic-grep-rg-tool-will-history-tracking.jsonld"}}