claudecode-linter 2.1.150 → 2.1.152

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.
@@ -25,36 +25,87 @@ function getAjv() {
25
25
  addFormats(ajvShared);
26
26
  return ajvShared;
27
27
  }
28
- function loadCompiledSchema(fileName) {
29
- if (compiledCache.has(fileName))
30
- return compiledCache.get(fileName) ?? null;
28
+ function tryReadFile(relPath) {
31
29
  const candidates = [
32
- ...assetCandidates(import.meta.url, ["..", "contracts", fileName]),
33
- ...assetCandidates(import.meta.url, ["..", "..", "contracts", fileName]),
30
+ ...assetCandidates(import.meta.url, ["..", ...relPath]),
31
+ ...assetCandidates(import.meta.url, ["..", "..", ...relPath]),
34
32
  ];
35
- let raw = null;
36
33
  for (const p of candidates) {
37
34
  try {
38
- raw = readFileSync(p, "utf8");
39
- break;
35
+ return readFileSync(p, "utf8");
40
36
  }
41
37
  catch {
42
38
  // try next
43
39
  }
44
40
  }
41
+ return null;
42
+ }
43
+ /**
44
+ * Compile a schema document, handling both wrapper shapes:
45
+ * - extracted (`{ extractedFromClaudeCodeVersion, schema: {...} }`)
46
+ * - bare JSON Schema (the schemastore.org files, no envelope)
47
+ */
48
+ function compileSchemaDoc(raw, sourceLabel) {
49
+ const parsed = JSON.parse(raw);
50
+ let schema;
51
+ let version;
52
+ if ("extractedFromClaudeCodeVersion" in parsed &&
53
+ typeof parsed.extractedFromClaudeCodeVersion === "string" &&
54
+ "schema" in parsed &&
55
+ typeof parsed.schema === "object" &&
56
+ parsed.schema !== null) {
57
+ schema = parsed.schema;
58
+ version = parsed.extractedFromClaudeCodeVersion;
59
+ }
60
+ else {
61
+ // Bare schemastore document. Strip `$id` (Ajv registers it globally
62
+ // and trips `$id already exists` on a second compile) and any
63
+ // `$schema` meta-pointer Ajv2020 can't resolve (draft-07 etc.).
64
+ const { $id: _id, $schema: _meta, ...rest } = parsed;
65
+ void _id;
66
+ void _meta;
67
+ schema = rest;
68
+ version = sourceLabel;
69
+ }
70
+ const propSrc = schema.properties ?? {};
71
+ return {
72
+ validate: getAjv().compile(schema),
73
+ extractedFromVersion: version,
74
+ knownFields: new Set(Object.keys(propSrc)),
75
+ };
76
+ }
77
+ function loadCompiledSchema(fileName) {
78
+ if (compiledCache.has(fileName))
79
+ return compiledCache.get(fileName) ?? null;
80
+ const raw = tryReadFile(["contracts", fileName]);
45
81
  if (!raw) {
46
82
  compiledCache.set(fileName, null);
47
83
  return null;
48
84
  }
49
- const wrapped = JSON.parse(raw);
50
- const compiled = {
51
- validate: getAjv().compile(wrapped.schema),
52
- extractedFromVersion: wrapped.extractedFromClaudeCodeVersion,
53
- knownFields: new Set(Object.keys(wrapped.schema.properties ?? {})),
54
- };
85
+ const compiled = compileSchemaDoc(raw, fileName);
55
86
  compiledCache.set(fileName, compiled);
56
87
  return compiled;
57
88
  }
89
+ /**
90
+ * Load a schemastore.org-curated schema from `contracts/schemastore/`.
91
+ * Schemastore is preferred for top-level artifact validation because it's
92
+ * Anthropic's own published source of truth and stays stable across Claude
93
+ * Code minifier rotations. The Zod-extracted schemas (`loadCompiledSchema`)
94
+ * remain authoritative for sub-shapes schemastore doesn't enumerate.
95
+ */
96
+ function loadSchemastoreSchema(fileName) {
97
+ const cacheKey = `schemastore/${fileName}`;
98
+ if (compiledCache.has(cacheKey))
99
+ return compiledCache.get(cacheKey) ?? null;
100
+ const raw = tryReadFile(["contracts", "schemastore", fileName]);
101
+ if (!raw) {
102
+ compiledCache.set(cacheKey, null);
103
+ return null;
104
+ }
105
+ const compiled = compileSchemaDoc(raw, `schemastore:${fileName}`);
106
+ compiledCache.set(cacheKey, compiled);
107
+ return compiled;
108
+ }
58
109
  export function loadLspSchema() {
59
110
  return loadCompiledSchema("lsp.schema.json");
60
111
  }
@@ -62,8 +113,24 @@ export function loadMonitorsSchema() {
62
113
  return loadCompiledSchema("monitors.schema.json");
63
114
  }
64
115
  export function loadSettingsSchema() {
116
+ // gitea#6: prefer the Zod-extracted schema (runtime truth, e.g.
117
+ // `disableAutoMode` is `boolean` in 2.1.150) over schemastore (which
118
+ // still lists it as a string literal — out of date). Schemastore is
119
+ // only used for artifacts with no Zod source (marketplace, keybindings).
65
120
  return loadCompiledSchema("settings.schema.json");
66
121
  }
122
+ /**
123
+ * Marketplace and keybindings schemas come exclusively from schemastore —
124
+ * there is no Zod source for them in the Claude Code bundle. Returns null
125
+ * if the user's install was shipped without the schemastore bundle (rare;
126
+ * the package.json includes them in `files`).
127
+ */
128
+ export function loadMarketplaceSchema() {
129
+ return loadSchemastoreSchema("marketplace.schema.json");
130
+ }
131
+ export function loadKeybindingsSchema() {
132
+ return loadSchemastoreSchema("keybindings.schema.json");
133
+ }
67
134
  export function loadMcpJsonSchema() {
68
135
  return loadCompiledSchema("mcp.schema.json");
69
136
  }
@@ -82,36 +149,17 @@ export function loadCommandFrontmatterSchema() {
82
149
  export function loadPluginSchema() {
83
150
  if (cached)
84
151
  return cached;
85
- const candidates = [
86
- ...assetCandidates(import.meta.url, ["..", "contracts", "plugin.schema.json"]),
87
- ...assetCandidates(import.meta.url, [
88
- "..",
89
- "..",
90
- "contracts",
91
- "plugin.schema.json",
92
- ]),
93
- ];
94
- let raw = null;
95
- for (const p of candidates) {
96
- try {
97
- raw = readFileSync(p, "utf8");
98
- break;
99
- }
100
- catch {
101
- // try next
102
- }
103
- }
152
+ // gitea#6: prefer the Zod-extracted plugin.schema.json (runtime truth);
153
+ // schemastore copy stays committed at `contracts/schemastore/` for
154
+ // reference and for the marketplace/keybindings artifacts only.
155
+ const raw = tryReadFile(["contracts", "plugin.schema.json"]);
104
156
  if (!raw)
105
157
  return null;
106
- const wrapped = JSON.parse(raw);
107
- const ajv = new Ajv2020({ allErrors: true, strict: false });
108
- addFormats(ajv);
109
- const validate = ajv.compile(wrapped.schema);
110
- const knownFields = new Set(Object.keys(wrapped.schema.properties ?? {}));
158
+ const compiled = compileSchemaDoc(raw, "plugin.schema.json");
111
159
  cached = {
112
- validate,
113
- extractedFromVersion: wrapped.extractedFromClaudeCodeVersion,
114
- knownFields,
160
+ validate: compiled.validate,
161
+ extractedFromVersion: compiled.extractedFromVersion,
162
+ knownFields: compiled.knownFields,
115
163
  };
116
164
  return cached;
117
165
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claudecode-linter",
3
- "version": "2.1.150",
3
+ "version": "2.1.152",
4
4
  "description": "Standalone linter for Claude Code plugins and configuration files",
5
5
  "type": "module",
6
6
  "bin": {
@@ -17,8 +17,9 @@
17
17
  "deps:update": "ncu -u",
18
18
  "knip": "knip --exclude types",
19
19
  "check-deps": "tsx scripts/check-deps.ts",
20
- "extract-contracts": "tsx scripts/extract-contracts.ts && tsx scripts/extract-plugin-schema.ts",
20
+ "extract-contracts": "tsx scripts/extract-contracts.ts && tsx scripts/extract-plugin-schema.ts && tsx scripts/fetch-schemastore.ts",
21
21
  "extract-plugin-schema": "tsx scripts/extract-plugin-schema.ts",
22
+ "fetch-schemastore": "tsx scripts/fetch-schemastore.ts",
22
23
  "generate-contracts": "tsx scripts/generate-contracts.ts"
23
24
  },
24
25
  "files": [
@@ -32,6 +33,7 @@
32
33
  "contracts/command-frontmatter.schema.json",
33
34
  "contracts/mcp.schema.json",
34
35
  "contracts/hooks.schema.json",
36
+ "contracts/schemastore/*.json",
35
37
  ".claudecode-lint.defaults.yaml",
36
38
  "README.md"
37
39
  ],