@pqqqqqdev/agent-context-lint 0.1.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.
Files changed (43) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +174 -0
  3. package/dist/cli.d.ts +3 -0
  4. package/dist/cli.d.ts.map +1 -0
  5. package/dist/cli.js +122 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/index.d.ts +3 -0
  8. package/dist/index.d.ts.map +1 -0
  9. package/dist/index.js +92 -0
  10. package/dist/index.js.map +1 -0
  11. package/dist/reporter.d.ts +4 -0
  12. package/dist/reporter.d.ts.map +1 -0
  13. package/dist/reporter.js +67 -0
  14. package/dist/reporter.js.map +1 -0
  15. package/dist/rules/dangerousCommands.d.ts +3 -0
  16. package/dist/rules/dangerousCommands.d.ts.map +1 -0
  17. package/dist/rules/dangerousCommands.js +32 -0
  18. package/dist/rules/dangerousCommands.js.map +1 -0
  19. package/dist/rules/duplicateLines.d.ts +3 -0
  20. package/dist/rules/duplicateLines.d.ts.map +1 -0
  21. package/dist/rules/duplicateLines.js +45 -0
  22. package/dist/rules/duplicateLines.js.map +1 -0
  23. package/dist/rules/missingCommands.d.ts +3 -0
  24. package/dist/rules/missingCommands.d.ts.map +1 -0
  25. package/dist/rules/missingCommands.js +66 -0
  26. package/dist/rules/missingCommands.js.map +1 -0
  27. package/dist/rules/tokenBudget.d.ts +7 -0
  28. package/dist/rules/tokenBudget.d.ts.map +1 -0
  29. package/dist/rules/tokenBudget.js +28 -0
  30. package/dist/rules/tokenBudget.js.map +1 -0
  31. package/dist/rules/vagueInstructions.d.ts +3 -0
  32. package/dist/rules/vagueInstructions.d.ts.map +1 -0
  33. package/dist/rules/vagueInstructions.js +37 -0
  34. package/dist/rules/vagueInstructions.js.map +1 -0
  35. package/dist/scanner.d.ts +5 -0
  36. package/dist/scanner.d.ts.map +1 -0
  37. package/dist/scanner.js +125 -0
  38. package/dist/scanner.js.map +1 -0
  39. package/dist/types.d.ts +26 -0
  40. package/dist/types.d.ts.map +1 -0
  41. package/dist/types.js +3 -0
  42. package/dist/types.js.map +1 -0
  43. package/package.json +55 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 agent-context-lint contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,174 @@
