Languages: English · 日本語 · 简体中文
An MCP server that replaces a coding agent's generic file-edit tool with eighteen kind-specific edit tools, each encoding the testing obligations for that kind of change directly in its tool description.
Instructions in CLAUDE.md decay across turns. Skills only fire when the agent decides to invoke them. Both rely on text the model might re-read — neither has structural force at the moment of action.
Tool definitions don't decay. The schema and description of the tool the agent is about to call are loaded at every invocation. Instead of hoping the agent remembers "add boundary tests", meta-edit splits the single Edit primitive into eighteen kind-specific tools, each carrying — in its own description — when to use it, when not to use it, what tests must accompany the edit, and when to stop and ask the user.
The bet: the shape of the tool surface is what changes AI editing behavior, not detection or post-hoc verification. The conceptual ancestor is SQLite's testing strategy — boundary values, MC/DC condition coverage, anomaly testing, per-change checklists — translated from C library quality into application-level edit categories. See docs/SPEC.md for the full specification (Part I constitution + Part II derived spec), and OBSERVED-FAILURES.md for the post-v0.2 detection backlog.
Status: 0.3.1 pre-release. v0.2 reframes the mechanism as declaration + token binding (per SPEC.md Article 5): the MCP server validates declarations and issues short-lived tokens; native Edit / Write performs the actual writes under the deny-raw-edit hook's binding-validation gate. Distributed as a single-plugin Claude Code marketplace (this repo) and as the @hiniachi/meta-edit npm package (not yet published).
edit_refactor_only edit_test_only_change
edit_boundary_condition edit_boolean_condition
edit_state_transition edit_db_schema
edit_data_migration edit_api_contract
edit_serialization edit_error_handling
edit_retry_timeout edit_concurrency
edit_external_side_effect edit_cache_invalidation
edit_permission_logic edit_dependency_config
edit_policy_change edit_docs_only
Each tool description specifies:
- when to use it,
- when not to use it,
- which tests must accompany the edit,
- when to stop and ask the user.
The first time meta-edit was self-applied to its own repository, with the conversation context already ~80% full, an OBSERVED-FAILURES.md append was requested. None of the (then) seventeen tools cleanly matched a documentation-only edit. Instead of stretching one to fit, the agent paused:
OBSERVED-FAILURES.md is a docs file and matches none of the seventeen
edit_*tools strictly (descriptions assume "production code" / "test files" / "policy/governance"). CLAUDE.md §9 says "no edit_* tool fits, stop and ask." Two options: (a) stretchedit_refactor_only— its MUST-NOT list (operator / guard / return-shape) trivially passes for prose, and the "no observable behavior change" intent is satisfied; (b)/plugin disable meta-editand use rawEdit. Which do you prefer?
Three things worth noting:
- The "stop and ask" instruction was honoured at 80% context — exactly when
CLAUDE.md-style instructions normally decay. - The agent identified the spec gap in its own terms ("seventeen tools don't cover docs files").
- It produced the v0.2 entry that subsequently became the eighteenth tool,
edit_docs_only.
Tool-shaped instructions read at every call won out over text-shaped instructions read once at session start. (This very README was rewritten through edit_docs_only.)
This repository is a single-plugin marketplace. Add it once, then install meta-edit:
/plugin marketplace add hiniachi/meta-edit
/plugin install meta-edit@meta-editThat auto-registers the meta-edit MCP server (the eighteen edit_*
tools) and the two safety hooks (deny-raw-edit,
deny-bash-write-bypass). The plugin runs prebuilt JavaScript shipped
under dist/ — Node 20+ is the only runtime requirement; no Bun, no
npm install, no build step on the consumer side.
/plugin install reuses the marketplace clone Claude Code already has
on disk. After a new meta-edit release lands on main, the local clone
can lag — /plugin install meta-edit@meta-edit will keep installing
the previous commit's dist/ until the clone is refreshed. To pick up
the latest version:
git -C ~/.claude/plugins/marketplaces/meta-edit pull origin main
rm -rf ~/.claude/plugins/cache/meta-edit
/plugin install meta-edit@meta-edit
/reload-pluginsVerify by checking
~/.claude/plugins/marketplaces/meta-edit/.claude-plugin/plugin.json's
version field. (Option B users with the npm-installed binary on
PATH can also run meta-edit --version.) Tracked upstream as a
Claude Code limitation; this section will go away once /plugin install performs an automatic fetch.
npm install -g @hiniachi/meta-edit
# then enable the safety hooks
meta-edit install-hooks --scope userOr per-project:
npm install --save-dev @hiniachi/meta-edit
meta-edit install-hooks --scope projectAdd the server to your Claude Code MCP configuration:
{
"mcpServers": {
"meta-edit": { "command": "meta-edit", "args": ["serve"] }
}
}The same npm package ships an opencode plugin under the ./opencode
subpath. The MCP server is unchanged — only the harness adapter is
different. Install:
npm install -g @hiniachi/meta-edit
meta-edit install-opencode --scope userOr per-project:
npm install --save-dev @hiniachi/meta-edit
meta-edit install-opencode --scope projectThe installer writes (or merges into) opencode.json:
{
"mcp": {
"meta-edit": {
"type": "local",
"command": ["meta-edit", "serve"],
"enabled": true
}
},
"plugin": ["@hiniachi/meta-edit/opencode"]
}A reference snippet lives at
examples/.opencode/opencode.json.
The plugin runs in opencode's in-process runtime, denies raw edit
/ write / apply_patch and dangerous bash, and shares the
.meta-edit/state/grants/ and .meta-edit/state/edits.jsonl with
any concurrent MCP-server-side issuer. Same eighteen typed_edit tool
descriptions, same audit log, same grant flow as the Claude Code
path.
@opencode-ai/plugin is declared as an optional peer dependency;
opencode bundles it at runtime, so users do not need a separate
npm install.
To uninstall:
meta-edit uninstall-opencode --scope project- Node 20 LTS or newer on the consumer side. The plugin and the npm bin both invoke
nodeagainst the prebuiltdist/cli.jsshipped in the package. - POSIX shell environment for the bash-write-bypass hook. Windows is not currently a target.
- Bun is used only for development and CI (
bun run build,bun test); consumers do not need it installed.
meta-edit serve Run the MCP stdio server
meta-edit log [--tool NAME] [--risk LEVEL] [--since DATE] Print edits.jsonl entries
meta-edit summary [--since DATE] Aggregate statistics from the edit log
meta-edit install-hooks --scope user|project Install Claude Code hooks into settings.json
meta-edit uninstall-hooks --scope user|project Remove Claude Code hooks from settings.json
meta-edit install-opencode --scope user|project Install opencode mcp + plugin into opencode.json
meta-edit uninstall-opencode --scope user|project Remove opencode mcp + plugin from opencode.json
# Show all edits to billing code that landed since the start of April:
meta-edit log --tool edit_boundary_condition --since 2026-04-01
# Show high-risk and critical edits only:
meta-edit log --risk high
meta-edit log --risk critical
# Aggregate summary for the last seven days (date in YYYY-MM-DD or any ISO 8601 form):
meta-edit summary --since 2026-04-23
# Install hooks for the current project (writes .claude/settings.json):
meta-edit install-hooks --scope project
# Install hooks for the user (writes ~/.claude/settings.json):
meta-edit install-hooks --scope userEach typed_edit call produces up to two JSONL lines in .meta-edit/state/edits.jsonl. The schema follows SPEC.md §6:
-
issued— written when the MCP server accepts the declaration and issues a token:{"edit_id":"edit_20260502_0001","ts":"2026-05-02T19:00:00+09:00","phase":"issued","kind":"edit_boundary_condition","target_file":"src/billing/charge.ts","rationale":"Allow exact-balance charges by changing < to <=","risk_level":"high","test_files":["tests/billing/charge.test.ts"],"binding":[{"file":"src/billing/charge.ts","before_sha256":"…"}],"token":"met_20260502_a3f9b2…"} -
consumed— written when thedeny-raw-edithook authorizes the corresponding native Edit / Write call (PreToolUse, before the write executes):{"edit_id":"edit_20260502_0001","ts":"2026-05-02T19:02:43+09:00","phase":"consumed","consuming_tool":"Edit"}
Validation rejections produce a single phase: "rejected" entry with a non-empty audit_error. The patch body is not stored — your VCS history is the source of truth. An issued record without a consumed sibling is evidence of an abandoned or expired declaration.
A reference workflow at examples/.github/workflows/meta-edit-summary.yml
runs meta-edit summary on every PR and uploads the report as a build
artifact. Drop it into your own repo's .github/workflows/ directory.
If meta-edit saves you time or prevents a bad edit, please consider buying the author a coffee:
Your support helps fund:
- New
edit_*categories based on observed AI failure modes - The optional future lightweight diff classifier as a backstop if descriptions prove insufficient (see
SPEC.mdArticle 2) - Tighter Claude Code Plugin integration
MIT. See LICENSE.