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.
Files changed (73) hide show
  1. package/.claudecode-lint.defaults.yaml +112 -0
  2. package/LICENSE +21 -0
  3. package/README.md +181 -0
  4. package/dist/config.d.ts +3 -0
  5. package/dist/config.js +66 -0
  6. package/dist/config.js.map +1 -0
  7. package/dist/discovery.d.ts +8 -0
  8. package/dist/discovery.js +219 -0
  9. package/dist/discovery.js.map +1 -0
  10. package/dist/fixers/claude-md.d.ts +2 -0
  11. package/dist/fixers/claude-md.js +24 -0
  12. package/dist/fixers/claude-md.js.map +1 -0
  13. package/dist/fixers/frontmatter.d.ts +2 -0
  14. package/dist/fixers/frontmatter.js +85 -0
  15. package/dist/fixers/frontmatter.js.map +1 -0
  16. package/dist/fixers/hooks-json.d.ts +2 -0
  17. package/dist/fixers/hooks-json.js +23 -0
  18. package/dist/fixers/hooks-json.js.map +1 -0
  19. package/dist/fixers/mcp-json.d.ts +2 -0
  20. package/dist/fixers/mcp-json.js +47 -0
  21. package/dist/fixers/mcp-json.js.map +1 -0
  22. package/dist/fixers/plugin-json.d.ts +2 -0
  23. package/dist/fixers/plugin-json.js +34 -0
  24. package/dist/fixers/plugin-json.js.map +1 -0
  25. package/dist/fixers/settings-json.d.ts +2 -0
  26. package/dist/fixers/settings-json.js +47 -0
  27. package/dist/fixers/settings-json.js.map +1 -0
  28. package/dist/formatters/human.d.ts +2 -0
  29. package/dist/formatters/human.js +47 -0
  30. package/dist/formatters/human.js.map +1 -0
  31. package/dist/formatters/json.d.ts +2 -0
  32. package/dist/formatters/json.js +10 -0
  33. package/dist/formatters/json.js.map +1 -0
  34. package/dist/index.d.ts +2 -0
  35. package/dist/index.js +246 -0
  36. package/dist/index.js.map +1 -0
  37. package/dist/linters/agent-md.d.ts +8 -0
  38. package/dist/linters/agent-md.js +92 -0
  39. package/dist/linters/agent-md.js.map +1 -0
  40. package/dist/linters/claude-md.d.ts +8 -0
  41. package/dist/linters/claude-md.js +109 -0
  42. package/dist/linters/claude-md.js.map +1 -0
  43. package/dist/linters/command-md.d.ts +8 -0
  44. package/dist/linters/command-md.js +61 -0
  45. package/dist/linters/command-md.js.map +1 -0
  46. package/dist/linters/hooks-json.d.ts +8 -0
  47. package/dist/linters/hooks-json.js +123 -0
  48. package/dist/linters/hooks-json.js.map +1 -0
  49. package/dist/linters/mcp-json.d.ts +8 -0
  50. package/dist/linters/mcp-json.js +160 -0
  51. package/dist/linters/mcp-json.js.map +1 -0
  52. package/dist/linters/plugin-json.d.ts +8 -0
  53. package/dist/linters/plugin-json.js +166 -0
  54. package/dist/linters/plugin-json.js.map +1 -0
  55. package/dist/linters/settings-json.d.ts +8 -0
  56. package/dist/linters/settings-json.js +187 -0
  57. package/dist/linters/settings-json.js.map +1 -0
  58. package/dist/linters/skill-md.d.ts +8 -0
  59. package/dist/linters/skill-md.js +97 -0
  60. package/dist/linters/skill-md.js.map +1 -0
  61. package/dist/types.d.ts +39 -0
  62. package/dist/types.js +15 -0
  63. package/dist/types.js.map +1 -0
  64. package/dist/utils/frontmatter.d.ts +9 -0
  65. package/dist/utils/frontmatter.js +65 -0
  66. package/dist/utils/frontmatter.js.map +1 -0
  67. package/dist/utils/kebab-case.d.ts +2 -0
  68. package/dist/utils/kebab-case.js +14 -0
  69. package/dist/utils/kebab-case.js.map +1 -0
  70. package/dist/utils/prettier.d.ts +2 -0
  71. package/dist/utils/prettier.js +17 -0
  72. package/dist/utils/prettier.js.map +1 -0
  73. 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
@@ -0,0 +1,3 @@
1
+ import type { LinterConfig } from "./types.js";
2
+ export declare function loadConfig(configPath?: string): LinterConfig;
3
+ export declare function mergeCliRules(config: LinterConfig, enable: string[], disable: string[]): LinterConfig;
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,2 @@
1
+ import type { Fixer } from "../types.js";
2
+ export declare const claudeMdFixer: Fixer;
@@ -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"}
@@ -0,0 +1,2 @@
1
+ import type { Fixer } from "../types.js";
2
+ export declare const frontmatterFixer: Fixer;