claudecode-linter 2.1.138 → 2.1.139

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.
@@ -9,6 +9,7 @@
9
9
  rules:
10
10
  # ── plugin.json ──────────────────────────────────────────
11
11
  plugin-json/valid-json: error
12
+ plugin-json/schema-valid: error
12
13
  plugin-json/name-required: error
13
14
  plugin-json/name-kebab-case: error
14
15
  plugin-json/name-length: error
@@ -110,3 +111,27 @@ rules:
110
111
  claude-md/no-absolute-paths: info
111
112
  claude-md/no-todo-markers: info
112
113
  claude-md/no-trailing-whitespace: info
114
+
115
+ # ── .lsp.json ────────────────────────────────────────────
116
+ # Standalone LSP config files at plugin root. Most common bug:
117
+ # wrapping content under a top-level "lspServers" key, which only
118
+ # belongs inline in plugin.json. Claude Code parses with
119
+ # E.record(E.string(), <per-server>).safeParse — wrapped files
120
+ # validate as a single (invalid) server entry at session start.
121
+ lsp-json/valid-json: error
122
+ lsp-json/no-lsp-servers-wrapper: error
123
+ lsp-json/schema-valid: error
124
+
125
+ # ── monitors/monitors.json ───────────────────────────────
126
+ # Array of monitor entries. Names must be unique within a plugin
127
+ # (Claude Code refines this; not expressible in JSON Schema).
128
+ monitors-json/valid-json: error
129
+ monitors-json/schema-valid: error
130
+ monitors-json/unique-names: error
131
+
132
+ # ── Misplaced files ──────────────────────────────────────
133
+ # Reserved artifact basenames (plugin.json, hooks.json,
134
+ # marketplace.json, SKILL.md) found at non-canonical paths inside
135
+ # a plugin tree. Claude Code silently ignores them — easy to make,
136
+ # hard to debug.
137
+ misplaced-file/canonical-location: warning
package/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
 
8
8
  Standalone linter for [Claude Code](https://docs.anthropic.com/en/docs/claude-code) plugins and configuration files.
9
9
 
10
- Validates `plugin.json`, `SKILL.md`, agent/command markdown, `hooks.json`, `mcp.json`, `settings.json`, and `CLAUDE.md` files with 90 rules across 8 artifact types.
10
+ Validates `plugin.json`, `SKILL.md`, agent/command markdown, `hooks.json`, `mcp.json`, `settings.json`, `CLAUDE.md`, `.lsp.json`, and `monitors/monitors.json` files. plugin.json/lsp.json/monitors.json are checked against JSON Schemas auto-extracted from Claude Code's runtime Zod validators — failures the linter reports are the same failures Claude Code would raise at session start.
11
11
 
12
12
  ![demo](assets/demo.gif)
13
13
 
@@ -111,7 +111,7 @@ No issues found.
111
111
 
112
112
  | Type | Files | Rules |
113
113
  |------|-------|-------|
114
- | plugin-json | `.claude-plugin/plugin.json` | 12 |
114
+ | plugin-json | `.claude-plugin/plugin.json` | 13 |
115
115
  | skill-md | `skills/*/SKILL.md` | 11 |
116
116
  | agent-md | `agents/*.md` | 13 |
117
117
  | command-md | `commands/*.md` | 5 |
@@ -119,6 +119,24 @@ No issues found.
119
119
  | settings-json | `.claude-plugin/settings.json` | 14 |
120
120
  | mcp-json | `.claude-plugin/mcp.json` | 16 |
121
121
  | claude-md | `CLAUDE.md` | 10 |
122
+ | lsp-json | `.lsp.json` | 3 |
123
+ | monitors-json | `monitors/monitors.json` | 3 |
124
+
125
+ ## Schema-derived rules
126
+
127
+ `plugin-json/schema-valid`, `lsp-json/schema-valid`, and `monitors-json/schema-valid` validate against JSON Schemas that are *auto-extracted from Claude Code's cli.js bundle* — same Zod schemas that the runtime calls `.safeParse(content)` on. This catches:
128
+
129
+ - **Missing required fields** (e.g., LSP server without `extensionToLanguage`)
130
+ - **Wrong field types** (e.g., `name: 42` instead of a string)
131
+ - **Invalid enum values** (e.g., `transport: "websocket"` when only `stdio`/`socket` are accepted)
132
+ - **Nested-shape violations** in discriminated unions (e.g., `mcpServers.x.type: "telegraph"` when only `stdio`/`sse`/`http`/... are accepted)
133
+ - **Unknown fields** in strict objects (e.g., `filetypes`/`rootPatterns` inside an LSP server config — common confusion with editor-LSP shapes from other ecosystems)
134
+
135
+ The schemas live at `contracts/{plugin,lsp,monitors}.schema.json` and are regenerated by `npm run extract-contracts` on every Claude Code release.
136
+
137
+ `lsp-json/no-lsp-servers-wrapper` catches a specific authoring mistake: putting your `.lsp.json` content under a top-level `lspServers` key. That wrapper belongs **inline in plugin.json** only — the dedicated file is a flat map of server-name → config.
138
+
139
+ `monitors-json/unique-names` enforces Claude Code's "monitor names must be unique within a plugin" check (a `refine()` predicate that JSON Schema can't express natively).
122
140
 
