@yawlabs/ctxlint 0.7.0 → 0.9.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.
@@ -5,4 +5,3 @@
5
5
  language: node
6
6
  always_run: true
7
7
  pass_filenames: false
8
- types: [file]
@@ -44,6 +44,8 @@ This is the third pillar alongside context file linting (`CLAUDE.md`, `.cursorru
44
44
  - [2.3 session/missing-workflow](#23-sessionmissing-workflow)
45
45
  - [2.4 session/stale-memory](#24-sessionstale-memory)
46
46
  - [2.5 session/duplicate-memory](#25-sessionduplicate-memory)
47
+ - [2.6 session/consecutive-repeat](#26-sessionconsecutive-repeat)
48
+ - [2.7 session/cyclic-pattern](#27-sessioncyclic-pattern)
47
49
  - [3. Rule Catalog (machine-readable)](#3-rule-catalog-machine-readable)
48
50
  - [4. Implementing This Specification](#4-implementing-this-specification)
49
51
  - [5. Contributing](#5-contributing)
@@ -125,7 +127,7 @@ Skip hidden directories (starting with `.`) and `node_modules`.
125
127
 
126
128
  ## 2. Lint Rules
127
129
 
128
- 5 rules in 1 category (`session`). All rules in this category perform cross-project checks using sibling detection.
130
+ 7 rules in 1 category (`session`). All rules in this category perform cross-project checks using sibling detection or per-project history analysis.
129
131
 
130
132
  Severity levels:
131
133
  - **error** -- the session data reveals a verifiably missing configuration. Should fail CI.
@@ -263,6 +265,53 @@ Detects memory entries from different projects that have significant content ove
263
265
 
264
266
  ---
265
267
 
268
+ ### 2.6 session/consecutive-repeat
269
+
270
+ Detects when an agent runs the same command 3 or more times consecutively, indicating a loop.
271
+
272
+ | Field | Value |
273
+ |---|---|
274
+ | **Rule ID** | `session/consecutive-repeat` |
275
+ | **Severity** | warning |
276
+ | **Trigger** | 3+ consecutive history entries with identical `display` values for the current project |
277
+ | **Message** | `Command run <N> times consecutively: "<command>"` |
278
+
279
+ **Detection algorithm:**
280
+
281
+ 1. Filter history entries to the current project path (normalized).
282
+ 2. Sort entries by timestamp.
283
+ 3. Slide a window over the sorted entries. For each run of 3+ entries with identical `display` values, emit a warning.
284
+
285
+ **Notes:**
286
+ - Truncates long command strings to 80 characters in the message for readability.
287
+ - This rule helps surface cases where an agent is stuck retrying a failing command instead of changing approach.
288
+
289
+ ---
290
+
291
+ ### 2.7 session/cyclic-pattern
292
+
293
+ Detects short repeating cycles of commands, indicating an agent stuck in a loop.
294
+
295
+ | Field | Value |
296
+ |---|---|
297
+ | **Rule ID** | `session/cyclic-pattern` |
298
+ | **Severity** | warning |
299
+ | **Trigger** | A sequence of 2-3 distinct commands repeating 2+ times consecutively (e.g. A,B,A,B) |
300
+ | **Message** | `Cyclic pattern repeated <N> times: <cycle>` |
301
+
302
+ **Detection algorithm:**
303
+
304
+ 1. Filter history entries to the current project, sorted by timestamp.
305
+ 2. For cycle lengths 2 and 3, slide a window checking if the next `cycleLen` entries match the current cycle.
306
+ 3. Cycles where every element is the same are excluded (already caught by `session/consecutive-repeat`).
307
+ 4. Subsumption: if a shorter cycle is fully contained within an already-reported longer cycle at the same position, skip it.
308
+
309
+ **Notes:**
310
+ - A cycle like "edit file → run tests → edit file → run tests" is a common pattern when an agent is making iterative fixes. This rule flags when the cycle repeats enough times to suggest the agent isn't making progress.
311
+ - The suggestion directs users to check if a context file is missing workflow instructions.
312
+
313
+ ---
314
+
266
315
  ## 3. Rule Catalog (machine-readable)
267
316
 
268
317
  A machine-readable JSON catalog of all rules is available at [`agent-session-lint-rules.json`](./agent-session-lint-rules.json).
@@ -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
- - 19 lint rules organized into 7 categories with defined severities
18
+ - 21 lint rules organized into 9 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
@@ -45,6 +45,8 @@ The specification includes:
45
45
  - [3.5 redundancy — inferable content](#35-redundancy--inferable-content)
46
46
  - [3.6 contradictions — cross-file conflicts](#36-contradictions--cross-file-conflicts)
47
47
  - [3.7 frontmatter — client metadata validation](#37-frontmatter--client-metadata-validation)
48
+ - [3.8 ci-coverage — CI workflow documentation](#38-ci-coverage--ci-workflow-documentation)
49
+ - [3.9 ci-secrets — CI secrets documentation](#39-ci-secrets--ci-secrets-documentation)
48
50
  - [4. Rule Catalog (machine-readable)](#4-rule-catalog-machine-readable)
49
51
  - [5. Implementing This Specification](#5-implementing-this-specification)
50
52
  - [6. Contributing](#6-contributing)
@@ -298,7 +300,7 @@ Context files consume an agent's context window. Counting tokens helps teams und
298
300
 
299
301
  ## 3. Lint Rules
300
302
 
301
- 19 rules organized into 7 categories.
303
+ 21 rules organized into 9 categories.
302
304
 
303
305
  Severity levels:
304
306
  - **error** — the context file has a verifiably incorrect reference or invalid metadata. Should fail CI.
@@ -537,6 +539,43 @@ Validates YAML frontmatter required by specific clients. Only applies to file fo
537
539
 
538
540
  ---
539
541
 
542
+ ### 3.8 ci-coverage — CI workflow documentation
543
+
544
+ Checks that release/deploy CI workflows are documented in context files. When agents encounter a project with CI release workflows but no documentation about how releases work, they guess — often incorrectly.
545
+
546
+ | Rule ID | Severity | Trigger | Message |
547
+ |---|---|---|---|
548
+ | `ci/no-release-docs` | info | `.github/workflows/` contains release/deploy/publish workflow(s) but no context file mentions the release process | `Release workflow(s) found but no context file documents the release process` |
549
+
550
+ **Detection algorithm:**
551
+
552
+ 1. Check if `.github/workflows/` exists. If not, skip.
553
+ 2. Scan workflow filenames for release-related patterns: `release`, `deploy`, `publish`, `cd`.
554
+ 3. For workflows not matched by filename, read the YAML `name:` field and check for the same patterns.
555
+ 4. If no release-related workflows exist, skip (only CI/test workflows — no documentation gap).
556
+ 5. Search all context file content for release documentation phrases (e.g., "release process", "push a v* tag", "npm publish", "deploy to").
557
+ 6. If no context file mentions release processes, emit one info-level issue.
558
+
559
+ ---
560
+
561
+ ### 3.9 ci-secrets — CI secrets documentation
562
+
563
+ Checks that secrets referenced in CI workflow files are mentioned in context files. Undocumented secrets are a common source of agent looping — agents try to create new tokens, pull from `.npmrc`, or guess at auth setup.
564
+
565
+ | Rule ID | Severity | Trigger | Message |
566
+ |---|---|---|---|
567
+ | `ci/undocumented-secret` | info | `${{ secrets.NAME }}` found in workflow YAML but `NAME` not mentioned in any context file | `CI secret "{name}" is used in {workflow} but not mentioned in any context file` |
568
+
569
+ **Detection algorithm:**
570
+
571
+ 1. Check if `.github/workflows/` exists. If not, skip.
572
+ 2. Read all `.yml`/`.yaml` files and extract `${{ secrets.NAME }}` references via regex.
573
+ 3. Exclude GitHub-provided secrets: `GITHUB_TOKEN`, `ACTIONS_RUNTIME_TOKEN`, `ACTIONS_RUNTIME_URL`, `ACTIONS_CACHE_URL`, `ACTIONS_ID_TOKEN_REQUEST_TOKEN`, `ACTIONS_ID_TOKEN_REQUEST_URL`, `ACTIONS_RESULTS_URL`.
574
+ 4. For each remaining secret, search all context files for the secret name (case-insensitive, flexible underscore/space matching).
575
+ 5. Emit one info-level issue per undocumented secret.
576
+
577
+ ---
578
+
540
579
  ## 4. Rule Catalog (machine-readable)
541
580
 
542
581
  A machine-readable JSON catalog of all rules and supported context file formats is available at [`context-lint-rules.json`](./context-lint-rules.json).
@@ -572,7 +611,7 @@ For each discovered file:
572
611
 
573
612
  Run per-file checks (paths, commands, staleness, tokens, redundancy, frontmatter) independently per file. These can be parallelized.
574
613
 
575
- Run cross-file checks (aggregate tokens, duplicate content, contradictions) after all per-file parsing is complete. These need the full set of parsed files.
614
+ Run cross-file checks (aggregate tokens, duplicate content, contradictions, ci-coverage, ci-secrets) after all per-file parsing is complete. These need the full set of parsed files.
576
615
 
577
616
  ### Reporting
578
617
 
package/README.md CHANGED
@@ -12,15 +12,23 @@ Your `CLAUDE.md` is lying to your agent. Your `.mcp.json` has a hardcoded API ke
12
12
 
13
13
  ## Why ctxlint?
14
14
 
15
- Context files rot fast. You rename a file, change a build script, or switch from Jest to Vitest and your `CLAUDE.md` still says the old thing. Your agent follows those instructions faithfully, then fails.
15
+ Every AI coding tool ships a context file: `CLAUDE.md`, `.cursorrules`, `AGENTS.md`, `.mcp.json`. These files are the single most important interface between you and your agent they tell it what to build, how to test, where things live.
16
16
 
17
+ But context files rot fast. You rename a file, change a build script, or switch from Jest to Vitest — and your `CLAUDE.md` still says the old thing. Your agent follows those stale instructions faithfully, then fails. You lose 10 minutes debugging what turns out to be a wrong path in line 12 of a markdown file.
18
+
19
+ Multiply that across a team with 5 context files, 3 MCP configs, and 2 people who touched the build system last week — and you have a real problem with no existing solution.
20
+
21
+ ctxlint is a linter purpose-built for this. It reads your context files, cross-references them against your actual codebase, and catches the drift before your agent does.
22
+
23
+ - **Instant startup** — ships as a single self-contained bundle with zero runtime dependencies. `npx` downloads ~200 KB and starts immediately
17
24
  - **Catches real problems** — broken paths, wrong commands, stale references, contradictions across files
18
25
  - **Smart suggestions** — detects git renames and fuzzy-matches to suggest the right path
19
26
  - **Auto-fix** — `--fix` rewrites broken paths automatically using git history
20
27
  - **Token-aware** — shows how much context window your files consume and flags redundant content
21
28
  - **Every AI tool** — supports Claude Code, Cursor, Copilot, Windsurf, Gemini, Cline, Aider, and 14 more
22
29
  - **Multiple outputs** — text, JSON, and SARIF (GitHub Code Scanning)
23
- - **MCP server** — 5 tools for IDE/agent integration with tool annotations for auto-approval
30
+ - **MCP server** — 6 tools for IDE/agent integration with tool annotations for auto-approval
31
+ - **Watch mode** — `--watch` re-lints automatically when context files change
24
32
 
25
33
  ## Install
26
34
 
@@ -67,6 +75,8 @@ Useful if you want `ctxlint` available in every project without per-project setu
67
75
  | **Redundancy** | Content the agent can already infer (e.g. "We use React" when react is in package.json) |
68
76
  | **Contradictions** | Conflicting directives across context files (e.g. "use Jest" in one, "use Vitest" in another) |
69
77
  | **Frontmatter** | Invalid or missing YAML frontmatter in Cursor .mdc, Copilot instructions, and Windsurf rules |
78
+ | **CI coverage** | Release/deploy workflows in `.github/workflows/` not documented in any context file |
79
+ | **CI secrets** | Secrets used in CI workflows (`${{ secrets.X }}`) not mentioned in context files |
70
80
 
71
81
  ## Supported Context Files
72
82
 
@@ -161,7 +171,7 @@ The full specification for MCP config linting rules, the cross-client config lan
161
171
  ## Example Output
162
172
 
163
173
  ```
164
- ctxlint v0.6.0
174
+ ctxlint v0.7.0
165
175
 
166
176
  Scanning /Users/you/my-app...
167
177
 
@@ -205,6 +215,7 @@ Options:
205
215
  --mcp-only Run only MCP config checks, skip context file checks
206
216
  --mcp-global Also scan user/global MCP config files (implies --mcp)
207
217
  --mcp-server Start the MCP server (for IDE/agent integration)
218
+ --watch Re-lint on context file changes
208
219
  -V, --version Output the version number
209
220
  -h, --help Display help
210
221
 
@@ -212,10 +223,18 @@ Commands:
212
223
  init Set up a git pre-commit hook
213
224
  ```
214
225
 
215
- **Available checks:** `paths`, `commands`, `staleness`, `tokens`, `redundancy`, `contradictions`, `frontmatter`, `mcp-schema`, `mcp-security`, `mcp-commands`, `mcp-deprecated`, `mcp-env`, `mcp-urls`, `mcp-consistency`, `mcp-redundancy`
226
+ **Available checks:** `paths`, `commands`, `staleness`, `tokens`, `redundancy`, `contradictions`, `frontmatter`, `ci-coverage`, `ci-secrets`, `mcp-schema`, `mcp-security`, `mcp-commands`, `mcp-deprecated`, `mcp-env`, `mcp-urls`, `mcp-consistency`, `mcp-redundancy`
216
227
 
217
228
  Passing any `mcp-*` check name implies `--mcp`.
218
229
 
230
+ ## Watch Mode
231
+
232
+ ```bash
233
+ npx @yawlabs/ctxlint --watch
234
+ ```
235
+
236
+ Re-lints automatically when any context file, MCP config, or `package.json` changes. Useful during development when you're editing context files alongside code.
237
+
219
238
  ## Use in CI
220
239
 
221
240
  ```yaml
@@ -225,6 +244,22 @@ Passing any `mcp-*` check name implies `--mcp`.
225
244
 
226
245
  Exits with code 1 if any errors or warnings are found.
227
246
 
247
+ ### GitHub Action
248
+
249
+ ```yaml
250
+ - name: Lint context files
251
+ uses: yawlabs/ctxlint-action@v1
252
+ ```
253
+
254
+ Or with options:
255
+
256
+ ```yaml
257
+ - name: Lint context files
258
+ uses: yawlabs/ctxlint-action@v1
259
+ with:
260
+ args: '--strict --mcp'
261
+ ```
262
+
228
263
  ### SARIF Output (GitHub Code Scanning)
229
264
 
230
265
  ```yaml
@@ -262,7 +297,7 @@ Add to your `.pre-commit-config.yaml`:
262
297
  ```yaml
263
298
  repos:
264
299
  - repo: https://github.com/yawlabs/ctxlint
265
- rev: v0.6.0
300
+ rev: v0.7.0
266
301
  hooks:
267
302
  - id: ctxlint
268
303
  ```
package/action.yml ADDED
@@ -0,0 +1,27 @@
1
+ name: 'ctxlint'
2
+ description: 'Lint AI agent context files and MCP server configs against your actual codebase'
3
+ branding:
4
+ icon: 'check-circle'
5
+ color: 'blue'
6
+
7
+ inputs:
8
+ args:
9
+ description: 'CLI arguments to pass to ctxlint (default: --strict)'
10
+ required: false
11
+ default: '--strict'
12
+ version:
13
+ description: 'ctxlint version to use (default: latest)'
14
+ required: false
15
+ default: 'latest'
16
+
17
+ runs:
18
+ using: 'composite'
19
+ steps:
20
+ - name: Set up Node.js
21
+ uses: actions/setup-node@v4
22
+ with:
23
+ node-version: '20'
24
+
25
+ - name: Run ctxlint
26
+ shell: bash
27
+ run: npx -y @yawlabs/ctxlint@${{ inputs.version }} ${{ inputs.args }}
@@ -68,6 +68,24 @@
68
68
  "trigger": "Two memory files from different projects have >60% line overlap.",
69
69
  "message": "\"{file1}\" and \"{file2}\" have {overlap}% content overlap",
70
70
  "fixable": false
71
+ },
72
+ {
73
+ "id": "session/consecutive-repeat",
74
+ "category": "session",
75
+ "severity": "warning",
76
+ "description": "Same command run 3+ times consecutively in agent history.",
77
+ "trigger": "Agent history for the current project shows the same display string 3+ times in a row.",
78
+ "message": "Command run {count} times consecutively: \"{command}\"",
79
+ "fixable": false
80
+ },
81
+ {
82
+ "id": "session/cyclic-pattern",
83
+ "category": "session",
84
+ "severity": "warning",
85
+ "description": "Cyclic command pattern detected in agent history.",
86
+ "trigger": "Agent history shows a repeating sequence of 2-3 commands (e.g. A,B,A,B) repeated 2+ times.",
87
+ "message": "Cyclic pattern repeated {count} times: {cycle}",
88
+ "fixable": false
71
89
  }
72
90
  ],
73
91
  "dataSources": [
@@ -46,6 +46,18 @@
46
46
  "name": "Client Metadata Validation",
47
47
  "description": "Validates YAML frontmatter required by specific AI coding clients.",
48
48
  "scope": "per-file"
49
+ },
50
+ {
51
+ "id": "ci-coverage",
52
+ "name": "CI Workflow Documentation",
53
+ "description": "Checks that release/deploy CI workflows are documented in context files.",
54
+ "scope": "cross-file"
55
+ },
56
+ {
57
+ "id": "ci-secrets",
58
+ "name": "CI Secrets Documentation",
59
+ "description": "Checks that CI secrets used in workflows are documented in context files.",
60
+ "scope": "cross-file"
49
61
  }
50
62
  ],
51
63
  "rules": [
@@ -269,6 +281,24 @@
269
281
  "message": "No activation field — rule may not be applied automatically",
270
282
  "fixable": false,
271
283
  "appliesTo": ["cursor-mdc"]
284
+ },
285
+ {
286
+ "id": "ci/no-release-docs",
287
+ "category": "ci-coverage",
288
+ "severity": "info",
289
+ "description": "Release/deploy CI workflow exists but no context file documents the process.",
290
+ "trigger": ".github/workflows/ contains release/deploy/publish workflow but no context file mentions the release process.",
291
+ "message": "Release workflow found but no context file documents the release process",
292
+ "fixable": false
293
+ },
294
+ {
295
+ "id": "ci/undocumented-secret",
296
+ "category": "ci-secrets",
297
+ "severity": "info",
298
+ "description": "CI workflow references a secret not mentioned in any context file.",
299
+ "trigger": "${{ secrets.NAME }} found in workflow YAML but NAME not mentioned in any context file. GitHub-provided secrets (GITHUB_TOKEN, etc.) are excluded.",
300
+ "message": "CI secret \"{name}\" is used in {workflow} but not mentioned in any context file",
301
+ "fixable": false
272
302
  }
273
303
  ],
274
304
  "formats": [