LLM-native code operations
Not a human tool with an API bolted on.
This is not a matter of adding an API to a tool. Every interaction assumption built into human tools fails when the user is an LLM.
So the Six-He line is not a “stronger tool”. It is three compensation mechanisms plus a task line that hands control back.
Not a diagram for the pitch deck. The index stores symbols, references, and file dependencies — nodes, edges, cycles — that you can query and run impact analysis on.
The default tier does not require memorizing 44 tool names. Every read-only lookup lives in code_query:
{
"nodes": [ ... ],
"edges": [ ... ],
"cycles": [
["resolve.js", "parse.js"]
]
}
Dynamic dispatch, reflection, and cross-language references are invisible — a static-analysis boundary, written into the limitations of every response. No pretending to be omniscient.
Better to say “can't see it” than let the model decide on a fake graph.
A five-step main chain, four return loops, six side exits. Every step names the node it reached; next_action gives the next one.
Before doing anything: know where you are, what you can read, and how far the blast reaches.
Each one previews, leaves evidence, and can be undone. Failure never turns into “done” on its own.
Cross-file transactions and undo. Also part of classic 44 — the catalog is frozen byte for byte, so it stays as is.
Every arrow below is a call that actually happens. In between: one failed check, one fix, one re-check — not a single step relies on the model believing it is done.
No read_ref, no write. If the file changes externally in between, you get VERSION_CONFLICT — never a silent overwrite.
After a failed test, progress stays partial and next_action points back to the same check scope — fix first, then prove.
next_action:null + host_required: the tool states that mechanical requirements are met; whether the business goal is met is the host's call.
“Done” is not a feeling. It is an inspectable projection. All state machines collapse into one read-only verdict, resolved by fixed priority — the first match is the fact.
Top to bottom, the first state that matches is the current fact. Read-only, not writable.
These four lines are a contract, not copy: next_action:null means the tool has no next step; whether the business goal is truly met is not something the tool signs off on.
Green tests are not enough — completion is driven by audit. It enters awaiting_decision only when audit evidence applies to the current goal and files.
12 state machines live in the workspace and are reprojected on every call. Context truncated, session restarted — the task does not get lost. That is the real patch for having no memory.
The journal and delegation ledger exist mainly for crash reconciliation and idempotency; every other state appears in the response fields the LLM receives.
The entry layer can stay small because the complexity sits where it belongs: state on disk, index and parsing outside the process.
If the MCP process dies, the task does not go with it. On restart it reconciles against the journal: recover what can be recovered, mark the rest indeterminate.
tree-sitter runs in a Rust service; panics are caught by catch_unwind and returned as error codes instead of taking the whole MCP process down.
Write locks, batch locks, atomic writes. On the sql.js backend a second writer process is explicitly rejected — not quietly allowed to overwrite.
The Six-He line is not a task drawn as a graph. On the task side there is only route, node, and next_action computed fresh each call — no persisted edges. The real graph is on the code side: {nodes, edges, cycles}. We put that distinction in the docs rather than blur it.
The three tiers are parallel catalogs, not nested ones. Everything is plain regex / AST — zero LLM calls.
The tool catalog is re-sent with context on every turn. Do that math before talking throughput.
The counterintuitive part: trimming descriptions saves at most 0.6k. The only way to cut the catalog tax is to cut the parameter surface — which is exactly why 13 entries absorb the schemas of 44 tools.
Same scripted path — not a host-side baseline, not an agentic conversation.
code_batch packs N edits into one call (all or nothing). Edit site by site, and every turn re-sends the system prompt, tool catalog, conversation history, and the files already read; a batch sends once — N−1 fewer turns, multiplied by the whole context, not by the catalog tax — and N−1 fewer model waits.
No build artifacts. Clone, install dependencies, point your MCP client at it.
# 1) clone git clone https://github.com/wulun811/LiuHe.git # 2) install (includes better-sqlite3) cd LiuHe/malong && npm ci # 3) start the MCP server node --max-old-space-size=512 mcp-server.js \ --workspace /path/to/project
{
"mcp": {
"malong": {
"type": "local",
"command": ["node", "--max-old-space-size=512",
"malong/mcp-server.js",
"--workspace", "."],
"enabled": true
}
}
}