@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.
@@ -1,248 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- ALL_CHECKS,
4
- ALL_MCP_CHECKS,
5
- VERSION,
6
- applyFixes,
7
- fileExists,
8
- findRenames,
9
- freeEncoder,
10
- isDirectory,
11
- parseContextFile,
12
- resetGit,
13
- runAudit,
14
- scanForContextFiles
15
- } from "./chunk-PLYBGRJD.js";
16
- import "./chunk-ZXMDA7VB.js";
17
-
18
- // src/mcp/server.ts
19
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
20
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
21
- import { z } from "zod";
22
- import * as path from "path";
23
- var checkEnum = z.enum([
24
- "paths",
25
- "commands",
26
- "staleness",
27
- "tokens",
28
- "redundancy",
29
- "contradictions",
30
- "frontmatter",
31
- "mcp-schema",
32
- "mcp-security",
33
- "mcp-commands",
34
- "mcp-deprecated",
35
- "mcp-env",
36
- "mcp-urls",
37
- "mcp-consistency",
38
- "mcp-redundancy"
39
- ]);
40
- var server = new McpServer({
41
- name: "ctxlint",
42
- version: VERSION
43
- });
44
- server.tool(
45
- "ctxlint_audit",
46
- "Audit all AI agent context files (CLAUDE.md, AGENTS.md, etc.) and optionally MCP server configs in the project. Checks for stale references, invalid commands, redundant content, contradictions, frontmatter issues, token waste, and MCP config errors.",
47
- {
48
- projectPath: z.string().optional().describe("Path to the project root. Defaults to current working directory."),
49
- checks: z.array(checkEnum).optional().describe("Which checks to run. Defaults to all.")
50
- },
51
- {
52
- readOnlyHint: true,
53
- destructiveHint: false,
54
- idempotentHint: true,
55
- openWorldHint: false
56
- },
57
- async ({ projectPath, checks }) => {
58
- const root = path.resolve(projectPath || process.cwd());
59
- const activeChecks = checks || ALL_CHECKS;
60
- try {
61
- const result = await runAudit(root, activeChecks);
62
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
63
- } catch (err) {
64
- const msg = err instanceof Error ? err.message : String(err);
65
- return {
66
- content: [{ type: "text", text: JSON.stringify({ error: msg }) }],
67
- isError: true
68
- };
69
- } finally {
70
- freeEncoder();
71
- resetGit();
72
- }
73
- }
74
- );
75
- server.tool(
76
- "ctxlint_validate_path",
77
- "Check if a file path referenced in a context file actually exists in the project. Returns the file status and suggests corrections if the path is invalid.",
78
- {
79
- path: z.string().describe("The file path to validate"),
80
- projectPath: z.string().optional().describe("Project root. Defaults to cwd.")
81
- },
82
- {
83
- readOnlyHint: true,
84
- destructiveHint: false,
85
- idempotentHint: true,
86
- openWorldHint: false
87
- },
88
- async ({ path: filePath, projectPath }) => {
89
- try {
90
- const root = path.resolve(projectPath || process.cwd());
91
- const resolved = path.resolve(root, filePath);
92
- const result = {
93
- path: filePath,
94
- exists: fileExists(resolved) || isDirectory(resolved)
95
- };
96
- if (!result.exists) {
97
- const rename = await findRenames(root, filePath);
98
- if (rename) {
99
- result.renamed = true;
100
- result.newPath = rename.newPath;
101
- result.renameCommit = rename.commitHash;
102
- result.daysAgo = rename.daysAgo;
103
- }
104
- }
105
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
106
- } catch (err) {
107
- const msg = err instanceof Error ? err.message : String(err);
108
- return {
109
- content: [{ type: "text", text: JSON.stringify({ error: msg }) }],
110
- isError: true
111
- };
112
- } finally {
113
- resetGit();
114
- }
115
- }
116
- );
117
- server.tool(
118
- "ctxlint_token_report",
119
- "Get a token count breakdown for all context files in the project. Shows per-file and aggregate token usage, plus estimated waste from redundant content.",
120
- {
121
- projectPath: z.string().optional().describe("Project root. Defaults to cwd.")
122
- },
123
- {
124
- readOnlyHint: true,
125
- destructiveHint: false,
126
- idempotentHint: true,
127
- openWorldHint: false
128
- },
129
- async ({ projectPath }) => {
130
- const root = path.resolve(projectPath || process.cwd());
131
- try {
132
- const discovered = await scanForContextFiles(root);
133
- const parsed = discovered.map((f) => parseContextFile(f));
134
- const files = parsed.map((f) => ({
135
- path: f.relativePath,
136
- tokens: f.totalTokens,
137
- lines: f.totalLines,
138
- isSymlink: f.isSymlink
139
- }));
140
- const totalTokens = files.reduce((sum, f) => sum + f.tokens, 0);
141
- return {
142
- content: [
143
- {
144
- type: "text",
145
- text: JSON.stringify(
146
- { files, totalTokens, note: "Token counts use GPT-4 cl100k_base tokenizer" },
147
- null,
148
- 2
149
- )
150
- }
151
- ]
152
- };
153
- } catch (err) {
154
- const msg = err instanceof Error ? err.message : String(err);
155
- return {
156
- content: [{ type: "text", text: JSON.stringify({ error: msg }) }],
157
- isError: true
158
- };
159
- } finally {
160
- freeEncoder();
161
- }
162
- }
163
- );
164
- server.tool(
165
- "ctxlint_fix",
166
- "Run the linter with --fix mode to auto-correct broken file paths in context files using git history and fuzzy matching. Returns a summary of what was fixed.",
167
- {
168
- projectPath: z.string().optional().describe("Path to the project root. Defaults to current working directory."),
169
- checks: z.array(checkEnum).optional().describe("Which checks to run before fixing. Defaults to all.")
170
- },
171
- {
172
- readOnlyHint: false,
173
- destructiveHint: false,
174
- idempotentHint: true,
175
- openWorldHint: false
176
- },
177
- async ({ projectPath, checks }) => {
178
- const root = path.resolve(projectPath || process.cwd());
179
- const activeChecks = checks || ALL_CHECKS;
180
- try {
181
- const result = await runAudit(root, activeChecks);
182
- const fixSummary = applyFixes(result);
183
- return {
184
- content: [
185
- {
186
- type: "text",
187
- text: JSON.stringify(
188
- {
189
- totalFixes: fixSummary.totalFixes,
190
- filesModified: fixSummary.filesModified,
191
- remainingIssues: result.summary
192
- },
193
- null,
194
- 2
195
- )
196
- }
197
- ]
198
- };
199
- } catch (err) {
200
- const msg = err instanceof Error ? err.message : String(err);
201
- return {
202
- content: [{ type: "text", text: JSON.stringify({ error: msg }) }],
203
- isError: true
204
- };
205
- } finally {
206
- freeEncoder();
207
- resetGit();
208
- }
209
- }
210
- );
211
- server.tool(
212
- "ctxlint_mcp_audit",
213
- "Lint MCP server configuration files in a project. Checks for schema errors, hardcoded secrets, deprecated transports, wrong env var syntax, URL issues, and cross-client inconsistencies.",
214
- {
215
- projectPath: z.string().optional().describe("Path to the project root. Defaults to current working directory."),
216
- checks: z.array(checkEnum).optional().describe("Specific MCP checks to run (default: all mcp-* checks)."),
217
- includeGlobal: z.boolean().optional().describe("Also scan global/user-level MCP configs.")
218
- },
219
- {
220
- readOnlyHint: true,
221
- destructiveHint: false,
222
- idempotentHint: true,
223
- openWorldHint: false
224
- },
225
- async ({ projectPath, checks, includeGlobal }) => {
226
- const root = path.resolve(projectPath || process.cwd());
227
- const activeChecks = checks || ALL_MCP_CHECKS;
228
- try {
229
- const result = await runAudit(root, activeChecks, {
230
- mcp: true,
231
- mcpOnly: true,
232
- mcpGlobal: includeGlobal || false
233
- });
234
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
235
- } catch (err) {
236
- const msg = err instanceof Error ? err.message : String(err);
237
- return {
238
- content: [{ type: "text", text: JSON.stringify({ error: msg }) }],
239
- isError: true
240
- };
241
- } finally {
242
- freeEncoder();
243
- resetGit();
244
- }
245
- }
246
- );
247
- var transport = new StdioServerTransport();
248
- await server.connect(transport);