@aurelienbbn/agentlint 0.1.0 → 0.1.1
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/dist/bin.mjs +50 -124
- package/dist/bin.mjs.map +1 -1
- package/dist/index.d.mts +6 -6
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +3 -3
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
package/dist/bin.mjs
CHANGED
|
@@ -2,9 +2,8 @@
|
|
|
2
2
|
import { n as wrapNode, r as FlagRecord } from "./node-yh9mLvnE.mjs";
|
|
3
3
|
import * as NodeRuntime from "@effect/platform-node/NodeRuntime";
|
|
4
4
|
import * as NodeServices from "@effect/platform-node/NodeServices";
|
|
5
|
-
import { Console, Effect, FileSystem, HashMap, HashSet, Layer, Option, Path, Schema } from "effect";
|
|
5
|
+
import { Console, Context, Effect, FileSystem, HashMap, HashSet, Layer, Option, Path, Schema } from "effect";
|
|
6
6
|
import { Argument, Command, Flag } from "effect/unstable/cli";
|
|
7
|
-
import * as ServiceMap from "effect/ServiceMap";
|
|
8
7
|
import picomatch from "picomatch";
|
|
9
8
|
import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process";
|
|
10
9
|
import { Language, Parser } from "web-tree-sitter";
|
|
@@ -30,7 +29,7 @@ import { Language, Parser } from "web-tree-sitter";
|
|
|
30
29
|
* @since 0.1.0
|
|
31
30
|
* @category services
|
|
32
31
|
*/
|
|
33
|
-
var Env = class Env extends
|
|
32
|
+
var Env = class Env extends Context.Service()("agentlint/Env") {
|
|
34
33
|
/**
|
|
35
34
|
* Default layer — reads from `process` globals exactly once.
|
|
36
35
|
*
|
|
@@ -58,7 +57,7 @@ var Env = class Env extends ServiceMap.Service()("agentreview/Env") {
|
|
|
58
57
|
* via `jiti` (for TypeScript support without pre-compilation), and
|
|
59
58
|
* validates the exported shape.
|
|
60
59
|
*
|
|
61
|
-
* **Search order**: `
|
|
60
|
+
* **Search order**: `agentlint.config.ts` → `.js` → `.mts` → `.mjs`.
|
|
62
61
|
* The first match wins.
|
|
63
62
|
*
|
|
64
63
|
* @module
|
|
@@ -78,10 +77,10 @@ var ConfigError = class extends Schema.TaggedErrorClass()("ConfigError", { messa
|
|
|
78
77
|
* @category constants
|
|
79
78
|
*/
|
|
80
79
|
const CONFIG_NAMES = [
|
|
81
|
-
"
|
|
82
|
-
"
|
|
83
|
-
"
|
|
84
|
-
"
|
|
80
|
+
"agentlint.config.ts",
|
|
81
|
+
"agentlint.config.js",
|
|
82
|
+
"agentlint.config.mts",
|
|
83
|
+
"agentlint.config.mjs"
|
|
85
84
|
];
|
|
86
85
|
/**
|
|
87
86
|
* Discover the config file path by checking candidates in order.
|
|
@@ -94,10 +93,10 @@ const discoverConfig = (fs, path, cwd) => Effect.gen(function* () {
|
|
|
94
93
|
const candidate = path.resolve(cwd, name);
|
|
95
94
|
if (yield* fs.exists(candidate).pipe(Effect.orElseSucceed(() => false))) return candidate;
|
|
96
95
|
}
|
|
97
|
-
return yield* new ConfigError({ message: `No
|
|
96
|
+
return yield* new ConfigError({ message: `No agentlint config found. Create agentlint.config.ts in ${cwd}` });
|
|
98
97
|
});
|
|
99
98
|
/**
|
|
100
|
-
* Effect service that discovers and loads the
|
|
99
|
+
* Effect service that discovers and loads the agentlint config file.
|
|
101
100
|
*
|
|
102
101
|
* Uses `jiti` under the hood so TypeScript configs work without a
|
|
103
102
|
* separate compilation step.
|
|
@@ -117,7 +116,7 @@ const discoverConfig = (fs, path, cwd) => Effect.gen(function* () {
|
|
|
117
116
|
* @since 0.1.0
|
|
118
117
|
* @category services
|
|
119
118
|
*/
|
|
120
|
-
var ConfigLoader = class ConfigLoader extends
|
|
119
|
+
var ConfigLoader = class ConfigLoader extends Context.Service()("agentlint/ConfigLoader") {
|
|
121
120
|
static layer = Layer.effect(ConfigLoader, Effect.gen(function* () {
|
|
122
121
|
const env = yield* Env;
|
|
123
122
|
const fs = yield* FileSystem.FileSystem;
|
|
@@ -142,7 +141,7 @@ var ConfigLoader = class ConfigLoader extends ServiceMap.Service()("agentreview/
|
|
|
142
141
|
/**
|
|
143
142
|
* Local state store for tracking reviewed flags.
|
|
144
143
|
*
|
|
145
|
-
* Manages a `.
|
|
144
|
+
* Manages a `.agentlint-state` file in the project root that stores
|
|
146
145
|
* hashes of flags that have been reviewed. This file is intended to
|
|
147
146
|
* be **gitignored** — it is per-developer scratch state for tracking
|
|
148
147
|
* progress during review sweeps.
|
|
@@ -152,7 +151,7 @@ var ConfigLoader = class ConfigLoader extends ServiceMap.Service()("agentreview/
|
|
|
152
151
|
* above a reviewed flag shifts its position and invalidates the hash.
|
|
153
152
|
* This is by design — changed context should be re-reviewed.
|
|
154
153
|
* - Stale hashes (from flags that no longer exist) accumulate harmlessly.
|
|
155
|
-
* Use `
|
|
154
|
+
* Use `agentlint review --reset` to start fresh.
|
|
156
155
|
*
|
|
157
156
|
* @module
|
|
158
157
|
* @since 0.1.0
|
|
@@ -163,7 +162,7 @@ var ConfigLoader = class ConfigLoader extends ServiceMap.Service()("agentreview/
|
|
|
163
162
|
* @since 0.1.0
|
|
164
163
|
* @category constants
|
|
165
164
|
*/
|
|
166
|
-
const STATE_FILENAME = ".
|
|
165
|
+
const STATE_FILENAME = ".agentlint-state";
|
|
167
166
|
/**
|
|
168
167
|
* Parse the state file into a set of hashes.
|
|
169
168
|
* Tolerates blank lines and `#`-prefixed comments.
|
|
@@ -194,7 +193,7 @@ function serializeHashes(hashes) {
|
|
|
194
193
|
* @since 0.1.0
|
|
195
194
|
* @category services
|
|
196
195
|
*/
|
|
197
|
-
var StateStore = class StateStore extends
|
|
196
|
+
var StateStore = class StateStore extends Context.Service()("agentlint/StateStore") {
|
|
198
197
|
/**
|
|
199
198
|
* Default layer — resolves the state file path from `Env.cwd`.
|
|
200
199
|
*
|
|
@@ -345,13 +344,9 @@ var RuleContextImpl = class {
|
|
|
345
344
|
/**
|
|
346
345
|
* File resolution service.
|
|
347
346
|
*
|
|
348
|
-
* Determines which files to lint by applying the
|
|
347
|
+
* Determines which files to lint by applying the filter pipeline:
|
|
349
348
|
* 1. Candidate files (from git diff or all files)
|
|
350
|
-
* 2.
|
|
351
|
-
* 3. .gitignore
|
|
352
|
-
* 4. .agentreviewignore
|
|
353
|
-
* 5. Config include/ignore
|
|
354
|
-
* (Steps 2-4 are handled by IgnoreReader)
|
|
349
|
+
* 2. Config include/ignore
|
|
355
350
|
*
|
|
356
351
|
* Per-rule filtering (languages, include, ignore) is done by the check command.
|
|
357
352
|
*
|
|
@@ -410,7 +405,7 @@ function listAllFiles(dir, base, fs, path) {
|
|
|
410
405
|
* @since 0.1.0
|
|
411
406
|
* @category constructors
|
|
412
407
|
*/
|
|
413
|
-
function resolveFiles(options,
|
|
408
|
+
function resolveFiles(options, gitService) {
|
|
414
409
|
return Effect.gen(function* () {
|
|
415
410
|
const env = yield* Env;
|
|
416
411
|
const fs = yield* FileSystem.FileSystem;
|
|
@@ -422,7 +417,7 @@ function resolveFiles(options, ignoreReader, gitService) {
|
|
|
422
417
|
else candidates = [...yield* Effect.mapError(gitService.changedFiles(options.baseRef), (e) => new FileResolverError({ message: `Git error: ${e}` }))];
|
|
423
418
|
const includeMatcher = options.configInclude?.length ? picomatch(options.configInclude) : void 0;
|
|
424
419
|
const ignoreMatcher = options.configIgnore?.length ? picomatch(options.configIgnore) : void 0;
|
|
425
|
-
return candidates.filter((f) => !
|
|
420
|
+
return candidates.filter((f) => !includeMatcher || includeMatcher(f)).filter((f) => !ignoreMatcher || !ignoreMatcher(f)).filter((f) => path.extname(f).length > 0).toSorted();
|
|
426
421
|
});
|
|
427
422
|
}
|
|
428
423
|
//#endregion
|
|
@@ -497,7 +492,7 @@ const collectChangedFiles = (cwd, baseRef) => Effect.all([
|
|
|
497
492
|
*
|
|
498
493
|
* @since 0.1.0
|
|
499
494
|
*/
|
|
500
|
-
var Git = class Git extends
|
|
495
|
+
var Git = class Git extends Context.Service()("agentlint/Git") {
|
|
501
496
|
static layer = Layer.effect(Git, Effect.gen(function* () {
|
|
502
497
|
const env = yield* Env;
|
|
503
498
|
const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
|
|
@@ -509,74 +504,6 @@ var Git = class Git extends ServiceMap.Service()("agentreview/Git") {
|
|
|
509
504
|
}));
|
|
510
505
|
};
|
|
511
506
|
//#endregion
|
|
512
|
-
//#region src/shared/infrastructure/ignore-reader.ts
|
|
513
|
-
/**
|
|
514
|
-
* Ignore-pattern aggregation service.
|
|
515
|
-
*
|
|
516
|
-
* Merges built-in ignore patterns (e.g. `node_modules`, `dist`) with
|
|
517
|
-
* the project's `.gitignore` to produce a single `isIgnored` predicate.
|
|
518
|
-
* Patterns are compiled once via `picomatch` and reused for every file.
|
|
519
|
-
*
|
|
520
|
-
* @module
|
|
521
|
-
* @since 0.1.0
|
|
522
|
-
*/
|
|
523
|
-
/**
|
|
524
|
-
* Paths that are always ignored regardless of user configuration.
|
|
525
|
-
*
|
|
526
|
-
* Covers common build output, package manager artifacts, source maps,
|
|
527
|
-
* lockfiles, and cache directories.
|
|
528
|
-
*
|
|
529
|
-
* @since 0.1.0
|
|
530
|
-
* @category constants
|
|
531
|
-
*/
|
|
532
|
-
const BUILTIN_IGNORE_PATTERNS = [
|
|
533
|
-
"node_modules/**",
|
|
534
|
-
"dist/**",
|
|
535
|
-
"build/**",
|
|
536
|
-
".git/**",
|
|
537
|
-
".next/**",
|
|
538
|
-
".cache/**",
|
|
539
|
-
"coverage/**",
|
|
540
|
-
"**/*.min.js",
|
|
541
|
-
"**/*.min.css",
|
|
542
|
-
"**/*.map",
|
|
543
|
-
"**/*.lock",
|
|
544
|
-
"**/*.log",
|
|
545
|
-
"**/*.tsbuildinfo",
|
|
546
|
-
".agentreview-state"
|
|
547
|
-
];
|
|
548
|
-
/**
|
|
549
|
-
* Parse a `.gitignore`-style file into an array of glob patterns.
|
|
550
|
-
* Strips blank lines and comments (lines starting with `#`).
|
|
551
|
-
*
|
|
552
|
-
* @since 0.1.0
|
|
553
|
-
* @category internals
|
|
554
|
-
*/
|
|
555
|
-
function parseIgnoreFile(content) {
|
|
556
|
-
return content.split("\n").map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
|
|
557
|
-
}
|
|
558
|
-
/**
|
|
559
|
-
* Effect service that tests whether a file path should be ignored.
|
|
560
|
-
*
|
|
561
|
-
* Combines built-in patterns with `.gitignore` into a single
|
|
562
|
-
* `picomatch` matcher. Constructed once per run.
|
|
563
|
-
*
|
|
564
|
-
* @since 0.1.0
|
|
565
|
-
* @category services
|
|
566
|
-
*/
|
|
567
|
-
var IgnoreReader = class IgnoreReader extends ServiceMap.Service()("agentreview/IgnoreReader") {
|
|
568
|
-
static layer = Layer.unwrap(Effect.gen(function* () {
|
|
569
|
-
const env = yield* Env;
|
|
570
|
-
const fs = yield* FileSystem.FileSystem;
|
|
571
|
-
const path = yield* Path.Path;
|
|
572
|
-
const { cwd } = env;
|
|
573
|
-
const gitignorePath = path.resolve(cwd, ".gitignore");
|
|
574
|
-
const gitignorePatterns = (yield* fs.exists(gitignorePath).pipe(Effect.orElseSucceed(() => false))) ? parseIgnoreFile(yield* fs.readFileString(gitignorePath).pipe(Effect.orElseSucceed(() => ""))) : [];
|
|
575
|
-
const matcher = picomatch([...BUILTIN_IGNORE_PATTERNS, ...gitignorePatterns], { dot: true });
|
|
576
|
-
return Layer.succeed(IgnoreReader, IgnoreReader.of({ isIgnored: (filepath) => matcher(filepath) }));
|
|
577
|
-
}));
|
|
578
|
-
};
|
|
579
|
-
//#endregion
|
|
580
507
|
//#region src/shared/infrastructure/parser.ts
|
|
581
508
|
/**
|
|
582
509
|
* Tree-sitter WASM parser.
|
|
@@ -618,7 +545,7 @@ const GRAMMAR_FILES = HashMap.make(["typescript", "tree-sitter-typescript.wasm"]
|
|
|
618
545
|
* @since 0.1.0
|
|
619
546
|
* @category services
|
|
620
547
|
*/
|
|
621
|
-
var Parser$1 = class Parser$1 extends
|
|
548
|
+
var Parser$1 = class Parser$1 extends Context.Service()("agentlint/Parser") {
|
|
622
549
|
/** Default layer — lazily initializes WASM and caches grammars. */
|
|
623
550
|
static layer = Layer.effect(Parser$1, Effect.gen(function* () {
|
|
624
551
|
const env = yield* Env;
|
|
@@ -681,16 +608,16 @@ var Parser$1 = class Parser$1 extends ServiceMap.Service()("agentreview/Parser")
|
|
|
681
608
|
* @module
|
|
682
609
|
*/
|
|
683
610
|
/**
|
|
684
|
-
* Regex matching `
|
|
611
|
+
* Regex matching `agentlint-ignore` comments.
|
|
685
612
|
*
|
|
686
613
|
* Captures an optional rule name after the directive:
|
|
687
|
-
* - `//
|
|
688
|
-
* - `//
|
|
614
|
+
* - `// agentlint-ignore` → suppresses all rules on the next line
|
|
615
|
+
* - `// agentlint-ignore my-rule` → suppresses only `my-rule`
|
|
689
616
|
*
|
|
690
617
|
* @since 0.1.0
|
|
691
618
|
* @category constants
|
|
692
619
|
*/
|
|
693
|
-
const IGNORE_PATTERN = /
|
|
620
|
+
const IGNORE_PATTERN = /agentlint-ignore(?:\s+(\S+))?/;
|
|
694
621
|
/**
|
|
695
622
|
* Walk files with the given rules, collecting all flags.
|
|
696
623
|
*
|
|
@@ -760,7 +687,7 @@ function walkFile(tree, rules) {
|
|
|
760
687
|
*
|
|
761
688
|
* Maps every supported file extension to the grammar name used by
|
|
762
689
|
* the parser service. This is the single source of truth for which
|
|
763
|
-
* file types
|
|
690
|
+
* file types agentlint can analyze.
|
|
764
691
|
*
|
|
765
692
|
* Uses Effect `HashMap` for an immutable, structurally-equal lookup table.
|
|
766
693
|
*
|
|
@@ -804,7 +731,6 @@ const collectFlags = Effect.fn("collectFlags")(function* (options) {
|
|
|
804
731
|
const env = yield* Env;
|
|
805
732
|
const fs = yield* FileSystem.FileSystem;
|
|
806
733
|
const path = yield* Path.Path;
|
|
807
|
-
const ignoreReader = yield* IgnoreReader;
|
|
808
734
|
const gitService = yield* Git;
|
|
809
735
|
const parserService = yield* Parser$1;
|
|
810
736
|
const config = yield* configLoader.load();
|
|
@@ -824,7 +750,7 @@ const collectFlags = Effect.fn("collectFlags")(function* (options) {
|
|
|
824
750
|
configInclude: includePatterns,
|
|
825
751
|
configIgnore: ignorePatterns,
|
|
826
752
|
positionalFiles: options.files.length > 0 ? [...options.files] : void 0
|
|
827
|
-
},
|
|
753
|
+
}, gitService);
|
|
828
754
|
if (files.length === 0) return {
|
|
829
755
|
flags: [],
|
|
830
756
|
noMatchingRules: false
|
|
@@ -980,19 +906,19 @@ var InitResult = class extends Schema.TaggedClass()("InitResult", {
|
|
|
980
906
|
* @since 0.1.0
|
|
981
907
|
*/
|
|
982
908
|
/**
|
|
983
|
-
* Minimal starter config written by `
|
|
909
|
+
* Minimal starter config written by `agentlint init`.
|
|
984
910
|
*
|
|
985
911
|
* @since 0.1.0
|
|
986
912
|
* @category constants
|
|
987
913
|
*/
|
|
988
|
-
const STARTER_CONFIG = `import { defineConfig } from "
|
|
914
|
+
const STARTER_CONFIG = `import { defineConfig } from "agentlint"
|
|
989
915
|
|
|
990
916
|
export default defineConfig({
|
|
991
917
|
include: ["src/**/*.{ts,tsx}"],
|
|
992
918
|
rules: {},
|
|
993
919
|
})
|
|
994
920
|
`;
|
|
995
|
-
const SKILLS_ADD_CMD = "npx skills@latest add aurelienbobenrieth/
|
|
921
|
+
const SKILLS_ADD_CMD = "npx skills@latest add aurelienbobenrieth/agentlint";
|
|
996
922
|
const INTENT_INSTALL_CMD = "npx @tanstack/intent install";
|
|
997
923
|
/**
|
|
998
924
|
* Detect which skill installation method is most likely appropriate.
|
|
@@ -1014,28 +940,28 @@ const initHandler = Effect.fn("initHandler")(function* (_command) {
|
|
|
1014
940
|
const env = yield* Env;
|
|
1015
941
|
const fs = yield* FileSystem.FileSystem;
|
|
1016
942
|
const path = yield* Path.Path;
|
|
1017
|
-
const configPath = path.resolve(env.cwd, "
|
|
943
|
+
const configPath = path.resolve(env.cwd, "agentlint.config.ts");
|
|
1018
944
|
const gitignorePath = path.resolve(env.cwd, ".gitignore");
|
|
1019
945
|
const configCreated = !(yield* fs.exists(configPath));
|
|
1020
946
|
if (configCreated) yield* fs.writeFileString(configPath, STARTER_CONFIG);
|
|
1021
947
|
let gitignoreUpdated = false;
|
|
1022
948
|
if (yield* fs.exists(gitignorePath)) {
|
|
1023
949
|
const content = yield* fs.readFileString(gitignorePath);
|
|
1024
|
-
if (!content.includes(".
|
|
950
|
+
if (!content.includes(".agentlint-state")) {
|
|
1025
951
|
const separator = content.endsWith("\n") ? "" : "\n";
|
|
1026
|
-
yield* fs.writeFileString(gitignorePath, content + separator + "\n#
|
|
952
|
+
yield* fs.writeFileString(gitignorePath, content + separator + "\n# agentlint local state\n.agentlint-state\n");
|
|
1027
953
|
gitignoreUpdated = true;
|
|
1028
954
|
}
|
|
1029
955
|
} else {
|
|
1030
|
-
yield* fs.writeFileString(gitignorePath, "#
|
|
956
|
+
yield* fs.writeFileString(gitignorePath, "# agentlint local state\n.agentlint-state\n");
|
|
1031
957
|
gitignoreUpdated = true;
|
|
1032
958
|
}
|
|
1033
959
|
const lines = [];
|
|
1034
|
-
if (configCreated) lines.push("✓ Created
|
|
1035
|
-
else lines.push("·
|
|
1036
|
-
if (gitignoreUpdated) lines.push("✓ Added .
|
|
960
|
+
if (configCreated) lines.push("✓ Created agentlint.config.ts");
|
|
961
|
+
else lines.push("· agentlint.config.ts already exists — skipped");
|
|
962
|
+
if (gitignoreUpdated) lines.push("✓ Added .agentlint-state to .gitignore");
|
|
1037
963
|
const skillCmd = (yield* detectSkillMethod(env.cwd)) === "intent" ? INTENT_INSTALL_CMD : SKILLS_ADD_CMD;
|
|
1038
|
-
lines.push("", "Next steps:", " 1. Add rules to your config", ` 2. Install the
|
|
964
|
+
lines.push("", "Next steps:", " 1. Add rules to your config", ` 2. Install the agentlint skill for your AI agents:`, ` ${skillCmd}`, " 3. Run: npx agentlint check --all");
|
|
1039
965
|
return new InitResult({
|
|
1040
966
|
created: configCreated,
|
|
1041
967
|
message: lines.join("\n")
|
|
@@ -1116,7 +1042,7 @@ const reviewHandler = Effect.fn("reviewHandler")(function* (command) {
|
|
|
1116
1042
|
const stateStore = yield* StateStore;
|
|
1117
1043
|
if (command.reset) {
|
|
1118
1044
|
yield* stateStore.reset();
|
|
1119
|
-
return new ReviewResult({ message: "Cleared .
|
|
1045
|
+
return new ReviewResult({ message: "Cleared .agentlint-state" });
|
|
1120
1046
|
}
|
|
1121
1047
|
if (command.all) {
|
|
1122
1048
|
const allFlags = (yield* collectFlags({
|
|
@@ -1136,9 +1062,9 @@ const reviewHandler = Effect.fn("reviewHandler")(function* (command) {
|
|
|
1136
1062
|
}
|
|
1137
1063
|
return new ReviewResult({ message: [
|
|
1138
1064
|
"Usage:",
|
|
1139
|
-
"
|
|
1140
|
-
"
|
|
1141
|
-
"
|
|
1065
|
+
" agentlint review <hash...> Mark specific flags as reviewed",
|
|
1066
|
+
" agentlint review --all Mark all current flags as reviewed",
|
|
1067
|
+
" agentlint review --reset Wipe the state file"
|
|
1142
1068
|
].join("\n") });
|
|
1143
1069
|
});
|
|
1144
1070
|
//#endregion
|
|
@@ -1206,7 +1132,7 @@ const formatReport = Effect.fn("formatReport")(function* (flags, rulesMeta, opti
|
|
|
1206
1132
|
const env = yield* Env;
|
|
1207
1133
|
const path = yield* Path.Path;
|
|
1208
1134
|
const ansi = makeAnsi(env.noColor);
|
|
1209
|
-
if (flags.length === 0) return `${ansi.bold("
|
|
1135
|
+
if (flags.length === 0) return `${ansi.bold("agentlint")} ${ansi.dim(`v${options.version}`)} ${ansi.dim("-")} no rules triggered.`;
|
|
1210
1136
|
const { cwd } = env;
|
|
1211
1137
|
const lines = [];
|
|
1212
1138
|
const grouped = groupBy(flags, (f) => f.ruleName);
|
|
@@ -1251,7 +1177,7 @@ const formatReport = Effect.fn("formatReport")(function* (flags, rulesMeta, opti
|
|
|
1251
1177
|
//#endregion
|
|
1252
1178
|
//#region src/bin.ts
|
|
1253
1179
|
/**
|
|
1254
|
-
* CLI entry point for `
|
|
1180
|
+
* CLI entry point for `agentlint`.
|
|
1255
1181
|
*
|
|
1256
1182
|
* Thin adapter that translates CLI arguments into feature commands,
|
|
1257
1183
|
* dispatches to the appropriate handler, and formats the result
|
|
@@ -1290,17 +1216,17 @@ const check = Command.make("check", {
|
|
|
1290
1216
|
return;
|
|
1291
1217
|
}
|
|
1292
1218
|
if (result.totalFlags === 0) {
|
|
1293
|
-
yield* Console.log(`
|
|
1219
|
+
yield* Console.log(`agentlint v0.1.1 - no rules triggered.`);
|
|
1294
1220
|
return;
|
|
1295
1221
|
}
|
|
1296
1222
|
const cfg = yield* (yield* ConfigLoader).load();
|
|
1297
1223
|
const rulesMeta = HashMap.fromIterable(Object.entries(cfg.rules).map(([name, rule]) => [name, rule.meta]));
|
|
1298
1224
|
const output = yield* formatReport(result.flags, rulesMeta, {
|
|
1299
1225
|
dryRun: config.dryRun,
|
|
1300
|
-
version: "0.1.
|
|
1226
|
+
version: "0.1.1"
|
|
1301
1227
|
});
|
|
1302
1228
|
yield* Console.log(output);
|
|
1303
|
-
if (result.filteredCount > 0) yield* Console.log(` (${result.filteredCount} reviewed flag(s) hidden — run
|
|
1229
|
+
if (result.filteredCount > 0) yield* Console.log(` (${result.filteredCount} reviewed flag(s) hidden — run agentlint review --reset to clear)`);
|
|
1304
1230
|
if (result.flags.length > 0) env.setExitCode(1);
|
|
1305
1231
|
});
|
|
1306
1232
|
}).pipe(Command.withDescription("Scan files and output report for AI agents"));
|
|
@@ -1326,7 +1252,7 @@ const list = Command.make("list", {}, () => Effect.gen(function* () {
|
|
|
1326
1252
|
const init = Command.make("init", {}, () => Effect.gen(function* () {
|
|
1327
1253
|
const result = yield* initHandler(new InitCommand({}));
|
|
1328
1254
|
yield* Console.log(result.message);
|
|
1329
|
-
})).pipe(Command.withDescription("Create
|
|
1255
|
+
})).pipe(Command.withDescription("Create agentlint.config.ts and set up agent skill discovery"));
|
|
1330
1256
|
/** The `review` subcommand — manage reviewed-flag state. */
|
|
1331
1257
|
const review = Command.make("review", {
|
|
1332
1258
|
hashes: Argument.string("hashes").pipe(Argument.withDescription("Flag hashes to mark as reviewed"), Argument.variadic()),
|
|
@@ -1340,14 +1266,14 @@ const review = Command.make("review", {
|
|
|
1340
1266
|
}));
|
|
1341
1267
|
yield* Console.log(result.message);
|
|
1342
1268
|
})).pipe(Command.withDescription("Mark flags as reviewed (filters them from check output)"));
|
|
1343
|
-
const
|
|
1269
|
+
const agentlint = Command.make("agentlint").pipe(Command.withDescription("Deterministic linting for AI agents"), Command.withSubcommands([
|
|
1344
1270
|
check,
|
|
1345
1271
|
list,
|
|
1346
1272
|
init,
|
|
1347
1273
|
review
|
|
1348
1274
|
]));
|
|
1349
|
-
const AppLayer = Layer.mergeAll(ConfigLoader.layer, Parser$1.layer, Git.layer,
|
|
1350
|
-
const program = Command.run(
|
|
1275
|
+
const AppLayer = Layer.mergeAll(ConfigLoader.layer, Parser$1.layer, Git.layer, StateStore.layer).pipe(Layer.provideMerge(NodeServices.layer), Layer.provideMerge(Env.layer));
|
|
1276
|
+
const program = Command.run(agentlint, { version: "0.1.1" }).pipe(Effect.provide(AppLayer));
|
|
1351
1277
|
NodeRuntime.runMain(program);
|
|
1352
1278
|
//#endregion
|
|
1353
1279
|
export {};
|
package/dist/bin.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bin.mjs","names":["#filename","#source","Parser","TSParser","Parser","Parser"],"sources":["../src/config/env.ts","../src/shared/infrastructure/config-loader.ts","../src/shared/infrastructure/state-store.ts","../src/domain/hash.ts","../src/domain/rule-context.ts","../src/shared/pipeline/file-resolver.ts","../src/shared/infrastructure/git.ts","../src/shared/infrastructure/ignore-reader.ts","../src/shared/infrastructure/parser.ts","../src/shared/pipeline/tree-walker.ts","../src/shared/pipeline/language-map.ts","../src/shared/pipeline/collect-flags.ts","../src/features/check/request.ts","../src/features/check/handler.ts","../src/features/init/request.ts","../src/features/init/handler.ts","../src/features/list/request.ts","../src/features/list/handler.ts","../src/features/review/request.ts","../src/features/review/handler.ts","../src/cli/reporter.ts","../src/bin.ts"],"sourcesContent":["/**\n * Centralised process / environment access.\n *\n * **This is the only module in the codebase that may touch `process.*`.**\n *\n * Every other module that needs the working directory, TTY state, colour\n * preference, or exit-code control must depend on the `Env` service\n * instead of reaching into `process` directly.\n *\n * The layer is built once at startup with `Layer.sync` (no external\n * dependencies), so it can be provided before every other service layer.\n *\n * @module\n * @since 0.1.0\n */\n\nimport { Layer } from \"effect\";\nimport * as ServiceMap from \"effect/ServiceMap\";\n\n/**\n * Read-only snapshot of the runtime environment.\n *\n * @since 0.1.0\n * @category services\n */\nexport class Env extends ServiceMap.Service<\n Env,\n {\n /** Current working directory, captured at startup. */\n readonly cwd: string;\n /** `true` when ANSI colour codes should be suppressed (`NO_COLOR` or non-TTY). */\n readonly noColor: boolean;\n /** `true` when stdout is an interactive terminal. */\n readonly isTTY: boolean;\n /** Set the process exit code (non-zero signals failure to the shell). */\n setExitCode(code: number): void;\n }\n>()(\"agentreview/Env\") {\n /**\n * Default layer — reads from `process` globals exactly once.\n *\n * @since 0.1.0\n * @category layers\n */\n static readonly layer: Layer.Layer<Env> = Layer.sync(Env, () => {\n /* eslint-disable n/no-process-env -- single authorised access point */\n const isTTY = process.stdout.isTTY ?? false;\n return Env.of({\n cwd: process.cwd(),\n noColor: !!process.env[\"NO_COLOR\"] || !isTTY,\n isTTY,\n setExitCode: (code) => {\n process.exitCode = code;\n },\n });\n /* eslint-enable n/no-process-env */\n });\n}\n","/**\n * Configuration file discovery and loading.\n *\n * Searches the current working directory for a config file, imports it\n * via `jiti` (for TypeScript support without pre-compilation), and\n * validates the exported shape.\n *\n * **Search order**: `agentreview.config.ts` → `.js` → `.mts` → `.mjs`.\n * The first match wins.\n *\n * @module\n * @since 0.1.0\n */\n\nimport { Effect, FileSystem, Layer, Path, Schema } from \"effect\";\nimport * as ServiceMap from \"effect/ServiceMap\";\nimport { Env } from \"../../config/env.js\";\nimport type { AgentReviewConfig } from \"../../domain/config.js\";\n\n/**\n * Raised when the config file is missing, malformed, or fails to import.\n *\n * @since 0.1.0\n * @category errors\n */\nexport class ConfigError extends Schema.TaggedErrorClass<ConfigError>()(\"ConfigError\", {\n message: Schema.String,\n}) {}\n\n/**\n * Candidate config file names, checked in order.\n *\n * @since 0.1.0\n * @category constants\n */\nconst CONFIG_NAMES = [\n \"agentreview.config.ts\",\n \"agentreview.config.js\",\n \"agentreview.config.mts\",\n \"agentreview.config.mjs\",\n];\n\n/**\n * Discover the config file path by checking candidates in order.\n *\n * @since 0.1.0\n * @category internals\n */\nconst discoverConfig = (fs: FileSystem.FileSystem, path: Path.Path, cwd: string): Effect.Effect<string, ConfigError> =>\n Effect.gen(function* () {\n for (const name of CONFIG_NAMES) {\n const candidate = path.resolve(cwd, name);\n if (yield* fs.exists(candidate).pipe(Effect.orElseSucceed(() => false))) {\n return candidate;\n }\n }\n return yield* new ConfigError({\n message: `No agentreview config found. Create agentreview.config.ts in ${cwd}`,\n });\n });\n\n/**\n * Effect service that discovers and loads the agentreview config file.\n *\n * Uses `jiti` under the hood so TypeScript configs work without a\n * separate compilation step.\n *\n * @example\n * ```ts\n * import { Console, Effect } from \"effect\"\n * import { ConfigLoader } from \"./infrastructure/config-loader.js\"\n *\n * const program = Effect.gen(function* () {\n * const loader = yield* ConfigLoader\n * const config = yield* loader.load()\n * yield* Console.log(Object.keys(config.rules))\n * })\n * ```\n *\n * @since 0.1.0\n * @category services\n */\nexport class ConfigLoader extends ServiceMap.Service<\n ConfigLoader,\n {\n /** Discover and import the config file from the working directory. */\n load(): Effect.Effect<AgentReviewConfig, ConfigError>;\n }\n>()(\"agentreview/ConfigLoader\") {\n static readonly layer: Layer.Layer<ConfigLoader, never, FileSystem.FileSystem | Path.Path | Env> = Layer.effect(\n ConfigLoader,\n Effect.gen(function* () {\n const env = yield* Env;\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n\n return ConfigLoader.of({\n load: () =>\n Effect.gen(function* () {\n const configPath = yield* discoverConfig(fs, path, env.cwd);\n\n const config = yield* Effect.tryPromise({\n try: async () => {\n const { createJiti } = await import(\"jiti\");\n const jiti = createJiti(import.meta.url, {\n interopDefault: true,\n });\n const loaded = await jiti.import(configPath);\n return (loaded as { default?: AgentReviewConfig }).default ?? (loaded as AgentReviewConfig);\n },\n catch: (error) =>\n new ConfigError({\n message: error instanceof Error ? error.message : String(error),\n }),\n });\n\n if (!config || typeof config !== \"object\" || !(\"rules\" in config)) {\n return yield* new ConfigError({\n message: `Invalid config at ${configPath}: must export an object with a \"rules\" field`,\n });\n }\n\n return config;\n }),\n });\n }),\n );\n}\n","/**\n * Local state store for tracking reviewed flags.\n *\n * Manages a `.agentreview-state` file in the project root that stores\n * hashes of flags that have been reviewed. This file is intended to\n * be **gitignored** — it is per-developer scratch state for tracking\n * progress during review sweeps.\n *\n * **Caveats**:\n * - Hashes encode file path, line, column, and message. Editing code\n * above a reviewed flag shifts its position and invalidates the hash.\n * This is by design — changed context should be re-reviewed.\n * - Stale hashes (from flags that no longer exist) accumulate harmlessly.\n * Use `agentreview review --reset` to start fresh.\n *\n * @module\n * @since 0.1.0\n */\n\nimport { Effect, FileSystem, HashSet, Layer, Path } from \"effect\";\nimport * as ServiceMap from \"effect/ServiceMap\";\nimport { Env } from \"../../config/env.js\";\n\n/**\n * The filename used for local review state.\n *\n * @since 0.1.0\n * @category constants\n */\nconst STATE_FILENAME = \".agentreview-state\";\n\n/**\n * Parse the state file into a set of hashes.\n * Tolerates blank lines and `#`-prefixed comments.\n *\n * @since 0.1.0\n * @category internals\n */\nfunction parseStateFile(content: string): HashSet.HashSet<string> {\n let hashes: HashSet.HashSet<string> = HashSet.empty();\n for (const raw of content.split(\"\\n\")) {\n const line = raw.trim();\n if (line.length > 0 && !line.startsWith(\"#\")) {\n hashes = HashSet.add(hashes, line);\n }\n }\n return hashes;\n}\n\n/**\n * Serialize a set of hashes into file content.\n *\n * @since 0.1.0\n * @category internals\n */\nfunction serializeHashes(hashes: HashSet.HashSet<string>): string {\n return [...hashes].join(\"\\n\") + \"\\n\";\n}\n\n/**\n * Effect service for loading and persisting reviewed-flag state.\n *\n * @since 0.1.0\n * @category services\n */\nexport class StateStore extends ServiceMap.Service<\n StateStore,\n {\n /** Load reviewed hashes from `.agentreview-state`. Returns an empty set if the file is missing. */\n load(): Effect.Effect<HashSet.HashSet<string>>;\n /** Append one or more hashes to `.agentreview-state`, deduplicating against existing entries. */\n append(hashes: ReadonlyArray<string>): Effect.Effect<void>;\n /** Delete the `.agentreview-state` file entirely. */\n reset(): Effect.Effect<void>;\n }\n>()(\"agentreview/StateStore\") {\n /**\n * Default layer — resolves the state file path from `Env.cwd`.\n *\n * @since 0.1.0\n * @category layers\n */\n static readonly layer: Layer.Layer<StateStore, never, FileSystem.FileSystem | Path.Path | Env> = Layer.unwrap(\n Effect.gen(function* () {\n const env = yield* Env;\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const statePath = path.resolve(env.cwd, STATE_FILENAME);\n\n return Layer.succeed(\n StateStore,\n StateStore.of({\n load: () =>\n fs.exists(statePath).pipe(\n Effect.orElseSucceed(() => false),\n Effect.flatMap((exists) =>\n exists\n ? fs.readFileString(statePath).pipe(\n Effect.map(parseStateFile),\n Effect.orElseSucceed(() => HashSet.empty<string>()),\n )\n : Effect.succeed(HashSet.empty<string>()),\n ),\n ),\n\n append: (hashes) =>\n fs.exists(statePath).pipe(\n Effect.orElseSucceed(() => false),\n Effect.flatMap((exists) =>\n exists\n ? fs.readFileString(statePath).pipe(\n Effect.map(parseStateFile),\n Effect.orElseSucceed(() => HashSet.empty<string>()),\n )\n : Effect.succeed(HashSet.empty<string>()),\n ),\n Effect.map((existing) => hashes.reduce((acc, h) => HashSet.add(acc, h), existing)),\n Effect.flatMap((merged) =>\n fs.writeFileString(statePath, serializeHashes(merged)).pipe(Effect.orElseSucceed(() => {})),\n ),\n ),\n\n reset: () =>\n fs.exists(statePath).pipe(\n Effect.orElseSucceed(() => false),\n Effect.flatMap((exists) =>\n exists ? fs.remove(statePath).pipe(Effect.orElseSucceed(() => {})) : Effect.void,\n ),\n ),\n }),\n );\n }),\n );\n}\n","/**\n * FNV-1a hashing utility.\n *\n * Produces a 7-character hex digest used for stable, deterministic\n * flag identification. The hash encodes rule name, file path, position,\n * and message so that identical matches across runs share the same id.\n *\n * @see https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function\n *\n * @module\n * @since 0.1.0\n */\n\n/**\n * FNV-1a 32-bit offset basis.\n *\n * @since 0.1.0\n * @category constants\n */\nconst FNV_OFFSET_BASIS = 0x811c9dc5;\n\n/**\n * FNV-1a 32-bit prime multiplier.\n *\n * @since 0.1.0\n * @category constants\n */\nconst FNV_PRIME = 0x01000193;\n\n/**\n * Compute a 7-character hex FNV-1a hash of `input`.\n *\n * The result is the first 7 hex characters of the unsigned 32-bit\n * FNV-1a digest — short enough for display, long enough to avoid\n * collisions in typical lint runs.\n *\n * @example\n * ```ts\n * import { fnv1a7 } from \"./utils/hash.js\"\n *\n * fnv1a7(\"my-rule:src/index.ts:10:1:message\") // => \"a3f4b2c\"\n * ```\n *\n * @since 0.1.0\n * @category constructors\n */\nexport function fnv1a7(input: string): string {\n let hash = FNV_OFFSET_BASIS;\n for (let i = 0; i < input.length; i++) {\n hash ^= input.charCodeAt(i);\n hash = Math.imul(hash, FNV_PRIME);\n }\n return (hash >>> 0).toString(16).padStart(8, \"0\").slice(0, 7);\n}\n","/**\n * Rule context — the interface rules use to interact with the runner.\n *\n * Provides file metadata, source access, and the {@link RuleContext.flag}\n * method for recording matches.\n *\n * @module\n * @since 0.1.0\n */\n\nimport { fnv1a7 } from \"./hash.js\";\nimport { type FlagOptions, FlagRecord } from \"./flag.js\";\n\n/**\n * Context object passed to `createOnce`. Available throughout the rule's lifecycle.\n *\n * @since 0.1.0\n * @category models\n */\nexport interface RuleContext {\n /** Absolute path of the current file being analyzed. */\n getFilename(): string;\n /** Full source content of the current file. */\n getSourceCode(): string;\n /**\n * Lines around the given 1-based line number, formatted with line numbers.\n * @param line 1-based line number\n * @param radius number of lines above/below to include (default 10)\n */\n getLinesAround(line: number, radius?: number): string;\n /** Record a match for the output report. */\n flag(options: FlagOptions): void;\n}\n\n/**\n * Internal implementation of {@link RuleContext}.\n *\n * Tracks the current file, accumulates flags, and provides source\n * access helpers. The check command calls {@link setFile} before each\n * file and {@link drainFlags} after the tree walk to collect results.\n *\n * @since 0.1.0\n * @category internals\n */\nexport class RuleContextImpl implements RuleContext {\n readonly ruleName: string;\n readonly flags: FlagRecord[] = [];\n\n #filename = \"\";\n #source = \"\";\n\n constructor(ruleName: string) {\n this.ruleName = ruleName;\n }\n\n /**\n * Set the current file context. Called by the check command before\n * each file is walked.\n */\n setFile(filename: string, source: string): void {\n this.#filename = filename;\n this.#source = source;\n }\n\n /**\n * Remove and return all accumulated flags. Called after the tree\n * walk for each file to collect results.\n */\n drainFlags(): FlagRecord[] {\n return this.flags.splice(0);\n }\n\n getFilename(): string {\n return this.#filename;\n }\n\n getSourceCode(): string {\n return this.#source;\n }\n\n getLinesAround(line: number, radius = 10): string {\n const lines = this.#source.split(\"\\n\");\n const start = Math.max(0, line - 1 - radius);\n const end = Math.min(lines.length, line + radius);\n return lines\n .slice(start, end)\n .map((l, i) => `${String(start + i + 1).padStart(4)} | ${l}`)\n .join(\"\\n\");\n }\n\n flag(options: FlagOptions): void {\n const line = options.node.startPosition.row + 1;\n const col = options.node.startPosition.column + 1;\n const sourceLines = this.#source.split(\"\\n\");\n const rawLine = sourceLines[line - 1] ?? \"\";\n const trimmed = rawLine.trim();\n const sourceSnippet = trimmed.length > 100 ? trimmed.slice(0, 97) + \"...\" : trimmed;\n\n const hash = fnv1a7(`${this.ruleName}:${this.#filename}:${line}:${col}:${options.message}`);\n\n this.flags.push(\n new FlagRecord({\n ruleName: this.ruleName,\n filename: this.#filename,\n line,\n col,\n message: options.message,\n sourceSnippet,\n hash,\n instruction: options.instruction,\n suggest: options.suggest,\n }),\n );\n }\n}\n","/**\n * File resolution service.\n *\n * Determines which files to lint by applying the 6-layer filter pipeline:\n * 1. Candidate files (from git diff or all files)\n * 2. Built-in ignores\n * 3. .gitignore\n * 4. .agentreviewignore\n * 5. Config include/ignore\n * (Steps 2-4 are handled by IgnoreReader)\n *\n * Per-rule filtering (languages, include, ignore) is done by the check command.\n *\n * @module\n */\n\nimport { Effect, FileSystem, HashSet, Path, Schema } from \"effect\";\nimport { Env } from \"../../config/env.js\";\nimport picomatch from \"picomatch\";\n\n/**\n * Raised when file resolution fails — e.g. a git error bubbling up\n * from the changed-files query.\n *\n * @since 0.1.0\n * @category errors\n */\nexport class FileResolverError extends Schema.TaggedErrorClass<FileResolverError>()(\"FileResolverError\", {\n message: Schema.String,\n}) {}\n\n/**\n * Options controlling which files enter the lint pipeline.\n *\n * @since 0.1.0\n * @category models\n */\nexport const ResolveOptions = Schema.Struct({\n /** When `true`, scan all files instead of only git-changed files. */\n all: Schema.Boolean,\n /** Git ref to diff against. Defaults to the detected default branch. */\n baseRef: Schema.optional(Schema.String),\n /** Global include globs from the config file. */\n configInclude: Schema.optional(Schema.Array(Schema.String)),\n /** Global ignore globs from the config file. */\n configIgnore: Schema.optional(Schema.Array(Schema.String)),\n /** Explicit file paths passed as CLI positional arguments. */\n positionalFiles: Schema.optional(Schema.Array(Schema.String)),\n});\n\n/** @since 0.1.0 */\nexport type ResolveOptions = Schema.Schema.Type<typeof ResolveOptions>;\n\n/** Directories that are always skipped during recursive listing. */\nconst SKIP_DIRS: HashSet.HashSet<string> = HashSet.make(\"node_modules\", \".git\", \"dist\");\n\n/**\n * Recursively list all files under `dir`, returning paths relative to `base`.\n *\n * Skips `node_modules`, `.git`, and `dist` directories. Errors (e.g.\n * permission denied) are silently swallowed.\n *\n * Uses the Effect `FileSystem` and `Path` services for cross-platform\n * file system access.\n *\n * @since 0.1.0\n * @category internals\n */\nfunction listAllFiles(dir: string, base: string, fs: FileSystem.FileSystem, path: Path.Path): Effect.Effect<string[]> {\n return Effect.gen(function* () {\n const entries = yield* fs.readDirectory(dir);\n const results: string[] = [];\n\n for (const name of entries) {\n if (HashSet.has(SKIP_DIRS, name)) continue;\n\n const fullPath = path.resolve(dir, name);\n const info = yield* fs.stat(fullPath);\n const relPath = path.relative(base, fullPath).replace(/\\\\/g, \"/\");\n\n if (info.type === \"Directory\") {\n results.push(...(yield* listAllFiles(fullPath, base, fs, path)));\n } else {\n results.push(relPath);\n }\n }\n\n return results;\n }).pipe(Effect.catch(() => Effect.succeed([] as string[])));\n}\n\n/**\n * Determine the final set of files to lint.\n *\n * Applies the multi-layer filter pipeline described in the module header,\n * then sorts the result alphabetically for deterministic output.\n *\n * @since 0.1.0\n * @category constructors\n */\nexport function resolveFiles(\n options: ResolveOptions,\n ignoreReader: { isIgnored(path: string): boolean },\n gitService: {\n changedFiles(baseRef?: string): Effect.Effect<ReadonlyArray<string>, any>;\n },\n): Effect.Effect<ReadonlyArray<string>, FileResolverError, FileSystem.FileSystem | Path.Path | Env> {\n return Effect.gen(function* () {\n const env = yield* Env;\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const { cwd } = env;\n let candidates: string[];\n\n if (options.positionalFiles && options.positionalFiles.length > 0) {\n candidates = [...options.positionalFiles];\n } else if (options.all) {\n candidates = yield* listAllFiles(cwd, cwd, fs, path);\n } else {\n const changed = yield* Effect.mapError(\n gitService.changedFiles(options.baseRef),\n (e) => new FileResolverError({ message: `Git error: ${e}` }),\n );\n candidates = [...changed];\n }\n\n const includeMatcher = options.configInclude?.length ? picomatch(options.configInclude as string[]) : undefined;\n const ignoreMatcher = options.configIgnore?.length ? picomatch(options.configIgnore as string[]) : undefined;\n\n return candidates\n .filter((f) => !ignoreReader.isIgnored(f))\n .filter((f) => !includeMatcher || includeMatcher(f))\n .filter((f) => !ignoreMatcher || !ignoreMatcher(f))\n .filter((f) => path.extname(f).length > 0)\n .toSorted();\n });\n}\n","/**\n * Git integration — default branch detection and changed file collection.\n *\n * @module\n * @since 0.1.0\n */\n\nimport { Effect, HashSet, Layer, Schema } from \"effect\";\nimport * as ServiceMap from \"effect/ServiceMap\";\nimport { Env } from \"../../config/env.js\";\nimport { ChildProcess, ChildProcessSpawner } from \"effect/unstable/process\";\n\n/**\n * Raised when a git operation fails — e.g. not a git repo,\n * invalid ref, or `git` binary not found.\n *\n * @since 0.1.0\n * @category errors\n */\nexport class GitError extends Schema.TaggedErrorClass<GitError>()(\"GitError\", {\n message: Schema.String,\n}) {}\n\n/**\n * Execute a git command and return trimmed stdout.\n *\n * Uses the array form of `ChildProcess.make` so that dynamic arguments\n * are properly tokenized.\n *\n * @since 0.1.0\n * @category internals\n */\nconst gitCmd = (args: string, cwd: string) =>\n Effect.gen(function* () {\n const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;\n return (yield* spawner.string(ChildProcess.make(\"git\", args.split(/\\s+/), { cwd }))).trim();\n });\n\n/**\n * Detect the default branch by checking whether `main` or `master` exists.\n * Falls back to `\"main\"` when neither can be verified.\n *\n * @since 0.1.0\n * @category internals\n */\nconst detectDefault = (cwd: string) =>\n gitCmd(\"rev-parse --verify main\", cwd).pipe(\n Effect.map(() => \"main\" as string),\n Effect.catch(() =>\n gitCmd(\"rev-parse --verify master\", cwd).pipe(\n Effect.map(() => \"master\" as string),\n Effect.catch(() => Effect.succeed(\"main\" as string)),\n ),\n ),\n );\n\n/**\n * Collect all files that differ from `baseRef`.\n *\n * Gathers the union of committed diffs, uncommitted changes, and\n * untracked files. Each source is caught so partial failures\n * (e.g. empty repo, no merge-base) are silently skipped.\n *\n * @since 0.1.0\n * @category internals\n */\nconst parseLines = (output: string): ReadonlyArray<string> =>\n output\n .split(\"\\n\")\n .map((f) => f.trim())\n .filter((f) => f.length > 0);\n\nconst collectChangedFiles = (cwd: string, baseRef: string) =>\n Effect.all([\n // Committed changes since merge-base\n gitCmd(`merge-base HEAD ${baseRef}`, cwd).pipe(\n Effect.flatMap((mergeBase) => gitCmd(`diff --name-only ${mergeBase}...HEAD`, cwd)),\n Effect.catch(() => Effect.succeed(\"\")),\n ),\n // Uncommitted changes\n gitCmd(\"diff --name-only HEAD\", cwd).pipe(Effect.catch(() => Effect.succeed(\"\"))),\n // Untracked files\n gitCmd(\"ls-files --others --exclude-standard\", cwd).pipe(Effect.catch(() => Effect.succeed(\"\"))),\n ]).pipe(\n Effect.map(([committed, uncommitted, untracked]) =>\n [\n ...HashSet.fromIterable([...parseLines(committed), ...parseLines(uncommitted), ...parseLines(untracked)]),\n ].toSorted(),\n ),\n );\n\n/**\n * @example\n * ```ts\n * import { Console, Effect } from \"effect\"\n * import { Git } from \"./infrastructure/git.js\"\n *\n * const program = Effect.gen(function* () {\n * const git = yield* Git\n * const branch = yield* git.detectDefaultBranch()\n * const changed = yield* git.changedFiles(branch)\n * yield* Console.log(`${changed.length} files changed since ${branch}`)\n * })\n * ```\n *\n * @since 0.1.0\n */\nexport class Git extends ServiceMap.Service<\n Git,\n {\n /** Detect whether the default branch is `main` or `master`. */\n detectDefaultBranch(): Effect.Effect<string, GitError>;\n /** Return sorted list of files changed relative to `baseRef` (defaults to the detected default branch). */\n changedFiles(baseRef?: string): Effect.Effect<ReadonlyArray<string>, GitError>;\n }\n>()(\"agentreview/Git\") {\n static readonly layer: Layer.Layer<Git, never, ChildProcessSpawner.ChildProcessSpawner | Env> = Layer.effect(\n Git,\n Effect.gen(function* () {\n const env = yield* Env;\n const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;\n const provide = <A, E>(effect: Effect.Effect<A, E, ChildProcessSpawner.ChildProcessSpawner>) =>\n Effect.provideService(effect, ChildProcessSpawner.ChildProcessSpawner, spawner);\n\n return Git.of({\n detectDefaultBranch: () =>\n provide(detectDefault(env.cwd)).pipe(Effect.mapError((e) => new GitError({ message: String(e) }))),\n\n changedFiles: (baseRef) =>\n (baseRef ? Effect.succeed(baseRef) : provide(detectDefault(env.cwd))).pipe(\n Effect.flatMap((base) => provide(collectChangedFiles(env.cwd, base))),\n Effect.mapError((e) => new GitError({ message: String(e) })),\n ),\n });\n }),\n );\n}\n","/**\n * Ignore-pattern aggregation service.\n *\n * Merges built-in ignore patterns (e.g. `node_modules`, `dist`) with\n * the project's `.gitignore` to produce a single `isIgnored` predicate.\n * Patterns are compiled once via `picomatch` and reused for every file.\n *\n * @module\n * @since 0.1.0\n */\n\nimport { Effect, FileSystem, Layer, Path } from \"effect\";\nimport * as ServiceMap from \"effect/ServiceMap\";\nimport { Env } from \"../../config/env.js\";\nimport picomatch from \"picomatch\";\n\n/**\n * Paths that are always ignored regardless of user configuration.\n *\n * Covers common build output, package manager artifacts, source maps,\n * lockfiles, and cache directories.\n *\n * @since 0.1.0\n * @category constants\n */\nconst BUILTIN_IGNORE_PATTERNS = [\n \"node_modules/**\",\n \"dist/**\",\n \"build/**\",\n \".git/**\",\n \".next/**\",\n \".cache/**\",\n \"coverage/**\",\n \"**/*.min.js\",\n \"**/*.min.css\",\n \"**/*.map\",\n \"**/*.lock\",\n \"**/*.log\",\n \"**/*.tsbuildinfo\",\n \".agentreview-state\",\n];\n\n/**\n * Parse a `.gitignore`-style file into an array of glob patterns.\n * Strips blank lines and comments (lines starting with `#`).\n *\n * @since 0.1.0\n * @category internals\n */\nfunction parseIgnoreFile(content: string): ReadonlyArray<string> {\n return content\n .split(\"\\n\")\n .map((line) => line.trim())\n .filter((line) => line.length > 0 && !line.startsWith(\"#\"));\n}\n\n/**\n * Effect service that tests whether a file path should be ignored.\n *\n * Combines built-in patterns with `.gitignore` into a single\n * `picomatch` matcher. Constructed once per run.\n *\n * @since 0.1.0\n * @category services\n */\nexport class IgnoreReader extends ServiceMap.Service<\n IgnoreReader,\n {\n /** Returns `true` if `filepath` matches any built-in or `.gitignore` pattern. */\n isIgnored(filepath: string): boolean;\n }\n>()(\"agentreview/IgnoreReader\") {\n static readonly layer: Layer.Layer<IgnoreReader, never, FileSystem.FileSystem | Path.Path | Env> = Layer.unwrap(\n Effect.gen(function* () {\n const env = yield* Env;\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const { cwd } = env;\n const gitignorePath = path.resolve(cwd, \".gitignore\");\n\n const gitignoreExists = yield* fs.exists(gitignorePath).pipe(Effect.orElseSucceed(() => false));\n const gitignorePatterns: ReadonlyArray<string> = gitignoreExists\n ? parseIgnoreFile(yield* fs.readFileString(gitignorePath).pipe(Effect.orElseSucceed(() => \"\")))\n : [];\n\n const allPatterns = [...BUILTIN_IGNORE_PATTERNS, ...gitignorePatterns];\n const matcher = picomatch(allPatterns, { dot: true });\n\n return Layer.succeed(\n IgnoreReader,\n IgnoreReader.of({\n isIgnored: (filepath) => matcher(filepath),\n }),\n );\n }),\n );\n}\n","/**\n * Tree-sitter WASM parser.\n *\n * WASM init is lazy — the first `parse` call triggers initialization.\n * Grammars are cached after first load.\n *\n * @module\n * @since 0.1.0\n */\n\nimport { Effect, FileSystem, HashMap, Layer, Option, Path, Schema } from \"effect\";\nimport * as ServiceMap from \"effect/ServiceMap\";\nimport { Env } from \"../../config/env.js\";\nimport { Language, Parser as TSParser, type Tree } from \"web-tree-sitter\";\n\n/**\n * Raised when parsing fails — e.g. missing grammar, corrupt WASM, or\n * tree-sitter returning a null tree.\n *\n * @since 0.1.0\n * @category errors\n */\nexport class ParserError extends Schema.TaggedErrorClass<ParserError>()(\"ParserError\", {\n message: Schema.String,\n}) {}\n\n/**\n * Maps grammar names to their corresponding `.wasm` filenames.\n *\n * @since 0.1.0\n * @category constants\n */\nconst GRAMMAR_FILES: HashMap.HashMap<string, string> = HashMap.make(\n [\"typescript\", \"tree-sitter-typescript.wasm\"],\n [\"tsx\", \"tree-sitter-tsx.wasm\"],\n [\"javascript\", \"tree-sitter-javascript.wasm\"],\n);\n\n/**\n * @example\n * ```ts\n * import { Console, Effect } from \"effect\"\n * import { Parser } from \"./infrastructure/parser.js\"\n *\n * const program = Effect.gen(function* () {\n * const parser = yield* Parser\n * const tree = yield* parser.parse(\"const x = 1\", \"typescript\")\n * yield* Console.log(tree.rootNode.type) // \"program\"\n * })\n * ```\n *\n * @since 0.1.0\n * @category services\n */\nexport class Parser extends ServiceMap.Service<\n Parser,\n {\n parse(source: string, grammar: string): Effect.Effect<Tree, ParserError>;\n }\n>()(\"agentreview/Parser\") {\n /** Default layer — lazily initializes WASM and caches grammars. */\n static readonly layer: Layer.Layer<Parser, never, FileSystem.FileSystem | Path.Path | Env> = Layer.effect(\n Parser,\n Effect.gen(function* () {\n const env = yield* Env;\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n\n const resolveWasmPath = (filename: string): Effect.Effect<string, ParserError> =>\n Effect.gen(function* () {\n const thisDir = path.dirname(path.resolve(import.meta.dirname ?? \".\", \"\"));\n const distPath = path.resolve(thisDir, \"wasm\", filename);\n if (yield* fs.exists(distPath).pipe(Effect.orElseSucceed(() => false))) return distPath;\n\n const nmBase = path.resolve(env.cwd, \"node_modules\");\n if (filename === \"tree-sitter.wasm\") {\n const p = path.resolve(nmBase, \"web-tree-sitter\", filename);\n if (yield* fs.exists(p).pipe(Effect.orElseSucceed(() => false))) return p;\n } else {\n const p = path.resolve(nmBase, \"tree-sitter-wasms\", \"out\", filename);\n if (yield* fs.exists(p).pipe(Effect.orElseSucceed(() => false))) return p;\n }\n\n return yield* new ParserError({ message: `WASM file not found: ${filename}` });\n });\n\n let parserInstance: TSParser | undefined;\n let languageCache: HashMap.HashMap<string, Language> = HashMap.empty();\n\n return Parser.of({\n parse: (source, grammar) =>\n Effect.gen(function* () {\n if (!parserInstance) {\n const initPath = yield* resolveWasmPath(\"tree-sitter.wasm\");\n yield* Effect.tryPromise({\n try: async () => {\n await TSParser.init({ locateFile: () => initPath });\n parserInstance = new TSParser();\n },\n catch: (error) => new ParserError({ message: error instanceof Error ? error.message : String(error) }),\n });\n }\n\n let lang = Option.getOrUndefined(HashMap.get(languageCache, grammar));\n if (!lang) {\n const file = Option.getOrUndefined(HashMap.get(GRAMMAR_FILES, grammar));\n if (!file) return yield* new ParserError({ message: `Unknown grammar: ${grammar}` });\n\n const wasmPath = yield* resolveWasmPath(file);\n lang = yield* Effect.tryPromise({\n try: () => Language.load(wasmPath),\n catch: (error) => new ParserError({ message: error instanceof Error ? error.message : String(error) }),\n });\n languageCache = HashMap.set(languageCache, grammar, lang);\n }\n\n parserInstance!.setLanguage(lang);\n const tree = parserInstance!.parse(source);\n if (!tree) return yield* new ParserError({ message: \"Parser returned null tree\" });\n return tree;\n }),\n });\n }),\n );\n}\n","/**\n * Single-pass multi-rule tree walker.\n *\n * Builds a dispatch table from all active rules' visitor methods,\n * walks the tree once using tree-sitter's cursor API, and calls\n * all matching handlers per node.\n *\n * @module\n */\n\nimport { Effect, HashMap, Option } from \"effect\";\nimport type { Tree, TreeCursor } from \"web-tree-sitter\";\nimport { type AgentReviewNode, wrapNode } from \"../../domain/node.js\";\nimport type { FlagRecord } from \"../../domain/flag.js\";\nimport type { VisitorHandler, Visitors } from \"../../domain/rule.js\";\nimport type { RuleContextImpl } from \"../../domain/rule-context.js\";\n\n/**\n * Regex matching `agentreview-ignore` comments.\n *\n * Captures an optional rule name after the directive:\n * - `// agentreview-ignore` → suppresses all rules on the next line\n * - `// agentreview-ignore my-rule` → suppresses only `my-rule`\n *\n * @since 0.1.0\n * @category constants\n */\nconst IGNORE_PATTERN = /agentreview-ignore(?:\\s+(\\S+))?/;\n\n/**\n * Internal binding of a rule to its context and visitors for a walk pass.\n *\n * @since 0.1.0\n * @category models\n */\ninterface RuleEntry {\n readonly ruleName: string;\n readonly context: RuleContextImpl;\n readonly visitors: Visitors;\n}\n\n/**\n * A single handler entry in the dispatch table, keyed by node type.\n *\n * @since 0.1.0\n * @category models\n */\ninterface DispatchHandler {\n readonly ruleName: string;\n readonly handler: VisitorHandler;\n}\n\n/**\n * Records an `agentreview-ignore` comment that suppresses a specific\n * rule (or all rules) on the immediately following line.\n *\n * @since 0.1.0\n * @category models\n */\ninterface Suppression {\n /** Rule name, or `\"*\"` for all rules. */\n readonly ruleName: string;\n /** 0-indexed line number of the *comment* (the suppressed line is `line + 1`). */\n readonly line: number;\n}\n\n/**\n * Walk files with the given rules, collecting all flags.\n *\n * Call this once per file. The caller is responsible for:\n * - Calling `context.setFile()` before this function\n * - Calling `before()` and filtering out skipped rules\n * - Calling `after()` after all files are processed\n *\n * @internal\n */\nexport function walkFile(tree: Tree, rules: ReadonlyArray<RuleEntry>): ReadonlyArray<FlagRecord> {\n const dispatchTable: HashMap.HashMap<string, DispatchHandler[]> = HashMap.mutate(\n HashMap.empty<string, DispatchHandler[]>(),\n (m) => {\n for (const entry of rules) {\n for (const key of Object.keys(entry.visitors)) {\n if (key === \"before\" || key === \"after\") continue;\n const handler = entry.visitors[key];\n if (typeof handler !== \"function\") continue;\n\n const existing = Option.getOrUndefined(HashMap.get(m, key));\n if (existing) {\n existing.push({ ruleName: entry.ruleName, handler: handler as VisitorHandler });\n } else {\n HashMap.set(m, key, [{ ruleName: entry.ruleName, handler: handler as VisitorHandler }]);\n }\n }\n }\n },\n );\n\n const suppressions: Suppression[] = [];\n\n const cursor: TreeCursor = tree.walk();\n let reachedEnd = false;\n\n while (!reachedEnd) {\n const nodeType = cursor.nodeType;\n\n if (nodeType === \"comment\") {\n const node = cursor.currentNode;\n const match = IGNORE_PATTERN.exec(node.text);\n if (match) {\n const ruleNameArg = match[1];\n const ruleName = ruleNameArg?.split(\"--\")[0]?.trim() ?? \"*\";\n suppressions.push({\n ruleName,\n line: node.startPosition.row + 1,\n });\n }\n }\n\n const handlers = Option.getOrUndefined(HashMap.get(dispatchTable, nodeType));\n if (handlers) {\n const wrapped: AgentReviewNode = wrapNode(cursor.currentNode);\n for (const { handler } of handlers) {\n handler(wrapped);\n }\n }\n\n if (cursor.gotoFirstChild()) continue;\n while (!cursor.gotoNextSibling()) {\n if (!cursor.gotoParent()) {\n reachedEnd = true;\n break;\n }\n }\n }\n\n const allFlags: FlagRecord[] = [];\n for (const entry of rules) {\n allFlags.push(...entry.context.drainFlags());\n }\n\n if (suppressions.length === 0) return allFlags;\n\n return allFlags.filter((flag) => {\n const flagLine0 = flag.line - 1;\n return !suppressions.some((s) => (s.ruleName === \"*\" || s.ruleName === flag.ruleName) && s.line === flagLine0);\n });\n}\n\n/**\n * Effect wrapper around {@link walkFile}.\n *\n * Runs the tree walk synchronously inside `Effect.sync`, making it\n * composable with the rest of the Effect pipeline.\n *\n * @since 0.1.0\n * @category constructors\n */\nexport function walkFileEffect(tree: Tree, rules: ReadonlyArray<RuleEntry>): Effect.Effect<ReadonlyArray<FlagRecord>> {\n return Effect.sync(() => walkFile(tree, rules));\n}\n","/**\n * File extension → tree-sitter grammar mapping.\n *\n * Maps every supported file extension to the grammar name used by\n * the parser service. This is the single source of truth for which\n * file types agentreview can analyze.\n *\n * Uses Effect `HashMap` for an immutable, structurally-equal lookup table.\n *\n * @module\n * @since 0.1.0\n */\n\nimport { HashMap, Option } from \"effect\";\n\n/**\n * Maps file extensions (without leading dot) to their tree-sitter\n * grammar name.\n *\n * @since 0.1.0\n * @category constants\n */\nconst EXTENSION_TO_GRAMMAR: HashMap.HashMap<string, string> = HashMap.make(\n [\"ts\", \"typescript\"],\n [\"tsx\", \"tsx\"],\n [\"js\", \"javascript\"],\n [\"jsx\", \"javascript\"],\n [\"mts\", \"typescript\"],\n [\"cts\", \"typescript\"],\n [\"mjs\", \"javascript\"],\n [\"cjs\", \"javascript\"],\n);\n\n/**\n * Look up the tree-sitter grammar name for a file extension.\n *\n * Returns `undefined` for unsupported extensions — callers should\n * skip those files.\n *\n * @since 0.1.0\n * @category constructors\n */\nexport function grammarForExtension(ext: string): string | undefined {\n return Option.getOrUndefined(HashMap.get(EXTENSION_TO_GRAMMAR, ext));\n}\n\n/**\n * Return all file extensions that agentreview can parse.\n *\n * @since 0.1.0\n * @category constructors\n */\nexport function supportedExtensions(): ReadonlyArray<string> {\n return [...HashMap.keys(EXTENSION_TO_GRAMMAR)];\n}\n","/**\n * Flag collection pipeline — shared between `check` and `review`.\n *\n * @module\n * @since 0.1.0\n */\n\nimport { Effect, FileSystem, Path, Schema } from \"effect\";\nimport picomatch from \"picomatch\";\nimport { Env } from \"../../config/env.js\";\nimport { FlagRecord } from \"../../domain/flag.js\";\nimport type { AgentReviewRule, Visitors } from \"../../domain/rule.js\";\nimport { RuleContextImpl } from \"../../domain/rule-context.js\";\nimport { ConfigLoader } from \"../infrastructure/config-loader.js\";\nimport { resolveFiles } from \"./file-resolver.js\";\nimport { Git } from \"../infrastructure/git.js\";\nimport { IgnoreReader } from \"../infrastructure/ignore-reader.js\";\nimport { Parser } from \"../infrastructure/parser.js\";\nimport { walkFile } from \"./tree-walker.js\";\nimport { grammarForExtension } from \"./language-map.js\";\n\n/**\n * @since 0.1.0\n * @category models\n */\nexport const CollectResult = Schema.Struct({\n /** Collected flags. */\n flags: Schema.Array(FlagRecord),\n /** `true` when the `--rule` filter matched no registered rules. */\n noMatchingRules: Schema.Boolean,\n});\n\n/** @since 0.1.0 */\nexport type CollectResult = Schema.Schema.Type<typeof CollectResult>;\n\n/**\n * @since 0.1.0\n * @category models\n */\nexport const CollectOptions = Schema.Struct({\n /** When `true`, scan all files instead of only git-changed files. */\n all: Schema.Boolean,\n /** Rule name filter. Empty array means \"run all rules\". */\n rules: Schema.Array(Schema.String),\n /** When `true`, suppress instruction and hint blocks in output. */\n dryRun: Schema.Boolean,\n /** Git ref to diff against. `undefined` means auto-detect. */\n base: Schema.UndefinedOr(Schema.String),\n /** Explicit file paths from positional CLI arguments. */\n files: Schema.Array(Schema.String),\n});\n\n/** @since 0.1.0 */\nexport type CollectOptions = Schema.Schema.Type<typeof CollectOptions>;\n\n/** @since 0.1.0 */\nexport const collectFlags = Effect.fn(\"collectFlags\")(function* (\n options: CollectOptions,\n): Generator<any, CollectResult> {\n const configLoader = yield* ConfigLoader;\n const env = yield* Env;\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const ignoreReader = yield* IgnoreReader;\n const gitService = yield* Git;\n const parserService = yield* Parser;\n\n const config = yield* configLoader.load();\n\n let activeRules: Array<[string, AgentReviewRule]> = Object.entries(config.rules);\n if (options.rules.length > 0) {\n activeRules = activeRules.filter(([name]) => options.rules.includes(name));\n if (activeRules.length === 0) return { flags: [], noMatchingRules: true };\n }\n\n const includePatterns = config.include ? [...config.include] : undefined;\n const ignorePatterns = config.ignore ? [...config.ignore] : undefined;\n\n const files = yield* resolveFiles(\n {\n all: options.all,\n baseRef: options.base,\n configInclude: includePatterns,\n configIgnore: ignorePatterns,\n positionalFiles: options.files.length > 0 ? [...options.files] : undefined,\n },\n ignoreReader,\n gitService,\n );\n\n if (files.length === 0) return { flags: [], noMatchingRules: false };\n\n const ruleEntries: Array<{\n name: string;\n rule: AgentReviewRule;\n context: RuleContextImpl;\n visitors: Visitors;\n }> = [];\n\n for (const [name, rule] of activeRules) {\n const context = new RuleContextImpl(name);\n const visitors = rule.createOnce(context);\n ruleEntries.push({ name, rule, context, visitors });\n }\n\n const allFlags: FlagRecord[] = [];\n\n for (const file of files) {\n const ext = path.extname(file).slice(1);\n const absPath = path.resolve(env.cwd, file);\n\n const applicableRules = ruleEntries.filter((entry) => {\n if (!entry.rule.meta.languages.includes(ext)) return false;\n\n if (entry.rule.meta.include && entry.rule.meta.include.length > 0) {\n const matcher = picomatch([...entry.rule.meta.include]);\n if (!matcher(file)) return false;\n }\n\n if (entry.rule.meta.ignore && entry.rule.meta.ignore.length > 0) {\n const matcher = picomatch([...entry.rule.meta.ignore]);\n if (matcher(file)) return false;\n }\n\n return true;\n });\n\n if (applicableRules.length === 0) continue;\n\n const sourceResult = yield* fs.readFileString(absPath).pipe(Effect.result);\n if (sourceResult._tag === \"Failure\") continue;\n const source = sourceResult.success;\n\n const grammar = grammarForExtension(ext);\n if (!grammar) continue;\n\n const tree = yield* parserService.parse(source, grammar);\n\n const rulesForFile: Array<{\n ruleName: string;\n context: RuleContextImpl;\n visitors: Visitors;\n }> = [];\n\n for (const entry of applicableRules) {\n entry.context.setFile(absPath, source);\n const beforeResult = entry.visitors.before?.(absPath);\n if (beforeResult === false) continue;\n rulesForFile.push({\n ruleName: entry.name,\n context: entry.context,\n visitors: entry.visitors,\n });\n }\n\n if (rulesForFile.length === 0) continue;\n\n const fileFlags = walkFile(tree, rulesForFile);\n allFlags.push(...fileFlags);\n }\n\n for (const entry of ruleEntries) {\n entry.visitors.after?.();\n }\n\n return { flags: allFlags, noMatchingRules: false };\n});\n","/**\n * @module\n * @since 0.1.0\n */\n\nimport { Schema } from \"effect\";\nimport { FlagRecord } from \"../../domain/flag.js\";\n\n/**\n * @since 0.1.0\n * @category models\n */\nexport class CheckCommand extends Schema.TaggedClass<CheckCommand>()(\"CheckCommand\", {\n /** When `true`, scan all files instead of only git-changed files. */\n all: Schema.Boolean,\n /** Rule name filter. Empty array means \"run all rules\". */\n rules: Schema.Array(Schema.String),\n /** When `true`, suppress instruction and hint blocks in output. */\n dryRun: Schema.Boolean,\n /** Git ref to diff against. `undefined` means auto-detect. */\n base: Schema.UndefinedOr(Schema.String),\n /** Explicit file paths from positional CLI arguments. */\n files: Schema.Array(Schema.String),\n}) {}\n\n/**\n * @since 0.1.0\n * @category models\n */\nexport class CheckResult extends Schema.TaggedClass<CheckResult>()(\"CheckResult\", {\n /** Unreviewed flags to display. */\n flags: Schema.Array(FlagRecord),\n /** Total flags before filtering out reviewed ones. */\n totalFlags: Schema.Number,\n /** Number of flags filtered out because they were previously reviewed. */\n filteredCount: Schema.Number,\n /** `true` when the `--rule` filter matched no registered rules. */\n noMatchingRules: Schema.Boolean,\n /** Available rule names (for error messages when no match). */\n availableRules: Schema.Array(Schema.String),\n}) {}\n","/**\n * @module\n * @since 0.1.0\n */\n\nimport { Effect, HashSet } from \"effect\";\nimport { ConfigLoader } from \"../../shared/infrastructure/config-loader.js\";\nimport { StateStore } from \"../../shared/infrastructure/state-store.js\";\nimport { collectFlags } from \"../../shared/pipeline/collect-flags.js\";\nimport { CheckCommand, CheckResult } from \"./request.js\";\n\n/** @since 0.1.0 */\nexport const checkHandler = Effect.fn(\"checkHandler\")(function* (command: CheckCommand) {\n const configLoader = yield* ConfigLoader;\n const stateStore = yield* StateStore;\n\n const config = yield* configLoader.load();\n const availableRules = Object.keys(config.rules);\n\n const result = yield* collectFlags({\n all: command.all,\n rules: command.rules,\n dryRun: command.dryRun,\n base: command.base,\n files: command.files,\n });\n\n if (result.noMatchingRules) {\n return new CheckResult({\n flags: [],\n totalFlags: 0,\n filteredCount: 0,\n noMatchingRules: true,\n availableRules,\n });\n }\n\n const allFlags = result.flags;\n\n if (allFlags.length === 0) {\n return new CheckResult({\n flags: [],\n totalFlags: 0,\n filteredCount: 0,\n noMatchingRules: false,\n availableRules,\n });\n }\n\n const reviewed = yield* stateStore.load();\n const reviewedSize = HashSet.size(reviewed);\n const filteredCount = reviewedSize > 0 ? allFlags.filter((f) => HashSet.has(reviewed, f.hash)).length : 0;\n const unreviewedFlags = reviewedSize > 0 ? allFlags.filter((f) => !HashSet.has(reviewed, f.hash)) : allFlags;\n\n return new CheckResult({\n flags: unreviewedFlags,\n totalFlags: allFlags.length,\n filteredCount,\n noMatchingRules: false,\n availableRules,\n });\n});\n","/**\n * @module\n * @since 0.1.0\n */\n\nimport { Schema } from \"effect\";\n\n/**\n * @since 0.1.0\n * @category models\n */\nexport class InitCommand extends Schema.TaggedClass<InitCommand>()(\"InitCommand\", {}) {}\n\n/**\n * @since 0.1.0\n * @category models\n */\nexport class InitResult extends Schema.TaggedClass<InitResult>()(\"InitResult\", {\n /** Whether a new config file was created. */\n created: Schema.Boolean,\n /** Human-readable message describing what happened. */\n message: Schema.String,\n}) {}\n","/**\n * @module\n * @since 0.1.0\n */\n\nimport { Effect, FileSystem, Path } from \"effect\";\nimport { Env } from \"../../config/env.js\";\nimport { InitCommand, InitResult } from \"./request.js\";\n\n/**\n * Minimal starter config written by `agentreview init`.\n *\n * @since 0.1.0\n * @category constants\n */\nconst STARTER_CONFIG = `import { defineConfig } from \"agentreview\"\n\nexport default defineConfig({\n include: [\"src/**/*.{ts,tsx}\"],\n rules: {},\n})\n`;\n\nconst SKILLS_ADD_CMD = \"npx skills@latest add aurelienbobenrieth/agentreview\";\nconst INTENT_INSTALL_CMD = \"npx @tanstack/intent install\";\n\n/**\n * Detect which skill installation method is most likely appropriate.\n *\n * Checks for TanStack Intent or existing AGENTS.md with intent block.\n */\nconst detectSkillMethod = Effect.fn(\"detectSkillMethod\")(function* (cwd: string) {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n\n // Check if TanStack Intent is installed\n const hasIntent = yield* fs\n .exists(path.resolve(cwd, \"node_modules/@tanstack/intent\"))\n .pipe(Effect.orElseSucceed(() => false));\n\n if (hasIntent) return \"intent\" as const;\n\n // Check if AGENTS.md has an intent-skills block (installed by intent previously)\n const agentsPath = path.resolve(cwd, \"AGENTS.md\");\n if (yield* fs.exists(agentsPath)) {\n const content = yield* fs.readFileString(agentsPath);\n if (content.includes(\"intent-skills:start\")) return \"intent\" as const;\n }\n\n return \"skills\" as const;\n});\n\n/** @since 0.1.0 */\nexport const initHandler = Effect.fn(\"initHandler\")(function* (_command: InitCommand) {\n const env = yield* Env;\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const configPath = path.resolve(env.cwd, \"agentreview.config.ts\");\n\n const gitignorePath = path.resolve(env.cwd, \".gitignore\");\n\n // --- Step 1: Create config ---\n const configCreated = !(yield* fs.exists(configPath));\n if (configCreated) {\n yield* fs.writeFileString(configPath, STARTER_CONFIG);\n }\n\n // --- Step 2: Ensure .agentreview-state is gitignored ---\n let gitignoreUpdated = false;\n const gitignoreExists = yield* fs.exists(gitignorePath);\n if (gitignoreExists) {\n const content = yield* fs.readFileString(gitignorePath);\n if (!content.includes(\".agentreview-state\")) {\n const separator = content.endsWith(\"\\n\") ? \"\" : \"\\n\";\n yield* fs.writeFileString(\n gitignorePath,\n content + separator + \"\\n# agentreview local state\\n.agentreview-state\\n\",\n );\n gitignoreUpdated = true;\n }\n } else {\n yield* fs.writeFileString(gitignorePath, \"# agentreview local state\\n.agentreview-state\\n\");\n gitignoreUpdated = true;\n }\n\n const lines: Array<string> = [];\n\n if (configCreated) {\n lines.push(\"✓ Created agentreview.config.ts\");\n } else {\n lines.push(\"· agentreview.config.ts already exists — skipped\");\n }\n\n if (gitignoreUpdated) {\n lines.push(\"✓ Added .agentreview-state to .gitignore\");\n }\n\n // --- Step 2: Next steps ---\n const method = yield* detectSkillMethod(env.cwd);\n const skillCmd = method === \"intent\" ? INTENT_INSTALL_CMD : SKILLS_ADD_CMD;\n\n lines.push(\n \"\",\n \"Next steps:\",\n \" 1. Add rules to your config\",\n ` 2. Install the agentreview skill for your AI agents:`,\n ` ${skillCmd}`,\n \" 3. Run: npx agentreview check --all\",\n );\n\n return new InitResult({\n created: configCreated,\n message: lines.join(\"\\n\"),\n });\n});\n","/**\n * @module\n * @since 0.1.0\n */\n\nimport { Schema } from \"effect\";\n\n/**\n * @since 0.1.0\n * @category models\n */\nexport const RuleSummary = Schema.Struct({\n name: Schema.String,\n description: Schema.String,\n languages: Schema.Array(Schema.String),\n include: Schema.UndefinedOr(Schema.Array(Schema.String)),\n ignore: Schema.UndefinedOr(Schema.Array(Schema.String)),\n});\n\n/** @since 0.1.0 */\nexport type RuleSummary = Schema.Schema.Type<typeof RuleSummary>;\n\n/**\n * @since 0.1.0\n * @category models\n */\nexport class ListCommand extends Schema.TaggedClass<ListCommand>()(\"ListCommand\", {}) {}\n\n/**\n * @since 0.1.0\n * @category models\n */\nexport class ListResult extends Schema.TaggedClass<ListResult>()(\"ListResult\", {\n /** All registered rules with their metadata. */\n rules: Schema.Array(RuleSummary),\n}) {}\n","/**\n * @module\n * @since 0.1.0\n */\n\nimport { Effect } from \"effect\";\nimport { ConfigLoader } from \"../../shared/infrastructure/config-loader.js\";\nimport { ListCommand, ListResult } from \"./request.js\";\n\n/** @since 0.1.0 */\nexport const listHandler = Effect.fn(\"listHandler\")(function* (_command: ListCommand) {\n const configLoader = yield* ConfigLoader;\n const config = yield* configLoader.load();\n\n const rules = Object.entries(config.rules).map(([name, rule]) => ({\n name,\n description: rule.meta.description,\n languages: rule.meta.languages,\n include: rule.meta.include,\n ignore: rule.meta.ignore,\n }));\n\n return new ListResult({ rules });\n});\n","/**\n * @module\n * @since 0.1.0\n */\n\nimport { Schema } from \"effect\";\n\n/**\n * @since 0.1.0\n * @category models\n */\nexport class ReviewCommand extends Schema.TaggedClass<ReviewCommand>()(\"ReviewCommand\", {\n /** Specific hashes to mark as reviewed. */\n hashes: Schema.Array(Schema.String),\n /** When `true`, mark all current flags as reviewed. */\n all: Schema.Boolean,\n /** When `true`, wipe the state file. */\n reset: Schema.Boolean,\n}) {}\n\n/**\n * @since 0.1.0\n * @category models\n */\nexport class ReviewResult extends Schema.TaggedClass<ReviewResult>()(\"ReviewResult\", {\n /** Human-readable message describing what happened. */\n message: Schema.String,\n}) {}\n","/**\n * @module\n * @since 0.1.0\n */\n\nimport { Effect } from \"effect\";\nimport { StateStore } from \"../../shared/infrastructure/state-store.js\";\nimport { collectFlags } from \"../../shared/pipeline/collect-flags.js\";\nimport { ReviewCommand, ReviewResult } from \"./request.js\";\n\n/** @since 0.1.0 */\nexport const reviewHandler = Effect.fn(\"reviewHandler\")(function* (command: ReviewCommand) {\n const stateStore = yield* StateStore;\n\n if (command.reset) {\n yield* stateStore.reset();\n return new ReviewResult({ message: \"Cleared .agentreview-state\" });\n }\n\n if (command.all) {\n const result = yield* collectFlags({\n all: true,\n rules: [],\n dryRun: false,\n base: undefined,\n files: [],\n });\n const allFlags = result.flags;\n if (allFlags.length === 0) {\n return new ReviewResult({ message: \"No flags to review.\" });\n }\n yield* stateStore.append(allFlags.map((f) => f.hash));\n return new ReviewResult({ message: `Marked ${allFlags.length} flag(s) as reviewed.` });\n }\n\n if (command.hashes.length > 0) {\n yield* stateStore.append([...command.hashes]);\n return new ReviewResult({ message: `Marked ${command.hashes.length} hash(es) as reviewed.` });\n }\n\n return new ReviewResult({\n message: [\n \"Usage:\",\n \" agentreview review <hash...> Mark specific flags as reviewed\",\n \" agentreview review --all Mark all current flags as reviewed\",\n \" agentreview review --reset Wipe the state file\",\n ].join(\"\\n\"),\n });\n});\n","/**\n * Terminal reporter — formats flag results into human-readable output.\n *\n * Respects `NO_COLOR` and non-TTY environments. Groups flags by rule,\n * then by file, and appends instruction/hint blocks when available.\n *\n * @module\n * @since 0.1.0\n */\n\nimport { Effect, HashMap, Option, Path, Schema } from \"effect\";\nimport { Env } from \"../config/env.js\";\nimport type { FlagRecord } from \"../domain/flag.js\";\nimport type { RuleMeta } from \"../domain/rule.js\";\n\n/**\n * Group an array by a key function, returning a HashMap of arrays.\n *\n * @since 0.1.0\n * @category internals\n */\nfunction groupBy<A>(items: ReadonlyArray<A>, key: (a: A) => string): HashMap.HashMap<string, A[]> {\n return items.reduce((acc, item) => {\n const k = key(item);\n const existing = Option.getOrUndefined(HashMap.get(acc, k));\n return existing ? (existing.push(item), acc) : HashMap.set(acc, k, [item]);\n }, HashMap.empty<string, A[]>());\n}\n\n/**\n * Build the minimal ANSI escape helpers. Each function is a no-op when\n * `noColor` is `true`.\n *\n * @since 0.1.0\n * @category internals\n */\nfunction makeAnsi(noColor: boolean) {\n return {\n bold: (s: string) => (noColor ? s : `\\x1b[1m${s}\\x1b[22m`),\n dim: (s: string) => (noColor ? s : `\\x1b[2m${s}\\x1b[22m`),\n yellow: (s: string) => (noColor ? s : `\\x1b[33m${s}\\x1b[39m`),\n cyan: (s: string) => (noColor ? s : `\\x1b[36m${s}\\x1b[39m`),\n magenta: (s: string) => (noColor ? s : `\\x1b[35m${s}\\x1b[39m`),\n gray: (s: string) => (noColor ? s : `\\x1b[90m${s}\\x1b[39m`),\n underline: (s: string) => (noColor ? s : `\\x1b[4m${s}\\x1b[24m`),\n reset: noColor ? \"\" : \"\\x1b[0m\",\n };\n}\n\n/**\n * Options that control the reporter's output format.\n *\n * @since 0.1.0\n * @category models\n */\nexport const ReporterOptions = Schema.Struct({\n /** When `true`, instruction and hint blocks are suppressed. */\n dryRun: Schema.Boolean,\n /** Version string displayed in the header line. */\n version: Schema.String,\n});\n\n/** @since 0.1.0 */\nexport type ReporterOptions = Schema.Schema.Type<typeof ReporterOptions>;\n\n/**\n * Format flag results into a terminal-friendly report string.\n *\n * Groups flags by rule name, then by file path. Includes source\n * snippets, per-match instructions/hints, and a summary line.\n * Returns a single \"no rules triggered\" line when the flag list\n * is empty.\n *\n * Uses the `Env` service for colour/cwd detection and the Effect\n * `Path` service for cross-platform path resolution.\n *\n * @since 0.1.0\n * @category constructors\n */\nexport const formatReport = Effect.fn(\"formatReport\")(function* (\n flags: ReadonlyArray<FlagRecord>,\n rulesMeta: HashMap.HashMap<string, RuleMeta>,\n options: ReporterOptions,\n) {\n const env = yield* Env;\n const path = yield* Path.Path;\n const ansi = makeAnsi(env.noColor);\n\n if (flags.length === 0) {\n return `${ansi.bold(\"agentreview\")} ${ansi.dim(`v${options.version}`)} ${ansi.dim(\"-\")} no rules triggered.`;\n }\n\n const { cwd } = env;\n const lines: string[] = [];\n\n const grouped = groupBy(flags, (f) => f.ruleName);\n\n const groupedSize = HashMap.size(grouped);\n\n if (flags.length > 50) {\n lines.push(\n ansi.yellow(\"⚠\") +\n ` ${flags.length} matches across ${groupedSize} rules. ` +\n ansi.dim(\"Consider narrowing scope with --rule or targeting specific files.\"),\n );\n lines.push(\"\");\n }\n\n for (const [ruleName, ruleFlags] of grouped) {\n const meta = Option.getOrUndefined(HashMap.get(rulesMeta, ruleName));\n\n lines.push(ansi.yellow(` x ${ruleName}`) + ansi.dim(meta ? `: ${meta.description}` : \"\"));\n lines.push(\"\");\n\n const byFile = groupBy(ruleFlags, (f) => path.relative(cwd, f.filename).replace(/\\\\/g, \"/\"));\n\n for (const [_filePath, fileFlags] of byFile) {\n for (const flag of fileFlags) {\n const relPath = path.relative(cwd, flag.filename).replace(/\\\\/g, \"/\");\n const loc = `${relPath}:${flag.line}:${flag.col}`;\n const snippet = flag.sourceSnippet.length > 80 ? flag.sourceSnippet.slice(0, 77) + \"...\" : flag.sourceSnippet;\n\n lines.push(` ${ansi.cyan(loc)} ${ansi.dim(`[${flag.hash}]`)} ${flag.message}`);\n if (snippet && snippet !== flag.message) {\n lines.push(` ${ansi.dim(snippet)}`);\n }\n lines.push(\"\");\n }\n }\n\n if (!options.dryRun && meta?.instruction) {\n lines.push(ansi.dim(\" ┌─ Instruction ─────────────────────────────────\"));\n for (const instrLine of meta.instruction.split(\"\\n\")) {\n lines.push(ansi.dim(` │ ${instrLine}`));\n }\n lines.push(ansi.dim(\" └───────────────────────────────────────────────\"));\n lines.push(\"\");\n }\n\n const matchNotes = ruleFlags.filter((f) => f.instruction || f.suggest);\n if (!options.dryRun && matchNotes.length > 0) {\n for (const flag of matchNotes) {\n const relPath = path.relative(cwd, flag.filename).replace(/\\\\/g, \"/\");\n if (flag.instruction) {\n lines.push(` ${ansi.magenta(\"note\")} ${ansi.dim(`${relPath}:${flag.line}`)} ${flag.instruction}`);\n }\n if (flag.suggest) {\n lines.push(` ${ansi.magenta(\"hint\")} ${ansi.dim(`${relPath}:${flag.line}`)} ${flag.suggest}`);\n }\n }\n lines.push(\"\");\n }\n }\n\n const ruleWord = groupedSize === 1 ? \"rule\" : \"rules\";\n const matchWord = flags.length === 1 ? \"match\" : \"matches\";\n lines.push(ansi.bold(ansi.yellow(`Found ${flags.length} ${matchWord}`)) + ansi.dim(` (${groupedSize} ${ruleWord})`));\n\n return lines.join(\"\\n\");\n});\n","#!/usr/bin/env node\n/**\n * CLI entry point for `agentreview`.\n *\n * Thin adapter that translates CLI arguments into feature commands,\n * dispatches to the appropriate handler, and formats the result\n * for terminal output.\n *\n * @module\n * @since 0.1.0\n */\n\nimport * as NodeRuntime from \"@effect/platform-node/NodeRuntime\";\nimport * as NodeServices from \"@effect/platform-node/NodeServices\";\nimport { Console, Effect, HashMap, Layer, Option } from \"effect\";\nimport { Argument, Command, Flag } from \"effect/unstable/cli\";\nimport { checkHandler } from \"./features/check/handler.js\";\nimport { CheckCommand } from \"./features/check/request.js\";\nimport { initHandler } from \"./features/init/handler.js\";\nimport { InitCommand } from \"./features/init/request.js\";\nimport { listHandler } from \"./features/list/handler.js\";\nimport { ListCommand } from \"./features/list/request.js\";\nimport { reviewHandler } from \"./features/review/handler.js\";\nimport { ReviewCommand } from \"./features/review/request.js\";\nimport { Env } from \"./config/env.js\";\nimport { ConfigLoader } from \"./shared/infrastructure/config-loader.js\";\nimport { Git } from \"./shared/infrastructure/git.js\";\nimport { IgnoreReader } from \"./shared/infrastructure/ignore-reader.js\";\nimport { Parser } from \"./shared/infrastructure/parser.js\";\nimport { StateStore } from \"./shared/infrastructure/state-store.js\";\nimport { formatReport } from \"./cli/reporter.js\";\nimport type { RuleMeta } from \"./domain/rule.js\";\n\ndeclare const __AGENTREVIEW_VERSION__: string;\n\n/** The `check` subcommand — scans files and outputs a report. */\nconst check = Command.make(\n \"check\",\n {\n files: Argument.string(\"files\").pipe(\n Argument.withDescription(\"Specific files or globs to scan\"),\n Argument.variadic(),\n ),\n all: Flag.boolean(\"all\").pipe(Flag.withAlias(\"a\"), Flag.withDescription(\"Scan all files (not just git diff)\")),\n rule: Flag.string(\"rule\").pipe(\n Flag.withAlias(\"r\"),\n Flag.withDescription(\"Run only this rule (comma-separated for multiple)\"),\n Flag.optional,\n ),\n dryRun: Flag.boolean(\"dry-run\").pipe(\n Flag.withAlias(\"d\"),\n Flag.withDescription(\"Show counts only, no instruction blocks\"),\n ),\n base: Flag.string(\"base\").pipe(Flag.withDescription(\"Git ref to diff against\"), Flag.optional),\n },\n (config) => {\n const ruleFilter = Option.match(config.rule, {\n onNone: () => [] as ReadonlyArray<string>,\n onSome: (r: string) => r.split(\",\").map((s) => s.trim()),\n });\n const baseRef = Option.match(config.base, {\n onNone: () => undefined,\n onSome: (b: string) => b,\n });\n\n return Effect.gen(function* () {\n const env = yield* Env;\n const result = yield* checkHandler(\n new CheckCommand({\n all: config.all,\n rules: ruleFilter,\n dryRun: config.dryRun,\n base: baseRef,\n files: config.files,\n }),\n );\n\n if (result.noMatchingRules) {\n yield* Console.log(`No matching rules found. Available: ${result.availableRules.join(\", \")}`);\n return;\n }\n\n if (result.totalFlags === 0) {\n yield* Console.log(`agentreview v${__AGENTREVIEW_VERSION__} - no rules triggered.`);\n return;\n }\n\n const configLoader = yield* ConfigLoader;\n const cfg = yield* configLoader.load();\n const rulesMeta: HashMap.HashMap<string, RuleMeta> = HashMap.fromIterable(\n Object.entries(cfg.rules).map(([name, rule]) => [name, rule.meta] as const),\n );\n\n const output = yield* formatReport(result.flags, rulesMeta, {\n dryRun: config.dryRun,\n version: __AGENTREVIEW_VERSION__,\n });\n\n yield* Console.log(output);\n\n if (result.filteredCount > 0) {\n yield* Console.log(\n ` (${result.filteredCount} reviewed flag(s) hidden — run agentreview review --reset to clear)`,\n );\n }\n\n if (result.flags.length > 0) {\n env.setExitCode(1);\n }\n });\n },\n).pipe(Command.withDescription(\"Scan files and output report for AI agents\"));\n\n/** The `list` subcommand — prints all registered rules. */\nconst list = Command.make(\"list\", {}, () =>\n Effect.gen(function* () {\n const result = yield* listHandler(new ListCommand({}));\n\n if (result.rules.length === 0) {\n yield* Console.log(\"No rules registered.\");\n return;\n }\n\n yield* Console.log(`${result.rules.length} rule(s) registered:\\n`);\n\n for (const rule of result.rules) {\n const langs = rule.languages.join(\", \");\n yield* Console.log(` ${rule.name}`);\n yield* Console.log(` ${rule.description}`);\n yield* Console.log(` Languages: ${langs}`);\n if (rule.include) {\n yield* Console.log(` Include: ${rule.include.join(\", \")}`);\n }\n if (rule.ignore) {\n yield* Console.log(` Ignore: ${rule.ignore.join(\", \")}`);\n }\n yield* Console.log();\n }\n }),\n).pipe(Command.withDescription(\"List all registered rules\"));\n\n/** The `init` subcommand — scaffolds a starter config file. */\nconst init = Command.make(\"init\", {}, () =>\n Effect.gen(function* () {\n const result = yield* initHandler(new InitCommand({}));\n yield* Console.log(result.message);\n }),\n).pipe(Command.withDescription(\"Create agentreview.config.ts and set up agent skill discovery\"));\n\n/** The `review` subcommand — manage reviewed-flag state. */\nconst review = Command.make(\n \"review\",\n {\n hashes: Argument.string(\"hashes\").pipe(\n Argument.withDescription(\"Flag hashes to mark as reviewed\"),\n Argument.variadic(),\n ),\n all: Flag.boolean(\"all\").pipe(Flag.withAlias(\"a\"), Flag.withDescription(\"Mark all current flags as reviewed\")),\n reset: Flag.boolean(\"reset\").pipe(Flag.withDescription(\"Wipe the state file\")),\n },\n (config) =>\n Effect.gen(function* () {\n const result = yield* reviewHandler(\n new ReviewCommand({\n hashes: config.hashes,\n all: config.all,\n reset: config.reset,\n }),\n );\n yield* Console.log(result.message);\n }),\n).pipe(Command.withDescription(\"Mark flags as reviewed (filters them from check output)\"));\n\nconst agentreview = Command.make(\"agentreview\").pipe(\n Command.withDescription(\"Deterministic linting for AI agents\"),\n Command.withSubcommands([check, list, init, review]),\n);\n\nconst AppLayer = Layer.mergeAll(ConfigLoader.layer, Parser.layer, Git.layer, IgnoreReader.layer, StateStore.layer).pipe(\n Layer.provideMerge(NodeServices.layer),\n Layer.provideMerge(Env.layer),\n);\n\n// The CLI framework uses `unknown` for aggregated service requirements,\n// which doesn't fully resolve via `Effect.provide`. Cast to satisfy\n// `NodeRuntime.runMain`'s `never` requirement constraint.\nconst program = Command.run(agentreview, { version: __AGENTREVIEW_VERSION__ }).pipe(\n Effect.provide(AppLayer),\n) as Effect.Effect<void>;\n\nNodeRuntime.runMain(program);\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"bin.mjs","names":["#filename","#source","Parser","TSParser","Parser","Parser"],"sources":["../src/config/env.ts","../src/shared/infrastructure/config-loader.ts","../src/shared/infrastructure/state-store.ts","../src/domain/hash.ts","../src/domain/rule-context.ts","../src/shared/pipeline/file-resolver.ts","../src/shared/infrastructure/git.ts","../src/shared/infrastructure/parser.ts","../src/shared/pipeline/tree-walker.ts","../src/shared/pipeline/language-map.ts","../src/shared/pipeline/collect-flags.ts","../src/features/check/request.ts","../src/features/check/handler.ts","../src/features/init/request.ts","../src/features/init/handler.ts","../src/features/list/request.ts","../src/features/list/handler.ts","../src/features/review/request.ts","../src/features/review/handler.ts","../src/cli/reporter.ts","../src/bin.ts"],"sourcesContent":["/**\n * Centralised process / environment access.\n *\n * **This is the only module in the codebase that may touch `process.*`.**\n *\n * Every other module that needs the working directory, TTY state, colour\n * preference, or exit-code control must depend on the `Env` service\n * instead of reaching into `process` directly.\n *\n * The layer is built once at startup with `Layer.sync` (no external\n * dependencies), so it can be provided before every other service layer.\n *\n * @module\n * @since 0.1.0\n */\n\nimport { Context, Layer } from \"effect\";\n\n/**\n * Read-only snapshot of the runtime environment.\n *\n * @since 0.1.0\n * @category services\n */\nexport class Env extends Context.Service<\n Env,\n {\n /** Current working directory, captured at startup. */\n readonly cwd: string;\n /** `true` when ANSI colour codes should be suppressed (`NO_COLOR` or non-TTY). */\n readonly noColor: boolean;\n /** `true` when stdout is an interactive terminal. */\n readonly isTTY: boolean;\n /** Set the process exit code (non-zero signals failure to the shell). */\n setExitCode(code: number): void;\n }\n>()(\"agentlint/Env\") {\n /**\n * Default layer — reads from `process` globals exactly once.\n *\n * @since 0.1.0\n * @category layers\n */\n static readonly layer: Layer.Layer<Env> = Layer.sync(Env, () => {\n /* eslint-disable n/no-process-env -- single authorised access point */\n const isTTY = process.stdout.isTTY ?? false;\n return Env.of({\n cwd: process.cwd(),\n noColor: !!process.env[\"NO_COLOR\"] || !isTTY,\n isTTY,\n setExitCode: (code) => {\n process.exitCode = code;\n },\n });\n /* eslint-enable n/no-process-env */\n });\n}\n","/**\n * Configuration file discovery and loading.\n *\n * Searches the current working directory for a config file, imports it\n * via `jiti` (for TypeScript support without pre-compilation), and\n * validates the exported shape.\n *\n * **Search order**: `agentlint.config.ts` → `.js` → `.mts` → `.mjs`.\n * The first match wins.\n *\n * @module\n * @since 0.1.0\n */\n\nimport { Context, Effect, FileSystem, Layer, Path, Schema } from \"effect\";\nimport { Env } from \"../../config/env.js\";\nimport type { AgentReviewConfig } from \"../../domain/config.js\";\n\n/**\n * Raised when the config file is missing, malformed, or fails to import.\n *\n * @since 0.1.0\n * @category errors\n */\nexport class ConfigError extends Schema.TaggedErrorClass<ConfigError>()(\"ConfigError\", {\n message: Schema.String,\n}) {}\n\n/**\n * Candidate config file names, checked in order.\n *\n * @since 0.1.0\n * @category constants\n */\nconst CONFIG_NAMES = [\"agentlint.config.ts\", \"agentlint.config.js\", \"agentlint.config.mts\", \"agentlint.config.mjs\"];\n\n/**\n * Discover the config file path by checking candidates in order.\n *\n * @since 0.1.0\n * @category internals\n */\nconst discoverConfig = (fs: FileSystem.FileSystem, path: Path.Path, cwd: string): Effect.Effect<string, ConfigError> =>\n Effect.gen(function* () {\n for (const name of CONFIG_NAMES) {\n const candidate = path.resolve(cwd, name);\n if (yield* fs.exists(candidate).pipe(Effect.orElseSucceed(() => false))) {\n return candidate;\n }\n }\n return yield* new ConfigError({\n message: `No agentlint config found. Create agentlint.config.ts in ${cwd}`,\n });\n });\n\n/**\n * Effect service that discovers and loads the agentlint config file.\n *\n * Uses `jiti` under the hood so TypeScript configs work without a\n * separate compilation step.\n *\n * @example\n * ```ts\n * import { Console, Effect } from \"effect\"\n * import { ConfigLoader } from \"./infrastructure/config-loader.js\"\n *\n * const program = Effect.gen(function* () {\n * const loader = yield* ConfigLoader\n * const config = yield* loader.load()\n * yield* Console.log(Object.keys(config.rules))\n * })\n * ```\n *\n * @since 0.1.0\n * @category services\n */\nexport class ConfigLoader extends Context.Service<\n ConfigLoader,\n {\n /** Discover and import the config file from the working directory. */\n load(): Effect.Effect<AgentReviewConfig, ConfigError>;\n }\n>()(\"agentlint/ConfigLoader\") {\n static readonly layer: Layer.Layer<ConfigLoader, never, FileSystem.FileSystem | Path.Path | Env> = Layer.effect(\n ConfigLoader,\n Effect.gen(function* () {\n const env = yield* Env;\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n\n return ConfigLoader.of({\n load: () =>\n Effect.gen(function* () {\n const configPath = yield* discoverConfig(fs, path, env.cwd);\n\n const config = yield* Effect.tryPromise({\n try: async () => {\n const { createJiti } = await import(\"jiti\");\n const jiti = createJiti(import.meta.url, {\n interopDefault: true,\n });\n const loaded = await jiti.import(configPath);\n return (loaded as { default?: AgentReviewConfig }).default ?? (loaded as AgentReviewConfig);\n },\n catch: (error) =>\n new ConfigError({\n message: error instanceof Error ? error.message : String(error),\n }),\n });\n\n if (!config || typeof config !== \"object\" || !(\"rules\" in config)) {\n return yield* new ConfigError({\n message: `Invalid config at ${configPath}: must export an object with a \"rules\" field`,\n });\n }\n\n return config;\n }),\n });\n }),\n );\n}\n","/**\n * Local state store for tracking reviewed flags.\n *\n * Manages a `.agentlint-state` file in the project root that stores\n * hashes of flags that have been reviewed. This file is intended to\n * be **gitignored** — it is per-developer scratch state for tracking\n * progress during review sweeps.\n *\n * **Caveats**:\n * - Hashes encode file path, line, column, and message. Editing code\n * above a reviewed flag shifts its position and invalidates the hash.\n * This is by design — changed context should be re-reviewed.\n * - Stale hashes (from flags that no longer exist) accumulate harmlessly.\n * Use `agentlint review --reset` to start fresh.\n *\n * @module\n * @since 0.1.0\n */\n\nimport { Context, Effect, FileSystem, HashSet, Layer, Path } from \"effect\";\nimport { Env } from \"../../config/env.js\";\n\n/**\n * The filename used for local review state.\n *\n * @since 0.1.0\n * @category constants\n */\nconst STATE_FILENAME = \".agentlint-state\";\n\n/**\n * Parse the state file into a set of hashes.\n * Tolerates blank lines and `#`-prefixed comments.\n *\n * @since 0.1.0\n * @category internals\n */\nfunction parseStateFile(content: string): HashSet.HashSet<string> {\n let hashes: HashSet.HashSet<string> = HashSet.empty();\n for (const raw of content.split(\"\\n\")) {\n const line = raw.trim();\n if (line.length > 0 && !line.startsWith(\"#\")) {\n hashes = HashSet.add(hashes, line);\n }\n }\n return hashes;\n}\n\n/**\n * Serialize a set of hashes into file content.\n *\n * @since 0.1.0\n * @category internals\n */\nfunction serializeHashes(hashes: HashSet.HashSet<string>): string {\n return [...hashes].join(\"\\n\") + \"\\n\";\n}\n\n/**\n * Effect service for loading and persisting reviewed-flag state.\n *\n * @since 0.1.0\n * @category services\n */\nexport class StateStore extends Context.Service<\n StateStore,\n {\n /** Load reviewed hashes from `.agentlint-state`. Returns an empty set if the file is missing. */\n load(): Effect.Effect<HashSet.HashSet<string>>;\n /** Append one or more hashes to `.agentlint-state`, deduplicating against existing entries. */\n append(hashes: ReadonlyArray<string>): Effect.Effect<void>;\n /** Delete the `.agentlint-state` file entirely. */\n reset(): Effect.Effect<void>;\n }\n>()(\"agentlint/StateStore\") {\n /**\n * Default layer — resolves the state file path from `Env.cwd`.\n *\n * @since 0.1.0\n * @category layers\n */\n static readonly layer: Layer.Layer<StateStore, never, FileSystem.FileSystem | Path.Path | Env> = Layer.unwrap(\n Effect.gen(function* () {\n const env = yield* Env;\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const statePath = path.resolve(env.cwd, STATE_FILENAME);\n\n return Layer.succeed(\n StateStore,\n StateStore.of({\n load: () =>\n fs.exists(statePath).pipe(\n Effect.orElseSucceed(() => false),\n Effect.flatMap((exists) =>\n exists\n ? fs.readFileString(statePath).pipe(\n Effect.map(parseStateFile),\n Effect.orElseSucceed(() => HashSet.empty<string>()),\n )\n : Effect.succeed(HashSet.empty<string>()),\n ),\n ),\n\n append: (hashes) =>\n fs.exists(statePath).pipe(\n Effect.orElseSucceed(() => false),\n Effect.flatMap((exists) =>\n exists\n ? fs.readFileString(statePath).pipe(\n Effect.map(parseStateFile),\n Effect.orElseSucceed(() => HashSet.empty<string>()),\n )\n : Effect.succeed(HashSet.empty<string>()),\n ),\n Effect.map((existing) => hashes.reduce((acc, h) => HashSet.add(acc, h), existing)),\n Effect.flatMap((merged) =>\n fs.writeFileString(statePath, serializeHashes(merged)).pipe(Effect.orElseSucceed(() => {})),\n ),\n ),\n\n reset: () =>\n fs.exists(statePath).pipe(\n Effect.orElseSucceed(() => false),\n Effect.flatMap((exists) =>\n exists ? fs.remove(statePath).pipe(Effect.orElseSucceed(() => {})) : Effect.void,\n ),\n ),\n }),\n );\n }),\n );\n}\n","/**\n * FNV-1a hashing utility.\n *\n * Produces a 7-character hex digest used for stable, deterministic\n * flag identification. The hash encodes rule name, file path, position,\n * and message so that identical matches across runs share the same id.\n *\n * @see https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function\n *\n * @module\n * @since 0.1.0\n */\n\n/**\n * FNV-1a 32-bit offset basis.\n *\n * @since 0.1.0\n * @category constants\n */\nconst FNV_OFFSET_BASIS = 0x811c9dc5;\n\n/**\n * FNV-1a 32-bit prime multiplier.\n *\n * @since 0.1.0\n * @category constants\n */\nconst FNV_PRIME = 0x01000193;\n\n/**\n * Compute a 7-character hex FNV-1a hash of `input`.\n *\n * The result is the first 7 hex characters of the unsigned 32-bit\n * FNV-1a digest — short enough for display, long enough to avoid\n * collisions in typical lint runs.\n *\n * @example\n * ```ts\n * import { fnv1a7 } from \"./utils/hash.js\"\n *\n * fnv1a7(\"my-rule:src/index.ts:10:1:message\") // => \"a3f4b2c\"\n * ```\n *\n * @since 0.1.0\n * @category constructors\n */\nexport function fnv1a7(input: string): string {\n let hash = FNV_OFFSET_BASIS;\n for (let i = 0; i < input.length; i++) {\n hash ^= input.charCodeAt(i);\n hash = Math.imul(hash, FNV_PRIME);\n }\n return (hash >>> 0).toString(16).padStart(8, \"0\").slice(0, 7);\n}\n","/**\n * Rule context — the interface rules use to interact with the runner.\n *\n * Provides file metadata, source access, and the {@link RuleContext.flag}\n * method for recording matches.\n *\n * @module\n * @since 0.1.0\n */\n\nimport { fnv1a7 } from \"./hash.js\";\nimport { type FlagOptions, FlagRecord } from \"./flag.js\";\n\n/**\n * Context object passed to `createOnce`. Available throughout the rule's lifecycle.\n *\n * @since 0.1.0\n * @category models\n */\nexport interface RuleContext {\n /** Absolute path of the current file being analyzed. */\n getFilename(): string;\n /** Full source content of the current file. */\n getSourceCode(): string;\n /**\n * Lines around the given 1-based line number, formatted with line numbers.\n * @param line 1-based line number\n * @param radius number of lines above/below to include (default 10)\n */\n getLinesAround(line: number, radius?: number): string;\n /** Record a match for the output report. */\n flag(options: FlagOptions): void;\n}\n\n/**\n * Internal implementation of {@link RuleContext}.\n *\n * Tracks the current file, accumulates flags, and provides source\n * access helpers. The check command calls {@link setFile} before each\n * file and {@link drainFlags} after the tree walk to collect results.\n *\n * @since 0.1.0\n * @category internals\n */\nexport class RuleContextImpl implements RuleContext {\n readonly ruleName: string;\n readonly flags: FlagRecord[] = [];\n\n #filename = \"\";\n #source = \"\";\n\n constructor(ruleName: string) {\n this.ruleName = ruleName;\n }\n\n /**\n * Set the current file context. Called by the check command before\n * each file is walked.\n */\n setFile(filename: string, source: string): void {\n this.#filename = filename;\n this.#source = source;\n }\n\n /**\n * Remove and return all accumulated flags. Called after the tree\n * walk for each file to collect results.\n */\n drainFlags(): FlagRecord[] {\n return this.flags.splice(0);\n }\n\n getFilename(): string {\n return this.#filename;\n }\n\n getSourceCode(): string {\n return this.#source;\n }\n\n getLinesAround(line: number, radius = 10): string {\n const lines = this.#source.split(\"\\n\");\n const start = Math.max(0, line - 1 - radius);\n const end = Math.min(lines.length, line + radius);\n return lines\n .slice(start, end)\n .map((l, i) => `${String(start + i + 1).padStart(4)} | ${l}`)\n .join(\"\\n\");\n }\n\n flag(options: FlagOptions): void {\n const line = options.node.startPosition.row + 1;\n const col = options.node.startPosition.column + 1;\n const sourceLines = this.#source.split(\"\\n\");\n const rawLine = sourceLines[line - 1] ?? \"\";\n const trimmed = rawLine.trim();\n const sourceSnippet = trimmed.length > 100 ? trimmed.slice(0, 97) + \"...\" : trimmed;\n\n const hash = fnv1a7(`${this.ruleName}:${this.#filename}:${line}:${col}:${options.message}`);\n\n this.flags.push(\n new FlagRecord({\n ruleName: this.ruleName,\n filename: this.#filename,\n line,\n col,\n message: options.message,\n sourceSnippet,\n hash,\n instruction: options.instruction,\n suggest: options.suggest,\n }),\n );\n }\n}\n","/**\n * File resolution service.\n *\n * Determines which files to lint by applying the filter pipeline:\n * 1. Candidate files (from git diff or all files)\n * 2. Config include/ignore\n *\n * Per-rule filtering (languages, include, ignore) is done by the check command.\n *\n * @module\n */\n\nimport { Effect, FileSystem, HashSet, Path, Schema } from \"effect\";\nimport { Env } from \"../../config/env.js\";\nimport picomatch from \"picomatch\";\n\n/**\n * Raised when file resolution fails — e.g. a git error bubbling up\n * from the changed-files query.\n *\n * @since 0.1.0\n * @category errors\n */\nexport class FileResolverError extends Schema.TaggedErrorClass<FileResolverError>()(\"FileResolverError\", {\n message: Schema.String,\n}) {}\n\n/**\n * Options controlling which files enter the lint pipeline.\n *\n * @since 0.1.0\n * @category models\n */\nexport const ResolveOptions = Schema.Struct({\n /** When `true`, scan all files instead of only git-changed files. */\n all: Schema.Boolean,\n /** Git ref to diff against. Defaults to the detected default branch. */\n baseRef: Schema.optional(Schema.String),\n /** Global include globs from the config file. */\n configInclude: Schema.optional(Schema.Array(Schema.String)),\n /** Global ignore globs from the config file. */\n configIgnore: Schema.optional(Schema.Array(Schema.String)),\n /** Explicit file paths passed as CLI positional arguments. */\n positionalFiles: Schema.optional(Schema.Array(Schema.String)),\n});\n\n/** @since 0.1.0 */\nexport type ResolveOptions = Schema.Schema.Type<typeof ResolveOptions>;\n\n/** Directories that are always skipped during recursive listing. */\nconst SKIP_DIRS: HashSet.HashSet<string> = HashSet.make(\"node_modules\", \".git\", \"dist\");\n\n/**\n * Recursively list all files under `dir`, returning paths relative to `base`.\n *\n * Skips `node_modules`, `.git`, and `dist` directories. Errors (e.g.\n * permission denied) are silently swallowed.\n *\n * Uses the Effect `FileSystem` and `Path` services for cross-platform\n * file system access.\n *\n * @since 0.1.0\n * @category internals\n */\nfunction listAllFiles(dir: string, base: string, fs: FileSystem.FileSystem, path: Path.Path): Effect.Effect<string[]> {\n return Effect.gen(function* () {\n const entries = yield* fs.readDirectory(dir);\n const results: string[] = [];\n\n for (const name of entries) {\n if (HashSet.has(SKIP_DIRS, name)) continue;\n\n const fullPath = path.resolve(dir, name);\n const info = yield* fs.stat(fullPath);\n const relPath = path.relative(base, fullPath).replace(/\\\\/g, \"/\");\n\n if (info.type === \"Directory\") {\n results.push(...(yield* listAllFiles(fullPath, base, fs, path)));\n } else {\n results.push(relPath);\n }\n }\n\n return results;\n }).pipe(Effect.catch(() => Effect.succeed([] as string[])));\n}\n\n/**\n * Determine the final set of files to lint.\n *\n * Applies the multi-layer filter pipeline described in the module header,\n * then sorts the result alphabetically for deterministic output.\n *\n * @since 0.1.0\n * @category constructors\n */\nexport function resolveFiles(\n options: ResolveOptions,\n gitService: {\n changedFiles(baseRef?: string): Effect.Effect<ReadonlyArray<string>, any>;\n },\n): Effect.Effect<ReadonlyArray<string>, FileResolverError, FileSystem.FileSystem | Path.Path | Env> {\n return Effect.gen(function* () {\n const env = yield* Env;\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const { cwd } = env;\n let candidates: string[];\n\n if (options.positionalFiles && options.positionalFiles.length > 0) {\n candidates = [...options.positionalFiles];\n } else if (options.all) {\n candidates = yield* listAllFiles(cwd, cwd, fs, path);\n } else {\n const changed = yield* Effect.mapError(\n gitService.changedFiles(options.baseRef),\n (e) => new FileResolverError({ message: `Git error: ${e}` }),\n );\n candidates = [...changed];\n }\n\n const includeMatcher = options.configInclude?.length ? picomatch(options.configInclude as string[]) : undefined;\n const ignoreMatcher = options.configIgnore?.length ? picomatch(options.configIgnore as string[]) : undefined;\n\n return candidates\n .filter((f) => !includeMatcher || includeMatcher(f))\n .filter((f) => !ignoreMatcher || !ignoreMatcher(f))\n .filter((f) => path.extname(f).length > 0)\n .toSorted();\n });\n}\n","/**\n * Git integration — default branch detection and changed file collection.\n *\n * @module\n * @since 0.1.0\n */\n\nimport { Context, Effect, HashSet, Layer, Schema } from \"effect\";\nimport { Env } from \"../../config/env.js\";\nimport { ChildProcess, ChildProcessSpawner } from \"effect/unstable/process\";\n\n/**\n * Raised when a git operation fails — e.g. not a git repo,\n * invalid ref, or `git` binary not found.\n *\n * @since 0.1.0\n * @category errors\n */\nexport class GitError extends Schema.TaggedErrorClass<GitError>()(\"GitError\", {\n message: Schema.String,\n}) {}\n\n/**\n * Execute a git command and return trimmed stdout.\n *\n * Uses the array form of `ChildProcess.make` so that dynamic arguments\n * are properly tokenized.\n *\n * @since 0.1.0\n * @category internals\n */\nconst gitCmd = (args: string, cwd: string) =>\n Effect.gen(function* () {\n const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;\n return (yield* spawner.string(ChildProcess.make(\"git\", args.split(/\\s+/), { cwd }))).trim();\n });\n\n/**\n * Detect the default branch by checking whether `main` or `master` exists.\n * Falls back to `\"main\"` when neither can be verified.\n *\n * @since 0.1.0\n * @category internals\n */\nconst detectDefault = (cwd: string) =>\n gitCmd(\"rev-parse --verify main\", cwd).pipe(\n Effect.map(() => \"main\" as string),\n Effect.catch(() =>\n gitCmd(\"rev-parse --verify master\", cwd).pipe(\n Effect.map(() => \"master\" as string),\n Effect.catch(() => Effect.succeed(\"main\" as string)),\n ),\n ),\n );\n\n/**\n * Collect all files that differ from `baseRef`.\n *\n * Gathers the union of committed diffs, uncommitted changes, and\n * untracked files. Each source is caught so partial failures\n * (e.g. empty repo, no merge-base) are silently skipped.\n *\n * @since 0.1.0\n * @category internals\n */\nconst parseLines = (output: string): ReadonlyArray<string> =>\n output\n .split(\"\\n\")\n .map((f) => f.trim())\n .filter((f) => f.length > 0);\n\nconst collectChangedFiles = (cwd: string, baseRef: string) =>\n Effect.all([\n // Committed changes since merge-base\n gitCmd(`merge-base HEAD ${baseRef}`, cwd).pipe(\n Effect.flatMap((mergeBase) => gitCmd(`diff --name-only ${mergeBase}...HEAD`, cwd)),\n Effect.catch(() => Effect.succeed(\"\")),\n ),\n // Uncommitted changes\n gitCmd(\"diff --name-only HEAD\", cwd).pipe(Effect.catch(() => Effect.succeed(\"\"))),\n // Untracked files\n gitCmd(\"ls-files --others --exclude-standard\", cwd).pipe(Effect.catch(() => Effect.succeed(\"\"))),\n ]).pipe(\n Effect.map(([committed, uncommitted, untracked]) =>\n [\n ...HashSet.fromIterable([...parseLines(committed), ...parseLines(uncommitted), ...parseLines(untracked)]),\n ].toSorted(),\n ),\n );\n\n/**\n * @example\n * ```ts\n * import { Console, Effect } from \"effect\"\n * import { Git } from \"./infrastructure/git.js\"\n *\n * const program = Effect.gen(function* () {\n * const git = yield* Git\n * const branch = yield* git.detectDefaultBranch()\n * const changed = yield* git.changedFiles(branch)\n * yield* Console.log(`${changed.length} files changed since ${branch}`)\n * })\n * ```\n *\n * @since 0.1.0\n */\nexport class Git extends Context.Service<\n Git,\n {\n /** Detect whether the default branch is `main` or `master`. */\n detectDefaultBranch(): Effect.Effect<string, GitError>;\n /** Return sorted list of files changed relative to `baseRef` (defaults to the detected default branch). */\n changedFiles(baseRef?: string): Effect.Effect<ReadonlyArray<string>, GitError>;\n }\n>()(\"agentlint/Git\") {\n static readonly layer: Layer.Layer<Git, never, ChildProcessSpawner.ChildProcessSpawner | Env> = Layer.effect(\n Git,\n Effect.gen(function* () {\n const env = yield* Env;\n const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;\n const provide = <A, E>(effect: Effect.Effect<A, E, ChildProcessSpawner.ChildProcessSpawner>) =>\n Effect.provideService(effect, ChildProcessSpawner.ChildProcessSpawner, spawner);\n\n return Git.of({\n detectDefaultBranch: () =>\n provide(detectDefault(env.cwd)).pipe(Effect.mapError((e) => new GitError({ message: String(e) }))),\n\n changedFiles: (baseRef) =>\n (baseRef ? Effect.succeed(baseRef) : provide(detectDefault(env.cwd))).pipe(\n Effect.flatMap((base) => provide(collectChangedFiles(env.cwd, base))),\n Effect.mapError((e) => new GitError({ message: String(e) })),\n ),\n });\n }),\n );\n}\n","/**\n * Tree-sitter WASM parser.\n *\n * WASM init is lazy — the first `parse` call triggers initialization.\n * Grammars are cached after first load.\n *\n * @module\n * @since 0.1.0\n */\n\nimport { Context, Effect, FileSystem, HashMap, Layer, Option, Path, Schema } from \"effect\";\nimport { Env } from \"../../config/env.js\";\nimport { Language, Parser as TSParser, type Tree } from \"web-tree-sitter\";\n\n/**\n * Raised when parsing fails — e.g. missing grammar, corrupt WASM, or\n * tree-sitter returning a null tree.\n *\n * @since 0.1.0\n * @category errors\n */\nexport class ParserError extends Schema.TaggedErrorClass<ParserError>()(\"ParserError\", {\n message: Schema.String,\n}) {}\n\n/**\n * Maps grammar names to their corresponding `.wasm` filenames.\n *\n * @since 0.1.0\n * @category constants\n */\nconst GRAMMAR_FILES: HashMap.HashMap<string, string> = HashMap.make(\n [\"typescript\", \"tree-sitter-typescript.wasm\"],\n [\"tsx\", \"tree-sitter-tsx.wasm\"],\n [\"javascript\", \"tree-sitter-javascript.wasm\"],\n);\n\n/**\n * @example\n * ```ts\n * import { Console, Effect } from \"effect\"\n * import { Parser } from \"./infrastructure/parser.js\"\n *\n * const program = Effect.gen(function* () {\n * const parser = yield* Parser\n * const tree = yield* parser.parse(\"const x = 1\", \"typescript\")\n * yield* Console.log(tree.rootNode.type) // \"program\"\n * })\n * ```\n *\n * @since 0.1.0\n * @category services\n */\nexport class Parser extends Context.Service<\n Parser,\n {\n parse(source: string, grammar: string): Effect.Effect<Tree, ParserError>;\n }\n>()(\"agentlint/Parser\") {\n /** Default layer — lazily initializes WASM and caches grammars. */\n static readonly layer: Layer.Layer<Parser, never, FileSystem.FileSystem | Path.Path | Env> = Layer.effect(\n Parser,\n Effect.gen(function* () {\n const env = yield* Env;\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n\n const resolveWasmPath = (filename: string): Effect.Effect<string, ParserError> =>\n Effect.gen(function* () {\n const thisDir = path.dirname(path.resolve(import.meta.dirname ?? \".\", \"\"));\n const distPath = path.resolve(thisDir, \"wasm\", filename);\n if (yield* fs.exists(distPath).pipe(Effect.orElseSucceed(() => false))) return distPath;\n\n const nmBase = path.resolve(env.cwd, \"node_modules\");\n if (filename === \"tree-sitter.wasm\") {\n const p = path.resolve(nmBase, \"web-tree-sitter\", filename);\n if (yield* fs.exists(p).pipe(Effect.orElseSucceed(() => false))) return p;\n } else {\n const p = path.resolve(nmBase, \"tree-sitter-wasms\", \"out\", filename);\n if (yield* fs.exists(p).pipe(Effect.orElseSucceed(() => false))) return p;\n }\n\n return yield* new ParserError({ message: `WASM file not found: ${filename}` });\n });\n\n let parserInstance: TSParser | undefined;\n let languageCache: HashMap.HashMap<string, Language> = HashMap.empty();\n\n return Parser.of({\n parse: (source, grammar) =>\n Effect.gen(function* () {\n if (!parserInstance) {\n const initPath = yield* resolveWasmPath(\"tree-sitter.wasm\");\n yield* Effect.tryPromise({\n try: async () => {\n await TSParser.init({ locateFile: () => initPath });\n parserInstance = new TSParser();\n },\n catch: (error) => new ParserError({ message: error instanceof Error ? error.message : String(error) }),\n });\n }\n\n let lang = Option.getOrUndefined(HashMap.get(languageCache, grammar));\n if (!lang) {\n const file = Option.getOrUndefined(HashMap.get(GRAMMAR_FILES, grammar));\n if (!file) return yield* new ParserError({ message: `Unknown grammar: ${grammar}` });\n\n const wasmPath = yield* resolveWasmPath(file);\n lang = yield* Effect.tryPromise({\n try: () => Language.load(wasmPath),\n catch: (error) => new ParserError({ message: error instanceof Error ? error.message : String(error) }),\n });\n languageCache = HashMap.set(languageCache, grammar, lang);\n }\n\n parserInstance!.setLanguage(lang);\n const tree = parserInstance!.parse(source);\n if (!tree) return yield* new ParserError({ message: \"Parser returned null tree\" });\n return tree;\n }),\n });\n }),\n );\n}\n","/**\n * Single-pass multi-rule tree walker.\n *\n * Builds a dispatch table from all active rules' visitor methods,\n * walks the tree once using tree-sitter's cursor API, and calls\n * all matching handlers per node.\n *\n * @module\n */\n\nimport { Effect, HashMap, Option } from \"effect\";\nimport type { Tree, TreeCursor } from \"web-tree-sitter\";\nimport { type AgentReviewNode, wrapNode } from \"../../domain/node.js\";\nimport type { FlagRecord } from \"../../domain/flag.js\";\nimport type { VisitorHandler, Visitors } from \"../../domain/rule.js\";\nimport type { RuleContextImpl } from \"../../domain/rule-context.js\";\n\n/**\n * Regex matching `agentlint-ignore` comments.\n *\n * Captures an optional rule name after the directive:\n * - `// agentlint-ignore` → suppresses all rules on the next line\n * - `// agentlint-ignore my-rule` → suppresses only `my-rule`\n *\n * @since 0.1.0\n * @category constants\n */\nconst IGNORE_PATTERN = /agentlint-ignore(?:\\s+(\\S+))?/;\n\n/**\n * Internal binding of a rule to its context and visitors for a walk pass.\n *\n * @since 0.1.0\n * @category models\n */\ninterface RuleEntry {\n readonly ruleName: string;\n readonly context: RuleContextImpl;\n readonly visitors: Visitors;\n}\n\n/**\n * A single handler entry in the dispatch table, keyed by node type.\n *\n * @since 0.1.0\n * @category models\n */\ninterface DispatchHandler {\n readonly ruleName: string;\n readonly handler: VisitorHandler;\n}\n\n/**\n * Records an `agentlint-ignore` comment that suppresses a specific\n * rule (or all rules) on the immediately following line.\n *\n * @since 0.1.0\n * @category models\n */\ninterface Suppression {\n /** Rule name, or `\"*\"` for all rules. */\n readonly ruleName: string;\n /** 0-indexed line number of the *comment* (the suppressed line is `line + 1`). */\n readonly line: number;\n}\n\n/**\n * Walk files with the given rules, collecting all flags.\n *\n * Call this once per file. The caller is responsible for:\n * - Calling `context.setFile()` before this function\n * - Calling `before()` and filtering out skipped rules\n * - Calling `after()` after all files are processed\n *\n * @internal\n */\nexport function walkFile(tree: Tree, rules: ReadonlyArray<RuleEntry>): ReadonlyArray<FlagRecord> {\n const dispatchTable: HashMap.HashMap<string, DispatchHandler[]> = HashMap.mutate(\n HashMap.empty<string, DispatchHandler[]>(),\n (m) => {\n for (const entry of rules) {\n for (const key of Object.keys(entry.visitors)) {\n if (key === \"before\" || key === \"after\") continue;\n const handler = entry.visitors[key];\n if (typeof handler !== \"function\") continue;\n\n const existing = Option.getOrUndefined(HashMap.get(m, key));\n if (existing) {\n existing.push({ ruleName: entry.ruleName, handler: handler as VisitorHandler });\n } else {\n HashMap.set(m, key, [{ ruleName: entry.ruleName, handler: handler as VisitorHandler }]);\n }\n }\n }\n },\n );\n\n const suppressions: Suppression[] = [];\n\n const cursor: TreeCursor = tree.walk();\n let reachedEnd = false;\n\n while (!reachedEnd) {\n const nodeType = cursor.nodeType;\n\n if (nodeType === \"comment\") {\n const node = cursor.currentNode;\n const match = IGNORE_PATTERN.exec(node.text);\n if (match) {\n const ruleNameArg = match[1];\n const ruleName = ruleNameArg?.split(\"--\")[0]?.trim() ?? \"*\";\n suppressions.push({\n ruleName,\n line: node.startPosition.row + 1,\n });\n }\n }\n\n const handlers = Option.getOrUndefined(HashMap.get(dispatchTable, nodeType));\n if (handlers) {\n const wrapped: AgentReviewNode = wrapNode(cursor.currentNode);\n for (const { handler } of handlers) {\n handler(wrapped);\n }\n }\n\n if (cursor.gotoFirstChild()) continue;\n while (!cursor.gotoNextSibling()) {\n if (!cursor.gotoParent()) {\n reachedEnd = true;\n break;\n }\n }\n }\n\n const allFlags: FlagRecord[] = [];\n for (const entry of rules) {\n allFlags.push(...entry.context.drainFlags());\n }\n\n if (suppressions.length === 0) return allFlags;\n\n return allFlags.filter((flag) => {\n const flagLine0 = flag.line - 1;\n return !suppressions.some((s) => (s.ruleName === \"*\" || s.ruleName === flag.ruleName) && s.line === flagLine0);\n });\n}\n\n/**\n * Effect wrapper around {@link walkFile}.\n *\n * Runs the tree walk synchronously inside `Effect.sync`, making it\n * composable with the rest of the Effect pipeline.\n *\n * @since 0.1.0\n * @category constructors\n */\nexport function walkFileEffect(tree: Tree, rules: ReadonlyArray<RuleEntry>): Effect.Effect<ReadonlyArray<FlagRecord>> {\n return Effect.sync(() => walkFile(tree, rules));\n}\n","/**\n * File extension → tree-sitter grammar mapping.\n *\n * Maps every supported file extension to the grammar name used by\n * the parser service. This is the single source of truth for which\n * file types agentlint can analyze.\n *\n * Uses Effect `HashMap` for an immutable, structurally-equal lookup table.\n *\n * @module\n * @since 0.1.0\n */\n\nimport { HashMap, Option } from \"effect\";\n\n/**\n * Maps file extensions (without leading dot) to their tree-sitter\n * grammar name.\n *\n * @since 0.1.0\n * @category constants\n */\nconst EXTENSION_TO_GRAMMAR: HashMap.HashMap<string, string> = HashMap.make(\n [\"ts\", \"typescript\"],\n [\"tsx\", \"tsx\"],\n [\"js\", \"javascript\"],\n [\"jsx\", \"javascript\"],\n [\"mts\", \"typescript\"],\n [\"cts\", \"typescript\"],\n [\"mjs\", \"javascript\"],\n [\"cjs\", \"javascript\"],\n);\n\n/**\n * Look up the tree-sitter grammar name for a file extension.\n *\n * Returns `undefined` for unsupported extensions — callers should\n * skip those files.\n *\n * @since 0.1.0\n * @category constructors\n */\nexport function grammarForExtension(ext: string): string | undefined {\n return Option.getOrUndefined(HashMap.get(EXTENSION_TO_GRAMMAR, ext));\n}\n\n/**\n * Return all file extensions that agentlint can parse.\n *\n * @since 0.1.0\n * @category constructors\n */\nexport function supportedExtensions(): ReadonlyArray<string> {\n return [...HashMap.keys(EXTENSION_TO_GRAMMAR)];\n}\n","/**\n * Flag collection pipeline — shared between `check` and `review`.\n *\n * @module\n * @since 0.1.0\n */\n\nimport { Effect, FileSystem, Path, Schema } from \"effect\";\nimport picomatch from \"picomatch\";\nimport { Env } from \"../../config/env.js\";\nimport { FlagRecord } from \"../../domain/flag.js\";\nimport type { AgentReviewRule, Visitors } from \"../../domain/rule.js\";\nimport { RuleContextImpl } from \"../../domain/rule-context.js\";\nimport { ConfigLoader } from \"../infrastructure/config-loader.js\";\nimport { resolveFiles } from \"./file-resolver.js\";\nimport { Git } from \"../infrastructure/git.js\";\nimport { Parser } from \"../infrastructure/parser.js\";\nimport { walkFile } from \"./tree-walker.js\";\nimport { grammarForExtension } from \"./language-map.js\";\n\n/**\n * @since 0.1.0\n * @category models\n */\nexport const CollectResult = Schema.Struct({\n /** Collected flags. */\n flags: Schema.Array(FlagRecord),\n /** `true` when the `--rule` filter matched no registered rules. */\n noMatchingRules: Schema.Boolean,\n});\n\n/** @since 0.1.0 */\nexport type CollectResult = Schema.Schema.Type<typeof CollectResult>;\n\n/**\n * @since 0.1.0\n * @category models\n */\nexport const CollectOptions = Schema.Struct({\n /** When `true`, scan all files instead of only git-changed files. */\n all: Schema.Boolean,\n /** Rule name filter. Empty array means \"run all rules\". */\n rules: Schema.Array(Schema.String),\n /** When `true`, suppress instruction and hint blocks in output. */\n dryRun: Schema.Boolean,\n /** Git ref to diff against. `undefined` means auto-detect. */\n base: Schema.UndefinedOr(Schema.String),\n /** Explicit file paths from positional CLI arguments. */\n files: Schema.Array(Schema.String),\n});\n\n/** @since 0.1.0 */\nexport type CollectOptions = Schema.Schema.Type<typeof CollectOptions>;\n\n/** @since 0.1.0 */\nexport const collectFlags = Effect.fn(\"collectFlags\")(function* (\n options: CollectOptions,\n): Generator<any, CollectResult> {\n const configLoader = yield* ConfigLoader;\n const env = yield* Env;\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const gitService = yield* Git;\n const parserService = yield* Parser;\n\n const config = yield* configLoader.load();\n\n let activeRules: Array<[string, AgentReviewRule]> = Object.entries(config.rules);\n if (options.rules.length > 0) {\n activeRules = activeRules.filter(([name]) => options.rules.includes(name));\n if (activeRules.length === 0) return { flags: [], noMatchingRules: true };\n }\n\n const includePatterns = config.include ? [...config.include] : undefined;\n const ignorePatterns = config.ignore ? [...config.ignore] : undefined;\n\n const files = yield* resolveFiles(\n {\n all: options.all,\n baseRef: options.base,\n configInclude: includePatterns,\n configIgnore: ignorePatterns,\n positionalFiles: options.files.length > 0 ? [...options.files] : undefined,\n },\n gitService,\n );\n\n if (files.length === 0) return { flags: [], noMatchingRules: false };\n\n const ruleEntries: Array<{\n name: string;\n rule: AgentReviewRule;\n context: RuleContextImpl;\n visitors: Visitors;\n }> = [];\n\n for (const [name, rule] of activeRules) {\n const context = new RuleContextImpl(name);\n const visitors = rule.createOnce(context);\n ruleEntries.push({ name, rule, context, visitors });\n }\n\n const allFlags: FlagRecord[] = [];\n\n for (const file of files) {\n const ext = path.extname(file).slice(1);\n const absPath = path.resolve(env.cwd, file);\n\n const applicableRules = ruleEntries.filter((entry) => {\n if (!entry.rule.meta.languages.includes(ext)) return false;\n\n if (entry.rule.meta.include && entry.rule.meta.include.length > 0) {\n const matcher = picomatch([...entry.rule.meta.include]);\n if (!matcher(file)) return false;\n }\n\n if (entry.rule.meta.ignore && entry.rule.meta.ignore.length > 0) {\n const matcher = picomatch([...entry.rule.meta.ignore]);\n if (matcher(file)) return false;\n }\n\n return true;\n });\n\n if (applicableRules.length === 0) continue;\n\n const sourceResult = yield* fs.readFileString(absPath).pipe(Effect.result);\n if (sourceResult._tag === \"Failure\") continue;\n const source = sourceResult.success;\n\n const grammar = grammarForExtension(ext);\n if (!grammar) continue;\n\n const tree = yield* parserService.parse(source, grammar);\n\n const rulesForFile: Array<{\n ruleName: string;\n context: RuleContextImpl;\n visitors: Visitors;\n }> = [];\n\n for (const entry of applicableRules) {\n entry.context.setFile(absPath, source);\n const beforeResult = entry.visitors.before?.(absPath);\n if (beforeResult === false) continue;\n rulesForFile.push({\n ruleName: entry.name,\n context: entry.context,\n visitors: entry.visitors,\n });\n }\n\n if (rulesForFile.length === 0) continue;\n\n const fileFlags = walkFile(tree, rulesForFile);\n allFlags.push(...fileFlags);\n }\n\n for (const entry of ruleEntries) {\n entry.visitors.after?.();\n }\n\n return { flags: allFlags, noMatchingRules: false };\n});\n","/**\n * @module\n * @since 0.1.0\n */\n\nimport { Schema } from \"effect\";\nimport { FlagRecord } from \"../../domain/flag.js\";\n\n/**\n * @since 0.1.0\n * @category models\n */\nexport class CheckCommand extends Schema.TaggedClass<CheckCommand>()(\"CheckCommand\", {\n /** When `true`, scan all files instead of only git-changed files. */\n all: Schema.Boolean,\n /** Rule name filter. Empty array means \"run all rules\". */\n rules: Schema.Array(Schema.String),\n /** When `true`, suppress instruction and hint blocks in output. */\n dryRun: Schema.Boolean,\n /** Git ref to diff against. `undefined` means auto-detect. */\n base: Schema.UndefinedOr(Schema.String),\n /** Explicit file paths from positional CLI arguments. */\n files: Schema.Array(Schema.String),\n}) {}\n\n/**\n * @since 0.1.0\n * @category models\n */\nexport class CheckResult extends Schema.TaggedClass<CheckResult>()(\"CheckResult\", {\n /** Unreviewed flags to display. */\n flags: Schema.Array(FlagRecord),\n /** Total flags before filtering out reviewed ones. */\n totalFlags: Schema.Number,\n /** Number of flags filtered out because they were previously reviewed. */\n filteredCount: Schema.Number,\n /** `true` when the `--rule` filter matched no registered rules. */\n noMatchingRules: Schema.Boolean,\n /** Available rule names (for error messages when no match). */\n availableRules: Schema.Array(Schema.String),\n}) {}\n","/**\n * @module\n * @since 0.1.0\n */\n\nimport { Effect, HashSet } from \"effect\";\nimport { ConfigLoader } from \"../../shared/infrastructure/config-loader.js\";\nimport { StateStore } from \"../../shared/infrastructure/state-store.js\";\nimport { collectFlags } from \"../../shared/pipeline/collect-flags.js\";\nimport { CheckCommand, CheckResult } from \"./request.js\";\n\n/** @since 0.1.0 */\nexport const checkHandler = Effect.fn(\"checkHandler\")(function* (command: CheckCommand) {\n const configLoader = yield* ConfigLoader;\n const stateStore = yield* StateStore;\n\n const config = yield* configLoader.load();\n const availableRules = Object.keys(config.rules);\n\n const result = yield* collectFlags({\n all: command.all,\n rules: command.rules,\n dryRun: command.dryRun,\n base: command.base,\n files: command.files,\n });\n\n if (result.noMatchingRules) {\n return new CheckResult({\n flags: [],\n totalFlags: 0,\n filteredCount: 0,\n noMatchingRules: true,\n availableRules,\n });\n }\n\n const allFlags = result.flags;\n\n if (allFlags.length === 0) {\n return new CheckResult({\n flags: [],\n totalFlags: 0,\n filteredCount: 0,\n noMatchingRules: false,\n availableRules,\n });\n }\n\n const reviewed = yield* stateStore.load();\n const reviewedSize = HashSet.size(reviewed);\n const filteredCount = reviewedSize > 0 ? allFlags.filter((f) => HashSet.has(reviewed, f.hash)).length : 0;\n const unreviewedFlags = reviewedSize > 0 ? allFlags.filter((f) => !HashSet.has(reviewed, f.hash)) : allFlags;\n\n return new CheckResult({\n flags: unreviewedFlags,\n totalFlags: allFlags.length,\n filteredCount,\n noMatchingRules: false,\n availableRules,\n });\n});\n","/**\n * @module\n * @since 0.1.0\n */\n\nimport { Schema } from \"effect\";\n\n/**\n * @since 0.1.0\n * @category models\n */\nexport class InitCommand extends Schema.TaggedClass<InitCommand>()(\"InitCommand\", {}) {}\n\n/**\n * @since 0.1.0\n * @category models\n */\nexport class InitResult extends Schema.TaggedClass<InitResult>()(\"InitResult\", {\n /** Whether a new config file was created. */\n created: Schema.Boolean,\n /** Human-readable message describing what happened. */\n message: Schema.String,\n}) {}\n","/**\n * @module\n * @since 0.1.0\n */\n\nimport { Effect, FileSystem, Path } from \"effect\";\nimport { Env } from \"../../config/env.js\";\nimport { InitCommand, InitResult } from \"./request.js\";\n\n/**\n * Minimal starter config written by `agentlint init`.\n *\n * @since 0.1.0\n * @category constants\n */\nconst STARTER_CONFIG = `import { defineConfig } from \"agentlint\"\n\nexport default defineConfig({\n include: [\"src/**/*.{ts,tsx}\"],\n rules: {},\n})\n`;\n\nconst SKILLS_ADD_CMD = \"npx skills@latest add aurelienbobenrieth/agentlint\";\nconst INTENT_INSTALL_CMD = \"npx @tanstack/intent install\";\n\n/**\n * Detect which skill installation method is most likely appropriate.\n *\n * Checks for TanStack Intent or existing AGENTS.md with intent block.\n */\nconst detectSkillMethod = Effect.fn(\"detectSkillMethod\")(function* (cwd: string) {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n\n // Check if TanStack Intent is installed\n const hasIntent = yield* fs\n .exists(path.resolve(cwd, \"node_modules/@tanstack/intent\"))\n .pipe(Effect.orElseSucceed(() => false));\n\n if (hasIntent) return \"intent\" as const;\n\n // Check if AGENTS.md has an intent-skills block (installed by intent previously)\n const agentsPath = path.resolve(cwd, \"AGENTS.md\");\n if (yield* fs.exists(agentsPath)) {\n const content = yield* fs.readFileString(agentsPath);\n if (content.includes(\"intent-skills:start\")) return \"intent\" as const;\n }\n\n return \"skills\" as const;\n});\n\n/** @since 0.1.0 */\nexport const initHandler = Effect.fn(\"initHandler\")(function* (_command: InitCommand) {\n const env = yield* Env;\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const configPath = path.resolve(env.cwd, \"agentlint.config.ts\");\n\n const gitignorePath = path.resolve(env.cwd, \".gitignore\");\n\n // --- Step 1: Create config ---\n const configCreated = !(yield* fs.exists(configPath));\n if (configCreated) {\n yield* fs.writeFileString(configPath, STARTER_CONFIG);\n }\n\n // --- Step 2: Ensure .agentlint-state is gitignored ---\n let gitignoreUpdated = false;\n const gitignoreExists = yield* fs.exists(gitignorePath);\n if (gitignoreExists) {\n const content = yield* fs.readFileString(gitignorePath);\n if (!content.includes(\".agentlint-state\")) {\n const separator = content.endsWith(\"\\n\") ? \"\" : \"\\n\";\n yield* fs.writeFileString(gitignorePath, content + separator + \"\\n# agentlint local state\\n.agentlint-state\\n\");\n gitignoreUpdated = true;\n }\n } else {\n yield* fs.writeFileString(gitignorePath, \"# agentlint local state\\n.agentlint-state\\n\");\n gitignoreUpdated = true;\n }\n\n const lines: Array<string> = [];\n\n if (configCreated) {\n lines.push(\"✓ Created agentlint.config.ts\");\n } else {\n lines.push(\"· agentlint.config.ts already exists — skipped\");\n }\n\n if (gitignoreUpdated) {\n lines.push(\"✓ Added .agentlint-state to .gitignore\");\n }\n\n // --- Step 2: Next steps ---\n const method = yield* detectSkillMethod(env.cwd);\n const skillCmd = method === \"intent\" ? INTENT_INSTALL_CMD : SKILLS_ADD_CMD;\n\n lines.push(\n \"\",\n \"Next steps:\",\n \" 1. Add rules to your config\",\n ` 2. Install the agentlint skill for your AI agents:`,\n ` ${skillCmd}`,\n \" 3. Run: npx agentlint check --all\",\n );\n\n return new InitResult({\n created: configCreated,\n message: lines.join(\"\\n\"),\n });\n});\n","/**\n * @module\n * @since 0.1.0\n */\n\nimport { Schema } from \"effect\";\n\n/**\n * @since 0.1.0\n * @category models\n */\nexport const RuleSummary = Schema.Struct({\n name: Schema.String,\n description: Schema.String,\n languages: Schema.Array(Schema.String),\n include: Schema.UndefinedOr(Schema.Array(Schema.String)),\n ignore: Schema.UndefinedOr(Schema.Array(Schema.String)),\n});\n\n/** @since 0.1.0 */\nexport type RuleSummary = Schema.Schema.Type<typeof RuleSummary>;\n\n/**\n * @since 0.1.0\n * @category models\n */\nexport class ListCommand extends Schema.TaggedClass<ListCommand>()(\"ListCommand\", {}) {}\n\n/**\n * @since 0.1.0\n * @category models\n */\nexport class ListResult extends Schema.TaggedClass<ListResult>()(\"ListResult\", {\n /** All registered rules with their metadata. */\n rules: Schema.Array(RuleSummary),\n}) {}\n","/**\n * @module\n * @since 0.1.0\n */\n\nimport { Effect } from \"effect\";\nimport { ConfigLoader } from \"../../shared/infrastructure/config-loader.js\";\nimport { ListCommand, ListResult } from \"./request.js\";\n\n/** @since 0.1.0 */\nexport const listHandler = Effect.fn(\"listHandler\")(function* (_command: ListCommand) {\n const configLoader = yield* ConfigLoader;\n const config = yield* configLoader.load();\n\n const rules = Object.entries(config.rules).map(([name, rule]) => ({\n name,\n description: rule.meta.description,\n languages: rule.meta.languages,\n include: rule.meta.include,\n ignore: rule.meta.ignore,\n }));\n\n return new ListResult({ rules });\n});\n","/**\n * @module\n * @since 0.1.0\n */\n\nimport { Schema } from \"effect\";\n\n/**\n * @since 0.1.0\n * @category models\n */\nexport class ReviewCommand extends Schema.TaggedClass<ReviewCommand>()(\"ReviewCommand\", {\n /** Specific hashes to mark as reviewed. */\n hashes: Schema.Array(Schema.String),\n /** When `true`, mark all current flags as reviewed. */\n all: Schema.Boolean,\n /** When `true`, wipe the state file. */\n reset: Schema.Boolean,\n}) {}\n\n/**\n * @since 0.1.0\n * @category models\n */\nexport class ReviewResult extends Schema.TaggedClass<ReviewResult>()(\"ReviewResult\", {\n /** Human-readable message describing what happened. */\n message: Schema.String,\n}) {}\n","/**\n * @module\n * @since 0.1.0\n */\n\nimport { Effect } from \"effect\";\nimport { StateStore } from \"../../shared/infrastructure/state-store.js\";\nimport { collectFlags } from \"../../shared/pipeline/collect-flags.js\";\nimport { ReviewCommand, ReviewResult } from \"./request.js\";\n\n/** @since 0.1.0 */\nexport const reviewHandler = Effect.fn(\"reviewHandler\")(function* (command: ReviewCommand) {\n const stateStore = yield* StateStore;\n\n if (command.reset) {\n yield* stateStore.reset();\n return new ReviewResult({ message: \"Cleared .agentlint-state\" });\n }\n\n if (command.all) {\n const result = yield* collectFlags({\n all: true,\n rules: [],\n dryRun: false,\n base: undefined,\n files: [],\n });\n const allFlags = result.flags;\n if (allFlags.length === 0) {\n return new ReviewResult({ message: \"No flags to review.\" });\n }\n yield* stateStore.append(allFlags.map((f) => f.hash));\n return new ReviewResult({ message: `Marked ${allFlags.length} flag(s) as reviewed.` });\n }\n\n if (command.hashes.length > 0) {\n yield* stateStore.append([...command.hashes]);\n return new ReviewResult({ message: `Marked ${command.hashes.length} hash(es) as reviewed.` });\n }\n\n return new ReviewResult({\n message: [\n \"Usage:\",\n \" agentlint review <hash...> Mark specific flags as reviewed\",\n \" agentlint review --all Mark all current flags as reviewed\",\n \" agentlint review --reset Wipe the state file\",\n ].join(\"\\n\"),\n });\n});\n","/**\n * Terminal reporter — formats flag results into human-readable output.\n *\n * Respects `NO_COLOR` and non-TTY environments. Groups flags by rule,\n * then by file, and appends instruction/hint blocks when available.\n *\n * @module\n * @since 0.1.0\n */\n\nimport { Effect, HashMap, Option, Path, Schema } from \"effect\";\nimport { Env } from \"../config/env.js\";\nimport type { FlagRecord } from \"../domain/flag.js\";\nimport type { RuleMeta } from \"../domain/rule.js\";\n\n/**\n * Group an array by a key function, returning a HashMap of arrays.\n *\n * @since 0.1.0\n * @category internals\n */\nfunction groupBy<A>(items: ReadonlyArray<A>, key: (a: A) => string): HashMap.HashMap<string, A[]> {\n return items.reduce((acc, item) => {\n const k = key(item);\n const existing = Option.getOrUndefined(HashMap.get(acc, k));\n return existing ? (existing.push(item), acc) : HashMap.set(acc, k, [item]);\n }, HashMap.empty<string, A[]>());\n}\n\n/**\n * Build the minimal ANSI escape helpers. Each function is a no-op when\n * `noColor` is `true`.\n *\n * @since 0.1.0\n * @category internals\n */\nfunction makeAnsi(noColor: boolean) {\n return {\n bold: (s: string) => (noColor ? s : `\\x1b[1m${s}\\x1b[22m`),\n dim: (s: string) => (noColor ? s : `\\x1b[2m${s}\\x1b[22m`),\n yellow: (s: string) => (noColor ? s : `\\x1b[33m${s}\\x1b[39m`),\n cyan: (s: string) => (noColor ? s : `\\x1b[36m${s}\\x1b[39m`),\n magenta: (s: string) => (noColor ? s : `\\x1b[35m${s}\\x1b[39m`),\n gray: (s: string) => (noColor ? s : `\\x1b[90m${s}\\x1b[39m`),\n underline: (s: string) => (noColor ? s : `\\x1b[4m${s}\\x1b[24m`),\n reset: noColor ? \"\" : \"\\x1b[0m\",\n };\n}\n\n/**\n * Options that control the reporter's output format.\n *\n * @since 0.1.0\n * @category models\n */\nexport const ReporterOptions = Schema.Struct({\n /** When `true`, instruction and hint blocks are suppressed. */\n dryRun: Schema.Boolean,\n /** Version string displayed in the header line. */\n version: Schema.String,\n});\n\n/** @since 0.1.0 */\nexport type ReporterOptions = Schema.Schema.Type<typeof ReporterOptions>;\n\n/**\n * Format flag results into a terminal-friendly report string.\n *\n * Groups flags by rule name, then by file path. Includes source\n * snippets, per-match instructions/hints, and a summary line.\n * Returns a single \"no rules triggered\" line when the flag list\n * is empty.\n *\n * Uses the `Env` service for colour/cwd detection and the Effect\n * `Path` service for cross-platform path resolution.\n *\n * @since 0.1.0\n * @category constructors\n */\nexport const formatReport = Effect.fn(\"formatReport\")(function* (\n flags: ReadonlyArray<FlagRecord>,\n rulesMeta: HashMap.HashMap<string, RuleMeta>,\n options: ReporterOptions,\n) {\n const env = yield* Env;\n const path = yield* Path.Path;\n const ansi = makeAnsi(env.noColor);\n\n if (flags.length === 0) {\n return `${ansi.bold(\"agentlint\")} ${ansi.dim(`v${options.version}`)} ${ansi.dim(\"-\")} no rules triggered.`;\n }\n\n const { cwd } = env;\n const lines: string[] = [];\n\n const grouped = groupBy(flags, (f) => f.ruleName);\n\n const groupedSize = HashMap.size(grouped);\n\n if (flags.length > 50) {\n lines.push(\n ansi.yellow(\"⚠\") +\n ` ${flags.length} matches across ${groupedSize} rules. ` +\n ansi.dim(\"Consider narrowing scope with --rule or targeting specific files.\"),\n );\n lines.push(\"\");\n }\n\n for (const [ruleName, ruleFlags] of grouped) {\n const meta = Option.getOrUndefined(HashMap.get(rulesMeta, ruleName));\n\n lines.push(ansi.yellow(` x ${ruleName}`) + ansi.dim(meta ? `: ${meta.description}` : \"\"));\n lines.push(\"\");\n\n const byFile = groupBy(ruleFlags, (f) => path.relative(cwd, f.filename).replace(/\\\\/g, \"/\"));\n\n for (const [_filePath, fileFlags] of byFile) {\n for (const flag of fileFlags) {\n const relPath = path.relative(cwd, flag.filename).replace(/\\\\/g, \"/\");\n const loc = `${relPath}:${flag.line}:${flag.col}`;\n const snippet = flag.sourceSnippet.length > 80 ? flag.sourceSnippet.slice(0, 77) + \"...\" : flag.sourceSnippet;\n\n lines.push(` ${ansi.cyan(loc)} ${ansi.dim(`[${flag.hash}]`)} ${flag.message}`);\n if (snippet && snippet !== flag.message) {\n lines.push(` ${ansi.dim(snippet)}`);\n }\n lines.push(\"\");\n }\n }\n\n if (!options.dryRun && meta?.instruction) {\n lines.push(ansi.dim(\" ┌─ Instruction ─────────────────────────────────\"));\n for (const instrLine of meta.instruction.split(\"\\n\")) {\n lines.push(ansi.dim(` │ ${instrLine}`));\n }\n lines.push(ansi.dim(\" └───────────────────────────────────────────────\"));\n lines.push(\"\");\n }\n\n const matchNotes = ruleFlags.filter((f) => f.instruction || f.suggest);\n if (!options.dryRun && matchNotes.length > 0) {\n for (const flag of matchNotes) {\n const relPath = path.relative(cwd, flag.filename).replace(/\\\\/g, \"/\");\n if (flag.instruction) {\n lines.push(` ${ansi.magenta(\"note\")} ${ansi.dim(`${relPath}:${flag.line}`)} ${flag.instruction}`);\n }\n if (flag.suggest) {\n lines.push(` ${ansi.magenta(\"hint\")} ${ansi.dim(`${relPath}:${flag.line}`)} ${flag.suggest}`);\n }\n }\n lines.push(\"\");\n }\n }\n\n const ruleWord = groupedSize === 1 ? \"rule\" : \"rules\";\n const matchWord = flags.length === 1 ? \"match\" : \"matches\";\n lines.push(ansi.bold(ansi.yellow(`Found ${flags.length} ${matchWord}`)) + ansi.dim(` (${groupedSize} ${ruleWord})`));\n\n return lines.join(\"\\n\");\n});\n","#!/usr/bin/env node\n/**\n * CLI entry point for `agentlint`.\n *\n * Thin adapter that translates CLI arguments into feature commands,\n * dispatches to the appropriate handler, and formats the result\n * for terminal output.\n *\n * @module\n * @since 0.1.0\n */\n\nimport * as NodeRuntime from \"@effect/platform-node/NodeRuntime\";\nimport * as NodeServices from \"@effect/platform-node/NodeServices\";\nimport { Console, Effect, HashMap, Layer, Option } from \"effect\";\nimport { Argument, Command, Flag } from \"effect/unstable/cli\";\nimport { checkHandler } from \"./features/check/handler.js\";\nimport { CheckCommand } from \"./features/check/request.js\";\nimport { initHandler } from \"./features/init/handler.js\";\nimport { InitCommand } from \"./features/init/request.js\";\nimport { listHandler } from \"./features/list/handler.js\";\nimport { ListCommand } from \"./features/list/request.js\";\nimport { reviewHandler } from \"./features/review/handler.js\";\nimport { ReviewCommand } from \"./features/review/request.js\";\nimport { Env } from \"./config/env.js\";\nimport { ConfigLoader } from \"./shared/infrastructure/config-loader.js\";\nimport { Git } from \"./shared/infrastructure/git.js\";\nimport { Parser } from \"./shared/infrastructure/parser.js\";\nimport { StateStore } from \"./shared/infrastructure/state-store.js\";\nimport { formatReport } from \"./cli/reporter.js\";\nimport type { RuleMeta } from \"./domain/rule.js\";\n\ndeclare const __AGENTLINT_VERSION__: string;\n\n/** The `check` subcommand — scans files and outputs a report. */\nconst check = Command.make(\n \"check\",\n {\n files: Argument.string(\"files\").pipe(\n Argument.withDescription(\"Specific files or globs to scan\"),\n Argument.variadic(),\n ),\n all: Flag.boolean(\"all\").pipe(Flag.withAlias(\"a\"), Flag.withDescription(\"Scan all files (not just git diff)\")),\n rule: Flag.string(\"rule\").pipe(\n Flag.withAlias(\"r\"),\n Flag.withDescription(\"Run only this rule (comma-separated for multiple)\"),\n Flag.optional,\n ),\n dryRun: Flag.boolean(\"dry-run\").pipe(\n Flag.withAlias(\"d\"),\n Flag.withDescription(\"Show counts only, no instruction blocks\"),\n ),\n base: Flag.string(\"base\").pipe(Flag.withDescription(\"Git ref to diff against\"), Flag.optional),\n },\n (config) => {\n const ruleFilter = Option.match(config.rule, {\n onNone: () => [] as ReadonlyArray<string>,\n onSome: (r: string) => r.split(\",\").map((s) => s.trim()),\n });\n const baseRef = Option.match(config.base, {\n onNone: () => undefined,\n onSome: (b: string) => b,\n });\n\n return Effect.gen(function* () {\n const env = yield* Env;\n const result = yield* checkHandler(\n new CheckCommand({\n all: config.all,\n rules: ruleFilter,\n dryRun: config.dryRun,\n base: baseRef,\n files: config.files,\n }),\n );\n\n if (result.noMatchingRules) {\n yield* Console.log(`No matching rules found. Available: ${result.availableRules.join(\", \")}`);\n return;\n }\n\n if (result.totalFlags === 0) {\n yield* Console.log(`agentlint v${__AGENTLINT_VERSION__} - no rules triggered.`);\n return;\n }\n\n const configLoader = yield* ConfigLoader;\n const cfg = yield* configLoader.load();\n const rulesMeta: HashMap.HashMap<string, RuleMeta> = HashMap.fromIterable(\n Object.entries(cfg.rules).map(([name, rule]) => [name, rule.meta] as const),\n );\n\n const output = yield* formatReport(result.flags, rulesMeta, {\n dryRun: config.dryRun,\n version: __AGENTLINT_VERSION__,\n });\n\n yield* Console.log(output);\n\n if (result.filteredCount > 0) {\n yield* Console.log(\n ` (${result.filteredCount} reviewed flag(s) hidden — run agentlint review --reset to clear)`,\n );\n }\n\n if (result.flags.length > 0) {\n env.setExitCode(1);\n }\n });\n },\n).pipe(Command.withDescription(\"Scan files and output report for AI agents\"));\n\n/** The `list` subcommand — prints all registered rules. */\nconst list = Command.make(\"list\", {}, () =>\n Effect.gen(function* () {\n const result = yield* listHandler(new ListCommand({}));\n\n if (result.rules.length === 0) {\n yield* Console.log(\"No rules registered.\");\n return;\n }\n\n yield* Console.log(`${result.rules.length} rule(s) registered:\\n`);\n\n for (const rule of result.rules) {\n const langs = rule.languages.join(\", \");\n yield* Console.log(` ${rule.name}`);\n yield* Console.log(` ${rule.description}`);\n yield* Console.log(` Languages: ${langs}`);\n if (rule.include) {\n yield* Console.log(` Include: ${rule.include.join(\", \")}`);\n }\n if (rule.ignore) {\n yield* Console.log(` Ignore: ${rule.ignore.join(\", \")}`);\n }\n yield* Console.log();\n }\n }),\n).pipe(Command.withDescription(\"List all registered rules\"));\n\n/** The `init` subcommand — scaffolds a starter config file. */\nconst init = Command.make(\"init\", {}, () =>\n Effect.gen(function* () {\n const result = yield* initHandler(new InitCommand({}));\n yield* Console.log(result.message);\n }),\n).pipe(Command.withDescription(\"Create agentlint.config.ts and set up agent skill discovery\"));\n\n/** The `review` subcommand — manage reviewed-flag state. */\nconst review = Command.make(\n \"review\",\n {\n hashes: Argument.string(\"hashes\").pipe(\n Argument.withDescription(\"Flag hashes to mark as reviewed\"),\n Argument.variadic(),\n ),\n all: Flag.boolean(\"all\").pipe(Flag.withAlias(\"a\"), Flag.withDescription(\"Mark all current flags as reviewed\")),\n reset: Flag.boolean(\"reset\").pipe(Flag.withDescription(\"Wipe the state file\")),\n },\n (config) =>\n Effect.gen(function* () {\n const result = yield* reviewHandler(\n new ReviewCommand({\n hashes: config.hashes,\n all: config.all,\n reset: config.reset,\n }),\n );\n yield* Console.log(result.message);\n }),\n).pipe(Command.withDescription(\"Mark flags as reviewed (filters them from check output)\"));\n\nconst agentlint = Command.make(\"agentlint\").pipe(\n Command.withDescription(\"Deterministic linting for AI agents\"),\n Command.withSubcommands([check, list, init, review]),\n);\n\nconst AppLayer = Layer.mergeAll(ConfigLoader.layer, Parser.layer, Git.layer, StateStore.layer).pipe(\n Layer.provideMerge(NodeServices.layer),\n Layer.provideMerge(Env.layer),\n);\n\n// The CLI framework uses `unknown` for aggregated service requirements,\n// which doesn't fully resolve via `Effect.provide`. Cast to satisfy\n// `NodeRuntime.runMain`'s `never` requirement constraint.\nconst program = Command.run(agentlint, { version: __AGENTLINT_VERSION__ }).pipe(\n Effect.provide(AppLayer),\n) as Effect.Effect<void>;\n\nNodeRuntime.runMain(program);\n"],"mappings":"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"}
|
package/dist/index.d.mts
CHANGED
|
@@ -85,7 +85,7 @@ interface FlagOptions {
|
|
|
85
85
|
/** Hint toward the fix. Not a command - just a nudge. */
|
|
86
86
|
readonly suggest?: string | undefined;
|
|
87
87
|
}
|
|
88
|
-
declare const FlagRecord_base: Schema.
|
|
88
|
+
declare const FlagRecord_base: Schema.Class<FlagRecord, Schema.Struct<{
|
|
89
89
|
readonly ruleName: Schema.String;
|
|
90
90
|
readonly filename: Schema.String; /** 1-based line number. */
|
|
91
91
|
readonly line: Schema.Number; /** 1-based column number. */
|
|
@@ -141,7 +141,7 @@ interface RuleContext {
|
|
|
141
141
|
* @category models
|
|
142
142
|
*/
|
|
143
143
|
declare const RuleMeta: Schema.Struct<{
|
|
144
|
-
/** Unique identifier. kebab-case. Used in output, --rule filtering,
|
|
144
|
+
/** Unique identifier. kebab-case. Used in output, --rule filtering, agentlint-ignore. */readonly name: Schema.String; /** One-liner explaining what the rule checks. */
|
|
145
145
|
readonly description: Schema.String; /** File extensions this rule applies to, without the dot. e.g. `["ts", "tsx"]` */
|
|
146
146
|
readonly languages: Schema.$Array<Schema.String>;
|
|
147
147
|
/**
|
|
@@ -193,7 +193,7 @@ type Visitors = {
|
|
|
193
193
|
interface AgentReviewRule {
|
|
194
194
|
readonly meta: RuleMeta;
|
|
195
195
|
/**
|
|
196
|
-
* Called once per
|
|
196
|
+
* Called once per agentlint run (not per file).
|
|
197
197
|
* The returned visitor object is reused across files.
|
|
198
198
|
* Per-file state must be reset in `before()`.
|
|
199
199
|
*/
|
|
@@ -204,7 +204,7 @@ interface AgentReviewRule {
|
|
|
204
204
|
*
|
|
205
205
|
* @example
|
|
206
206
|
* ```ts
|
|
207
|
-
* import { defineRule } from "
|
|
207
|
+
* import { defineRule } from "agentlint"
|
|
208
208
|
*
|
|
209
209
|
* export const myRule = defineRule({
|
|
210
210
|
* meta: {
|
|
@@ -230,7 +230,7 @@ declare function defineRule(rule: AgentReviewRule): AgentReviewRule;
|
|
|
230
230
|
//#endregion
|
|
231
231
|
//#region src/domain/config.d.ts
|
|
232
232
|
/**
|
|
233
|
-
* Top-level configuration schema for `
|
|
233
|
+
* Top-level configuration schema for `agentlint.config.ts`.
|
|
234
234
|
*
|
|
235
235
|
* @since 0.1.0
|
|
236
236
|
* @category models
|
|
@@ -248,7 +248,7 @@ interface AgentReviewConfig {
|
|
|
248
248
|
*
|
|
249
249
|
* @example
|
|
250
250
|
* ```ts
|
|
251
|
-
* import { defineConfig } from "
|
|
251
|
+
* import { defineConfig } from "agentlint"
|
|
252
252
|
*
|
|
253
253
|
* export default defineConfig({
|
|
254
254
|
* include: ["src/**\/*.ts"],
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/domain/node.ts","../src/domain/node-types.ts","../src/domain/flag.ts","../src/domain/rule-context.ts","../src/domain/rule.ts","../src/domain/config.ts"],"mappings":";;;;;;;;;;;cAuBa,QAAA,EAAQ,MAAA,CAAA,MAAA;EAAA;;;;KAMT,QAAA,GAAW,MAAA,CAAO,MAAA,CAAO,IAAA,QAAY,QAAA;;;;;;AAWjD;;;;UAAiB,eAAA;EAYkB;EAAA,SAVxB,IAAA;EAYQ;EAAA,SAVR,IAAA;EAiBmC;EAAA,SAfnC,aAAA,EAAe,QAAA;EAiBuB;EAAA,SAftC,WAAA,EAAa,QAAA;EAewB;EAAA,SAbrC,OAAA;EARA;EAAA,SAUA,QAAA,EAAU,aAAA,CAAc,eAAA;EANxB;EAAA,SAQA,MAAA,EAAQ,eAAA;EANR;EAAA,SAQA,UAAA;EANA;EAST,gBAAA,CAAiB,IAAA,WAAe,eAAA;EAPb;EASnB,cAAA,CAAe,IAAA,WAAe,aAAA,CAAc,eAAA;EAPnC;EAST,iBAAA,CAAkB,IAAA,WAAe,aAAA,CAAc,eAAA;AAAA;;;;;;;AAxCjD;;;;;;;;;KCTY,kBAAA;;;;;;;;;UCKK,WAAA;;WAEN,IAAA,EAAM,eAAA;;WAEN,OAAA;EFMC;;;;EAAA,SEDD,WAAA;EFCmB;EAAA,SECnB,OAAA;AAAA;AAAA,cACV,eAAA;;oCFS+B;EAAA,8BAMN;EAAA;;yCAQP;EAAA;;;;;;;;;;;;;cEZN,UAAA,SAAmB,eAAA;;;;;;;;;UCvBf,WAAA;;EAEf,WAAA;;EAEA,aAAA;;AHMF;;;;EGAE,cAAA,CAAe,IAAA,UAAc,MAAA;EHAD;EGE5B,IAAA,CAAK,OAAA,EAAS,WAAA;AAAA;;;;;;;;;;;AHFhB;;cIHa,QAAA,EAAQ,MAAA,CAAA,MAAA;EJGoB,
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/domain/node.ts","../src/domain/node-types.ts","../src/domain/flag.ts","../src/domain/rule-context.ts","../src/domain/rule.ts","../src/domain/config.ts"],"mappings":";;;;;;;;;;;cAuBa,QAAA,EAAQ,MAAA,CAAA,MAAA;EAAA;;;;KAMT,QAAA,GAAW,MAAA,CAAO,MAAA,CAAO,IAAA,QAAY,QAAA;;;;;;AAWjD;;;;UAAiB,eAAA;EAYkB;EAAA,SAVxB,IAAA;EAYQ;EAAA,SAVR,IAAA;EAiBmC;EAAA,SAfnC,aAAA,EAAe,QAAA;EAiBuB;EAAA,SAftC,WAAA,EAAa,QAAA;EAewB;EAAA,SAbrC,OAAA;EARA;EAAA,SAUA,QAAA,EAAU,aAAA,CAAc,eAAA;EANxB;EAAA,SAQA,MAAA,EAAQ,eAAA;EANR;EAAA,SAQA,UAAA;EANA;EAST,gBAAA,CAAiB,IAAA,WAAe,eAAA;EAPb;EASnB,cAAA,CAAe,IAAA,WAAe,aAAA,CAAc,eAAA;EAPnC;EAST,iBAAA,CAAkB,IAAA,WAAe,aAAA,CAAc,eAAA;AAAA;;;;;;;AAxCjD;;;;;;;;;KCTY,kBAAA;;;;;;;;;UCKK,WAAA;;WAEN,IAAA,EAAM,eAAA;;WAEN,OAAA;EFMC;;;;EAAA,SEDD,WAAA;EFCmB;EAAA,SECnB,OAAA;AAAA;AAAA,cACV,eAAA;;oCFS+B;EAAA,8BAMN;EAAA;;yCAQP;EAAA;;;;;;;;;;;;;cEZN,UAAA,SAAmB,eAAA;;;;;;;;;UCvBf,WAAA;;EAEf,WAAA;;EAEA,aAAA;;AHMF;;;;EGAE,cAAA,CAAe,IAAA,UAAc,MAAA;EHAD;EGE5B,IAAA,CAAK,OAAA,EAAS,WAAA;AAAA;;;;;;;;;;;AHFhB;;cIHa,QAAA,EAAQ,MAAA,CAAA,MAAA;EJGoB,uHAAX;EAAA,qCAAmB;EAAA;EAAQ;AAWzD;;;EAXyD,qCAmBjC;EAAA,iEAIH;EAAA;;;KIPT,QAAA,GAAW,MAAA,CAAO,MAAA,CAAO,IAAA,QAAY,QAAA;;;;;;;KAQrC,cAAA,IAAkB,IAAA,EAAM,eAAA;;;;;;;;;;KAWxB,QAAA;EJLV;;;;EIUA,MAAA,KAAW,QAAA;EJRmB;;;;EIa9B,KAAA;AAAA,YACU,kBAAA,IAAsB,cAAA;EAAA,CAC/B,QAAA,WAAmB,cAAA,KAAmB,QAAA;AAAA;;AH9DzC;;;;;UGuEiB,eAAA;EAAA,SACN,IAAA,EAAM,QAAA;;AFnEjB;;;;WEyEW,UAAA,GAAa,OAAA,EAAS,WAAA,KAAgB,QAAA;AAAA;;;;;;AF7DhD;;;;;;;;;;;;;;;;;;;;;;iBE2Fe,UAAA,CAAW,IAAA,EAAM,eAAA,GAAkB,eAAA;;;;;;;;;UCrGlC,iBAAA;;WAEN,KAAA,EAAO,MAAA,SAAe,eAAA;;WAEtB,OAAA,GAAU,aAAA;ELIT;EAAA,SKFD,MAAA,GAAS,aAAA;AAAA;;;;;;;;ALapB;;;;;;;;;iBKMgB,YAAA,CAAa,MAAA,EAAQ,iBAAA,GAAoB,iBAAA"}
|
package/dist/index.mjs
CHANGED
|
@@ -34,7 +34,7 @@ const RuleMeta = Schema.Struct({
|
|
|
34
34
|
*
|
|
35
35
|
* @example
|
|
36
36
|
* ```ts
|
|
37
|
-
* import { defineRule } from "
|
|
37
|
+
* import { defineRule } from "agentlint"
|
|
38
38
|
*
|
|
39
39
|
* export const myRule = defineRule({
|
|
40
40
|
* meta: {
|
|
@@ -65,7 +65,7 @@ function defineRule(rule) {
|
|
|
65
65
|
/**
|
|
66
66
|
* Configuration types and the `defineConfig` helper.
|
|
67
67
|
*
|
|
68
|
-
* A config file (`
|
|
68
|
+
* A config file (`agentlint.config.ts`) default-exports an {@link AgentReviewConfig}
|
|
69
69
|
* object that maps rule names to rule definitions and optionally scopes which
|
|
70
70
|
* files are scanned.
|
|
71
71
|
*
|
|
@@ -77,7 +77,7 @@ function defineRule(rule) {
|
|
|
77
77
|
*
|
|
78
78
|
* @example
|
|
79
79
|
* ```ts
|
|
80
|
-
* import { defineConfig } from "
|
|
80
|
+
* import { defineConfig } from "agentlint"
|
|
81
81
|
*
|
|
82
82
|
* export default defineConfig({
|
|
83
83
|
* include: ["src/**\/*.ts"],
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/domain/rule.ts","../src/domain/config.ts"],"sourcesContent":["/**\n * Rule definition types and the `defineRule` helper.\n *\n * A rule is a reusable lint check defined by {@link RuleMeta} (what to check)\n * and a `createOnce` factory (how to check it). The factory returns a\n * {@link Visitors} object whose keys are tree-sitter node types.\n *\n * @module\n * @since 0.1.0\n */\n\nimport { Schema } from \"effect\";\nimport type { AgentReviewNode } from \"./node.js\";\nimport type { TreeSitterNodeType } from \"./node-types.js\";\nimport type { RuleContext } from \"./rule-context.js\";\n\n/**\n * Static metadata for a rule.\n *\n * Defined as a `Schema.Struct` so that rule metadata is validated at\n * runtime when `defineRule` is called — catches typos, missing fields,\n * and wrong types with clear error messages.\n *\n * @since 0.1.0\n * @category models\n */\nexport const RuleMeta = Schema.Struct({\n /** Unique identifier. kebab-case. Used in output, --rule filtering,
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/domain/rule.ts","../src/domain/config.ts"],"sourcesContent":["/**\n * Rule definition types and the `defineRule` helper.\n *\n * A rule is a reusable lint check defined by {@link RuleMeta} (what to check)\n * and a `createOnce` factory (how to check it). The factory returns a\n * {@link Visitors} object whose keys are tree-sitter node types.\n *\n * @module\n * @since 0.1.0\n */\n\nimport { Schema } from \"effect\";\nimport type { AgentReviewNode } from \"./node.js\";\nimport type { TreeSitterNodeType } from \"./node-types.js\";\nimport type { RuleContext } from \"./rule-context.js\";\n\n/**\n * Static metadata for a rule.\n *\n * Defined as a `Schema.Struct` so that rule metadata is validated at\n * runtime when `defineRule` is called — catches typos, missing fields,\n * and wrong types with clear error messages.\n *\n * @since 0.1.0\n * @category models\n */\nexport const RuleMeta = Schema.Struct({\n /** Unique identifier. kebab-case. Used in output, --rule filtering, agentlint-ignore. */\n name: Schema.String,\n /** One-liner explaining what the rule checks. */\n description: Schema.String,\n /** File extensions this rule applies to, without the dot. e.g. `[\"ts\", \"tsx\"]` */\n languages: Schema.Array(Schema.String),\n /**\n * Natural language instruction for the calling AI agent.\n * Defines pass/fail criteria and how to evaluate flagged matches.\n */\n instruction: Schema.String,\n /** If provided, rule only runs on files matching these globs (after global filtering). */\n include: Schema.optional(Schema.Array(Schema.String)),\n /** If provided, files matching these globs are excluded from this rule. */\n ignore: Schema.optional(Schema.Array(Schema.String)),\n});\n\n/** @since 0.1.0 */\nexport type RuleMeta = Schema.Schema.Type<typeof RuleMeta>;\n\n/**\n * Callback invoked when a matching AST node type is visited.\n *\n * @since 0.1.0\n * @category models\n */\nexport type VisitorHandler = (node: AgentReviewNode) => void;\n\n/**\n * Visitor object returned by `createOnce`.\n *\n * Maps tree-sitter node type strings to handler functions.\n * Known node types provide autocomplete; any string is accepted.\n *\n * @since 0.1.0\n * @category models\n */\nexport type Visitors = {\n /**\n * Called once before each file is traversed.\n * Return `false` to skip this file entirely for this rule.\n */\n before?: ((filename: string) => boolean | void) | undefined;\n /**\n * Called once after all files have been visited.\n * Use for aggregate analysis.\n */\n after?: (() => void) | undefined;\n} & { [K in TreeSitterNodeType]?: VisitorHandler } & {\n [nodeType: string]: VisitorHandler | ((filename: string) => boolean | void) | (() => void) | undefined;\n};\n\n/**\n * A complete rule definition.\n *\n * @since 0.1.0\n * @category models\n */\nexport interface AgentReviewRule {\n readonly meta: RuleMeta;\n /**\n * Called once per agentlint run (not per file).\n * The returned visitor object is reused across files.\n * Per-file state must be reset in `before()`.\n */\n readonly createOnce: (context: RuleContext) => Visitors;\n}\n\n/**\n * Identity function that provides type inference and IDE support for rule definitions.\n *\n * @example\n * ```ts\n * import { defineRule } from \"agentlint\"\n *\n * export const myRule = defineRule({\n * meta: {\n * name: \"my-rule\",\n * description: \"Checks for something\",\n * languages: [\"ts\", \"tsx\"],\n * instruction: \"Evaluate whether ...\"\n * },\n * createOnce(context) {\n * return {\n * comment(node) {\n * context.flag({ node, message: \"Found comment\" })\n * }\n * }\n * }\n * })\n * ```\n *\n * @since 0.1.0\n * @category constructors\n */\nexport function defineRule(rule: AgentReviewRule): AgentReviewRule {\n Schema.decodeUnknownSync(RuleMeta)(rule.meta);\n return rule;\n}\n","/**\n * Configuration types and the `defineConfig` helper.\n *\n * A config file (`agentlint.config.ts`) default-exports an {@link AgentReviewConfig}\n * object that maps rule names to rule definitions and optionally scopes which\n * files are scanned.\n *\n * @module\n * @since 0.1.0\n */\n\nimport { Schema } from \"effect\";\nimport type { AgentReviewRule } from \"./rule.js\";\nimport { RuleMeta } from \"./rule.js\";\n\n/**\n * Top-level configuration schema for `agentlint.config.ts`.\n *\n * @since 0.1.0\n * @category models\n */\nexport interface AgentReviewConfig {\n /** Rule registry. Keys are kebab-case names used in output and `--rule` filtering. */\n readonly rules: Record<string, AgentReviewRule>;\n /** If provided, only files matching at least one pattern are scanned. */\n readonly include?: ReadonlyArray<string> | undefined;\n /** Files matching any pattern are excluded (merged with built-in defaults). */\n readonly ignore?: ReadonlyArray<string> | undefined;\n}\n\n/**\n * Identity function that provides type inference and IDE support for config files.\n *\n * @example\n * ```ts\n * import { defineConfig } from \"agentlint\"\n *\n * export default defineConfig({\n * include: [\"src/**\\/*.ts\"],\n * rules: { \"my-rule\": myRule }\n * })\n * ```\n *\n * @since 0.1.0\n * @category constructors\n */\nexport function defineConfig(config: AgentReviewConfig): AgentReviewConfig {\n for (const rule of Object.values(config.rules)) {\n Schema.decodeUnknownSync(RuleMeta)(rule.meta);\n }\n return config;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AA0BA,MAAa,WAAW,OAAO,OAAO;CAEpC,MAAM,OAAO;CAEb,aAAa,OAAO;CAEpB,WAAW,OAAO,MAAM,OAAO,OAAO;CAKtC,aAAa,OAAO;CAEpB,SAAS,OAAO,SAAS,OAAO,MAAM,OAAO,OAAO,CAAC;CAErD,QAAQ,OAAO,SAAS,OAAO,MAAM,OAAO,OAAO,CAAC;CACrD,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgFF,SAAgB,WAAW,MAAwC;AACjE,QAAO,kBAAkB,SAAS,CAAC,KAAK,KAAK;AAC7C,QAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC9ET,SAAgB,aAAa,QAA8C;AACzE,MAAK,MAAM,QAAQ,OAAO,OAAO,OAAO,MAAM,CAC5C,QAAO,kBAAkB,SAAS,CAAC,KAAK,KAAK;AAE/C,QAAO"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aurelienbbn/agentlint",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Stateless, deterministic CLI that bridges traditional linters and AI-assisted code review",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent",
|
|
@@ -33,8 +33,8 @@
|
|
|
33
33
|
}
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@effect/platform-node": "4.0.0-beta.
|
|
37
|
-
"effect": "
|
|
36
|
+
"@effect/platform-node": "4.0.0-beta.44",
|
|
37
|
+
"effect": "4.0.0-beta.44",
|
|
38
38
|
"jiti": "^2.4.2",
|
|
39
39
|
"picomatch": "^4.0.2",
|
|
40
40
|
"web-tree-sitter": "^0.25.3"
|