@yawlabs/ctxlint 0.12.3 → 0.13.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/.pre-commit-hooks.yaml +1 -1
- package/AGENT_SESSION_LINT_SPEC.md +3 -4
- package/AGENT_SKILL_LINT_SPEC.md +107 -0
- package/CONTEXT_LINT_SPEC.md +21 -4
- package/MCP_CONFIG_LINT_SPEC.md +1 -2
- package/README.md +12 -8
- package/agent-session-lint-rules.json +1 -1
- package/agent-skill-lint-rules.json +75 -0
- package/context-lint-rules.json +17 -1
- package/dist/index.js +797 -175
- package/mcp-config-lint-rules.json +1 -1
- package/mcph-config-lint-rules.json +1 -1
- package/package.json +6 -1
- package/schemas/ctxlint-catalog.schema.json +94 -0
package/.pre-commit-hooks.yaml
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# Version-pinned so a checkout at `rev: vX.Y.Z` runs exactly that release
|
|
5
5
|
# of ctxlint — matches the pinning done by `ctxlint init`. release.sh keeps
|
|
6
6
|
# this in sync with package.json on each bump.
|
|
7
|
-
entry: npx @yawlabs/ctxlint@0.
|
|
7
|
+
entry: npx @yawlabs/ctxlint@0.13.0 --strict
|
|
8
8
|
language: node
|
|
9
9
|
always_run: true
|
|
10
10
|
pass_filenames: false
|
|
@@ -15,7 +15,7 @@ This specification defines a standard set of lint rules for validating agent ses
|
|
|
15
15
|
|
|
16
16
|
The specification includes:
|
|
17
17
|
- A reference of session data locations across 8 AI coding agents
|
|
18
|
-
-
|
|
18
|
+
- 8 lint rules in the `session` category with defined severities
|
|
19
19
|
- A machine-readable rule catalog ([`agent-session-lint-rules.json`](./agent-session-lint-rules.json))
|
|
20
20
|
- Sibling-repo detection for cross-project checks
|
|
21
21
|
|
|
@@ -128,7 +128,7 @@ Skip hidden directories (starting with `.`) and `node_modules`.
|
|
|
128
128
|
|
|
129
129
|
## 2. Lint Rules
|
|
130
130
|
|
|
131
|
-
|
|
131
|
+
8 rules in 1 category (`session`). All rules in this category perform cross-project checks using sibling detection or per-project history analysis.
|
|
132
132
|
|
|
133
133
|
Severity levels:
|
|
134
134
|
- **error** -- the session data reveals a verifiably missing configuration. Should fail CI.
|
|
@@ -437,6 +437,5 @@ This specification follows semver:
|
|
|
437
437
|
|
|
438
438
|
- [AI Context File Linting Specification](./CONTEXT_LINT_SPEC.md) -- context file lint rules (the first pillar)
|
|
439
439
|
- [MCP Server Configuration Linting Specification](./MCP_CONFIG_LINT_SPEC.md) -- MCP config lint rules (the second pillar)
|
|
440
|
-
- [ctxlint](https://github.com/YawLabs/ctxlint) -- reference implementation of all
|
|
440
|
+
- [ctxlint](https://github.com/YawLabs/ctxlint) -- reference implementation of all four specifications
|
|
441
441
|
- [mcp-compliance](https://github.com/YawLabs/mcp-compliance) -- tests MCP server behavior against the protocol spec
|
|
442
|
-
- [mcp.hosting](https://mcp.hosting) -- managed MCP server hosting
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# AI Agent Skill Linting Specification
|
|
2
|
+
|
|
3
|
+
**Version:** 1.0.0-draft
|
|
4
|
+
**Date:** 2026-06-02
|
|
5
|
+
**Maintained by:** [Yaw Labs](https://yaw.sh) / [ctxlint](https://github.com/YawLabs/ctxlint)
|
|
6
|
+
**License:** CC BY 4.0
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## What is this?
|
|
11
|
+
|
|
12
|
+
AI coding agents are increasingly extended with **skills** and **subagents** -- reusable, named units of behavior the agent loads on demand. In Claude Code these live as:
|
|
13
|
+
|
|
14
|
+
- **Skills:** `~/.claude/skills/<name>/SKILL.md` -- a markdown file with YAML frontmatter (`name`, `description`) plus a body of instructions.
|
|
15
|
+
- **Agents (subagents):** `~/.claude/agents/*.md` -- a markdown file with frontmatter (`name`, `description`, optional `tools` restriction) plus a body.
|
|
16
|
+
|
|
17
|
+
These definition files have the same failure modes as context files: their frontmatter goes missing, their body references paths that no longer exist, their trigger phrases collide so the wrong one fires, a skill directory is left without a `SKILL.md`, or a subagent restricts itself to a tool name that doesn't exist. When that happens the skill/agent silently misbehaves -- it never fires, fires for the wrong prompt, or runs with the wrong tool set.
|
|
18
|
+
|
|
19
|
+
This specification defines a standard set of lint rules for validating agent-skill definitions. It is the **fourth pillar** alongside context-file linting, MCP-config linting, and session-data linting:
|
|
20
|
+
|
|
21
|
+
| Pillar | What it checks | Specification |
|
|
22
|
+
|---|---|---|
|
|
23
|
+
| Context files | Instructions the agent reads | [CONTEXT_LINT_SPEC.md](./CONTEXT_LINT_SPEC.md) |
|
|
24
|
+
| MCP configs | Tools the agent can use | [MCP_CONFIG_LINT_SPEC.md](./MCP_CONFIG_LINT_SPEC.md) |
|
|
25
|
+
| Session data | History and memory the agent carries | [AGENT_SESSION_LINT_SPEC.md](./AGENT_SESSION_LINT_SPEC.md) |
|
|
26
|
+
| Agent skills | Skills and subagents the agent loads | This document |
|
|
27
|
+
|
|
28
|
+
**v1 scope is deliberately tight: Claude Code only** (`~/.claude/skills/<name>/SKILL.md` and `~/.claude/agents/*.md`). Other agents' skill/subagent formats may be added in later spec versions.
|
|
29
|
+
|
|
30
|
+
**Reference implementation:** [ctxlint](https://github.com/YawLabs/ctxlint) -- run via `ctxlint --skills` (or `--skills-only`).
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## 1. Agent Skill Landscape Reference
|
|
35
|
+
|
|
36
|
+
### 1.1 Data sources (v1)
|
|
37
|
+
|
|
38
|
+
| Kind | Location | Required frontmatter |
|
|
39
|
+
|---|---|---|
|
|
40
|
+
| Skill | `~/.claude/skills/<name>/SKILL.md` | `name`, `description` |
|
|
41
|
+
| Agent (subagent) | `~/.claude/agents/<name>.md` | `name`, `description` (optional `tools` / `allowed-tools`) |
|
|
42
|
+
|
|
43
|
+
The skill `<name>` is the directory name; the agent `<name>` is the filename without `.md`. A `~/.claude/skills/<name>/` directory with no `SKILL.md` is an **orphaned skill** -- the directory exists but Claude Code has nothing to load.
|
|
44
|
+
|
|
45
|
+
### 1.2 Scan targets
|
|
46
|
+
|
|
47
|
+
Skill/agent definitions live in the user-global `~/.claude/` tree, NOT inside the project. The checks therefore scan the home directory, like the session pillar -- they are opt-in (`--skills`) for the same reason: they read files outside the project directory.
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## 2. Lint Rules
|
|
52
|
+
|
|
53
|
+
5 rules in 1 category (`skill`). All rules audit Claude Code skill (`SKILL.md`) and agent (`.md`) definition files.
|
|
54
|
+
|
|
55
|
+
Severity levels:
|
|
56
|
+
- **error** -- the definition is structurally broken (e.g. unclosed or absent frontmatter). The skill/agent will not load as intended.
|
|
57
|
+
- **warning** -- the definition has a likely problem worth investigating (missing field, broken reference, trigger collision, dead tool restriction).
|
|
58
|
+
- **info** -- reserved for future advisory rules.
|
|
59
|
+
|
|
60
|
+
### 2.1 skill — agent skill audit
|
|
61
|
+
|
|
62
|
+
| Rule ID | Severity | Trigger | Message |
|
|
63
|
+
|---|---|---|---|
|
|
64
|
+
| `skill/missing-frontmatter` | warning (error when frontmatter absent/unclosed) | A SKILL.md / agent .md has no `---`-delimited frontmatter, has unclosed frontmatter, or is missing a required field (`name`, `description`) | `{file}: missing required frontmatter field "{field}"` |
|
|
65
|
+
| `skill/broken-ref` | warning | A `./` or `../` path reference in the body (outside example code blocks) does not exist relative to the skill directory | `{file}: references "{path}" which does not exist relative to the skill directory` |
|
|
66
|
+
| `skill/trigger-collision` | warning | A normalized trigger phrase (quoted phrase in the description, or a `trigger`/`triggers` field) is declared by more than one distinct skill/agent | `Trigger phrase "{trigger}" is declared by {count} skills/agents — only one will win` |
|
|
67
|
+
| `skill/orphaned` | warning | A `~/.claude/skills/<name>/` directory contains no `SKILL.md` | `{dir}: skill directory has no SKILL.md — Claude Code has nothing to load` |
|
|
68
|
+
| `skill/dead-tool-restriction` | warning | An agent's `tools` / `allowed-tools` frontmatter lists a non-MCP, non-wildcard tool name that is not a known Claude Code built-in tool | `{file}: tool restriction lists "{tool}" which is not a known Claude Code tool` |
|
|
69
|
+
|
|
70
|
+
**Notes:**
|
|
71
|
+
|
|
72
|
+
- **`skill/broken-ref`** reuses the path-reference detection shape from the context-file pillar. Only explicitly-relative references (`./`, `../`) are verified, resolved against the skill/agent file's own directory; bare `foo/bar` tokens in prose are too ambiguous to resolve without false positives, so they are skipped. References inside example code blocks (`ts`, `py`, `json`, ...) are excluded.
|
|
73
|
+
- **`skill/trigger-collision`** extracts triggers from quoted phrases inside the `description` (e.g. `"ship 1.3.X"`) and from an optional `trigger`/`triggers` field. Phrases are lowercased and whitespace-collapsed before comparison.
|
|
74
|
+
- **`skill/dead-tool-restriction`** validates only against the known built-in tool set. MCP-namespaced tools (`mcp__server__tool`) and wildcard entries are skipped because their validity depends on the loaded MCP servers, which the linter cannot see statically.
|
|
75
|
+
|
|
76
|
+
All v1 rules are marked **experimental** in the catalog -- the heuristics are conservative and may broaden as more skill/agent shapes are observed. Experimental rules bump patch; promotion to stable bumps minor (see `CHANGELOG.md` versioning policy).
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## 3. Rule Catalog (machine-readable)
|
|
81
|
+
|
|
82
|
+
A machine-readable JSON catalog of all rules is available at [`agent-skill-lint-rules.json`](./agent-skill-lint-rules.json). It conforms to the shared catalog schema ([`schemas/ctxlint-catalog.schema.json`](./schemas/ctxlint-catalog.schema.json)), like the other three pillars.
|
|
83
|
+
|
|
84
|
+
Rule IDs use the ctxlint `category/slug` format (see [CONTRIBUTING.md](./CONTRIBUTING.md) "Rule ID format" for why this differs from the sibling mcp-compliance project).
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## 4. Implementing This Specification
|
|
89
|
+
|
|
90
|
+
1. Discover skill files (`~/.claude/skills/<name>/SKILL.md`) and agent files (`~/.claude/agents/*.md`). Record each `~/.claude/skills/<name>/` directory that has no `SKILL.md` as an orphaned skill.
|
|
91
|
+
2. Parse YAML frontmatter (`name`, `description`, and for agents `tools` / `allowed-tools`).
|
|
92
|
+
3. Run the five rules in section 2.1.
|
|
93
|
+
4. Report findings against the user-global path (`~/.claude/...`) -- these are not project files.
|
|
94
|
+
|
|
95
|
+
### Versioning
|
|
96
|
+
|
|
97
|
+
This spec follows semver:
|
|
98
|
+
- **Patch** (1.0.x): Typo fixes, clarifications, no rule changes.
|
|
99
|
+
- **Minor** (1.x.0): New rules added, new agents/skill formats documented.
|
|
100
|
+
- **Major** (x.0.0): Rules removed or semantics changed in breaking ways.
|
|
101
|
+
|
|
102
|
+
### Related specifications and tools
|
|
103
|
+
|
|
104
|
+
- [AI Context File Linting Specification](./CONTEXT_LINT_SPEC.md) -- context file lint rules (the first pillar)
|
|
105
|
+
- [MCP Server Configuration Linting Specification](./MCP_CONFIG_LINT_SPEC.md) -- MCP config lint rules (the second pillar)
|
|
106
|
+
- [AI Agent Session Linting Specification](./AGENT_SESSION_LINT_SPEC.md) -- session data lint rules (the third pillar)
|
|
107
|
+
- [ctxlint](https://github.com/YawLabs/ctxlint) -- reference implementation of all four specifications
|
package/CONTEXT_LINT_SPEC.md
CHANGED
|
@@ -15,7 +15,7 @@ This specification defines a standard set of lint rules for validating AI agent
|
|
|
15
15
|
|
|
16
16
|
The specification includes:
|
|
17
17
|
- A complete reference of context file formats across 17 AI coding clients (21+ file patterns)
|
|
18
|
-
-
|
|
18
|
+
- 28 lint rules organized into 11 categories with defined severities
|
|
19
19
|
- A machine-readable rule and format catalog ([`context-lint-rules.json`](./context-lint-rules.json))
|
|
20
20
|
- Auto-fix definitions for rules that support automated correction
|
|
21
21
|
- Frontmatter schema requirements per client
|
|
@@ -47,6 +47,7 @@ The specification includes:
|
|
|
47
47
|
- [3.7 frontmatter — client metadata validation](#37-frontmatter--client-metadata-validation)
|
|
48
48
|
- [3.8 ci-coverage — CI workflow documentation](#38-ci-coverage--ci-workflow-documentation)
|
|
49
49
|
- [3.9 ci-secrets — CI secrets documentation](#39-ci-secrets--ci-secrets-documentation)
|
|
50
|
+
- [3.11 hook-coverage — hook enforcement coverage](#311-hook-coverage--hook-enforcement-coverage)
|
|
50
51
|
- [4. Rule Catalog (machine-readable)](#4-rule-catalog-machine-readable)
|
|
51
52
|
- [5. Implementing This Specification](#5-implementing-this-specification)
|
|
52
53
|
- [6. Contributing](#6-contributing)
|
|
@@ -300,7 +301,7 @@ Context files consume an agent's context window. Counting tokens helps teams und
|
|
|
300
301
|
|
|
301
302
|
## 3. Lint Rules
|
|
302
303
|
|
|
303
|
-
|
|
304
|
+
28 rules organized into 11 categories.
|
|
304
305
|
|
|
305
306
|
Severity levels:
|
|
306
307
|
- **error** — the context file has a verifiably incorrect reference or invalid metadata. Should fail CI.
|
|
@@ -592,6 +593,23 @@ Checks that secrets referenced in CI workflow files are mentioned in context fil
|
|
|
592
593
|
4. For each remaining secret, search all context files for the secret name (case-insensitive, flexible underscore/space matching).
|
|
593
594
|
5. Emit one info-level issue per undocumented secret.
|
|
594
595
|
|
|
596
|
+
### 3.11 hook-coverage — hook enforcement coverage
|
|
597
|
+
|
|
598
|
+
The inverse of `tier-tokens/hard-enforcement-missing`. Where `tier-tokens` flags an inviolable rule that has *no* hook to enforce it, `hook-coverage` flags a hook (or permissions entry) that points at a script which no longer exists — a dead gate that silently no-ops. Claude Code cannot run a script that isn't on disk, so a `PreToolUse` hook whose `command` references a deleted/renamed file stops blocking anything, while the user still believes the protection is in place.
|
|
599
|
+
|
|
600
|
+
| Rule ID | Severity | Trigger | Message |
|
|
601
|
+
|---|---|---|---|
|
|
602
|
+
| `hook-coverage/dead-hook` | warning | A `hooks.<Event>[].hooks[].command` or `permissions.{allow,deny,ask}[]` entry in `.claude/settings.json` contains a path-shaped token that does not exist on disk | `{origin} references "{path}" which does not exist on disk — the gate silently no-ops` |
|
|
603
|
+
|
|
604
|
+
**Detection algorithm:**
|
|
605
|
+
|
|
606
|
+
1. Load settings from project `.claude/settings.json`, project `.claude/settings.local.json`, and user `~/.claude/settings.json` (parsed as JSONC; missing files are skipped).
|
|
607
|
+
2. For each hook command and each `permissions` list entry, tokenize on whitespace (respecting quotes) and keep tokens that look like script paths (a path separator + a script extension such as `.sh`/`.js`/`.py`/`.ps1`, or an explicit `./` `~/` `/` `$VAR/` `C:\` prefix). Inline tool matchers like `Bash(npm login)` yield no path tokens.
|
|
608
|
+
3. Resolve each path token: expand a leading `~` and the env vars Claude Code documents for settings paths — `$CLAUDE_PROJECT_DIR`, `$CLAUDE_CONFIG_DIR`, `$HOME`, `$USERPROFILE`. A token that still contains an unresolvable `$VAR` is skipped (it cannot be verified, and a false "dead hook" is worse than a missed one).
|
|
609
|
+
4. Emit one warning per resolved path that does not exist on disk, with the source file's line number for project files (the user-global file is noted inline).
|
|
610
|
+
|
|
611
|
+
**Stability:** experimental — the path-extraction heuristic is conservative by design (it prefers a missed dead hook over a false positive) and may broaden as more hook-command shapes are observed.
|
|
612
|
+
|
|
595
613
|
---
|
|
596
614
|
|
|
597
615
|
## 4. Rule Catalog (machine-readable)
|
|
@@ -665,6 +683,5 @@ This specification follows semver:
|
|
|
665
683
|
- [AAIF AGENTS.md](https://github.com/anthropics/agents-spec) — Linux Foundation standard for multi-agent context files
|
|
666
684
|
- [Model Context Protocol Specification](https://spec.modelcontextprotocol.io/) — the protocol that MCP server configs serve
|
|
667
685
|
- [MCP Server Configuration Linting Specification](./MCP_CONFIG_LINT_SPEC.md) — companion spec for linting MCP server configs
|
|
668
|
-
- [ctxlint](https://github.com/YawLabs/ctxlint) — reference implementation of
|
|
686
|
+
- [ctxlint](https://github.com/YawLabs/ctxlint) — reference implementation of all four specifications
|
|
669
687
|
- [mcp-compliance](https://github.com/YawLabs/mcp-compliance) — tests MCP server behavior against the protocol spec
|
|
670
|
-
- [mcp.hosting](https://mcp.hosting) — managed MCP server hosting
|
package/MCP_CONFIG_LINT_SPEC.md
CHANGED
|
@@ -16,7 +16,7 @@ This specification defines a standard set of lint rules for validating MCP serve
|
|
|
16
16
|
|
|
17
17
|
The specification includes:
|
|
18
18
|
- A complete reference of MCP config file locations, formats, and client-specific behaviors
|
|
19
|
-
-
|
|
19
|
+
- 27 lint rules organized into 8 categories with defined severities
|
|
20
20
|
- A machine-readable rule catalog ([`mcp-config-lint-rules.json`](./mcp-config-lint-rules.json))
|
|
21
21
|
- Auto-fix definitions for rules that support automated correction
|
|
22
22
|
|
|
@@ -408,4 +408,3 @@ This specification follows semver:
|
|
|
408
408
|
- [Model Context Protocol Specification](https://spec.modelcontextprotocol.io/) — the underlying protocol this config format serves
|
|
409
409
|
- [ctxlint](https://github.com/YawLabs/ctxlint) — reference implementation of this specification
|
|
410
410
|
- [mcp-compliance](https://github.com/YawLabs/mcp-compliance) — tests MCP server *behavior* against the protocol spec (complementary to config linting)
|
|
411
|
-
- [mcp.hosting](https://mcp.hosting) — managed MCP server hosting (eliminates many config issues by providing remote endpoints)
|
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
Your `CLAUDE.md` is lying to your agent. Your `.mcp.json` has a hardcoded API key. ctxlint catches both.
|
|
13
13
|
|
|
14
|
-
[](yaw
|
|
14
|
+
[](https://yaw.sh/mcp/install?name=ctxlint&command=npx&args=-y%2C%40yawlabs%2Fctxlint%2Cserve&description=Lint%20AI%20agent%20context%20files%20and%20MCP%20configs%20against%20your%20codebase&source=https%3A%2F%2Fgithub.com%2FYawLabs%2Fctxlint)
|
|
15
15
|
|
|
16
16
|
One click adds this to your local Yaw MCP config so it's available in every Yaw Terminal session. Or install manually below.
|
|
17
17
|
|
|
@@ -82,6 +82,7 @@ Useful if you want `ctxlint` available in every project without per-project setu
|
|
|
82
82
|
| **Frontmatter** | Invalid or missing YAML frontmatter in Cursor .mdc, Copilot instructions, and Windsurf rules |
|
|
83
83
|
| **CI coverage** | Release/deploy workflows in `.github/workflows/` not documented in any context file |
|
|
84
84
|
| **CI secrets** | Secrets used in CI workflows (`${{ secrets.X }}`) not mentioned in context files |
|
|
85
|
+
| **Dead hooks** | PreToolUse hooks / permissions entries in `.claude/settings.json` pointing at scripts that no longer exist (a dead gate silently no-ops). Scans project `.claude/settings.json[.local]` by default; pass `--hooks-global` to also scan the user-global `~/.claude/settings.json` |
|
|
85
86
|
| **Missing secrets** | GitHub secrets set on sibling repos but missing from current project |
|
|
86
87
|
| **Diverged configs** | Canonical config files (CI, tsconfig, etc.) drifting across sibling projects |
|
|
87
88
|
| **Missing workflows** | GitHub Actions workflows present in 2+ siblings but absent here |
|
|
@@ -176,12 +177,14 @@ Summary: 3 errors, 2 warnings, 1 info
|
|
|
176
177
|
|
|
177
178
|
The full specification for MCP config linting rules, the cross-client config landscape, and a machine-readable rule catalog are published as open specifications:
|
|
178
179
|
|
|
179
|
-
- **[`MCP_CONFIG_LINT_SPEC.md`](./MCP_CONFIG_LINT_SPEC.md)** —
|
|
180
|
+
- **[`MCP_CONFIG_LINT_SPEC.md`](./MCP_CONFIG_LINT_SPEC.md)** — the full lint-rule set (rule count in the [Specifications](#specifications) family table), the complete client/format reference, and implementation guidance. Tool-agnostic — any linter can implement it.
|
|
180
181
|
- **[`mcp-config-lint-rules.json`](./mcp-config-lint-rules.json)** — Machine-readable rule catalog for programmatic consumption by AI agents, CI systems, and other tools.
|
|
181
182
|
|
|
182
183
|
## mcph Config Linting
|
|
183
184
|
|
|
184
|
-
ctxlint also lints `.mcph.json` — the config file read by the [`@yawlabs/mcph`](https://github.com/YawLabs/mcph) CLI
|
|
185
|
+
ctxlint also lints `.mcph.json` — the config file read by the [`@yawlabs/mcph`](https://github.com/YawLabs/mcph) CLI. Distinct from `.mcp.json` (different schema, different threat model). Applies across the user-global (`~/.mcph.json`), per-project (`.mcph.json`), and machine-local (`.mcph.local.json`) scope cascade.
|
|
186
|
+
|
|
187
|
+
> **Note:** the mcph rule family (and the `ctxlint_mcph_audit` MCP tool) is **under review** — the upstream mcp.hosting platform is archived, so this family's future is being reassessed. The rules and tool still ship and work; only their long-term status is undecided.
|
|
185
188
|
|
|
186
189
|
```bash
|
|
187
190
|
# Lint context files + .mcph.json
|
|
@@ -536,14 +539,15 @@ Returns structured JSON with all file results, issues, and summary — useful fo
|
|
|
536
539
|
|
|
537
540
|
## Specifications
|
|
538
541
|
|
|
539
|
-
ctxlint is the reference implementation of
|
|
542
|
+
ctxlint is the reference implementation of four open specifications for linting AI agent interfaces. These specs are tool-agnostic — any linter, IDE extension, or CI system can implement them.
|
|
540
543
|
|
|
541
544
|
| Spec | What it covers |
|
|
542
545
|
| -------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
543
|
-
| **[AI Context File Linting Spec](./CONTEXT_LINT_SPEC.md)** |
|
|
544
|
-
| **[MCP Config Linting Spec](./MCP_CONFIG_LINT_SPEC.md)** |
|
|
546
|
+
| **[AI Context File Linting Spec](./CONTEXT_LINT_SPEC.md)** | 28 rules for validating context files (CLAUDE.md, .cursorrules, AGENTS.md, etc.) across 17 clients. Covers file formats, frontmatter schemas, path/command validation, staleness, token budgets, redundancy, and contradictions. |
|
|
547
|
+
| **[MCP Config Linting Spec](./MCP_CONFIG_LINT_SPEC.md)** | 27 rules for validating MCP server configs (.mcp.json, .cursor/mcp.json, .vscode/mcp.json, etc.) across 8 clients. Covers schema validation, hardcoded secrets, env var syntax, deprecated transports, and cross-file consistency. |
|
|
545
548
|
| **mcph Config Linting** (`mcph-config-lint-rules.json`) | 10 rules for validating `.mcph.json` — the config file read by the `@yawlabs/mcph` CLI. Covers PAT format + leakage, env-var posture, plaintext API endpoints, schema drift, and allow/deny list semantics across the scope cascade. |
|
|
546
|
-
| **[Agent Session Linting Spec](./AGENT_SESSION_LINT_SPEC.md)** |
|
|
549
|
+
| **[Agent Session Linting Spec](./AGENT_SESSION_LINT_SPEC.md)** | 8 rules for auditing agent session data (history, memory) across 8 agents. Covers cross-project secret consistency, config drift, stale memory, and loop detection. |
|
|
550
|
+
| **[Agent Skill Linting Spec](./AGENT_SKILL_LINT_SPEC.md)** | 5 rules for auditing Claude Code skill (`SKILL.md`) and agent (`.md`) definitions under `~/.claude`. Covers frontmatter presence, broken refs, trigger-phrase collisions, orphaned skills, and dead tool restrictions. (v1, experimental) |
|
|
547
551
|
|
|
548
552
|
All specs include machine-readable rule catalogs for programmatic consumption:
|
|
549
553
|
|
|
@@ -551,11 +555,11 @@ All specs include machine-readable rule catalogs for programmatic consumption:
|
|
|
551
555
|
- [`mcp-config-lint-rules.json`](./mcp-config-lint-rules.json) — MCP config rules and 8 client definitions
|
|
552
556
|
- [`mcph-config-lint-rules.json`](./mcph-config-lint-rules.json) — mcph CLI config rules (`.mcph.json`)
|
|
553
557
|
- [`agent-session-lint-rules.json`](./agent-session-lint-rules.json) — session lint rules and 8 agent data source definitions
|
|
558
|
+
- [`agent-skill-lint-rules.json`](./agent-skill-lint-rules.json) — agent-skill lint rules (`~/.claude/skills`, `~/.claude/agents`)
|
|
554
559
|
|
|
555
560
|
## Also By Yaw Labs
|
|
556
561
|
|
|
557
562
|
- [Yaw](https://yaw.sh) — The AI-native terminal
|
|
558
|
-
- [mcp.hosting](https://mcp.hosting) — MCP server proxy platform
|
|
559
563
|
- [Spend](https://spend.sh) — AI spend tracking, cost estimation, and provider comparison across 10+ providers
|
|
560
564
|
- [Token Limit News](https://tokenlimit.news) — Weekly AI dev tooling newsletter
|
|
561
565
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$schema": "https://
|
|
2
|
+
"$schema": "https://raw.githubusercontent.com/YawLabs/ctxlint/main/schemas/ctxlint-catalog.schema.json",
|
|
3
3
|
"title": "AI Agent Session Lint Rules",
|
|
4
4
|
"description": "Machine-readable catalog of lint rules for AI agent session data cross-project consistency",
|
|
5
5
|
"specVersion": "1.0.0-draft",
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://raw.githubusercontent.com/YawLabs/ctxlint/main/schemas/ctxlint-catalog.schema.json",
|
|
3
|
+
"title": "AI Agent Skill Lint Rules",
|
|
4
|
+
"description": "Machine-readable catalog of lint rules for Claude Code agent-skill definitions (~/.claude/skills/<name>/SKILL.md and ~/.claude/agents/*.md). The fourth ctxlint pillar. v1 is intentionally Claude-Code-only; other agents' skill/subagent formats may be added in later spec versions.",
|
|
5
|
+
"specVersion": "1.0.0-draft",
|
|
6
|
+
"specDate": "2026-06-02",
|
|
7
|
+
"repository": "https://github.com/YawLabs/ctxlint",
|
|
8
|
+
"categories": [
|
|
9
|
+
{
|
|
10
|
+
"id": "skill",
|
|
11
|
+
"name": "Agent Skill Audit",
|
|
12
|
+
"description": "Validates Claude Code skill (SKILL.md) and agent (.md) definition files for frontmatter presence, broken references, trigger collisions, orphaned skills, and dead tool restrictions.",
|
|
13
|
+
"scope": "user-global"
|
|
14
|
+
}
|
|
15
|
+
],
|
|
16
|
+
"rules": [
|
|
17
|
+
{
|
|
18
|
+
"id": "skill/missing-frontmatter",
|
|
19
|
+
"category": "skill",
|
|
20
|
+
"severity": "warning",
|
|
21
|
+
"description": "A SKILL.md or agent .md file is missing required YAML frontmatter, or a required field within it. Skills and agents key off `name` + `description`; the description drives selection, so a missing one means the skill/agent is never invoked.",
|
|
22
|
+
"trigger": "The file has no `---`-delimited frontmatter, has unclosed frontmatter, or is missing a required field (name, description). A missing file body altogether is reported as error; a missing single field as warning.",
|
|
23
|
+
"message": "{file}: missing required frontmatter field \"{field}\"",
|
|
24
|
+
"fixable": false,
|
|
25
|
+
"stability": "experimental"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"id": "skill/broken-ref",
|
|
29
|
+
"category": "skill",
|
|
30
|
+
"severity": "warning",
|
|
31
|
+
"description": "A relative path reference (./ or ../) in a SKILL.md / agent body does not resolve to a file or directory relative to the skill/agent file. Reuses the path-reference detection shape from the context-file pillar; only explicitly-relative refs are verified to avoid false positives on prose.",
|
|
32
|
+
"trigger": "A ./ or ../ path token in the body (outside example code blocks) does not exist on disk relative to the skill directory.",
|
|
33
|
+
"message": "{file}: references \"{path}\" which does not exist relative to the skill directory",
|
|
34
|
+
"fixable": false,
|
|
35
|
+
"stability": "experimental"
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"id": "skill/trigger-collision",
|
|
39
|
+
"category": "skill",
|
|
40
|
+
"severity": "warning",
|
|
41
|
+
"description": "Two or more skills/agents declare the same trigger phrase (a quoted phrase in the description, or a trigger/triggers field). When triggers collide, only one wins -- invocation becomes non-deterministic.",
|
|
42
|
+
"trigger": "A normalized trigger phrase (lowercased, whitespace-collapsed) is declared by more than one distinct skill/agent file.",
|
|
43
|
+
"message": "Trigger phrase \"{trigger}\" is declared by {count} skills/agents -- only one will win",
|
|
44
|
+
"fixable": false,
|
|
45
|
+
"stability": "experimental"
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"id": "skill/orphaned",
|
|
49
|
+
"category": "skill",
|
|
50
|
+
"severity": "warning",
|
|
51
|
+
"description": "A ~/.claude/skills/<name>/ directory exists but contains no SKILL.md. Claude Code has nothing to load for that skill -- the directory is dead weight.",
|
|
52
|
+
"trigger": "A subdirectory of ~/.claude/skills/ does not contain a SKILL.md file.",
|
|
53
|
+
"message": "{dir}: skill directory has no SKILL.md -- Claude Code has nothing to load",
|
|
54
|
+
"fixable": false,
|
|
55
|
+
"stability": "experimental"
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"id": "skill/dead-tool-restriction",
|
|
59
|
+
"category": "skill",
|
|
60
|
+
"severity": "warning",
|
|
61
|
+
"description": "An agent definition's tool restriction (tools / allowed-tools frontmatter) lists a tool name that is not a known Claude Code built-in tool. A dead restriction silently fails to grant/deny the intended tool. MCP-namespaced tools (mcp__server__tool) and wildcards are not validated (their existence depends on loaded MCP servers).",
|
|
62
|
+
"trigger": "A non-MCP, non-wildcard entry in an agent's tools/allowed-tools list does not match a known built-in Claude Code tool name (Bash, Read, Edit, Glob, Grep, Write, Task, WebFetch, WebSearch, etc.).",
|
|
63
|
+
"message": "{file}: tool restriction lists \"{tool}\" which is not a known Claude Code tool",
|
|
64
|
+
"fixable": false,
|
|
65
|
+
"stability": "experimental"
|
|
66
|
+
}
|
|
67
|
+
],
|
|
68
|
+
"dataSources": [
|
|
69
|
+
{
|
|
70
|
+
"agent": "claude-code",
|
|
71
|
+
"skills": "~/.claude/skills/<name>/SKILL.md",
|
|
72
|
+
"agents": "~/.claude/agents/*.md"
|
|
73
|
+
}
|
|
74
|
+
]
|
|
75
|
+
}
|
package/context-lint-rules.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$schema": "https://
|
|
2
|
+
"$schema": "https://raw.githubusercontent.com/YawLabs/ctxlint/main/schemas/ctxlint-catalog.schema.json",
|
|
3
3
|
"specVersion": "1.0.0-draft",
|
|
4
4
|
"specDate": "2026-04-07",
|
|
5
5
|
"repository": "https://github.com/YawLabs/ctxlint",
|
|
@@ -64,6 +64,12 @@
|
|
|
64
64
|
"name": "CI Secrets Documentation",
|
|
65
65
|
"description": "Checks that CI secrets used in workflows are documented in context files.",
|
|
66
66
|
"scope": "cross-file"
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"id": "hook-coverage",
|
|
70
|
+
"name": "Hook Enforcement Coverage",
|
|
71
|
+
"description": "Validates that PreToolUse hooks and permissions entries in .claude/settings.json reference scripts that exist on disk. The inverse of tier-tokens/hard-enforcement-missing.",
|
|
72
|
+
"scope": "cross-file"
|
|
67
73
|
}
|
|
68
74
|
],
|
|
69
75
|
"rules": [
|
|
@@ -379,6 +385,16 @@
|
|
|
379
385
|
"message": "CI secret \"{name}\" is used in {workflow} but not mentioned in any context file",
|
|
380
386
|
"fixable": false,
|
|
381
387
|
"stability": "stable"
|
|
388
|
+
},
|
|
389
|
+
{
|
|
390
|
+
"id": "hook-coverage/dead-hook",
|
|
391
|
+
"category": "hook-coverage",
|
|
392
|
+
"severity": "warning",
|
|
393
|
+
"description": "A PreToolUse (or other) hook command, or a permissions entry, in .claude/settings.json references a script/path that does not exist on disk. The gate silently no-ops -- Claude Code cannot run a missing script, so the protection the user believes is in place does nothing.",
|
|
394
|
+
"trigger": "A hooks.<Event>[].hooks[].command or permissions.{allow,deny,ask}[] entry contains a path-shaped token (resolved through ~, $CLAUDE_PROJECT_DIR, $CLAUDE_CONFIG_DIR, $HOME) that does not exist on disk. Entries with unresolvable env vars are skipped (cannot be verified). Scans project .claude/settings.json + settings.local.json and user ~/.claude/settings.json.",
|
|
395
|
+
"message": "{origin} references \"{path}\" which does not exist on disk -- the gate silently no-ops",
|
|
396
|
+
"fixable": false,
|
|
397
|
+
"stability": "experimental"
|
|
382
398
|
}
|
|
383
399
|
],
|
|
384
400
|
"formats": [
|