Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
444992e
Add Copilot-only LSP frontmatter support
Copilot Jun 26, 2026
8444c8e
Improve LSP config diagnostics
Copilot Jun 26, 2026
0b8e753
Merge branch 'main' into copilot/add-lsp-compiler-support
pelikhan Jun 26, 2026
16f2a77
feat(jsweep): add TypeScript LSP server to enable JavaScript/TypeScri…
Copilot Jun 26, 2026
018f278
docs: add .github/aw/lsp.md instructions for LSP frontmatter configur…
Copilot Jun 26, 2026
436b56c
Merge remote-tracking branch 'origin/main' into copilot/add-lsp-compi…
Copilot Jun 26, 2026
2379f24
fix(lsp): deterministic key normalization, use ResolveEngineID, regis…
Copilot Jun 26, 2026
45a42b4
chore: recompile detection-analysis-report workflow after main merge
Copilot Jun 26, 2026
2d89153
fix(lsp): add --ignore-scripts to npm LSP install commands
Copilot Jun 27, 2026
3af6d45
feat(lsp): delegate LSP runtime setup to runtime manager for proper S…
Copilot Jun 27, 2026
553ec35
feat(lsp): align LSP npm package installation with runtime manager
Copilot Jun 27, 2026
f76dcfa
Merge remote-tracking branch 'origin/main' into copilot/add-lsp-compi…
Copilot Jun 27, 2026
af19dab
feat(lsp): mark lsp as experimental with compile-time warning, schema…
Copilot Jun 27, 2026
237eb78
Merge branch 'main' into copilot/add-lsp-compiler-support
github-actions[bot] Jun 27, 2026
4aa65ef
feat(smoke-codex): enable TypeScript LSP and add function-count LSP test
Copilot Jun 27, 2026
079a3f8
feat(lsp): pin packages to releases, add frontmatter version override
Copilot Jun 27, 2026
6fc5b52
refactor(lsp): move TypeScript LSP test from smoke-codex to smoke-cop…
Copilot Jun 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
171 changes: 171 additions & 0 deletions .github/aw/lsp.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
---
description: Language Server Protocol (LSP) configuration reference for gh-aw Copilot workflows — frontmatter syntax, supported servers, and file extension mapping.
---

# LSP Configuration

> ⚠️ **Experimental feature.** The `lsp` frontmatter field is experimental. Using it will emit a compile-time warning. The interface may change in future releases.

The `lsp` frontmatter field lets Copilot-engine workflows declare language servers. At compile time, the compiler:

1. Validates the configuration and rejects the workflow if `lsp` is used with a non-Copilot engine.
2. Generates `~/.copilot/settings.json` with an `lspServers` block the Copilot CLI reads at startup.
3. Injects install steps for known server ecosystems into the agent setup job.

> ⚠️ **`lsp` is only supported with `engine: copilot`**. Using it with any other engine causes a compile-time error.

## Syntax

```yaml
engine:
id: copilot

lsp:
<language-key>:
command: <server-executable>
args: [<arg1>, <arg2>] # optional
fileExtensions:
".<ext>": <language-id> # at least one required
```

Each key under `lsp` is a language identifier (lowercase, alphanumeric, hyphens, or underscores). It maps to a server definition with:

| Field | Required | Description |
|---|---|---|
| `command` | **yes** | Executable name or path for the language server |
| `args` | no | Command-line arguments passed to the server on startup |
| `fileExtensions` | **yes** | Map of file extension (with leading `.`) to LSP language ID |
| `version` | no | Package version to install (e.g. `"5.8.3"`). Overrides the built-in pinned default for known servers. |

## Built-in Servers

For the languages below, the compiler automatically injects an install step — no manual `steps:` entry is needed. Each server is pinned to a known-good release by default; use the `version` field to override.

| Language key | Default version | Install command | Example `command` |
|---|---|---|---|
| `bash` | `5.4.0` | `npm install -g --ignore-scripts bash-language-server@5.4.0` | `bash-language-server` |
| `go` | `0.18.1` | `go install golang.org/x/tools/gopls@v0.18.1` | `gopls` |
| `php` | `1.14.1` | `npm install -g --ignore-scripts intelephense@1.14.1` | `intelephense` |
| `python` | `1.1.399` | `npm install -g --ignore-scripts pyright@1.1.399` | `pyright-langserver` |
| `ruby` | `0.50.0` | `gem install solargraph -v 0.50.0` | `solargraph` |
| `rust` | n/a | `rustup component add rust-analyzer` | `rust-analyzer` |
| `typescript` | `5.8.3` / `4.3.3` | `npm install -g --ignore-scripts typescript@5.8.3 typescript-language-server@4.3.3` | `typescript-language-server` |
| `yaml` | `1.15.0` | `npm install -g --ignore-scripts yaml-language-server@1.15.0` | `yaml-language-server` |

> The `version` field overrides the pinned version for the primary language server package (the last package in the install list). For `typescript`, it controls `typescript-language-server`; `typescript` itself stays at its hardcoded companion version (`5.8.3`).

Language keys not in this table still work — the compiler simply skips the auto-install step. Add a manual `steps:` entry to install the server yourself.

## Examples

### TypeScript / JavaScript

```yaml
engine:
id: copilot

lsp:
typescript:
command: typescript-language-server
args: ["--stdio"]
fileExtensions:
".ts": typescript
".tsx": typescriptreact
".js": javascript
".cjs": javascript
".mjs": javascript
```

### Python

```yaml
engine:
id: copilot

lsp:
python:
command: pyright-langserver
args: ["--stdio"]
fileExtensions:
".py": python
```

### Go

```yaml
engine:
id: copilot

lsp:
go:
command: gopls
fileExtensions:
".go": go
```

