@yawlabs/ctxlint 0.9.1 → 0.9.3
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/AGENT_SESSION_LINT_SPEC.md +27 -0
- package/CONTEXT_LINT_SPEC.md +23 -5
- package/README.md +35 -4
- package/agent-session-lint-rules.json +25 -7
- package/context-lint-rules.json +191 -45
- package/dist/index.js +1827 -1461
- package/mcp-config-lint-rules.json +189 -44
- package/package.json +8 -2
|
@@ -46,6 +46,7 @@ This is the third pillar alongside context file linting (`CLAUDE.md`, `.cursorru
|
|
|
46
46
|
- [2.5 session/duplicate-memory](#25-sessionduplicate-memory)
|
|
47
47
|
- [2.6 session/consecutive-repeat](#26-sessionconsecutive-repeat)
|
|
48
48
|
- [2.7 session/cyclic-pattern](#27-sessioncyclic-pattern)
|
|
49
|
+
- [2.8 session/memory-index-overflow](#28-sessionmemory-index-overflow)
|
|
49
50
|
- [3. Rule Catalog (machine-readable)](#3-rule-catalog-machine-readable)
|
|
50
51
|
- [4. Implementing This Specification](#4-implementing-this-specification)
|
|
51
52
|
- [5. Contributing](#5-contributing)
|
|
@@ -312,6 +313,32 @@ Detects short repeating cycles of commands, indicating an agent stuck in a loop.
|
|
|
312
313
|
|
|
313
314
|
---
|
|
314
315
|
|
|
316
|
+
### 2.8 session/memory-index-overflow
|
|
317
|
+
|
|
318
|
+
Detects when `MEMORY.md` exceeds Claude Code's session-load cap. Claude Code loads the first 200 lines OR 25KB of `MEMORY.md` at session start — whichever comes first. Entries past the cap are silently dropped, so auto-memory pointers beyond that point are effectively invisible to the agent.
|
|
319
|
+
|
|
320
|
+
| Field | Value |
|
|
321
|
+
|---|---|
|
|
322
|
+
| **Rule ID** | `session/memory-index-overflow` |
|
|
323
|
+
| **Severity** | warning |
|
|
324
|
+
| **Trigger** | `~/.claude/projects/<encoded-project>/memory/MEMORY.md` exceeds 200 lines OR 25,600 bytes |
|
|
325
|
+
| **Message (lines)** | `MEMORY.md has <N> lines — only the first 200 are loaded. <excess> line(s) are effectively invisible.` |
|
|
326
|
+
| **Message (bytes)** | `MEMORY.md is <N> bytes — only the first 25,600 bytes are loaded. ~<excess> bytes are effectively invisible.` |
|
|
327
|
+
| **Source** | [code.claude.com/docs/en/memory](https://code.claude.com/docs/en/memory) |
|
|
328
|
+
|
|
329
|
+
**Detection algorithm:**
|
|
330
|
+
|
|
331
|
+
1. Resolve `MEMORY.md` via the Claude-encoded project directory: `~/.claude/projects/<encode(currentProject)>/memory/MEMORY.md`.
|
|
332
|
+
2. If the file doesn't exist, no-op.
|
|
333
|
+
3. Count lines and bytes. Emit a warning for each dimension that exceeds its cap.
|
|
334
|
+
|
|
335
|
+
**Notes:**
|
|
336
|
+
- Each MEMORY.md entry should stay under ~150 characters (one-line pointer, not content).
|
|
337
|
+
- The remediation is to trim older entries, consolidate duplicates, or move detail into the corresponding topic file — topic files stay on-demand and don't count toward the cap.
|
|
338
|
+
- Both line and byte caps can fire independently (a short file with very long lines trips the byte cap first; a long file with short lines trips the line cap first).
|
|
339
|
+
|
|
340
|
+
---
|
|
341
|
+
|
|
315
342
|
## 3. Rule Catalog (machine-readable)
|
|
316
343
|
|
|
317
344
|
A machine-readable JSON catalog of all rules is available at [`agent-session-lint-rules.json`](./agent-session-lint-rules.json).
|
package/CONTEXT_LINT_SPEC.md
CHANGED
|
@@ -380,13 +380,31 @@ Monitors context file size to help teams manage context window consumption.
|
|
|
380
380
|
| `warning` | 3000 | Per-file warning |
|
|
381
381
|
| `error` | 8000 | Per-file error |
|
|
382
382
|
| `aggregate` | 5000 | Cross-file combined warning |
|
|
383
|
+
| `tierBreakdown` | 1000 | Triggers `tier-tokens/section-breakdown` on an always-loaded file |
|
|
384
|
+
| `tierAggregate` | 4000 | Triggers `tier-tokens/aggregate` across always-loaded files |
|
|
385
|
+
|
|
386
|
+
### 3.5 tier-tokens — tier-aware token accounting
|
|
387
|
+
|
|
388
|
+
Reports token cost attributable to the **always-loaded** tier: files Claude Code (and similar agents) load into every session regardless of request. Complements `tokens` by surfacing which sections / files are costing budget every turn — and which inviolable rules need hook-based enforcement to actually bind.
|
|
389
|
+
|
|
390
|
+
"Always-loaded" basenames: `CLAUDE.md`, `CLAUDE.local.md`, `AGENTS.md`, `AGENTS.override.md`, `AGENT.md`, `GEMINI.md`, `.cursorrules`, `.windsurfrules`, `.clinerules`, `.aiderules`, `.continuerules`, `.rules`, `.goosehints`, `replit.md`, `.github/copilot-instructions.md`, `.junie/guidelines.md`, `.goose/instructions.md`. Rules files in `/rules/` directories are classified by frontmatter: if `paths:` is set they're path-scoped on-demand; otherwise they're always-loaded.
|
|
391
|
+
|
|
392
|
+
| Rule ID | Severity | Trigger | Message |
|
|
393
|
+
|---|---|---|---|
|
|
394
|
+
| `tier-tokens/section-breakdown` | info | Always-loaded file exceeds `tierBreakdown` tokens AND has H1/H2 sections | `{N} tokens loaded every session — heaviest top-level section(s): ...` |
|
|
395
|
+
| `tier-tokens/aggregate` | warning | Two or more always-loaded files combined exceed `tierAggregate` tokens | `{count} always-loaded files total {N} tokens — loaded every session` |
|
|
396
|
+
| `tier-tokens/hard-enforcement-missing` | info | Line in an always-loaded file uses inviolable framing (NEVER/ALWAYS/DO NOT/MUST NOT) with a backticked command, and no matching PreToolUse hook or `permissions.deny` entry exists in `.claude/settings.json` or `~/.claude/settings.json` | `Inviolable framing ("{line}") without a hook to back it up` |
|
|
397
|
+
|
|
398
|
+
**Note on overlap with `tokens`:** `tokens/info` and `tier-tokens/section-breakdown` both fire on a large CLAUDE.md. They're complementary — `tokens` is tier-agnostic ("this file is large"), `tier-tokens` adds the always-loaded attribution and demotion guidance. Use `--ignore tokens` or `--ignore tier-tokens` to pick one.
|
|
399
|
+
|
|
400
|
+
**Source:** [Claude Code memory docs](https://code.claude.com/docs/en/memory). Rule behavior is grounded in the documented loading model ("there's no guarantee of strict compliance" → hard-enforcement-missing; section-demotion-to-skills → structurally reduces per-session cost).
|
|
383
401
|
|
|
384
402
|
**Suggestions:**
|
|
385
403
|
- For `excessive`: `Consider splitting into focused sections or removing redundant content.`
|
|
386
404
|
- For `large`: `Consider trimming — research shows diminishing returns past ~300 lines.`
|
|
387
405
|
- For `aggregate`: `Consider consolidating or trimming to reduce per-session context cost.`
|
|
388
406
|
|
|
389
|
-
### 3.
|
|
407
|
+
### 3.6 redundancy — inferable content
|
|
390
408
|
|
|
391
409
|
Detects content that the agent can already infer from project metadata, reducing unnecessary context window consumption.
|
|
392
410
|
|
|
@@ -442,7 +460,7 @@ Implementations should maintain and extend this mapping as the ecosystem evolves
|
|
|
442
460
|
|
|
443
461
|
**Suggestion for `duplicate-content`:** `Consider consolidating into a single context file.`
|
|
444
462
|
|
|
445
|
-
### 3.
|
|
463
|
+
### 3.7 contradictions — cross-file conflicts
|
|
446
464
|
|
|
447
465
|
Detects conflicting directives across multiple context files. This is a cross-file check.
|
|
448
466
|
|
|
@@ -518,7 +536,7 @@ Detects conflicting directives across multiple context files. This is a cross-fi
|
|
|
518
536
|
- Only flag contradictions *across* files. A single file contradicting itself is unusual and likely intentional (e.g., "use camelCase for variables, PascalCase for components").
|
|
519
537
|
- Include the exact line numbers and text from both files in the detail.
|
|
520
538
|
|
|
521
|
-
### 3.
|
|
539
|
+
### 3.8 frontmatter — client metadata validation
|
|
522
540
|
|
|
523
541
|
Validates YAML frontmatter required by specific clients. Only applies to file formats that use frontmatter.
|
|
524
542
|
|
|
@@ -539,7 +557,7 @@ Validates YAML frontmatter required by specific clients. Only applies to file fo
|
|
|
539
557
|
|
|
540
558
|
---
|
|
541
559
|
|
|
542
|
-
### 3.
|
|
560
|
+
### 3.9 ci-coverage — CI workflow documentation
|
|
543
561
|
|
|
544
562
|
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
563
|
|
|
@@ -558,7 +576,7 @@ Checks that release/deploy CI workflows are documented in context files. When ag
|
|
|
558
576
|
|
|
559
577
|
---
|
|
560
578
|
|
|
561
|
-
### 3.
|
|
579
|
+
### 3.10 ci-secrets — CI secrets documentation
|
|
562
580
|
|
|
563
581
|
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
582
|
|
package/README.md
CHANGED
|
@@ -205,10 +205,11 @@ Session checks are **opt-in** because they access files outside the project dire
|
|
|
205
205
|
| **Stale memory** | Memory entries referencing file paths that no longer exist |
|
|
206
206
|
| **Duplicate memory** | Near-duplicate memory entries across projects (>60% overlap) |
|
|
207
207
|
| **Loop detection** | Agent stuck in a loop — 3+ consecutive identical commands, or cyclic A,B,A,B patterns |
|
|
208
|
+
| **Memory index overflow** | `MEMORY.md` exceeds Claude Code's documented 200-line / 25KB session-load cap, so entries past the cap are invisible to the agent |
|
|
208
209
|
|
|
209
210
|
### Session Linting Specification
|
|
210
211
|
|
|
211
|
-
- **[`AGENT_SESSION_LINT_SPEC.md`](./AGENT_SESSION_LINT_SPEC.md)** —
|
|
212
|
+
- **[`AGENT_SESSION_LINT_SPEC.md`](./AGENT_SESSION_LINT_SPEC.md)** — 8 lint rules, the agent session data landscape across 8 agents, sibling detection strategy, and implementation guidance.
|
|
212
213
|
- **[`agent-session-lint-rules.json`](./agent-session-lint-rules.json)** — Machine-readable rule catalog.
|
|
213
214
|
|
|
214
215
|
## Example Output
|
|
@@ -268,7 +269,7 @@ Commands:
|
|
|
268
269
|
init Set up a git pre-commit hook
|
|
269
270
|
```
|
|
270
271
|
|
|
271
|
-
**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`, `session-missing-secret`, `session-diverged-file`, `session-missing-workflow`, `session-stale-memory`, `session-duplicate-memory`, `session-loop-detection`
|
|
272
|
+
**Available checks:** `paths`, `commands`, `staleness`, `tokens`, `tier-tokens`, `redundancy`, `contradictions`, `frontmatter`, `ci-coverage`, `ci-secrets`, `mcp-schema`, `mcp-security`, `mcp-commands`, `mcp-deprecated`, `mcp-env`, `mcp-urls`, `mcp-consistency`, `mcp-redundancy`, `session-missing-secret`, `session-diverged-file`, `session-missing-workflow`, `session-stale-memory`, `session-duplicate-memory`, `session-loop-detection`, `session-memory-index-overflow`
|
|
272
273
|
|
|
273
274
|
Passing any `mcp-*` check name implies `--mcp`. Passing any `session-*` check name implies `--session`.
|
|
274
275
|
|
|
@@ -287,7 +288,15 @@ Re-lints automatically when any context file, MCP config, or `package.json` chan
|
|
|
287
288
|
run: npx @yawlabs/ctxlint --strict
|
|
288
289
|
```
|
|
289
290
|
|
|
290
|
-
|
|
291
|
+
### Exit Codes
|
|
292
|
+
|
|
293
|
+
| Code | Meaning |
|
|
294
|
+
|---|---|
|
|
295
|
+
| `0` | Success — no issues, or issues below the strict threshold |
|
|
296
|
+
| `1` | Strict mode caught at least one error or warning (`--strict` is set) |
|
|
297
|
+
| `2` | Config error, invalid CLI option, or internal failure |
|
|
298
|
+
|
|
299
|
+
In non-strict mode ctxlint always exits `0` — it's a reporting tool by default. Pass `--strict` to enforce in CI.
|
|
291
300
|
|
|
292
301
|
### GitHub Action
|
|
293
302
|
|
|
@@ -360,7 +369,9 @@ Create a `.ctxlintrc` or `.ctxlintrc.json` in your project root:
|
|
|
360
369
|
"info": 500,
|
|
361
370
|
"warning": 2000,
|
|
362
371
|
"error": 5000,
|
|
363
|
-
"aggregate": 4000
|
|
372
|
+
"aggregate": 4000,
|
|
373
|
+
"tierBreakdown": 1000,
|
|
374
|
+
"tierAggregate": 4000
|
|
364
375
|
},
|
|
365
376
|
"contextFiles": ["CONVENTIONS.md", "docs/ai-rules.md"]
|
|
366
377
|
}
|
|
@@ -368,6 +379,26 @@ Create a `.ctxlintrc` or `.ctxlintrc.json` in your project root:
|
|
|
368
379
|
|
|
369
380
|
The `contextFiles` array adds custom file patterns to scan alongside the built-in list. Useful for project-specific context files like `CONVENTIONS.md`.
|
|
370
381
|
|
|
382
|
+
### Config Reference
|
|
383
|
+
|
|
384
|
+
| Field | Type | Default | Meaning |
|
|
385
|
+
|---|---|---|---|
|
|
386
|
+
| `checks` | `string[]` | all checks | Checks to run. Check names include `paths`, `commands`, `tokens`, `tier-tokens`, `redundancy`, `contradictions`, `frontmatter`, `staleness`, `ci-coverage`, `ci-secrets`, plus any `mcp-*` / `session-*`. |
|
|
387
|
+
| `ignore` | `string[]` | `[]` | Checks to skip, evaluated after `checks`. |
|
|
388
|
+
| `strict` | `boolean` | `false` | Exit non-zero on any warning or error. |
|
|
389
|
+
| `tokenThresholds` | `object` | see below | Per-file and cross-file token thresholds. |
|
|
390
|
+
| `tokenThresholds.info` | `number` | `1000` | Per-file info threshold for `tokens/info`. |
|
|
391
|
+
| `tokenThresholds.warning` | `number` | `3000` | Per-file warning threshold for `tokens/large`. |
|
|
392
|
+
| `tokenThresholds.error` | `number` | `8000` | Per-file error threshold for `tokens/excessive`. |
|
|
393
|
+
| `tokenThresholds.aggregate` | `number` | `5000` | Cross-file total threshold for `tokens/aggregate`. |
|
|
394
|
+
| `tokenThresholds.tierBreakdown` | `number` | `1000` | Always-loaded file threshold for `tier-tokens/section-breakdown`. |
|
|
395
|
+
| `tokenThresholds.tierAggregate` | `number` | `4000` | Combined always-loaded threshold for `tier-tokens/aggregate`. |
|
|
396
|
+
| `contextFiles` | `string[]` | `[]` | Extra glob patterns to scan alongside the built-in list. |
|
|
397
|
+
| `mcp` | `boolean` | `false` | Enable MCP config checks by default (same as `--mcp`). |
|
|
398
|
+
| `mcpGlobal` | `boolean` | `false` | Also scan user/global MCP configs (same as `--mcp-global`). |
|
|
399
|
+
|
|
400
|
+
Config file resolution order: `.ctxlintrc` → `.ctxlintrc.json` in the project root. Use `--config <path>` to point elsewhere. CLI flags override config fields.
|
|
401
|
+
|
|
371
402
|
CLI flags override config file settings. Use `--config <path>` to load a config from a custom location.
|
|
372
403
|
|
|
373
404
|
## Use as MCP Server
|
|
@@ -21,7 +21,8 @@
|
|
|
21
21
|
"description": "GitHub secret set on sibling repos but not this project.",
|
|
22
22
|
"trigger": "gh secret set command found in agent history for 2+ siblings but not current project.",
|
|
23
23
|
"message": "Secret \"{name}\" is set on {count} sibling repos but not this project",
|
|
24
|
-
"fixable": false
|
|
24
|
+
"fixable": false,
|
|
25
|
+
"stability": "stable"
|
|
25
26
|
},
|
|
26
27
|
{
|
|
27
28
|
"id": "session/diverged-file",
|
|
@@ -40,7 +41,8 @@
|
|
|
40
41
|
".eslintrc.json",
|
|
41
42
|
"tsconfig.json",
|
|
42
43
|
".gitignore"
|
|
43
|
-
]
|
|
44
|
+
],
|
|
45
|
+
"stability": "stable"
|
|
44
46
|
},
|
|
45
47
|
{
|
|
46
48
|
"id": "session/missing-workflow",
|
|
@@ -49,7 +51,8 @@
|
|
|
49
51
|
"description": "GitHub Actions workflow missing from this project.",
|
|
50
52
|
"trigger": "Workflow file exists in 2+ siblings but not current project (which has .github).",
|
|
51
53
|
"message": "Workflow \"{workflow}\" exists in {count} sibling repos but not this project",
|
|
52
|
-
"fixable": false
|
|
54
|
+
"fixable": false,
|
|
55
|
+
"stability": "stable"
|
|
53
56
|
},
|
|
54
57
|
{
|
|
55
58
|
"id": "session/stale-memory",
|
|
@@ -58,7 +61,8 @@
|
|
|
58
61
|
"description": "Memory file references paths that no longer exist.",
|
|
59
62
|
"trigger": "Claude Code memory file contains path references to files that have been deleted or moved.",
|
|
60
63
|
"message": "Memory references \"{path}\" which no longer exists",
|
|
61
|
-
"fixable": false
|
|
64
|
+
"fixable": false,
|
|
65
|
+
"stability": "stable"
|
|
62
66
|
},
|
|
63
67
|
{
|
|
64
68
|
"id": "session/duplicate-memory",
|
|
@@ -67,7 +71,8 @@
|
|
|
67
71
|
"description": "Near-duplicate memory entries across projects.",
|
|
68
72
|
"trigger": "Two memory files from different projects have >60% line overlap.",
|
|
69
73
|
"message": "\"{file1}\" and \"{file2}\" have {overlap}% content overlap",
|
|
70
|
-
"fixable": false
|
|
74
|
+
"fixable": false,
|
|
75
|
+
"stability": "stable"
|
|
71
76
|
},
|
|
72
77
|
{
|
|
73
78
|
"id": "session/consecutive-repeat",
|
|
@@ -76,7 +81,8 @@
|
|
|
76
81
|
"description": "Same command run 3+ times consecutively in agent history.",
|
|
77
82
|
"trigger": "Agent history for the current project shows the same display string 3+ times in a row.",
|
|
78
83
|
"message": "Command run {count} times consecutively: \"{command}\"",
|
|
79
|
-
"fixable": false
|
|
84
|
+
"fixable": false,
|
|
85
|
+
"stability": "experimental"
|
|
80
86
|
},
|
|
81
87
|
{
|
|
82
88
|
"id": "session/cyclic-pattern",
|
|
@@ -85,7 +91,19 @@
|
|
|
85
91
|
"description": "Cyclic command pattern detected in agent history.",
|
|
86
92
|
"trigger": "Agent history shows a repeating sequence of 2-3 commands (e.g. A,B,A,B) repeated 2+ times.",
|
|
87
93
|
"message": "Cyclic pattern repeated {count} times: {cycle}",
|
|
88
|
-
"fixable": false
|
|
94
|
+
"fixable": false,
|
|
95
|
+
"stability": "experimental"
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
"id": "session/memory-index-overflow",
|
|
99
|
+
"category": "session",
|
|
100
|
+
"severity": "warning",
|
|
101
|
+
"description": "MEMORY.md exceeds Claude Code's session-load cap. The first 200 lines / 25KB are loaded every session; anything beyond is effectively invisible to the agent.",
|
|
102
|
+
"trigger": "~/.claude/projects/<encoded-project>/memory/MEMORY.md exceeds 200 lines OR 25,600 bytes.",
|
|
103
|
+
"message": "MEMORY.md has {N} lines — only the first 200 are loaded. {excess} lines are effectively invisible.",
|
|
104
|
+
"fixable": false,
|
|
105
|
+
"source": "code.claude.com/docs/en/memory",
|
|
106
|
+
"stability": "stable"
|
|
89
107
|
}
|
|
90
108
|
],
|
|
91
109
|
"dataSources": [
|