claudecode-linter 0.1.0
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/.claudecode-lint.defaults.yaml +112 -0
- package/LICENSE +21 -0
- package/README.md +181 -0
- package/dist/config.d.ts +3 -0
- package/dist/config.js +66 -0
- package/dist/config.js.map +1 -0
- package/dist/discovery.d.ts +8 -0
- package/dist/discovery.js +219 -0
- package/dist/discovery.js.map +1 -0
- package/dist/fixers/claude-md.d.ts +2 -0
- package/dist/fixers/claude-md.js +24 -0
- package/dist/fixers/claude-md.js.map +1 -0
- package/dist/fixers/frontmatter.d.ts +2 -0
- package/dist/fixers/frontmatter.js +85 -0
- package/dist/fixers/frontmatter.js.map +1 -0
- package/dist/fixers/hooks-json.d.ts +2 -0
- package/dist/fixers/hooks-json.js +23 -0
- package/dist/fixers/hooks-json.js.map +1 -0
- package/dist/fixers/mcp-json.d.ts +2 -0
- package/dist/fixers/mcp-json.js +47 -0
- package/dist/fixers/mcp-json.js.map +1 -0
- package/dist/fixers/plugin-json.d.ts +2 -0
- package/dist/fixers/plugin-json.js +34 -0
- package/dist/fixers/plugin-json.js.map +1 -0
- package/dist/fixers/settings-json.d.ts +2 -0
- package/dist/fixers/settings-json.js +47 -0
- package/dist/fixers/settings-json.js.map +1 -0
- package/dist/formatters/human.d.ts +2 -0
- package/dist/formatters/human.js +47 -0
- package/dist/formatters/human.js.map +1 -0
- package/dist/formatters/json.d.ts +2 -0
- package/dist/formatters/json.js +10 -0
- package/dist/formatters/json.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +246 -0
- package/dist/index.js.map +1 -0
- package/dist/linters/agent-md.d.ts +8 -0
- package/dist/linters/agent-md.js +92 -0
- package/dist/linters/agent-md.js.map +1 -0
- package/dist/linters/claude-md.d.ts +8 -0
- package/dist/linters/claude-md.js +109 -0
- package/dist/linters/claude-md.js.map +1 -0
- package/dist/linters/command-md.d.ts +8 -0
- package/dist/linters/command-md.js +61 -0
- package/dist/linters/command-md.js.map +1 -0
- package/dist/linters/hooks-json.d.ts +8 -0
- package/dist/linters/hooks-json.js +123 -0
- package/dist/linters/hooks-json.js.map +1 -0
- package/dist/linters/mcp-json.d.ts +8 -0
- package/dist/linters/mcp-json.js +160 -0
- package/dist/linters/mcp-json.js.map +1 -0
- package/dist/linters/plugin-json.d.ts +8 -0
- package/dist/linters/plugin-json.js +166 -0
- package/dist/linters/plugin-json.js.map +1 -0
- package/dist/linters/settings-json.d.ts +8 -0
- package/dist/linters/settings-json.js +187 -0
- package/dist/linters/settings-json.js.map +1 -0
- package/dist/linters/skill-md.d.ts +8 -0
- package/dist/linters/skill-md.js +97 -0
- package/dist/linters/skill-md.js.map +1 -0
- package/dist/types.d.ts +39 -0
- package/dist/types.js +15 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/frontmatter.d.ts +9 -0
- package/dist/utils/frontmatter.js +65 -0
- package/dist/utils/frontmatter.js.map +1 -0
- package/dist/utils/kebab-case.d.ts +2 -0
- package/dist/utils/kebab-case.js +14 -0
- package/dist/utils/kebab-case.js.map +1 -0
- package/dist/utils/prettier.d.ts +2 -0
- package/dist/utils/prettier.js +17 -0
- package/dist/utils/prettier.js.map +1 -0
- package/package.json +53 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# claudecode-lint default configuration
|
|
2
|
+
# Copy this file to .claudecode-lint.yaml in your project root to customize.
|
|
3
|
+
# Set a rule to `false` to disable it, or override its severity.
|
|
4
|
+
#
|
|
5
|
+
# Examples:
|
|
6
|
+
# plugin-json/no-unknown-fields: false
|
|
7
|
+
# claude-md/file-length: { enabled: true, severity: error }
|
|
8
|
+
|
|
9
|
+
rules:
|
|
10
|
+
# ── plugin.json ──────────────────────────────────────────
|
|
11
|
+
plugin-json/valid-json: error
|
|
12
|
+
plugin-json/name-required: error
|
|
13
|
+
plugin-json/name-kebab-case: error
|
|
14
|
+
plugin-json/name-length: error
|
|
15
|
+
plugin-json/description-required: warning
|
|
16
|
+
plugin-json/version-semver: warning
|
|
17
|
+
plugin-json/author-object: info
|
|
18
|
+
plugin-json/repository-url: warning
|
|
19
|
+
plugin-json/keywords-array: warning
|
|
20
|
+
plugin-json/keywords-no-duplicates: warning
|
|
21
|
+
plugin-json/no-unknown-fields: info
|
|
22
|
+
plugin-json/license-spdx: info
|
|
23
|
+
|
|
24
|
+
# ── SKILL.md ─────────────────────────────────────────────
|
|
25
|
+
skill-md/valid-frontmatter: error
|
|
26
|
+
skill-md/name-required: error
|
|
27
|
+
skill-md/name-kebab-case: error
|
|
28
|
+
skill-md/name-max-length: error
|
|
29
|
+
skill-md/description-required: error
|
|
30
|
+
skill-md/description-max-length: error
|
|
31
|
+
skill-md/description-no-angle-brackets: error
|
|
32
|
+
skill-md/description-trigger-phrases: warning
|
|
33
|
+
skill-md/no-unknown-frontmatter: warning
|
|
34
|
+
skill-md/body-word-count: warning
|
|
35
|
+
skill-md/body-has-headers: info
|
|
36
|
+
|
|
37
|
+
# ── Agent .md ────────────────────────────────────────────
|
|
38
|
+
agent-md/valid-frontmatter: error
|
|
39
|
+
agent-md/name-required: error
|
|
40
|
+
agent-md/name-format: error
|
|
41
|
+
agent-md/description-required: error
|
|
42
|
+
agent-md/description-examples: warning
|
|
43
|
+
agent-md/model-required: error
|
|
44
|
+
agent-md/model-valid: warning
|
|
45
|
+
agent-md/color-required: error
|
|
46
|
+
agent-md/color-valid: warning
|
|
47
|
+
agent-md/system-prompt-present: error
|
|
48
|
+
agent-md/system-prompt-length: warning
|
|
49
|
+
agent-md/system-prompt-second-person: info
|
|
50
|
+
|
|
51
|
+
# ── Command .md ──────────────────────────────────────────
|
|
52
|
+
command-md/valid-frontmatter: error
|
|
53
|
+
command-md/description-required: error
|
|
54
|
+
command-md/allowed-tools-valid: warning
|
|
55
|
+
command-md/body-present: warning
|
|
56
|
+
|
|
57
|
+
# ── hooks.json ───────────────────────────────────────────
|
|
58
|
+
hooks-json/valid-json: error
|
|
59
|
+
hooks-json/root-hooks-key: error
|
|
60
|
+
hooks-json/valid-event-names: error
|
|
61
|
+
hooks-json/hook-type-required: error
|
|
62
|
+
hooks-json/command-has-command: error
|
|
63
|
+
hooks-json/no-hardcoded-paths: warning
|
|
64
|
+
hooks-json/prompt-has-prompt: error
|
|
65
|
+
hooks-json/prompt-event-support: warning
|
|
66
|
+
hooks-json/timeout-range: warning
|
|
67
|
+
|
|
68
|
+
# ── settings.json ────────────────────────────────────────
|
|
69
|
+
settings-json/valid-json: error
|
|
70
|
+
settings-json/scope-file-name: error
|
|
71
|
+
settings-json/scope-field: warning
|
|
72
|
+
settings-json/no-unknown-fields: warning
|
|
73
|
+
settings-json/permissions-object: error
|
|
74
|
+
settings-json/allow-array: error
|
|
75
|
+
settings-json/allow-known-tools: warning
|
|
76
|
+
settings-json/deny-array: error
|
|
77
|
+
settings-json/env-object: error
|
|
78
|
+
settings-json/env-string-values: warning
|
|
79
|
+
settings-json/plugins-object: error
|
|
80
|
+
settings-json/plugins-boolean: warning
|
|
81
|
+
settings-json/plugins-format: warning
|
|
82
|
+
settings-json/skip-prompt-boolean: error
|
|
83
|
+
|
|
84
|
+
# ── mcp.json ─────────────────────────────────────────────
|
|
85
|
+
mcp-json/scope-file-name: warning
|
|
86
|
+
mcp-json/valid-json: error
|
|
87
|
+
mcp-json/servers-required: error
|
|
88
|
+
mcp-json/servers-object: error
|
|
89
|
+
mcp-json/server-name-kebab: info
|
|
90
|
+
mcp-json/server-object: error
|
|
91
|
+
mcp-json/server-transport: error
|
|
92
|
+
mcp-json/url-protocol: warning
|
|
93
|
+
mcp-json/url-valid: error
|
|
94
|
+
mcp-json/type-matches-transport: warning
|
|
95
|
+
mcp-json/command-args-split: info
|
|
96
|
+
mcp-json/args-array: error
|
|
97
|
+
mcp-json/env-object: error
|
|
98
|
+
mcp-json/env-string-values: warning
|
|
99
|
+
mcp-json/no-unknown-server-fields: info
|
|
100
|
+
mcp-json/no-unknown-root-fields: info
|
|
101
|
+
|
|
102
|
+
# ── CLAUDE.md ────────────────────────────────────────────
|
|
103
|
+
claude-md/not-empty: warning
|
|
104
|
+
claude-md/starts-with-heading: info
|
|
105
|
+
claude-md/has-sections: warning
|
|
106
|
+
claude-md/user-level-concise: info
|
|
107
|
+
claude-md/project-has-overview: info
|
|
108
|
+
claude-md/no-secrets: error
|
|
109
|
+
claude-md/file-length: warning
|
|
110
|
+
claude-md/no-absolute-paths: info
|
|
111
|
+
claude-md/no-todo-markers: info
|
|
112
|
+
claude-md/no-trailing-whitespace: info
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 oleks
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
# claudecode-linter
|
|
2
|
+
|
|
3
|
+
Standalone linter for [Claude Code](https://docs.anthropic.com/en/docs/claude-code) plugin artifacts.
|
|
4
|
+
|
|
5
|
+
Validates `plugin.json`, `SKILL.md`, agent/command markdown, `hooks.json`, `mcp.json`, `settings.json`, and `CLAUDE.md` files with 88 rules across 8 artifact types.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install -g claudecode-linter
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Or run directly:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npx claudecode-linter ~/projects/my-plugin/
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
### Lint
|
|
22
|
+
|
|
23
|
+
Check plugin artifacts for errors without modifying files. This is the default mode — `--lint` is optional:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
# Lint a plugin directory
|
|
27
|
+
claudecode-linter --lint path/to/plugin/
|
|
28
|
+
claudecode-linter path/to/plugin/ # same thing
|
|
29
|
+
|
|
30
|
+
# Lint multiple paths
|
|
31
|
+
claudecode-linter plugin-a/ plugin-b/
|
|
32
|
+
|
|
33
|
+
# JSON output
|
|
34
|
+
claudecode-linter --output json path/to/plugin/
|
|
35
|
+
|
|
36
|
+
# Errors only
|
|
37
|
+
claudecode-linter --quiet path/to/plugin/
|
|
38
|
+
|
|
39
|
+
# Filter by rule
|
|
40
|
+
claudecode-linter --rule plugin-json/name-kebab-case path/to/plugin/
|
|
41
|
+
|
|
42
|
+
# Enable/disable specific rules
|
|
43
|
+
claudecode-linter --enable skill-md/word-count --disable claude-md/no-todos path/to/plugin/
|
|
44
|
+
|
|
45
|
+
# List all available rules
|
|
46
|
+
claudecode-linter --list-rules
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Format
|
|
50
|
+
|
|
51
|
+
Reformat all artifacts for consistent style (sorted keys, normalized indentation, trailing whitespace, kebab-case names, quoted YAML values). No lint output — just formats and reports what changed:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
# Format all artifacts in place
|
|
55
|
+
claudecode-linter --format path/to/plugin/
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Fix
|
|
59
|
+
|
|
60
|
+
Fix lint violations in place, then lint the result — output shows only issues that remain after fixing:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
# Fix issues in place
|
|
64
|
+
claudecode-linter --fix path/to/plugin/
|
|
65
|
+
|
|
66
|
+
# Preview fixes without writing (shows diff)
|
|
67
|
+
claudecode-linter --fix-dry-run path/to/plugin/
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Example Output
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
$ claudecode-linter my-plugin/
|
|
74
|
+
|
|
75
|
+
my-plugin/skills/example/SKILL.md
|
|
76
|
+
warn Body has 117 words (recommended: 500-5000) skill-md/body-word-count:5
|
|
77
|
+
|
|
78
|
+
my-plugin/.claude/settings.json
|
|
79
|
+
error "settings.json" should only exist at user level (~/.claude/).
|
|
80
|
+
Use "settings.local.json" for project-level settings settings-json/scope-file-name
|
|
81
|
+
warn "env" is a user-level field — it has no effect in
|
|
82
|
+
project-level settings.local.json settings-json/scope-field:9:3
|
|
83
|
+
|
|
84
|
+
1 error, 2 warnings
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
```
|
|
88
|
+
$ claudecode-linter --fix-dry-run my-plugin/
|
|
89
|
+
|
|
90
|
+
--- my-plugin/skills/deploy/SKILL.md
|
|
91
|
+
+++ my-plugin/skills/deploy/SKILL.md (fixed)
|
|
92
|
+
-name: My Deploy Skill
|
|
93
|
+
+name: my-deploy-skill
|
|
94
|
+
-description: Use when the user asks to "deploy": handles both cases.
|
|
95
|
+
+description: "Use when the user asks to \"deploy\": handles both cases."
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
```
|
|
99
|
+
$ claudecode-linter my-plugin/
|
|
100
|
+
No issues found.
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Artifact Types
|
|
104
|
+
|
|
105
|
+
| Type | Files | Rules |
|
|
106
|
+
|------|-------|-------|
|
|
107
|
+
| plugin-json | `.claude-plugin/plugin.json` | 12 |
|
|
108
|
+
| skill-md | `skills/*/SKILL.md` | 11 |
|
|
109
|
+
| agent-md | `agents/*.md` | 12 |
|
|
110
|
+
| command-md | `commands/*.md` | 4 |
|
|
111
|
+
| hooks-json | `hooks/hooks.json` | 9 |
|
|
112
|
+
| settings-json | `.claude-plugin/settings.json` | 8 |
|
|
113
|
+
| mcp-json | `.claude-plugin/mcp.json` | 10 |
|
|
114
|
+
| claude-md | `CLAUDE.md` | 22 |
|
|
115
|
+
|
|
116
|
+
## Configuration
|
|
117
|
+
|
|
118
|
+
Generate a config file with all rules and their default severities:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
# Create .claudecode-lint.yaml in current directory
|
|
122
|
+
claudecode-linter --init
|
|
123
|
+
|
|
124
|
+
# Create in a specific directory
|
|
125
|
+
claudecode-linter --init ~/projects/my-plugin/
|
|
126
|
+
|
|
127
|
+
# Create in home directory (applies globally)
|
|
128
|
+
claudecode-linter --init ~
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
claudecode-linter looks for config in this order:
|
|
132
|
+
|
|
133
|
+
1. `.claudecode-lint.yaml` or `.claudecode-lint.yml` in the current directory
|
|
134
|
+
2. `.claudecode-lint.yaml` or `.claudecode-lint.yml` in `$HOME`
|
|
135
|
+
3. Bundled defaults (all rules enabled at their default severity)
|
|
136
|
+
|
|
137
|
+
Example config:
|
|
138
|
+
|
|
139
|
+
```yaml
|
|
140
|
+
rules:
|
|
141
|
+
plugin-json/name-kebab-case: true
|
|
142
|
+
skill-md/word-count:
|
|
143
|
+
severity: warning
|
|
144
|
+
min: 50
|
|
145
|
+
claude-md/no-todos: false
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Fixers
|
|
149
|
+
|
|
150
|
+
Both `--format` and `--fix` run the same fixers. The difference: `--format` only formats and reports changes, `--fix` also lints the result afterwards.
|
|
151
|
+
|
|
152
|
+
Formatting is powered by [prettier](https://prettier.io/) for consistent JSON and markdown output. Custom logic handles domain-specific transformations that prettier can't (key sorting, YAML fixes, kebab-case normalization).
|
|
153
|
+
|
|
154
|
+
| Artifact | Prettier | Custom logic |
|
|
155
|
+
|----------|----------|--------------|
|
|
156
|
+
| plugin-json | Tab-indented JSON | Canonical key ordering |
|
|
157
|
+
| hooks-json | 2-space JSON | Alphabetical key sorting |
|
|
158
|
+
| mcp-json | 2-space JSON | Server name sorting, canonical field ordering |
|
|
159
|
+
| settings-json | 2-space JSON | Canonical key ordering, permission array sorting |
|
|
160
|
+
| skill-md / agent-md / command-md | Markdown body | Frontmatter YAML normalization, kebab-case names, pre-parse quoting |
|
|
161
|
+
| claude-md | Markdown | Blank line before headings |
|
|
162
|
+
|
|
163
|
+
## Exit Codes
|
|
164
|
+
|
|
165
|
+
| Code | Meaning |
|
|
166
|
+
|------|---------|
|
|
167
|
+
| 0 | No errors |
|
|
168
|
+
| 1 | Lint errors found |
|
|
169
|
+
| 2 | Fatal error |
|
|
170
|
+
|
|
171
|
+
## Development
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
npm install
|
|
175
|
+
npm run build
|
|
176
|
+
npm test
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## License
|
|
180
|
+
|
|
181
|
+
MIT
|
package/dist/config.d.ts
ADDED
package/dist/config.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { readFileSync, existsSync } from "node:fs";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { homedir } from "node:os";
|
|
5
|
+
import { parse as parseYaml } from "yaml";
|
|
6
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
const DEFAULT_CONFIG = {
|
|
8
|
+
rules: {},
|
|
9
|
+
};
|
|
10
|
+
export function loadConfig(configPath) {
|
|
11
|
+
const path = configPath ?? findConfigFile();
|
|
12
|
+
if (!path)
|
|
13
|
+
return DEFAULT_CONFIG;
|
|
14
|
+
try {
|
|
15
|
+
const content = readFileSync(path, "utf-8");
|
|
16
|
+
const parsed = parseYaml(content);
|
|
17
|
+
if (!parsed || typeof parsed !== "object")
|
|
18
|
+
return DEFAULT_CONFIG;
|
|
19
|
+
const config = { rules: {} };
|
|
20
|
+
if (parsed.rules && typeof parsed.rules === "object") {
|
|
21
|
+
for (const [key, value] of Object.entries(parsed.rules)) {
|
|
22
|
+
if (typeof value === "boolean") {
|
|
23
|
+
config.rules[key] = value;
|
|
24
|
+
}
|
|
25
|
+
else if (typeof value === "object" && value !== null) {
|
|
26
|
+
config.rules[key] = value;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return config;
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return DEFAULT_CONFIG;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function findConfigFile() {
|
|
37
|
+
// 1. Check cwd
|
|
38
|
+
const cwdCandidates = [".claudecode-lint.yaml", ".claudecode-lint.yml"];
|
|
39
|
+
for (const name of cwdCandidates) {
|
|
40
|
+
if (existsSync(name))
|
|
41
|
+
return name;
|
|
42
|
+
}
|
|
43
|
+
// 2. Check home directory
|
|
44
|
+
const home = homedir();
|
|
45
|
+
for (const name of cwdCandidates) {
|
|
46
|
+
const homePath = join(home, name);
|
|
47
|
+
if (existsSync(homePath))
|
|
48
|
+
return homePath;
|
|
49
|
+
}
|
|
50
|
+
// 3. Fall back to bundled defaults
|
|
51
|
+
const bundled = join(__dirname, "..", ".claudecode-lint.defaults.yaml");
|
|
52
|
+
if (existsSync(bundled))
|
|
53
|
+
return bundled;
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
export function mergeCliRules(config, enable, disable) {
|
|
57
|
+
const merged = { rules: { ...config.rules } };
|
|
58
|
+
for (const rule of enable) {
|
|
59
|
+
merged.rules[rule] = true;
|
|
60
|
+
}
|
|
61
|
+
for (const rule of disable) {
|
|
62
|
+
merged.rules[rule] = false;
|
|
63
|
+
}
|
|
64
|
+
return merged;
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ConfigScope, DiscoveredArtifact } from "./types.js";
|
|
2
|
+
export interface DiscoverOptions {
|
|
3
|
+
/** Filter artifacts by scope, or override detected scope */
|
|
4
|
+
scope?: ConfigScope;
|
|
5
|
+
/** Glob patterns to ignore (in addition to .claudecode-lint-ignore) */
|
|
6
|
+
ignore?: string[];
|
|
7
|
+
}
|
|
8
|
+
export declare function discoverArtifacts(targetPath: string, options?: DiscoverOptions): DiscoveredArtifact[];
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import { statSync, existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { basename, dirname, resolve, join } from "node:path";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { globSync } from "glob";
|
|
5
|
+
import { minimatch } from "minimatch";
|
|
6
|
+
const CLAUDE_USER_DIR = join(homedir(), ".claude");
|
|
7
|
+
function loadIgnoreFile(dir) {
|
|
8
|
+
const ignoreFile = join(dir, ".claudecode-lint-ignore");
|
|
9
|
+
if (!existsSync(ignoreFile))
|
|
10
|
+
return [];
|
|
11
|
+
return readFileSync(ignoreFile, "utf-8")
|
|
12
|
+
.split("\n")
|
|
13
|
+
.map((line) => line.trim())
|
|
14
|
+
.filter((line) => line.length > 0 && !line.startsWith("#"));
|
|
15
|
+
}
|
|
16
|
+
function isIgnored(filePath, patterns) {
|
|
17
|
+
if (patterns.length === 0)
|
|
18
|
+
return false;
|
|
19
|
+
const abs = resolve(filePath);
|
|
20
|
+
for (const pattern of patterns) {
|
|
21
|
+
// Match against absolute path
|
|
22
|
+
if (minimatch(abs, pattern, { matchBase: true, dot: true }))
|
|
23
|
+
return true;
|
|
24
|
+
// Also match against the basename alone (for simple patterns like "*.md")
|
|
25
|
+
if (minimatch(basename(abs), pattern, { dot: true }))
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
export function discoverArtifacts(targetPath, options) {
|
|
31
|
+
const resolved = resolve(targetPath);
|
|
32
|
+
const stat = statSync(resolved);
|
|
33
|
+
// Combine .claudecode-lint-ignore patterns with CLI --ignore patterns
|
|
34
|
+
const ignoreDir = stat.isDirectory() ? resolved : dirname(resolved);
|
|
35
|
+
const ignorePatterns = [
|
|
36
|
+
...loadIgnoreFile(ignoreDir),
|
|
37
|
+
...(options?.ignore ?? []),
|
|
38
|
+
];
|
|
39
|
+
let artifacts;
|
|
40
|
+
if (!stat.isDirectory()) {
|
|
41
|
+
const type = classifyFile(resolved);
|
|
42
|
+
if (type) {
|
|
43
|
+
artifacts = [{ filePath: resolved, artifactType: type, scope: detectScope(resolved) }];
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
artifacts = [];
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
artifacts = discoverInDirectory(resolved);
|
|
51
|
+
// If targeting home dir, also discover in ~/.claude/
|
|
52
|
+
const home = homedir();
|
|
53
|
+
if (resolved === home && existsSync(CLAUDE_USER_DIR)) {
|
|
54
|
+
const userArtifacts = discoverInDirectory(CLAUDE_USER_DIR);
|
|
55
|
+
// Deduplicate by filePath
|
|
56
|
+
const seen = new Set(artifacts.map((a) => a.filePath));
|
|
57
|
+
for (const a of userArtifacts) {
|
|
58
|
+
if (!seen.has(a.filePath))
|
|
59
|
+
artifacts.push(a);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// Apply ignore patterns
|
|
64
|
+
if (ignorePatterns.length > 0) {
|
|
65
|
+
artifacts = artifacts.filter((a) => !isIgnored(a.filePath, ignorePatterns));
|
|
66
|
+
}
|
|
67
|
+
// Apply scope filter/override
|
|
68
|
+
if (options?.scope) {
|
|
69
|
+
artifacts = artifacts
|
|
70
|
+
.map((a) => ({ ...a, scope: a.scope ?? options.scope }))
|
|
71
|
+
.filter((a) => a.scope === options.scope);
|
|
72
|
+
}
|
|
73
|
+
return artifacts;
|
|
74
|
+
}
|
|
75
|
+
function detectScope(filePath) {
|
|
76
|
+
const resolved = resolve(filePath);
|
|
77
|
+
// Inside ~/.claude/ itself (not a subdirectory project)
|
|
78
|
+
if (resolved.startsWith(CLAUDE_USER_DIR + "/")) {
|
|
79
|
+
const relative = resolved.slice(CLAUDE_USER_DIR.length + 1);
|
|
80
|
+
// Files directly in ~/.claude/ (settings.json, mcp.json, CLAUDE.md)
|
|
81
|
+
if (!relative.includes("/") || relative.startsWith("plugins/")) {
|
|
82
|
+
return "user";
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// Inside a project's .claude/ directory
|
|
86
|
+
const name = basename(filePath);
|
|
87
|
+
const parent = basename(dirname(filePath));
|
|
88
|
+
if (parent === ".claude") {
|
|
89
|
+
// Check if this .claude/ is inside another .claude/ (subdirectory scope)
|
|
90
|
+
const projectDir = dirname(dirname(filePath));
|
|
91
|
+
if (isSubdirectoryProject(projectDir)) {
|
|
92
|
+
return "subdirectory";
|
|
93
|
+
}
|
|
94
|
+
return "project";
|
|
95
|
+
}
|
|
96
|
+
// CLAUDE.md
|
|
97
|
+
if (name === "CLAUDE.md") {
|
|
98
|
+
const dir = dirname(resolved);
|
|
99
|
+
if (dir === CLAUDE_USER_DIR || dir === homedir())
|
|
100
|
+
return "user";
|
|
101
|
+
return "project";
|
|
102
|
+
}
|
|
103
|
+
// .mcp.json at project root
|
|
104
|
+
if (name === ".mcp.json")
|
|
105
|
+
return "project";
|
|
106
|
+
// settings files directly in ~/.claude/
|
|
107
|
+
if (name === "settings.json" && dirname(resolved) === CLAUDE_USER_DIR) {
|
|
108
|
+
return "user";
|
|
109
|
+
}
|
|
110
|
+
// settings.local.json in ~/.claude/ — this is misplaced (user level), detect it so the linter can warn
|
|
111
|
+
if (name === "settings.local.json" && dirname(resolved) === CLAUDE_USER_DIR) {
|
|
112
|
+
return "user";
|
|
113
|
+
}
|
|
114
|
+
return undefined;
|
|
115
|
+
}
|
|
116
|
+
function isSubdirectoryProject(dir) {
|
|
117
|
+
// Walk up looking for a parent with .claude-plugin/ or another .claude/
|
|
118
|
+
let current = dirname(dir);
|
|
119
|
+
for (let i = 0; i < 10; i++) {
|
|
120
|
+
if (existsSync(join(current, ".claude-plugin")))
|
|
121
|
+
return true;
|
|
122
|
+
if (existsSync(join(current, ".claude")) && current !== dir)
|
|
123
|
+
return true;
|
|
124
|
+
if (existsSync(join(current, ".git")))
|
|
125
|
+
return false; // reached git root
|
|
126
|
+
const parent = dirname(current);
|
|
127
|
+
if (parent === current)
|
|
128
|
+
break;
|
|
129
|
+
current = parent;
|
|
130
|
+
}
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
function discoverInDirectory(dir) {
|
|
134
|
+
const artifacts = [];
|
|
135
|
+
const isUserDir = resolve(dir) === CLAUDE_USER_DIR;
|
|
136
|
+
// plugin.json
|
|
137
|
+
const pluginJsons = globSync(".claude-plugin/plugin.json", { cwd: dir, absolute: true });
|
|
138
|
+
for (const f of pluginJsons) {
|
|
139
|
+
artifacts.push({ filePath: f, artifactType: "plugin-json" });
|
|
140
|
+
}
|
|
141
|
+
// SKILL.md files
|
|
142
|
+
const skills = globSync("skills/*/SKILL.md", { cwd: dir, absolute: true });
|
|
143
|
+
for (const f of skills) {
|
|
144
|
+
artifacts.push({ filePath: f, artifactType: "skill-md" });
|
|
145
|
+
}
|
|
146
|
+
// Agent definitions (plugin agents/ and .claude/agents/)
|
|
147
|
+
const agentPatterns = isUserDir
|
|
148
|
+
? ["agents/*.md"]
|
|
149
|
+
: ["agents/*.md", ".claude/agents/*.md"];
|
|
150
|
+
for (const pattern of agentPatterns) {
|
|
151
|
+
const agents = globSync(pattern, { cwd: dir, absolute: true });
|
|
152
|
+
for (const f of agents) {
|
|
153
|
+
artifacts.push({ filePath: f, artifactType: "agent-md" });
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
// Command definitions
|
|
157
|
+
const commands = globSync("commands/*.md", { cwd: dir, absolute: true });
|
|
158
|
+
for (const f of commands) {
|
|
159
|
+
artifacts.push({ filePath: f, artifactType: "command-md" });
|
|
160
|
+
}
|
|
161
|
+
// hooks.json
|
|
162
|
+
const hooks = globSync("hooks/hooks.json", { cwd: dir, absolute: true });
|
|
163
|
+
for (const f of hooks) {
|
|
164
|
+
artifacts.push({ filePath: f, artifactType: "hooks-json" });
|
|
165
|
+
}
|
|
166
|
+
// Claude config files — settings
|
|
167
|
+
for (const name of ["settings.json", "settings.local.json"]) {
|
|
168
|
+
// Direct in dir (handles both ~/.claude/settings.json and project root)
|
|
169
|
+
const atRoot = join(dir, name);
|
|
170
|
+
if (existsSync(atRoot)) {
|
|
171
|
+
artifacts.push({ filePath: atRoot, artifactType: "settings-json", scope: detectScope(atRoot) });
|
|
172
|
+
}
|
|
173
|
+
// In .claude/ subdirectory (skip if we're already in ~/.claude/)
|
|
174
|
+
if (!isUserDir) {
|
|
175
|
+
const inClaude = join(dir, ".claude", name);
|
|
176
|
+
if (existsSync(inClaude) && !existsSync(atRoot)) {
|
|
177
|
+
artifacts.push({ filePath: inClaude, artifactType: "settings-json", scope: detectScope(inClaude) });
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
// MCP config
|
|
182
|
+
const mcpDot = join(dir, ".mcp.json");
|
|
183
|
+
if (existsSync(mcpDot)) {
|
|
184
|
+
artifacts.push({ filePath: mcpDot, artifactType: "mcp-json", scope: detectScope(mcpDot) });
|
|
185
|
+
}
|
|
186
|
+
const mcpPlain = join(dir, "mcp.json");
|
|
187
|
+
if (existsSync(mcpPlain)) {
|
|
188
|
+
artifacts.push({ filePath: mcpPlain, artifactType: "mcp-json", scope: detectScope(mcpPlain) });
|
|
189
|
+
}
|
|
190
|
+
// CLAUDE.md
|
|
191
|
+
const claudeMd = join(dir, "CLAUDE.md");
|
|
192
|
+
if (existsSync(claudeMd)) {
|
|
193
|
+
artifacts.push({ filePath: claudeMd, artifactType: "claude-md", scope: detectScope(claudeMd) });
|
|
194
|
+
}
|
|
195
|
+
return artifacts;
|
|
196
|
+
}
|
|
197
|
+
function classifyFile(filePath) {
|
|
198
|
+
const name = basename(filePath);
|
|
199
|
+
const parent = basename(dirname(filePath));
|
|
200
|
+
if (name === "plugin.json" && parent === ".claude-plugin")
|
|
201
|
+
return "plugin-json";
|
|
202
|
+
if (name === "SKILL.md")
|
|
203
|
+
return "skill-md";
|
|
204
|
+
if (name === "hooks.json" && parent === "hooks")
|
|
205
|
+
return "hooks-json";
|
|
206
|
+
if (name.endsWith(".md") && parent === "agents")
|
|
207
|
+
return "agent-md";
|
|
208
|
+
if (name.endsWith(".md") && parent === "commands")
|
|
209
|
+
return "command-md";
|
|
210
|
+
// Claude config files
|
|
211
|
+
if (name === "settings.json" || name === "settings.local.json")
|
|
212
|
+
return "settings-json";
|
|
213
|
+
if (name === ".mcp.json" || name === "mcp.json")
|
|
214
|
+
return "mcp-json";
|
|
215
|
+
if (name === "CLAUDE.md")
|
|
216
|
+
return "claude-md";
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
//# sourceMappingURL=discovery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discovery.js","sourceRoot":"","sources":["../src/discovery.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { formatMarkdown } from "../utils/prettier.js";
|
|
2
|
+
export const claudeMdFixer = {
|
|
3
|
+
artifactType: "claude-md",
|
|
4
|
+
async fix(_filePath, content, _config) {
|
|
5
|
+
if (content === "")
|
|
6
|
+
return content;
|
|
7
|
+
// Run prettier markdown formatting
|
|
8
|
+
let result = await formatMarkdown(content);
|
|
9
|
+
// Ensure blank line before headings unless it's the first line
|
|
10
|
+
const lines = result.split("\n");
|
|
11
|
+
const out = [];
|
|
12
|
+
for (let i = 0; i < lines.length; i++) {
|
|
13
|
+
const line = lines[i];
|
|
14
|
+
if (line !== undefined && line.startsWith("#") && i > 0 && out[out.length - 1] !== "") {
|
|
15
|
+
out.push("");
|
|
16
|
+
}
|
|
17
|
+
if (line !== undefined) {
|
|
18
|
+
out.push(line);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return out.join("\n");
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
//# sourceMappingURL=claude-md.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-md.js","sourceRoot":"","sources":["../../src/fixers/claude-md.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,MAAM,CAAC,MAAM,aAAa,GAAU;IAClC,YAAY,EAAE,WAAW;IAEzB,KAAK,CAAC,GAAG,CAAC,SAAiB,EAAE,OAAe,EAAE,OAAqB;QACjE,IAAI,OAAO,KAAK,EAAE;YAAE,OAAO,OAAO,CAAC;QAEnC,mCAAmC;QACnC,IAAI,MAAM,GAAG,MAAM,cAAc,CAAC,OAAO,CAAC,CAAC;QAE3C,+DAA+D;QAC/D,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACjC,MAAM,GAAG,GAAa,EAAE,CAAC;QACzB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACtC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YACtB,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC;gBACtF,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACf,CAAC;YACD,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;gBACvB,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACjB,CAAC;QACH,CAAC;QACD,OAAO,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACxB,CAAC;CACF,CAAC"}
|