litclaude-ai 0.3.3 → 0.3.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +71 -0
- package/README.md +5 -5
- package/README_ko-KR.md +5 -5
- package/RELEASE_CHECKLIST.md +8 -6
- package/cover.png +0 -0
- package/generate_cover.py +1 -1
- package/package.json +1 -1
- package/plugins/litclaude/.claude-plugin/plugin.json +1 -1
- package/plugins/litclaude/bin/litclaude-hook.js +42 -6
- package/plugins/litclaude/commands/dynamic-workflow.md +6 -6
- package/plugins/litclaude/commands/litresearch.md +37 -0
- package/plugins/litclaude/commands/review-work.md +5 -5
- package/plugins/litclaude/hooks/hooks.json +12 -0
- package/plugins/litclaude/lib/litgoal/autoloop.mjs +78 -0
- package/plugins/litclaude/lib/litgoal/cli.mjs +8 -0
- package/plugins/litclaude/skills/lit-loop/SKILL.md +12 -10
- package/plugins/litclaude/skills/lit-plan/SKILL.md +9 -10
- package/plugins/litclaude/skills/litgoal/SKILL.md +41 -5
- package/plugins/litclaude/skills/litresearch/SKILL.md +148 -0
- package/plugins/litclaude/skills/lsp-setup/SKILL.md +151 -0
- package/plugins/litclaude/skills/lsp-setup/references/bash/README.md +63 -0
- package/plugins/litclaude/skills/lsp-setup/references/c-cpp/README.md +72 -0
- package/plugins/litclaude/skills/lsp-setup/references/csharp/README.md +81 -0
- package/plugins/litclaude/skills/lsp-setup/references/dart/README.md +53 -0
- package/plugins/litclaude/skills/lsp-setup/references/elixir/README.md +57 -0
- package/plugins/litclaude/skills/lsp-setup/references/go/README.md +59 -0
- package/plugins/litclaude/skills/lsp-setup/references/haskell/README.md +64 -0
- package/plugins/litclaude/skills/lsp-setup/references/java/README.md +66 -0
- package/plugins/litclaude/skills/lsp-setup/references/julia/README.md +62 -0
- package/plugins/litclaude/skills/lsp-setup/references/kotlin/README.md +68 -0
- package/plugins/litclaude/skills/lsp-setup/references/lua/README.md +64 -0
- package/plugins/litclaude/skills/lsp-setup/references/php/README.md +56 -0
- package/plugins/litclaude/skills/lsp-setup/references/python/README.md +68 -0
- package/plugins/litclaude/skills/lsp-setup/references/ruby/README.md +62 -0
- package/plugins/litclaude/skills/lsp-setup/references/rust/README.md +61 -0
- package/plugins/litclaude/skills/lsp-setup/references/swift/README.md +60 -0
- package/plugins/litclaude/skills/lsp-setup/references/terraform/README.md +61 -0
- package/plugins/litclaude/skills/lsp-setup/references/typescript/README.md +77 -0
- package/plugins/litclaude/skills/lsp-setup/references/yaml/README.md +60 -0
- package/plugins/litclaude/skills/lsp-setup/references/zig/README.md +55 -0
- package/plugins/litclaude/skills/lsp-setup/scripts/detect-lsp.ts +220 -0
- package/plugins/litclaude/skills/lsp-setup/scripts/lsp-server-table.ts +146 -0
- package/plugins/litclaude/skills/lsp-setup/scripts/tsconfig.json +18 -0
- package/plugins/litclaude/skills/lsp-setup/scripts/verify-lsp.ts +242 -0
- package/plugins/litclaude/skills/start-work/SKILL.md +12 -13
- package/plugins/litclaude/skills/visual-qa/SKILL.md +259 -0
- package/plugins/litclaude/skills/visual-qa/scripts/ansi.ts +17 -0
- package/plugins/litclaude/skills/visual-qa/scripts/cli.ts +96 -0
- package/plugins/litclaude/skills/visual-qa/scripts/east-asian-width.ts +72 -0
- package/plugins/litclaude/skills/visual-qa/scripts/image-diff.ts +109 -0
- package/plugins/litclaude/skills/visual-qa/scripts/png-crc.ts +27 -0
- package/plugins/litclaude/skills/visual-qa/scripts/png-decode.ts +206 -0
- package/plugins/litclaude/skills/visual-qa/scripts/png-synth.ts +57 -0
- package/plugins/litclaude/skills/visual-qa/scripts/tui-grid.ts +88 -0
- package/plugins/litclaude/skills/visual-qa/scripts/types.ts +54 -0
- package/scripts/qa-portable-install.sh +1 -1
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Rust — LSP setup (Claude Code / LitClaude)
|
|
2
|
+
|
|
3
|
+
- **Recommended server:** `rust-analyzer`
|
|
4
|
+
- **Extensions:** `.rs`
|
|
5
|
+
- **Install hint:** `rustup component add rust-analyzer`
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
- **macOS:** `rustup component add rust-analyzer` (or `brew install rust-analyzer`)
|
|
10
|
+
- **Linux:** `rustup component add rust-analyzer`
|
|
11
|
+
- **Windows:** `rustup component add rust-analyzer`
|
|
12
|
+
|
|
13
|
+
The rustup component is the recommended path — it stays pinned to your toolchain.
|
|
14
|
+
`rust-analyzer` also needs the `rust-src` component to index the standard library
|
|
15
|
+
(`rustup component add rust-src`).
|
|
16
|
+
|
|
17
|
+
Confirm it resolves:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
command -v rust-analyzer
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Configure
|
|
24
|
+
|
|
25
|
+
Add a `rust` entry to `plugins/litclaude/.lsp.json`:
|
|
26
|
+
|
|
27
|
+
```json
|
|
28
|
+
{
|
|
29
|
+
"rust": {
|
|
30
|
+
"command": ["rust-analyzer"],
|
|
31
|
+
"extensionToLanguage": {
|
|
32
|
+
".rs": "rust"
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Claude Code routes `.rs` edits to `rust-analyzer` via the extension map. Options
|
|
39
|
+
such as switching the check command to clippy live in `rust-analyzer` settings
|
|
40
|
+
consumed by your editor, not in `.lsp.json`.
|
|
41
|
+
|
|
42
|
+
## Alternatives
|
|
43
|
+
|
|
44
|
+
None — `rust-analyzer` is the official and sole Rust language server.
|
|
45
|
+
|
|
46
|
+
## Troubleshooting
|
|
47
|
+
- **PATH:** `rust-analyzer` must be on PATH; reopen shell after install. The rustup shim lives in `~/.cargo/bin`.
|
|
48
|
+
- **Exits while loading rust-src:** if rust-analyzer crashes during stdlib indexing, reinstall the source component:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
rustup component remove rust-src && rustup component add rust-src
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
- **No proc-macro / build script support:** ensure the project builds with `cargo check`; rust-analyzer reuses the same toolchain.
|
|
55
|
+
|
|
56
|
+
## Verify
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
node --experimental-strip-types ../../scripts/verify-lsp.ts path/to/file.rs
|
|
60
|
+
# or: bun ../../scripts/verify-lsp.ts path/to/file.rs
|
|
61
|
+
```
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Swift — LSP setup (Claude Code / LitClaude)
|
|
2
|
+
|
|
3
|
+
- **Recommended server:** `sourcekit-lsp`
|
|
4
|
+
- **Extensions:** `.swift .objc .objcpp`
|
|
5
|
+
- **Install hint:** `Included with Xcode or the Swift toolchain`
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
`sourcekit-lsp` ships with the Swift toolchain — no separate install.
|
|
10
|
+
|
|
11
|
+
- **macOS:** `xcode-select --install` (or install full Xcode). It resolves to the active toolchain selected by `xcode-select`.
|
|
12
|
+
- **Linux:** Install a swift.org toolchain (`sourcekit-lsp` ships inside it); add the toolchain's `usr/bin` to PATH.
|
|
13
|
+
- **Windows:** Install the swift.org Windows toolchain; `sourcekit-lsp` is bundled.
|
|
14
|
+
|
|
15
|
+
Confirm it resolves:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
command -v sourcekit-lsp
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Configure
|
|
22
|
+
|
|
23
|
+
Add a `swift` entry to `plugins/litclaude/.lsp.json`:
|
|
24
|
+
|
|
25
|
+
```json
|
|
26
|
+
{
|
|
27
|
+
"swift": {
|
|
28
|
+
"command": ["sourcekit-lsp"],
|
|
29
|
+
"extensionToLanguage": {
|
|
30
|
+
".swift": "swift",
|
|
31
|
+
".objc": "objective-c",
|
|
32
|
+
".objcpp": "objective-cpp"
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Claude Code routes these extensions to `sourcekit-lsp` via the map. For best
|
|
39
|
+
results the project needs a **SwiftPM `Package.swift`** or a
|
|
40
|
+
`compile_commands.json` compilation database so the server can resolve modules.
|
|
41
|
+
|
|
42
|
+
## Alternatives
|
|
43
|
+
|
|
44
|
+
**No mainstream alternative.** `sourcekit-lsp` is the official Apple/swift.org
|
|
45
|
+
server and the only practical choice.
|
|
46
|
+
|
|
47
|
+
## Troubleshooting
|
|
48
|
+
|
|
49
|
+
- **PATH:** `sourcekit-lsp` on PATH; reopen shell after install (or after `xcode-select -s`).
|
|
50
|
+
- **Wrong toolchain (macOS):** point `xcode-select` at the right Xcode/toolchain; mismatches cause stale or missing results.
|
|
51
|
+
- **No `Package.swift` / compile db:** add a SwiftPM manifest or generate `compile_commands.json` for accurate indexing.
|
|
52
|
+
- **Objective-C (`.objc`/`.objcpp`):** needs a compilation database to resolve headers and frameworks.
|
|
53
|
+
- **First build slow:** the server builds the module graph on first open; wait for it.
|
|
54
|
+
|
|
55
|
+
## Verify
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
node --experimental-strip-types ../../scripts/verify-lsp.ts path/to/File.swift
|
|
59
|
+
# or: bun ../../scripts/verify-lsp.ts path/to/File.swift
|
|
60
|
+
```
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Terraform — LSP setup (Claude Code / LitClaude)
|
|
2
|
+
|
|
3
|
+
- **Recommended server:** `terraform-ls serve`
|
|
4
|
+
- **Extensions:** `.tf .tfvars`
|
|
5
|
+
- **Install hint:** See https://github.com/hashicorp/terraform-ls
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
- **macOS:** `brew install hashicorp/tap/terraform-ls`
|
|
10
|
+
- **Linux:** download a release from https://github.com/hashicorp/terraform-ls/releases and place `terraform-ls` on PATH (or `apt`/`dnf` via the HashiCorp repo)
|
|
11
|
+
- **Windows:** `choco install terraform-ls` (or download a release zip)
|
|
12
|
+
|
|
13
|
+
`terraform-ls` requires the `terraform` binary itself to be installed and on PATH.
|
|
14
|
+
|
|
15
|
+
Confirm both resolve:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
command -v terraform-ls
|
|
19
|
+
command -v terraform
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Configure
|
|
23
|
+
|
|
24
|
+
Add a `terraform` entry to `plugins/litclaude/.lsp.json`:
|
|
25
|
+
|
|
26
|
+
```json
|
|
27
|
+
{
|
|
28
|
+
"terraform": {
|
|
29
|
+
"command": ["terraform-ls", "serve"],
|
|
30
|
+
"extensionToLanguage": {
|
|
31
|
+
".tf": "terraform",
|
|
32
|
+
".tfvars": "terraform-vars"
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Claude Code routes `.tf`/`.tfvars` edits to `terraform-ls` via the map.
|
|
39
|
+
Provider/module completion comes from schemas generated by `terraform init`; run
|
|
40
|
+
it in each module root before expecting full completion:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
terraform init
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Alternatives
|
|
47
|
+
|
|
48
|
+
- `tflint` standalone for opinionated linting (complements the LSP).
|
|
49
|
+
- `terraform fmt` for formatting.
|
|
50
|
+
|
|
51
|
+
## Troubleshooting
|
|
52
|
+
- **PATH:** `terraform-ls` AND `terraform` both on PATH; reopen shell after install.
|
|
53
|
+
- **No provider completion:** run `terraform init` so the `.terraform/` schema cache exists.
|
|
54
|
+
- **`.tfvars` not analyzed:** open the containing module so the server has root context.
|
|
55
|
+
|
|
56
|
+
## Verify
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
node --experimental-strip-types ../../scripts/verify-lsp.ts path/to/file.tf
|
|
60
|
+
# or: bun ../../scripts/verify-lsp.ts path/to/file.tf
|
|
61
|
+
```
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# TypeScript / JavaScript — LSP setup (Claude Code / LitClaude)
|
|
2
|
+
|
|
3
|
+
- **Recommended server:** `typescript-language-server --stdio`
|
|
4
|
+
- **Extensions:** `.ts .tsx .js .jsx .mjs .cjs .mts .cts`
|
|
5
|
+
- **Install hint:** `npm install -g typescript-language-server typescript`
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
- **macOS:** `npm install -g typescript-language-server typescript`
|
|
10
|
+
- **Linux:** `npm install -g typescript-language-server typescript`
|
|
11
|
+
- **Windows:** `npm install -g typescript-language-server typescript` (PowerShell or cmd)
|
|
12
|
+
|
|
13
|
+
`typescript-language-server` is only a thin wrapper — it needs the `typescript`
|
|
14
|
+
package (`tsserver`) present too, either globally or in the project's
|
|
15
|
+
`node_modules`. Always install both.
|
|
16
|
+
|
|
17
|
+
Confirm it resolves:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
command -v typescript-language-server
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Configure
|
|
24
|
+
|
|
25
|
+
LitClaude declares language servers in `plugins/litclaude/.lsp.json`. Each entry
|
|
26
|
+
is keyed by language name and holds a `command` array plus an
|
|
27
|
+
`extensionToLanguage` map. TypeScript/JavaScript already ships in the default
|
|
28
|
+
config:
|
|
29
|
+
|
|
30
|
+
```json
|
|
31
|
+
{
|
|
32
|
+
"typescript": {
|
|
33
|
+
"command": ["typescript-language-server", "--stdio"],
|
|
34
|
+
"extensionToLanguage": {
|
|
35
|
+
".ts": "typescript",
|
|
36
|
+
".tsx": "typescriptreact",
|
|
37
|
+
".js": "javascript",
|
|
38
|
+
".jsx": "javascriptreact",
|
|
39
|
+
".mjs": "javascript",
|
|
40
|
+
".cjs": "javascript"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Add `.mts`/`.cts` to `extensionToLanguage` if your project uses them. Claude Code
|
|
47
|
+
resolves the server for an edited file by matching its extension against these
|
|
48
|
+
maps.
|
|
49
|
+
|
|
50
|
+
## Alternatives
|
|
51
|
+
|
|
52
|
+
Swap the `command` for your toolchain. Keep the same `.lsp.json` shape and adjust
|
|
53
|
+
`extensionToLanguage` to cover the extensions that server should own:
|
|
54
|
+
|
|
55
|
+
| command | when to choose |
|
|
56
|
+
| ----------------------------------------- | --------------------------------------- |
|
|
57
|
+
| `["deno", "lsp"]` | Deno projects (handles `.ts/.tsx/.js`) |
|
|
58
|
+
| `["biome", "lsp-proxy", "--stdio"]` | Biome lint/format as the LSP |
|
|
59
|
+
| `["vscode-eslint-language-server", "--stdio"]` | ESLint diagnostics |
|
|
60
|
+
| `["oxlint", "--lsp"]` | fast Oxc-based linting |
|
|
61
|
+
| `["vue-language-server", "--stdio"]` | `.vue` single-file components |
|
|
62
|
+
| `["svelteserver", "--stdio"]` | `.svelte` files |
|
|
63
|
+
| `["astro-ls", "--stdio"]` | `.astro` files |
|
|
64
|
+
|
|
65
|
+
`eslint` install: `npm i -g vscode-langservers-extracted`. To run Deno instead
|
|
66
|
+
of the default, replace the `typescript` entry's `command` with `["deno", "lsp"]`.
|
|
67
|
+
|
|
68
|
+
## Troubleshooting
|
|
69
|
+
- **PATH:** `typescript-language-server` must be on PATH; reopen shell after `npm i -g`. Check your global bin with `npm bin -g`.
|
|
70
|
+
- **Missing tsserver:** errors like "Could not find tsserver" mean the `typescript` package is absent — install it globally or in the project.
|
|
71
|
+
|
|
72
|
+
## Verify
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
node --experimental-strip-types ../../scripts/verify-lsp.ts path/to/file.ts
|
|
76
|
+
# or: bun ../../scripts/verify-lsp.ts path/to/file.ts
|
|
77
|
+
```
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# YAML — LSP setup (Claude Code / LitClaude)
|
|
2
|
+
|
|
3
|
+
- **Recommended server:** `yaml-language-server --stdio`
|
|
4
|
+
- **Extensions:** `.yaml .yml`
|
|
5
|
+
- **Install hint:** `npm install -g yaml-language-server`
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
- **macOS:** `npm install -g yaml-language-server`
|
|
10
|
+
- **Linux:** `npm install -g yaml-language-server`
|
|
11
|
+
- **Windows:** `npm install -g yaml-language-server` (PowerShell)
|
|
12
|
+
|
|
13
|
+
Confirm it resolves:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
command -v yaml-language-server
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Configure
|
|
20
|
+
|
|
21
|
+
Add a `yaml` entry to `plugins/litclaude/.lsp.json`:
|
|
22
|
+
|
|
23
|
+
```json
|
|
24
|
+
{
|
|
25
|
+
"yaml": {
|
|
26
|
+
"command": ["yaml-language-server", "--stdio"],
|
|
27
|
+
"extensionToLanguage": {
|
|
28
|
+
".yaml": "yaml",
|
|
29
|
+
".yml": "yaml"
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Claude Code routes `.yaml`/`.yml` edits to `yaml-language-server` via the map.
|
|
36
|
+
Schema association is the main reason to tune the server, but those options
|
|
37
|
+
travel through the editor's LSP `initializationOptions` (`yaml.schemas`,
|
|
38
|
+
`yaml.schemaStore`), not `.lsp.json`. An inline
|
|
39
|
+
`# yaml-language-server: $schema=<url>` modeline works without any config:
|
|
40
|
+
|
|
41
|
+
```yaml
|
|
42
|
+
# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Alternatives
|
|
46
|
+
|
|
47
|
+
- `redhat.vscode-yaml` bundles the same server in editors.
|
|
48
|
+
- `yamllint` standalone for style/lint-only checks.
|
|
49
|
+
|
|
50
|
+
## Troubleshooting
|
|
51
|
+
- **PATH:** `yaml-language-server` on PATH; reopen shell after `npm -g` install.
|
|
52
|
+
- **No validation:** no schema matched — add a `$schema` modeline or a `yaml.schemas` mapping in your editor's init options.
|
|
53
|
+
- **Wrong schema applied:** SchemaStore guessed by filename; pin explicitly with a modeline.
|
|
54
|
+
|
|
55
|
+
## Verify
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
node --experimental-strip-types ../../scripts/verify-lsp.ts path/to/file.yaml
|
|
59
|
+
# or: bun ../../scripts/verify-lsp.ts path/to/file.yaml
|
|
60
|
+
```
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Zig — LSP setup (Claude Code / LitClaude)
|
|
2
|
+
|
|
3
|
+
- **Recommended server:** `zls`
|
|
4
|
+
- **Extensions:** `.zig .zon`
|
|
5
|
+
- **Install hint:** `https://github.com/zigtools/zls`
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
ZLS (the Zig Language Server) must be built against the **same Zig version** you use.
|
|
10
|
+
See `https://github.com/zigtools/zls`.
|
|
11
|
+
|
|
12
|
+
- **macOS:** `brew install zls`
|
|
13
|
+
- **Linux:** download a prebuilt release matching your Zig version, or `zig build -Doptimize=ReleaseSafe` from the zls source
|
|
14
|
+
- **Windows:** download the matching release from the zls GitHub releases, or build from source
|
|
15
|
+
|
|
16
|
+
Confirm it resolves:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
command -v zls
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Configure
|
|
23
|
+
|
|
24
|
+
Add a `zig` entry to `plugins/litclaude/.lsp.json`:
|
|
25
|
+
|
|
26
|
+
```json
|
|
27
|
+
{
|
|
28
|
+
"zig": {
|
|
29
|
+
"command": ["zls"],
|
|
30
|
+
"extensionToLanguage": {
|
|
31
|
+
".zig": "zig",
|
|
32
|
+
".zon": "zig"
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Claude Code routes `.zig`/`.zon` edits to `zls` via the map. No extra
|
|
39
|
+
configuration is normally required.
|
|
40
|
+
|
|
41
|
+
## Alternatives
|
|
42
|
+
|
|
43
|
+
None.
|
|
44
|
+
|
|
45
|
+
## Troubleshooting
|
|
46
|
+
- **VERSION MATCH (critical):** zls version MUST match your zig version — build/install zls against the exact same Zig. A mismatch causes crashes, parse errors, or silent failures. After upgrading Zig, upgrade/rebuild zls too.
|
|
47
|
+
- **PATH:** `zls` must be on PATH; reopen the shell after install.
|
|
48
|
+
- **zig not found:** zls invokes `zig` for builds — make sure `zig` itself is also on PATH.
|
|
49
|
+
|
|
50
|
+
## Verify
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
node --experimental-strip-types ../../scripts/verify-lsp.ts path/to/file.zig
|
|
54
|
+
# or: bun ../../scripts/verify-lsp.ts path/to/file.zig
|
|
55
|
+
```
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// detect-lsp.ts <targetDir> [--json] [--config=<path>] — scan a directory for
|
|
3
|
+
// source languages and report, per detected language: the recommended language
|
|
4
|
+
// server, whether its executable is on PATH, an install hint, and whether the
|
|
5
|
+
// LitClaude .lsp.json already declares that language.
|
|
6
|
+
//
|
|
7
|
+
// Runtime: Node 22.6+ with `node --experimental-strip-types`, or `bun`.
|
|
8
|
+
// node --experimental-strip-types scripts/detect-lsp.ts <dir>
|
|
9
|
+
// bun scripts/detect-lsp.ts <dir>
|
|
10
|
+
|
|
11
|
+
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs"
|
|
12
|
+
import { delimiter, extname, join, sep } from "node:path"
|
|
13
|
+
import process from "node:process"
|
|
14
|
+
|
|
15
|
+
import { LANGUAGES, type LanguageServer, LSP_CONFIG_FILE } from "./lsp-server-table.ts"
|
|
16
|
+
|
|
17
|
+
const SKIP_DIRECTORIES = new Set<string>([
|
|
18
|
+
"node_modules",
|
|
19
|
+
".git",
|
|
20
|
+
"dist",
|
|
21
|
+
"build",
|
|
22
|
+
".next",
|
|
23
|
+
"out",
|
|
24
|
+
"target",
|
|
25
|
+
".venv",
|
|
26
|
+
"venv",
|
|
27
|
+
"vendor",
|
|
28
|
+
".cache",
|
|
29
|
+
"__pycache__",
|
|
30
|
+
".turbo",
|
|
31
|
+
"coverage",
|
|
32
|
+
])
|
|
33
|
+
|
|
34
|
+
const MAX_FILES = 50_000
|
|
35
|
+
|
|
36
|
+
interface ConfigState {
|
|
37
|
+
readonly path: string
|
|
38
|
+
readonly exists: boolean
|
|
39
|
+
readonly languages: readonly string[]
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
interface DetectionResult {
|
|
43
|
+
readonly server: LanguageServer
|
|
44
|
+
readonly executable: string
|
|
45
|
+
readonly installed: boolean
|
|
46
|
+
readonly resolvedPath: string | null
|
|
47
|
+
readonly configured: boolean
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function collectExtensions(root: string): ReadonlySet<string> {
|
|
51
|
+
const found = new Set<string>()
|
|
52
|
+
const stack: string[] = [root]
|
|
53
|
+
let visited = 0
|
|
54
|
+
|
|
55
|
+
while (stack.length > 0) {
|
|
56
|
+
const current = stack.pop()
|
|
57
|
+
if (current === undefined) break
|
|
58
|
+
|
|
59
|
+
let entries: string[]
|
|
60
|
+
try {
|
|
61
|
+
entries = readdirSync(current)
|
|
62
|
+
} catch {
|
|
63
|
+
continue
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
for (const entry of entries) {
|
|
67
|
+
if (visited >= MAX_FILES) return found
|
|
68
|
+
const fullPath = join(current, entry)
|
|
69
|
+
|
|
70
|
+
let kind: "dir" | "file" | "other" = "other"
|
|
71
|
+
try {
|
|
72
|
+
const stat = statSync(fullPath)
|
|
73
|
+
kind = stat.isDirectory() ? "dir" : stat.isFile() ? "file" : "other"
|
|
74
|
+
} catch {
|
|
75
|
+
continue
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (kind === "dir") {
|
|
79
|
+
if (!SKIP_DIRECTORIES.has(entry)) stack.push(fullPath)
|
|
80
|
+
} else if (kind === "file") {
|
|
81
|
+
visited += 1
|
|
82
|
+
const ext = extname(entry).toLowerCase()
|
|
83
|
+
if (ext.length > 0) found.add(ext)
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return found
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function pathDirectories(): readonly string[] {
|
|
92
|
+
return (process.env["PATH"] ?? "").split(delimiter).filter((dir: string) => dir.length > 0)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function resolveExecutable(command: string): string | null {
|
|
96
|
+
const extensions =
|
|
97
|
+
process.platform === "win32" ? (process.env["PATHEXT"]?.split(";") ?? [".EXE", ".CMD", ".BAT"]) : [""]
|
|
98
|
+
const bases =
|
|
99
|
+
command.includes("/") || command.includes(sep)
|
|
100
|
+
? [command]
|
|
101
|
+
: pathDirectories().map((dir: string) => join(dir, command))
|
|
102
|
+
|
|
103
|
+
for (const base of bases) {
|
|
104
|
+
for (const ext of extensions) {
|
|
105
|
+
const candidate = ext.length > 0 ? `${base}${ext}` : base
|
|
106
|
+
try {
|
|
107
|
+
if (existsSync(candidate) && statSync(candidate).isFile()) return candidate
|
|
108
|
+
} catch {
|
|
109
|
+
continue
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return null
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
117
|
+
return typeof value === "object" && value !== null && !Array.isArray(value)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// LitClaude .lsp.json is keyed by language name:
|
|
121
|
+
// { "typescript": { "command": [...], "extensionToLanguage": {...} } }
|
|
122
|
+
function parseConfiguredLanguages(path: string): readonly string[] {
|
|
123
|
+
let parsed: unknown
|
|
124
|
+
try {
|
|
125
|
+
parsed = JSON.parse(readFileSync(path, "utf-8"))
|
|
126
|
+
} catch {
|
|
127
|
+
return []
|
|
128
|
+
}
|
|
129
|
+
if (!isRecord(parsed)) return []
|
|
130
|
+
return Object.keys(parsed)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function readConfigState(configPath: string): ConfigState {
|
|
134
|
+
if (!existsSync(configPath)) return { path: configPath, exists: false, languages: [] }
|
|
135
|
+
return { path: configPath, exists: true, languages: parseConfiguredLanguages(configPath) }
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function detect(root: string, configured: readonly string[]): readonly DetectionResult[] {
|
|
139
|
+
const extensions = collectExtensions(root)
|
|
140
|
+
const configuredSet = new Set(configured)
|
|
141
|
+
const results: DetectionResult[] = []
|
|
142
|
+
|
|
143
|
+
for (const server of LANGUAGES) {
|
|
144
|
+
if (!server.extensions.some((ext: string) => extensions.has(ext))) continue
|
|
145
|
+
|
|
146
|
+
const executable = server.command[0] ?? server.language
|
|
147
|
+
const resolvedPath = resolveExecutable(executable)
|
|
148
|
+
results.push({
|
|
149
|
+
server,
|
|
150
|
+
executable,
|
|
151
|
+
installed: resolvedPath !== null,
|
|
152
|
+
resolvedPath,
|
|
153
|
+
configured: configuredSet.has(server.language),
|
|
154
|
+
})
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return results
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function renderReport(root: string, results: readonly DetectionResult[], config: ConfigState): string {
|
|
161
|
+
const lines: string[] = [`LSP setup scan: ${root}`]
|
|
162
|
+
lines.push(
|
|
163
|
+
`Config file: ${config.path} (${config.exists ? `present: ${config.languages.length} language(s)` : "absent"})`,
|
|
164
|
+
"",
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
if (results.length === 0) {
|
|
168
|
+
lines.push("No languages with a recommended LSP server were detected here.")
|
|
169
|
+
return lines.join("\n")
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
lines.push("DETECTED LANGUAGES (recommended server per language)")
|
|
173
|
+
for (const result of results) {
|
|
174
|
+
const mark = result.installed ? "OK " : "MISS"
|
|
175
|
+
const state = result.installed ? `installed (${result.resolvedPath})` : "NOT installed"
|
|
176
|
+
const config = result.configured ? "declared in .lsp.json" : "NOT in .lsp.json"
|
|
177
|
+
lines.push(
|
|
178
|
+
`[${mark}] ${result.server.language.padEnd(12)} exe=${result.executable} ${state} ${config}`,
|
|
179
|
+
)
|
|
180
|
+
if (!result.installed) lines.push(` install: ${result.server.installHint}`)
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const missing = results.filter((result) => !result.installed)
|
|
184
|
+
const notConfigured = results.filter((result) => !result.configured)
|
|
185
|
+
lines.push(
|
|
186
|
+
"",
|
|
187
|
+
missing.length === 0
|
|
188
|
+
? `All ${results.length} detected server(s) installed.`
|
|
189
|
+
: `${missing.length}/${results.length} server(s) NOT installed: ${missing.map((m) => m.server.language).join(", ")}`,
|
|
190
|
+
notConfigured.length === 0
|
|
191
|
+
? "All detected languages are declared in .lsp.json."
|
|
192
|
+
: `${notConfigured.length} language(s) absent from .lsp.json: ${notConfigured.map((m) => m.server.language).join(", ")}`,
|
|
193
|
+
"Next: read references/<language>/README.md, then add the language to plugins/litclaude/.lsp.json.",
|
|
194
|
+
)
|
|
195
|
+
return lines.join("\n")
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function main(): void {
|
|
199
|
+
const args = process.argv.slice(2)
|
|
200
|
+
const wantsJson = args.includes("--json")
|
|
201
|
+
const configFlag = args.find((arg: string) => arg.startsWith("--config="))
|
|
202
|
+
const configPath = configFlag ? configFlag.slice("--config=".length) : LSP_CONFIG_FILE
|
|
203
|
+
const root = args.find((arg: string) => !arg.startsWith("--")) ?? process.cwd()
|
|
204
|
+
|
|
205
|
+
if (!existsSync(root)) {
|
|
206
|
+
process.stderr.write(`detect-lsp: target directory does not exist: ${root}\n`)
|
|
207
|
+
process.exit(2)
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const config = readConfigState(configPath)
|
|
211
|
+
const results = detect(root, config.languages)
|
|
212
|
+
|
|
213
|
+
if (wantsJson) {
|
|
214
|
+
process.stdout.write(`${JSON.stringify({ root, config, results }, null, 2)}\n`)
|
|
215
|
+
return
|
|
216
|
+
}
|
|
217
|
+
process.stdout.write(`${renderReport(root, results, config)}\n`)
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
main()
|