@yawlabs/ctxlint 0.6.0 → 0.8.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]
@@ -0,0 +1,366 @@
1
+ # Agent Session Data Linting Specification
2
+
3
+ **Version:** 1.0.0-draft
4
+ **Date:** 2026-04-08
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 persist session data -- command history, memory files, learned preferences -- across projects. When a developer runs the same agent across multiple repositories, these files accumulate state that shapes future behavior. Secrets get set in some repos but not others. Config files drift apart. Memory entries go stale or duplicate across projects.
13
+
14
+ This specification defines a standard set of lint rules for validating agent session data for cross-project consistency. It does NOT define agent session formats (those are owned by each agent vendor). Instead, it defines what to CHECK across the session data that already exists.
15
+
16
+ The specification includes:
17
+ - A reference of session data locations across 8 AI coding agents
18
+ - 5 lint rules in the `session` category with defined severities
19
+ - A machine-readable rule catalog ([`agent-session-lint-rules.json`](./agent-session-lint-rules.json))
20
+ - Sibling-repo detection for cross-project checks
21
+
22
+ This is the third pillar alongside context file linting (`CLAUDE.md`, `.cursorrules`) and MCP config linting (`.mcp.json`). Together they cover everything that shapes what an AI agent knows and can do:
23
+
24
+ | Pillar | What it checks | Specification |
25
+ |---|---|---|
26
+ | Context files | Instructions the agent reads | [CONTEXT_LINT_SPEC.md](./CONTEXT_LINT_SPEC.md) |
27
+ | MCP configs | Tools the agent can use | [MCP_CONFIG_LINT_SPEC.md](./MCP_CONFIG_LINT_SPEC.md) |
28
+ | Session data | History and memory the agent carries | This document |
29
+
30
+ **Reference implementation:** [ctxlint](https://github.com/YawLabs/ctxlint) (v0.7.0+)
31
+
32
+ ---
33
+
34
+ ## Table of contents
35
+
36
+ - [1. Agent Session Data Landscape Reference](#1-agent-session-data-landscape-reference)
37
+ - [1.1 What is agent session data?](#11-what-is-agent-session-data)
38
+ - [1.2 Data sources by agent](#12-data-sources-by-agent)
39
+ - [1.3 Scan targets](#13-scan-targets)
40
+ - [1.4 Sibling detection](#14-sibling-detection)
41
+ - [2. Lint Rules](#2-lint-rules)
42
+ - [2.1 session/missing-secret](#21-sessionmissing-secret)
43
+ - [2.2 session/diverged-file](#22-sessiondiverged-file)
44
+ - [2.3 session/missing-workflow](#23-sessionmissing-workflow)
45
+ - [2.4 session/stale-memory](#24-sessionstale-memory)
46
+ - [2.5 session/duplicate-memory](#25-sessionduplicate-memory)
47
+ - [3. Rule Catalog (machine-readable)](#3-rule-catalog-machine-readable)
48
+ - [4. Implementing This Specification](#4-implementing-this-specification)
49
+ - [5. Contributing](#5-contributing)
50
+
51
+ ---
52
+
53
+ ## 1. Agent Session Data Landscape Reference
54
+
55
+ ### 1.1 What is agent session data?
56
+
57
+ Agent session data is persistent state that carries across conversations. Unlike context files (which are authored by developers) and MCP configs (which are authored once and committed), session data is generated by the agent itself during use. It includes:
58
+
59
+ - **History files** -- a log of commands the agent executed, prompts it received, or actions it took. Typically appended per-conversation.
60
+ - **Memory files** -- notes the agent writes to itself about the project: conventions it learned, decisions it recorded, file paths it considers important.
61
+ - **Session transcripts** -- full conversation logs including all messages, tool calls, and responses. These can be very large (hundreds of megabytes for active projects).
62
+ - **Learned preferences** -- implicit or explicit settings derived from user behavior: preferred tools, naming conventions, workflow patterns.
63
+
64
+ This data is usually stored in the user's home directory, not in the project repository. It is rarely version-controlled and is often in undocumented or unstable formats.
65
+
66
+ ### 1.2 Data sources by agent
67
+
68
+ | Agent | Provider | History Location | Format | Memory / Preferences |
69
+ |---|---|---|---|---|
70
+ | Claude Code | Anthropic | `~/.claude/history.jsonl` | JSONL | `~/.claude/projects/*/memory/*.md` (Markdown with YAML frontmatter) |
71
+ | Codex CLI | OpenAI | `~/.codex/history.jsonl` | JSONL | `~/.codex/sessions/YYYY/MM/DD/rollout-*.jsonl` |
72
+ | Aider | Paul Gauthier | `.aider.chat.history.md` (per-project) | Markdown | `.aider.input.history` (per-project, readline format) |
73
+ | Vibe CLI | Mistral | `~/.vibe/sessions/*/messages.jsonl` | JSONL | `~/.vibe/sessions/*/meta.json` |
74
+ | Amazon Q | AWS | `~/.aws/amazonq/history/chat-history-*.json` | JSON | Keyed by workspace path hash |
75
+ | Goose | Block | `~/.local/share/goose/sessions/sessions.db` (Linux/macOS), `%APPDATA%\Block\goose\data\sessions\` (Windows) | SQLite | Schema v9, versioned migrations |
76
+ | Continue.dev | Continue | `~/.continue/sessions/*.json` | JSON | `~/.continue/dev_data/*.jsonl` |
77
+ | Windsurf | Codeium | `~/.windsurf/transcripts/*.jsonl` | JSONL | `~/.codeium/windsurf/cascade/` |
78
+
79
+ **Platform notes:**
80
+ - On Windows, `~` refers to `%USERPROFILE%` (typically `C:\Users\<name>`).
81
+ - Goose uses platform-specific paths: XDG on Linux, `~/Library/Application Support/` on macOS, `%APPDATA%` on Windows.
82
+ - Aider stores history in the project directory itself, not in the user's home directory. These files are typically gitignored.
83
+
84
+ **Format stability:**
85
+ - Claude Code's JSONL history and Markdown memory files are the most stable and well-documented formats.
86
+ - Codex CLI's JSONL format is documented via OpenAI's CLI docs.
87
+ - Goose uses SQLite with versioned schema migrations (v9 as of April 2026), making the format stable but requiring a SQLite dependency to read.
88
+ - Amazon Q, Vibe CLI, Windsurf, and Continue.dev formats are largely undocumented and may change without notice.
89
+
90
+ ### 1.3 Scan targets
91
+
92
+ Session data varies enormously in size and format. This specification targets only lightweight, high-signal data sources for scanning.
93
+
94
+ **What we scan:**
95
+
96
+ - **History files** -- command logs in JSONL or Markdown format. Typically under 2 MB per project. Parsed line-by-line for specific patterns (e.g., `gh secret set` commands).
97
+ - **Memory files** -- small Markdown files, typically under 1 MB total per project. Parsed for path references and content overlap.
98
+ - **Sibling repo filesystem** -- file existence checks and file content reads for cross-project comparisons (diverged configs, missing workflows). No deep scanning.
99
+
100
+ **What we explicitly DO NOT scan:**
101
+
102
+ - **Full session transcripts** -- too large. Active projects can accumulate hundreds of megabytes of transcript data. Scanning these would be slow and yield low-signal results.
103
+ - **SQLite databases** -- Goose's `sessions.db` requires a SQLite dependency. Out of scope for v1. Future versions may add opt-in SQLite support.
104
+ - **File history or shell snapshots** -- some agents capture filesystem state or shell output. These are agent-internal data and not useful for cross-project linting.
105
+
106
+ ### 1.4 Sibling detection
107
+
108
+ Several rules in this specification compare the current project against "sibling" repositories -- other projects by the same developer or team. Sibling detection determines which repos to compare against.
109
+
110
+ **Default strategy:** scan the parent directory of the current project. Filter to directories that contain at least one project indicator file:
111
+
112
+ - `.git`
113
+ - `package.json`
114
+ - `Cargo.toml`
115
+ - `go.mod`
116
+ - `pyproject.toml`
117
+
118
+ Skip hidden directories (starting with `.`) and `node_modules`.
119
+
120
+ **Overflow strategy:** if the parent directory contains more than 50 candidate projects, narrow the set by parsing `git remote get-url origin` in each candidate and filtering to repositories in the same GitHub organization or user namespace. This prevents false positives in shared development directories (e.g., `~/src/` containing forks from dozens of orgs).
121
+
122
+ **Configuration:** implementors should allow users to override sibling detection with an explicit list of paths if the default heuristics don't match their workspace layout.
123
+
124
+ ---
125
+
126
+ ## 2. Lint Rules
127
+
128
+ 5 rules in 1 category (`session`). All rules in this category perform cross-project checks using sibling detection.
129
+
130
+ Severity levels:
131
+ - **error** -- the session data reveals a verifiably missing configuration. Should fail CI.
132
+ - **warning** -- the session data reveals likely drift worth investigating. May fail CI in strict mode.
133
+ - **info** -- the session data reveals a potential improvement. Never fails CI.
134
+
135
+ ### 2.1 session/missing-secret
136
+
137
+ Detects GitHub secrets that have been set on sibling repositories but not the current project.
138
+
139
+ | Field | Value |
140
+ |---|---|
141
+ | **Rule ID** | `session/missing-secret` |
142
+ | **Severity** | error |
143
+ | **Trigger** | `gh secret set <NAME>` found in agent history for 2+ sibling repos but not the current project |
144
+ | **Message** | `GitHub secret "<name>" is set on <N> sibling repos (<names>) but not on this project` |
145
+
146
+ **Detection algorithm:**
147
+
148
+ 1. Parse agent history files (JSONL) line-by-line.
149
+ 2. Match entries against the pattern: `gh secret set <SECRET_NAME>` (with optional flags like `--repo`, `--body`, `--env`).
150
+ 3. Group matches by secret name and the project path the history entry is associated with.
151
+ 4. For each secret name, check if it was set on 2+ sibling repos but not on the current project.
152
+ 5. Flag each missing secret as an error.
153
+
154
+ **Example scenario:** A developer uses Claude Code across three repos. They ran `gh secret set NPM_TOKEN` in `lib-a` and `lib-b` but forgot `lib-c`. When linting `lib-c`, this rule flags the missing `NPM_TOKEN`.
155
+
156
+ ### 2.2 session/diverged-file
157
+
158
+ Detects canonical configuration files that have drifted between the current project and its siblings.
159
+
160
+ | Field | Value |
161
+ |---|---|
162
+ | **Rule ID** | `session/diverged-file` |
163
+ | **Severity** | warning |
164
+ | **Trigger** | A canonical file exists in both the current project and 1+ siblings, and line-level overlap is 20-90% |
165
+ | **Message** | `<file> has diverged from sibling repos: <sibling> (<N>% overlap)` |
166
+
167
+ **Canonical files:**
168
+
169
+ | File path | Purpose |
170
+ |---|---|
171
+ | `release.sh` | Release automation script |
172
+ | `.github/workflows/ci.yml` | CI pipeline |
173
+ | `.github/workflows/release.yml` | Release pipeline |
174
+ | `biome.json` | Biome linter config |
175
+ | `.prettierrc` | Prettier config |
176
+ | `.eslintrc.json` | ESLint config |
177
+ | `tsconfig.json` | TypeScript config |
178
+ | `.gitignore` | Git ignore rules |
179
+
180
+ **Detection algorithm:**
181
+
182
+ 1. For each canonical file that exists in the current project, check if the same file exists in any sibling repo.
183
+ 2. For each pair, compute line-level overlap: the number of lines present in both files divided by the total number of unique lines across both files, expressed as a percentage.
184
+ 3. Classify the result:
185
+ - **Below 20%** -- intentionally different. No flag. The files serve different purposes despite the shared name.
186
+ - **20-90%** -- drift. Flag as warning. The files were likely once in sync and have diverged.
187
+ - **Above 90%** -- close enough. No flag. Minor differences are expected (e.g., different project names).
188
+
189
+ **Notes:**
190
+ - Blank lines and comment-only lines should be excluded from the overlap calculation.
191
+ - Compare each sibling independently. Report the sibling with the lowest overlap percentage first.
192
+
193
+ ### 2.3 session/missing-workflow
194
+
195
+ Detects GitHub Actions workflow files that exist across sibling repos but are absent from the current project.
196
+
197
+ | Field | Value |
198
+ |---|---|
199
+ | **Rule ID** | `session/missing-workflow` |
200
+ | **Severity** | warning |
201
+ | **Trigger** | A GitHub Actions workflow file exists in 2+ sibling repos but not in the current project (which has a `.github` directory) |
202
+ | **Message** | `GitHub Actions workflow "<file>" exists in <N> sibling repos (<names>) but not in this project` |
203
+
204
+ **Detection algorithm:**
205
+
206
+ 1. Check if the current project has a `.github` directory. If not, skip this rule entirely -- the project may not use GitHub Actions at all.
207
+ 2. Enumerate `.github/workflows/*.yml` and `.github/workflows/*.yaml` in all sibling repos.
208
+ 3. Group workflow filenames across siblings.
209
+ 4. For each filename that exists in 2+ siblings but not in the current project, flag it.
210
+
211
+ **Notes:**
212
+ - Match by filename only, not by content. A `ci.yml` in one repo may be very different from `ci.yml` in another, but the absence of any CI workflow is still worth flagging.
213
+ - Exclude workflow files that are clearly project-specific (e.g., containing the repo name in the filename). Implementors may use heuristics here.
214
+
215
+ ### 2.4 session/stale-memory
216
+
217
+ Detects Claude Code memory entries that reference file paths which no longer exist in the project.
218
+
219
+ | Field | Value |
220
+ |---|---|
221
+ | **Rule ID** | `session/stale-memory` |
222
+ | **Severity** | info |
223
+ | **Trigger** | A memory file references file paths that no longer exist in the project |
224
+ | **Message** | `Memory "<name>" references <N> path(s) that no longer exist: <paths>` |
225
+
226
+ **Scope:** This rule only checks memory files for the current project. Claude Code stores per-project memories in `~/.claude/projects/<encoded-path>/memory/`, where `<encoded-path>` encodes the project's absolute path using `--` as the directory separator.
227
+
228
+ **Detection algorithm:**
229
+
230
+ 1. Determine the current project's encoded path (see [Section 4](#4-implementing-this-specification) for encoding details).
231
+ 2. Read all `.md` files in the corresponding memory directory.
232
+ 3. Extract path references from each memory file using the same path extraction logic as context file linting (forward-slash-separated segments, relative paths, etc.).
233
+ 4. Check each extracted path against the project filesystem.
234
+ 5. Flag memory files that reference 1+ paths that no longer exist.
235
+
236
+ **Notes:**
237
+ - This rule is `info` severity because stale memories are low-risk -- they waste a small amount of context but don't cause incorrect behavior.
238
+ - Implementors may suggest `claude memory remove` or manual deletion as a fix.
239
+
240
+ ### 2.5 session/duplicate-memory
241
+
242
+ Detects memory entries from different projects that have significant content overlap.
243
+
244
+ | Field | Value |
245
+ |---|---|
246
+ | **Rule ID** | `session/duplicate-memory` |
247
+ | **Severity** | info |
248
+ | **Trigger** | Two memory entries from different projects have >60% line overlap |
249
+ | **Message** | `Memory "<nameA>" (<projA>) and "<nameB>" (<projB>) have <N>% overlap` |
250
+
251
+ **Detection algorithm:**
252
+
253
+ 1. Enumerate all memory files across all projects in `~/.claude/projects/*/memory/*.md`.
254
+ 2. Exclude `MEMORY.md` index files (these are auto-generated summaries, not authored memories).
255
+ 3. Exclude very short entries (fewer than 50 characters after stripping whitespace) -- too short for meaningful overlap comparison.
256
+ 4. Perform pairwise comparison of all remaining memory entries. Skip pairs from the same project.
257
+ 5. Compute line-level overlap percentage (same algorithm as `session/diverged-file`).
258
+ 6. Flag pairs with >60% overlap.
259
+
260
+ **Notes:**
261
+ - This rule helps identify boilerplate that has been memorized per-project instead of being placed in a shared context file or user-level config.
262
+ - A common pattern is the same coding conventions memorized independently in 5+ projects. Consolidating to a user-level `CLAUDE.md` or `.claude/settings.json` would be more efficient.
263
+
264
+ ---
265
+
266
+ ## 3. Rule Catalog (machine-readable)
267
+
268
+ A machine-readable JSON catalog of all rules is available at [`agent-session-lint-rules.json`](./agent-session-lint-rules.json).
269
+
270
+ The catalog schema follows the same structure as the sibling specs' catalogs (`context-lint-rules.json`, `mcp-config-lint-rules.json`). Each rule entry includes:
271
+
272
+ | Field | Type | Description |
273
+ |---|---|---|
274
+ | `id` | string | Rule ID in `category/rule-name` format |
275
+ | `severity` | `"error"` \| `"warning"` \| `"info"` | Default severity level |
276
+ | `description` | string | One-line description of what the rule checks |
277
+ | `messageTemplate` | string | Message template with `<placeholder>` variables |
278
+ | `category` | string | Rule category (`session`) |
279
+ | `crossProject` | boolean | Whether the rule compares across sibling repos |
280
+
281
+ See the JSON file for the full catalog.
282
+
283
+ ---
284
+
285
+ ## 4. Implementing This Specification
286
+
287
+ ### Opt-in activation
288
+
289
+ Session checks are opt-in. Implementors should require an explicit flag (e.g., `--session`) to enable these rules. Rationale:
290
+
291
+ - Session data lives outside the project directory and may contain sensitive information.
292
+ - Cross-project checks require filesystem access to sibling repos, which is slower than single-project linting.
293
+ - Users should consciously opt into scanning their agent history and memory files.
294
+
295
+ ### Path handling
296
+
297
+ Sibling detection and history file scanning must handle both Windows and Unix paths correctly.
298
+
299
+ - Use `path.resolve()` or equivalent to normalize paths before comparison.
300
+ - On Windows, handle both `\` and `/` as separators.
301
+ - Environment variable expansion (`%USERPROFILE%`, `$HOME`, `~`) should work on both platforms.
302
+
303
+ ### History file parsing
304
+
305
+ Agent history files (JSONL) should be parsed line-by-line using streaming reads. Do not load entire files into memory -- active developers may have history files in the tens of megabytes.
306
+
307
+ For each line:
308
+ 1. Parse as JSON.
309
+ 2. Extract the command/action string.
310
+ 3. Match against rule-specific patterns (e.g., `gh secret set` regex).
311
+ 4. Track the associated project path for cross-project grouping.
312
+
313
+ ### Claude Code project directory encoding
314
+
315
+ Claude Code encodes project paths in its directory structure using `--` as the path separator. The encoding replaces each directory separator with `--` and strips the leading separator.
316
+
317
+ **Examples:**
318
+
319
+ | Actual path | Encoded directory name |
320
+ |---|---|
321
+ | `C:/Users/jeff/yaw/ctxlint` | `C--Users-jeff-yaw-ctxlint` |
322
+ | `/home/dev/projects/my-app` | `-home-dev-projects-my-app` |
323
+ | `/Users/dev/work/api-server` | `-Users-dev-work-api-server` |
324
+
325
+ Note: each `/` or `\` in the path becomes a single `-` in the encoded name, except when the path component itself contains a hyphen (which is preserved). The `--` separator replaces `:/` on Windows drive letters.
326
+
327
+ To decode: implementors should reverse the encoding by reading the actual directory names from `~/.claude/projects/` and matching against the current project's absolute path.
328
+
329
+ ### Scope of v1
330
+
331
+ The v1 specification focuses on:
332
+
333
+ - **Claude Code and Codex CLI** for history file scanning -- these have well-documented, stable JSONL formats.
334
+ - **Filesystem-based checks** (`session/diverged-file`, `session/missing-workflow`) that work for all agents because they scan the project filesystem, not agent-specific data.
335
+ - **Claude Code** for memory file checks (`session/stale-memory`, `session/duplicate-memory`) -- the only agent with a well-documented, file-based memory system.
336
+
337
+ Future versions may add support for:
338
+ - Goose SQLite session parsing (requires bundling or requiring SQLite).
339
+ - Aider per-project history parsing.
340
+ - Additional memory/preference formats as agents stabilize their storage.
341
+
342
+ ---
343
+
344
+ ## 5. Contributing
345
+
346
+ This specification is maintained at [github.com/YawLabs/ctxlint](https://github.com/YawLabs/ctxlint).
347
+
348
+ To propose changes:
349
+ - **New rules:** Open an issue describing the rule, its severity, trigger condition, and which agents it applies to.
350
+ - **Agent additions:** As new AI coding agents emerge or existing agents change their session data formats, submit a PR updating the data sources table in Section 1.2.
351
+ - **Corrections:** If any agent behavior or file location documented here is inaccurate, open an issue with evidence (agent docs, source code, or reproduction steps).
352
+
353
+ ### Versioning
354
+
355
+ This specification follows semver:
356
+ - **Patch** (1.0.x): Typo fixes, clarifications, no rule changes
357
+ - **Minor** (1.x.0): New rules added, new agents documented, new canonical files for diverged-file checks
358
+ - **Major** (x.0.0): Rules removed or semantics changed in breaking ways
359
+
360
+ ### Related specifications and tools
361
+
362
+ - [AI Context File Linting Specification](./CONTEXT_LINT_SPEC.md) -- context file lint rules (the first pillar)
363
+ - [MCP Server Configuration Linting Specification](./MCP_CONFIG_LINT_SPEC.md) -- MCP config lint rules (the second pillar)
364
+ - [ctxlint](https://github.com/YawLabs/ctxlint) -- reference implementation of all three specifications
365
+ - [mcp-compliance](https://github.com/YawLabs/mcp-compliance) -- tests MCP server behavior against the protocol spec
366
+ - [mcp.hosting](https://mcp.hosting) -- managed MCP server hosting
package/README.md CHANGED
@@ -4,6 +4,7 @@
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
5
5
  [![GitHub stars](https://img.shields.io/github/stars/YawLabs/ctxlint)](https://github.com/YawLabs/ctxlint/stargazers)
6
6
  [![CI](https://github.com/YawLabs/ctxlint/actions/workflows/ci.yml/badge.svg)](https://github.com/YawLabs/ctxlint/actions/workflows/ci.yml)
7
+ [![Release](https://github.com/YawLabs/ctxlint/actions/workflows/release.yml/badge.svg)](https://github.com/YawLabs/ctxlint/actions/workflows/release.yml)
7
8
 
8
9
  **Lint your AI agent context files and MCP server configs against your actual codebase.** Context linting + MCP config linting. 21+ context formats, 8 MCP clients, auto-fix. Works as a CLI, CI step, pre-commit hook, or MCP server.
9
10
 
@@ -11,15 +12,23 @@ Your `CLAUDE.md` is lying to your agent. Your `.mcp.json` has a hardcoded API ke
11
12
 
12
13
  ## Why ctxlint?
13
14
 
14
- 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.
15
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
16
24
  - **Catches real problems** — broken paths, wrong commands, stale references, contradictions across files
17
25
  - **Smart suggestions** — detects git renames and fuzzy-matches to suggest the right path
18
26
  - **Auto-fix** — `--fix` rewrites broken paths automatically using git history
19
27
  - **Token-aware** — shows how much context window your files consume and flags redundant content
20
28
  - **Every AI tool** — supports Claude Code, Cursor, Copilot, Windsurf, Gemini, Cline, Aider, and 14 more
21
29
  - **Multiple outputs** — text, JSON, and SARIF (GitHub Code Scanning)
22
- - **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
23
32
 
24
33
  ## Install
25
34
 
@@ -160,7 +169,7 @@ The full specification for MCP config linting rules, the cross-client config lan
160
169
  ## Example Output
161
170
 
162
171
  ```
163
- ctxlint v0.6.0
172
+ ctxlint v0.7.0
164
173
 
165
174
  Scanning /Users/you/my-app...
166
175
 
@@ -204,6 +213,7 @@ Options:
204
213
  --mcp-only Run only MCP config checks, skip context file checks
205
214
  --mcp-global Also scan user/global MCP config files (implies --mcp)
206
215
  --mcp-server Start the MCP server (for IDE/agent integration)
216
+ --watch Re-lint on context file changes
207
217
  -V, --version Output the version number
208
218
  -h, --help Display help
209
219
 
@@ -215,6 +225,14 @@ Commands:
215
225
 
216
226
  Passing any `mcp-*` check name implies `--mcp`.
217
227
 
228
+ ## Watch Mode
229
+
230
+ ```bash
231
+ npx @yawlabs/ctxlint --watch
232
+ ```
233
+
234
+ Re-lints automatically when any context file, MCP config, or `package.json` changes. Useful during development when you're editing context files alongside code.
235
+
218
236
  ## Use in CI
219
237
 
220
238
  ```yaml
@@ -224,6 +242,22 @@ Passing any `mcp-*` check name implies `--mcp`.
224
242
 
225
243
  Exits with code 1 if any errors or warnings are found.
226
244
 
245
+ ### GitHub Action
246
+
247
+ ```yaml
248
+ - name: Lint context files
249
+ uses: yawlabs/ctxlint-action@v1
250
+ ```
251
+
252
+ Or with options:
253
+
254
+ ```yaml
255
+ - name: Lint context files
256
+ uses: yawlabs/ctxlint-action@v1
257
+ with:
258
+ args: '--strict --mcp'
259
+ ```
260
+
227
261
  ### SARIF Output (GitHub Code Scanning)
228
262
 
229
263
  ```yaml
@@ -261,7 +295,7 @@ Add to your `.pre-commit-config.yaml`:
261
295
  ```yaml
262
296
  repos:
263
297
  - repo: https://github.com/yawlabs/ctxlint
264
- rev: v0.6.0
298
+ rev: v0.7.0
265
299
  hooks:
266
300
  - id: ctxlint
267
301
  ```
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 }}
@@ -0,0 +1,157 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "title": "AI Agent Session Lint Rules",
4
+ "description": "Machine-readable catalog of lint rules for AI agent session data cross-project consistency",
5
+ "specVersion": "1.0.0-draft",
6
+ "specDate": "2026-04-08",
7
+ "repository": "https://github.com/YawLabs/ctxlint",
8
+ "categories": [
9
+ {
10
+ "id": "session",
11
+ "name": "Session Audit",
12
+ "description": "Cross-project consistency checks using AI agent session data.",
13
+ "scope": "cross-project"
14
+ }
15
+ ],
16
+ "rules": [
17
+ {
18
+ "id": "session/missing-secret",
19
+ "category": "session",
20
+ "severity": "error",
21
+ "description": "GitHub secret set on sibling repos but not this project.",
22
+ "trigger": "gh secret set command found in agent history for 2+ siblings but not current project.",
23
+ "message": "Secret \"{name}\" is set on {count} sibling repos but not this project",
24
+ "fixable": false
25
+ },
26
+ {
27
+ "id": "session/diverged-file",
28
+ "category": "session",
29
+ "severity": "warning",
30
+ "description": "Canonical file has diverged from sibling repos.",
31
+ "trigger": "File exists in current project and siblings with 20-90% line overlap.",
32
+ "message": "\"{file}\" has {overlap}% overlap with {sibling} — may have diverged",
33
+ "fixable": false,
34
+ "canonicalFiles": [
35
+ "release.sh",
36
+ ".github/workflows/ci.yml",
37
+ ".github/workflows/release.yml",
38
+ "biome.json",
39
+ ".prettierrc",
40
+ ".eslintrc.json",
41
+ "tsconfig.json",
42
+ ".gitignore"
43
+ ]
44
+ },
45
+ {
46
+ "id": "session/missing-workflow",
47
+ "category": "session",
48
+ "severity": "warning",
49
+ "description": "GitHub Actions workflow missing from this project.",
50
+ "trigger": "Workflow file exists in 2+ siblings but not current project (which has .github).",
51
+ "message": "Workflow \"{workflow}\" exists in {count} sibling repos but not this project",
52
+ "fixable": false
53
+ },
54
+ {
55
+ "id": "session/stale-memory",
56
+ "category": "session",
57
+ "severity": "info",
58
+ "description": "Memory file references paths that no longer exist.",
59
+ "trigger": "Claude Code memory file contains path references to files that have been deleted or moved.",
60
+ "message": "Memory references \"{path}\" which no longer exists",
61
+ "fixable": false
62
+ },
63
+ {
64
+ "id": "session/duplicate-memory",
65
+ "category": "session",
66
+ "severity": "info",
67
+ "description": "Near-duplicate memory entries across projects.",
68
+ "trigger": "Two memory files from different projects have >60% line overlap.",
69
+ "message": "\"{file1}\" and \"{file2}\" have {overlap}% content overlap",
70
+ "fixable": false
71
+ }
72
+ ],
73
+ "dataSources": [
74
+ {
75
+ "id": "claude-code",
76
+ "name": "Claude Code",
77
+ "provider": "Anthropic",
78
+ "historyLocation": "~/.claude/history.jsonl",
79
+ "historyFormat": "JSONL",
80
+ "memoryLocation": "~/.claude/projects/*/memory/*.md",
81
+ "documented": true
82
+ },
83
+ {
84
+ "id": "codex-cli",
85
+ "name": "Codex CLI",
86
+ "provider": "OpenAI",
87
+ "historyLocation": "~/.codex/history.jsonl",
88
+ "historyFormat": "JSONL",
89
+ "sessionsLocation": "~/.codex/sessions/**/*.jsonl",
90
+ "documented": true
91
+ },
92
+ {
93
+ "id": "aider",
94
+ "name": "Aider",
95
+ "provider": "Paul Gauthier",
96
+ "historyLocation": ".aider.chat.history.md",
97
+ "historyFormat": "Markdown",
98
+ "documented": true,
99
+ "notes": "History file is per-project, stored in the project root."
100
+ },
101
+ {
102
+ "id": "vibe-cli",
103
+ "name": "Vibe CLI",
104
+ "provider": "Mistral",
105
+ "historyLocation": "~/.vibe/sessions/*/messages.jsonl",
106
+ "historyFormat": "JSONL",
107
+ "documented": false,
108
+ "notes": "Partially documented."
109
+ },
110
+ {
111
+ "id": "amazon-q",
112
+ "name": "Amazon Q Developer",
113
+ "provider": "AWS",
114
+ "historyLocation": "~/.aws/amazonq/history/chat-history-*.json",
115
+ "historyFormat": "JSON",
116
+ "documented": false,
117
+ "notes": "Semi-documented; file layout inferred from observation."
118
+ },
119
+ {
120
+ "id": "goose",
121
+ "name": "Goose",
122
+ "provider": "Block",
123
+ "historyLocation": "~/.local/share/goose/sessions/sessions.db",
124
+ "historyFormat": "SQLite",
125
+ "documented": true,
126
+ "notes": "Requires SQLite dependency to read session data."
127
+ },
128
+ {
129
+ "id": "continue",
130
+ "name": "Continue",
131
+ "provider": "Continue.dev",
132
+ "historyLocation": "~/.continue/sessions/*.json",
133
+ "historyFormat": "JSON",
134
+ "documented": false,
135
+ "notes": "Partially documented."
136
+ },
137
+ {
138
+ "id": "windsurf",
139
+ "name": "Windsurf",
140
+ "provider": "Codeium",
141
+ "historyLocation": "~/.windsurf/transcripts/*.jsonl",
142
+ "historyFormat": "JSONL",
143
+ "documented": false,
144
+ "notes": "Partially documented; format marked unstable by vendor."
145
+ }
146
+ ],
147
+ "agents": [
148
+ "claude-code",
149
+ "codex-cli",
150
+ "aider",
151
+ "vibe-cli",
152
+ "amazon-q",
153
+ "goose",
154
+ "continue",
155
+ "windsurf"
156
+ ]
157
+ }