feat: add lsp_format_document tool - #1194
will-lamerton merged 5 commits into
Conversation
Expose the existing LSP formatDocument API as an agent-callable tool so formatting can use the connected language server instead of guessing a CLI.
will-lamerton
left a comment
There was a problem hiding this comment.
Nice work - the integration is more thorough than most tool-addition PRs. Plan-mode exclusion is wired in code and not just docs, checkpointing works for free via isReadOnly, and the approval matrix is covered across all four modes. A few things to fix before merge.
1. Hardcoded 2-space formatting will mangle tab-indented projects. formatFileWithLsp calls manager.formatDocument(absPath) with no options, so LSPClient.formatDocument falls back to tabSize: 2, insertSpaces: true (source/lsp/lsp-client.ts:270). Servers that honour FormattingOptions (typescript-language-server does) will convert tabs to spaces. This repo is Biome-formatted with tabs, so running the tool on nanocoder's own source reformats it against project style - which contradicts the tool description's promise of "project-style formatting". Please read .editorconfig / the project formatter config, or at minimum expose tabSize / insertSpaces in the input schema.
2. "No formatting changes needed" is returned when the server cannot format at all. LSPClient.formatDocument returns [] when documentFormattingProvider is absent (source/lsp/lsp-client.ts:263), and formatFileWithLsp maps empty edits to No formatting changes needed for X. The agent then concludes the file is clean when nothing was attempted. Please distinguish "server has no formatting support" from a genuine no-op.
3. positionToOffset does not clamp character to the end of its line. It clamps only to text.length, so an end position whose character overshoots the line length swallows every following line. On "ab\ncd\n", an end of {line: 0, character: 50} resolves to offset 6 (end of file) rather than 2 (end of line 0). Some servers emit large sentinel characters for "to end of line". Worth a test case too. Separately, the doc comment calls the result a "UTF-16-agnostic byte offset" - it is a UTF-16 code-unit offset, which is correct for LSP's default encoding, but the comment states the opposite of what the code does.
4. Missing changeset. New user-facing tool, so it needs one (pnpm changeset). Note the package name must be @nanocollective/nanocoder.
5. --plain --output-format json will not report formatted files. source/plain/shell.ts:317 lists ['write_file', 'string_replace', 'diff_edit'] as the mutating tools that populate filesChanged. lsp_format_document mutates a file and takes a path arg but is missing, so its edits silently drop out of the report.
Minor, non-blocking: FILE_ARG_TOOLS in source/acp/acp-timeline.ts:8 does not include the tool, so ACP checkpoints fall back to an 'opaque' git-status scan instead of a targeted path.
Honour .editorconfig (and optional tabSize/insertSpaces), distinguish unsupported formatting from no-op edits, clamp LSP positions to line end, report the tool in plain JSON filesChanged, and add a changeset.
Bring in schema/sandbox CI prerequisites from main, and replace RegExp-based editorconfig globs with a star matcher so Semgrep stays clean.
|
Great work @Dhirenderchoudhary :) |
Description
Adds an agent-callable
lsp_format_documenttool that uses the existingLSPManager.formatDocument()API to format a file via the connected language server, apply the returned edits, and write the result to disk. This replaces the need to guess a formatter CLI throughexecute_bash.Also wires approval, plan-mode exclusion, ACP tool kind, compact result display, and a short docs note.
Closes #1171
Type of Change
Changeset
pnpm changeset) describing this change for the changelogDocs-only or internal chores need no changeset (or run
pnpm changeset --emptyto note that intentionally).Testing
Automated Tests
.spec.ts/tsxfilespnpm test:allcompletes successfully)Manual Testing
Checklist