123
141
  ## Configuration
124
142
 
@@ -0,0 +1,82 @@
1
+ {
2
+ "extractedFromClaudeCodeVersion": "2.1.138",
3
+ "extractedAt": "2026-05-11T08:02:42.911Z",
4
+ "schema": {
5
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
6
+ "title": "Claude Code .lsp.json",
7
+ "type": "object",
8
+ "additionalProperties": {
9
+ "type": "object",
10
+ "properties": {
11
+ "command": {
12
+ "type": "string",
13
+ "minLength": 1,
14
+ "description": "Command to execute the LSP server (e.g., \"typescript-language-server\")"
15
+ },
16
+ "args": {
17
+ "type": "array",
18
+ "items": {
19
+ "type": "string",
20
+ "minLength": 1
21
+ },
22
+ "description": "Command-line arguments to pass to the server"
23
+ },
24
+ "extensionToLanguage": {
25
+ "type": "object",
26
+ "additionalProperties": {
27
+ "type": "string",
28
+ "minLength": 1
29
+ },
30
+ "description": "Mapping from file extension to LSP language ID. File extensions and languages are derived from this mapping."
31
+ },
32
+ "transport": {
33
+ "enum": [
34
+ "stdio",
35
+ "socket"
36
+ ],
37
+ "default": "stdio",
38
+ "description": "Communication transport mechanism"
39
+ },
40
+ "env": {
41
+ "type": "object",
42
+ "additionalProperties": {
43
+ "type": "string"
44
+ },
45
+ "description": "Environment variables to set when starting the server"
46
+ },
47
+ "initializationOptions": {
48
+ "description": "Initialization options passed to the server during initialization"
49
+ },
50
+ "settings": {
51
+ "description": "Settings passed to the server via workspace/didChangeConfiguration"
52
+ },
53
+ "workspaceFolder": {
54
+ "type": "string",
55
+ "description": "Workspace folder path to use for the server"
56
+ },
57
+ "startupTimeout": {
58
+ "type": "number",
59
+ "description": "Maximum time to wait for server startup (milliseconds)"
60
+ },
61
+ "shutdownTimeout": {
62
+ "type": "number",
63
+ "description": "Maximum time to wait for graceful shutdown (milliseconds)"
64
+ },
65
+ "restartOnCrash": {
66
+ "type": "boolean",
67
+ "description": "Whether to restart the server if it crashes"
68
+ },
69
+ "maxRestarts": {
70
+ "type": "number",
71
+ "description": "Maximum number of restart attempts before giving up"
72
+ }
73
+ },
74
+ "required": [
75
+ "command",
76
+ "extensionToLanguage"
77
+ ],
78
+ "additionalProperties": false
79
+ },
80
+ "description": "Flat map of server-name → LSP server config. The top-level keys are server names; their values match the per-server schema. This file is loaded by Claude Code when present at the plugin root."
81
+ }
82
+ }
@@ -0,0 +1,48 @@
1
+ {
2
+ "extractedFromClaudeCodeVersion": "2.1.138",
3
+ "extractedAt": "2026-05-11T08:02:42.913Z",
4
+ "schema": {
5
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
6
+ "title": "Claude Code monitors.json",
7
+ "type": "array",
8
+ "items": {
9
+ "type": "object",
10
+ "properties": {
11
+ "name": {
12
+ "type": "string",
13
+ "minLength": 1,
14
+ "description": "Identifier for this monitor, unique within the plugin. Used to dedupe so re-arming (plugin reload, repeat skill invoke) does not spawn duplicates."
15
+ },
16
+ "command": {
17
+ "type": "string",
18
+ "minLength": 1,
19
+ "description": "Shell command to run as a persistent background monitor. Each stdout line is delivered to the model as a <task_notification> event; the process runs for the session lifetime. ${CLAUDE_PLUGIN_ROOT}, ${CLAUDE_PLUGIN_DATA}, ${user_config.*}, and ${ENV_VAR} are substituted. Runs in the session cwd \\u2014 prefix with `cd \"${CLAUDE_PLUGIN_ROOT}\" && ` if the script needs its own directory."
20
+ },
21
+ "description": {
22
+ "type": "string",
23
+ "minLength": 1,
24
+ "description": "Short human-readable description of what is being monitored (shown in task panel and notification summary)."
25
+ },
26
+ "when": {
27
+ "anyOf": [
28
+ {
29
+ "const": "always"
30
+ },
31
+ {
32
+ "type": "string"
33
+ }
34
+ ],
35
+ "default": "always",
36
+ "description": "Arm trigger. \"always\" arms at session start and on plugin reload. \"on-skill-invoke:<skill>\" arms the first time that skill is dispatched (via Skill tool or slash command)."
37
+ }
38
+ },
39
+ "required": [
40
+ "name",
41
+ "command",
42
+ "description"
43
+ ],
44
+ "additionalProperties": false
45
+ },
46
+ "description": "Array of monitor entries. Each monitor's `name` must be unique within the plugin (Claude Code enforces this with a refine() check that is not expressible in JSON Schema; the monitors-json linter does it separately)."
47
+ }
48
+ }