@yawlabs/ctxlint 0.9.1 → 0.9.2

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.
@@ -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).
@@ -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.5 redundancy — inferable content
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.6 contradictions — cross-file conflicts
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.7 frontmatter — client metadata validation
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.8 ci-coverage — CI workflow documentation
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.9 ci-secrets — CI secrets documentation
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
@@ -268,7 +268,7 @@ Commands:
268
268
  init Set up a git pre-commit hook
269
269
  ```
270
270
 
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`
271
+ **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
272
 
273
273
  Passing any `mcp-*` check name implies `--mcp`. Passing any `session-*` check name implies `--session`.
274
274
 
@@ -360,7 +360,9 @@ Create a `.ctxlintrc` or `.ctxlintrc.json` in your project root:
360
360
  "info": 500,
361
361
  "warning": 2000,
362
362
  "error": 5000,
363
- "aggregate": 4000
363
+ "aggregate": 4000,
364
+ "tierBreakdown": 1000,
365
+ "tierAggregate": 4000
364
366
  },
365
367
  "contextFiles": ["CONVENTIONS.md", "docs/ai-rules.md"]
366
368
  }
@@ -86,6 +86,16 @@
86
86
  "trigger": "Agent history shows a repeating sequence of 2-3 commands (e.g. A,B,A,B) repeated 2+ times.",
87
87
  "message": "Cyclic pattern repeated {count} times: {cycle}",
88
88
  "fixable": false
89
+ },
90
+ {
91
+ "id": "session/memory-index-overflow",
92
+ "category": "session",
93
+ "severity": "warning",
94
+ "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.",
95
+ "trigger": "~/.claude/projects/<encoded-project>/memory/MEMORY.md exceeds 200 lines OR 25,600 bytes.",
96
+ "message": "MEMORY.md has {N} lines — only the first 200 are loaded. {excess} lines are effectively invisible.",
97
+ "fixable": false,
98
+ "source": "code.claude.com/docs/en/memory"
89
99
  }
90
100
  ],
91
101
  "dataSources": [
@@ -29,6 +29,12 @@
29
29
  "description": "Monitors context file size to help teams manage context window consumption.",
30
30
  "scope": "per-file and cross-file"
31
31
  },
32
+ {
33
+ "id": "tier-tokens",
34
+ "name": "Tier-Aware Token Accounting",
35
+ "description": "Reports per-section token cost for always-loaded context files and flags demotion candidates for on-demand tiers (skills, subagents, memory).",
36
+ "scope": "per-file"
37
+ },
32
38
  {
33
39
  "id": "redundancy",
34
40
  "name": "Inferable Content",
@@ -196,6 +202,40 @@
196
202
  "configurable": true,
197
203
  "defaultThreshold": 5000
198
204
  },
205
+ {
206
+ "id": "tier-tokens/section-breakdown",
207
+ "category": "tier-tokens",
208
+ "severity": "info",
209
+ "description": "Reports the heaviest top-level sections in an always-loaded context file and suggests demoting the largest to an on-demand tier (skill, subagent, or memory).",
210
+ "trigger": "An always-loaded context file (CLAUDE.md, AGENTS.md, .cursorrules, etc. — excluding path-scoped rules files) exceeds the tierBreakdown threshold (default 1000 tokens) and contains H1 or H2 sections.",
211
+ "message": "{N} tokens loaded every session — heaviest top-level section(s): ...",
212
+ "fixable": false,
213
+ "configurable": true,
214
+ "configKey": "tokenThresholds.tierBreakdown",
215
+ "defaultThreshold": 1000
216
+ },
217
+ {
218
+ "id": "tier-tokens/aggregate",
219
+ "category": "tier-tokens",
220
+ "severity": "warning",
221
+ "description": "Combined token cost of always-loaded context files exceeds the recommended budget.",
222
+ "trigger": "Two or more always-loaded files total more than the tierAggregate threshold (default 4000 tokens).",
223
+ "message": "{count} always-loaded files total {N} tokens — loaded every session",
224
+ "fixable": false,
225
+ "configurable": true,
226
+ "configKey": "tokenThresholds.tierAggregate",
227
+ "defaultThreshold": 4000
228
+ },
229
+ {
230
+ "id": "tier-tokens/hard-enforcement-missing",
231
+ "category": "tier-tokens",
232
+ "severity": "info",
233
+ "description": "An always-loaded file uses inviolable framing (NEVER / ALWAYS / DO NOT / MUST NOT) paired with a concrete command, but no corresponding PreToolUse hook or permissions.deny entry exists in settings.json. Rules in always-loaded files are advisory — without a hook the agent may still run the command.",
234
+ "trigger": "Line in an always-loaded file matches NEVER|ALWAYS|DO NOT|MUST NOT with a backticked command, and .claude/settings.json / ~/.claude/settings.json does not contain a matching PreToolUse hook or permissions.deny entry.",
235
+ "message": "Inviolable framing without a hook to back it up",
236
+ "fixable": false,
237
+ "source": "code.claude.com/docs/en/hooks-guide"
238
+ },
199
239
  {
200
240
  "id": "redundancy/tech-mention",
201
241
  "category": "redundancy",