@aurelienbbn/agentlint 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.
@@ -0,0 +1,137 @@
1
+ import { Schema } from "effect";
2
+ //#region src/domain/flag.ts
3
+ /**
4
+ * Flag types — what rules produce when they detect patterns.
5
+ *
6
+ * {@link FlagOptions} is the rule-author API (passed to `context.flag()`).
7
+ * {@link FlagRecord} is the enriched internal record used by the reporter.
8
+ *
9
+ * @module
10
+ * @since 0.1.0
11
+ */
12
+ /**
13
+ * Enriched flag record after processing — ready for the reporter.
14
+ *
15
+ * Uses `Schema.Class` for structural equality AND runtime validation
16
+ * on construction — invalid fields throw a clear `ParseError`.
17
+ *
18
+ * @since 0.1.0
19
+ * @category models
20
+ */
21
+ var FlagRecord = class extends Schema.Class("FlagRecord")({
22
+ ruleName: Schema.String,
23
+ filename: Schema.String,
24
+ line: Schema.Number,
25
+ col: Schema.Number,
26
+ message: Schema.String,
27
+ sourceSnippet: Schema.String,
28
+ hash: Schema.String,
29
+ instruction: Schema.UndefinedOr(Schema.String),
30
+ suggest: Schema.UndefinedOr(Schema.String)
31
+ }) {};
32
+ //#endregion
33
+ //#region src/domain/node.ts
34
+ /**
35
+ * Lazy wrapper around tree-sitter's `Node`.
36
+ *
37
+ * Provides a stable public API that keeps the tree-sitter dependency
38
+ * out of the consumer-facing surface. Children and parent are wrapped
39
+ * on first access — nodes that are never inspected cost nothing.
40
+ *
41
+ * @module
42
+ * @since 0.1.0
43
+ */
44
+ /**
45
+ * 0-indexed position in source text.
46
+ *
47
+ * Defined as a `Schema.Struct` so positions can be decoded and
48
+ * validated from external sources if needed.
49
+ *
50
+ * @since 0.1.0
51
+ * @category models
52
+ */
53
+ const Position = Schema.Struct({
54
+ row: Schema.Number,
55
+ column: Schema.Number
56
+ });
57
+ /**
58
+ * Private implementation of {@link AgentReviewNode}.
59
+ *
60
+ * Wraps a tree-sitter `Node` and lazily creates child/parent wrappers
61
+ * on first access. Nodes that are never traversed incur zero allocation.
62
+ *
63
+ * @since 0.1.0
64
+ * @category internals
65
+ */
66
+ var AgentReviewNodeImpl = class AgentReviewNodeImpl {
67
+ #inner;
68
+ #children;
69
+ #parent;
70
+ constructor(inner) {
71
+ this.#inner = inner;
72
+ }
73
+ get type() {
74
+ return this.#inner.type;
75
+ }
76
+ get text() {
77
+ return this.#inner.text;
78
+ }
79
+ get startPosition() {
80
+ return this.#inner.startPosition;
81
+ }
82
+ get endPosition() {
83
+ return this.#inner.endPosition;
84
+ }
85
+ get isNamed() {
86
+ return this.#inner.isNamed;
87
+ }
88
+ get childCount() {
89
+ return this.#inner.childCount;
90
+ }
91
+ get children() {
92
+ if (this.#children === void 0) {
93
+ const result = [];
94
+ for (const c of this.#inner.children) if (c !== null) result.push(new AgentReviewNodeImpl(c));
95
+ this.#children = result;
96
+ }
97
+ return this.#children;
98
+ }
99
+ get parent() {
100
+ if (this.#parent === void 0) {
101
+ const p = this.#inner.parent;
102
+ this.#parent = p ? new AgentReviewNodeImpl(p) : null;
103
+ }
104
+ return this.#parent;
105
+ }
106
+ childByFieldName(name) {
107
+ const child = this.#inner.childForFieldName(name);
108
+ return child ? new AgentReviewNodeImpl(child) : null;
109
+ }
110
+ childrenByType(type) {
111
+ const result = [];
112
+ for (const c of this.#inner.children) if (c !== null && c.type === type) result.push(new AgentReviewNodeImpl(c));
113
+ return result;
114
+ }
115
+ descendantsOfType(type) {
116
+ const result = [];
117
+ for (const c of this.#inner.descendantsOfType(type)) if (c !== null) result.push(new AgentReviewNodeImpl(c));
118
+ return result;
119
+ }
120
+ };
121
+ /**
122
+ * Wrap a raw tree-sitter node in the public {@link AgentReviewNode} interface.
123
+ *
124
+ * This is the only bridge between the internal tree-sitter dependency
125
+ * and the consumer-facing API. All child/parent nodes are lazily wrapped
126
+ * on access.
127
+ *
128
+ * @since 0.1.0
129
+ * @category constructors
130
+ */
131
+ function wrapNode(inner) {
132
+ return new AgentReviewNodeImpl(inner);
133
+ }
134
+ //#endregion
135
+ export { wrapNode as n, FlagRecord as r, Position as t };
136
+
137
+ //# sourceMappingURL=node-yh9mLvnE.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node-yh9mLvnE.mjs","names":["#inner","#children","#parent"],"sources":["../src/domain/flag.ts","../src/domain/node.ts"],"sourcesContent":["/**\n * Flag types — what rules produce when they detect patterns.\n *\n * {@link FlagOptions} is the rule-author API (passed to `context.flag()`).\n * {@link FlagRecord} is the enriched internal record used by the reporter.\n *\n * @module\n * @since 0.1.0\n */\n\nimport { Schema } from \"effect\";\nimport type { AgentReviewNode } from \"./node.js\";\n\n/**\n * Options passed to `context.flag()` by rule visitors.\n *\n * @since 0.1.0\n * @category models\n */\nexport interface FlagOptions {\n /** The AST node that triggered the match. */\n readonly node: AgentReviewNode;\n /** Short one-liner displayed next to file:line in output. */\n readonly message: string;\n /**\n * Override `meta.instruction` for this specific match.\n * Appears inline in per-match notes.\n */\n readonly instruction?: string | undefined;\n /** Hint toward the fix. Not a command - just a nudge. */\n readonly suggest?: string | undefined;\n}\n\n/**\n * Enriched flag record after processing — ready for the reporter.\n *\n * Uses `Schema.Class` for structural equality AND runtime validation\n * on construction — invalid fields throw a clear `ParseError`.\n *\n * @since 0.1.0\n * @category models\n */\nexport class FlagRecord extends Schema.Class<FlagRecord>(\"FlagRecord\")({\n ruleName: Schema.String,\n filename: Schema.String,\n /** 1-based line number. */\n line: Schema.Number,\n /** 1-based column number. */\n col: Schema.Number,\n message: Schema.String,\n sourceSnippet: Schema.String,\n /** 7-char hex hash for stable match identification. */\n hash: Schema.String,\n instruction: Schema.UndefinedOr(Schema.String),\n suggest: Schema.UndefinedOr(Schema.String),\n}) {}\n","/**\n * Lazy wrapper around tree-sitter's `Node`.\n *\n * Provides a stable public API that keeps the tree-sitter dependency\n * out of the consumer-facing surface. Children and parent are wrapped\n * on first access — nodes that are never inspected cost nothing.\n *\n * @module\n * @since 0.1.0\n */\n\nimport { Schema } from \"effect\";\nimport type { Node as TSNode } from \"web-tree-sitter\";\n\n/**\n * 0-indexed position in source text.\n *\n * Defined as a `Schema.Struct` so positions can be decoded and\n * validated from external sources if needed.\n *\n * @since 0.1.0\n * @category models\n */\nexport const Position = Schema.Struct({\n row: Schema.Number,\n column: Schema.Number,\n});\n\n/** @since 0.1.0 */\nexport type Position = Schema.Schema.Type<typeof Position>;\n\n/**\n * Read-only view of a syntax tree node.\n *\n * One universal type — no per-kind subtypes. Rules narrow via\n * `node.type` string checks and field accessors.\n *\n * @since 0.1.0\n * @category models\n */\nexport interface AgentReviewNode {\n /** tree-sitter grammar node type (e.g. `\"function_declaration\"`, `\"comment\"`) */\n readonly type: string;\n /** Full source text covered by this node. */\n readonly text: string;\n /** 0-indexed start position. */\n readonly startPosition: Position;\n /** 0-indexed end position. */\n readonly endPosition: Position;\n /** Whether this is a named node in the grammar. */\n readonly isNamed: boolean;\n /** Direct child nodes (lazily wrapped). */\n readonly children: ReadonlyArray<AgentReviewNode>;\n /** Parent node, or null for the root. */\n readonly parent: AgentReviewNode | null;\n /** Number of direct children. */\n readonly childCount: number;\n\n /** Get a child by its grammar field name (e.g. `\"name\"`, `\"body\"`). */\n childByFieldName(name: string): AgentReviewNode | null;\n /** All direct children matching the given node type. */\n childrenByType(type: string): ReadonlyArray<AgentReviewNode>;\n /** Recursively collect all descendants matching the given node type. */\n descendantsOfType(type: string): ReadonlyArray<AgentReviewNode>;\n}\n\n/**\n * Private implementation of {@link AgentReviewNode}.\n *\n * Wraps a tree-sitter `Node` and lazily creates child/parent wrappers\n * on first access. Nodes that are never traversed incur zero allocation.\n *\n * @since 0.1.0\n * @category internals\n */\nclass AgentReviewNodeImpl implements AgentReviewNode {\n readonly #inner: TSNode;\n #children: ReadonlyArray<AgentReviewNode> | undefined;\n #parent: AgentReviewNode | null | undefined;\n\n constructor(inner: TSNode) {\n this.#inner = inner;\n }\n\n get type(): string {\n return this.#inner.type;\n }\n\n get text(): string {\n return this.#inner.text;\n }\n\n get startPosition(): Position {\n return this.#inner.startPosition;\n }\n\n get endPosition(): Position {\n return this.#inner.endPosition;\n }\n\n get isNamed(): boolean {\n return this.#inner.isNamed;\n }\n\n get childCount(): number {\n return this.#inner.childCount;\n }\n\n get children(): ReadonlyArray<AgentReviewNode> {\n if (this.#children === undefined) {\n const result: AgentReviewNode[] = [];\n for (const c of this.#inner.children) {\n if (c !== null) result.push(new AgentReviewNodeImpl(c));\n }\n this.#children = result;\n }\n return this.#children;\n }\n\n get parent(): AgentReviewNode | null {\n if (this.#parent === undefined) {\n const p = this.#inner.parent;\n this.#parent = p ? new AgentReviewNodeImpl(p) : null;\n }\n return this.#parent;\n }\n\n childByFieldName(name: string): AgentReviewNode | null {\n const child = this.#inner.childForFieldName(name);\n return child ? new AgentReviewNodeImpl(child) : null;\n }\n\n childrenByType(type: string): ReadonlyArray<AgentReviewNode> {\n const result: AgentReviewNode[] = [];\n for (const c of this.#inner.children) {\n if (c !== null && c.type === type) result.push(new AgentReviewNodeImpl(c));\n }\n return result;\n }\n\n descendantsOfType(type: string): ReadonlyArray<AgentReviewNode> {\n const result: AgentReviewNode[] = [];\n for (const c of this.#inner.descendantsOfType(type)) {\n if (c !== null) result.push(new AgentReviewNodeImpl(c));\n }\n return result;\n }\n}\n\n/**\n * Wrap a raw tree-sitter node in the public {@link AgentReviewNode} interface.\n *\n * This is the only bridge between the internal tree-sitter dependency\n * and the consumer-facing API. All child/parent nodes are lazily wrapped\n * on access.\n *\n * @since 0.1.0\n * @category constructors\n */\nexport function wrapNode(inner: TSNode): AgentReviewNode {\n return new AgentReviewNodeImpl(inner);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AA0CA,IAAa,aAAb,cAAgC,OAAO,MAAkB,aAAa,CAAC;CACrE,UAAU,OAAO;CACjB,UAAU,OAAO;CAEjB,MAAM,OAAO;CAEb,KAAK,OAAO;CACZ,SAAS,OAAO;CAChB,eAAe,OAAO;CAEtB,MAAM,OAAO;CACb,aAAa,OAAO,YAAY,OAAO,OAAO;CAC9C,SAAS,OAAO,YAAY,OAAO,OAAO;CAC3C,CAAC,CAAC;;;;;;;;;;;;;;;;;;;;;;AChCH,MAAa,WAAW,OAAO,OAAO;CACpC,KAAK,OAAO;CACZ,QAAQ,OAAO;CAChB,CAAC;;;;;;;;;;AAiDF,IAAM,sBAAN,MAAM,oBAA+C;CACnD;CACA;CACA;CAEA,YAAY,OAAe;AACzB,QAAA,QAAc;;CAGhB,IAAI,OAAe;AACjB,SAAO,MAAA,MAAY;;CAGrB,IAAI,OAAe;AACjB,SAAO,MAAA,MAAY;;CAGrB,IAAI,gBAA0B;AAC5B,SAAO,MAAA,MAAY;;CAGrB,IAAI,cAAwB;AAC1B,SAAO,MAAA,MAAY;;CAGrB,IAAI,UAAmB;AACrB,SAAO,MAAA,MAAY;;CAGrB,IAAI,aAAqB;AACvB,SAAO,MAAA,MAAY;;CAGrB,IAAI,WAA2C;AAC7C,MAAI,MAAA,aAAmB,KAAA,GAAW;GAChC,MAAM,SAA4B,EAAE;AACpC,QAAK,MAAM,KAAK,MAAA,MAAY,SAC1B,KAAI,MAAM,KAAM,QAAO,KAAK,IAAI,oBAAoB,EAAE,CAAC;AAEzD,SAAA,WAAiB;;AAEnB,SAAO,MAAA;;CAGT,IAAI,SAAiC;AACnC,MAAI,MAAA,WAAiB,KAAA,GAAW;GAC9B,MAAM,IAAI,MAAA,MAAY;AACtB,SAAA,SAAe,IAAI,IAAI,oBAAoB,EAAE,GAAG;;AAElD,SAAO,MAAA;;CAGT,iBAAiB,MAAsC;EACrD,MAAM,QAAQ,MAAA,MAAY,kBAAkB,KAAK;AACjD,SAAO,QAAQ,IAAI,oBAAoB,MAAM,GAAG;;CAGlD,eAAe,MAA8C;EAC3D,MAAM,SAA4B,EAAE;AACpC,OAAK,MAAM,KAAK,MAAA,MAAY,SAC1B,KAAI,MAAM,QAAQ,EAAE,SAAS,KAAM,QAAO,KAAK,IAAI,oBAAoB,EAAE,CAAC;AAE5E,SAAO;;CAGT,kBAAkB,MAA8C;EAC9D,MAAM,SAA4B,EAAE;AACpC,OAAK,MAAM,KAAK,MAAA,MAAY,kBAAkB,KAAK,CACjD,KAAI,MAAM,KAAM,QAAO,KAAK,IAAI,oBAAoB,EAAE,CAAC;AAEzD,SAAO;;;;;;;;;;;;;AAcX,SAAgB,SAAS,OAAgC;AACvD,QAAO,IAAI,oBAAoB,MAAM"}
Binary file
Binary file
package/package.json ADDED
@@ -0,0 +1,69 @@
1
+ {
2
+ "name": "@aurelienbbn/agentlint",
3
+ "version": "0.1.0",
4
+ "description": "Stateless, deterministic CLI that bridges traditional linters and AI-assisted code review",
5
+ "keywords": [
6
+ "agent",
7
+ "ai",
8
+ "ast",
9
+ "code-review",
10
+ "linter",
11
+ "tanstack-intent",
12
+ "tree-sitter"
13
+ ],
14
+ "license": "MIT",
15
+ "author": "Aurelien Bobenrieth",
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "https://github.com/aurelienbobenrieth/agentlint"
19
+ },
20
+ "bin": {
21
+ "agentlint": "dist/bin.mjs"
22
+ },
23
+ "files": [
24
+ "dist",
25
+ "skills",
26
+ "!skills/_artifacts"
27
+ ],
28
+ "type": "module",
29
+ "exports": {
30
+ ".": {
31
+ "types": "./dist/index.d.mts",
32
+ "import": "./dist/index.mjs"
33
+ }
34
+ },
35
+ "dependencies": {
36
+ "@effect/platform-node": "4.0.0-beta.43",
37
+ "effect": "^4.0.0-beta.43",
38
+ "jiti": "^2.4.2",
39
+ "picomatch": "^4.0.2",
40
+ "web-tree-sitter": "^0.25.3"
41
+ },
42
+ "devDependencies": {
43
+ "@changesets/changelog-github": "^0.6.0",
44
+ "@changesets/cli": "^2.30.0",
45
+ "@tanstack/intent": "latest",
46
+ "@types/node": "^22.13.14",
47
+ "@types/picomatch": "^4.0.0",
48
+ "oxfmt": "latest",
49
+ "oxlint": "^0.16.7",
50
+ "tree-sitter-wasms": "^0.1.13",
51
+ "tsdown": "latest",
52
+ "typescript": "^5.7.3",
53
+ "vitest": "^3.1.1"
54
+ },
55
+ "engines": {
56
+ "node": ">=22"
57
+ },
58
+ "scripts": {
59
+ "build": "tsdown",
60
+ "typecheck": "tsc --noEmit",
61
+ "test": "vitest run",
62
+ "test:watch": "vitest",
63
+ "lint": "oxlint",
64
+ "fmt": "oxfmt --write",
65
+ "fmt:check": "oxfmt --check",
66
+ "changeset": "changeset",
67
+ "check": "pnpm typecheck && pnpm lint && pnpm fmt:check && pnpm test"
68
+ }
69
+ }
@@ -0,0 +1,131 @@
1
+ ---
2
+ name: agentlint/rule-advisor
3
+ description: >
4
+ Classify a code quality concern into the right enforcement tool and act on it.
5
+ Activate when the user wants to enforce a pattern, catch a mistake, add a
6
+ check, create a rule, prevent a practice, guard against regressions, set up
7
+ linting, improve their feedback loop, or asks "how do I make sure X."
8
+ type: core
9
+ library: agentlint
10
+ library_version: "0.1.0"
11
+ sources:
12
+ - "aurelienbobenrieth/agentlint:README.md"
13
+ - "aurelienbobenrieth/agentlint:CONTRIBUTING.md"
14
+ ---
15
+
16
+ # Rule Advisor
17
+
18
+ Classify what the user wants to enforce, pick the right tool, act on it.
19
+
20
+ ## How to use this skill
21
+
22
+ 1. Read the user's intent
23
+ 2. If the intent is too vague to classify, clarify with one targeted question
24
+ 3. Once clear, classify using the decision tree below
25
+ 4. State your classification and reasoning in one sentence
26
+ 5. Implement or guide the user to implement it
27
+
28
+ ## Decision tree
29
+
30
+ **Can a pattern be detected by looking at code structure (AST, file, imports)?**
31
+
32
+ NO, and no metric or structure can trigger detection either
33
+ → Document as a code review guideline. This is a team convention.
34
+
35
+ YES, and the correct action is always the same (mechanical fix)
36
+ → Existing linter rule (e.g. oxlint, eslint, biome, or similar)
37
+ if a well-known rule exists for it.
38
+ → Custom lint rule (e.g. eslint plugin, or similar)
39
+ if it's project-specific but still deterministic.
40
+ → Codemod (e.g. jscodeshift, ts-morph, or similar)
41
+ if it needs automated rewriting across files.
42
+
43
+ YES, but what to do about each finding requires judgment
44
+ → **agentlint rule.** Deterministic detection, AI-evaluated action.
45
+
46
+ **Is it about module or dependency structure?**
47
+ → Dependency analysis (e.g. dependency-cruiser, madge, or similar)
48
+ for import direction and boundaries.
49
+ → CI check or agentlint rule for file/directory naming conventions.
50
+
51
+ **Is it about runtime behavior?**
52
+ → Type-level enforcement (e.g. branded types, schema validation, or similar)
53
+ if it can be caught at compile time.
54
+ → Tests (unit, integration, property-based) if it needs execution.
55
+ → Monitoring if it needs production signals.
56
+
57
+ ## When the answer is agentlint
58
+
59
+ agentlint's niche: deterministic detection + non-deterministic evaluation.
60
+
61
+ Three scopes are available:
62
+
63
+ - **Single-node visitor** - flag individual AST nodes (comments, functions,
64
+ catch blocks, magic numbers)
65
+ - **Program visitor** - analyze the full file AST (duplicate function bodies,
66
+ nested loops, export counts, prop counts)
67
+ - **File-level targeting** - use include/ignore globs to scope rules to
68
+ specific directories or file patterns
69
+
70
+ ### Rule template
71
+
72
+ ```typescript
73
+ import { defineRule } from "agentlint";
74
+
75
+ const myRule = defineRule({
76
+ meta: {
77
+ name: "rule-name",
78
+ description: "One-line description of what this detects",
79
+ languages: ["ts", "tsx"],
80
+ include: ["src/**"], // optional: scope to paths
81
+ ignore: ["**/*.test.*"], // optional: exclude paths
82
+ instruction: `[Be specific. Tell the AI agent exactly what to evaluate
83
+ for each finding, when a finding is acceptable, and what action to take
84
+ when it is a true positive.]`,
85
+ },
86
+ createOnce(context) {
87
+ return {
88
+ // Available visitors: program, function_declaration, arrow_function,
89
+ // call_expression, identifier, string, comment, class_declaration,
90
+ // method_definition, property_identifier, type_annotation,
91
+ // interface_declaration, import_statement, export_statement,
92
+ // variable_declarator, if_statement, return_statement,
93
+ // object, array, pair, jsx_element, jsx_self_closing_element
94
+ comment(node) {
95
+ context.flag({ node, message: node.text.trim() });
96
+ },
97
+ };
98
+ },
99
+ });
100
+ ```
101
+
102
+ ### AgentReviewNode API
103
+
104
+ - `text` - source text of the node
105
+ - `type` - tree-sitter node type
106
+ - `startPosition` / `endPosition` - `{ row, column }`
107
+ - `children` - child nodes array
108
+ - `parent` - parent node or undefined
109
+ - `child(i)` / `namedChild(i)` - get child by index
110
+ - `childForFieldName(name)` - get child by field name
111
+ - `descendantsOfType(type)` - find all descendants matching a type
112
+
113
+ ### Writing good instructions
114
+
115
+ The instruction is what the AI agent reads to evaluate each finding.
116
+ It determines whether findings are actionable or noise.
117
+
118
+ Bad: "Review this code"
119
+
120
+ Good: "Evaluate each flagged catch block. If the error is logged and
121
+ re-thrown, it is acceptable. If the error is silently swallowed - empty
122
+ catch or catch with only a console.log - suggest adding proper error
123
+ handling or propagation."
124
+
125
+ ### Testing
126
+
127
+ ```bash
128
+ npx agentlint check --all --rule rule-name
129
+ npx agentlint check src/handlers/checkout.ts --rule rule-name
130
+ npx agentlint check --all --rule rule-name --dry-run
131
+ ```
@@ -0,0 +1,157 @@
1
+ ---
2
+ name: agentlint/usage
3
+ description: >
4
+ Run agentlint CLI after code changes to catch patterns for AI evaluation.
5
+ Activate when finishing code modifications, before committing, or when
6
+ the developer asks to lint, scan, or review code with agentlint. Covers
7
+ agentlint check, agentlint list, agentlint review, agentlint init,
8
+ inline suppression, and output interpretation.
9
+ type: core
10
+ library: agentlint
11
+ library_version: "0.1.0"
12
+ sources:
13
+ - "aurelienbobenrieth/agentlint:README.md"
14
+ - "aurelienbobenrieth/agentlint:src/bin.ts"
15
+ ---
16
+
17
+ # agentlint
18
+
19
+ Stateless, deterministic linter whose output is designed for you (an AI coding agent) to evaluate. It uses tree-sitter to parse code, runs visitor-based rules that flag suspicious patterns, and outputs structured reports with natural language instructions.
20
+
21
+ ## Setup
22
+
23
+ ```bash
24
+ npm install agentlint
25
+ npx agentlint init
26
+ ```
27
+
28
+ This creates `agentlint.config.ts` with a starter template. Add rules to the `rules` object.
29
+
30
+ ## Core Patterns
31
+
32
+ ### Run after code changes
33
+
34
+ ```bash
35
+ # Default: scan files changed in current branch
36
+ npx agentlint check
37
+
38
+ # Scan specific files or globs
39
+ npx agentlint check src/utils.ts "src/**/*.tsx"
40
+
41
+ # Scan all files
42
+ npx agentlint check --all
43
+
44
+ # Only run a specific rule
45
+ npx agentlint check --rule no-noise-comments
46
+
47
+ # Dry-run (counts only, no instruction blocks)
48
+ npx agentlint check --dry-run
49
+
50
+ # Diff against a specific branch
51
+ npx agentlint check --base main
52
+ ```
53
+
54
+ ### Read and act on output
55
+
56
+ Output is grouped by rule. Each rule section contains:
57
+
58
+ 1. **Match listings** with `[hash] file:line:col source-line`
59
+ 2. **Instruction block** explaining how to evaluate the matches
60
+
61
+ Process one rule section at a time:
62
+
63
+ 1. Read the instruction block for the rule
64
+ 2. Evaluate each match against the criteria in the instruction
65
+ 3. For matches that are genuine issues: fix them
66
+ 4. For matches that are acceptable: move on
67
+ 5. Re-run `agentlint check` after fixes — resolved matches disappear
68
+
69
+ ### Mark findings as reviewed
70
+
71
+ ```bash
72
+ # Mark specific hashes as reviewed (they disappear from future output)
73
+ npx agentlint review abc1234 def5678
74
+
75
+ # Mark all current flags as reviewed
76
+ npx agentlint review --all
77
+
78
+ # Reset reviewed state (see all flags again)
79
+ npx agentlint review --reset
80
+ ```
81
+
82
+ ### Suppress inline
83
+
84
+ ```typescript
85
+ // agentlint-ignore no-noise-comments -- explains the retry formula
86
+ const delay = baseDelay * Math.pow(2, attempt);
87
+ ```
88
+
89
+ ### Write a rule
90
+
91
+ ```typescript
92
+ import { defineConfig, defineRule } from "agentlint";
93
+
94
+ const noTodos = defineRule({
95
+ meta: {
96
+ name: "no-todos",
97
+ description: "Flags TODO comments for evaluation",
98
+ languages: ["ts", "tsx"],
99
+ instruction: "Evaluate each TODO. Convert actionable ones to issues, remove stale ones.",
100
+ },
101
+ createOnce(context) {
102
+ return {
103
+ comment(node) {
104
+ if (node.text.includes("TODO")) {
105
+ context.flag({ node, message: node.text.trim() });
106
+ }
107
+ },
108
+ };
109
+ },
110
+ });
111
+
112
+ export default defineConfig({
113
+ include: ["src/**/*.{ts,tsx}"],
114
+ rules: { "no-todos": noTodos },
115
+ });
116
+ ```
117
+
118
+ ## Common Mistakes
119
+
120
+ ### HIGH Exit code 1 is not an error
121
+
122
+ Wrong:
123
+
124
+ ```bash
125
+ # Treating non-zero exit as a failure and stopping
126
+ npx agentlint check || echo "agentlint failed"
127
+ ```
128
+
129
+ Correct:
130
+
131
+ ```bash
132
+ # Exit code 1 means findings exist — read and evaluate the output
133
+ npx agentlint check
134
+ # Then process the stdout, don't treat it as a crash
135
+ ```
136
+
137
+ agentlint exits with code 1 when findings exist. This is expected behavior, not a crash. Read the output and evaluate each finding.
138
+
139
+ ### HIGH Fixing all findings without reading instructions
140
+
141
+ Wrong: Blindly "fixing" every flagged line without reading the rule's instruction block.
142
+
143
+ Correct: Read the instruction block first. It tells you the evaluation criteria. Some findings are intentionally acceptable — the instruction explains which.
144
+
145
+ ### MEDIUM Looping on already-evaluated findings
146
+
147
+ Wrong: Re-evaluating the same findings across multiple agentlint runs in one session.
148
+
149
+ Correct: Evaluate each finding once. If it persists after your fix, tell the developer rather than retrying. Use `agentlint review <hash>` to mark evaluated findings.
150
+
151
+ ## Constraints
152
+
153
+ - Process one rule section at a time, not all findings at once
154
+ - Never attempt more than one fix per finding
155
+ - If agentlint still flags something after your fix, tell the developer
156
+ - When the instruction says "ask the developer," do that instead of guessing
157
+ - Do not loop on findings you already evaluated in this session