Deploy the drones. Map the terrain. Return with intel.
📚 Full documentation at lliwcwill.github.io/fsuite
A suite-level guide plus twelve operational tools for filesystem reconnaissance, continuity, patching, replay, binary analysis, and analytics.
fsuite provides one suite-level guide command plus twelve operational tools that turn filesystem exploration into a clean, scriptable, agent-friendly investigation workflow:
| Tool | Purpose |
|---|---|
fsuite |
Print the suite-level conceptual flow, tool roles, and headless usage guidance |
fs |
Unified search entry point — classifies query intent and auto-routes to fsearch, fcontent, or fmap |
ftree |
Visualize directory structure with smart defaults and recon mode |
fsearch |
Find files by name, extension, or glob pattern |
fcontent |
Search inside files for text (powered by ripgrep) |
fmap |
Extract structural skeleton from code (code cartography) |
fread |
Read files with budgets, ranges, context windows, and diff-aware input |
fcase |
Preserve investigation state, evidence, and handoffs once the seam is known |
fedit |
Apply surgical text patches with dry-run diffs, preconditions, and symbol scoping |
fwrite |
Write or overwrite files from agent output — MCP-native, safe atomic writes (MCP adapter only) |
freplay |
Deterministic replay of recorded investigation command sequences |
fprobe |
Binary and opaque-file reconnaissance — strings, scan, and byte-window reads |
fmetrics |
Analyze telemetry, history, combo patterns, recommendations, and predicted runtime |
fls |
Quick directory listing — thin ftree router for list, little tree, and recon modes |
The first reconnaissance tools are the sensor layer. fs is the unified entry point that accepts a raw query, classifies intent (path pattern, literal content, or structural symbol), and builds the right tool chain automatically — removing the decision overhead from the agent's first move. fcase is the continuity ledger. fedit is the surgical patch arm. fwrite is the safe write surface exposed through the MCP adapter. freplay is the investigation playback engine. fprobe is the binary sensor. fmetrics is the flight recorder and analyst. Together they cover scout -> find/search -> map -> read -> preserve -> edit -> replay -> probe -> measure. The fsuite command is the suite-level explainer that teaches that flow to humans and agents on first contact.
The flow an agent should internalize:
fs -> ftree -> fsearch | fcontent -> fmap -> fread -> fcase -> fedit -> freplay -> fmetrics
^
fprobe (binary/opaque files)
fs auto-routes the opening move. fprobe branches off the main pipeline whenever the target is a compiled binary, packed asset, or SEA bundle. Everything else flows left to right.
Works with Claude Code, Codex, OpenCode, and any shell-capable agent harness that can call local binaries.
| Install Path | Best For | Status |
|---|---|---|
./install.sh --user |
Fast local install without sudo | Recommended |
| Debian package | Linux release installs | Available |
| Source + manual symlink | Power users and repo hacking | Available |
MCP adapter (mcp/) |
Native tool calls in Claude Code / Codex / OpenCode — exposes all tools as first-class MCP calls | Available |
| Homebrew tap | macOS-native package install | Roadmap |
| npm wrapper | Installer/distribution wrapper, not a rewrite | Roadmap |
The MCP adapter (mcp/index.js) is a stateless Node.js dispatcher built on @modelcontextprotocol/sdk. It uses execFile — never exec — so arguments are array elements, not shell strings. Add it to your Claude Code or Codex harness config to make every fsuite tool show up as a native tool call alongside Read, Edit, and Grep.
- Why This Exists
- Quick Start
- First-Contact Mindset
- fsuite Help
- Fast Paths
- Tools
- fs — unified entry point
- fsearch — filename / path search
- fcontent — file content search
- ftree — directory structure visualization
- fmap — code cartography
- fread — budgeted file reading
- fcase — continuity / handoff ledger
- fedit — surgical patching
- fwrite — safe atomic writes (MCP adapter)
- freplay — deterministic investigation replay
- fprobe — binary / opaque file reconnaissance
- fmetrics — telemetry analytics
- Chain Combinations
- MCP Adapter
- Dev Mode
- Binary Patching
- Language Support
- Output Formats
- Agent / Headless Usage
- Cheat Sheet
- Quick Reference — Flags
- Optional Dependencies
- Telemetry
- Security Notes
- Installation
- Testing
- Changelog
- License
The honest version: fsuite was always supposed to be CLI-first. The MCP server, the hooks, the enforcement stack — those came later, in the wrong order, and we're still catching up to the lightbulb moment that probably should have come first.
Here's the sequence, for the record:
- Built the fsuite CLI tools.
fread,fedit,ftree,fs,fmap,fcase, the whole stack. The core idea: give coding agents bounded, structured, token-budgeted alternatives togrep,find,cat,sed, andbash. CLI-first, always. That was the plan. - Built the MCP server. Because even with fsuite installed, Claude would still reach for its native
Bashtool to runfreadinstead of something structured. So we wrapped the whole suite in MCP to give it a cleaner call path. The thing picked up a color scheme (Monokai) along the way, which turned out to be the part we love the most about it. - Built
fbash. Because we realized we could replace theBashtool too — token-budgeted, classified, session-aware. At this point fsuite had 14 tools and a whole MCP layer and we still thought we were doing the right thing. - Discovered Claude Code hooks. After all of the above. It turns out you can just block native tools at the source and force agents to use fsuite instead. One
PreToolUsehook per blocked tool. No MCP needed to enforce usage. If we'd known about hooks first, the MCP might not exist at all. - Tried to rely on hooks alone. Couldn't. Hooks block, but they don't route — an agent can get blocked on
Readbut has no automatic translation tofread. So we defaulted back to the MCP as the "easy path" for agents to discover and invoke the suite. The two layers ended up complementary: hooks block the old tools, MCP exposes the new ones, agents have nowhere to land except on fsuite.
The recursion problem. If you removed the MCP today, an agent would have to use its native Bash tool to call fbash to call fread. That's two hops, and it defeats the whole "use fsuite instead of Bash" thesis. The honest truth: without the MCP, the agent would just use its Bash tool to call fread directly — which still works, but you lose the Monokai-colored structured output the MCP layer renders. Which is the part we love the most about the MCP path. Which should probably just be on the CLI tools too. Real TODO. Not shipped yet.
So that's how we got here. Messy build order, three layers stacked on top of each other, and somewhere in the middle of the chaos the thing started to work. Don't take our word for it — we didn't believe it either until we pointed Claude Code at the repo, told it to clone, study, and live-test the tools, and asked it to do a Tony Stark autopsy: compare fsuite against its own built-in toolkit and tell us honestly what it would change.
It didn't just say "nice tools." It wrote a full self-assessment. Unprompted conclusions. No instructions on what to find.
The headline finding:
"The gap isn't in any single tool. It's in the reconnaissance layer. I have no native way to answer the question: 'What is this project, how big is it, and where should I look first?'"
"fsuite doesn't make any of my tools obsolete, but it fills the reconnaissance gap that is genuinely my weakest phase of operation. I'm good at reading code, editing code, and running commands. I'm bad at efficiently finding what to read in the first place. fsuite is built specifically for that phase, and built specifically for how I operate."
— Claude Code (Opus 4.5), self-assessment, January 2026
What the agent said it would do:
| Tool | Agent's Verdict |
|---|---|
| ftree | "Net new capability. Nothing I have comes close." — Replaces the Explore agent for structural recon. |
| fsearch | "Augment. Use alongside Glob for discovery and pipeline scenarios." — Pattern normalization + pipeline composability. |
| fcontent | "Augment. Use for pipeline searches and scoped discovery." — Piped mode + match caps designed for LLM context windows. |
That first round exposed the real missing step: after recon and search, the agent still had to spend extra calls just to read the right slice of a file. fmap and fread close that gap. fcase preserves the investigation once the seam is known. fedit turns that bounded context into a safe patch surface. fmetrics closes the final loop by turning live usage into operational feedback instead of guesswork.
Since v2.2.0, the fleet has expanded further. fprobe extends the sensor layer into binary and opaque files — compiled Node.js SEA bundles, packed assets, anything fread can't reach. freplay makes recorded investigation sequences deterministically reproducible across agents and sessions. fs removes the opening routing decision entirely: give it a raw query and it classifies intent, builds the chain, and returns ranked results without the agent choosing between fsearch, fcontent, and fmap on the first move.
The workflow shift — before and after:
BEFORE fsuite:
Spawn Explore agent -> 10-15 internal tool calls -> still blind on structure
AFTER fsuite (v2.3.0):
fs <query> /project -> ftree --snapshot -o json -> fmap -o json -> fread -o json
-> fcase init <seam> --goal "..." -> fedit -o json -> freplay -> fmetrics stats
7-9 calls. Structural context, bounded reads, durable continuity, previewable edits,
replay verification, and runtime measurement. Dramatically fewer invocations.
Binary targets? Add fprobe strings / fprobe scan to the branch.
And once those reads are happening in the real world:
AFTER execution:
... -> fcontent -o json (only if exact text confirmation is needed)
-> fcase handoff <seam> -o json
-> fedit -o json
-> freplay --verify
-> fmetrics import -> fmetrics stats / combos / recommend / predict
Search inside the narrowed set, preserve what matters, patch surgically,
verify the replay, then measure what actually happened and plan the next pass.
Proof callout — Nightfox investigation: In a live Nightfox runtime incident, the breakthrough came when the operator coached the agent to stop overcompensating and trust fsuite's direct contracts. The useful path was not a sacred sequence. It was a clean combination of
fsearch,fmap,fread, and targetedfcontentthat surfaced a real subprocess-lifecycle bug. The milestone was not just that the tools worked. It was that the agent stopped fighting them.
The full unedited analysis is in AGENT-ANALYSIS.md — the raw self-assessment, exactly as Claude Code wrote it after studying and testing every tool in this repo.
That document is the pitch. Not because we wrote it, but because the agent did.
# Clone and make executable
git clone https://github.com/lliWcWill/fsuite.git
cd fsuite
# Recommended: install into ~/.local/bin
./install.sh --user
# Suite-level guide
./fsuite
# Unified search — auto-routes by query shape
./fs "*.log" /var/log
./fs renderTool /project/src
./fs "error loading config" /project
# Find all .log files under /var/log
./fsearch '*.log' /var/log
# Search inside files for "ERROR"
./fcontent "ERROR" /var/log
# Scout a project: what's here?
./ftree --recon /project
# Show the directory tree (depth 3, smart defaults)
./ftree /project
# Map code structure before opening files
./fmap -o json /project/src
# Read targeted context around a function or match
./fread /project/src/auth.py --around "def authenticate" -A 20
# Preserve the live investigation once the seam is known
./fcase init auth-seam --goal "Trace authenticate flow"
# Combine: find logs, then grep inside them
./fsearch --output paths '*.log' /var/log | ./fcontent "ERROR"
# Import telemetry and inspect runtime history
./fmetrics import && ./fmetrics stats
# Binary recon — inspect a compiled binary
./fprobe strings ./binary --filter "renderTool"
./fprobe scan ./binary --pattern "SNAPSHOT_BLOB" -o jsonfsuite works best when the agent stops compensating for weak default tooling and starts reasoning natively with the contracts in front of it. This is a composable sensor suite, not a single sacred sequence. Some combinations are stronger than others, but the right move is to choose the smallest chain that increases certainty without flooding context.
- Use
-o jsonand-o pathsaggressively. They are the default agent surfaces. ftreeis powerful, but use it intentionally and sparingly on large repos.fsis the fastest opening move — it auto-classifies and routes your query.fsearch -> fmapis a strong default when you know the area but not the seam.fcontent -o paths -> fmapis strong when literal evidence is the cleanest narrowing signal.fsearch -> fcontent -o paths -> fmapis a real narrowing pattern, not an anti-pattern.fmapis not just a producer beforefread; it is the bridge in the middle of the pipeline.fmap + freadis the power pair for understanding code.fcasebegins once the seam is known and continuity becomes the bottleneck.feditcomes after inspected context, not before.fprobebranches off the main pipeline when the target is binary or opaque.fmetricsis for observability and next-pass planning, not a reason to spamftree.
The mindset shift is simple: literal search is a strength here, not a fallback. If the exact token, phrase, or signature in front of you is the best narrowing handle, use it directly. The goal is not to imitate the habits agents learned from weaker default tools. The goal is to use the strongest local contract available.
1. Run `fsuite` once per session to load the suite-level mental model.
2. Use `fs` as the opening move — let it classify and route your query automatically.
3. Run `ftree` once to establish territory before narrowing.
4. Start with `fsearch` to narrow candidate files by path or filename.
5. Prefer `fmap + fread` before broad `fcontent`.
6. Use `fcontent` only for exact-text confirmation after narrowing.
7. Prefer `-o paths` for piping, `-o json` for programmatic decisions, and `pretty` only for human terminal output.
8. Use `-q` for silent control flow and existence checks.
9. Use `fcase` once the seam is known and continuity becomes the bottleneck.
10. Use `fprobe` when the target is a binary, SEA bundle, or packed asset.
11. Use `fmetrics` to measure what happened and estimate the next pass.
12. Do not overcompensate for weak default tools. Literal search is a strength here, not a fallback.
fsuite is a real suite-level guide command. The operational work still happens through the twelve underlying tools, but fsuite is the fastest way to load the mental model.
If your harness reads repo instructions automatically, use the bundled AGENTS.md as the suite-level operating guide.
If an agent only remembers one thing, it should remember this:
fs -> ftree -> fsearch | fcontent -> fmap -> fread -> fcase -> fedit -> freplay -> fmetrics
Auto Scout Narrowing Bridge Read Preserve Edit Replay Measure
^
fprobe (binary)
The CLI equivalent is:
fsuite- Composable sensor suite, not a single sacred path
- Stronger and weaker combinations exist, but not one true sequence
- Use
-o jsonand-o pathsaggressively - Use
fsfor the opening move — it auto-classifies intent - Treat
fmapas the bridge in the middle of the pipeline - Treat literal search as a first-class narrowing move
- Use
fcaseto preserve state once the seam is known - Use
feditonly after inspected context - Use
fprobefor binaries and opaque files - Use
fmetricsfor observability, not as a reason to repeat recon
| Tool | Use it when you need to answer | Best output for agents |
|---|---|---|
fs |
"Find me anything related to this query — route it for me." | -o json |
ftree |
"What is in this project, how big is it, and where should I look first?" | -o json |
fsearch |
"Which files match this name, extension, or glob?" | -o paths or -o json |
fmap |
"What symbols exist in these source files?" | -o json |
fcontent |
"Which narrowed files contain this exact text?" | -o json or -o paths |
fread |
"Show me the exact lines around this function, match, or diff hunk." | -o json |
fcase |
"What matters now, what have we ruled out, and what should the next agent do?" | -o json |
fedit |
"Preview and apply a surgical patch against the exact symbol or anchor I just inspected." | -o json |
fwrite |
"Create or overwrite a file from agent output." (MCP only) | -o json |
freplay |
"Show me the exact commands that produced this investigation state." | -o json |
fprobe |
"What strings, patterns, or byte sequences live inside this binary?" | -o json |
fmetrics |
"What did these runs cost, and what will the next one cost?" | stats -o json, predict |
- Prefer
-o jsonwhen the next step is programmatic decision-making. - Prefer
-o pathswhen the next step is piping into another fsuite tool. - Prefer
prettyonly for human terminal use. - Errors go to
stderr. Results go tostdout. -qis for existence checks and silent control flow.- Use
fsuitefor the suite-level mental model and each tool's--helpfor the full flag breakdown.
# 0) Load the suite-level guide once
fsuite
# 1) Auto-route the opening move
fs "authenticate" /project/src
# 2) Scout the target once
ftree --snapshot -o json /project
# 3) Narrow to candidate files
fsearch -o paths '*.py' /project/src
# 4) Map structure before broad reads
fsearch -o paths '*.py' /project/src | fmap -o json
# 5) Read the exact code neighborhood you care about
fread -o json /project/src/auth.py --around "def authenticate" -B 5 -A 20
# 6) Preserve the current case once the seam is known
fcase init auth-seam --goal "Trace authenticate flow"
fcase evidence auth-seam --tool fread --path /project/src/auth.py --lines 40:72 --summary "Authenticate branch" --body "..."
fcase next auth-seam --body "Patch denial branch after reviewing symbol map"
# 7) Only if you still need exact text confirmation, search inside the narrowed set
fsearch -o paths '*.py' /project/src | fcontent -o json "authenticate"
# 8) Preview and then apply the patch
fedit -o json /project/src/auth.py --symbol authenticate --replace "return False" --with "return deny()"
fedit -o json /project/src/auth.py --symbol authenticate --replace "return False" --with "return deny()" --apply
# 9) Import telemetry and inspect the cost of what just happened
fmetrics import
fmetrics stats -o json
fmetrics predict /projectThis is a strong default, not a sacred sequence. When exact text is the cleanest narrowing signal, use fcontent directly. When you already have a narrowed path set, prefer -o paths and keep the next step cheap.
# Path narrowing to structure
fsearch -o paths '*.py' /project/src | fmap -o json
# Literal narrowing to structure
fcontent -o paths "authenticate" /project/src | fmap -o json
# Path narrowing, then literal confirmation, then structure
fsearch -o paths '*.py' /project/src | fcontent -o paths "authenticate" | fmap -o json
# Structure + bounded reading
fsearch -o paths '*.py' /project/src | fmap -o json
fread -o json /project/src/auth.py --around "def authenticate" -B 5 -A 20
# Preserve the seam for a reset or handoff
fcase init auth-seam --goal "Trace authenticate flow"
fcase next auth-seam --body "Review denial branch after map/read pass"- Use
fsas the opening move for unfamiliar codebases. - Run
ftreeonce to establish territory. - Start with
fsearchto narrow candidate files. - Add
fcontentonly if exact-text confirmation is needed. - Prefer
fmap+freadbefore broadfcontent. - Use
fcasewhen continuity, evidence tracking, or handoff becomes the bottleneck. - Use
fcontentas exact-text confirmation after narrowing, not as the first conceptual repo search. - Use
fprobewhen the target is binary or opaque. - Do not rediscover the repo twice unless the target changes or a contradiction appears.
| If you need... | Use... |
|---|---|
| auto-routed search across file, symbol, and content | fs |
| project shape, size, likely hotspots | ftree |
| candidate filenames | fsearch |
| structural skeleton without reading full files | fmap |
| content matches across files | fcontent |
| bounded context from a known file | fread |
| durable case state, evidence, or a handoff | fcase |
| safe patch application against inspected context | fedit |
| file creation or full replacement (MCP) | fwrite |
| replaying an investigation sequence | freplay |
| binary or opaque file reconnaissance | fprobe |
| runtime history or a preflight estimate | fmetrics |
Four workflows that cover the common cases without improvisation. Copy, paste, go.
ftree --snapshot -o json /project | jq .fsearch -o paths '*.ts' /project/src | fcontent -o json "Auth" | jq .fsearch -o paths '*.py' /project/src | fmap -o json | jq '.files[:5]'
fread /project/src/auth.py --around "def authenticate" -B 5 -A 20ftree --recon -o json /project | jq '.entries | sort_by(-.size_bytes) | .[:10]'Note:
jqis optional — used only in these examples for pretty-printing JSON. All tools output valid JSON natively with-o json.
One call. Auto-routes. fs classifies your query's intent — file, symbol, or content — then builds and fires the optimal fsuite tool chain behind the scenes. You never have to decide whether to reach for fsearch, fcontent, or fmap separately. The drone swarm assembles itself.
fs [OPTIONS] <query> [path]What it does:
fs runs a Python engine (fs-engine.py) that classifies the query, selects tools, executes them in sequence, and returns ranked results with enrichment metadata and a next_hint field for follow-up refinement. Output is pipeline-safe: auto-switches from pretty to json when stdout is not a terminal.
Intent classification rules:
| Query shape | Detected intent | Tool chain fired |
|---|---|---|
*.py, *.log, *.rs |
file |
fsearch |
renderTool, McpServer, AuthHandler (camelCase / PascalCase) |
symbol |
fsearch -> fmap --name |
parse_tokens, emit_chunk (snake_case) |
symbol |
fsearch -> fmap --name |
MAX_RETRIES, DB_PATH (SCREAMING_CASE) |
symbol |
fsearch -> fmap --name |
"error loading config", "failed to connect" (multi-word quoted) |
content |
fcontent |
-i symbol authenticate (forced override) |
symbol |
fmap --name |
router, config, logger (single bare word) |
content (low-confidence) |
fcontent |
Key capabilities:
- Single entry point for all search intent — no more per-tool decision overhead
- Automatic intent detection from query shape (glob, camelCase, multi-word phrase)
--intentflag to override classification when auto-detection is wrong--scope GLOBnarrows the candidate file set before tool chain execution- Ranked hits with enrichment: file size, language, symbol count where available
next_hintin JSON output tells the agent what to call next- Hard caps:
--max-candidates(default 50),--max-enrich(default 15),--timeout(default 10s) - Auto output mode:
prettyfor terminal,jsonfor pipe
Examples:
# File search — glob pattern detected
fs "*.py"
# Symbol search — camelCase detected automatically
fs renderTool
# Content search — multi-word phrase detected
fs "error loading config" src/
# Symbol search scoped to TypeScript files only
fs -s "*.ts" McpServer
# Force symbol intent when auto-detection would miss it
fs -i symbol authenticate
# JSON output piped to jq
fs -o json "*.rs" | jq '.hits'
# Scoped content search with explicit path
fs -p /home/user/project "failed to parse"
# Override candidate cap for large monorepos
fs --max-candidates 200 "*.go" /repoJSON output example:
{
"tool": "fs",
"version": "2.3.0",
"intent": "symbol",
"query": "renderTool",
"hits": [
{
"path": "src/tools/render.ts",
"score": 0.97,
"symbol": "renderTool",
"line": 42,
"language": "typescript"
}
],
"hit_count": 1,
"next_hint": "fread src/tools/render.ts --symbol renderTool"
}Flags:
| Flag | Default | Description |
|---|---|---|
-s, --scope GLOB |
— | Glob filter applied before tool chain (e.g. "*.py") |
-i, --intent MODE |
auto |
Override intent: auto | file | content | symbol |
-o, --output MODE |
pretty/json |
Output format; auto-selects based on tty |
-p, --path PATH |
. |
Search root; overrides positional path argument |
--max-candidates N |
50 |
Cap on candidate files fed into the chain |
--max-enrich N |
15 |
Cap on files enriched with symbol/content metadata |
--timeout N |
10 |
Wall-time cap in seconds for the full chain |
-h, --help |
— | Show usage |
--version |
— | Print version |
MCP contract (v2.3.0+):
When called through the MCP adapter, fs declares an outputSchema (Zod-validated JSON shape) and returns both a content field (human-readable text summary for display) and a structuredContent field (the full machine-readable JSON result). This lets the client agent consume structuredContent directly — no JSON parsing of the text summary required.
content → plain-text hit summary ("3 hits in 2 files — next: fread src/auth.ts --symbol renderTool")
structuredContent → { tool, version, intent, query, hits[], hit_count, next_hint }
The outputSchema shape matches the JSON output example above. If structuredContent is absent (older clients), the agent should parse the JSON from content instead.
Searches for files by name or glob pattern. Automatically picks the fastest available backend (fd > find).
fsearch [OPTIONS] <pattern_or_ext> [path]Key features:
- Glob-aware:
'upscale*','*progress*','*.log' - Smart pattern handling:
- Wildcards are preserved (
*/?). - Leading dot becomes an extension:
.log->*.log. - Dotted names are literal:
file.txt->file.txt. - Short lowercase tokens (<=4 chars,
^[a-z0-9]+$, not purely numeric) become extensions:py->*.py,main->*.main. - Numeric-only tokens are treated literally (e.g.
123stays123).
- Wildcards are preserved (
- Auto-selects
fd/fdfindwhen available, falls back to POSIXfind - Interactive mode (prompts for missing args) or fully headless
--include/--excludesupport for post-search filtering, both repeatable and wildcard-aware- Built-in low-signal directory suppression by default:
node_modules,dist,build,.next,coverage,.git,vendor,target- disable with
--no-default-ignore
- Quiet mode (
-q) for existence checks — exit 0 if found, 1 if not - Three output formats:
pretty(default),paths(one per line),json
Examples:
# Human-friendly output
fsearch '*.conf' /etc
# Agent-friendly: paths only
fsearch --output paths '*.py' /home/user/projects
# Agent-friendly: structured JSON
fsearch --output json '*token*' /home/user
# Force the fd backend
fsearch --backend fd '*.rs' /opt/src
# Monorepo noise reduction (skip generated dirs)
fsearch --exclude node_modules --exclude .git '*.py' /repo
# Focused scan with inclusion and exclusion
fsearch -I 'services/*' -x '*test*' '*.go' /project/src
# Interactive mode (prompts for pattern and path)
fsearch -iSearches inside files using rg (ripgrep). Accepts a directory or a piped list of file paths from stdin.
fcontent [OPTIONS] <query> [path]Key features:
- Directory mode: recursively searches a path
- Piped mode: reads file paths from stdin (pairs with
fsearch --output paths) - Directory mode suppresses low-signal dependency/build trees by default:
node_modules,dist,build,.next,coverage,.git,vendor,target- disable with
--no-default-ignore
- Three output formats:
pretty(default),paths(matched files only),json - Configurable match (
-m) and file (-n) caps to prevent terminal floods - Quiet mode (
-q) for existence checks — exit 0 if found, 1 if not - Pass-through for extra
rgflags via--rg-args - MCP mode forces fixed-string matching by default so regex metacharacters stay literal for agents
Operational note: use fcontent after narrowing with fsearch/fmap/fread, but do not treat literal search as a fallback. Within a narrowed scope, exact text is often the cleanest and fastest route to the next structural step. fcontent -o paths is especially useful when the next move is fmap.
Examples:
# Search a directory
fcontent "database" /home/user/project
# Pipe from fsearch
fsearch --output paths '*.log' /var/log | fcontent "CRITICAL"
# Narrow by exact text, then bridge into structure
fsearch --output paths '*.py' /project/src | fcontent --output paths "authenticate" | fmap -o json
# JSON output for agent consumption
fcontent --output json "api_key" /home/user/project
# Case-insensitive search with hidden files
fcontent --rg-args "-i --hidden" "secret" /home/userWraps tree with smart defaults, context-budget awareness, and a recon mode for scouting directories before committing context window to a full tree dump.
ftree [OPTIONS] [path]Key features:
- Smart defaults: depth 3, 200-line cap, noise directories excluded (node_modules, .git, venv, etc.)
- Recon mode: per-directory item counts and sizes without full tree expansion
- Excluded-dir summaries in recon show what's hidden and how big it is
--includepromotes excluded dirs back to normal treatment- Multiple
--ignoreflags accumulate (v1.0.1+):-I 'docs' -I '*.md'excludes both - Quiet mode (
-q) for silent operation — exit code only - Three output formats:
pretty(default),paths(flat file list),json - Snapshot mode (
--snapshot): combined recon + tree in one command (v1.2.0+) - Truncation with overflow count and drill-down suggestion
- Clear error messages when flags are missing their required values
Examples:
# Default tree (depth 3, smart excludes, 200-line cap)
ftree /project
# Recon: scout per-directory sizes before committing context
ftree --recon /project
# JSON for agent consumption
ftree -o json /project
# Flat file list for piping
ftree -o paths /project
# Drill into a subdirectory
ftree -L 5 /project/src
# Include a normally-excluded directory
ftree --include .git /project
# Stack multiple ignore patterns (each -I adds to the list)
ftree -I 'docs' -I '*.md' /project
# Snapshot: recon + tree in one shot
ftree --snapshot /project
# Snapshot JSON for agents
ftree --snapshot -o json /project
# Recon without excluded-dir summaries
ftree --recon --hide-excluded /project
# Show sizes in tree output
ftree -s /project
# Directories only
ftree -d /projectSee docs/ftree.md for the full deep-dive: architecture, all flags, headless agent workflows, interactive human usage, and tandem usage with fsearch/fcontent.
Extracts the structural skeleton from source files — functions, classes, imports, types, exports, constants — without reading full file contents. Fills the gap between "found files" and "read full files."
fmap [OPTIONS] [path]Key features:
- Zero dependencies beyond
grep(usesgrep -n -E -I) - Three modes: directory (recursive), single file, piped file list from stdin
- 18 languages / formats: Python, JavaScript, TypeScript, Kotlin, Swift, Rust, Go, Java, C, C++, Ruby, Lua, PHP, Bash, Dockerfile, Makefile, YAML, Markdown
- Bash function detection: both
name() {andfunction name {forms - Shebang fallback for extensionless files (
#!/usr/bin/env bash) - Symbol type filtering (
-t function,-t class, etc.) - Import removal (
--no-imports) with precedence rule (-t importoverrides) - Three output formats:
pretty(default),paths(file list),json - Symbol and file caps (
-m,-n) with truncation indicators
Examples:
# Map a project directory
fmap /project
# Single file analysis
fmap /project/src/auth.py
# JSON output for agents
fmap -o json /project
# Rank exact then substring symbol-name hits
fmap --name authenticate -o json /project/src
# Pipeline: find Python files, extract structure
fsearch -o paths '*.py' /project | fmap -o json
# Functions only, no imports
fmap -t function --no-imports /project
# Force language detection
fmap -L bash /project/scripts/deploy
# Cap symbols for large projects
fmap -m 100 /projectReads just enough file content for the next step in an investigation. It fills the gap after fsearch/fmap: once you know which file matters, fread lets you read a range, read around a line or literal pattern, cap by lines/bytes/tokens, or feed it paths/diffs from stdin.
fread [OPTIONS] <file>
fread [OPTIONS] <dir> --symbol <name>Key features:
- Range reads (
-r 120:220), head/tail, and context windows around a line or literal pattern - Budget controls:
--max-lines,--max-bytes,--token-budget - Stdin modes:
--stdin-format=pathsforfsearch -> fread--stdin-format=unified-diffforgit diff -> fread
- Binary detection with
--force-textescape hatch next_hintoutput on truncation so agents can continue exactly where they stopped- Three output formats:
pretty(default),paths,json
Examples:
# Read a bounded file excerpt
fread /project/src/auth.py --head 80
# Read a precise range
fread /project/src/auth.py -r 120:220
# Read around a literal pattern
fread /project/src/auth.py --around "def authenticate" -B 5 -A 20
# Read one exact symbol block from a known file or directory scope
fread /project/src/auth.py --symbol authenticate -o json
fread /project/src --symbol authenticate -o json
# Read around a changed hunk from git diff
git diff | fread --from-stdin --stdin-format=unified-diff -B 3 -A 10
# Pipe file paths from fsearch and cap how many get read
fsearch -o paths '*.py' /project | fread --from-stdin --stdin-format=paths --max-files 5 -o jsonPreserves the live shape of an investigation once the seam is known. fcase tracks what the case is trying to solve, which targets matter, what evidence has been captured, which hypotheses are open or rejected, and what the next best move is for a reset or handoff.
fcase <subcommand> [options]Key features:
- Separate SQLite ledger at
~/.fsuite/fcase.db - Fast current-state reads for
statusandhandoff - Typed targets, evidence, and hypotheses instead of ad hoc notes
- Append-only events for history without making handoff reconstruct the world
- Explicit imports from
fmapandfreadJSON when continuity should start from existing proof - Pretty or JSON output for human and agent use
Examples:
# Start a case once the seam is known
fcase init auth-seam --goal "Trace authenticate flow"
# Track the file and symbol that matter
fcase target add auth-seam --path /project/src/auth.py --symbol authenticate --symbol-type function --state active --reason "Primary decision point"
# Preserve proof from a targeted read
fcase evidence auth-seam --tool fread --path /project/src/auth.py --lines 40:72 \
--summary "Authenticate branch" --body "return deny() is bypassed in the false branch"
# Import structured targets straight from fmap
fmap -o json /project/src/auth.py | fcase target import auth-seam
# Import structured evidence straight from fread
fread -o json /project/src/auth.py --around "def authenticate" -B 5 -A 20 | fcase evidence import auth-seam
# Keep the next best move current
fcase next auth-seam --body "Patch denial branch after reviewing symbol map"
# Hand off cleanly
fcase handoff auth-seam -o jsonsequenceDiagram
actor User
participant fsuite
participant ftree
participant fsearch
participant fcontent
participant fmap
participant fread
participant fcase
participant SQLite as SQLite (fcase.db)
User->>fsuite: first contact
fsuite-->>User: suite-level workflow guidance
User->>ftree: scout once
ftree-->>User: territory, project shape, hotspots
User->>fsearch: -o paths <pattern>
fsearch-->>User: candidate paths
opt exact-text confirmation only if needed
User->>fcontent: -o paths <literal-or-regex>
fcontent-->>User: narrowed paths
end
User->>fmap: -o json [paths] [--name <symbol>]
fmap-->>User: ranked symbols and structure
User->>fcase: init <slug>
fcase->>SQLite: CREATE case, session, event
fcase-->>User: case envelope
User->>fcase: target import <slug> (fmap JSON)
fcase->>SQLite: INSERT targets, event
fcase-->>User: ack
User->>fread: --symbol <name> [--symbol-type <type>] <path>
fread->>fmap: resolve symbol
fmap-->>fread: symbol metadata
fread-->>User: bounded context + lines
User->>fcase: evidence import <slug> (fread JSON)
fcase->>SQLite: INSERT evidence, event
fcase-->>User: ack
User->>fcase: next <slug>
fcase->>SQLite: UPDATE next_move, event
fcase-->>User: ack
User->>fcase: handoff <slug>
fcase->>SQLite: SELECT case, targets, evidence, hypotheses, events
fcase-->>User: handoff JSON envelope
Applies preview-first text patches after you have already narrowed the target with fsearch, fread, and fmap. It defaults to dry-run, emits a unified diff, and only mutates the file when --apply is present.
Important behavior difference — CLI vs MCP:
- CLI:
feditdefaults to dry-run (preview only). You must pass--applyto write changes.- MCP adapter:
feditdefaults to apply=true (writes immediately). Setapply: falsefor a dry-run preview.This difference is intentional. CLI users expect to preview before committing. MCP agents have already decided to write by the time they call a mutation tool.
fedit [OPTIONS] <file>Key features:
- Dry-run by default (CLI);
--applyis required to write - Exact replacement plus
--after/--beforeanchor insertion - Line-range replacement with
--lines START:END(v2.2.0+) - Preconditions with
--expectand--expect-sha256 --symbol/--symbol-typescope a patch to onefmap-resolved symbol block- Three output formats:
pretty(default),paths,json
Examples:
# Preview a direct replacement
fedit /project/src/auth.py --replace 'return False' --with 'return deny()'
# Apply the replacement only inside one symbol
fedit /project/src/auth.py --symbol authenticate --symbol-type function \
--replace 'return False' --with 'return deny()' --apply
# Insert after an anchor
fedit /project/src/auth.py --after 'def authenticate(user):' \
--content-file patch.txt --applyReplaces a specific line range with new content. The replacement is identified by line numbers, not by anchor text — no anchor ambiguity, no pattern matching, no regex. Designed for use directly after fread returns line numbers in its output.
Typical workflow:
# 1. Read the file — fread output includes line numbers in every chunk
fread /project/src/config.ts --around "defaultTimeout" --after 15
# Output shows lines 88-103 contain the block you need to replace
# 2. Replace exactly those lines (dry-run preview)
fedit /project/src/config.ts --lines 88:103 --with "$(cat new-block.ts)"
# 3. Apply when confirmed
fedit /project/src/config.ts --lines 88:103 --with "$(cat new-block.ts)" --applyKey behaviors:
- Line numbers are 1-indexed, inclusive on both ends (
88:103replaces lines 88 through 103) - Combines with
--with TEXTfor inline replacement content - Combines with
--content-file PATHfor multi-line payloads from a file - Combines with
--stdinfor pipeline-delivered content - Dry-run by default — requires explicit
--applyto mutate - Fails closed if the line range is out of bounds for the target file
- Rejects inverted ranges (
end < start) with a clear error - JSON output (
-o json) returnslines_replaced,line_start,line_endin the result envelope
Examples:
# Replace lines 88-103 with inline content
fedit /project/src/config.ts --lines 88:103 \
--with " defaultTimeout: 5000," \
--apply
# Replace a block with a multi-line payload from a file
fedit /project/src/handler.py --lines 42:67 \
--content-file patch-body.py \
--apply
# Dry-run preview only (default — no --apply)
fedit /project/src/auth.ts --lines 120:135 \
--with " return deny(reason);"
# JSON output for agent verification
fedit /project/src/auth.ts --lines 120:135 \
--with " return deny(reason);" \
--apply -o json
# Combine with fread line-number output in an agent loop
RANGE=$(fread /project/src/server.ts --around "startServer" --after 20 -o json \
| jq -r '"\(.chunks[0].start_line):\(.chunks[0].end_line)"')
fedit /project/src/server.ts --lines "$RANGE" --content-file new-start.ts --applyfwrite is not a CLI command. It is an MCP-only virtual tool that routes through fedit's mutation engine. When an agent calls fwrite via the MCP server, the server translates the call into the appropriate fedit --create or fedit --replace-file --apply operation. One mutation brain; two surfaces.
This design means agents never need to decide between fedit and a separate write primitive. File creation and full-file replacement go through the same dry-run / apply / precondition stack as surgical patches.
MCP tool signature:
{
"name": "fwrite",
"parameters": {
"path": "Absolute file path to write",
"content": "File content to write",
"overwrite": "Replace existing file (default: false = create only)",
"apply": "Apply changes (default: true). Set false for dry-run preview."
}
}Behavior:
overwrite: false(default): fails if the file already exists — safe createoverwrite: true: replaces the entire file fromcontent— equivalent tofedit --replace-file --applyapply: false: returns a diff preview without writing — dry-run mode inherited fromfedit- All writes are atomic: fedit's temp-file + rename pipeline, not a direct
echo >write
When to use fwrite vs fedit (agent guidance):
| Task | Tool |
|---|---|
| Create a new file from scratch | fwrite (MCP) |
| Replace an entire file | fwrite with overwrite: true |
| Surgical inline patch (replace a block, insert after anchor) | fedit |
| Line-range replacement | fedit --lines |
| Batch patch across multiple files | fedit --targets-file |
fwriteis only available in MCP-connected agent sessions. It has no CLI equivalent — runningfwritefrom a shell will produce a "command not found" error. Usefedit --createorfedit --replace-filefor the same operations from the command line.
The investigation flight recorder. freplay wraps any fsuite command invocation in a record/replay envelope: it runs the command, captures the invocation arguments, output, exit code, and timestamp into a persistent SQLite store keyed by case slug. Replays can be shown, verified, promoted to canonical status, or exported as JSON for handoffs.
Every recorded replay is linked to an fcase case. This closes the loop between investigation state (fcase) and the exact commands that produced it (freplay).
freplay record <case-slug> [--purpose "..."] [--link <type:id>]... -- <fsuite-command...>
freplay show <case-slug> [--replay-id N] [-o pretty|json]
freplay list <case-slug> [-o pretty|json]
freplay export <case-slug> [--replay-id N] [-o json]
freplay verify <case-slug> [--replay-id N] [-o pretty|json]
freplay promote <case-slug> <replay-id>
freplay archive <case-slug> <replay-id>Key capabilities:
record: runs the target fsuite command and stores its full invocation + resultshow: retrieves a stored replay (latest, or by--replay-id)list: shows all replays for a case with timestamps and statusverify: validates a stored replay without re-executing — checks paths, tool availability, linked entitiespromote: marks a replay as canonical for a casearchive: soft-deletes a replay (recoverable)--purposeannotates the recording with human-readable intent--link type:idcreates a cross-reference to anfcaseevidence or hypothesis entryfreplayandfmetricsare excluded from recording (denylist — no recursive loops)feditrecordings are classifiedread_onlyby default;mutatingwhen--applyis present- Verify exit codes:
0= pass,1= warn,2= fail
Examples:
# Record a fprobe scan with purpose annotation
freplay record sea-bundle-audit --purpose "Locate snapshot blob marker" -- \
fprobe scan ./claude-binary --pattern "SNAPSHOT_BLOB" -o json
# Record a fread with a case link
freplay record auth-seam --link evidence:7 -- \
fread /project/src/auth.ts --symbol authenticate
# Show the latest replay for a case
freplay show auth-seam
# Show a specific replay by ID
freplay show auth-seam --replay-id 3
# List all replays for a case
freplay list sea-bundle-audit
# Verify a replay is still valid (paths exist, tools present)
freplay verify auth-seam --replay-id 2
# Promote replay 2 to canonical
freplay promote auth-seam 2
# Export as JSON for handoff
freplay export auth-seam --replay-id 2 -o jsonSubcommand reference:
| Subcommand | Description |
|---|---|
record |
Run command, store invocation + result |
show |
Display a stored replay |
list |
List all replays for a case |
export |
Export replay as JSON |
verify |
Validate replay integrity without executing |
promote |
Mark replay as canonical |
archive |
Soft-delete a replay |
The deep-scan drone for files that fread cannot parse. fprobe treats its target as a raw byte stream and applies three specialized subcommands: extract printable strings, scan for literal byte patterns with offset context, or read a raw byte window at a known address. Purpose-built for SEA binaries, compiled bundles, and packed assets.
Architecture: Bash CLI layer + Python mmap engine (fprobe-engine.py). The engine does byte-safe memory-mapped reads — no shell text processing on binary content.
fprobe strings <file> [--filter <literal>] [--ignore-case] [-o pretty|json]
fprobe scan <file> --pattern <literal> [--context N] [--ignore-case] [-o pretty|json]
fprobe window <file> --offset N [--before N] [--after N] [--decode printable|utf8|hex] [-o pretty|json]Key capabilities:
- Three focused subcommands map directly to three phases of binary recon
strings: extracts printable ASCII runs (minimum 6 chars);--filternarrows to literal matchesscan: finds a literal byte pattern anywhere in the file; returns byte offset + surrounding context windowwindow: reads a raw byte range at a known offset; decodes as printable text, UTF-8, or hex dump- Python
mmapengine — safe on multi-GB binaries, no full-file read into memory --ignore-caseonstringsandscanfor case-insensitive matching- Auto output mode:
prettyfor terminal with color offsets,jsonfor pipeline/agent consumption - Read-only by design — no mutations, no side effects
Examples:
# Extract all printable strings from a compiled binary
fprobe strings ./claude-binary
# Find strings containing a specific token
fprobe strings ./claude-binary --filter "renderTool"
# Scan for a literal pattern and get byte-offset context
fprobe scan ./claude-binary --pattern "userFacingName" --context 500
# Read 3000 bytes around a known offset (from a previous scan hit)
fprobe window ./claude-binary --offset 112202147 --before 200 --after 3000
# Inspect file header as hex (magic bytes, format identification)
fprobe window ./claude-binary --offset 0 --after 16 --decode hex
# Case-insensitive string filter
fprobe strings ./bundle.js --filter "secret" --ignore-case
# JSON output for agent pipeline
fprobe scan ./app.node --pattern "SNAPSHOT_BLOB" -o json | jq '.matches[0].offset'JSON output example (scan):
{
"tool": "fprobe",
"version": "2.3.0",
"subcommand": "scan",
"file": "./claude-binary",
"pattern": "userFacingName",
"matches": [
{
"offset": 112202147,
"context_before": "...{\"name\":\"",
"match": "userFacingName",
"context_after": "\",\"description\":\"..."
}
],
"match_count": 1
}Subcommand flags:
| Subcommand | Flag | Description |
|---|---|---|
strings |
--filter LITERAL |
Narrow output to strings containing this literal |
strings |
--ignore-case |
Case-insensitive filter matching |
scan |
--pattern LITERAL |
Required. Byte pattern to locate |
scan |
--context N |
Bytes of surrounding context to return (default: 200) |
scan |
--ignore-case |
Case-insensitive pattern matching |
window |
--offset N |
Required. Byte offset to read from |
window |
--before N |
Bytes to include before offset (default: 0) |
window |
--after N |
Bytes to include after offset (default: 200) |
window |
--decode MODE |
Decode output as printable (default), utf8, or hex |
| all | -o pretty|json |
Output format; auto-selects based on tty |
Closes the loop after reconnaissance. fmetrics ingests local telemetry, shows dashboards and history, and predicts how long scans will take on a target before you launch them.
fmetrics <subcommand> [options]Key features:
importmoves JSONL telemetry into SQLitestatsshows usage, runtime, and reliability summarieshistoryfilters by tool and projectpredictestimates runtime from historical dataprofileexposes the Tier 3 machine profile
Examples:
# Import telemetry for analysis
fmetrics import
# See runtime and reliability dashboard
fmetrics stats
# Review recent ftree runs
fmetrics history --tool ftree --limit 10
# Estimate scan time before a large recon
fmetrics predict /projectsequenceDiagram
actor User
participant fmetrics
participant predict as fmetrics-predict.py
participant DB as History DB
alt explicit ftree mode
User->>fmetrics: predict --tool ftree --mode tree
fmetrics->>predict: requested_tool=ftree, requested_mode=tree
predict->>DB: fetch ftree history for mode=tree
DB-->>predict: historical ftree:tree records
predict->>predict: per-feature delta, distance, confidence
predict-->>fmetrics: actionable tree-scoped prediction
fmetrics-->>User: pretty / JSON output for ftree:tree
else default ftree prediction
User->>fmetrics: predict --tool ftree
fmetrics->>predict: requested_tool=ftree, requested_mode=null
predict->>DB: fetch ftree history for tree/recon/snapshot
DB-->>predict: per-mode historical records
predict->>predict: compute per-mode predictions
predict->>predict: collapse to one advisory mixed row with by_mode
predict-->>fmetrics: mixed advisory prediction + by_mode detail
fmetrics-->>User: one top-level ftree row with by_mode
end
Note over predict: If zero_spread_mismatch, confidence = low
Note over fmetrics,predict: If model path is unavailable, average fallback keeps the same contract
The chain system is the highest-leverage feature in fsuite. Two tools piped together can answer questions in one command that would take a dozen raw filesystem calls to answer blindly. This section covers the mechanics, the validated patterns, and — critically — what not to chain.
Every chainable tool communicates via one of two machine-readable output modes:
| Flag | Output | Role |
|---|---|---|
-o paths |
One absolute file path per line | Pipe currency — feeds the next tool |
-o json |
Structured JSON | Terminal output for programmatic decisions |
The rule is simple: producers emit paths, consumers read paths from stdin. Break this contract and the pipe silently produces garbage.
| Tool | Flag | What it produces |
|---|---|---|
fsearch |
-o paths |
File paths matching a glob or name pattern |
fcontent |
-o paths |
File paths containing a literal string |
Both tools support up to 2000 files in a single run.
| Tool | stdin behavior | Notes |
|---|---|---|
fcontent |
Reads file paths, searches inside them | stdin_files mode |
fmap |
Reads file paths, maps symbols in each | stdin_files mode |
fcontent is both a producer and a consumer. This is what makes deep narrowing chains possible.
These tools take paths or identifiers as positional arguments. They are terminal nodes in a workflow, not pipe links.
| Tool | Why it cannot be piped from | How to use it |
|---|---|---|
fread |
Outputs file content, not file paths | Call it after fmap identifies the exact symbol or line range |
fedit |
--stdin reads payload text, not a file list |
Call it after fread confirms the seam |
ftree |
Outputs a tree visualization | Use it first, not mid-chain |
fprobe |
Outputs JSON or text report on a single binary | Standalone recon |
fcase |
Outputs investigation state | Standalone continuity ledger |
freplay |
Outputs derivation history | Standalone tracker |
fmetrics |
Reads telemetry database | Standalone analytics |
These are the validated patterns, tested end-to-end on 2026-03-29.
Purpose: establish territory on a new or unfamiliar codebase.
ftree --snapshot -o json /projectftree with --snapshot walks the tree once and caches it. Use this as the first move on any session. It is not a pipe node — it is the orientation step that makes every subsequent chain cheaper.
# MCP equivalent
ftree(path: "/project", snapshot: true, output: "json")Purpose: find every file that touches a concept, then get the symbol map.
fcontent -o paths "authenticate" src | fmap -o jsonThis answers: "which files mention this token, and what functions/classes live in them?" The output is a symbol map you can scan without opening any file.
# MCP equivalent (sequential calls)
fcontent(query: "authenticate", path: "src", output: "paths")
# -> returns file list
fmap(path: "<each file from results>", output: "json")Purpose: narrow by file type first, then by content.
fsearch -o paths '*.py' src | fcontent -o paths "def authenticate"The fsearch pass keeps fcontent from scanning unrelated file types. On a polyglot repo this can cut the candidate set by 80%.
Purpose: get from a concept to an exact edit target in the fewest steps.
# Step 1 — find the right files
fsearch -o paths '*.rs' src | fcontent -o paths "pub fn"
# Step 2 — map what's in them
fsearch -o paths '*.rs' src | fcontent -o paths "pub fn" | fmap -o json
# Step 3 — read the exact symbol
fread src/auth.rs --symbol authenticate
# Step 4 — edit the seam
fedit src/auth.rs --function authenticate --replace "return true" --with "return verify(token)"Steps 1-2 are a single compound pipe. Steps 3-4 are individual tool calls operating on the exact coordinates the pipe produced.
Purpose: new codebase, understand all public API symbols across every source file.
ftree --snapshot -o json /project
fsearch -o paths '*.rs' src | fcontent -o paths "pub fn" | fmap -o json
fread src/auth.rs --symbol authenticate
fcase init auth-fix --goal "Fix authenticate bypass"
fedit src/auth.rs --function authenticate --replace "return true" --with "return verify(token)"
fmetrics statsTested against fsuite itself: this pipeline produced 1956 symbols from the repo in one pass.
Purpose: drill through multiple content filters before mapping.
# Narrow -> narrow -> map
fsearch -o paths '*.ts' src | fcontent -o paths "export" | fcontent -o paths "async" | fmap -o jsonThree filters, one final symbol map. Each fcontent -o paths stage shrinks the file list. The final fmap only runs on files that passed all three gates.
Purpose: find which JSON files in a project reference a specific key.
fsearch -o paths '*.json' . | fcontent "api_key"No -o paths on the terminal fcontent — you want the match lines, not more paths.
Purpose: see what test files exist and what they test.
fsearch -o paths 'test_*.py' tests | fmap -o jsonBinary recon workflow (standalone, not a pipe chain):
fprobe scan binary --pattern "renderTool" --context 300
fprobe window binary --offset 112730723 --before 50 --after 200
fprobe strings binary --filter "diffAdded"In MCP mode (Claude Code, Codex), there are no Unix pipes. The agent reconstructs the equivalent chain by calling tools sequentially and passing results forward explicitly.
CLI pipe: fsearch -o paths '*.py' | fcontent -o paths "def " | fmap -o json
MCP sequence: fsearch(query: "*.py")
-> returns ["/src/auth.py", "/src/user.py", ...]
fcontent(query: "def ", path: "/src/auth.py")
fcontent(query: "def ", path: "/src/user.py")
-> returns filtered list
fmap(path: "/src/auth.py")
fmap(path: "/src/user.py")
-> returns symbol maps
The MCP adapter always returns structured JSON internally, so agents receive clean data without parsing ANSI output.
Key difference: the CLI pipe is O(1) tool calls. The MCP sequence is O(n) calls proportional to the candidate file count. For large repos, run fsearch first to reduce n before calling fcontent per file.
These two-tool combinations cover the majority of agent use cases.
| Pair | Use case |
|---|---|
fsearch -> fread |
Find file by name, read exact symbol or line range |
fmap + fread |
Map symbols first, then read the one that matches |
fcontent -> fedit |
Confirm text location, then edit the seam |
fprobe -> fread |
Find offset in binary, then read source context |
fsearch -> fmap |
Find files by type, get their full symbol inventory |
fcontent -> fmap |
Find files by content, understand their structure |
The pattern in every pair: one tool establishes coordinates, the next tool acts on them. Never act without coordinates.
These chains look plausible but break the pipe contract. Each one is a common mistake.
| Anti-pattern | Why it fails | What to do instead |
|---|---|---|
fread | anything |
fread outputs file content, not file paths. The next tool receives raw text and silently ignores or misparses it. |
Use fread as the terminal step. Get coordinates from fmap first. |
fedit | anything |
fedit outputs a diff or confirmation message, not paths. |
fedit is always the final step. Chain ends here. |
ftree | fcontent |
ftree outputs a formatted tree visualization. fcontent expects one path per line. |
Use fsearch to produce paths for fcontent. ftree is for human orientation only. |
fmap | fread |
fmap outputs a JSON symbol map or pretty-printed list, not file paths. |
After fmap, read the exact symbol with fread --symbol using the path from fmap's output. |
fprobe | anything |
fprobe outputs binary analysis in JSON or text. Not a path emitter. |
fprobe findings -> use the offset/path manually with fread. |
fcase | anything |
fcase outputs investigation state JSON. Not a path emitter. |
fcase is a side-channel bookkeeping tool, not a pipeline node. |
fcontent (no -o paths) | fmap |
Without -o paths, fcontent outputs match lines with context, not bare paths. |
Always add -o paths to intermediate fcontent calls. |
Rule of thumb: if a tool's output is human-readable prose, colored text, or structured JSON describing file contents — it is not a producer. Only fsearch -o paths and fcontent -o paths are valid producers.
fsuite-mcp is a thin, stateless Node.js dispatcher that wraps the fsuite bash tools as native MCP tool calls. It does no work itself. Every tool call resolves to an execFile invocation against the corresponding bash binary — arguments are passed as an array (never shell-interpolated), and the process exits cleanly after each call.
The adapter's contract:
- Architecture: stateless dispatcher — no session, no cache, no shared state between calls
- Security: uses
execFile, notexec— shell injection is structurally impossible - Rendering: pretty output is produced by the bash tools, forwarded verbatim to the MCP client
- SDK:
@modelcontextprotocol/sdkv1.28.0,McpServer+registerToolAPI
Install dependencies:
cd /path/to/fsuite/mcp
npm installDependencies: @modelcontextprotocol/sdk, zod, highlight.js.
Register in Claude Code (~/.claude/settings.json or project-level .claude/settings.json):
{
"mcpServers": {
"fsuite": {
"command": "node",
"args": ["/path/to/fsuite/mcp/index.js"],
"type": "stdio"
}
}
}After adding the config, restart Claude Code. The tools appear as mcp__fsuite__fread, mcp__fsuite__fedit, etc. in the tool list.
Verify registration:
# In a Claude Code session, the tools should appear as:
# mcp__fsuite__ftree, mcp__fsuite__fmap, mcp__fsuite__fread,
# mcp__fsuite__fcontent, mcp__fsuite__fsearch, mcp__fsuite__fedit,
# mcp__fsuite__fwrite, mcp__fsuite__fcase, mcp__fsuite__fprobe,
# mcp__fsuite__fmetrics, mcp__fsuite__fsAll twelve tools with their functional roles:
| Tool | Category | Description |
|---|---|---|
fs |
Meta | Unified search orchestrator — load the mental model, auto-route queries, and return ranked results |
ftree |
Scout | Directory tree with snapshot mode and JSON output. The first call on any new project. |
fsearch |
Search | Find files by glob or name pattern. Produces -o paths compatible output. |
fcontent |
Search | Search file contents for a literal string. Can consume a file list from stdin in CLI mode; in MCP mode, accepts a path or file list directly and forces fixed-string matching by default. |
fmap |
Structure | Extract symbol maps (functions, classes, imports, exports) from source files. Core of the investigation chain. |
fread |
Read | Budgeted file reading with symbol resolution, line ranges, and context windows around patterns. The primary file reading tool. |
fedit |
Mutation | Surgical text editing — replace by function name, exact string, or line range. Emits a diff on completion. |
fwrite |
Mutation | Write or overwrite a file. Use when creating new files or when full-file replacement is cleaner than surgical edit. |
fcase |
Knowledge | Investigation ledger — init a case, record steps, preserve context across context window boundaries. |
freplay |
Knowledge | Derivation tracker — record and replay the reasoning chain for a code change. |
fprobe |
Diagnostic | Binary analysis — scan, string extraction, hex window, pattern search in compiled binaries. |
fmetrics |
Diagnostic | Usage telemetry — import, stats, and cost prediction for the fsuite tool suite itself. |
The MCP adapter produces terminal-quality output inside Claude Code's tool output pane. This is not cosmetic — it is a deliberate design choice to make tool output scannable without reading every line.
Syntax highlighting is applied via highlight.js with a full Monokai color mapping. Language is auto-detected from the file extension. The ANSI escape sequences use truecolor (24-bit RGB) matching Claude Code's exact rendering engine.
Monokai scope to ANSI color mapping (direct from source):
| Scope | RGB | Appears as |
|---|---|---|
keyword / operator |
249, 38, 114 |
Monokai pink |
storage / hljs-type |
102, 217, 239 |
Monokai cyan |
built_in / title / attr |
166, 226, 46 |
Monokai green |
string / regexp |
230, 219, 116 |
Monokai yellow |
literal / number / symbol |
190, 132, 255 |
Monokai purple |
params |
253, 151, 31 |
Monokai orange |
comment / meta |
117, 113, 94 |
Monokai grey |
variable / property |
255, 255, 255 |
White |
Diff rendering uses dedicated backgrounds:
| Diff line type | Background RGB | Gutter fg RGB |
|---|---|---|
| Added line | 2, 40, 0 |
80, 200, 80 |
| Removed line | 61, 1, 0 |
220, 90, 90 |
The diff renderer is pixel-matched to Claude Code's native diff view — the result is indistinguishable from the built-in Edit tool's output.
Each tool gets a distinct 256-color ANSI code for its header in the tool output pane. The palette follows a semantic grouping:
| Color | ANSI 256 | Tools | Semantic role |
|---|---|---|---|
| Neon green | 46 |
fread, ftree, freplay |
Read / scout — safe, non-mutating |
| Orange | 208 |
fedit, fwrite |
Mutation — write operations |
| Royal blue | 27 |
fcontent, fsearch, fs |
Search — content and structure discovery |
| Dark violet | 129 |
fmap, fcase |
Structure / knowledge — symbol maps and case state |
| Pure red | 196 |
fprobe, fmetrics |
Diagnostic / recon — binary and telemetry analysis |
The color is embedded as an ANSI escape in the tool's annotations.title field. The binary patch (fpatch-claude-mcp) enables Claude Code's renderer to pass the title through verbatim rather than stripping ANSI.
By default, mcp/index.js resolves tool binaries from the source tree — the directory one level up from mcp/. This means edits to source bash scripts take effect on the next MCP server restart without reinstallation.
# Default (source tree mode — dev workflow)
node mcp/index.js
# Force PATH resolution (production / installed binaries)
FSUITE_USE_PATH=1 node mcp/index.jsconst FSUITE_SRC_DIR = process.env.FSUITE_USE_PATH
? null
: join(dirname(new URL(import.meta.url).pathname), "..");
function resolveTool(name) {
if (FSUITE_SRC_DIR) return join(FSUITE_SRC_DIR, name);
return name; // resolve from PATH
}When FSUITE_USE_PATH is unset (default), FSUITE_SRC_DIR is the repo root (parent of mcp/). resolveTool("fread") returns /path/to/fsuite/fread — the source file, not the installed binary.
When FSUITE_USE_PATH=1, FSUITE_SRC_DIR is null, and resolveTool returns the bare name, letting execFile find it on $PATH.
# 1. Edit a source tool
vim /path/to/fsuite/fread
# 2. Restart the MCP server
# In Claude Code: /mcp restart fsuite
# Or kill and relaunch the node process
# 3. Next tool call picks up the change immediately
# No npm install, no build step requiredThe only time you need FSUITE_USE_PATH=1 is when running the MCP server against a globally installed fsuite where the source tree is not authoritative (e.g., CI or a shared environment).
fpatch-claude-mcp patches the Claude Code Electron binary to clean up how MCP tool names render in the tool output header.
What it patches:
The binary contains a userFacingName() function that formats MCP tool names as "fsuite - fread (MCP)" in plain white. The patch rewrites this to emit just "fread" in the configured color (default: bold cyan). It uses fprobe to locate the relevant byte offset dynamically, so it survives minor Claude Code version updates without a hardcoded offset.
Safety:
- Creates a
.bakbackup before writing any bytes. - Idempotent: running twice does not corrupt the binary.
--dry-runshows what would be patched without writing.--restorereverts from the.bakbackup.
fpatch-claude-mcp # Apply patch (bold cyan, latest binary)
fpatch-claude-mcp --dry-run # Preview only
fpatch-claude-mcp --color green # Use a different color
fpatch-claude-mcp --binary PATH # Target a specific binary version
fpatch-claude-mcp --restore # Revert to .bakAvailable colors: cyan, green, yellow, magenta, bold_cyan, bold_green.
Status note: fpatch-claude-mcp is the original bash-based patcher. Current binary work — including the truecolor title embedding used by the tool palette — is done via manual Python patchers that operate at the renderer level rather than the userFacingName function. fpatch-claude-mcp remains available for the userFacingName patch specifically, but should be treated as a maintenance-mode tool.
fsuite works on any normal text file for fsearch, fcontent, fread, and plain fedit.
The table below tracks the language-aware structural layer in fmap and the symbol-scoped edit path that depends on it.
| Language / ecosystem | fmap support |
Symbol-scoped fedit |
Notes |
|---|---|---|---|
| Python | Yes | Yes | Core AI / automation / backend |
| JavaScript | Yes | Yes | Web, Node, agent tooling |
| TypeScript | Yes | Yes | Web, agents, infrastructure tooling |
| Kotlin | Yes | Yes | Android / Kotlin-first mobile repos |
| Swift | Yes | Yes | Apple-native app analysis |
| Rust | Yes | Yes | Systems / infra / performance |
| Go | Yes | Yes | Services / CLIs / platform code |
| Java | Yes | Yes | Enterprise / Android-adjacent |
| C | Yes | Yes | Systems / embedded |
| C++ | Yes | Yes | Native / performance-heavy code |
| Ruby | Yes | Yes | Legacy web / scripting |
| Lua | Yes | Yes | Embedding / config / game tooling |
| PHP | Yes | Yes | Large real-world web footprint |
| Bash / Shell | Yes | Yes | DevOps / scripts / automation |
| Dockerfile | Yes | Yes | Container workflows |
| Makefile | Yes | Yes | Build systems |
| YAML | Yes | Yes | CI / config / infra manifests |
| Markdown | Yes | — | Headings, fences, frontmatter, links |
| Language / ecosystem | Why it matters | Priority | Notes |
|---|---|---|---|
| C# | Large .NET / Unity / enterprise footprint | P0 | Strong cross-industry demand |
| Dart / Flutter | Cross-platform mobile codebases | P1 | Strong next mobile follow-up |
| HCL / Terraform | Infra / platform repo coverage | P1 | High-value for agent audits |
| Objective-C | Legacy Apple codebases | P1 | Useful after Swift |
| Mojo | Emerging AI / GPU language | P2 | Strategic watchlist |
| Bundle | What it should cover |
|---|---|
| Apple-lite | Swift, Package.swift, Info.plist, later Objective-C |
| Android-lite | Kotlin, Gradle, Gradle Kotlin DSL, AndroidManifest.xml, resource/layout XML; narrow manifest/layout reconnaissance now lands in fmap |
| Python AI | Python plus better real-world recipes for PyTorch, JAX, NumPy, PyTensor |
| Infra | HCL / Terraform, Docker, CI config surfaces |
| Agent tooling | TypeScript / Python patterns for MCP, tool routing, workflow harnesses |
Open an issue or discussion with:
- the language or ecosystem you want
- 1-3 public repos we should test against
- the symbol types that matter most (
function,class,type,import,export,constant) - whether you care more about:
- reading / mapping
- editing
- mobile app analysis
- AI / ML codebases
- infra / DevOps code
All fsuite CLI tools share the same three output modes: pretty (default, human-readable), paths (one path per line for piping), json (structured, always machine-parseable). Pass -o json to any tool.
{
"tool": "fcase",
"version": "2.3.0",
"case": {
"slug": "auth-seam",
"goal": "Trace authenticate flow",
"next_move": "Patch denial branch after reviewing symbol map"
},
"targets": [],
"evidence": [],
"hypotheses": [],
"recent_events": []
}{
"tool": "fsearch",
"version": "2.3.0",
"pattern": "*token*",
"name_glob": "*token*",
"path": "/home/user",
"backend": "fd",
"search_type": "file",
"match_mode": "name",
"total_found": 12,
"shown": 12,
"truncated": false,
"count_mode": "exact",
"has_more": false,
"results": ["/home/user/.config/token.json", "..."],
"hits": [
{
"path": "/home/user/.config/token.json",
"kind": "file",
"matched_on": "name",
"next_hint": { "tool": "fread", "args": { "path": "/home/user/.config/token.json" } }
}
],
"next_hint": null
}{
"tool": "fcontent",
"version": "2.3.0",
"query": "ERROR",
"mode": "directory",
"path": "/var/log",
"total_matched_files": 3,
"shown_matches": 47,
"matches": ["file.log:12:ERROR something failed", "..."],
"matched_files": ["file.log", "..."]
}{
"tool": "ftree",
"version": "2.3.0",
"mode": "tree",
"backend": "tree",
"path": "/project",
"depth": 3,
"filelimit": 80,
"ignored": "node_modules|.git|venv|...",
"total_dirs": 12,
"total_files": 47,
"total_lines": 89,
"shown_lines": 89,
"truncated": false,
"tree_json": [ "... native tree -J output ..." ]
}{
"tool": "ftree",
"version": "2.3.0",
"mode": "recon",
"backend": "find/du/stat",
"path": "/project",
"recon_depth": 1,
"total_entries": 8,
"visible": 5,
"excluded": 3,
"entries": [
{"name": "src", "type": "directory", "items_total": 234, "size_bytes": 1258291, "size_human": "1.2M", "excluded": false},
{"name": "package.json", "type": "file", "size_bytes": 2355, "size_human": "2.3K", "excluded": false},
{"name": "node_modules", "type": "directory", "items_total": 1847, "size_bytes": -1, "size_human": "?", "reason": "excluded", "excluded": true}
]
}Base fields are always present. query and matches are emitted when --name is active.
{
"tool": "fmap",
"version": "2.3.0",
"mode": "single_file",
"path": "/project/src/auth.py",
"total_files_scanned": 1,
"total_files_with_symbols": 1,
"total_symbols": 12,
"shown_symbols": 12,
"truncated": false,
"query": "authenticate",
"languages": {"python": 1},
"matches": [
{
"path": "/project/src/auth.py",
"symbol": "authenticate",
"symbol_type": "function",
"line_start": 14,
"line_end": 27,
"match_kind": "exact",
"rank": 1
}
],
"files": [
{
"path": "auth.py",
"language": "python",
"symbol_count": 12,
"symbols": [
{"line": 14, "type": "function", "indent": 0, "text": "def authenticate(user, token):"},
{"line": 47, "type": "class", "indent": 0, "text": "class AuthError(Exception):"}
]
}
]
}Base fields are always present. symbol_resolution and candidates are emitted when --symbol is active.
{
"tool": "fread",
"version": "2.3.0",
"mode": "symbol",
"truncated": false,
"truncation_reason": "none",
"token_estimate": 148,
"token_estimator": "bytes_div_3_conservative",
"bytes_emitted": 444,
"lines_emitted": 18,
"max_lines": 200,
"max_bytes": 50000,
"token_budget": 0,
"next_hint": null,
"chunks": [
{
"path": "/project/src/auth.py",
"start_line": 120,
"end_line": 137,
"match_line": 125,
"content": ["120 ...", "125 def authenticate(user, token):", "137 ..."]
}
],
"files": [
{
"path": "/project/src/auth.py",
"language": "python",
"binary": false,
"binary_state": "false",
"file_size_bytes": 8124,
"file_total_lines": 244,
"status": "read"
}
],
"symbol_resolution": {
"query": "authenticate",
"symbol": "authenticate",
"symbol_type": "function",
"path": "/project/src/auth.py",
"line_start": 120,
"line_end": 137
},
"candidates": [],
"warnings": [],
"errors": []
}{
"tool": "fedit",
"version": "2.3.0",
"mode": "lines",
"file": "/project/src/auth.ts",
"line_start": 120,
"line_end": 135,
"lines_replaced": 16,
"applied": true,
"diff": "@@ -120,16 +120,1 @@\n- return false;\n+ return deny(reason);\n"
}{
"tool": "fmetrics",
"version": "2.3.0",
"subcommand": "stats",
"total_runs": 24,
"db_path": "/home/user/.fsuite/telemetry.db",
"db_size_bytes": 28672,
"oldest_run": "2026-03-08T20:20:13Z",
"newest_run": "2026-03-08T20:20:14Z",
"tools": [
{"name": "ftree", "runs": 8, "avg_ms": 6, "min_ms": 6, "max_ms": 7, "success_rate": 100.0}
],
"top_projects": [
{"name": "myproject", "runs": 12}
]
}{
"tool": "fprobe",
"version": "2.3.0",
"subcommand": "scan",
"file": "./claude-binary",
"pattern": "userFacingName",
"matches": [
{
"offset": 112202147,
"context_before": "...{\"name\":\"",
"match": "userFacingName",
"context_after": "\",\"description\":\"..."
}
],
"match_count": 1
}{
"tool": "fprobe",
"version": "2.3.0",
"mode": "strings",
"path": "/path/to/binary",
"min_len": 4,
"filter": null,
"total_found": 412,
"strings": [
{ "offset": "0x1a30", "value": "https://api.example.com/v1" },
{ "offset": "0x1a58", "value": "Authorization: Bearer" }
],
"truncated": false
}{
"tool": "fprobe",
"version": "2.3.0",
"mode": "window",
"path": "/path/to/binary",
"offset": 256,
"size": 64,
"hex": "4d5a9000 03000000 04000000 ffff0000",
"ascii": "MZ..............",
"truncated": false
}{
"tool": "fs",
"version": "2.3.0",
"query": "authenticate",
"scope": "/project",
"results": [
{
"rank": 1,
"type": "symbol",
"path": "/project/src/auth.py",
"symbol": "authenticate",
"symbol_type": "function",
"line": 42,
"confidence": 0.97
},
{
"rank": 2,
"type": "content",
"path": "/project/tests/test_auth.py",
"line": 18,
"match": "def test_authenticate_rejects_expired():",
"confidence": 0.81
}
],
"total": 2,
"tools_used": ["fmap", "fcontent"],
"truncated": false
}{
"tool": "freplay",
"version": "2.3.0",
"case": "auth-seam",
"steps": [
{
"step": 1,
"ts": "2026-03-29T14:02:11Z",
"cmd": "fread /project/src/auth.py --around 'def authenticate' -A 20",
"note": "Located denial branch at line 71"
},
{
"step": 2,
"ts": "2026-03-29T14:04:33Z",
"cmd": "fedit /project/src/auth.py --lines 71:73 --with ' return deny()\\n' --apply",
"note": "Patched denial path"
}
],
"total_steps": 2
}These tools are designed to be called programmatically by AI agents, automation scripts, or CI pipelines.
Recommended drill-down workflow for agents:
# Step 1: Auto-route opening query
fs "authenticate" /project
# Step 2: Scout — what's in this project?
ftree --recon -o json /project
# -> per-dir item counts and sizes, agent picks targets programmatically
# Step 3: Structure — show me the tree
ftree /project
# -> depth-3 tree, 200-line cap, noise excluded
# Step 4: Zoom — drill into interesting part
ftree -L 5 /project/src
# -> deeper view of just src/
# Step 5: Find candidate files (deterministic, structured)
fsearch --output json '*.py' /project
# Step 6: Map code structure (structural skeleton)
fsearch --output paths '*.py' /project | fmap --output json
# -> functions, classes, imports for each file
# Step 7: Search inside candidates (structured results)
fsearch --output paths '*.py' /project | fcontent --output json "import torch"
# Step 8: Read the exact code neighborhood
fread --output json /project/src/auth.py --around "def authenticate" -B 5 -A 20
# Step 9: Measure and plan the next pass
fmetrics import && fmetrics stats -o jsonThe full pipeline:
fs -> ftree --snapshot -> fsearch -o paths -> fmap -o json -> fread -o json -> fcontent -o json -> fmetrics
Auto Scout Find Map Read Search Measure
Why this matters:
--output jsongives structured data an agent can parse without regex--output pathsproduces clean line-delimited output for piping- No interactive prompts in headless mode (prompts only trigger when pattern is missing)
- Exit codes follow convention:
0= success,2= usage error,3= missing dependency - Errors go to stderr, results go to stdout
Copy-paste ready. Every command runs headless (no prompts, no TTY needed) unless marked Interactive.
| Call | What it does |
|---|---|
fs "authenticate" |
Unified search — scouts structure, finds files, maps symbols, returns ranked results in one call |
fs "error handler" --scope '**/*.py' |
Narrow the search surface to a file-type glob |
fs "class AuthHandler" --intent symbol |
Force symbol-name intent over auto-detected content |
fs "TODO" --intent content |
Force in-file text content intent |
fs "*.log" --intent file |
Force file-name pattern intent |
fs "def authenticate" -o json |
Structured JSON with ranked hits, tool breakdown, and confidence |
| Command | What it does |
|---|---|
fprobe strings /path/to/binary |
Extract printable strings from a binary file |
fprobe strings /path/to/binary --min-len 8 |
Only strings of 8+ characters |
fprobe strings /path/to/binary --filter "http" |
Strings containing a literal substring |
fprobe scan /path/to/binary --pattern "userFacingName" |
Find a literal byte pattern; returns byte offset + context |
fprobe scan /path/to/binary --pattern "renderTool" --context 200 -o json |
JSON with offset, hex context window, and surrounding bytes |
fprobe window /path/to/binary --offset 0x100 --after 256 |
Read 256 bytes after offset |
fprobe window /path/to/binary --offset 0x100 --before 64 --after 256 |
Read bytes before and after offset |
fprobe window /path/to/binary --offset 0x100 --after 256 --decode hex |
Hex dump of the window |
fprobe window /path/to/binary --offset 0x100 --after 256 -o json |
JSON with hex, printable text, and offset metadata |
fprobe --self-check |
Verify file, strings, xxd/od availability |
fprobe --version |
Print version |
| Command | What it does |
|---|---|
freplay record auth-seam --purpose "Traced denial branch" -- fread /project/src/auth.py --around 'def authenticate' |
Record a derivation step: case slug, optional purpose, then -- separator, then the fsuite command |
freplay record auth-seam -- fcontent -o paths "authenticate" src |
Record a content search step with no purpose annotation |
freplay show auth-seam |
Show the full replay chain for a case in order |
freplay show auth-seam -o json |
Machine-readable replay chain with timestamps |
freplay list |
List all cases that have replay chains |
freplay list -o json |
JSON list of case slugs with step counts |
freplay --version |
Print version |
| Command | What it does |
|---|---|
fsearch '*.log' /var/log |
Find all .log files under /var/log (pretty output) |
fsearch log /var/log |
Bare word log auto-expands to *.log |
fsearch .log /var/log |
Dotted .log also auto-expands to *.log |
fsearch 'upscale*' /home/user |
Files whose names start with upscale |
fsearch '*progress*' /home/user |
Files containing progress anywhere in the name |
fsearch '*error' /var/log |
Files whose names end with error |
fsearch --output paths '*.py' /project |
One path per line — ideal for piping |
fsearch --output json '*.conf' /etc |
Structured JSON with total_found, results[], backend |
fsearch --include 'src' --exclude '*test*' '*.py' /project |
Scope to source, skip tests |
fsearch --exclude 'node_modules' --exclude '.git' '*.log' /repo |
Skip noisy dirs |
fsearch --max 10 '*.py' /project |
Limit to first 10 results |
fsearch --backend fd '*.rs' /src |
Force fd backend (faster, if installed) |
fsearch --self-check |
Show which backends are available |
fsearch -q '*.py' /project |
Quiet mode — exit code only |
fsearch -i |
Interactive — prompts for pattern and path |
| Command | What it does |
|---|---|
fcontent "ERROR" /var/log |
Search for ERROR inside all files under /var/log |
fcontent "TODO" /project |
Find every TODO in a project tree |
fcontent --output paths "ERROR" /var/log |
Print only file paths that matched (one per line) |
fcontent --output json "ERROR" /var/log |
Structured JSON with matches[], matched_files[], counts |
fcontent -m 20 "debug" /project |
Cap output to 20 match lines |
fcontent -n 50 "debug" /project |
Cap to 50 files searched |
fcontent --rg-args "-i" "error" /var/log |
Case-insensitive search |
fcontent --rg-args "--hidden" "secret" ~ |
Include hidden/dotfiles |
fcontent --rg-args "-w" "main" /project |
Whole-word match only |
fcontent --self-check |
Verify rg is installed |
fcontent -q "TODO" /project |
Quiet mode — exit code only (0=found, 1=not found) |
| Command | What it does |
|---|---|
ftree /project |
Tree view, depth 3, default excludes, 200-line cap |
ftree --recon /project |
Recon: per-dir item counts and sizes |
ftree -L5 /project/src |
Deeper tree (depth 5) of a subdirectory |
ftree -o json /project |
Structured JSON tree with metadata envelope |
ftree -o paths /project |
Flat file list, one per line |
ftree --recon -o json /project |
Recon JSON: agent-parseable per-dir inventory |
ftree --snapshot /project |
Recon inventory + tree excerpt in one output |
ftree --snapshot -o json /project |
Snapshot JSON: combined recon + tree for agents |
ftree --recon --hide-excluded /project |
Clean recon, no excluded-dir summaries |
ftree --include .git /project |
Show .git even though it's in the default ignore list |
ftree -I 'docs|*.md' /project |
Exclude additional patterns (appended to defaults) |
ftree --no-default-ignore /project |
Disable built-in ignore list entirely |
ftree --snapshot --no-lines -o json /project |
Snapshot JSON without tree.lines array |
ftree --self-check |
Verify tree is installed, check --gitignore support |
| Command | What it does |
|---|---|
fmap /project |
Map all source files under /project (pretty output) |
fmap /project/src/auth.py |
Map a single file |
fmap -o json /project |
JSON output with symbol metadata |
fmap --name authenticate -o json /project |
Rank/filter by symbol name matches |
fmap -o paths /project |
File paths that contain symbols |
fmap -t function /project |
Show only function definitions |
fmap -t class /project |
Show only class definitions |
fmap --no-imports /project |
Skip import lines |
fmap -L bash /project/scripts |
Force language to Bash |
fmap -m 50 /project |
Cap shown symbols to 50 |
fmap -n 100 /project |
Cap files processed to 100 |
fmap --self-check |
Verify grep is available |
fsearch -o paths '*.py' /project | fmap -o json |
Pipeline: find then map |
| Command | What it does |
|---|---|
fread /project/src/auth.py |
Read a file with default caps |
fread /project/src/auth.py -r 120:220 |
Read a precise inclusive line range |
fread /project/src/auth.py --head 50 |
Read the first 50 lines |
fread /project/src/auth.py --tail 40 |
Read the last 40 lines |
fread /project/src/auth.py --around-line 150 -B 5 -A 15 |
Read context around line 150 |
fread /project/src/auth.py --around "def authenticate" -B 5 -A 20 |
Read around the first literal pattern match |
fread /project/src/auth.py --symbol authenticate -o json |
Read one exact symbol block from a file |
fread /project/src --symbol authenticate -o json |
Resolve and read one exact symbol from a directory scope |
fread /project/src/auth.py --all-matches --around "TODO" |
Read around every match until caps are hit |
fread /project/src/auth.py --max-lines 80 --max-bytes 12000 |
Enforce hard output budgets |
fread /project/src/auth.py --token-budget 2000 -o json |
Cap by estimated token cost |
fsearch -o paths '*.py' /project | fread --from-stdin --stdin-format=paths --max-files 5 |
Read first 5 files from a pipeline |
git diff | fread --from-stdin --stdin-format=unified-diff -B 3 -A 10 |
Read context around changed hunks |
fread --self-check |
Verify dependencies (sed, awk, grep, wc, od, perl) |
| Command | What it does |
|---|---|
fcase init auth-seam --goal "Trace authenticate flow" |
Create a new investigation case |
fcase list -o json |
List known cases for automation |
fcase status auth-seam -o json |
Read current case state |
fcase note auth-seam --body "Focused on denial branch" |
Append a note to the case history |
fcase target add auth-seam --path /project/src/auth.py --symbol authenticate --symbol-type function --state active |
Mark a file/symbol seam as active |
fcase evidence auth-seam --tool fread --path /project/src/auth.py --lines 40:72 --summary "..." --body "..." |
Store structured proof from a read |
fcase hypothesis add auth-seam --body "Cleanup bug in tool cancellation" |
Track an open hypothesis |
fcase reject auth-seam --hypothesis-id 1 --reason "Process survives normal completion too" |
Reject a hypothesis explicitly |
fmap -o json /project/src/auth.py | fcase target import auth-seam |
Import mapped symbols as structured targets |
fread -o json /project/src/auth.py --around "def authenticate" -A 20 | fcase evidence import auth-seam |
Import bounded reads as structured evidence |
fcase next auth-seam --body "Patch denial branch after reviewing symbol map" |
Update the next best move |
fcase handoff auth-seam -o json |
Emit a concise handoff packet for the next agent |
fcase export auth-seam -o json |
Export the full portable case envelope |
fcase --version |
Print version |
| Command | What it does |
|---|---|
fedit /project/src/auth.py --replace 'old' --with 'new' |
Preview an exact replacement (dry-run by default) |
fedit /project/src/auth.py --replace 'old' --with 'new' --apply |
Apply the exact replacement |
fedit /project/src/auth.py --lines 71:73 --with " return deny()\n" |
Replace a specific line range with new content |
fedit /project/src/auth.py --after 'def authenticate(user):' --content-file patch.txt |
Preview an insertion after an anchor |
fedit /project/src/auth.py --before 'return True' --stdin --apply |
Insert payload from stdin before an anchor |
fedit /project/src/auth.py --symbol authenticate --replace 'return False' --with 'return deny()' |
Scope the patch to one fmap-resolved symbol |
fedit /project/src/auth.py --function authenticate --replace 'return False' --with 'return deny()' |
Scope to a function without spelling --symbol-type |
fedit /project/src/auth.py --class AuthHandler --after 'self.ready = False' --with $'\n self.ready = True' |
Target one class block |
printf '/project/a.py\n/project/b.py\n' | fedit --targets-file - --targets-format paths --replace 'x = 1' --with 'x = 2' |
Preview a batch patch from stdin targets |
fedit --targets-file map.json --targets-format fmap-json --function authenticate --replace 'return False' --with 'return deny()' --apply |
Apply symbol-scoped edit across fmap JSON targets |
fedit /project/src/auth.py --expect 'def authenticate' --replace 'old' --with 'new' |
Require expected text before patching |
fedit /project/src/auth.py --expect-sha256 HASH --replace 'old' --with 'new' --apply |
Guard the write with a content hash |
fedit --create /project/src/new_file.py --content-file body.txt --apply |
Create a new file from payload |
fedit --replace-file /project/src/auth.py --content-file rewrite.txt --apply |
Replace an entire file from payload |
fedit --self-check |
Verify perl, diff, mktemp, and SHA tooling |
MCP-only tool. Not available as a CLI binary. Call via the fsuite MCP server. Writes or overwrites a file from a string payload. Use
feditfor surgical patches;fwritefor complete file creation or full rewrites.
| Command | What it does |
|---|---|
fmetrics import |
Import telemetry.jsonl into SQLite |
fmetrics stats |
Show aggregate runtime and reliability dashboard |
fmetrics stats -o json |
Machine-readable stats for automation |
fmetrics history --tool ftree --limit 10 |
Show recent runs for one tool |
fmetrics history --project MyApp |
Filter telemetry by project name |
fmetrics combos --project fsuite |
Show evidence-backed combo patterns for one project |
fmetrics combos --starts-with ftree,fsearch --contains fmap -o json |
Filter combo analytics by prefix and required tool |
fmetrics recommend --after ftree,fsearch --project fsuite |
Suggest the strongest next step from historical telemetry |
fmetrics predict /project |
Estimate runtimes for the target path |
fmetrics predict --tool ftree /project |
Estimate a single tool only |
fmetrics profile |
Show Tier 3 machine profile |
fmetrics clean --days 30 |
Prune old telemetry |
fmetrics --self-check |
Verify sqlite3, python3, and predict helper availability |
| Workflow | Command chain |
|---|---|
| Full scout | ftree --snapshot -o json /project |
| Find + map | fsearch -o paths '*.py' /project | fmap -o json |
| Find + grep | fsearch -o paths '*.log' /var/log | fcontent "ERROR" |
| Find + read | fsearch -o paths '*.py' /project | fread --from-stdin --stdin-format=paths --max-files 5 -o json |
| Map + read | fmap --name authenticate -o json /project | fread --symbol authenticate -o json |
| Binary recon | fprobe scan /binary && fprobe strings /binary --filter "http" |
| Investigation | fcase init seam --goal "..." && fmap -o json /path | fcase target import seam && fcase handoff seam -o json |
| Batch patch | fsearch -o paths '*.py' /project | fedit --targets-file - --targets-format paths --replace 'x' --with 'y' --apply |
| Git diff read | git diff | fread --from-stdin --stdin-format=unified-diff -o json |
fsearch
| Flag | Short | Values | Default |
|---|---|---|---|
--output |
-o |
pretty, paths, json |
pretty |
--backend |
-b |
auto, find, fd |
auto |
--max |
-m |
any integer | 50 |
--include |
-I |
any pattern (repeatable) | — |
--exclude |
-x |
any pattern (repeatable) | — |
--quiet |
-q |
— | off |
--project-name |
— | any string | auto-detected |
--interactive |
-i |
— | off |
--self-check |
— | — | — |
--install-hints |
— | — | — |
Tip: Numeric flags support combined syntax:
-m50is equivalent to-m 50.
fcontent
| Flag | Short | Values | Default |
|---|---|---|---|
--output |
-o |
pretty, paths, json |
pretty |
--max-matches |
-m |
any integer | 200 |
--max-files |
-n |
any integer | 2000 |
--quiet |
-q |
— | off |
--project-name |
— | any string | auto-detected |
--rg-args |
— | quoted string of rg flags | none |
--self-check |
— | — | — |
--install-hints |
— | — | — |
Tip: Numeric flags support combined syntax:
-m50is equivalent to-m 50.
ftree
| Flag | Short | Values | Default |
|---|---|---|---|
--output |
-o |
pretty, paths, json |
pretty |
--depth |
-L |
any integer | 3 |
--max-lines |
-m |
any integer (0 = unlimited) | 200 |
--filelimit |
-F |
any integer | 80 |
--ignore |
-I |
pipe-separated pattern | built-in list |
--no-default-ignore |
— | — | off |
--include |
— | pattern (repeatable) | — |
--recon |
-r |
— | off |
--snapshot |
— | — | off |
--budget |
— | seconds (integer) | 30 |
--no-lines |
— | — | off |
--project-name |
— | any string | auto-detected |
--recon-depth |
— | any integer | 1 (2 in snapshot) |
--hide-excluded |
— | — | off |
--dirs-only |
-d |
— | off |
--sizes |
-s |
— | off |
--quiet |
-q |
— | off |
--gitignore |
— | — | off |
--full-paths |
-f |
— | off |
--self-check |
— | — | — |
--install-hints |
— | — | — |
Tip: Numeric flags support combined syntax:
-L5is equivalent to-L 5.
fmap
| Flag | Short | Values | Default |
|---|---|---|---|
--output |
-o |
pretty, paths, json |
pretty |
--max-symbols |
-m |
any integer | 500 |
--max-files |
-n |
any integer | 500 (dir) / 2000 (stdin) |
--lang |
-L |
language name | auto-detect |
--type |
-t |
function, class, import, type, export, constant |
all |
--no-imports |
— | — | off |
--no-default-ignore |
— | — | off |
--quiet |
-q |
— | off |
--project-name |
— | any string | auto-detected |
--self-check |
— | — | — |
--install-hints |
— | — | — |
Tip: Numeric flags support combined syntax:
-m50is equivalent to-m 50.
fedit
| Flag | Short | Values | Default |
|---|---|---|---|
--output |
-o |
pretty, paths, json |
pretty |
--replace |
— | exact text block | — |
--with |
— | payload text | — |
--after |
— | exact anchor text | — |
--before |
— | exact anchor text | — |
--lines |
— | START:END |
— |
--content-file |
— | readable file path | — |
--stdin |
— | — | off |
--expect |
— | exact text block | — |
--expect-sha256 |
— | SHA-256 hex digest | — |
--symbol |
— | symbol name | — |
--symbol-type |
— | function, class, import, type, export, constant |
any |
--function |
— | function name | — |
--class |
— | class name | — |
--method |
— | method/function name | — |
--import |
— | import text | — |
--constant |
— | constant name | — |
--type |
— | type name | — |
--fmap-json |
— | path to prior fmap -o json output |
auto-run fmap |
--targets-file |
— | path list file or - for stdin |
— |
--targets-format |
— | paths, fmap-json |
— |
--allow-multiple |
— | — | off |
--apply |
— | — | off (CLI) / on (MCP) |
--dry-run |
— | — | on (CLI) |
--create |
— | — | off |
--replace-file |
— | — | off |
--project-name |
— | any string | auto-detected |
--self-check |
— | — | — |
--install-hints |
— | — | — |
fcase
| Subcommand | Key Flags | Description |
|---|---|---|
init <slug> |
--goal, --priority, -o json |
Create a case and open the first session |
list |
-o json |
Show known cases |
status <slug> |
-o json |
Read current case state |
note <slug> |
--body |
Append a note event |
target add <slug> |
--path, --symbol, --symbol-type, --rank, --reason, --state |
Add a typed target |
evidence <slug> |
--tool, --path, --symbol, --lines, --match-line, --summary, --body or --body-file |
Store structured proof |
hypothesis add <slug> |
--body, --confidence |
Add a hypothesis |
hypothesis set <slug> |
--id, --status, --reason, --confidence |
Update a hypothesis state |
reject <slug> |
--target-id or --hypothesis-id, --reason |
Typed alias for ruling out a target or rejecting a hypothesis |
target import <slug> |
--input <path or ->, -o json |
Import structured targets from fmap -o json |
evidence import <slug> |
--input <path or ->, -o json |
Import structured evidence from fread -o json |
next <slug> |
--body, -o json |
Update the next best move |
handoff <slug> |
-o json |
Emit a concise handoff packet |
export <slug> |
-o json |
Export the full case envelope |
fread
| Flag | Short | Values | Default |
|---|---|---|---|
--output |
-o |
pretty, paths, json |
pretty |
--lines |
-r |
START:END |
— |
--head |
— | any integer | — |
--tail |
— | any integer | — |
--around-line |
— | any integer | — |
--around |
— | literal pattern | — |
--all-matches |
— | — | off |
--symbol |
— | exact symbol name | — |
--before |
-B |
any integer | 5 |
--after |
-A |
any integer | 10 |
--max-lines |
— | any integer | 200 |
--max-bytes |
— | any integer | 50000 |
--token-budget |
— | any integer | off |
--max-files |
— | any integer | 10 |
--from-stdin |
— | — | off |
--stdin-format |
— | paths, unified-diff |
required with --from-stdin |
--force-text |
— | — | off |
--quiet |
-q |
— | off |
--project-name |
— | any string | auto-detected |
--self-check |
— | — | — |
--install-hints |
— | — | — |
Tip:
freadtruncation reports anext_hintso agents can continue from the last emitted line without recalculating offsets.
fmetrics
| Subcommand | Key Flags | Description |
|---|---|---|
import |
— | Ingest telemetry.jsonl into SQLite |
stats |
-o json |
Usage dashboard (runtimes, reliability) |
history |
--tool <name>, --limit N (default 20), --project <name> |
Recent runs, filterable |
predict <path> |
--tool <name> |
Estimate runtime for a directory |
clean |
--days N (default 90), --dry-run |
Prune old telemetry |
profile |
— | Show machine profile (Tier 3) |
| Flag | Values | Default |
|---|---|---|
--output / -o |
pretty, json |
pretty |
--self-check |
— | — |
--install-hints |
— | — |
| Tool | Purpose | Install (Debian/Ubuntu) |
|---|---|---|
tree |
Required by ftree (tree mode) |
sudo apt install tree |
fd / fdfind |
Faster filename search backend | sudo apt install fd-find |
rg (ripgrep) |
Required by fcontent |
sudo apt install ripgrep |
perl |
Required by fread for JSON escaping and portable timing fallback |
sudo apt install perl |
sqlite3 |
Required by fcase case storage and fmetrics import/stats/history/clean |
sudo apt install sqlite3 |
python3 |
Required by fmetrics predict, fprobe engine, and fs engine |
sudo apt install python3 |
All tools include built-in guidance:
fsearch --self-check # Check what's available
fsearch --install-hints # Print install commands
fcontent --self-check
fcontent --install-hints
ftree --self-check # Verify tree + gitignore support
ftree --install-hints # Print install command for tree
fmap --self-check # Verify grep is available
fmap --install-hints # Print install command for grep
fread --self-check # Verify sed/awk/grep/wc/od/perl
fread --install-hints # Print install commands for core deps
fcase --help # Show case ledger subcommands
fprobe --self-check # Verify file/strings/xxd/od availability
fedit --self-check # Verify perl, diff, mktemp, and SHA tooling
fmetrics --self-check # Verify sqlite3 + python3 helper chain
fmetrics --install-hintsfsuite collects anonymous performance telemetry to help predict operation times and identify bottlenecks. All data stays local — nothing is transmitted anywhere.
Control telemetry via the FSUITE_TELEMETRY environment variable:
| Tier | Description | Data Collected |
|---|---|---|
0 |
Disabled | None |
1 |
Basic (default) | Duration, items scanned, bytes scanned, exit code |
2 |
Hardware | Tier 1 + CPU temp, disk temp, RAM usage, load average, filesystem type, storage type |
3 |
Full | Tier 2 + machine profile (CPU model, cores, total RAM) |
Tier 2 fields:
filesystem_type: ext4, ntfs, exfat, apfs, nfs, cifs, tmpfs, etc.storage_type: ssd, hdd, nvme, network, removable, tmpfs, etc.
# Disable telemetry
FSUITE_TELEMETRY=0 ftree /project
# Enable hardware metrics
FSUITE_TELEMETRY=2 fcontent "TODO" /project
# Full telemetry with machine profile
FSUITE_TELEMETRY=3 fsearch "*.py" /project- Telemetry log:
~/.fsuite/telemetry.jsonl(append-only JSONL) - SQLite database:
~/.fsuite/telemetry.db(afterfmetrics import) - Machine profile:
~/.fsuite/machine_profile.json(Tier 3 only, regenerated daily)
The fmetrics tool provides analytics on your telemetry data:
# Import telemetry into SQLite for analysis
fmetrics import
# View usage statistics
fmetrics stats
# View recent runs
fmetrics history --tool ftree --limit 10
# View the strongest combo patterns for one project
fmetrics combos --project fsuite
# Ask which next tool usually wins after a prefix
fmetrics recommend --after ftree,fsearch --project fsuite
# Predict runtime for a directory
fmetrics predict /project
# Predict for a specific tool only
fmetrics predict --tool ftree /project
# View machine profile
fmetrics profile
# Clean old data (keep last 30 days)
fmetrics clean --days 30- All data is local — nothing is sent to any server
- Paths are hashed — the actual path
/home/user/secret-projectis stored as a 16-character SHA256 prefix - No file contents — only metadata (counts, sizes, durations)
- Easy to disable — set
FSUITE_TELEMETRY=0globally in your shell config
- No tool stores passwords or credentials
- No tool writes to the filesystem or modifies files (except
fedit/fwritemutations and telemetry in~/.fsuite/) fprobeis read-only by design — no mutations on binary targets- For scanning protected directories, authenticate first with
sudo -vthen run withsudo - No auto-install behavior;
--install-hintsonly prints commands for you to run manually - MCP adapter uses
execFile(notexec) — shell injection is structurally impossible
git clone https://github.com/lliWcWill/fsuite.git
cd fsuite
chmod +x install.sh
./install.sh --userThis installs into ~/.local/bin and ~/.local/share/fsuite.
./install.sh --prefix /opt/fsuitechmod +x fsuite fs fsearch fcontent ftree fmap fread fcase fedit fprobe freplay fmetrics
sudo ln -s "$(pwd)/fsuite" /usr/local/bin/fsuite
sudo ln -s "$(pwd)/fs" /usr/local/bin/fs
sudo ln -s "$(pwd)/fsearch" /usr/local/bin/fsearch
sudo ln -s "$(pwd)/fcontent" /usr/local/bin/fcontent
sudo ln -s "$(pwd)/ftree" /usr/local/bin/ftree
sudo ln -s "$(pwd)/fmap" /usr/local/bin/fmap
sudo ln -s "$(pwd)/fread" /usr/local/bin/fread
sudo ln -s "$(pwd)/fcase" /usr/local/bin/fcase
sudo ln -s "$(pwd)/fedit" /usr/local/bin/fedit
sudo ln -s "$(pwd)/fprobe" /usr/local/bin/fprobe
sudo ln -s "$(pwd)/freplay" /usr/local/bin/freplay
sudo ln -s "$(pwd)/fmetrics" /usr/local/bin/fmetricscd /path/to/fsuite/mcp
npm installThen register in ~/.claude/settings.json:
{
"mcpServers": {
"fsuite": {
"command": "node",
"args": ["/path/to/fsuite/mcp/index.js"],
"type": "stdio"
}
}
}sudo apt install build-essential debhelper dpkg-dev
dpkg-buildpackage -us -uc -b
ls -lh ../fsuite_*_all.debsudo dpkg -i ../fsuite_*_all.deb
ftree --version
fread --version
fedit --version
fprobe --version
fmetrics --versionFor harnesses that read repo instructions, point them at AGENTS.md. For everyone else, the fsuite Help section above is the suite-level contract and each tool's --help remains the detailed interface.
The full test harness lives in tests/. Run via the master runner:
./tests/run_all_tests.sh # all suites
./tests/run_all_tests.sh --suite fprobe # one suite only
./tests/run_all_tests.sh --verbose # per-test output| Suite | File | Tests | Notes |
|---|---|---|---|
fsearch |
test_fsearch.sh |
35 | Pattern normalization, backend fallback, output modes |
fcontent |
test_fcontent.sh |
30 | rg passthrough, pipeline, caps |
ftree |
test_ftree.sh |
48 | Recon, snapshot, JSON schema, depth |
fmap |
test_fmap.sh |
80 | 18 languages, dedup, imports, Markdown |
fread |
test_fread.sh |
42 | Range, head/tail, around, symbol, stdin modes |
fcase |
test_fcase.sh |
25 | Lifecycle, SQLite, import, handoff, busy-timeout |
fedit |
test_fedit.sh |
38 | Dry-run, apply, symbol scope, batch, line-range |
fmetrics |
test_fmetrics.sh |
20 | Import, stats, predict, clean |
fprobe |
test_fprobe.sh |
25 | scan, strings, window, binary formats, self-check |
freplay |
test_freplay.sh |
14 | record, show, list, JSON schema |
fs |
test_fs.sh |
18 | Unified dispatch, ranking, scope, JSON output |
pipelines |
test_pipelines.sh |
22 | End-to-end cross-tool pipeline tests |
mcp_rendering |
test_mcp_rendering.sh |
16 | Pixel-perfect MCP display, dev mode, ANSI |
install |
test_install.sh |
12 | Installer, PATH, self-check, version |
Total: ~425 tests across 14 suites. All suites must pass green before any release.
Exit codes: 0 = all pass, 1 = failures (count printed), 2 = suite not found.
fs unified search orchestrator ships. MCP rendering overhauled for pixel-perfect output. Review fixes applied across two rounds.
New tools:
fs: Unified search orchestrator — dispatchesftree,fsearch,fmap, andfcontentin one call; returns ranked, typed results with confidence scores andtools_usedmetadatafreplay: Derivation replay — records investigation steps (command + note), shows ordered replay chains, supportsrecord|show|listsubcommands and JSON output
Rendering and display:
fedit: Full display overhaul — pixel-perfect unified diff rendering in MCP context; line numbers preserved in preview;--linesmode (--lines 71:73 --with "...") for direct line-range replacement without text anchors- MCP dev mode:
FSUITE_DEV=1env var enables verbose MCP trace output for server-side debugging without affecting client-facing JSON contracts - Pixel-perfect rendering: All tools emit clean ANSI-free output in non-TTY/MCP contexts; pretty mode renders correctly in Claude Code tool output panels
Review fixes (rounds 1 + 2):
fread:--symbolresolution now disambiguates same-name symbols across multiple files when scope is a directoryfedit:--linesrange validation rejects inverted ranges (end < start) with a clear error before any file mutationfprobe:stringsmode defaults to--min-len 4;windowmode validates offset + size does not exceed file sizefcase:next_moveupdate no longer clobbers evidence records added in the same sessionfs: Tie-breaking in ranking now favorssymbolhits overcontenthits at equal confidence
Tests:
test_fs.sh(18 tests),test_freplay.sh(14 tests),test_mcp_rendering.sh(16 tests) added- Full suite: 14 suites, ~425 tests, all green
fprobe binary recon and fedit --lines mode ship. MCP rendering stabilized at 10-tool baseline.
New tools:
fprobe: Binary reconnaissance —strings,scan, andwindowsubcommands; zero new dependencies (file,strings,xxd/od); structured JSON output with binary detection, entropy, section table, and offset-addressed windows- 25-test suite
test_fprobe.shcovering all three subcommands, format variants, and self-check
New features:
fedit --lines: Line-range replacement mode — specify--lines START:END --with "content"to replace an exact line range without needing a text anchor; safe on files with duplicate anchor strings- MCP rendering:
fread,fedit,fprobeoutput renders correctly in Claude Code MCP tool panels — ANSI stripping in non-TTY, consistent line-number formatting, no bleed between tool outputs
Tests:
- Full suite: 10 suites, ~350 tests, all green
Hotfix for fcase SQLite busy-timeout regression introduced in v2.1.1.
- fix(fcase): preserve clean JSON/stdout contracts while enabling SQLite timeout behavior when the sqlite frontend supports it
- fix(fcase): avoid
PRAGMA busy_timeout=5000result leakage in shim-based sqlite environments that printed5000ahead of JSON payloads - fix(fcase): restore healthy
fcasecommand behavior forinit,status,next,note,handoff,export, and structured imports - test: full suite restored green, including
25/25fcasetests and10/10passing test suites overall
fmap adds Markdown as its 18th language. Headings, fences, frontmatter, and links are mapped with CommonMark-compliant rules.
- feat(fmap): Markdown language support (
.md,.markdown,.mdx) - feat(fmap): ATX headings h1-h6, Setext headings with paragraph accumulation, fenced code block suppression, YAML frontmatter suppression, inline links and reference link definitions
- test: 21 markdown-specific regression tests, 122 total tests passing
fmap expands to cover Kotlin and Android-lite (manifest + layout XML). fcase and freplay ship.
New language and ecosystem coverage:
fmap: Kotlin symbol mapping for.ktand.ktsfmap: Android-lite mapping forAndroidManifest.xmlandres/layout/*.xml
New tools:
fcase: Investigation lifecycle ledger — SQLite-backed, typed targets/evidence/hypotheses, explicitfmapandfreadJSON imports, append-only history, handoff packetsfreplay: Derivation replay (initial version —record+showonly;listadded in v2.3.0)
Packaging: suite version unified at 2.1.0
fedit grows into a symbol-first, preflighted batch editor. Suite gains the full scout-map-read-edit loop at project scale.
New editing surface:
fedit: symbol shortcuts--function,--class,--method,--import,--constant,--typefedit: preflighted batch patching via--targets-filewithpathsorfmap-jsontarget formatsfedit: batch JSON envelope for headless agents, including per-target status and combined diffs
Workflow hardening:
fsearch/fcontent: default-ignore steering for dependency/build treesfsuite: new suite-level guide command for first-contact workflow orientation- Packaging: suite version unified at
2.0.0
fedit introduced — suite becomes scout-map-read-edit-measure.
fedit: preview-first surgical patching with exact replace, before/after anchors, and dry-run diffs by defaultfedit: preconditions (--expect,--expect-sha256), structured JSON error output, binary-target rejectionfedit:fmap-driven--symbol/--symbol-typescoping- Tests: dedicated
feditsuite; 9 test suites total
fread introduced — suite becomes scout-map-read-measure.
fread: budgeted file reading with range, head/tail, around-line, around-pattern, stdin path mode, and unified-diff modefread: token estimation, binary detection, truncationnext_hint, and structured JSON output- Tests: 7 test suites total
See docs/ftree.md and the v1.6.x entries below for full per-tool history.
Hardening and review fixes for fmap. No new features.
.hheader heuristic: dispatches to C++ when C++ constructs are detected- Locale pinning:
LC_ALL=Cset at script top - Performance: replaced O(n^2) string concatenation in
extract_symbolswith temp file streaming - JSON escape: handles
\b,\f, and all control chars (U+0000-U+001F) via perl - Safety:
--before filenames in grep calls; missing-value checks on all option flags - CodeRabbit review: 0 findings on this release
Introduces fmap — code cartography. Fourth tool in the suite, completing the recon pipeline from "what's here" to "what's inside the code."
fmap: 12 languages (Python, JavaScript, TypeScript, Rust, Go, Java, C, C++, Ruby, Lua, PHP, Bash/Shell)fmap: directory scan, single file, stdin pipe (fsearch -o paths '*.py' | fmap -o json)fmap: pretty, paths, JSON output — grep-only, zero new dependencies- Tests: 6 suites, 259 tests total
Suite-wide enhancements: duration_ms in JSON output, smart project name inference, --project-name override, --no-lines for snapshot JSON, --tool flag for fmetrics predict, telemetry flag accumulation.
See docs/ftree.md for full version history and JSON schema notes.
MIT