### Multiple Languages

```yaml
engine:
id: copilot

lsp:
typescript:
command: typescript-language-server
args: ["--stdio"]
fileExtensions:
".ts": typescript
".js": javascript
python:
command: pyright-langserver
args: ["--stdio"]
fileExtensions:
".py": python
```

### Custom Server (no built-in install)

For servers without a built-in install spec, add a manual install step:

```yaml
engine:
id: copilot

steps:
- name: Install custom language server
run: npm install -g my-custom-language-server

lsp:
mylang:
command: my-custom-language-server
args: ["--stdio"]
fileExtensions:
".ml": mylang
```

## Network Requirements

Installing LSP servers requires network access to the appropriate package registry. Add the matching ecosystem to `network.allowed`:

| Language | Ecosystem to add |
|---|---|
| `bash`, `php`, `python`, `typescript`, `yaml` | `node` |
| `go` | `go` |
| `ruby` | `ruby` |
| `rust` | `rust` |

```yaml
network:
allowed:
- node # for npm-installed servers (typescript, yaml, python/pyright, etc.)
- go # for gopls
```

## Compile-time Validation

The compiler enforces these rules at compile time:

- `lsp` requires `engine: copilot` — any other engine causes an error.
- Each language entry must have a non-empty `command`.
- Each language entry must define at least one `fileExtensions` mapping.
- Language keys are case-insensitive and trimmed; duplicate keys that collapse to the same lowercase value cause nondeterministic behavior and should be avoided.
1 change: 1 addition & 0 deletions .github/skills/agentic-workflows/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ Load these files from `github/gh-aw` (they are not available locally).
- `.github/aw/github-mcp-server.md`
- `.github/aw/llms.md`
- `.github/aw/loop.md`
- `.github/aw/lsp.md`
- `.github/aw/mcp-clis.md`
- `.github/aw/memory-stateful-patterns.md`
- `.github/aw/memory.md`
Expand Down
9 changes: 7 additions & 2 deletions .github/workflows/jsweep.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions .github/workflows/jsweep.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,16 @@ tools:
edit:
bash: ["*"]
cache-memory: true
lsp:
typescript:
command: typescript-language-server
args: ["--stdio"]
fileExtensions:
".js": javascript
".cjs": javascript
".mjs": javascript
".ts": typescript
".tsx": typescriptreact
steps:
- name: Install Node.js dependencies
working-directory: actions/setup/js
Expand Down
14 changes: 12 additions & 2 deletions .github/workflows/smoke-copilot.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 17 additions & 1 deletion .github/workflows/smoke-copilot.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,21 @@ tools:
mode: cli
web-fetch:
cli-proxy: true
lsp:
typescript:
command: typescript-language-server
args: ["--stdio"]
fileExtensions:
".js": javascript
".cjs": javascript
".mjs": javascript
".ts": typescript
".tsx": typescriptreact
runtimes:
go:
version: "1.26"
node:
version: "20"
models:
providers:
anthropic:
Expand Down Expand Up @@ -189,14 +201,18 @@ Run these checks and mark each as ✅/❌:
13. Comment memory: append an original 3-line haiku to `/tmp/gh-aw/comment-memory/*.md`.
14. Sub-agent: use `file-summarizer` on `README.md`.
15. Check run: call `create_check_run` with `conclusion=success`, title `Smoke Copilot - Run ${{ github.run_id }}`, summary `All smoke tests completed.`, text `Detailed results attached.`
16. **LSP TypeScript Testing**: Use the TypeScript language server (configured via `lsp.typescript` frontmatter) to count the number of functions in `${{ github.workspace }}/actions/setup/js/safe_output_helpers.cjs`:
- Open the file `${{ github.workspace }}/actions/setup/js/safe_output_helpers.cjs` via LSP
- Use LSP document symbols to list all symbols in the file and count functions
- Report the total function count as ✅ if at least 1 function is found, ❌ otherwise

## Output

1. **Create an issue** with a summary of the smoke test run:
- Use the temporary ID `aw_smoke1` for the issue so you can reference it later
- Title: "Smoke Test: Copilot - ${{ github.run_id }}"
- Body should include:
- Test results (✅ or ❌ for each test)
- Test results (✅ or ❌ for each test, including test #16 LSP TypeScript)
- Overall status: PASS or FAIL
- Run URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
- Timestamp
Expand Down
1 change: 1 addition & 0 deletions pkg/cli/data/agentic_workflows_fallback_aw_files.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"github-mcp-server.md",
"llms.md",
"loop.md",
"lsp.md",
"mcp-clis.md",
"memory-stateful-patterns.md",
"memory.md",
Expand Down
23 changes: 23 additions & 0 deletions pkg/parser/schema_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,29 @@ func TestValidateMainWorkflowFrontmatterWithSchemaAndLocation_ToolsEditBoolean(t
}
}

func TestValidateMainWorkflowFrontmatterWithSchemaAndLocation_LSPConfig(t *testing.T) {
t.Parallel()

frontmatter := map[string]any{
"on": "push",
"engine": "copilot",
"lsp": map[string]any{
"typescript": map[string]any{
"command": "typescript-language-server",
"args": []any{"--stdio"},
"fileExtensions": map[string]any{
".ts": "typescript",
},
},
},
}

err := ValidateMainWorkflowFrontmatterWithSchemaAndLocation(frontmatter, "/tmp/gh-aw/lsp-config-test.md")
if err != nil {
t.Fatalf("expected valid lsp configuration to pass schema validation, got: %v", err)
}
}

func TestValidateMainWorkflowFrontmatterWithSchemaAndLocation_MaxLimitsAllowExpressions(t *testing.T) {
t.Parallel()

Expand Down
Loading
Loading