bots-garden/mini-mepublic Fork
⑂ feature/ask-cli
Commits
⬇ Clone ▾
git clone https://git.rickub.com/bots-garden/mini-me.git
git clone ssh://git@rickub.com/bots-garden/mini-me.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

Download ZIP
📦 Mini Me 460cf23 · on feature/ask-cli · k33g · Sep 25, 2026
navigate-code-with-lsp.md · 110 lines · 7.3 KBmarkdown
Blame HistoryOpen raw

How to navigate and refactor code with a language server (lsp)

This guide shows how to give mm a language server, so the model can ask the compiler where a symbol is defined, who uses it, what its type is, what is wrong in a file, and rename it everywhere — instead of grepping and guessing. It assumes mm runs with a configuration file you can edit, and that at least one language server is installed.

Steps

  1. Install the server for your language and check it is on the PATH:

    Language Server Install command / args
    Go gopls go install golang.org/x/tools/gopls@latest gopls
    TypeScript, JavaScript typescript-language-server npm i -g typescript typescript-language-server typescript-language-server, ["--stdio"]
    Python pyright pip install pyright or npm i -g pyright pyright-langserver, ["--stdio"]
    Rust rust-analyzer rustup component add rust-analyzer rust-analyzer
    C, C++ clangd apt install clangd, brew install llvm clangd
    Java Eclipse JDT LS your package manager, or the jdtls release jdtls
    Zig zls zig toolchain release, or brew install zls zls

    which gopls (or the server's name) must answer. A server that is configured but not found is skipped with a warning at start-up.

  2. Declare the servers in the YAML file. The file extension picks the server:

    lsp:
      servers:
        - name: gopls
          command: gopls
          extensions: [".go"]
        - name: typescript
          command: typescript-language-server
          args: ["--stdio"]
          extensions: [".ts", ".tsx", ".js", ".jsx"]
        - name: python
          command: pyright-langserver
          args: ["--stdio"]
          extensions: [".py"]
        - name: rust
          command: rust-analyzer
          extensions: [".rs"]
    

    The shipped agent.lsp.yaml is agent.dmr.yaml with this block (gopls on, the others ready to uncomment) and a system prompt written for the tool — start from it.

  3. Tell the model when to use the tool. A small model does not reach for a new tool by itself; the system prompt has to say so. The block below is the one in agent.lsp.yaml; paste it into your own system::

    CODE NAVIGATION (lsp)
    You have an `lsp` tool: it asks the project's language server what the
    compiler knows. Its description says which file extensions it covers.
    Use it INSTEAD of grep or reading whole files for anything about a symbol:
    - "where is X defined / declared"        → action definition, symbol X
    - "who calls / uses X", "find usages"     → action references, symbol X
    - "what is the type / signature of X"    → action hover, symbol X
    - "what does this file contain"          → action symbols, path only
    - "are there errors", after every edit    → action diagnostics, path only
    - "rename X to Y everywhere"             → action rename, symbol X, new_name Y
      (rename NEEDS new_name — the new identifier; never rename with edit_file:
       {"action": "rename", "path": "main.go", "symbol": "Greet", "new_name": "Hello"})
    Point at a symbol with its NAME (the `symbol` field), exactly as written in
    the code; add `line` only to pick one occurrence among several. Do not
    guess columns. Answers are "path:line:column  <source line>": quote them,
    they are the evidence.
    Before changing a symbol used in several files, call references and say how
    many places are affected. After editing, call diagnostics on each edited
    file and fix what it reports before running tests.
    

    The lsp-navigation skill shipped in skills/ says the same at more length; with skillsDir pointing at it, the model can load it with read_skill when the task calls for it.

  4. Start mm from the project directory (the servers get it as their workspace root) and read the banner:

    [warning: lsp server ghost (no-such-lsp-server) not found on the PATH — skipped]
    mm v0.1.7 | config: agent.lsp.yaml | provider: dmr | model: … | ctx: unknown | tools: bash, read_skill, read_file, write_file, edit_file, lsp | skills: 4
    

    lsp in the tools: column means at least one server was found. Servers start on their first use, not here.

  5. Ask. A few prompts to test the six actions, on any project the server covers:

    Prompt What you should see
    Where is the function greet defined in main.go? Use lsp. 🔎 lsp: definition main.go greet then one main.go:6:6 func greet(…) line, quoted in the answer.
    Who uses greet? List every call site. 🔎 lsp: references main.go greet, N reference(s): with one line per site.
    What is the signature of greet? 🔎 lsp: hover main.go greet, the server's signature and doc comment.
    What does main.go contain? Give me its outline. 🔎 lsp: symbols main.go, one line per function, type, variable.
    Are there compile errors in main.go? 🔎 lsp: diagnostics main.go, no diagnostics: main.go is clean or one line per error.
    Rename greet to hello everywhere, then check main.go for errors. 🔎 lsp: rename main.go greet → hello, renamed to hello: N edit(s) in M file(s), then a diagnostics call.
    Add a parameter to greet. Before you edit, tell me how many places call it. references first, then edit_file, then diagnostics — the procedure the skill teaches.

    The recap line after the answer counts the calls: ⚙ 0 command(s) · 🔎 2 lsp call(s).

From an editor (ACP) and in the window mode

Nothing to configure: the same YAML applies. Navigation calls show up as tool calls of kind read, with no permission dialog; rename is kind edit and asks for permission like edit_file does (permissions: ask in the window mode). The servers start in the directory mm was launched in — for an editor, the project it opened.

Variants

  • A project in several languages. List one server per extension family; each starts only when a file of its kind is asked about.
  • A server with settings. Pass them as args (many servers read a flag or a config file in the project root); mm answers "no settings" to a server that asks for configuration, so it runs with its defaults.
  • Exact positions. When the model has an exact position — from read_file with numbered=true — it may send line and column (1-based, inside the identifier) instead of symbol. symbol is the reliable road: small models rarely count columns right.
  • No server on this machine. The tool is not declared and the banner says lsp: none of the configured servers is on the PATH — the lsp tool is not declared. The configuration can stay; the agent works without it.
  • Slow first answer. The first call for a language starts the server and lets it index the project; diagnostics may say as last published or no diagnostics received … within 3s — ask again once.

See also

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
# How to navigate and refactor code with a language server (`lsp`)

This guide shows how to give `mm` a language server, so the model can ask the compiler where a symbol is defined, who uses it, what its type is, what is wrong in a file, and rename it everywhere — instead of grepping and guessing. It assumes `mm` runs with a configuration file you can edit, and that at least one language server is installed.

## Steps

1. Install the server for your language and check it is on the `PATH`:

   | Language | Server | Install | `command` / `args` |
   |----------|--------|---------|--------------------|
   | Go | gopls | `go install golang.org/x/tools/gopls@latest` | `gopls` |
   | TypeScript, JavaScript | typescript-language-server | `npm i -g typescript typescript-language-server` | `typescript-language-server`, `["--stdio"]` |
   | Python | pyright | `pip install pyright` or `npm i -g pyright` | `pyright-langserver`, `["--stdio"]` |
   | Rust | rust-analyzer | `rustup component add rust-analyzer` | `rust-analyzer` |
   | C, C++ | clangd | `apt install clangd`, `brew install llvm` | `clangd` |
   | Java | Eclipse JDT LS | your package manager, or the jdtls release | `jdtls` |
   | Zig | zls | `zig` toolchain release, or `brew install zls` | `zls` |

   `which gopls` (or the server's name) must answer. A server that is configured but not found is skipped with a warning at start-up.

2. Declare the servers in the YAML file. The file extension picks the server:

   ```yaml
   lsp:
     servers:
       - name: gopls
         command: gopls
         extensions: [".go"]
       - name: typescript
         command: typescript-language-server
         args: ["--stdio"]
         extensions: [".ts", ".tsx", ".js", ".jsx"]
       - name: python
         command: pyright-langserver
         args: ["--stdio"]
         extensions: [".py"]
       - name: rust
         command: rust-analyzer
         extensions: [".rs"]
   ```

   The shipped `agent.lsp.yaml` is `agent.dmr.yaml` with this block (gopls on, the others ready to uncomment) and a system prompt written for the tool — start from it.

3. Tell the model when to use the tool. A small model does not reach for a new tool by itself; the system prompt has to say so. The block below is the one in `agent.lsp.yaml`; paste it into your own `system:`:

   ```text
   CODE NAVIGATION (lsp)
   You have an `lsp` tool: it asks the project's language server what the
   compiler knows. Its description says which file extensions it covers.
   Use it INSTEAD of grep or reading whole files for anything about a symbol:
   - "where is X defined / declared"        → action definition, symbol X
   - "who calls / uses X", "find usages"     → action references, symbol X
   - "what is the type / signature of X"    → action hover, symbol X
   - "what does this file contain"          → action symbols, path only
   - "are there errors", after every edit    → action diagnostics, path only
   - "rename X to Y everywhere"             → action rename, symbol X, new_name Y
     (rename NEEDS new_name — the new identifier; never rename with edit_file:
      {"action": "rename", "path": "main.go", "symbol": "Greet", "new_name": "Hello"})
   Point at a symbol with its NAME (the `symbol` field), exactly as written in
   the code; add `line` only to pick one occurrence among several. Do not
   guess columns. Answers are "path:line:column  <source line>": quote them,
   they are the evidence.
   Before changing a symbol used in several files, call references and say how
   many places are affected. After editing, call diagnostics on each edited
   file and fix what it reports before running tests.
   ```

   The `lsp-navigation` skill shipped in `skills/` says the same at more length; with `skillsDir` pointing at it, the model can load it with `read_skill` when the task calls for it.

4. Start `mm` from the project directory (the servers get it as their workspace root) and read the banner:

   ```text
   [warning: lsp server ghost (no-such-lsp-server) not found on the PATH — skipped]
   mm v0.1.7 | config: agent.lsp.yaml | provider: dmr | model: … | ctx: unknown | tools: bash, read_skill, read_file, write_file, edit_file, lsp | skills: 4
   ```

   `lsp` in the `tools:` column means at least one server was found. Servers start on their first use, not here.

5. Ask. A few prompts to test the six actions, on any project the server covers:

   | Prompt | What you should see |
   |--------|---------------------|
   | `Where is the function greet defined in main.go? Use lsp.` | `🔎 lsp: definition main.go greet` then one `main.go:6:6  func greet(…)` line, quoted in the answer. |
   | `Who uses greet? List every call site.` | `🔎 lsp: references main.go greet`, `N reference(s):` with one line per site. |
   | `What is the signature of greet?` | `🔎 lsp: hover main.go greet`, the server's signature and doc comment. |
   | `What does main.go contain? Give me its outline.` | `🔎 lsp: symbols main.go`, one line per function, type, variable. |
   | `Are there compile errors in main.go?` | `🔎 lsp: diagnostics main.go`, `no diagnostics: main.go is clean` or one line per error. |
   | `Rename greet to hello everywhere, then check main.go for errors.` | `🔎 lsp: rename main.go greet → hello`, `renamed to hello: N edit(s) in M file(s)`, then a `diagnostics` call. |
   | `Add a parameter to greet. Before you edit, tell me how many places call it.` | `references` first, then `edit_file`, then `diagnostics` — the procedure the skill teaches. |

   The recap line after the answer counts the calls: `⚙ 0 command(s) · 🔎 2 lsp call(s)`.

## From an editor (ACP) and in the window mode

Nothing to configure: the same YAML applies. Navigation calls show up as tool calls of kind `read`, with no permission dialog; `rename` is kind `edit` and asks for permission like `edit_file` does (`permissions: ask` in the window mode). The servers start in the directory `mm` was launched in — for an editor, the project it opened.

## Variants

- **A project in several languages.** List one server per extension family; each starts only when a file of its kind is asked about.
- **A server with settings.** Pass them as `args` (many servers read a flag or a config file in the project root); `mm` answers "no settings" to a server that asks for configuration, so it runs with its defaults.
- **Exact positions.** When the model has an exact position — from `read_file` with `numbered=true` — it may send `line` and `column` (1-based, inside the identifier) instead of `symbol`. `symbol` is the reliable road: small models rarely count columns right.
- **No server on this machine.** The tool is not declared and the banner says `lsp: none of the configured servers is on the PATH — the lsp tool is not declared`. The configuration can stay; the agent works without it.
- **Slow first answer.** The first call for a language starts the server and lets it index the project; `diagnostics` may say `as last published` or `no diagnostics received … within 3s` — ask again once.

## See also

- [Built-in tools reference](../reference/tools.md) — the `lsp` fields, outputs and failures
- [Configuration reference](../reference/configuration.md) — the `lsp.servers` keys and validation errors
- [Why one `lsp` tool, and why a `symbol` field](../explanation/lsp-tool.md)
- [Add a skill](add-a-skill.md) — the `lsp-navigation` skill's format