--- name: lsp-extract-function description: Extract a selected code block into a named function. Primary path uses the language server's extract-function code action; falls back to manual extraction when no code action is available. Validates captured variables, scope shadowing, and compilation after extraction. argument-hint: "[file-path] [start-line] [end-line] [new-function-name]" user-invocable: true allowed-tools: mcp__lsp__list_symbols mcp__lsp__suggest_fixes mcp__lsp__execute_command mcp__lsp__apply_edit mcp__lsp__get_diagnostics mcp__lsp__open_document mcp__lsp__format_document mcp__lsp__get_server_capabilities license: MIT compatibility: Requires the agent-lsp MCP server (github.com/blackwell-systems/agent-lsp) metadata: required-capabilities: codeActionProvider optional-capabilities: documentFormattingProvider documentSymbolProvider --- > Requires the agent-lsp MCP server. # lsp-extract-function: Extract Code Block into a Named Function **This skill RESTRUCTURES existing code** — it takes code that already exists and moves it into a new function. This is distinct from `/lsp-generate`, which creates NEW code that does not yet exist (stubs, mocks, interface implementations). Use this skill when the code is already written; use `/lsp-generate` when you need to generate code from scratch. **Invocation:** User provides `file_path` (absolute path), `start_line` and `end_line` (1-indexed range), and `new_function_name` (desired name for the extracted function). --- ## Prerequisites If LSP is not yet initialized, call `mcp__lsp__start_lsp` with the workspace root first. Auto-inference applies when file paths are provided, but an explicit start is required when switching workspaces. --- ## Step 1 — Get context (document symbols) Call `mcp__lsp__open_document` to open the file, then call `mcp__lsp__list_symbols` to understand the containing function and scope: ``` mcp__lsp__open_document({ "file_path": "" }) mcp__lsp__list_symbols({ "file_path": "" }) ``` This establishes: - Which function contains the selection - Whether `new_function_name` already exists in the file (name collision check) **Mandatory name collision check:** If `new_function_name` already exists as a symbol in the document symbols list, report the conflict and stop immediately: > Cannot extract: function `new_function_name` already exists in this file. > Choose a different name and retry. --- ## Step 2 — Check server capabilities Call `mcp__lsp__get_server_capabilities` to understand what the language server supports: ``` mcp__lsp__get_server_capabilities({}) ``` Check for `codeActionProvider` in the response. Note whether `execute_command` is listed in `executeCommandProvider.commands`. This determines whether the primary path (Step 3) is available. --- ## Step 3 — Primary path: LSP code action Call `mcp__lsp__suggest_fixes` with the selection range: ``` mcp__lsp__suggest_fixes({ "file_path": "", "start_line": N, "start_column": 1, "end_line": M, "end_column": 999 }) ``` Filter the returned actions for extract-function actions: include any action whose `kind` contains `"refactor.extract"` OR whose `title` contains both "Extract" and "function" (case-insensitive). **If an extract-function action is found:** - Display the action title to the user - If the action proposes a different name than `new_function_name`, ask for confirmation before proceeding - Execute via `mcp__lsp__execute_command` if the action has a `command` field: ``` mcp__lsp__execute_command({ "command": "", "arguments": }) ``` - OR apply directly via `mcp__lsp__apply_edit` if the action has an `edit` field: ``` mcp__lsp__apply_edit({ "workspace_edit": }) ``` - Skip to Step 5 after applying. **If no extract-function action is found:** fall through to Step 4 (manual fallback). --- ## Step 4 — Manual fallback When no code action is available, perform manual extraction: ### a) Analyze the selection Read the selected lines (`start_line` through `end_line`) and identify: - **Parameters:** Variables used inside the selection that are declared outside (captured from outer scope — must become function parameters) - **Return values:** Variables declared inside the selection that are used outside (must be returned from the extracted function) - **Early returns:** Return statements inside the selection (the extracted function must wrap these) ### b) Construct and confirm the proposed signature Build the extracted function signature based on the captured variables analysis. Display the proposed signature to the user before writing: > Proposed extraction: > ``` > func new_function_name(param1 Type1, param2 Type2) (ReturnType, error) { > // selected lines > } > ``` > Proceed with this signature? [y/n] Wait for user confirmation before applying any edit. ### c) Apply the extraction (order matters) Apply edits sequentially — do NOT batch edits from different line regions into a single `apply_edit` call: 1. **First:** Replace the selected lines with a call to the new function: ``` mcp__lsp__apply_edit({ "workspace_edit": { "changes": { "": [{ "range": { "start": { "line": start_line-1, "character": 0 }, "end": { "line": end_line, "character": 0 } }, "newText": " result := new_function_name(args...)\n" }] } } }) ``` 2. **Second:** Insert the new function definition after the containing function's closing brace: ``` mcp__lsp__apply_edit({ "workspace_edit": { "changes": { "": [{ "range": { "start": { "line": insert_line, "character": 0 }, "end": { "line": insert_line, "character": 0 } }, "newText": "\nfunc new_function_name(params) ReturnType {\n ...\n}\n" }] } } }) ``` Apply call-site replacement first, then insert the new function. This order preserves line numbers during editing: replacing call site does not shift the insertion point for the new function definition. --- ## Step 5 — Validate After extraction via either path: ### 1. Check diagnostics ``` mcp__lsp__get_diagnostics({ "file_path": "" }) ``` If errors are reported, display them with the table of common causes below. ### 2. Common post-extraction errors | Error type | Likely cause | Fix | |------------|--------------|-----| | Undefined variable | Captured var not passed as parameter | Add parameter | | Type mismatch | Return type inferred incorrectly | Adjust return type in signature | | Name shadows outer | New function name matches outer scope | Choose different name | | Unused variable | Return value not captured at call site | Add variable at call site | ### 3. Format the document ``` mcp__lsp__format_document({ "file_path": "" }) ``` This cleans up indentation introduced by the extraction. --- ## Output Format After completing extraction, display: ``` ## Extraction Summary - File: path/to/file.go - Extracted: lines N–M - New function: new_function_name - Path used: LSP code action / Manual fallback - Post-extraction errors: 0 ``` Follow with the Diagnostic Summary if any errors changed (format in [references/patterns.md](references/patterns.md)). --- ## Language-Specific Notes - **Go:** gopls may offer "Extract function" in code actions for selection ranges. Check code actions first; gopls support varies by version. - **TypeScript/JavaScript:** typescript-language-server may offer "Extract to function in global scope" or "Extract to inner function" — filter for these titles in Step 3. - **Python:** pylsp and pyright-langserver typically do NOT offer extract-function code actions. Manual fallback (Step 4) is required for Python files.