1
+ # agent-context-lint
2
+
3
+ A command-line linter that audits AI coding-agent context and instruction files for repositories.
4
+
5
+ It helps maintainers reduce:
6
+ - Token bloat
7
+ - Duplicated instructions
8
+ - Vague agent instructions ("make it better", "use best practices", etc.)
9
+ - Missing project commands (install, test, lint)
10
+ - Dangerous shell commands suggested to agents (recursive force-removal, privilege escalation, shell-piped downloads, etc.)
11
+
12
+ **No web UI. No React. Just a fast, focused CLI.**
13
+
14
+ ## Supported Files
15
+
16
+ - `AGENTS.md`
17
+ - `CLAUDE.md`
18
+ - `.cursorrules`
19
+ - `.windsurfrules`
20
+ - `.github/copilot-instructions.md`
21
+ - `README.md`
22
+ - `docs/**/*.md`
23
+
24
+ ## Installation
25
+
26
+ ```bash
27
+ # Global (recommended for daily use)
28
+ npm install -g @pqqqqqdev/agent-context-lint
29
+
30
+ # Or use npx (no install)
31
+ npx @pqqqqqdev/agent-context-lint .
32
+
33
+ # Local dev install
34
+ npm install --save-dev @pqqqqqdev/agent-context-lint
35
+ ```
36
+
37
+ ## Usage
38
+
39
+ ```bash
40
+ # Lint current directory
41
+ agent-context-lint .
42
+
43
+ # Lint a specific repo
44
+ agent-context-lint /path/to/repo
45
+
46
+ # JSON output (for CI, scripts, or piping)
47
+ agent-context-lint . --json
48
+
49
+ # Custom token budget (error threshold)
50
+ agent-context-lint . --max-tokens 2000
51
+
52
+ # Help
53
+ agent-context-lint --help
54
+ ```
55
+
56
+ ## CLI Options
57
+
58
+ | Option | Description |
59
+ |-------------------|--------------------------------------------------|
60
+ | `[path]` | Directory to scan (default: `.`) |
61
+ | `--json` | Emit machine-readable JSON instead of text |
62
+ | `--max-tokens N` | Override error threshold (default 3000). Warning threshold adapts to min(1500, N/2) |
63
+ | `--help`, `-h` | Show help |
64
+ | `--version`, `-v` | Show version |
65
+
66
+ ## Exit Codes
67
+
68
+ - `0` — Success (no errors). Warnings are acceptable.
69
+ - `1` — One or more **errors** were found.
70
+
71
+ ## Example Output (Human Readable)
72
+
73
+ ```
74
+ agent-context-lint: scanned 2 file(s) in /Users/dev/my-repo
75
+ Total estimated tokens: 8724
76
+ Errors: 2 Warnings: 5
77
+
78
+ 📄 AGENTS.md (6840 tokens)
79
+ ❌ [token-budget] File exceeds maximum token budget: 6840 tokens (limit: 3000)
80
+ ❌ [dangerous-commands] Dangerous shell command detected: [forceful recursive removal] (line 12)
81
+ > [example: destructive removal command]
82
+ ⚠️ [vague-instructions] Vague instruction detected: "make it better" (line 4)
83
+ > Always make it better when you touch code.
84
+ ⚠️ [duplicate-lines] Duplicated line appears 3 times (lines 19, 27, 41)
85
+ > Always run the tests.
86
+
87
+ 📄 README.md (1884 tokens)
88
+ ⚠️ [token-budget] File is large: 1884 tokens (warning threshold: 1500)
89
+ ⚠️ [missing-commands] No lint command or linting instructions detected
90
+
91
+ Scan failed with errors. Fix the issues above.
92
+ ```
93
+
94
+ ## Example JSON Output
95
+
96
+ ```json
97
+ {
98
+ "targetDir": "/Users/dev/my-repo",
99
+ "totalTokens": 8724,
100
+ "errorCount": 2,
101
+ "warningCount": 5,
102
+ "files": [
103
+ {
104
+ "relativePath": "AGENTS.md",
105
+ "tokenCount": 6840,
106
+ "findings": [
107
+ {
108
+ "type": "error",
109
+ "rule": "token-budget",
110
+ "message": "File exceeds maximum token budget: 6840 tokens (limit: 3000)"
111
+ }
112
+ ]
113
+ }
114
+ ]
115
+ }
116
+ ```
117
+
118
+ ## Rules
119
+
120
+ | Rule | Severity | Description |
121
+ |---------------------|-------------------|-------------|
122
+ | `token-budget` | warning / error | Warn >1500 tokens, error >3000 (or `--max-tokens`) |
123
+ | `duplicate-lines` | warning | Repeated non-blank lines inside the same file |
124
+ | `vague-instructions`| warning | Phrases like "make it better", "fix everything", "be careful", "use best practices", "clean code", "improve this", "handle edge cases" |
125
+ | `dangerous-commands`| error | forceful recursive removal, world-writable permissions, shell-piped downloads, privilege escalation |
126
+ | `missing-commands` | warning | No recognizable install, test, or lint instructions |
127
+
128
+ ## Examples
129
+
130
+ See the `examples/` directory:
131
+
132
+ - `examples/bloated-AGENTS.md` — triggers many rules
133
+ - `examples/clean-AGENTS.md` — minimal good example
134
+
135
+ Run against the examples (the files use demo names, so copy/rename first or run from a temp dir):
136
+
137
+ ```bash
138
+ # Example of testing the bloated case
139
+ mkdir -p /tmp/bloated-demo && cp examples/bloated-AGENTS.md /tmp/bloated-demo/AGENTS.md
140
+ agent-context-lint /tmp/bloated-demo
141
+ # (manually remove /tmp/bloated-demo afterward)
142
+ ```
143
+
144
+ ## Why This Exists
145
+
146
+ AI coding agents (Cursor, Claude, Copilot, Windsurf, etc.) are powerful, but their instruction files often grow out of control. Large, repetitive, or vague context wastes tokens and leads to poor or unsafe suggestions.
147
+
148
+ `agent-context-lint` gives maintainers an easy, automated way to keep these files healthy.
149
+
150
+ ## Development
151
+
152
+ ```bash
153
+ git clone https://github.com/yourusername/agent-context-lint.git
154
+ cd agent-context-lint
155
+ npm install
156
+ npm run build
157
+ npm test
158
+ ```
159
+
160
+ To test the CLI against the source without global install:
161
+
162
+ ```bash
163
+ npm run build
164
+ node dist/cli.js .
165
+ ```
166
+
167
+ ## Contributing
168
+
169
+ See [CONTRIBUTING.md](CONTRIBUTING.md).
170
+
171
+ ## License
172
+
173
+ MIT
174
+
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
package/dist/cli.js ADDED
@@ -0,0 +1,122 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const index_1 = require("./index");
5
+ const reporter_1 = require("./reporter");
6
+ function printHelp() {
7
+ console.log(`agent-context-lint - Lint AI agent context/instruction files
8
+
9
+ Usage:
10
+ agent-context-lint [path] [options]
11
+
12
+ Arguments:
13
+ path Directory to scan (default: current directory ".")
14
+
15
+ Options:
16
+ --json Output results as JSON
17
+ --max-tokens <N> Set custom max token threshold for errors (default: 3000)
18
+ --help, -h Show this help
19
+ --version, -v Show version
20
+
21
+ Exit codes:
22
+ 0 No errors found (warnings are allowed)
23
+ 1 One or more errors found
24
+
25
+ Examples:
26
+ agent-context-lint .
27
+ agent-context-lint ./my-repo --json
28
+ agent-context-lint . --max-tokens 2000
29
+ `);
30
+ }
31
+ function printVersion() {
32
+ // Read from package.json at runtime
33
+ try {
34
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
35
+ const pkg = require('../package.json');
36
+ console.log(pkg.version || '0.0.0');
37
+ }
38
+ catch {
39
+ console.log('0.1.0');
40
+ }
41
+ }
42
+ function parseArgs(argv) {
43
+ const args = argv.slice(2);
44
+ let target = '.';
45
+ const options = {};
46
+ let help = false;
47
+ let version = false;
48
+ for (let i = 0; i < args.length; i++) {
49
+ const arg = args[i];
50
+ if (arg === '--help' || arg === '-h') {
51
+ help = true;
52
+ }
53
+ else if (arg === '--version' || arg === '-v') {
54
+ version = true;
55
+ }
56
+ else if (arg === '--json') {
57
+ options.json = true;
58
+ }
59
+ else if (arg === '--max-tokens') {
60
+ const next = args[i + 1];
61
+ if (next && !next.startsWith('-')) {
62
+ const n = parseInt(next, 10);
63
+ if (!isNaN(n) && n > 0) {
64
+ options.maxTokens = n;
65
+ }
66
+ else {
67
+ console.error('Error: --max-tokens expects a positive integer');
68
+ process.exit(1);
69
+ }
70
+ i++;
71
+ }
72
+ else {
73
+ console.error('Error: --max-tokens requires a value');
74
+ process.exit(1);
75
+ }
76
+ }
77
+ else if (!arg.startsWith('-')) {
78
+ // positional target dir, take first
79
+ if (target === '.') {
80
+ target = arg;
81
+ }
82
+ }
83
+ else {
84
+ console.error(`Unknown option: ${arg}`);
85
+ process.exit(1);
86
+ }
87
+ }
88
+ return { target, options, help, version };
89
+ }
90
+ function main() {
91
+ const { target, options, help, version } = parseArgs(process.argv);
92
+ if (help) {
93
+ printHelp();
94
+ process.exit(0);
95
+ }
96
+ if (version) {
97
+ printVersion();
98
+ process.exit(0);
99
+ }
100
+ try {
101
+ const result = (0, index_1.lintDirectory)(target, options);
102
+ if (options.json) {
103
+ console.log((0, reporter_1.formatJson)(result));
104
+ }
105
+ else {
106
+ console.log((0, reporter_1.formatHumanReadable)(result));
107
+ }
108
+ // Exit code: 1 only on errors (warnings ok)
109
+ if (result.errorCount > 0) {
110
+ process.exit(1);
111
+ }
112
+ else {
113
+ process.exit(0);
114
+ }
115
+ }
116
+ catch (err) {
117
+ console.error('Unexpected error:', err?.message || err);
118
+ process.exit(1);
119
+ }
120
+ }
121
+ main();
122
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { LintOptions, LintResult } from './types';
2
+ export declare function lintDirectory(targetDir: string, options?: LintOptions): LintResult;
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAc,WAAW,EAAE,UAAU,EAAW,MAAM,SAAS,CAAC;AA2BvE,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,UAAU,CAsCtF"}
package/dist/index.js ADDED
@@ -0,0 +1,92 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.lintDirectory = lintDirectory;
37
+ const path = __importStar(require("path"));
38
+ const scanner_1 = require("./scanner");
39
+ const tokenBudget_1 = require("./rules/tokenBudget");
40
+ const duplicateLines_1 = require("./rules/duplicateLines");
41
+ const vagueInstructions_1 = require("./rules/vagueInstructions");
42
+ const dangerousCommands_1 = require("./rules/dangerousCommands");
43
+ const missingCommands_1 = require("./rules/missingCommands");
44
+ function applyRulesToFile(report, content, options) {
45
+ const findings = [];
46
+ // token budget: pass maxTokens as errorThreshold
47
+ const tokenOpts = {};
48
+ if (options.maxTokens != null) {
49
+ tokenOpts.errorThreshold = options.maxTokens;
50
+ // keep warn at 1500 or use half of max if smaller
51
+ tokenOpts.warnThreshold = Math.min(1500, Math.floor(options.maxTokens / 2));
52
+ }
53
+ findings.push(...(0, tokenBudget_1.checkTokenBudget)(content, tokenOpts));
54
+ findings.push(...(0, duplicateLines_1.checkDuplicateLines)(content));
55
+ findings.push(...(0, vagueInstructions_1.checkVagueInstructions)(content));
56
+ findings.push(...(0, dangerousCommands_1.checkDangerousCommands)(content));
57
+ findings.push(...(0, missingCommands_1.checkMissingCommands)(content));
58
+ return findings;
59
+ }
60
+ function lintDirectory(targetDir, options = {}) {
61
+ const absDir = path.resolve(targetDir);
62
+ const fileReports = (0, scanner_1.scanDirectory)(absDir);
63
+ let totalTokens = 0;
64
+ let errorCount = 0;
65
+ let warningCount = 0;
66
+ const processedFiles = [];
67
+ for (const report of fileReports) {
68
+ const content = (0, scanner_1.readFileContent)(report.filePath);
69
+ const findings = applyRulesToFile(report, content, options);
70
+ // attach
71
+ const updated = {
72
+ ...report,
73
+ findings,
74
+ };
75
+ processedFiles.push(updated);
76
+ totalTokens += updated.tokenCount;
77
+ for (const f of findings) {
78
+ if (f.type === 'error')
79
+ errorCount++;
80
+ else
81
+ warningCount++;
82
+ }
83
+ }
84
+ return {
85
+ files: processedFiles,
86
+ totalTokens,
87
+ errorCount,
88
+ warningCount,
89
+ targetDir: absDir,
90
+ };
91
+ }
92
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BA,sCAsCC;AAnED,2CAA6B;AAC7B,uCAA2E;AAE3E,qDAAuD;AACvD,2DAA6D;AAC7D,iEAAmE;AACnE,iEAAmE;AACnE,6DAA+D;AAE/D,SAAS,gBAAgB,CAAC,MAAkB,EAAE,OAAe,EAAE,OAAoB;IACjF,MAAM,QAAQ,GAAc,EAAE,CAAC;IAE/B,iDAAiD;IACjD,MAAM,SAAS,GAAwD,EAAE,CAAC;IAC1E,IAAI,OAAO,CAAC,SAAS,IAAI,IAAI,EAAE,CAAC;QAC9B,SAAS,CAAC,cAAc,GAAG,OAAO,CAAC,SAAS,CAAC;QAC7C,kDAAkD;QAClD,SAAS,CAAC,aAAa,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC;IAC9E,CAAC;IACD,QAAQ,CAAC,IAAI,CAAC,GAAG,IAAA,8BAAgB,EAAC,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC;IAEvD,QAAQ,CAAC,IAAI,CAAC,GAAG,IAAA,oCAAmB,EAAC,OAAO,CAAC,CAAC,CAAC;IAC/C,QAAQ,CAAC,IAAI,CAAC,GAAG,IAAA,0CAAsB,EAAC,OAAO,CAAC,CAAC,CAAC;IAClD,QAAQ,CAAC,IAAI,CAAC,GAAG,IAAA,0CAAsB,EAAC,OAAO,CAAC,CAAC,CAAC;IAClD,QAAQ,CAAC,IAAI,CAAC,GAAG,IAAA,sCAAoB,EAAC,OAAO,CAAC,CAAC,CAAC;IAEhD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,SAAgB,aAAa,CAAC,SAAiB,EAAE,UAAuB,EAAE;IACxE,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAEvC,MAAM,WAAW,GAAG,IAAA,uBAAa,EAAC,MAAM,CAAC,CAAC;IAE1C,IAAI,WAAW,GAAG,CAAC,CAAC;IACpB,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,IAAI,YAAY,GAAG,CAAC,CAAC;IAErB,MAAM,cAAc,GAAiB,EAAE,CAAC;IAExC,KAAK,MAAM,MAAM,IAAI,WAAW,EAAE,CAAC;QACjC,MAAM,OAAO,GAAG,IAAA,yBAAe,EAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QACjD,MAAM,QAAQ,GAAG,gBAAgB,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;QAE5D,SAAS;QACT,MAAM,OAAO,GAAe;YAC1B,GAAG,MAAM;YACT,QAAQ;SACT,CAAC;QAEF,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAE7B,WAAW,IAAI,OAAO,CAAC,UAAU,CAAC;QAElC,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;YACzB,IAAI,CAAC,CAAC,IAAI,KAAK,OAAO;gBAAE,UAAU,EAAE,CAAC;;gBAChC,YAAY,EAAE,CAAC;QACtB,CAAC;IACH,CAAC;IAED,OAAO;QACL,KAAK,EAAE,cAAc;QACrB,WAAW;QACX,UAAU;QACV,YAAY;QACZ,SAAS,EAAE,MAAM;KAClB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,4 @@
1
+ import { LintResult } from './types';
2
+ export declare function formatHumanReadable(result: LintResult): string;
3
+ export declare function formatJson(result: LintResult): string;
4
+ //# sourceMappingURL=reporter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reporter.d.ts","sourceRoot":"","sources":["../src/reporter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,UAAU,EAAW,MAAM,SAAS,CAAC;AAc1D,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAsC9D;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAmBrD"}
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.formatHumanReadable = formatHumanReadable;
4
+ exports.formatJson = formatJson;
5
+ function formatFinding(f) {
6
+ const icon = f.type === 'error' ? '❌' : '⚠️';
7
+ let out = ` ${icon} [${f.rule}] ${f.message}`;
8
+ if (f.line != null) {
9
+ out += ` (line ${f.line})`;
10
+ }
11
+ if (f.snippet) {
12
+ out += `\n > ${f.snippet}`;
13
+ }
14
+ return out;
15
+ }
16
+ function formatHumanReadable(result) {
17
+ const lines = [];
18
+ lines.push(`agent-context-lint: scanned ${result.files.length} file(s) in ${result.targetDir}`);
19
+ lines.push(`Total estimated tokens: ${result.totalTokens}`);
20
+ lines.push(`Errors: ${result.errorCount} Warnings: ${result.warningCount}`);
21
+ lines.push('');
22
+ if (result.files.length === 0) {
23
+ lines.push('No supported context/instruction files found.');
24
+ lines.push('');
25
+ return lines.join('\n');
26
+ }
27
+ for (const file of result.files) {
28
+ const rel = file.relativePath;
29
+ const tokenInfo = `${file.tokenCount} tokens`;
30
+ lines.push(`📄 ${rel} (${tokenInfo})`);
31
+ if (file.findings.length === 0) {
32
+ lines.push(' ✅ No issues found');
33
+ }
34
+ else {
35
+ for (const f of file.findings) {
36
+ lines.push(formatFinding(f));
37
+ }
38
+ }
39
+ lines.push('');
40
+ }
41
+ if (result.errorCount > 0) {
42
+ lines.push('Scan failed with errors. Fix the issues above.');
43
+ }
44
+ else if (result.warningCount > 0) {
45
+ lines.push('Scan completed with warnings.');
46
+ }
47
+ else {
48
+ lines.push('All clear. Good job!');
49
+ }
50
+ return lines.join('\n');
51
+ }
52
+ function formatJson(result) {
53
+ // Clean for JSON: omit absolute filePath in reports to keep portable
54
+ const cleanFiles = result.files.map(f => ({
55
+ relativePath: f.relativePath,
56
+ tokenCount: f.tokenCount,
57
+ findings: f.findings,
58
+ }));
59
+ return JSON.stringify({
60
+ targetDir: result.targetDir,
61
+ totalTokens: result.totalTokens,
62
+ errorCount: result.errorCount,
63
+ warningCount: result.warningCount,
64
+ files: cleanFiles,
65
+ }, null, 2);
66
+ }
67
+ //# sourceMappingURL=reporter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reporter.js","sourceRoot":"","sources":["../src/reporter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { Finding } from '../types';
2
+ export declare function checkDangerousCommands(content: string): Finding[];
3
+ //# sourceMappingURL=dangerousCommands.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dangerousCommands.d.ts","sourceRoot":"","sources":["../../src/rules/dangerousCommands.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAYnC,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,EAAE,CAqBjE"}
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.checkDangerousCommands = checkDangerousCommands;
4
+ const DANGEROUS_PATTERNS = [
5
+ { pattern: /\brm\s+(-[a-zA-Z]*f[a-zA-Z]*|--force)\b/i, label: 'rm -rf (or force recursive remove)' },
6
+ { pattern: /\bchmod\s+777\b/i, label: 'chmod 777' },
7
+ { pattern: /\bcurl\b[^\n]*\|\s*sh\b/i, label: 'curl | sh (pipe to shell)' },
8
+ { pattern: /\bwget\b[^\n]*\|\s*sh\b/i, label: 'wget | sh (pipe to shell)' },
9
+ { pattern: /\bsudo\b/i, label: 'sudo (privilege escalation)' },
10
+ ];
11
+ const NEGATION_WORDS = /\b(never|do not|don't|dont|avoid|warning|dangerous|unsafe|prohibited|forbidden|not recommended)\b/i;
12
+ function checkDangerousCommands(content) {
13
+ const lines = content.split(/\r?\n/);
14
+ const findings = [];
15
+ lines.forEach((line, idx) => {
16
+ for (const { pattern, label } of DANGEROUS_PATTERNS) {
17
+ if (pattern.test(line) && !NEGATION_WORDS.test(line)) {
18
+ const snippet = line.trim().length > 80 ? line.trim().slice(0, 77) + '...' : line.trim();
19
+ findings.push({
20
+ type: 'error',
21
+ rule: 'dangerous-commands',
22
+ message: `Dangerous shell command detected: ${label}`,
23
+ line: idx + 1,
24
+ snippet,
25
+ });
26
+ break; // avoid duplicate findings for same line
27
+ }
28
+ }
29
+ });
30
+ return findings;
31
+ }
32
+ //# sourceMappingURL=dangerousCommands.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dangerousCommands.js","sourceRoot":"","sources":["../../src/rules/dangerousCommands.ts"],"names":[],"mappings":";;AAYA,wDAqBC;AA/BD,MAAM,kBAAkB,GAA8C;IACpE,EAAE,OAAO,EAAE,0CAA0C,EAAE,KAAK,EAAE,oCAAoC,EAAE;IACpG,EAAE,OAAO,EAAE,kBAAkB,EAAE,KAAK,EAAE,WAAW,EAAE;IACnD,EAAE,OAAO,EAAE,0BAA0B,EAAE,KAAK,EAAE,2BAA2B,EAAE;IAC3E,EAAE,OAAO,EAAE,0BAA0B,EAAE,KAAK,EAAE,2BAA2B,EAAE;IAC3E,EAAE,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,6BAA6B,EAAE;CAC/D,CAAC;AAEF,MAAM,cAAc,GAAG,oGAAoG,CAAC;AAE5H,SAAgB,sBAAsB,CAAC,OAAe;IACpD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACrC,MAAM,QAAQ,GAAc,EAAE,CAAC;IAE/B,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;QAC1B,KAAK,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,kBAAkB,EAAE,CAAC;YACpD,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;gBACrD,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;gBACzF,QAAQ,CAAC,IAAI,CAAC;oBACZ,IAAI,EAAE,OAAO;oBACb,IAAI,EAAE,oBAAoB;oBAC1B,OAAO,EAAE,qCAAqC,KAAK,EAAE;oBACrD,IAAI,EAAE,GAAG,GAAG,CAAC;oBACb,OAAO;iBACR,CAAC,CAAC;gBACH,MAAM,CAAC,yCAAyC;YAClD,CAAC;QACH,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,QAAQ,CAAC;AAClB,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { Finding } from '../types';
2
+ export declare function checkDuplicateLines(content: string): Finding[];
3
+ //# sourceMappingURL=duplicateLines.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"duplicateLines.d.ts","sourceRoot":"","sources":["../../src/rules/duplicateLines.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAEnC,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,EAAE,CA4B9D"}
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.checkDuplicateLines = checkDuplicateLines;
4
+ function checkDuplicateLines(content) {
5
+ const lines = content.split(/\r?\n/);
6
+ const lineCounts = new Map();
7
+ lines.forEach((line, idx) => {
8
+ const trimmed = line.trim();
9
+ if (isIgnoredStructuralOrBlankLine(trimmed))
10
+ return;
11
+ if (!lineCounts.has(trimmed)) {
12
+ lineCounts.set(trimmed, []);
13
+ }
14
+ lineCounts.get(trimmed).push(idx + 1);
15
+ });
16
+ const findings = [];
17
+ for (const [lineText, lineNumbers] of lineCounts.entries()) {
18
+ if (lineNumbers.length > 1) {
19
+ const snippet = lineText.length > 80 ? lineText.slice(0, 77) + '...' : lineText;
20
+ findings.push({
21
+ type: 'warning',
22
+ rule: 'duplicate-lines',
23
+ message: `Duplicated line appears ${lineNumbers.length} times (lines ${lineNumbers.join(', ')})`,
24
+ snippet,
25
+ });
26
+ }
27
+ }
28
+ return findings;
29
+ }
30
+ function isIgnoredStructuralOrBlankLine(trimmed) {
31
+ if (trimmed.length === 0)
32
+ return true; // blank lines
33
+ if (trimmed.startsWith('```'))
34
+ return true; // markdown code fences: ```, ```bash, ```json, etc.
35
+ // exact common structural tokens from code blocks / json / md
36
+ if (trimmed === '{' || trimmed === '}' || trimmed === '[' || trimmed === ']' ||
37
+ trimmed === '],' || trimmed === '},' || trimmed === '{' || trimmed === '}') {
38
+ return true;
39
+ }
40
+ // punctuation-only lines (e.g. ---, ===, ..., ,, ;, etc. common in md and samples)
41
+ if (/^[\{\}\[\]\(\),;:.`'"\-_*#>+\/\\|~!@%^&*=]+$/.test(trimmed))
42
+ return true;
43
+ return false;
44
+ }
45
+ //# sourceMappingURL=duplicateLines.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"duplicateLines.js","sourceRoot":"","sources":["../../src/rules/duplicateLines.ts"],"names":[],"mappings":";;AAEA,kDA4BC;AA5BD,SAAgB,mBAAmB,CAAC,OAAe;IACjD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACrC,MAAM,UAAU,GAAG,IAAI,GAAG,EAAoB,CAAC;IAE/C,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;QAC1B,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAC5B,IAAI,8BAA8B,CAAC,OAAO,CAAC;YAAE,OAAO;QACpD,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;YAC7B,UAAU,CAAC,GAAG,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;QAC9B,CAAC;QACD,UAAU,CAAC,GAAG,CAAC,OAAO,CAAE,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;IAEH,MAAM,QAAQ,GAAc,EAAE,CAAC;IAE/B,KAAK,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,IAAI,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC;QAC3D,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC3B,MAAM,OAAO,GAAG,QAAQ,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC;YAChF,QAAQ,CAAC,IAAI,CAAC;gBACZ,IAAI,EAAE,SAAS;gBACf,IAAI,EAAE,iBAAiB;gBACvB,OAAO,EAAE,2BAA2B,WAAW,CAAC,MAAM,iBAAiB,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG;gBAChG,OAAO;aACR,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,SAAS,8BAA8B,CAAC,OAAe;IACrD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC,CAAC,cAAc;IACrD,IAAI,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC,CAAC,oDAAoD;IAChG,8DAA8D;IAC9D,IAAI,OAAO,KAAK,GAAG,IAAI,OAAO,KAAK,GAAG,IAAI,OAAO,KAAK,GAAG,IAAI,OAAO,KAAK,GAAG;QACxE,OAAO,KAAK,IAAI,IAAI,OAAO,KAAK,IAAI,IAAI,OAAO,KAAK,GAAG,IAAI,OAAO,KAAK,GAAG,EAAG,CAAC;QAChF,OAAO,IAAI,CAAC;IACd,CAAC;IACD,mFAAmF;IACnF,IAAI,8CAA8C,CAAC,IAAI,CAAC,OAAO,CAAC;QAAE,OAAO,IAAI,CAAC;IAC9E,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { Finding } from '../types';
2
+ export declare function checkMissingCommands(content: string): Finding[];
3
+ //# sourceMappingURL=missingCommands.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"missingCommands.d.ts","sourceRoot":"","sources":["../../src/rules/missingCommands.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AA2CnC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,EAAE,CA4B/D"}
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.checkMissingCommands = checkMissingCommands;
4
+ const INSTALL_PATTERNS = [
5
+ /\bnpm\s+(ci|install|i)\b/i,
6
+ /\byarn\s+(install|add)\b/i,
7
+ /\bpnpm\s+(install|i)\b/i,
8
+ /\bbun\s+(install|i)\b/i,
9
+ /\bgo\s+(get|install|mod\s+download)\b/i,
10
+ /\bpip\s+install\b/i,
11
+ /\bpython\s+-m\s+pip\s+install\b/i,
12
+ /\bcargo\s+(build|install)\b/i,
13
+ /install(ation)?\s+(instructions|steps|command)/i,
14
+ ];
15
+ const TEST_PATTERNS = [
16
+ /\bnpm\s+(test|run\s+test)\b/i,
17
+ /\byarn\s+test\b/i,
18
+ /\bpnpm\s+test\b/i,
19
+ /\bbun\s+test\b/i,
20
+ /\bgo\s+test\b/i,
21
+ /\bpytest\b/i,
22
+ /\bjest\b/i,
23
+ /\bvitest\b/i,
24
+ /\bcargo\s+test\b/i,
25
+ /test(ing|s)?\s+(instructions|command|script|how to)/i,
26
+ ];
27
+ const LINT_PATTERNS = [
28
+ /\bnpm\s+run\s+lint\b/i,
29
+ /\byarn\s+lint\b/i,
30
+ /\bpnpm\s+lint\b/i,
31
+ /\beslint\b/i,
32
+ /\bprettier\b/i,
33
+ /\bgolangci-lint\b/i,
34
+ /\bruff\b/i,
35
+ /\bclippy\b/i,
36
+ /\blint(ing)?\s+(instructions|command|script|how to)/i,
37
+ ];
38
+ function hasAnyPattern(content, patterns) {
39
+ return patterns.some(p => p.test(content));
40
+ }
41
+ function checkMissingCommands(content) {
42
+ const findings = [];
43
+ if (!hasAnyPattern(content, INSTALL_PATTERNS)) {
44
+ findings.push({
45
+ type: 'warning',
46
+ rule: 'missing-commands',
47
+ message: 'No install command or installation instructions detected',
48
+ });
49
+ }
50
+ if (!hasAnyPattern(content, TEST_PATTERNS)) {
51
+ findings.push({
52
+ type: 'warning',
53
+ rule: 'missing-commands',
54
+ message: 'No test command or testing instructions detected',
55
+ });
56
+ }
57
+ if (!hasAnyPattern(content, LINT_PATTERNS)) {
58
+ findings.push({
59
+ type: 'warning',
60
+ rule: 'missing-commands',
61
+ message: 'No lint command or linting instructions detected',
62
+ });
63
+ }
64
+ return findings;
65
+ }
66
+ //# sourceMappingURL=missingCommands.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"missingCommands.js","sourceRoot":"","sources":["../../src/rules/missingCommands.ts"],"names":[],"mappings":";;AA2CA,oDA4BC;AArED,MAAM,gBAAgB,GAAG;IACvB,2BAA2B;IAC3B,2BAA2B;IAC3B,yBAAyB;IACzB,wBAAwB;IACxB,wCAAwC;IACxC,oBAAoB;IACpB,kCAAkC;IAClC,8BAA8B;IAC9B,iDAAiD;CAClD,CAAC;AAEF,MAAM,aAAa,GAAG;IACpB,8BAA8B;IAC9B,kBAAkB;IAClB,kBAAkB;IAClB,iBAAiB;IACjB,gBAAgB;IAChB,aAAa;IACb,WAAW;IACX,aAAa;IACb,mBAAmB;IACnB,sDAAsD;CACvD,CAAC;AAEF,MAAM,aAAa,GAAG;IACpB,uBAAuB;IACvB,kBAAkB;IAClB,kBAAkB;IAClB,aAAa;IACb,eAAe;IACf,oBAAoB;IACpB,WAAW;IACX,aAAa;IACb,sDAAsD;CACvD,CAAC;AAEF,SAAS,aAAa,CAAC,OAAe,EAAE,QAAkB;IACxD,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;AAC7C,CAAC;AAED,SAAgB,oBAAoB,CAAC,OAAe;IAClD,MAAM,QAAQ,GAAc,EAAE,CAAC;IAE/B,IAAI,CAAC,aAAa,CAAC,OAAO,EAAE,gBAAgB,CAAC,EAAE,CAAC;QAC9C,QAAQ,CAAC,IAAI,CAAC;YACZ,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,kBAAkB;YACxB,OAAO,EAAE,0DAA0D;SACpE,CAAC,CAAC;IACL,CAAC;IAED,IAAI,CAAC,aAAa,CAAC,OAAO,EAAE,aAAa,CAAC,EAAE,CAAC;QAC3C,QAAQ,CAAC,IAAI,CAAC;YACZ,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,kBAAkB;YACxB,OAAO,EAAE,kDAAkD;SAC5D,CAAC,CAAC;IACL,CAAC;IAED,IAAI,CAAC,aAAa,CAAC,OAAO,EAAE,aAAa,CAAC,EAAE,CAAC;QAC3C,QAAQ,CAAC,IAAI,CAAC;YACZ,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,kBAAkB;YACxB,OAAO,EAAE,kDAAkD;SAC5D,CAAC,CAAC;IACL,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC"}
@@ -0,0 +1,7 @@
1
+ import { Finding } from '../types';
2
+ export interface TokenBudgetOptions {
3
+ warnThreshold?: number;
4
+ errorThreshold?: number;
5
+ }
6
+ export declare function checkTokenBudget(content: string, options?: TokenBudgetOptions): Finding[];
7
+ //# sourceMappingURL=tokenBudget.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tokenBudget.d.ts","sourceRoot":"","sources":["../../src/rules/tokenBudget.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAMnC,MAAM,WAAW,kBAAkB;IACjC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,kBAAuB,GAC/B,OAAO,EAAE,CAsBX"}
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.checkTokenBudget = checkTokenBudget;
4
+ const scanner_1 = require("../scanner");
5
+ const DEFAULT_WARN_THRESHOLD = 1500;
6
+ const DEFAULT_ERROR_THRESHOLD = 3000;
7
+ function checkTokenBudget(content, options = {}) {
8
+ const tokens = (0, scanner_1.estimateTokens)(content);
9
+ const warnThreshold = options.warnThreshold ?? DEFAULT_WARN_THRESHOLD;
10
+ const errorThreshold = options.errorThreshold ?? DEFAULT_ERROR_THRESHOLD;
11
+ const findings = [];
12
+ if (tokens > errorThreshold) {
13
+ findings.push({
14
+ type: 'error',
15
+ rule: 'token-budget',
16
+ message: `File exceeds maximum token budget: ${tokens} tokens (limit: ${errorThreshold})`,
17
+ });
18
+ }
19
+ else if (tokens > warnThreshold) {
20
+ findings.push({
21
+ type: 'warning',
22
+ rule: 'token-budget',
23
+ message: `File is large: ${tokens} tokens (warning threshold: ${warnThreshold})`,
24
+ });
25
+ }
26
+ return findings;
27
+ }
28
+ //# sourceMappingURL=tokenBudget.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tokenBudget.js","sourceRoot":"","sources":["../../src/rules/tokenBudget.ts"],"names":[],"mappings":";;AAWA,4CAyBC;AAnCD,wCAA4C;AAE5C,MAAM,sBAAsB,GAAG,IAAI,CAAC;AACpC,MAAM,uBAAuB,GAAG,IAAI,CAAC;AAOrC,SAAgB,gBAAgB,CAC9B,OAAe,EACf,UAA8B,EAAE;IAEhC,MAAM,MAAM,GAAG,IAAA,wBAAc,EAAC,OAAO,CAAC,CAAC;IACvC,MAAM,aAAa,GAAG,OAAO,CAAC,aAAa,IAAI,sBAAsB,CAAC;IACtE,MAAM,cAAc,GAAG,OAAO,CAAC,cAAc,IAAI,uBAAuB,CAAC;IAEzE,MAAM,QAAQ,GAAc,EAAE,CAAC;IAE/B,IAAI,MAAM,GAAG,cAAc,EAAE,CAAC;QAC5B,QAAQ,CAAC,IAAI,CAAC;YACZ,IAAI,EAAE,OAAO;YACb,IAAI,EAAE,cAAc;YACpB,OAAO,EAAE,sCAAsC,MAAM,mBAAmB,cAAc,GAAG;SAC1F,CAAC,CAAC;IACL,CAAC;SAAM,IAAI,MAAM,GAAG,aAAa,EAAE,CAAC;QAClC,QAAQ,CAAC,IAAI,CAAC;YACZ,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,cAAc;YACpB,OAAO,EAAE,kBAAkB,MAAM,+BAA+B,aAAa,GAAG;SACjF,CAAC,CAAC;IACL,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { Finding } from '../types';
2
+ export declare function checkVagueInstructions(content: string): Finding[];
3
+ //# sourceMappingURL=vagueInstructions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vagueInstructions.d.ts","sourceRoot":"","sources":["../../src/rules/vagueInstructions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAcnC,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,EAAE,CAuBjE"}
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.checkVagueInstructions = checkVagueInstructions;
4
+ const VAGUE_PHRASES = [
5
+ 'make it better',
6
+ 'fix everything',
7
+ 'be careful',
8
+ 'use best practices',
9
+ 'clean code',
10
+ 'improve this',
11
+ 'handle edge cases',
12
+ 'do it right',
13
+ 'make sure it works', // bonus common vague
14
+ ];
15
+ function checkVagueInstructions(content) {
16
+ const lines = content.split(/\r?\n/);
17
+ const findings = [];
18
+ const lowerPhrases = VAGUE_PHRASES.map(p => p.toLowerCase());
19
+ lines.forEach((line, idx) => {
20
+ const lower = line.toLowerCase();
21
+ for (const phrase of lowerPhrases) {
22
+ if (lower.includes(phrase)) {
23
+ const snippet = line.trim().length > 80 ? line.trim().slice(0, 77) + '...' : line.trim();
24
+ findings.push({
25
+ type: 'warning',
26
+ rule: 'vague-instructions',
27
+ message: `Vague instruction detected: "${phrase}"`,
28
+ line: idx + 1,
29
+ snippet,
30
+ });
31
+ break; // one finding per line max
32
+ }
33
+ }
34
+ });
35
+ return findings;
36
+ }
37
+ //# sourceMappingURL=vagueInstructions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vagueInstructions.js","sourceRoot":"","sources":["../../src/rules/vagueInstructions.ts"],"names":[],"mappings":";;AAcA,wDAuBC;AAnCD,MAAM,aAAa,GAAG;IACpB,gBAAgB;IAChB,gBAAgB;IAChB,YAAY;IACZ,oBAAoB;IACpB,YAAY;IACZ,cAAc;IACd,mBAAmB;IACnB,aAAa;IACb,oBAAoB,EAAE,qBAAqB;CAC5C,CAAC;AAEF,SAAgB,sBAAsB,CAAC,OAAe;IACpD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACrC,MAAM,QAAQ,GAAc,EAAE,CAAC;IAC/B,MAAM,YAAY,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;IAE7D,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;QAC1B,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;QACjC,KAAK,MAAM,MAAM,IAAI,YAAY,EAAE,CAAC;YAClC,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;gBAC3B,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;gBACzF,QAAQ,CAAC,IAAI,CAAC;oBACZ,IAAI,EAAE,SAAS;oBACf,IAAI,EAAE,oBAAoB;oBAC1B,OAAO,EAAE,gCAAgC,MAAM,GAAG;oBAClD,IAAI,EAAE,GAAG,GAAG,CAAC;oBACb,OAAO;iBACR,CAAC,CAAC;gBACH,MAAM,CAAC,2BAA2B;YACpC,CAAC;QACH,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,QAAQ,CAAC;AAClB,CAAC"}
@@ -0,0 +1,5 @@
1
+ import { FileReport } from './types';
2
+ export declare function estimateTokens(content: string): number;
3
+ export declare function scanDirectory(targetDir: string): FileReport[];
4
+ export declare function readFileContent(filePath: string): string;
5
+ //# sourceMappingURL=scanner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scanner.d.ts","sourceRoot":"","sources":["../src/scanner.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AA4DrC,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,UAAU,EAAE,CA6B7D;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAExD"}
@@ -0,0 +1,125 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.estimateTokens = estimateTokens;
37
+ exports.scanDirectory = scanDirectory;
38
+ exports.readFileContent = readFileContent;
39
+ const fs = __importStar(require("fs"));
40
+ const path = __importStar(require("path"));
41
+ const SUPPORTED_BASENAMES = new Set([
42
+ 'AGENTS.md',
43
+ 'CLAUDE.md',
44
+ '.cursorrules',
45
+ '.windsurfrules',
46
+ ]);
47
+ const SUPPORTED_RELATIVE = new Set([
48
+ '.github/copilot-instructions.md',
49
+ ]);
50
+ function shouldScanFile(filePath, rootDir) {
51
+ const base = path.basename(filePath);
52
+ const rel = path.relative(rootDir, filePath).replace(/\\/g, '/');
53
+ if (SUPPORTED_BASENAMES.has(base)) {
54
+ return true;
55
+ }
56
+ if (SUPPORTED_RELATIVE.has(rel)) {
57
+ return true;
58
+ }
59
+ // docs/**/*.md
60
+ if (rel.startsWith('docs/') && base.endsWith('.md')) {
61
+ return true;
62
+ }
63
+ // README.md at root or anywhere? spec says README.md, probably any README.md
64
+ if (base === 'README.md') {
65
+ return true;
66
+ }
67
+ return false;
68
+ }
69
+ function walkDir(dir, rootDir, files = []) {
70
+ let entries;
71
+ try {
72
+ entries = fs.readdirSync(dir, { withFileTypes: true });
73
+ }
74
+ catch {
75
+ return files;
76
+ }
77
+ for (const entry of entries) {
78
+ const fullPath = path.join(dir, entry.name);
79
+ const rel = path.relative(rootDir, fullPath).replace(/\\/g, '/');
80
+ if (entry.isDirectory()) {
81
+ if (entry.name === 'node_modules' || entry.name === '.git' || entry.name === 'dist') {
82
+ continue;
83
+ }
84
+ walkDir(fullPath, rootDir, files);
85
+ }
86
+ else if (entry.isFile()) {
87
+ if (shouldScanFile(fullPath, rootDir)) {
88
+ files.push(fullPath);
89
+ }
90
+ }
91
+ }
92
+ return files;
93
+ }
94
+ function estimateTokens(content) {
95
+ return Math.ceil(content.length / 4);
96
+ }
97
+ function scanDirectory(targetDir) {
98
+ const absTarget = path.resolve(targetDir);
99
+ const foundPaths = walkDir(absTarget, absTarget);
100
+ const reports = [];
101
+ for (const filePath of foundPaths) {
102
+ let content;
103
+ try {
104
+ content = fs.readFileSync(filePath, 'utf8');
105
+ }
106
+ catch {
107
+ continue;
108
+ }
109
+ const relativePath = path.relative(absTarget, filePath).replace(/\\/g, '/');
110
+ const tokenCount = estimateTokens(content);
111
+ reports.push({
112
+ filePath,
113
+ relativePath: relativePath || path.basename(filePath),
114
+ tokenCount,
115
+ findings: [],
116
+ });
117
+ }
118
+ // Sort for deterministic output: prefer top level, then alpha
119
+ reports.sort((a, b) => a.relativePath.localeCompare(b.relativePath));
120
+ return reports;
121
+ }
122
+ function readFileContent(filePath) {
123
+ return fs.readFileSync(filePath, 'utf8');
124
+ }
125
+ //# sourceMappingURL=scanner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scanner.js","sourceRoot":"","sources":["../src/scanner.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8DA,wCAEC;AAED,sCA6BC;AAED,0CAEC;AAnGD,uCAAyB;AACzB,2CAA6B;AAG7B,MAAM,mBAAmB,GAAG,IAAI,GAAG,CAAC;IAClC,WAAW;IACX,WAAW;IACX,cAAc;IACd,gBAAgB;CACjB,CAAC,CAAC;AAEH,MAAM,kBAAkB,GAAG,IAAI,GAAG,CAAC;IACjC,iCAAiC;CAClC,CAAC,CAAC;AAEH,SAAS,cAAc,CAAC,QAAgB,EAAE,OAAe;IACvD,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IACrC,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAEjE,IAAI,mBAAmB,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;QAClC,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,kBAAkB,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;QAChC,OAAO,IAAI,CAAC;IACd,CAAC;IACD,eAAe;IACf,IAAI,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QACpD,OAAO,IAAI,CAAC;IACd,CAAC;IACD,6EAA6E;IAC7E,IAAI,IAAI,KAAK,WAAW,EAAE,CAAC;QACzB,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,OAAO,CAAC,GAAW,EAAE,OAAe,EAAE,QAAkB,EAAE;IACjE,IAAI,OAAoB,CAAC;IACzB,IAAI,CAAC;QACH,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IACzD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;IAED,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QAC5C,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QAEjE,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;YACxB,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;gBACpF,SAAS;YACX,CAAC;YACD,OAAO,CAAC,QAAQ,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;QACpC,CAAC;aAAM,IAAI,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC;YAC1B,IAAI,cAAc,CAAC,QAAQ,EAAE,OAAO,CAAC,EAAE,CAAC;gBACtC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACvB,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAgB,cAAc,CAAC,OAAe;IAC5C,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AACvC,CAAC;AAED,SAAgB,aAAa,CAAC,SAAiB;IAC7C,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAC1C,MAAM,UAAU,GAAG,OAAO,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;IAEjD,MAAM,OAAO,GAAiB,EAAE,CAAC;IAEjC,KAAK,MAAM,QAAQ,IAAI,UAAU,EAAE,CAAC;QAClC,IAAI,OAAe,CAAC;QACpB,IAAI,CAAC;YACH,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAC9C,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;QAED,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QAC5E,MAAM,UAAU,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC;QAE3C,OAAO,CAAC,IAAI,CAAC;YACX,QAAQ;YACR,YAAY,EAAE,YAAY,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC;YACrD,UAAU;YACV,QAAQ,EAAE,EAAE;SACb,CAAC,CAAC;IACL,CAAC;IAED,8DAA8D;IAC9D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,aAAa,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC;IAErE,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAgB,eAAe,CAAC,QAAgB;IAC9C,OAAO,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;AAC3C,CAAC"}
@@ -0,0 +1,26 @@
1
+ export type Severity = 'warning' | 'error';
2
+ export interface Finding {
3
+ type: Severity;
4
+ rule: string;
5
+ message: string;
6
+ line?: number;
7
+ snippet?: string;
8
+ }
9
+ export interface FileReport {
10
+ filePath: string;
11
+ relativePath: string;
12
+ tokenCount: number;
13
+ findings: Finding[];
14
+ }
15
+ export interface LintOptions {
16
+ maxTokens?: number;
17
+ json?: boolean;
18
+ }
19
+ export interface LintResult {
20
+ files: FileReport[];
21
+ totalTokens: number;
22
+ errorCount: number;
23
+ warningCount: number;
24
+ targetDir: string;
25
+ }
26
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,QAAQ,GAAG,SAAS,GAAG,OAAO,CAAC;AAE3C,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,QAAQ,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,OAAO,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;CACnB"}
package/dist/types.js ADDED
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@pqqqqqdev/agent-context-lint",
3
+ "version": "0.1.0",
4
+ "description": "A command-line linter that audits AI coding-agent context/instruction files for repositories. Helps reduce token bloat, duplicated instructions, vague agent instructions, missing project commands, and dangerous shell commands.",
5
+ "license": "MIT",
6
+ "author": "pqqqqqdev",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/pqqqqqdev/agent-context-lint.git"
10
+ },
11
+ "bin": {
12
+ "agent-context-lint": "dist/cli.js"
13
+ },
14
+ "main": "./dist/index.js",
15
+ "types": "./dist/index.d.ts",
16
+ "files": [
17
+ "dist",
18
+ "README.md",
19
+ "LICENSE"
20
+ ],
21
+ "scripts": {
22
+ "build": "tsc",
23
+ "test": "vitest run",
24
+ "test:watch": "vitest",
25
+ "lint": "tsc --noEmit",
26
+ "prepublishOnly": "npm run build"
27
+ },
28
+ "keywords": [
29
+ "cli",
30
+ "lint",
31
+ "linter",
32
+ "ai",
33
+ "agent",
34
+ "context",
35
+ "instructions",
36
+ "copilot",
37
+ "cursor",
38
+ "claude",
39
+ "windsurf"
40
+ ],
41
+ "devDependencies": {
42
+ "@types/node": "^20.12.0",
43
+ "eslint": "^9.0.0",
44
+ "typescript": "^5.4.0",
45
+ "vitest": "^4.1.8"
46
+ },
47
+ "engines": {
48
+ "node": ">=18.0.0"
49
+ },
50
+ "type": "commonjs",
51
+ "bugs": {
52
+ "url": "https://github.com/pqqqqqdev/agent-context-lint/issues"
53
+ },
54
+ "homepage": "https://github.com/pqqqqqdev/agent-context-lint#readme"
55
+ }