Repository navigation
Releases: parcadei/fastedit
Releases · parcadei/fastedit
Release list
0.5.0
0.5.0 — 2026-04-23
Added
fast_move_to_fileMCP tool +fastedit move-to-fileCLI — relocate a symbol cross-file with automatic import rewrites across all 13 supported languages (Python, TS/JS, Java, Kotlin, Rust, Go, Swift, C#, Ruby, PHP, Scala, Elixir, Lua, C/C++). Rustusetrees handled via tree-sitter: simple, braced, arbitrary-depth nested, aliased (as Name), and wildcard (*) — the wildcard case appends an explicit import alongside the glob and surfaces an advisory note. Pass--dry-runfor preview.--only <kind>onfast_rename_all/kind_filteronfast_rename_allMCP tool — restrict rename toclass | function | method | variable. Resolved via tldr's structural classification, not regex.--dry-runonfastedit rename/dry_run=Trueonfast_renameMCP tool — parity withrename-all.--forceonfastedit delete/force=Trueonfast_delete— override the new cross-file caller safety check.
Changed
fast_renameandfast_rename_allAST-verified across all 13 supported languages viatldr references. Strings/comments/docstrings are structurally skipped using tldr's confidence axis — real code hits at 1.0, string/comment substring noise at 0.5. On AST-native langs (Python, TS/JS, Go, Rust), refs are classified as function/method/call/import/read/variable; on the other langs the same confidence filter still gives AST-verified-quality matching. No per-language AST walk in fastedit.fast_deleteruns a cross-file caller safety check before deleting. Refuses with a structured caller-location message (file:line:kind, up to 10 rows) if the symbol has references in other files, and suggestsfast_rename_allas a migration path. Pass--force/force=Trueto bypass. Falls open with a warning note when tldr is unavailable — infra failures never fail-closed. Works across all 13 languages.fast_editappends an impact note whenreplace=<name>changes the function's signature line. Reports caller counts and points attldr impact/fast_searchfor detailed review. Purely informational — the edit always proceeds. Signature detection outsourced totldr structure(13/13 lang coverage via tldr's per-definitionsignaturefield). Hot path stays subprocess-free for body-only edits (0.052ms p50 overhead verified on unchanged-signature edits).- Architectural refactor: fastedit now outsources per-language AST work to tldr wherever tldr already exposes the primitive. Kind classification, signature extraction, reference discovery, and import location all go through tldr. Net code reduction in
caller_safety.pyandrename.pydespite the feature additions. - MCP server instructions, per-tool descriptions, CLI help, README, and
claude-skill/SKILL.mdall synchronized to reflect the new capabilities.
Fixed
- UTF-8 BOM line-1 rename bug —
_char_col_to_byte_offsetwas translating character index to byte offset while tldr already emits byte columns, causing an off-by-3 misalignment on line-1 symbols of BOM-prefixed files. Replaced with_tldr_col_to_byte_offsetthat uses tldr's column directly. Multi-byte chars before the symbol on later lines are handled correctly too. delete_symbolno longer drops@decorator/@annotationlines above the deleted symbol. Root cause: upstreamtldr structurereported the wrong line-span for decorated defs. Switched to in-memory AST viaget_ast_map_from_source.get_ast_mapdebug log no longer leaks to stdout on empty/near-empty files (surfaced duringfast_move_to_filesmoke testing). Demoted from_log.warningto_log.debug; a regression test locks stdout cleanliness.- Zero-match
rename-allmessage no longer says "word-boundary" (stale from the pre-AST-verified impl). Updated to "AST-verified via tldr — strings/comments/vendor dirs excluded"; regression test locks the wording for both the MCP and CLI surfaces. - Rust
usetree rewriting forfast_move_to_filenow handles nested (arbitrary depth), aliased (as Name), and wildcard (*) cases via tree-sitter-rust. Wildcard case appends an explicit import for the moved symbol alongside the existing glob and surfaces an advisory note in the plan.
Tests
- 756 passed (up from 598 in 0.4.1, +158 new). Zero failures, zero xfails. 29 skipped (all intentional — per-language paths where a subsystem genuinely doesn't apply, not hidden failures).
- 91 tests from the hardening milestone (65 cross-language parametrized, 15 adversarial, 5 property/invariant, 6 tool-integration), 24 from the outsource refactor, plus regression locks on message wording, BOM line-1, decorator preservation, and Rust complex
usecases. - Language coverage matrix published at
docs/testing-matrix.md: M1, M2, M3, and M4 all verified at 13/13 languages.
0.4.1
0.4.1 — 2026-04-23
Fixed
fast_edit(replace='ClassName', ...)no longer spuriously rejects field-only class snippets. The safety guard that blocks multi-symbolreplace=snippets (to prevent silent deletion of nested methods) now usestldr structure's per-definitionkindclassification: methods and functions count as extras, field/variable/constant declarations do not. Previously the guard couldn't distinguishprivate int size = 100;frompublic void set(...), so anyreplace=ClassNamesnippet containing a full class body with only a field change was rejected even though the edit was a safe direct swap. tldr is already a fastedit prerequisite (used byfast_read,fast_search,fast_references), so this inherits accurate per-language extraction across all 13 supported languages at ~10 ms per call. Falls back to the in-process AST analyzer if tldr is temporarily unavailable. Closes thetest_batch_edit_mixed_preserve_siblingsregression that had been carried in the backlog.- Signature-presence check now recognizes Java / C# / TypeScript access modifiers. The internal helper that decides whether to auto-preserve a function/class signature (omitted signature → prepend from AST) only knew Rust's
puband TypeScript'sexport.public class Cache { ... },private static final class Foo { ... }, and similar snippets were classified as "signature missing" and got their declaration prepended a second time, producing duplicated lines once the guard above stopped blocking them. The helper now accepts any sequence of modifier keywords beforeclass/struct/interface/trait/enum/impl, and skips comment-prefixed lines (//,#,*,--) to avoid false positives from declarations referenced inside doc comments.
Changed
fastedit doctornow reports the tldr version (tldr /path/to/tldr (v0.1.4)) and treats tldr as a required dependency rather than optional, reflecting its role in the edit guard,fast_read, andfast_search. Missing-tldr now produces a failing row (exit code 1) instead of a skip.
Tests
- 7-case regression lock for the field-vs-method guard invariant across Python, Java, Rust, and TypeScript. Field-only class snippets pass silently; method-containing snippets raise
ValueErrorwith the sameadditional symbol(s)message as before. Locks both halves of the invariant so future changes to the guard can't regress one in favor of the other.
FastEdit 0.4.0
Added
fast_rename_allMCP tool +fastedit rename-allCLI — cross-file symbol rename. Walks every supported code file under a directory, applies word-boundary rename, skips matches inside strings/comments/docstrings via tree-sitter. Prunes.git,node_modules,__pycache__,target,dist,vendor, and other common vendor/build dirs. Also prunes any directory containing apyvenv.cfg(PEP 405 venv marker) so non-standard venv names like.venv311,myenv,virtualenvdon't escape the filter. Pass--dry-run/dry_run=Trueto preview. Symlinks are not followed (prevents double-rename). Same-name rename is a guarded no-op. Not scope-aware (text matching with string/comment skip zones, not LSP) — unique names are safer than short common identifiers. Instant, no model. 11-case test suite covers substring collisions, dotfile dirs, binary files, and venv pruning.
Fixed
- Multi-line signature auto-preserve bug.
fast_editwithreplace='func_name'and a body-only snippet corrupted output when the target had a multi-line parameter list. The auto-preserve path atchunked_merge.py:496grabbed only the first line (def foo() and dropped the continuation, producing unclosed parens. Replaced the line-grab heuristic with a tree-sitter lookup of the function'sbodyfield. Verified across all 13 supported languages: Python, JavaScript, TypeScript, Rust, Go, Java, Ruby, Swift, C, C++, C#, PHP, plus Kotlin (uses unnamedfunction_bodychild — handled by fallback) and Elixir (parsesdef foo(a) do ... endas a call with ado_blockchild — handled by fallback). 14-case parametrized regression lock plus 5 scenario tests.
FastEdit 0.3.2
Fixed
- Hook supported-extension list stays synced with the AST parser. Thanks to @chapayevdauren (PR #1) for the refactor: the hook now imports
EXTENSION_TO_LANGUAGEdirectly fromfastedit.data_gen.ast_analyzerwith a hardcoded fallback for edge cases. This eliminates three silent drift bugs we'd shipped in 0.2.7 —.mjsand.cjswere in the hook's allow-list but not in the parser (edits redirected tofast_edit, which then errored);.hxxsame;.hhwas in the parser but not in the hook (silently missed the redirect). Single source of truth now. - Dotfiles and extensionless files fall through to built-in Edit.
Makefile,Dockerfile,.env,.bashrc,.gitignore,README—Path.suffixis empty for any name that starts with a dot or has no dot at all, so under the 0.3.0 logic (if ext and ext not in SUPPORTED: fall_through) they hit the empty-extshort-circuit and got blocked by the default deny path. Flagged by CodeRabbit on PR #1. Condition is nowif file_path and ext not in SUPPORTED: fall_through, which treats any file without a recognized code extension as "not ours" — correct for every real non-code file type.
Changed
- Narrowed the hook's fallback
except Exceptiontoexcept ImportError, also per CodeRabbit review. Ifast_analyzerever has a real bug (SyntaxError, NameError) we want it to surface, not be silently masked by the hardcoded fallback.
Tests
- First test suite in the repo.
tests/test_hook.pycovers all 23 supported extensions (blocked with redirect), 11 common unsupported extensions (fall through silently), 6 dotfile / extensionless names (fall through), uppercase extensions, and missingfile_path. 34 cases total, all passing. Contributed by Dauren, extended here for the dotfile regression.
FastEdit 0.3.1
Added
fastedit doctornow includes a remote version check. Thefastedits packagerow shows{version} (up to date)when current, or{version} → {latest} available (uv tool upgrade fastedits)when a newer release exists. Shares the 24-hour PyPI-fetch cache (~/.cache/fastedit/update-check.json) andFASTEDIT_NO_UPDATE_CHECK=1escape hatch with the existing CLI exit notice and MCP banner — one HTTP call per day across all three surfaces. Falls back gracefully to{version} (remote version check unavailable)when PyPI can't be reached.
Changed
update_checkmodule grew a publicget_version_info()helper returning a(current, latest)tuple.get_update_notice()now builds on it instead of duplicating the cache-and-fetch logic.
FastEdit 0.3.0
Added
fastedit doctor— self-diagnostic that reports install health at a glance. Checks the three binaries on PATH (fastedit,fastedit-mcp,fastedit-hook), Python version, installed package version, optional backend extras (mlx,vllm,fastmcp), model cache (path, size, actual.safetensorspresence, honorsFASTEDIT_MODEL_PATH), Claude Code MCP config sanity (both~/.claude.jsonand$(pwd)/.mcp.json, with stale-path detection), and tldr availability. Prints per-section rows with ✓/!/✗/– markers and exits non-zero if any required check fails. Use as the first diagnostic when anything goes wrong — most common install issues (wrong extras, missing weights, stale MCP config) are now visible in one command.fastedit mcp-install— writes the fastedit entry into Claude Code's MCP config in one command. Defaults to--scope user(~/.claude.json); pass--scope projectto write$(pwd)/.mcp.json. Idempotent: detects an existing matching entry and exits. Safe on upgrades: if a stale entry exists (e.g. hardcoded venv path from an older install), the old entry is printed, the config is backed up with a timestamped.bak-*suffix, and the entry is replaced with{"command": "fastedit-mcp", "type": "stdio"}. Warns but still writes iffastedit-mcpisn't on PATH yet.
FastEdit 0.2.9
Added
fastedit-mcpconsole entry point. The MCP server is now installable as a standalone binary alongsidefasteditandfastedit-hook, so MCP config files can use"command": "fastedit-mcp"with no hardcoded venv paths, nopython -minvocation, nopythonvspython3ambiguity. Works identically in Claude Code, Cursor, Continue, and any other MCP host.
Fixed
fastedit pullsilently skipping weight download. The cache-hit check usedany(cached_path.iterdir()), which returned True on a partial cache (config.json + tokenizer.json present,.safetensorsshards missing).fastedit pullthen reportedModel readywithout downloading the weights, and every subsequent edit call failed at inference time with a cryptic model-load error. The check now requires at least one*.safetensorsfile in the cache before returning, so partial caches trigger a real download.
Changed
- README install + MCP setup aligned with how the package actually ships. Primary install commands now include the
mcpextra (uv tool install 'fastedits[mlx,mcp]'on Apple Silicon,[vllm,mcp]on GPU,[mcp]for external-server setups) — previously users who followed the README ended up with the MCP server importable but itsfastmcpdependency missing, and gotModuleNotFoundError: No module named 'mcp'on first connect.fastedit pullexamples now use the required--model mlx-8bit/--model bf16flag (--modelbecame required in 0.2.8). The MCP config example now uses"command": "fastedit-mcp"(the new entry point) instead of"command": "python", "args": ["-m", "fastedit.mcp_server"].
FastEdit 0.2.8
Fixed
fastedit pullbroken default. The CLI's--modeldefault wasfastedit-1.7b-mlx-8bit, a legacy long-form name thatmodel_download.pyno longer registers (valid names aremlx-8bitandbf16). Runningfastedit pullwith no args raisedRuntimeError: Unknown model 'fastedit-1.7b-mlx-8bit'. Fixed by making--modelrequired withchoices=["mlx-8bit", "bf16"]— no silent platform default, argparse prints both options on error, and Linux/GPU users aren't tricked into downloading MLX weights they can't run.
FastEdit 0.2.7
Fixed
fastedit-hookblanket-denying non-code Edits. The PreToolUse hook that redirects Claude Code's built-inEdittool tofast_editwas firing on every file — Markdown, YAML, TOML, JSON, plain text — even though FastEdit can't parse those formats. Callers sawerror: Use fast_edit MCP tool insteadwhen trying to edit READMEs, changelogs, or config files. Fixed by whitelisting the extensions FastEdit actually handles (.py, .js/.jsx/.mjs/.cjs, .ts/.tsx, .rs, .go, .java, .c/.h, .cpp/.cc/.cxx/.hpp, .rb, .php, .swift, .kt/.kts, .cs, .ex/.exs). Non-code Edits now fall through to the built-in tool as they should.
Changed
- README install section is now PEP 668-aware. Dropped the bare
pip install fasteditsinstructions — they fail out of the box on Homebrew and distro-managed Python. New default:uv tool install 'fastedits[mlx]', with pipx and plain-venv alternatives listed underneath. tldr is correctly labeled as optional (only read/search tools need it; editing runs pure in-memory tree-sitter).
FastEdit 0.2.6
Added
- PyPI version check with MCP-aware surfacing. On the first successful
fast_edit/fast_batch_editcall in an MCP server session, if a newer release exists on PyPI, the tool response gets a one-line banner appended so the host LLM can relay the notice to the human who actually owns the environment. The model can'tpip install, so the notice targets the user through the model's next turn. Subsequent calls in the same session are silent. CLI runs get a stderr notice on exit. Checks cache for 24 h in~/.cache/fastedit/update-check.json. Silent when up-to-date, on network failure, or whenFASTEDIT_NO_UPDATE_CHECK=1is set. Uses stdliburllib— no new dependencies.
Changed
- MCP tool instructions rewritten around minimal-payload patterns. The
fast_edittool description and the top-level FastMCP instructions now lead with the six use-case patterns (add-guard / append / middle / full-replace / after / preserve_siblings) paired with the minimal snippet shape for each. The marker-position semantics (top/bottom/middle insert) and short marker forms (#...///.../…) shipped in 0.2.4 are now surfaced to every LLM that picks up the tool, not buried in the changelog. Signature auto-preserve (0.2.2) is stated explicitly:replace='name'carries the def/fn/class line, so the snippet should omit it. - README teaser updated to use the minimal form — short
#...marker, no signature line. Shows what the calling agent actually writes today, not the 0.2.0 long form.
No breaking changes — all existing payloads keep working.