
Company News
Socket Named Top Sales Organization by RepVue
Socket won two 2026 Reppy Awards from RepVue, ranking in the top 5% of all sales orgs. AE Alexandra Lister shares what it's like to grow a sales career here.
@levnikolaevich/hex-line-mcp
Advanced tools
Hash-verified file editing MCP + token efficiency hook for AI coding agents. 9 tools: inspect_path, read, edit, write, grep, outline, verify, changes, bulk_replace.
Hash-verified file editing MCP + token efficiency hook for AI coding agents.
Every line carries an FNV-1a content hash. Every edit must present those hashes back -- proving the agent is editing what it thinks it's editing. No stale context, no silent corruption. Hashing works on normalized logical text; writes preserve the file's existing line endings and trailing-newline shape.
By default, mutating tools stay inside the current project root. If you intentionally need to edit a temp or external path, pass allow_external: true on edit_file, write_file, or bulk_replace.
Core day-to-day tools:
read_fileedit_filegrep_searchoutlineverifybulk_replaceAdvanced / occasional:
write_fileinspect_pathchanges| Tool | Description | Key Feature |
|---|---|---|
read_file | Read file with progressive disclosure, optional edit-ready metadata, and automatic graph hints when available | Minimal plain discovery by default, explicit edit_ready for verified edits |
edit_file | Revision-aware anchor edits (set_line, replace_lines, insert_after, replace_between) | Batched same-file edits + conservative auto-rebase |
write_file | Create new file or overwrite, auto-creates parent dirs | Path validation, no hash overhead |
grep_search | Search with ripgrep, summary-first discovery, and optional edit-ready hunks | summary by default, capped content mode with explicit allow_large_output escape hatch |
outline | AST-based structural overview with hash anchors via tree-sitter WASM. Supports JavaScript/TypeScript, Python, C#, PHP, and fence-aware markdown headings | 95% token reduction, direct edit anchors |
verify | Check if held checksums / revision are still current | Staleness check without full re-read |
inspect_path | Unified file-or-directory inspection | Minimal tree discovery by default, capped pattern-mode lists with refine hints |
changes | Compare file against git ref, shows added/removed/modified symbols | AST-level semantic diff with risk/provenance preview |
bulk_replace | Search-and-replace across multiple files inside an explicit root path | Compact summary (default) or capped diffs via format, dry_run, max_files |
| Event | Trigger | Action |
|---|---|---|
| PreToolUse | Read/Edit/Write/Grep/Glob on project text scope | Advises hex-line by default for project-scoped text files and file discovery; explicit hooks.mode: "blocking" hard redirects. Built-in tools stay available for binary/media, plan files in Plan Mode, and text paths outside the current project root |
| PreToolUse | Bash with dangerous commands | Blocks rm -rf /, git push --force, etc. Agent must confirm with user |
| PostToolUse | Bash with 50+ lines output | RTK: deduplicates, truncates, shows filtered summary to Claude as feedback |
| SessionStart | Session begins | Injects a short bootstrap hint; defers to the active output style when hex-line style is enabled |
PreToolUse also intercepts project-scoped file inspection Bash commands: cat, type, Get-Content, head, tail, ls, dir, tree, find, Get-ChildItem, stat, wc, Get-Item, grep, rg, findstr, Select-String, sed -i. Targeted inspection pipelines receive advisory hints by default and are hard redirected only in explicit blocking mode; Git/build/test/docker/network and stdin-filter pipelines remain allowed.
npm i -g @levnikolaevich/hex-line-mcp
claude mcp add -s user hex-line -- hex-line-mcp
ripgrep is bundled via @vscode/ripgrep — no manual install needed for grep_search.
Requires Node.js >= 20.19.0.
Hooks and output style are auto-synced on every MCP server startup. The server compares installed files with bundled versions and updates only when content differs. First run after npm i -g triggers full install automatically.
Hooks are written to global ~/.claude/settings.json with absolute path to hook.mjs. Output style is installed to ~/.claude/output-styles/hex-line.md and activated if no other style is set. To activate manually: /config > Output style > hex-line.
No extra manual setup is required after install. The startup sync uses the current Node runtime and a stable hook path under ~/.claude/hex-line, so the hook command survives spaces in the home directory on Windows, macOS, and Linux.
Use the normal package checks:
npm test
npm run lint
npm run check
For the full MCP workspace regression pass from the repository root, run the package test scripts explicitly:
npm --prefix mcp --workspace hex-common test
npm --prefix mcp --workspace hex-line-mcp test
npm --prefix mcp --workspace hex-graph-mcp test
npm --prefix mcp --workspace hex-ssh-mcp test:all
Maintainers can also run the internal scenario harness when they want reproducible repo-local workflow regressions:
npm run scenarios -- --repo /path/to/repo
npm run scenarios:diagnostic -- --repo /path/to/repo
The diagnostics run reports graph payload overhead and auto-refresh telemetry. Read those rows as engineering diagnostics, not as a compression score.
Comparative built-in vs hex-line benchmarks are maintained outside this package. External-baseline comparisons must reuse the same scenario suite and correctness contract before making broader claims.
If a project already has .hex-skills/codegraph/index.db, hex-line automatically adds lightweight graph hints to read_file, outline, grep_search, edit_file, and changes.
.hex-skills/codegraph/index.db is missing, stale, or unreadable, hex-line falls back to standard behavior silently.hex-line only uses the graph database that belongs to the resolved current project scope.better-sqlite3 is optional. If it is unavailable, hex-line still works without graph hints.read_file only emits the top-line Graph: header in verbosity=full.grep_search only emits line-level graph annotations in output=content when editReady=true. summary, files, count, and discovery-only content search stay graph-free.read_file, outline, and grep_search stay compact: they only surface high-signal local facts such as api, framework entrypoints, callers, flow, and clone hints.edit_file and changes surface the deeper review layer: external callers, downstream return/property flow, clone peers, public API risk, framework entrypoint risk, and same-name sibling warnings when present.payload_sections, graph_enrichment, count fields, and provenance_summary.hex-line suppresses stale hints, schedules a best-effort reindexFile(...) for a stale file, and escalates to one background indexProject(...) when several files go stale inside the burst window.edit_file may still use stale graph context as same-response advisory metadata when the file was just edited, but stale graph never gates the edit protocol itself.npm run scenarios:diagnostic -- --with-graph prints graph payload overhead plus an auto-refresh probe line with suppressions, file_refresh, project_refresh, and threshold_hits.hex-line does not read hex-graph internals directly anymore. The integration uses a small read-only contract exposed by hex-graph-mcp:
hex_line_symbolshex_line_line_factshex_line_edit_impactshex_line_edit_impact_factshex_line_clone_siblingsoutline for large code filesread_file for the exact range you neededit_file with all known hunks in one callrevision from the earlier read_file or edit_filebase_revisionverify before delayed or mixed-tool follow-up editsretry_edit, retry_edits, retry_checksum, or retry_plan, reuse those directlyUse replace_between inside edit_file when you know stable start/end anchors and want to replace a large function, class, or config block without reciting the old body.
Use bulk_replace for text rename patterns across one or more files inside a known project root or directory scope. Pass path explicitly. In normal agent workflows that scope should be auto-filled from the current project root, not typed manually. Returns compact summary by default; pass format: "full" for capped diffs. Do not use it as a substitute for structured block rewrites.
Read a file with progressive disclosure. Default mode is discovery-first: plain partial lines, low token cost, and optional continuation hints. When you need verified edits, pass edit_ready: true with verbosity: "full" to get canonical read_range blocks with anchors and checksums. Directories go through inspect_path.
| Parameter | Type | Required | Description |
|---|---|---|---|
file_path | string | yes | File path |
file_paths | string[] | no | Array of file paths to read (batch mode) |
offset | number | no | Start line, 1-indexed (default: 1) |
limit | number | no | Max lines to return (default: 200 for discovery, 2000 for edit-ready, 0 = all) |
ranges | array | no | Explicit line ranges, e.g. [{ "start": 10, "end": 30 }] |
plain | boolean | no | Omit hashes, output lineNum|content instead |
verbosity | enum | no | minimal, compact, or full |
edit_ready | boolean | no | Include hash/checksum edit protocol blocks explicitly |
Default output is discovery-first:
File: lib/search.mjs
meta: 282 lines, 10.2KB, 2 hours ago
continuation: {"kind":"offset","offset":4,"limit":200}
1|import { resolve } from "node:path";
2|import { readFileSync } from "node:fs";
3|...
Explicit edit-ready output:
File: lib/search.mjs
meta: 282 lines, 10.2KB, 2 hours ago
revision: rev-12-a1b2c3d4
file: 1-282:beefcafe
eol: lf
trailing_newline: true
block: read_range
span: 1-3
eol: lf
trailing_newline: true
ab.1 import { resolve } from "node:path";
cd.2 import { readFileSync } from "node:fs";
ef.3 ...
checksum: 1-3:f7e2a1b0
Edit using revision-aware hash-verified anchors. Prefer one batched call per file. For text rename use bulk_replace.
| Parameter | Type | Required | Description |
|---|---|---|---|
file_path | string | yes | File to edit |
edits | string | yes | JSON array of edit operations (see below) |
dry_run | boolean | no | Preview changes without writing |
restore_indent | boolean | no | Auto-fix indentation to match anchor context (default: false) |
base_revision | string | no | Prior revision from read_file / edit_file for same-file follow-up edits |
conflict_policy | enum | no | conservative or strict (default: conservative) |
allow_external | boolean | no | Allow editing a path outside the current project root |
Edit operations (JSON array):
[
{"set_line": {"anchor": "ab.12", "new_text": "replacement line"}},
{"replace_lines": {"start_anchor": "ab.10", "end_anchor": "cd.15", "new_text": "...", "range_checksum": "10-15:a1b2c3d4"}},
{"insert_after": {"anchor": "ab.20", "text": "inserted line"}},
{"replace_between": {"start_anchor": "ab.30", "end_anchor": "cd.80", "new_text": "...", "boundary_mode": "inclusive"}}
]
Discipline:
range_checksum. Copy it from read_file or grep_search(output_mode:"content").grep_search for narrow targets, or outline -> read_file(ranges) for structural edits.edit_file returns a fresh revision, continue from that state as base_revision.hex-line preserves existing file line endings on write; repo-level line-ending cleanup should be a separate deliberate operation, not a side effect of edit_file.Result footer includes:
status: OK | AUTO_REBASED | CONFLICTreason: ... as the canonical machine-readable cause for the current statusrevision: ...summary: ... with edited line span / diff counts on successful editspayload_sections: ... so callers know which detailed sections followgraph_enrichment: unavailable + graph_fix: ... when the project graph is missing or out of sync (silent when graph is healthy)⚠ Semantic impact: and ⚠ N clone(s): blocks emitted directly when graph-backed review data is availablechanged_ranges: ... when relevantrecovery_ranges: ... with the narrowest recommended read_file ranges for retrynext_action: ... as the canonical immediate choice: apply_retry_edit, apply_retry_batch, or reread_then_retryremapped_refs: ... when stale anchors were uniquely relocatedretry_checksum: ... on local conflicts, narrowed to the exact target range when possibleretry_edit: ... when the server can synthesize a ready-to-retry edit skeleton from current local stateretry_edits: ... on conservative batch conflicts when every conflicted edit can be retried directlysuggested_read_call: ... when rereading is the safest next stepretry_plan: ... with a compact machine-readable next-call plansummary: ... and snippet: ... instead of long prose blocks on conflictsedit_conflicts: N on conservative multi-edit preflight conflictsCreate a new file or overwrite an existing one. Creates parent directories automatically.
| Parameter | Type | Required | Description |
|---|---|---|---|
file_path | string | yes | File path |
content | string | yes | File content |
allow_external | boolean | no | Allow writing a path outside the current project root |
Search file contents using ripgrep. Default mode is summary for discovery. Use content with edit_ready: true when you need canonical search_hunk blocks for verified edits. files and count stay plain list modes.
| Parameter | Type | Required | Description |
|---|---|---|---|
pattern | string | yes | Search pattern (regex by default, literal if literal:true) |
path | string | no | Directory or file to search (default: cwd) |
glob | string | no | Glob filter, e.g. "*.ts" |
type | string | no | File type filter, e.g. "js", "py" |
output_mode | enum | no | Output format: "summary" (default), "content", "files_with_matches", "count" |
case_insensitive | boolean | no | Ignore case |
smart_case | boolean | no | CI when lowercase, CS when uppercase (-S) |
literal | boolean | no | Literal string search, no regex (-F) |
multiline | boolean | no | Pattern can span multiple lines (-U) |
context | number | no | Symmetric context lines around matches (-C) |
context_before | number | no | Context lines BEFORE match (-B) |
context_after | number | no | Context lines AFTER match (-A) |
limit | number | no | Max matches per file (default: 20 for summary, 100 for content) |
head_limit | number | no | Total match events across all files; multiline matches count as 1 (default: 50 for summary, 200 for content, 1000 for files_with_matches/count, 0 = unlimited) |
plain | boolean | no | Omit hash tags inside block entries, return lineNum|content |
edit_ready | boolean | no | Preserve hash/checksum search hunks in content mode |
allow_large_output | boolean | no | Bypass the default content-mode block/char caps when you intentionally need a larger payload |
summary mode returns counts, top files, and a few plain snippets. content mode returns canonical search_hunk blocks with per-hunk checksums enabling direct replace_lines from grep results without intermediate read_file.
content mode is intentionally capped to keep discovery cheap. When truncation happens, the diagnostic block includes shown_matches, file_count, truncated, next_action, and suggested_refine_call.allow_large_output: true as an explicit override for review/debug workflows, not as the normal discovery path.AST-based structural outline with hash anchors for direct edit_file usage. Supports JavaScript/TypeScript, Python, C#, PHP, and fence-aware markdown heading navigation (.md/.mdx). Each entry includes a hash tag for immediate anchor use without intermediate read_file.
| Parameter | Type | Required | Description |
|---|---|---|---|
file_path | string | yes | Source file path |
Supported languages: JavaScript (.js, .mjs, .cjs, .jsx), TypeScript (.ts, .tsx), Python (.py), C# (.cs), and PHP (.php) via tree-sitter WASM.
Not for .json, .yaml, .txt -- use read_file directly for those.
Check if range checksums from prior read/search blocks are still valid, optionally relative to a prior base_revision. Returns a deterministic verification report with canonical status, summary, next_action, and compact entry lines.
| Parameter | Type | Required | Description |
|---|---|---|---|
file_path | string | yes | File path |
checksums | string[] | yes | Array of checksum strings, e.g. ["1-50:f7e2a1b0"] |
base_revision | string | no | Prior revision to compare against latest state |
Example output:
status: STALE
reason: checksums_stale
revision: rev-17-deadbeef
summary: valid=0 stale=1 invalid=0
next_action: reread_ranges
base_revision: rev-16-feedcafe
changed_ranges: 10-12(replace)
suggested_read_call: {"tool":"mcp__hex-line__read_file","arguments":{"file_path":"/repo/file.ts","ranges":["10-12"]}}
entry: 1/1 | status: STALE | span: 10-12 | checksum: 10-12:oldc0de0 | current_checksum: 10-12:newc0de0 | next_action: reread_range | summary: content changed since checksum capture
Inspect a file or directory path without guessing which low-level tool to call first.
| Parameter | Type | Required | Description |
|---|---|---|---|
path | string | yes | File or directory path |
pattern | string | no | Glob filter on names (e.g. "*-mcp", "*.mjs"). Returns flat match list instead of tree |
type | string | no | "file", "dir", or "all" (default). Like find -type f/d |
max_depth | number | no | Max recursion depth (default: 2 for discovery, or 20 in pattern mode) |
max_entries | number | no | Max entries to show in pattern mode before truncation metadata is returned (default: 60, 0 = unlimited) |
gitignore | boolean | no | Respect root .gitignore patterns (default: true). Nested .gitignore not supported |
format | string | no | "compact" = shorter path view. "full" = include sizes / metadata where available |
verbosity | enum | no | minimal, compact, or full |
pattern, it switches to flat match mode and works as the preferred replacement for find / recursive ls.match_count, shown_count, truncated, next_action, and suggested_refine_call so the caller can narrow path before asking for more.The unified hook (hook.mjs) handles five Claude hook events:
Advises built-in Read, Edit, Write, Grep, and Glob on project-scoped text workflows to use hex-line. Missing or malformed hook state defaults to advisory mode; set .hex-skills/environment_state.json to hooks.mode: "blocking" to hard-route covered calls. Binary/media files (images, PDFs, notebooks, archives, executables, fonts, media), plan markdown files in Plan Mode, and text paths outside the current project root stay on built-in tools.
Intercepts project-scoped Bash file inspection commands (cat, type, Get-Content, head, tail, ls, dir, tree, find, Get-ChildItem, stat, wc, Get-Item, grep, rg, findstr, Select-String, sed -i, etc.) and advises hex-line tools by default. Explicit blocking mode hard redirects covered cases. Dangerous commands (rm -rf /, git push --force, git reset --hard, DROP TABLE, chmod 777, mkfs, dd) are always blocked.
Triggers on Bash tool output exceeding 50 lines. Pipeline:
(xN) countsInjects a compact startup reminder. If the hex-line output style is active, the hook emits only a minimal bootstrap hint plus ToolSearch('+hex-line read edit') fallback. Otherwise it injects the short preferred read/edit workflow directly, including the scope rule: use file paths for file tools and the current project root for repo-wide tools such as bulk_replace.
Hook policy constants in lib/hook-policy.mjs:
| Constant | Default | Purpose |
|---|---|---|
LINE_THRESHOLD | 50 | Minimum lines to trigger filtering |
HEAD_LINES | 15 | Lines to keep from start |
TAIL_LINES | 15 | Lines to keep from end |
Injects a short operational workflow into agent context at session start. If schemas are not loaded yet, it includes the ToolSearch('+hex-line read edit') fallback. Primary flow stays outline -> read_file -> edit_file -> verify, with targeted reads over full-file reads.
When permission_mode === "plan", blocks mutating hex-line tools (edit_file, write_file, bulk_replace) with a hard deny. Read-only tools pass through normally. This enforces plan mode for MCP tools that bypass the built-in Edit/Write permission layer.
Invalidates cached hook mode and hex-line disabled flag when settings change mid-session. Dispatches before the isHexLineDisabled() check so that toggling hex-line on/off mid-session takes effect without restart.
Logs when Claude denies a tool call after the hook delivered a redirect hint. Reads data.reason for hex-line tool mentions and Bash denials. Observability only — no action taken.
hex-line-mcp/
server.mjs MCP server (stdio transport, 9 tools)
hook.mjs Unified hook (PreToolUse + PostToolUse + SessionStart)
package.json
lib/
hook-policy.mjs Shared hook policy: redirects, thresholds, danger patterns
setup.mjs Startup autosync for hook + output style
read.mjs File reading with hash annotation
edit.mjs Anchor-based edits, diff output
search.mjs ripgrep wrapper with hash-annotated results
outline.mjs tree-sitter WASM AST outline
verify.mjs Range checksum verification
info.mjs File metadata (size, lines, mtime, type)
tree.mjs Directory tree with .gitignore support
inspect-path.mjs Unified file/directory inspection
changes.mjs Semantic git diff via AST
bulk-replace.mjs Multi-file search-and-replace
setup.mjs Claude hook installation + output style setup
format.mjs Output formatting utilities
security.mjs Path validation, binary detection, size limits
@levnikolaevich/hex-common/
text-protocol/hash.mjs Shared FNV-1a hashing and checksum protocol
output/normalize.mjs Shared output normalization helpers
ab.42 const x = calculateTotal(items);
ab -- 2-char FNV-1a tag derived from content (whitespace-normalized)42 -- line number (1-indexed)abcdefghijklmnopqrstuvwxyz234567 (32 symbols, bitwise selection)checksum: 1-50:f7e2a1b0
FNV-1a accumulator over all line hashes in the range (little-endian byte feed). Detects changes to any line, even ones not being edited.
realpathSync (resolves symlinks)Yes. hex-line-mcp is a standard MCP server (stdio transport). It works with any MCP-compatible client -- Claude Code, Gemini CLI, Codex CLI, or custom integrations. Hook installation is Claude-specific; Gemini/Codex use MCP Tool Preferences guidance instead.
The edit is rejected with an error showing which lines changed since the last read. The agent must re-read the affected range and retry. This prevents silent overwrites from stale context.
Outline works on JavaScript/TypeScript, Python, C#, PHP, and markdown heading navigation (.md/.mdx, fenced code blocks ignored). For JSON, YAML, and text files use read_file directly. Each outline entry includes a hash anchor (tag.line-range: symbol) for direct use in edit_file.
The PostToolUse hook normalizes Bash output (replaces UUIDs, timestamps, IPs with placeholders), deduplicates identical lines, and truncates to first 15 + last 15 lines. The filtered summary is shown to Claude as stderr feedback via exit code 2.
Yes. The supported runtime bypass is .hex-skills/environment_state.json -> { "hooks": { "mode": "advisory" } }, which downgrades project text redirects to advice. To remove the hook entirely, delete the hex-line hook entries from ~/.claude/settings.json. To disable the MCP server for one project, add hex-line to ~/.claude.json -> projects.{cwd}.disabledMcpServers.
| Package | Purpose | npm |
|---|---|---|
| hex-line-mcp | Local file editing with hash verification + hooks | |
| hex-ssh-mcp | Remote file editing over SSH | |
| hex-graph-mcp | Code knowledge graph with AST indexing |
MIT
FAQs
Hash-verified file editing MCP + token efficiency hook for AI coding agents. 9 tools: inspect_path, read, edit, write, grep, outline, verify, changes, bulk_replace.
We found that @levnikolaevich/hex-line-mcp demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Company News
Socket won two 2026 Reppy Awards from RepVue, ranking in the top 5% of all sales orgs. AE Alexandra Lister shares what it's like to grow a sales career here.

Security News
NIST will stop enriching most CVEs under a new risk-based model, narrowing the NVD's scope as vulnerability submissions continue to surge.

Company News
/Security News
Socket is an initial recipient of OpenAI's Cybersecurity Grant Program, which commits $10M in API credits to defenders securing open source software.