@yawlabs/ctxlint 0.1.0 → 0.2.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.
- package/README.md +40 -0
- package/dist/index.js +151 -16
- package/dist/mcp/server.js +36 -9
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -75,11 +75,15 @@ Options:
|
|
|
75
75
|
--strict Exit code 1 on any warning or error (for CI)
|
|
76
76
|
--checks <list> Comma-separated: paths, commands, staleness, tokens, redundancy
|
|
77
77
|
--ignore <list> Comma-separated checks to skip
|
|
78
|
+
--fix Auto-fix broken paths using git history and fuzzy matching
|
|
78
79
|
--format json Output as JSON (for programmatic use)
|
|
79
80
|
--tokens Show token breakdown per file
|
|
80
81
|
--verbose Show passing checks too
|
|
81
82
|
-V, --version Output the version number
|
|
82
83
|
-h, --help Display help
|
|
84
|
+
|
|
85
|
+
Commands:
|
|
86
|
+
init Set up a git pre-commit hook
|
|
83
87
|
```
|
|
84
88
|
|
|
85
89
|
## Use in CI
|
|
@@ -91,6 +95,42 @@ Options:
|
|
|
91
95
|
|
|
92
96
|
Exits with code 1 if any errors or warnings are found.
|
|
93
97
|
|
|
98
|
+
## Auto-fix
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
npx @yawlabs/ctxlint --fix
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
When a broken path was renamed in git or has a close match in the project, `--fix` rewrites the context file automatically.
|
|
105
|
+
|
|
106
|
+
## Pre-commit Hook
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
npx @yawlabs/ctxlint init
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Sets up a git pre-commit hook that runs `ctxlint --strict` before each commit.
|
|
113
|
+
|
|
114
|
+
## Config File
|
|
115
|
+
|
|
116
|
+
Create a `.ctxlintrc` or `.ctxlintrc.json` in your project root:
|
|
117
|
+
|
|
118
|
+
```json
|
|
119
|
+
{
|
|
120
|
+
"checks": ["paths", "commands", "tokens"],
|
|
121
|
+
"ignore": ["redundancy"],
|
|
122
|
+
"strict": true,
|
|
123
|
+
"tokenThresholds": {
|
|
124
|
+
"info": 500,
|
|
125
|
+
"warning": 2000,
|
|
126
|
+
"error": 5000,
|
|
127
|
+
"aggregate": 4000
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
CLI flags override config file settings.
|
|
133
|
+
|
|
94
134
|
## Use as MCP Server
|
|
95
135
|
|
|
96
136
|
ctxlint ships with an MCP server that exposes three tools (`ctxlint_audit`, `ctxlint_validate_path`, `ctxlint_token_report`):
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
3
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
4
|
+
}) : x)(function(x) {
|
|
5
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
6
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
7
|
+
});
|
|
2
8
|
|
|
3
9
|
// src/index.ts
|
|
4
10
|
import { Command } from "commander";
|
|
@@ -492,13 +498,16 @@ async function checkPaths(file, projectRoot) {
|
|
|
492
498
|
}
|
|
493
499
|
let suggestion;
|
|
494
500
|
let detail;
|
|
501
|
+
let fixTarget;
|
|
495
502
|
const rename = await findRenames(projectRoot, ref.value);
|
|
496
503
|
if (rename) {
|
|
504
|
+
fixTarget = rename.newPath;
|
|
497
505
|
suggestion = `Did you mean ${rename.newPath}?`;
|
|
498
506
|
detail = `Renamed ${rename.daysAgo} days ago in commit ${rename.commitHash}`;
|
|
499
507
|
} else {
|
|
500
508
|
const match = findClosestMatch(normalizedRef, projectFiles);
|
|
501
509
|
if (match) {
|
|
510
|
+
fixTarget = match;
|
|
502
511
|
suggestion = `Did you mean ${match}?`;
|
|
503
512
|
}
|
|
504
513
|
}
|
|
@@ -508,7 +517,8 @@ async function checkPaths(file, projectRoot) {
|
|
|
508
517
|
line: ref.line,
|
|
509
518
|
message: `${ref.value} does not exist`,
|
|
510
519
|
suggestion,
|
|
511
|
-
detail
|
|
520
|
+
detail,
|
|
521
|
+
fix: fixTarget ? { file: file.filePath, line: ref.line, oldText: ref.value, newText: fixTarget } : void 0
|
|
512
522
|
});
|
|
513
523
|
}
|
|
514
524
|
return issues;
|
|
@@ -697,13 +707,23 @@ async function checkStaleness(file, projectRoot) {
|
|
|
697
707
|
}
|
|
698
708
|
|
|
699
709
|
// src/core/checks/tokens.ts
|
|
700
|
-
var
|
|
701
|
-
|
|
702
|
-
|
|
710
|
+
var DEFAULT_THRESHOLDS = {
|
|
711
|
+
info: 1e3,
|
|
712
|
+
warning: 3e3,
|
|
713
|
+
error: 8e3,
|
|
714
|
+
aggregate: 5e3
|
|
715
|
+
};
|
|
716
|
+
var currentThresholds = DEFAULT_THRESHOLDS;
|
|
717
|
+
function setTokenThresholds(overrides) {
|
|
718
|
+
currentThresholds = { ...DEFAULT_THRESHOLDS, ...overrides };
|
|
719
|
+
}
|
|
720
|
+
function resetTokenThresholds() {
|
|
721
|
+
currentThresholds = DEFAULT_THRESHOLDS;
|
|
722
|
+
}
|
|
703
723
|
async function checkTokens(file, _projectRoot) {
|
|
704
724
|
const issues = [];
|
|
705
725
|
const tokens = file.totalTokens;
|
|
706
|
-
if (tokens >=
|
|
726
|
+
if (tokens >= currentThresholds.error) {
|
|
707
727
|
issues.push({
|
|
708
728
|
severity: "error",
|
|
709
729
|
check: "tokens",
|
|
@@ -711,7 +731,7 @@ async function checkTokens(file, _projectRoot) {
|
|
|
711
731
|
message: `${tokens.toLocaleString()} tokens \u2014 consumes significant context window space`,
|
|
712
732
|
suggestion: "Consider splitting into focused sections or removing redundant content."
|
|
713
733
|
});
|
|
714
|
-
} else if (tokens >=
|
|
734
|
+
} else if (tokens >= currentThresholds.warning) {
|
|
715
735
|
issues.push({
|
|
716
736
|
severity: "warning",
|
|
717
737
|
check: "tokens",
|
|
@@ -719,7 +739,7 @@ async function checkTokens(file, _projectRoot) {
|
|
|
719
739
|
message: `${tokens.toLocaleString()} tokens \u2014 large context file`,
|
|
720
740
|
suggestion: "Consider trimming \u2014 research shows diminishing returns past ~300 lines."
|
|
721
741
|
});
|
|
722
|
-
} else if (tokens >=
|
|
742
|
+
} else if (tokens >= currentThresholds.info) {
|
|
723
743
|
issues.push({
|
|
724
744
|
severity: "info",
|
|
725
745
|
check: "tokens",
|
|
@@ -731,7 +751,7 @@ async function checkTokens(file, _projectRoot) {
|
|
|
731
751
|
}
|
|
732
752
|
function checkAggregateTokens(files) {
|
|
733
753
|
const total = files.reduce((sum, f) => sum + f.tokens, 0);
|
|
734
|
-
if (total >
|
|
754
|
+
if (total > currentThresholds.aggregate && files.length > 1) {
|
|
735
755
|
return {
|
|
736
756
|
severity: "warning",
|
|
737
757
|
check: "tokens",
|
|
@@ -1005,25 +1025,100 @@ function formatIssue(issue) {
|
|
|
1005
1025
|
return line;
|
|
1006
1026
|
}
|
|
1007
1027
|
|
|
1008
|
-
// src/
|
|
1028
|
+
// src/core/fixer.ts
|
|
1029
|
+
import * as fs5 from "fs";
|
|
1030
|
+
import chalk2 from "chalk";
|
|
1031
|
+
function applyFixes(result) {
|
|
1032
|
+
const fixesByFile = /* @__PURE__ */ new Map();
|
|
1033
|
+
for (const file of result.files) {
|
|
1034
|
+
for (const issue of file.issues) {
|
|
1035
|
+
if (issue.fix) {
|
|
1036
|
+
const existing = fixesByFile.get(issue.fix.file) || [];
|
|
1037
|
+
existing.push(issue.fix);
|
|
1038
|
+
fixesByFile.set(issue.fix.file, existing);
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
let totalFixes = 0;
|
|
1043
|
+
const filesModified = [];
|
|
1044
|
+
for (const [filePath, fixes] of fixesByFile) {
|
|
1045
|
+
const content = fs5.readFileSync(filePath, "utf-8");
|
|
1046
|
+
const lines = content.split("\n");
|
|
1047
|
+
let modified = false;
|
|
1048
|
+
const sortedFixes = [...fixes].sort((a, b) => b.line - a.line);
|
|
1049
|
+
for (const fix of sortedFixes) {
|
|
1050
|
+
const lineIdx = fix.line - 1;
|
|
1051
|
+
if (lineIdx < 0 || lineIdx >= lines.length) continue;
|
|
1052
|
+
const line = lines[lineIdx];
|
|
1053
|
+
if (line.includes(fix.oldText)) {
|
|
1054
|
+
lines[lineIdx] = line.replace(fix.oldText, fix.newText);
|
|
1055
|
+
modified = true;
|
|
1056
|
+
totalFixes++;
|
|
1057
|
+
console.log(
|
|
1058
|
+
chalk2.green(" Fixed") + ` Line ${fix.line}: ${chalk2.dim(fix.oldText)} ${chalk2.dim("\u2192")} ${fix.newText}`
|
|
1059
|
+
);
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
if (modified) {
|
|
1063
|
+
fs5.writeFileSync(filePath, lines.join("\n"), "utf-8");
|
|
1064
|
+
filesModified.push(filePath);
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
return { totalFixes, filesModified };
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
// src/core/config.ts
|
|
1071
|
+
import * as fs6 from "fs";
|
|
1009
1072
|
import * as path7 from "path";
|
|
1010
|
-
var
|
|
1073
|
+
var CONFIG_FILENAMES = [".ctxlintrc", ".ctxlintrc.json"];
|
|
1074
|
+
function loadConfig(projectRoot) {
|
|
1075
|
+
for (const filename of CONFIG_FILENAMES) {
|
|
1076
|
+
const filePath = path7.join(projectRoot, filename);
|
|
1077
|
+
try {
|
|
1078
|
+
const content = fs6.readFileSync(filePath, "utf-8");
|
|
1079
|
+
return JSON.parse(content);
|
|
1080
|
+
} catch {
|
|
1081
|
+
continue;
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
return null;
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
// src/index.ts
|
|
1088
|
+
import * as path8 from "path";
|
|
1089
|
+
|
|
1090
|
+
// src/version.ts
|
|
1091
|
+
function loadVersion() {
|
|
1092
|
+
if (true) return "0.2.0";
|
|
1093
|
+
const fs7 = __require("fs");
|
|
1094
|
+
const path9 = __require("path");
|
|
1095
|
+
const pkgPath = path9.resolve(__dirname, "../package.json");
|
|
1096
|
+
const pkg = JSON.parse(fs7.readFileSync(pkgPath, "utf-8"));
|
|
1097
|
+
return pkg.version;
|
|
1098
|
+
}
|
|
1099
|
+
var VERSION = loadVersion();
|
|
1100
|
+
|
|
1101
|
+
// src/index.ts
|
|
1011
1102
|
var ALL_CHECKS = ["paths", "commands", "staleness", "tokens", "redundancy"];
|
|
1012
1103
|
var program = new Command();
|
|
1013
1104
|
program.name("ctxlint").description(
|
|
1014
1105
|
"Lint your AI agent context files (CLAUDE.md, AGENTS.md, etc.) against your actual codebase"
|
|
1015
|
-
).version(VERSION).argument("[path]", "Project directory to scan", ".").option("--strict", "Exit code 1 on any warning or error (for CI)", false).option("--checks <checks>", "Comma-separated list of checks to run", "").option("--format <format>", "Output format: text or json", "text").option("--tokens", "Show token breakdown per file", false).option("--verbose", "Show passing checks too", false).option("--ignore <checks>", "Comma-separated list of checks to ignore", "").action(async (projectPath, opts) => {
|
|
1016
|
-
const resolvedPath =
|
|
1106
|
+
).version(VERSION).argument("[path]", "Project directory to scan", ".").option("--strict", "Exit code 1 on any warning or error (for CI)", false).option("--checks <checks>", "Comma-separated list of checks to run", "").option("--format <format>", "Output format: text or json", "text").option("--tokens", "Show token breakdown per file", false).option("--verbose", "Show passing checks too", false).option("--fix", "Auto-fix broken paths using git history and fuzzy matching", false).option("--ignore <checks>", "Comma-separated list of checks to ignore", "").action(async (projectPath, opts) => {
|
|
1107
|
+
const resolvedPath = path8.resolve(projectPath);
|
|
1108
|
+
const config = loadConfig(resolvedPath);
|
|
1017
1109
|
const options = {
|
|
1018
1110
|
projectPath: resolvedPath,
|
|
1019
|
-
checks: opts.checks ? opts.checks.split(",").map((c) => c.trim()) : ALL_CHECKS,
|
|
1020
|
-
strict: opts.strict,
|
|
1111
|
+
checks: opts.checks ? opts.checks.split(",").map((c) => c.trim()) : config?.checks || ALL_CHECKS,
|
|
1112
|
+
strict: opts.strict || config?.strict || false,
|
|
1021
1113
|
format: opts.format,
|
|
1022
1114
|
verbose: opts.verbose,
|
|
1023
|
-
fix:
|
|
1024
|
-
ignore: opts.ignore ? opts.ignore.split(",").map((c) => c.trim()) : [],
|
|
1115
|
+
fix: opts.fix,
|
|
1116
|
+
ignore: opts.ignore ? opts.ignore.split(",").map((c) => c.trim()) : config?.ignore || [],
|
|
1025
1117
|
tokensOnly: opts.tokens
|
|
1026
1118
|
};
|
|
1119
|
+
if (config?.tokenThresholds) {
|
|
1120
|
+
setTokenThresholds(config.tokenThresholds);
|
|
1121
|
+
}
|
|
1027
1122
|
const activeChecks = options.checks.filter((c) => !options.ignore.includes(c));
|
|
1028
1123
|
const spinner = options.format === "text" ? ora("Scanning for context files...").start() : void 0;
|
|
1029
1124
|
try {
|
|
@@ -1124,6 +1219,16 @@ program.name("ctxlint").description(
|
|
|
1124
1219
|
}
|
|
1125
1220
|
};
|
|
1126
1221
|
spinner?.stop();
|
|
1222
|
+
if (options.fix) {
|
|
1223
|
+
const fixSummary = applyFixes(result);
|
|
1224
|
+
if (fixSummary.totalFixes > 0) {
|
|
1225
|
+
console.log(
|
|
1226
|
+
`
|
|
1227
|
+
Fixed ${fixSummary.totalFixes} issue${fixSummary.totalFixes !== 1 ? "s" : ""} in ${fixSummary.filesModified.length} file${fixSummary.filesModified.length !== 1 ? "s" : ""}.
|
|
1228
|
+
`
|
|
1229
|
+
);
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1127
1232
|
if (options.tokensOnly) {
|
|
1128
1233
|
console.log(formatTokenReport(result));
|
|
1129
1234
|
} else if (options.format === "json") {
|
|
@@ -1142,6 +1247,36 @@ program.name("ctxlint").description(
|
|
|
1142
1247
|
freeEncoder();
|
|
1143
1248
|
resetGit();
|
|
1144
1249
|
resetPathsCache();
|
|
1250
|
+
resetTokenThresholds();
|
|
1251
|
+
}
|
|
1252
|
+
});
|
|
1253
|
+
program.command("init").description("Set up a git pre-commit hook that runs ctxlint --strict").action(async () => {
|
|
1254
|
+
const fs7 = await import("fs");
|
|
1255
|
+
const hooksDir = path8.resolve(".git", "hooks");
|
|
1256
|
+
if (!fs7.existsSync(path8.resolve(".git"))) {
|
|
1257
|
+
console.error('Error: not a git repository. Run "git init" first.');
|
|
1258
|
+
process.exit(1);
|
|
1259
|
+
}
|
|
1260
|
+
if (!fs7.existsSync(hooksDir)) {
|
|
1261
|
+
fs7.mkdirSync(hooksDir, { recursive: true });
|
|
1262
|
+
}
|
|
1263
|
+
const hookPath = path8.join(hooksDir, "pre-commit");
|
|
1264
|
+
const hookContent = `#!/bin/sh
|
|
1265
|
+
# ctxlint pre-commit hook
|
|
1266
|
+
npx @yawlabs/ctxlint --strict
|
|
1267
|
+
`;
|
|
1268
|
+
if (fs7.existsSync(hookPath)) {
|
|
1269
|
+
const existing = fs7.readFileSync(hookPath, "utf-8");
|
|
1270
|
+
if (existing.includes("ctxlint")) {
|
|
1271
|
+
console.log("Pre-commit hook already includes ctxlint.");
|
|
1272
|
+
return;
|
|
1273
|
+
}
|
|
1274
|
+
fs7.appendFileSync(hookPath, "\n" + hookContent);
|
|
1275
|
+
console.log("Added ctxlint to existing pre-commit hook.");
|
|
1276
|
+
} else {
|
|
1277
|
+
fs7.writeFileSync(hookPath, hookContent, { mode: 493 });
|
|
1278
|
+
console.log("Created pre-commit hook at .git/hooks/pre-commit");
|
|
1145
1279
|
}
|
|
1280
|
+
console.log("ctxlint will now run automatically before each commit.");
|
|
1146
1281
|
});
|
|
1147
1282
|
program.parse();
|
package/dist/mcp/server.js
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
+
}) : x)(function(x) {
|
|
4
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
+
});
|
|
7
|
+
|
|
1
8
|
// src/mcp/server.ts
|
|
2
9
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
10
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
@@ -488,13 +495,16 @@ async function checkPaths(file, projectRoot) {
|
|
|
488
495
|
}
|
|
489
496
|
let suggestion;
|
|
490
497
|
let detail;
|
|
498
|
+
let fixTarget;
|
|
491
499
|
const rename = await findRenames(projectRoot, ref.value);
|
|
492
500
|
if (rename) {
|
|
501
|
+
fixTarget = rename.newPath;
|
|
493
502
|
suggestion = `Did you mean ${rename.newPath}?`;
|
|
494
503
|
detail = `Renamed ${rename.daysAgo} days ago in commit ${rename.commitHash}`;
|
|
495
504
|
} else {
|
|
496
505
|
const match = findClosestMatch(normalizedRef, projectFiles);
|
|
497
506
|
if (match) {
|
|
507
|
+
fixTarget = match;
|
|
498
508
|
suggestion = `Did you mean ${match}?`;
|
|
499
509
|
}
|
|
500
510
|
}
|
|
@@ -504,7 +514,8 @@ async function checkPaths(file, projectRoot) {
|
|
|
504
514
|
line: ref.line,
|
|
505
515
|
message: `${ref.value} does not exist`,
|
|
506
516
|
suggestion,
|
|
507
|
-
detail
|
|
517
|
+
detail,
|
|
518
|
+
fix: fixTarget ? { file: file.filePath, line: ref.line, oldText: ref.value, newText: fixTarget } : void 0
|
|
508
519
|
});
|
|
509
520
|
}
|
|
510
521
|
return issues;
|
|
@@ -693,13 +704,17 @@ async function checkStaleness(file, projectRoot) {
|
|
|
693
704
|
}
|
|
694
705
|
|
|
695
706
|
// src/core/checks/tokens.ts
|
|
696
|
-
var
|
|
697
|
-
|
|
698
|
-
|
|
707
|
+
var DEFAULT_THRESHOLDS = {
|
|
708
|
+
info: 1e3,
|
|
709
|
+
warning: 3e3,
|
|
710
|
+
error: 8e3,
|
|
711
|
+
aggregate: 5e3
|
|
712
|
+
};
|
|
713
|
+
var currentThresholds = DEFAULT_THRESHOLDS;
|
|
699
714
|
async function checkTokens(file, _projectRoot) {
|
|
700
715
|
const issues = [];
|
|
701
716
|
const tokens = file.totalTokens;
|
|
702
|
-
if (tokens >=
|
|
717
|
+
if (tokens >= currentThresholds.error) {
|
|
703
718
|
issues.push({
|
|
704
719
|
severity: "error",
|
|
705
720
|
check: "tokens",
|
|
@@ -707,7 +722,7 @@ async function checkTokens(file, _projectRoot) {
|
|
|
707
722
|
message: `${tokens.toLocaleString()} tokens \u2014 consumes significant context window space`,
|
|
708
723
|
suggestion: "Consider splitting into focused sections or removing redundant content."
|
|
709
724
|
});
|
|
710
|
-
} else if (tokens >=
|
|
725
|
+
} else if (tokens >= currentThresholds.warning) {
|
|
711
726
|
issues.push({
|
|
712
727
|
severity: "warning",
|
|
713
728
|
check: "tokens",
|
|
@@ -715,7 +730,7 @@ async function checkTokens(file, _projectRoot) {
|
|
|
715
730
|
message: `${tokens.toLocaleString()} tokens \u2014 large context file`,
|
|
716
731
|
suggestion: "Consider trimming \u2014 research shows diminishing returns past ~300 lines."
|
|
717
732
|
});
|
|
718
|
-
} else if (tokens >=
|
|
733
|
+
} else if (tokens >= currentThresholds.info) {
|
|
719
734
|
issues.push({
|
|
720
735
|
severity: "info",
|
|
721
736
|
check: "tokens",
|
|
@@ -727,7 +742,7 @@ async function checkTokens(file, _projectRoot) {
|
|
|
727
742
|
}
|
|
728
743
|
function checkAggregateTokens(files) {
|
|
729
744
|
const total = files.reduce((sum, f) => sum + f.tokens, 0);
|
|
730
|
-
if (total >
|
|
745
|
+
if (total > currentThresholds.aggregate && files.length > 1) {
|
|
731
746
|
return {
|
|
732
747
|
severity: "warning",
|
|
733
748
|
check: "tokens",
|
|
@@ -904,7 +919,19 @@ function escapeRegex(str) {
|
|
|
904
919
|
|
|
905
920
|
// src/mcp/server.ts
|
|
906
921
|
import * as path7 from "path";
|
|
907
|
-
|
|
922
|
+
|
|
923
|
+
// src/version.ts
|
|
924
|
+
function loadVersion() {
|
|
925
|
+
if (true) return "0.2.0";
|
|
926
|
+
const fs5 = __require("fs");
|
|
927
|
+
const path8 = __require("path");
|
|
928
|
+
const pkgPath = path8.resolve(__dirname, "../package.json");
|
|
929
|
+
const pkg = JSON.parse(fs5.readFileSync(pkgPath, "utf-8"));
|
|
930
|
+
return pkg.version;
|
|
931
|
+
}
|
|
932
|
+
var VERSION = loadVersion();
|
|
933
|
+
|
|
934
|
+
// src/mcp/server.ts
|
|
908
935
|
var ALL_CHECKS = ["paths", "commands", "staleness", "tokens", "redundancy"];
|
|
909
936
|
var server = new McpServer({
|
|
910
937
|
name: "ctxlint",
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yawlabs/ctxlint",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Lint your AI agent context files (CLAUDE.md, AGENTS.md, etc.) against your actual codebase",
|
|
5
5
|
"bin": {
|
|
6
|
-
"ctxlint": "
|
|
6
|
+
"ctxlint": "dist/index.js"
|
|
7
7
|
},
|
|
8
8
|
"type": "module",
|
|
9
9
|
"scripts": {
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"license": "MIT",
|
|
35
35
|
"repository": {
|
|
36
36
|
"type": "git",
|
|
37
|
-
"url": "https://github.com/yawlabs/ctxlint"
|
|
37
|
+
"url": "git+https://github.com/yawlabs/ctxlint.git"
|
|
38
38
|
},
|
|
39
39
|
"homepage": "https://github.com/yawlabs/ctxlint",
|
|
40
40
|
"author": "Yaw Labs <contact@yaw.sh>",
|
|
@@ -69,4 +69,4 @@
|
|
|
69
69
|
"esbuild"
|
|
70
70
|
]
|
|
71
71
|
}
|
|
72
|
-
}
|
|
72
|
+
}
|