diff --git a/README.md b/README.md index bf423238..a1d172a9 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Originally forked from [agentic-labs/lsproxy](https://github.com/agentic-labs/lsproxy). -[Reference Documentation](https://docs.nuanced.dev/lsp/overview) +[Reference Documentation](docs/overview.md) diff --git a/clients/typescript/README.md b/clients/typescript/README.md index 738756d7..5512be9e 100644 --- a/clients/typescript/README.md +++ b/clients/typescript/README.md @@ -130,7 +130,7 @@ We aim to support the Ruby versioned released in the last year. Below is a high-level overview of available API (arguments/options omitted here for brevity). -**Lifecycle [(reference docs)](https://docs.nuanced.dev/lsp/api-reference/container-lifecycle):** +**Lifecycle [(reference docs)](../../docs/api-reference/container-lifecycle.md):** - `up` – Start the Nuanced LSP Docker container - `down` – Stop the container @@ -139,16 +139,16 @@ Below is a high-level overview of available API (arguments/options omitted here - `run` – Run a script inside the container - `status` – Show Docker lifecycle status -**System [(reference docs)](https://docs.nuanced.dev/lsp/api-reference/system):** +**System [(reference docs)](../../docs/api-reference/system.md):** - `health` – Check server health and language readiness flags -**Workspace [(reference docs)](https://docs.nuanced.dev/lsp/api-reference/workspace):** +**Workspace [(reference docs)](../../docs/api-reference/workspace.md):** - `list-files` – List files detected in the workspace - `read-source` – Read file contents (optionally a range) -**Symbols [(reference docs)](https://docs.nuanced.dev/lsp/api-reference/symbols):** +**Symbols [(reference docs)](../../docs/api-reference/symbols.md):** - `definitions-in-file` – List symbol definitions in a file - `find-definition` – Find the definition at a given position diff --git a/docs/api-reference/container-lifecycle.md b/docs/api-reference/container-lifecycle.md new file mode 100644 index 00000000..a1428431 --- /dev/null +++ b/docs/api-reference/container-lifecycle.md @@ -0,0 +1,482 @@ +# Container Lifecycle API Reference + +Nuanced LSP runs as a Docker container. The following outlines the API for starting, stopping, modifying the container environment, viewing logs, and managing the Nuanced LSP Docker container and image. + +| Command | Description | +|---------------------------|-------------------------------------------------------------------| +| [Up](#up) | Start the container. | +| [Down](#down) | Stop the container. | +| [Logs](#logs) | View the container logs. | +| [Pull](#pull) | Pull the `nuanced-lsproxy` image. | +| [Run](#run) | Run a script within the container environment. | +| [Status](#status) | Check the status of the container. | + +> **Tip:** The `Pull` command is useful for pre-pulling the `nuanced-lsproxy` image for faster startup times, or to update to the latest version. + +## Up + +Starts the Nuanced LSP container. + +### CLI + +``` +> nuanced-lsp up /path/to/workspace +``` + +#### Arguments + +| Argument | Description | Required | +|-------------|----------------------------------------------------------------------|----------| +| `workspace` | Path to the host workspace to mount within the Nuanced LSP container | Yes | + +#### CLI options + +| Option | Description | Default | +|---------------------------|-----------------------------------------------------------------------------|-------------------------------------------------------------| +| `--host-port ` | Host port to bind to container process | `4444` | +| `--bind-host ` | Host/IP to bind (e.g. `127.0.0.1`, `0.0.0.0`, `192.168.1.10`) | `"127.0.0.1"` | +| `--image ` | Docker image to use for standing up the container process | `"ghcr.io/nuanced-dev/nuanced-lsproxy:"` | +| `--container-name ` | Container name | `"nuanced-lsp"` | +| `--timeout ` | Health check poll loop timeout seconds (`<=0` to skip) | `120` | +| `--sudo` | Docker commands are executed with `sudo` | Docker commands are executed without sudo | +| `--stream` | Process stdout, stderr, and container logs are streamed to the CLI's stdout | Process stdout, stderr, and container logs are not streamed | +| `--ro` | Mount workspace as read-only | Mount workspace is read-write | +| `--json` | Output machine readable JSON | Text output | +| `--debug` | Enable debug log level for container process | Process log level is info | + +#### Result + +Success result confirms the container is running and bound to the specified host and port: + +``` +Nuanced LSP container 'nuanced-lsp' bound at 127.0.0.1:4444 +``` + +### TypeScript + +```typescript +import { NuancedLspClient } from '@nuanced-dev/nuanced-lsp'; + +const lsp = new NuancedLspClient(); + +await lsp.up({ + workspace: "/path/to/workspace", +}); +``` + +#### Constructor options + +| Option | Type | Default / Notes | Description | +|----------------|-----------|-----------------------------------------------|-------------------------------------------------------------------------------| +| `containerName`| `string` | `"nuanced-lsp"` | Docker container name. | +| `lsProxyUrl` | `string` | `"http://127.0.0.1"` | Base URL used to reach Nuanced LSP. Trailing `/` is trimmed. | +| `lsProxyPort` | `number` | `4444` | Port used to reach LSProxy. | +| `timeoutSecs` | `number` | `120` | Overall timeout for lifecycle operations (seconds). | +| `retries` | `number` | `3` | Number of retry attempts for HTTP requests to account for transient failures. | +| `sudo` | `boolean` | `false` | Docker commands are executed with `sudo`. | +| `json` | `boolean` | `false` | Output machine readable JSON | + +#### Signature + +```typescript +up(opts: { + workspace: string; + mountDir?: string; + image?: string; + timeout?: number; + stream?: boolean; + ro?: boolean; + bindHost?: string; + json?: boolean; + debug?: boolean; +}): Promise; +``` + +| Option | Type | Default / Notes | Description | +|--------------|-----------|---------------------------------------------------|--------------------------------------------------------------| +| `workspace` | `string` | **required** | Host workspace directory to mount. | +| `mountDir` | `string` | `"/workspace"` | Path inside the container where the workspace is mounted. | +| `image` | `string` | `"ghcr.io/nuanced-dev/nuanced-lsproxy:"` | Docker image to use for starting the container. | +| `timeout` | `number` | `120` | Health check poll loop timeout (seconds). `<=0` to skip. | +| `stream` | `boolean` | `false` | Stream process stdout, stderr, and container logs on startup.| +| `ro` | `boolean` | `false` | Mount workspace as read-only (default is read-write). | +| `bindHost` | `string` | `"127.0.0.1"` | Host/IP to bind (e.g. `127.0.0.1`, `0.0.0.0`). | +| `json` | `boolean` | `false` | Output machine readable JSON | +| `debug` | `boolean` | `false` | Enable debug log level for container process | + +#### Return type + +```typescript +interface UpResult { + host_port: number; + base_url: string; +} +``` + +## Down + +Stops the Nuanced LSP container. + +### CLI + +``` +> nuanced-lsp down +``` + +#### CLI options + +| Option | Description | Default | +|--------------------------|----------------------------------------------|-------------------------------------------| +| `--container-name `| Container name | `"nuanced-lsp"` | +| `--timeout ` | Timeout seconds (`<=0` to skip) | `120` | +| `--sudo` | Docker commands are executed with `sudo` | Docker commands are executed without sudo | +| `--json` | Output machine readable JSON | Text output | + +#### Result + +Success result confirms the container was stopped: + +``` +Nuanced LSP Stopped container 'nuanced-lsp'. +``` + +### TypeScript + +```typescript +import { NuancedLspClient } from '@nuanced-dev/nuanced-lsp'; + +const lsp = new NuancedLspClient(); + +const result = await lsp.down(); +``` + +#### Signature + +```typescript +down(): Promise; +``` + +#### Return type + +```typescript +export interface CommandResult { + ok: boolean; + error?: string; +} +``` + +- `ok: true` — container stopped successfully +- `ok: false` + `error` — container stop failed + +## Logs + +Print logs from the Nuanced LSP container. + +### CLI + +``` +> nuanced-lsp logs +``` + +#### CLI options + +| Option | Description | Default | +|--------------------------|-----------------------------------------------------------------------|-------------------------------------------------------------| +| `--container-name `| Container name | `"nuanced-lsp"` | +| `--timeout ` | Timeout seconds (`<=0` to skip) | `120` | +| `--sudo` | Docker commands are executed with `sudo` | Docker commands are executed without sudo | +| `--stream` | Process stdout, stderr, and container logs are streamed to the CLI's stdout | Process stdout, stderr, and container logs are not streamed | +| `--since ` | Only show logs since (e.g. `10s`, `5m`, or RFC3339 timestamp) | — | +| `--tail ` | Show last N lines, or `all` for full history | — | +| `--json` | Output machine readable JSON | Text output | + +#### Result + +On success, logs are printed to stdout. + +Example: +``` +[2025-09-02T17:02:11Z] Starting Nuanced LSP... +[2025-09-02T17:02:12Z] Listening on 127.0.0.1:4444 +``` + +If the container is not running, an error is shown. + +### TypeScript + +```typescript +import { NuancedLspClient } from '@nuanced-dev/nuanced-lsp'; + +const lsp = new NuancedLspClient(); + +const result = await lsp.logs({ + since: "5m", + tail: 100, + stream: false, +}); +``` + +#### Signature + +```typescript +logs(opts?: { + since?: string; + tail?: number | "all"; + stream?: boolean; +}): Promise; +``` + +#### Return type + +```typescript +export interface LogsResult extends CommandResult { + ok: boolean; + error?: string; // present when ok === false + output?: string; // present when ok === true +} +``` + +#### Example result + +```json +{ + "ok": true, + "output": "2025-09-02T20:53:06.552155919Z 2025-09-02T20:53:06.552018Z INFO lsproxy: Starting on port 4444\n2025-09-02T20..." +} +``` + +## Pull + +Pull the Nuanced LSP image. + +### CLI + +``` +> nuanced-lsp pull +``` + +#### CLI options + +| Option | Description | Default | +|-------------------|----------------------------------------------------|-------------------------------------------------------| +| `--image ` | Nuanced LSP Docker image to pull | `"ghcr.io/nuanced-dev/nuanced-lsproxy:"` | +| `--sudo` | Docker commands are executed with `sudo` | Docker commands are executed without sudo | +| `--stream` | Process stdout, stderr, and container logs are streamed to the CLI's stdout | Process stdout, stderr, and container logs are not streamed | +| `--json` | Output machine readable JSON | Text output | + +#### Result + +On success, the image is pulled and confirmation is shown. + +Example: +``` +Pulled image ghcr.io/nuanced-dev/nuanced-lsproxy:0.3.8 +``` + +### TypeScript + +```typescript +import { NuancedLSP } from '@nuanced-dev/nuanced-lsp'; + +const lsp = new NuancedLSP(); + +const image = "ghcr.io/nuanced-dev/nuanced-lsproxy:0.3.8"; +const stream = true; + +const result = await lsp.pull( + image, + stream +); +``` + +#### Signature + +```typescript +pull( + image?: string, + stream?: boolean +): Promise; +``` + +#### Return type + +```typescript +export interface PullResult { + ok: boolean; + error?: string; + image?: string; // present when ok === true + output?: string; // raw docker pull logs if available +} +``` + +#### Example result + +```json +{ + "ok": true, + "image": "ghcr.io/nuanced-dev/nuanced-lsproxy:0.3.8", + "output": "Digest: sha256:abc123..." +} +``` + +- `ok: true` — `image` and optional `output` returned +- `ok: false` + `error` — pull failed + +## Run + +Run a script inside the Nuanced LSP container. + +> **Note:** The script is copied to the container and is executed within the container as the root user. + +### CLI + +``` +> nuanced-lsp run ./scripts/setup.sh +``` + +#### Arguments + +| Argument | Description | Required | +|-------------|--------------------------------------------|----------| +| `script` | Path to a script to copy to the container and execute | Yes | + +#### CLI options + +| Option | Description | Default | +|--------------------------|----------------------------------------------------------|-------------------------------------------------------------| +| `--container-name `| Container name | `"nuanced-lsp"` | +| `--timeout ` | Timeout seconds (`<=0` to skip) | `120` | +| `--sudo` | Docker commands are executed with `sudo` | Docker commands are executed without sudo | +| `--stream` | Process stdout, stderr, and container logs are streamed to the CLI's stdout | Process stdout, stderr, and container logs are not streamed | +| `--json` | Output machine readable JSON | Text output | + +#### Result + +On success: +``` +Script executed successfully in nuanced-lsp. +``` + +On failure, the error is printed, and the process exits with exit code 1. + +### TypeScript + +```typescript +import { NuancedLSP } from '@nuanced-dev/nuanced-lsp'; + +const lsp = new NuancedLspClient(); + +const script = "scripts/setup.sh"; + +// Run a script and stream the process stdout / stderr inside the container +const result = await lsp.run(script, { stream: true }); +``` + +#### Signature + +```typescript +run( + script: string, + opts?: { + stream: boolean; + } +): Promise; +``` + +#### Return type + +```typescript +export interface CommandResult { + ok: boolean; + error?: string; +} +``` + +#### Example result + +```json +{ + "ok": true +} +``` + +- `ok: true` — script executed successfully +- `ok: false` + `error` — script failed or container not running + +## Status + +Show the container lifecycle status of the Nuanced LSP container. + +This command does not perform a `/health` probe. It only checks Docker's view of the container. + +### CLI + +``` +> nuanced-lsp status +``` + +#### CLI options + +| Option | Description | Default | +|--------------------------|------------------------------------------|-----------------| +| `--container-name `| Container name | `"nuanced-lsp"` | +| `--timeout ` | Timeout seconds (`<=0` to skip) | `120` | +| `--sudo` | Docker commands are executed with `sudo` | Docker commands are executed without sudo | +| `--json` | Output machine readable JSON | Text output | + +#### Result + +**Default output:** +``` +Container: nuanced-lsp +Running: yes +Docker: Up 29 minutes +``` + +**With `--json`:** +```json +{ + "container_name": "nuanced-lsp", + "running": true, + "container_status": "Up 28 minutes" +} +``` + +### TypeScript + +```typescript +import { NuancedLSP } from '@nuanced-dev/nuanced-lsp'; + +const lsp = new NuancedLSP(); + +const status = await lsp.status(); +``` + +#### Signature + +```typescript +status(): Promise; +``` + +#### Return type + +```typescript +export interface Status { + container_name: string; + running: boolean; + container_status?: string | null; // e.g. "Up 10 seconds" +} +``` + +#### Example result + +```json +{ + "container_name": "nuanced-lsp", + "running": true, + "container_status": "Up 28 minutes" +} +``` + +- `running: true` — container is active +- `running: false` — container stopped or not found diff --git a/docs/api-reference/symbols.md b/docs/api-reference/symbols.md new file mode 100644 index 00000000..6a02cb44 --- /dev/null +++ b/docs/api-reference/symbols.md @@ -0,0 +1,761 @@ +# Symbols API Reference + +Retrieve definitions, identifiers, references, and related graph data. + +Nuanced LSP provides symbol-level APIs for exploring your codebase. +These commands let you list definitions, locate identifiers, and find references or symbol relationships within the workspace. + +| Command | Description | +|------------------------------------------------------|-----------------------------------------------------------------------------| +| [Definitions in File](#definitions-in-file) | List all symbol definitions in a specific file. | +| [Find Definition](#find-definition) | Find the definition of the identifier at a given position. | +| [Find Identifier](#find-identifier) | Find identifiers by name within a file. | +| [Find Referenced Symbols](#find-referenced-symbols) | Find symbols referenced by the identifier at a given position. | +| [Find References](#find-references) | Find all references to the identifier at a given position. | + +> **Tip:** These commands use **0-indexed positions** in the format `line:char` (e.g. `0:0` for the first character of the first line). + +## Definitions in File + +List all symbol definitions within a single file. + +> **Note:** The file path argument should always be relative to the workspace root. + +### CLI + +``` +> nuanced-lsp definitions-in-file path/to/file +``` + +#### Arguments + +| Argument | Description | Required | +|-------------|--------------------------------------------|----------| +| `file` | Path relative to workspace root | Yes | + +#### CLI options + +| Option | Description | Default | +|-------------------|---------------------------------|---------------------| +| `--lsp-url ` | Nuanced LSP base URL | `"http://127.0.0.1"`| +| `--lsp-port ` | Port for Nuanced LSP | `4444` | +| `--timeout ` | Timeout seconds (`<=0` to skip) | `120` | +| `--json` | Output machine readable JSON | Text output | + +#### Result + +Returns JSON array of definitions: + +```json +[ + { + "name": "registerTools", + "kind": "function", + "identifier_position": { + "path": "src/tools/index.ts", + "position": { "line": 4, "character": 16 } + }, + "file_range": { + "path": "src/tools/index.ts", + "range": { + "start": { "line": 4, "character": 0 }, + "end": { "line": 7, "character": 1 } + } + } + } +] +``` + +### TypeScript + +```typescript +import { NuancedLspClient } from '@nuanced-dev/nuanced-lsp'; + +const lsp = new NuancedLspClient(); + +const filePath = "path/to/file"; +const defs = await lsp.definitionsInFile(filePath); +``` + +#### Signature + +```typescript +definitionsInFile( + filePath: string, + timeoutSecs?: number +): Promise; +``` + +#### Types + +```typescript +export type DefinitionsInFileResult = DefinitionInFile[]; + +export interface DefinitionInFile { + file_range: FileRange; + identifier_position: IdentifierPosition; + kind: string; + name: string; +} + +export interface FileRange { path: string; range: Range } + +export interface Range { start: Position; end: Position } + +export interface Position { line: number; character: number } + +export interface IdentifierPosition { path: string; position: Position } +``` + +#### Example result + +```json +[ + { + "name": "registerTools", + "kind": "function", + "identifier_position": { + "path": "src/tools/index.ts", + "position": { "line": 4, "character": 16 } + }, + "file_range": { + "path": "src/tools/index.ts", + "range": { + "start": { "line": 4, "character": 0 }, + "end": { "line": 7, "character": 1 } + } + } + } +] +``` + +## Find Definition + +Find the definition of the identifier at a given position. + +> **Note:** Positions are always **0-indexed** in the format `line:char` (e.g., `0:0` for the first character of the first line). + +### CLI + +``` +> nuanced-lsp find-definition path/to/file 4:16 +``` + +#### Arguments + +| Argument | Description | Required | +|-------------|--------------------------------------------|----------| +| `file` | Path relative to workspace root | Yes | +| `position` | Position in `line:char` format (0-indexed) | Yes | + +#### CLI options + +| Option | Description | Default | +|-----------------------|---------------------------------|---------------------| +| `--lsp-url ` | Nuanced LSP base URL | `"http://127.0.0.1"`| +| `--lsp-port ` | Port for Nuanced LSP | `4444` | +| `--timeout ` | Timeout seconds (`<=0` to skip) | `120` | +| `--json` | Output machine readable JSON | Text output | + +#### Result + +Returns JSON matching `FindDefinitionResult`: + +```json +{ + "raw_response": [ + { + "range": { + "end": { "character": 29, "line": 4 }, + "start": { "character": 16, "line": 4 } + }, + "uri": "file:///workspace/src/tools/index.ts" + } + ], + "definitions": [ + { + "path": "src/tools/index.ts", + "position": { "line": 4, "character": 16 } + } + ], + "source_code_context": [ + { + "file_range": { + "path": "src/tools/index.ts", + "range": { + "start": { "line": 4, "character": 7 }, + "end": { "line": 7, "character": 1 } + } + }, + "source_code": "function registerTools(server: McpServer) {\n registerInitTool(server);\n registerEnrichTool(server);\n}" + } + ], + "selected_identifier": { + "name": "registerTools", + "file_range": { + "path": "src/tools/index.ts", + "range": { + "start": { "line": 4, "character": 16 }, + "end": { "line": 4, "character": 29 } + } + }, + "kind": null + } +} +``` + +### TypeScript + +```typescript +import { NuancedLspClient } from '@nuanced-dev/nuanced-lsp'; + +const lsp = new NuancedLspClient(); + +const filePath = "path/to/file"; +const line = 4; +const char = 16; + +const result = await lsp.findDefinition("path/to/file", line, char); +``` + +#### Signature + +```typescript +findDefinition( + filePath: string, + line: number, + character: number, + timeoutSecs?: number +): Promise; +``` + +#### Types + +```typescript +export interface FindDefinitionResult { + definitions: DefinitionLocation[]; + selected_identifier: SelectedIdentifier; + raw_response: unknown; + source_code_context: ContextSnippet[] | null; +} + +export interface DefinitionLocation { + path: string; + position: Position; +} + +export interface ContextSnippet { + file_range: FileRange; + source_code: string; +} + +export interface SelectedIdentifier { + file_range: FileRange; + kind: string | null; + name: string; +} + +export interface FileRange { path: string; range: Range } + +export interface Range { start: Position; end: Position } + +export interface Position { line: number; character: number } +``` + +#### Example result + +```json +{ + "definitions": [ + { "path": "src/tools/index.ts", "position": { "line": 4, "character": 16 } } + ], + "selected_identifier": { + "name": "registerTools", + "file_range": { + "path": "src/tools/index.ts", + "range": { + "start": { "line": 4, "character": 16 }, + "end": { "line": 4, "character": 29 } + } + }, + "kind": null + }, + "raw_response": {}, + "source_code_context": null +} +``` + +## Find Identifier + +Find identifiers by name within a file. Optionally provide a seed position. + +> **Note:** You can call this endpoint with or without a position to affect its behavior: +> * Without a position returns all matching identifiers in the file. +> * With a position scopes the identifier returned to the identifier and position provided, or if none are found at the precise position, the 3 closest (by position) identifiers matching the name. + +### CLI + +``` +> nuanced-lsp find-identifier path/to/file registerTools +``` + +With a seed position: +``` +> nuanced-lsp find-identifier path/to/file registerTools --position 15:29 +``` + +#### Arguments + +| Argument | Description | Required | +|-------------|--------------------------------------------|----------| +| `file` | Path relative to workspace root | Yes | +| `name` | Identifier name to search for | Yes | + +#### CLI options + +| Option | Description | Default | +|-------------------------|----------------------------------------------------------|---------------------| +| `--position `| Optional seed position (0-indexed `line:char`) | — | +| `--lsp-url ` | Nuanced LSP base URL | `"http://127.0.0.1"`| +| `--lsp-port ` | Port for Nuanced LSP | `4444` | +| `--timeout ` | Timeout seconds (`<=0` to skip) | `120` | +| `--json` | Output machine readable JSON | Text output | + +#### Result + +Returns JSON matching `FindIdentifierResult`: + +**Without position** +```json +{ + "identifiers": [ + { + "name": "registerTools", + "file_range": { + "path": "src/tools/index.ts", + "range": { + "start": { "line": 4, "character": 16 }, + "end": { "line": 4, "character": 29 } + } + }, + "kind": null + } + ] +} +``` + +**With position** +```json +{ + "identifiers": [ + { + "name": "registerTools", + "file_range": { + "path": "src/tools/index.ts", + "range": { + "start": { "line": 4, "character": 16 }, + "end": { "line": 4, "character": 29 } + } + }, + "kind": null + } + ] +} +``` + +### TypeScript + +```typescript +import { NuancedLspClient } from '@nuanced-dev/nuanced-lsp'; + +const lsp = new NuancedLspClient(); + +const filePath = "path/to/file"; +const identifierName = "registerTools"; + +// Without position +const res1 = await lsp.findIdentifier(filePath, identifierName); + +// With position +const position = { line: 15, character: 29 }; +const res2 = await lsp.findIdentifier(filePath, identifierName, position); +``` + +#### Signature + +```typescript +findIdentifier( + filePath: string, + name: string, + position?: { line: number; character: number } | null, + timeoutSecs?: number +): Promise; +``` + +#### Types + +```typescript +export interface FindIdentifierResult { + identifiers: Identifier[]; +} + +export interface Identifier { + file_range: FileRange; + kind: string | null; + name: string; +} + +export interface FileRange { path: string; range: Range } + +export interface Range { start: Position; end: Position } + +export interface Position { line: number; character: number } +``` + +#### Example result + +```json +{ + "identifiers": [ + { + "name": "registerTools", + "file_range": { + "path": "src/tools/index.ts", + "range": { + "start": { "line": 4, "character": 16 }, + "end": { "line": 4, "character": 29 } + } + }, + "kind": null + } + ] +} +``` + +## Find Referenced Symbols + +For a target symbol, returns all symbols referenced within the target's implementation. + +The referenced symbols are categorized into the following: + +* **Workspace symbols**: symbols found in the workspace along with their definitions. +* **External symbols**: symbols from external libraries or built-in functions. +* **Not found symbols**: symbols Nuanced LSP was unable to resolve. + +> **Note:** The full scan option uses more permissive rules for finding referenced symbols. Depending on the LSP server, may use type hints and chained indirection. + +### CLI + +``` +> nuanced-lsp find-referenced-symbols --full-scan path/to/file 4:16 +``` + +#### Arguments and options + +| Option | Description | Default | +|--------------------|----------------------------------------------------|---------------------| +| `file` | Path relative to workspace root | — | +| `position` | Position in `line:char` format (0-indexed) | — | +| `--full-scan` | Perform a broader workspace scan for references | `false` | +| `--lsp-url ` | Nuanced LSP base URL | `"http://127.0.0.1"`| +| `--lsp-port ` | Port for Nuanced LSP | `4444` | +| `--timeout ` | Timeout seconds (`<=0` to skip) | `120` | +| `--json` | Output machine readable JSON | Text output | + +#### Result + +Returns JSON matching `FindReferencedSymbolsResult` (excerpt): + +```json +{ + "workspace_symbols": [ + { + "reference": { + "name": "registerInitTool", + "file_range": { + "path": "src/tools/index.ts", + "range": { + "start": { "line": 5, "character": 2 }, + "end": { "line": 5, "character": 18 } + } + }, + "kind": "all-references" + }, + "definitions": [ + { + "name": "registerInitTool", + "kind": "function", + "identifier_position": { + "path": "src/tools/init.ts", + "position": { "line": 12, "character": 16 } + }, + "file_range": { + "path": "src/tools/init.ts", + "range": { + "start": { "line": 12, "character": 0 }, + "end": { "line": 22, "character": 1 } + } + } + } + ] + } + ], + "external_symbols": [], + "not_found": [] +} +``` + +### TypeScript + +```typescript +import { NuancedLspClient } from '@nuanced-dev/nuanced-lsp'; + +const lsp = new NuancedLspClient(); + +const filePath = "path/to/file"; +const line = 4; +const char = 16; + +// Basic usage +const res = await lsp.findReferencedSymbols(filePath, line, char); + +// Full workspace scan with timeout override (secs) +const fullScan = true; +const timeoutSecs = 60; +const resFull = await lsp.findReferencedSymbols(filePath, line, char, fullScan, timeoutSecs); +``` + +#### Signature + +```typescript +findReferencedSymbols( + filePath: string, + line: number, + character: number, + fullScan?: boolean, + timeoutSecs?: number +): Promise; +``` + +#### Types + +```typescript +export interface FindReferencedSymbolsResult { + external_symbols: ExternalSymbol[]; + not_found: NotFoundSymbol[]; + workspace_symbols: WorkspaceSymbol[]; +} + +export interface ExternalSymbol { + file_range: FileRange; + kind: string | null; + name: string; +} + +export interface NotFoundSymbol { + file_range: FileRange; + kind: string | null; + name: string; +} + +export interface WorkspaceSymbol { + reference: WorkspaceReference; + definitions: WorkspaceDefinition[]; +} + +export interface WorkspaceReference { + file_range: FileRange; + kind: string | null; + name: string; +} + +export interface WorkspaceDefinition { + file_range: FileRange; + identifier_position: IdentifierPosition; + kind: string | null; + name: string; +} + +export interface FileRange { path: string; range: Range } +export interface IdentifierPosition { path: string; position: Position } +export interface Range { start: Position; end: Position } +export interface Position { line: number; character: number } +``` + +#### Example result (excerpt) + +```json +{ + "workspace_symbols": [ + { + "reference": { + "name": "registerEnrichTool", + "file_range": { + "path": "src/tools/index.ts", + "range": { + "start": { "line": 6, "character": 2 }, + "end": { "line": 6, "character": 20 } + } + }, + "kind": "all-references" + }, + "definitions": [ + { "name": "registerEnrichTool", "kind": "function" } + ] + } + ], + "external_symbols": [], + "not_found": [] +} +``` + +## Find References + +Find all references to the identifier at a given position. Optionally include lines of code context around each reference. + +> **Note:** The optional context lines parameter includes lines of source code around each found reference. + +### CLI + +``` +> nuanced-lsp find-references path/to/file 6:21 +``` + +Include context lines: +``` +> nuanced-lsp find-references --context-lines 3 path/to/file 6:21 +``` + +#### Arguments + +| Argument | Description | Required | +|-------------|--------------------------------------------|----------| +| `file` | Path relative to workspace root | Yes | +| `position` | Position in `line:char` format (0-indexed) | Yes | + +#### CLI options + +| Option | Description | Default | +|------------------------|----------------------------------------------------------|---------------------| +| `--context-lines ` | Include `n` lines of surrounding code for each reference | `0` | +| `--lsp-url ` | Nuanced LSP base URL | `"http://127.0.0.1"`| +| `--lsp-port ` | Port for Nuanced LSP | `4444` | +| `--timeout ` | Timeout seconds (`<=0` to skip) | `120` | +| `--json` | Output machine readable JSON | Text output | + +#### Result + +Returns JSON matching `FindReferencesResult` (excerpt): + +```json +{ + "raw_response": [{ "range": { "start": { "line": 4, "character": 30 }, "end": { "line": 4, "character": 36 } }, "uri": "file:///workspace/src/tools/index.ts" }], + "references": [ + { "path": "src/tools/index.ts", "position": { "line": 4, "character": 30 } }, + { "path": "src/tools/index.ts", "position": { "line": 5, "character": 19 } }, + { "path": "src/tools/index.ts", "position": { "line": 6, "character": 21 } } + ], + "context": [ + { + "file_range": { + "path": "src/tools/index.ts", + "range": { "start": { "line": 4, "character": 0 }, "end": { "line": 4, "character": 0 } } + }, + "source_code": "" + } + ], + "selected_identifier": { + "name": "server", + "file_range": { + "path": "src/tools/index.ts", + "range": { "start": { "line": 6, "character": 21 }, "end": { "line": 6, "character": 27 } } + }, + "kind": null + } +} +``` + +### TypeScript + +```typescript +import { NuancedLspClient } from '@nuanced-dev/nuanced-lsp'; + +const lsp = new NuancedLspClient(); + +const filePath = "path/to/file"; +const line = 6; +const char = 21; + +// Basic usage +const refs = await lsp.findReferences(filePath, line, char); + +// Include 3 lines of context per reference, with a timeout override (secs) +const contextLines = 3; +const timeoutSecs = 60; +const refsWithContext = await lsp.findReferences(filePath, line, char, contextLines, timeoutSecs); +``` + +#### Signature + +```typescript +findReferences( + filePath: string, + line: number, + character: number, + contextLines?: number, // default 0 + timeoutSecs?: number +): Promise; +``` + +#### Types + +```typescript +export interface FindReferencesResult { + references: ReferenceLocation[]; + selected_identifier: SelectedIdentifier; + context: ContextSnippet[] | null; + raw_response: unknown; // raw language server response +} + +export interface ReferenceLocation { + path: string; + position: Position; +} + +export interface Position { line: number; character: number } + +export interface SelectedIdentifier { + file_range: FileRange; + kind: string | null; + name: string; +} + +export interface FileRange { path: string; range: Range } + +export interface Range { start: Position; end: Position } + +export interface ContextSnippet { + file_range: FileRange; + source_code: string; +} +``` + +#### Example result (excerpt) + +```json +{ + "references": [ + { "path": "src/tools/index.ts", "position": { "line": 4, "character": 30 } } + ], + "selected_identifier": { + "name": "server", + "file_range": { + "path": "src/tools/index.ts", + "range": { "start": { "line": 6, "character": 21 }, "end": { "line": 6, "character": 27 } } + }, + "kind": null + }, + "context": null, + "raw_response": {} +} +``` diff --git a/docs/api-reference/system.md b/docs/api-reference/system.md new file mode 100644 index 00000000..0d5176f6 --- /dev/null +++ b/docs/api-reference/system.md @@ -0,0 +1,81 @@ +# System API Reference + +Check system health and LSP server readiness. + +## Health + +Check Nuanced LSP server status and language readiness. + +### CLI + +``` +> nuanced-lsp health +``` + +#### CLI options + +| Option | Description | Default | +|-------------------|---------------------------------|---------------------| +| `--lsp-url ` | Nuanced LSP base URL | `"http://127.0.0.1"`| +| `--lsp-port ` | Port for Nuanced LSP | `4444` | +| `--timeout ` | Timeout seconds (`<=0` to skip) | `120` | +| `--json` | Output machine readable JSON | Text output | + +#### Result + +Outputs JSON matching `HealthResult`: + +```json +{ + "status": "ok", + "version": "0.4.4", + "languages": { + "php": false, + "python": false, + "java": false, + "cpp": false, + "typescript_javascript": true, + "csharp": false, + "ruby": false, + "golang": false, + "rust": false + } +} +``` + +### TypeScript + +```typescript +import { NuancedLspClient } from '@nuanced-dev/nuanced-lsp'; + +const lsp = new NuancedLspClient(); + +const health = await lsp.health(); +``` + +#### Signature + +```typescript +health(timeoutSecs?: number): Promise; +``` + +#### Return type + +```typescript +export interface HealthResult { + status: "ok" | "not ok"; + version?: string; + languages?: Record; + error?: string; +} +``` + +#### Example result + +```json +{ + "status": "ok", + "version": "0.4.4", + "languages": { "typescript_javascript": true } +} +``` diff --git a/docs/api-reference/workspace.md b/docs/api-reference/workspace.md new file mode 100644 index 00000000..140cefa8 --- /dev/null +++ b/docs/api-reference/workspace.md @@ -0,0 +1,159 @@ +# Workspace API Reference + +List all files and read source code from files in the workspace. + +| Command | Description | +|----------------------------------|----------------------------------------------------| +| [List Files](#list-files) | List all files in the workspace. | +| [Read Source](#read-source) | Read an individual source file in the workspace. | + +## List Files + +List all files in the Nuanced LSP workspace. + +### CLI + +``` +> nuanced-lsp list-files +``` + +#### CLI options + +| Option | Description | Default | +|-------------------|---------------------------------|---------------------| +| `--lsp-url ` | Nuanced LSP base URL | `"http://127.0.0.1"`| +| `--lsp-port ` | Port for Nuanced LSP | `4444` | +| `--timeout ` | Timeout seconds (`<=0` to skip) | `120` | +| `--json` | Output machine readable JSON | Text output | + +#### Result + +Prints one path per line, e.g.: + +``` +eslint.config.js +jest.config.js +src/index.ts +src/utils.ts +... +``` + +### TypeScript + +```typescript +import { NuancedLspClient } from '@nuanced-dev/nuanced-lsp'; + +const lsp = new NuancedLspClient(); + +// Default timeout +const files = await lsp.listFiles(); + +// With timeout override (seconds) +const timeoutSecs = 60; +const filesWithTimeout = await lsp.listFiles(timeoutSecs); +``` + +#### Signature + +```typescript +listFiles(timeoutSecs?: number): Promise; +``` + +#### Example result + +```json +[ + "eslint.config.js", + "src/index.ts", + "src/utils.ts" +] +``` + +--- + +## Read Source + +Read the full source of a file (or an optional range). + +### CLI + +``` +> nuanced-lsp read-source src/index.ts +``` + +Read a range (0-indexed `line:char-line:char`): +``` +> nuanced-lsp read-source --range 0:0-10:5 src/index.ts +``` + +#### CLI options + +| Option | Description | Default | +|--------------------------------|---------------------------------------------------------|----------------------| +| `file` (argument) | Path relative to workspace root | — | +| `--range `| Optional range (e.g. `0:0-10:5`) | — | +| `--lsp-url ` | Nuanced LSP base URL | `"http://127.0.0.1"` | +| `--lsp-port ` | Port for Nuanced LSP | `4444` | +| `--timeout ` | Timeout seconds (`<=0` to skip) | `120` | +| `--json` | Output machine readable JSON | Text output | + +#### Result + +Returns the file contents to stdout. Example (truncated): + +``` +import js from '@eslint/js'; +import typescript from '@typescript-eslint/eslint-plugin'; +... +``` + +### TypeScript + +```typescript +import { NuancedLspClient } from '@nuanced-dev/nuanced-lsp'; + +const lsp = new NuancedLspClient(); + +const filePath = "src/index.ts"; + +// Entire file +const full = await lsp.readSource(filePath); + +// Specific range +const range: Range = { + start: { line: 0, character: 0 }, + end: { line: 10, character: 5 } +}; +const timeoutSecs = 60; +const snippet = await lsp.readSource(filePath, range, timeoutSecs); +``` + +#### Signature + +```typescript +readSource( + filePath: string, + range?: Range | null, + timeoutSecs?: number +): Promise; +``` + +#### Types + +```typescript +export interface Position { line: number; character: number } + +export interface Range { start: Position; end: Position } + +export interface ReadSourceResult { + source_code: string; +} +``` + +#### Example result + +```json +{ + "source_code": "import js from '@eslint/js';\nimport typescript from '@typescript-eslint/eslint-plugin';\n..." +} +``` diff --git a/docs/overview.md b/docs/overview.md new file mode 100644 index 00000000..7b32705f --- /dev/null +++ b/docs/overview.md @@ -0,0 +1,48 @@ +# Nuanced LSP + +Nuanced LSP exposes extended LSP functionality to provide precise and detailed code context for a variety of agentic tasks. + +Nuanced LSP builds on the open-source [LSProxy](https://github.com/agentic-labs/lsproxy) project, and provides a convenient and easy to use TypeScript client library and CLI. Additional language support, process and performance improvements, along with additional capabilities are on Nuanced LSP's roadmap. + +## Prerequisites + +Before installing Nuanced LSP, you'll need: + +- Node.js and npm installed on your system +- Docker installed and running on your system + +## Installation + +Nuanced LSP is offered as a TypeScript client library or CLI. To install: + +```bash +npm install -g @nuanced-dev/lsp@latest +``` + +## Language Support + +Nuanced LSP supports the following languages and LSP servers: + +| Language | LSP Server | +|-------------|------------------------------| +| C / C++ | `clangd` | +| C# | `omnisharp` | +| Go | `gopls` | +| Java | `jdtls` | +| JavaScript | `typescript-language-server` | +| Python | `jedi-language-server` | +| PHP | `phpactor` | +| Ruby | `ruby-lsp` and `sorbet` | +| Rust | `rust-analyzer` | +| TypeScript | `typescript-language-server` | + +## API Reference + +Nuanced LSP's API is grouped into the following categories. + +- **[Container Lifecycle API](./api-reference/container-lifecycle.md)**: Docker container management and image operations. +- **[Symbols API](./api-reference/symbols.md)**: Retrieve definitions, identifiers, references, and related graph data. +- **[System API](./api-reference/system.md)**: System health and language readiness. +- **[Workspace API](./api-reference/workspace.md)**: File listing and source access. + +> **Note:** Nuanced LSP does not currently support authorization. If your use case requires authorization, please [contact us](mailto:support@nuanced.dev).