@lbroth/rothunter 1.0.0-rc.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/LICENSE +21 -0
- package/README.md +141 -0
- package/dist/adapters/llm.d.ts +68 -0
- package/dist/adapters/llm.d.ts.map +1 -0
- package/dist/adapters/llm.js +189 -0
- package/dist/adapters/llm.js.map +1 -0
- package/dist/config.d.ts +37 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +81 -0
- package/dist/config.js.map +1 -0
- package/dist/detector-registry.d.ts +32 -0
- package/dist/detector-registry.d.ts.map +1 -0
- package/dist/detector-registry.js +74 -0
- package/dist/detector-registry.js.map +1 -0
- package/dist/detectors/api-race.d.ts +6 -0
- package/dist/detectors/api-race.d.ts.map +1 -0
- package/dist/detectors/api-race.js +222 -0
- package/dist/detectors/api-race.js.map +1 -0
- package/dist/detectors/bad-config.d.ts +6 -0
- package/dist/detectors/bad-config.d.ts.map +1 -0
- package/dist/detectors/bad-config.js +529 -0
- package/dist/detectors/bad-config.js.map +1 -0
- package/dist/detectors/console-log-prod.d.ts +6 -0
- package/dist/detectors/console-log-prod.d.ts.map +1 -0
- package/dist/detectors/console-log-prod.js +72 -0
- package/dist/detectors/console-log-prod.js.map +1 -0
- package/dist/detectors/dead-api.d.ts +10 -0
- package/dist/detectors/dead-api.d.ts.map +1 -0
- package/dist/detectors/dead-api.js +115 -0
- package/dist/detectors/dead-api.js.map +1 -0
- package/dist/detectors/dead-export.d.ts +12 -0
- package/dist/detectors/dead-export.d.ts.map +1 -0
- package/dist/detectors/dead-export.js +140 -0
- package/dist/detectors/dead-export.js.map +1 -0
- package/dist/detectors/dead-handler.d.ts +12 -0
- package/dist/detectors/dead-handler.d.ts.map +1 -0
- package/dist/detectors/dead-handler.js +40 -0
- package/dist/detectors/dead-handler.js.map +1 -0
- package/dist/detectors/dead-module.d.ts +14 -0
- package/dist/detectors/dead-module.d.ts.map +1 -0
- package/dist/detectors/dead-module.js +50 -0
- package/dist/detectors/dead-module.js.map +1 -0
- package/dist/detectors/deep-nesting.d.ts +12 -0
- package/dist/detectors/deep-nesting.d.ts.map +1 -0
- package/dist/detectors/deep-nesting.js +133 -0
- package/dist/detectors/deep-nesting.js.map +1 -0
- package/dist/detectors/duplicate-function.d.ts +9 -0
- package/dist/detectors/duplicate-function.d.ts.map +1 -0
- package/dist/detectors/duplicate-function.js +199 -0
- package/dist/detectors/duplicate-function.js.map +1 -0
- package/dist/detectors/duplicate-type.d.ts +9 -0
- package/dist/detectors/duplicate-type.d.ts.map +1 -0
- package/dist/detectors/duplicate-type.js +166 -0
- package/dist/detectors/duplicate-type.js.map +1 -0
- package/dist/detectors/hot-hub-file.d.ts +11 -0
- package/dist/detectors/hot-hub-file.d.ts.map +1 -0
- package/dist/detectors/hot-hub-file.js +42 -0
- package/dist/detectors/hot-hub-file.js.map +1 -0
- package/dist/detectors/long-file.d.ts +12 -0
- package/dist/detectors/long-file.d.ts.map +1 -0
- package/dist/detectors/long-file.js +82 -0
- package/dist/detectors/long-file.js.map +1 -0
- package/dist/detectors/long-function.d.ts +12 -0
- package/dist/detectors/long-function.d.ts.map +1 -0
- package/dist/detectors/long-function.js +45 -0
- package/dist/detectors/long-function.js.map +1 -0
- package/dist/detectors/magic-numbers.d.ts +10 -0
- package/dist/detectors/magic-numbers.d.ts.map +1 -0
- package/dist/detectors/magic-numbers.js +332 -0
- package/dist/detectors/magic-numbers.js.map +1 -0
- package/dist/detectors/mutable-globals.d.ts +6 -0
- package/dist/detectors/mutable-globals.d.ts.map +1 -0
- package/dist/detectors/mutable-globals.js +95 -0
- package/dist/detectors/mutable-globals.js.map +1 -0
- package/dist/detectors/mutation.d.ts +11 -0
- package/dist/detectors/mutation.d.ts.map +1 -0
- package/dist/detectors/mutation.js +397 -0
- package/dist/detectors/mutation.js.map +1 -0
- package/dist/detectors/public-any.d.ts +6 -0
- package/dist/detectors/public-any.d.ts.map +1 -0
- package/dist/detectors/public-any.js +52 -0
- package/dist/detectors/public-any.js.map +1 -0
- package/dist/detectors/race-condition.d.ts +6 -0
- package/dist/detectors/race-condition.d.ts.map +1 -0
- package/dist/detectors/race-condition.js +608 -0
- package/dist/detectors/race-condition.js.map +1 -0
- package/dist/detectors/shared-db-write.d.ts +6 -0
- package/dist/detectors/shared-db-write.d.ts.map +1 -0
- package/dist/detectors/shared-db-write.js +656 -0
- package/dist/detectors/shared-db-write.js.map +1 -0
- package/dist/detectors/silent-catch.d.ts +6 -0
- package/dist/detectors/silent-catch.d.ts.map +1 -0
- package/dist/detectors/silent-catch.js +167 -0
- package/dist/detectors/silent-catch.js.map +1 -0
- package/dist/detectors/similar-functions.d.ts +15 -0
- package/dist/detectors/similar-functions.d.ts.map +1 -0
- package/dist/detectors/similar-functions.js +334 -0
- package/dist/detectors/similar-functions.js.map +1 -0
- package/dist/detectors/skip-tests.d.ts +6 -0
- package/dist/detectors/skip-tests.d.ts.map +1 -0
- package/dist/detectors/skip-tests.js +69 -0
- package/dist/detectors/skip-tests.js.map +1 -0
- package/dist/detectors/todo-comments.d.ts +29 -0
- package/dist/detectors/todo-comments.d.ts.map +1 -0
- package/dist/detectors/todo-comments.js +154 -0
- package/dist/detectors/todo-comments.js.map +1 -0
- package/dist/detectors/unused-deps.d.ts +8 -0
- package/dist/detectors/unused-deps.d.ts.map +1 -0
- package/dist/detectors/unused-deps.js +115 -0
- package/dist/detectors/unused-deps.js.map +1 -0
- package/dist/extraction/api-race-confirmer.d.ts +31 -0
- package/dist/extraction/api-race-confirmer.d.ts.map +1 -0
- package/dist/extraction/api-race-confirmer.js +110 -0
- package/dist/extraction/api-race-confirmer.js.map +1 -0
- package/dist/extraction/llm-confirmer.d.ts +25 -0
- package/dist/extraction/llm-confirmer.d.ts.map +1 -0
- package/dist/extraction/llm-confirmer.js +118 -0
- package/dist/extraction/llm-confirmer.js.map +1 -0
- package/dist/extraction/mutation-confirmer.d.ts +30 -0
- package/dist/extraction/mutation-confirmer.d.ts.map +1 -0
- package/dist/extraction/mutation-confirmer.js +73 -0
- package/dist/extraction/mutation-confirmer.js.map +1 -0
- package/dist/extraction/prompt-chunking.d.ts +37 -0
- package/dist/extraction/prompt-chunking.d.ts.map +1 -0
- package/dist/extraction/prompt-chunking.js +61 -0
- package/dist/extraction/prompt-chunking.js.map +1 -0
- package/dist/extraction/race-confirmer.d.ts +28 -0
- package/dist/extraction/race-confirmer.d.ts.map +1 -0
- package/dist/extraction/race-confirmer.js +68 -0
- package/dist/extraction/race-confirmer.js.map +1 -0
- package/dist/extraction/shared-db-write-confirmer.d.ts +31 -0
- package/dist/extraction/shared-db-write-confirmer.d.ts.map +1 -0
- package/dist/extraction/shared-db-write-confirmer.js +141 -0
- package/dist/extraction/shared-db-write-confirmer.js.map +1 -0
- package/dist/extraction/triage-confirmer.d.ts +59 -0
- package/dist/extraction/triage-confirmer.d.ts.map +1 -0
- package/dist/extraction/triage-confirmer.js +104 -0
- package/dist/extraction/triage-confirmer.js.map +1 -0
- package/dist/graph/cfg.d.ts +45 -0
- package/dist/graph/cfg.d.ts.map +1 -0
- package/dist/graph/cfg.js +198 -0
- package/dist/graph/cfg.js.map +1 -0
- package/dist/graph/decorator-entries.d.ts +2 -0
- package/dist/graph/decorator-entries.d.ts.map +1 -0
- package/dist/graph/decorator-entries.js +89 -0
- package/dist/graph/decorator-entries.js.map +1 -0
- package/dist/graph/entry-points.d.ts +12 -0
- package/dist/graph/entry-points.d.ts.map +1 -0
- package/dist/graph/entry-points.js +282 -0
- package/dist/graph/entry-points.js.map +1 -0
- package/dist/graph/handler-conventions.d.ts +2 -0
- package/dist/graph/handler-conventions.d.ts.map +1 -0
- package/dist/graph/handler-conventions.js +26 -0
- package/dist/graph/handler-conventions.js.map +1 -0
- package/dist/graph/iac-entries.d.ts +2 -0
- package/dist/graph/iac-entries.d.ts.map +1 -0
- package/dist/graph/iac-entries.js +123 -0
- package/dist/graph/iac-entries.js.map +1 -0
- package/dist/graph/import-graph.d.ts +48 -0
- package/dist/graph/import-graph.d.ts.map +1 -0
- package/dist/graph/import-graph.js +86 -0
- package/dist/graph/import-graph.js.map +1 -0
- package/dist/graph/monorepo-detect.d.ts +3 -0
- package/dist/graph/monorepo-detect.d.ts.map +1 -0
- package/dist/graph/monorepo-detect.js +166 -0
- package/dist/graph/monorepo-detect.js.map +1 -0
- package/dist/graph/tsconfig-paths.d.ts +23 -0
- package/dist/graph/tsconfig-paths.d.ts.map +1 -0
- package/dist/graph/tsconfig-paths.js +217 -0
- package/dist/graph/tsconfig-paths.js.map +1 -0
- package/dist/multi-workspace-scanner.d.ts +13 -0
- package/dist/multi-workspace-scanner.d.ts.map +1 -0
- package/dist/multi-workspace-scanner.js +130 -0
- package/dist/multi-workspace-scanner.js.map +1 -0
- package/dist/normalizers/type-normalizer.d.ts +16 -0
- package/dist/normalizers/type-normalizer.d.ts.map +1 -0
- package/dist/normalizers/type-normalizer.js +189 -0
- package/dist/normalizers/type-normalizer.js.map +1 -0
- package/dist/parsers/typescript-parser.d.ts +57 -0
- package/dist/parsers/typescript-parser.d.ts.map +1 -0
- package/dist/parsers/typescript-parser.js +502 -0
- package/dist/parsers/typescript-parser.js.map +1 -0
- package/dist/reporter/json-reporter.d.ts +12 -0
- package/dist/reporter/json-reporter.d.ts.map +1 -0
- package/dist/reporter/json-reporter.js +28 -0
- package/dist/reporter/json-reporter.js.map +1 -0
- package/dist/reporter/markdown-reporter.d.ts +11 -0
- package/dist/reporter/markdown-reporter.d.ts.map +1 -0
- package/dist/reporter/markdown-reporter.js +77 -0
- package/dist/reporter/markdown-reporter.js.map +1 -0
- package/dist/rothunter.d.ts +125 -0
- package/dist/rothunter.d.ts.map +1 -0
- package/dist/rothunter.js +1038 -0
- package/dist/rothunter.js.map +1 -0
- package/dist/server/false-positives.d.ts +34 -0
- package/dist/server/false-positives.d.ts.map +1 -0
- package/dist/server/false-positives.js +85 -0
- package/dist/server/false-positives.js.map +1 -0
- package/dist/server/index.d.ts +2 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +1529 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/marked-to-fix.d.ts +16 -0
- package/dist/server/marked-to-fix.d.ts.map +1 -0
- package/dist/server/marked-to-fix.js +36 -0
- package/dist/server/marked-to-fix.js.map +1 -0
- package/dist/server/scan-store.d.ts +147 -0
- package/dist/server/scan-store.d.ts.map +1 -0
- package/dist/server/scan-store.js +291 -0
- package/dist/server/scan-store.js.map +1 -0
- package/dist/server/settings-store.d.ts +28 -0
- package/dist/server/settings-store.d.ts.map +1 -0
- package/dist/server/settings-store.js +46 -0
- package/dist/server/settings-store.js.map +1 -0
- package/dist/server/workspace-store.d.ts +39 -0
- package/dist/server/workspace-store.d.ts.map +1 -0
- package/dist/server/workspace-store.js +108 -0
- package/dist/server/workspace-store.js.map +1 -0
- package/dist/types/detector-input.d.ts +37 -0
- package/dist/types/detector-input.d.ts.map +1 -0
- package/dist/types/detector-input.js +2 -0
- package/dist/types/detector-input.js.map +1 -0
- package/dist/types.d.ts +110 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/clustering.d.ts +14 -0
- package/dist/utils/clustering.d.ts.map +1 -0
- package/dist/utils/clustering.js +56 -0
- package/dist/utils/clustering.js.map +1 -0
- package/dist/utils/gitignore.d.ts +32 -0
- package/dist/utils/gitignore.d.ts.map +1 -0
- package/dist/utils/gitignore.js +122 -0
- package/dist/utils/gitignore.js.map +1 -0
- package/dist/utils/hash.d.ts +11 -0
- package/dist/utils/hash.d.ts.map +1 -0
- package/dist/utils/hash.js +14 -0
- package/dist/utils/hash.js.map +1 -0
- package/dist/utils/ignore-annotation.d.ts +28 -0
- package/dist/utils/ignore-annotation.d.ts.map +1 -0
- package/dist/utils/ignore-annotation.js +46 -0
- package/dist/utils/ignore-annotation.js.map +1 -0
- package/dist/utils/llm-json.d.ts +2 -0
- package/dist/utils/llm-json.d.ts.map +1 -0
- package/dist/utils/llm-json.js +53 -0
- package/dist/utils/llm-json.js.map +1 -0
- package/dist/utils/logger.d.ts +3 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +4 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/project-conventions.d.ts +2 -0
- package/dist/utils/project-conventions.d.ts.map +1 -0
- package/dist/utils/project-conventions.js +108 -0
- package/dist/utils/project-conventions.js.map +1 -0
- package/dist/utils/regex.d.ts +9 -0
- package/dist/utils/regex.d.ts.map +1 -0
- package/dist/utils/regex.js +11 -0
- package/dist/utils/regex.js.map +1 -0
- package/dist/utils/snippet.d.ts +20 -0
- package/dist/utils/snippet.d.ts.map +1 -0
- package/dist/utils/snippet.js +28 -0
- package/dist/utils/snippet.js.map +1 -0
- package/dist/utils/source-reader.d.ts +19 -0
- package/dist/utils/source-reader.d.ts.map +1 -0
- package/dist/utils/source-reader.js +32 -0
- package/dist/utils/source-reader.js.map +1 -0
- package/logo.png +0 -0
- package/package.json +92 -0
- package/scripts/start-llm.mjs +161 -0
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import ignore from 'ignore';
|
|
4
|
+
/**
|
|
5
|
+
* Load every `.gitignore` reachable from `workspaceRoot` AND the
|
|
6
|
+
* workspace-root `.rothunterignore` (gitignore-syntax extension, only
|
|
7
|
+
* for rothunter). Returns a unified matcher.
|
|
8
|
+
*
|
|
9
|
+
* The matcher answers "is this workspace-relative path ignored?" — used
|
|
10
|
+
* by the parser + by any detector that walks the filesystem on its own.
|
|
11
|
+
*
|
|
12
|
+
* Rules:
|
|
13
|
+
* - Top-level `.gitignore` always loads (when present).
|
|
14
|
+
* - Nested `.gitignore`s load too, prefixed with their relative dir so
|
|
15
|
+
* a pattern like `dist/` inside `packages/foo/.gitignore` matches
|
|
16
|
+
* `packages/foo/dist/...` and not the root `dist/`.
|
|
17
|
+
* - `.rothunterignore` at the workspace root loads with the same
|
|
18
|
+
* syntax — use this for files that ARE in git but should be hidden
|
|
19
|
+
* from rothunter (fixtures, vendored SDKs, generated test data).
|
|
20
|
+
* - `node_modules/` and `.git/` are always ignored regardless of
|
|
21
|
+
* what the operator's files say — the cost of scanning them is
|
|
22
|
+
* prohibitive on every repo and never produces useful findings.
|
|
23
|
+
*
|
|
24
|
+
* Always returns a non-null matcher; the always-on rules (`node_modules`
|
|
25
|
+
* + `.git`) ensure callers get sensible defaults even when no ignore
|
|
26
|
+
* file exists.
|
|
27
|
+
*/
|
|
28
|
+
export function loadGitignore(workspaceRoot) {
|
|
29
|
+
const ig = ignore();
|
|
30
|
+
// Always-on baseline. node_modules is the single biggest perf win;
|
|
31
|
+
// .git is opaque to detectors anyway. These are baked in so that a
|
|
32
|
+
// workspace without any .gitignore still scans something sensible.
|
|
33
|
+
ig.add(['.git', 'node_modules']);
|
|
34
|
+
const scan = (dir, relPrefix) => {
|
|
35
|
+
const gitignorePath = path.join(dir, '.gitignore');
|
|
36
|
+
if (fs.existsSync(gitignorePath)) {
|
|
37
|
+
try {
|
|
38
|
+
const raw = fs.readFileSync(gitignorePath, 'utf-8');
|
|
39
|
+
const patterns = raw
|
|
40
|
+
.split('\n')
|
|
41
|
+
.map((l) => l.trimEnd())
|
|
42
|
+
.filter((l) => l.length > 0 && !l.startsWith('#'));
|
|
43
|
+
if (relPrefix === '') {
|
|
44
|
+
ig.add(patterns);
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
// Nested .gitignore — scope every pattern to the containing dir
|
|
48
|
+
// unless the pattern is anchored (starts with `/`) or negated
|
|
49
|
+
// (`!`), in which case the `ignore` library's standard semantics
|
|
50
|
+
// around relative roots take over.
|
|
51
|
+
const scoped = patterns.map((p) => {
|
|
52
|
+
const neg = p.startsWith('!');
|
|
53
|
+
const body = neg ? p.slice(1) : p;
|
|
54
|
+
const anchored = body.startsWith('/');
|
|
55
|
+
const cleanBody = anchored ? body.slice(1) : body;
|
|
56
|
+
const scopedBody = path.posix.join(relPrefix, cleanBody);
|
|
57
|
+
return (neg ? '!' : '') + scopedBody;
|
|
58
|
+
});
|
|
59
|
+
ig.add(scoped);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
// Unreadable — silently skip. Better to scan more files than to
|
|
64
|
+
// crash the whole pipeline on a permissions error.
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
// Recurse into immediate sub-directories. Cap depth to avoid
|
|
68
|
+
// pathological monorepos eating O(n) syscalls per scan; the gitignore
|
|
69
|
+
// files we care about live within a handful of levels of the root.
|
|
70
|
+
let entries;
|
|
71
|
+
try {
|
|
72
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
for (const entry of entries) {
|
|
78
|
+
if (!entry.isDirectory())
|
|
79
|
+
continue;
|
|
80
|
+
if (entry.name === '.git' || entry.name === 'node_modules')
|
|
81
|
+
continue;
|
|
82
|
+
if (entry.name.startsWith('.') && entry.name !== '.rothunter')
|
|
83
|
+
continue;
|
|
84
|
+
const childDir = path.join(dir, entry.name);
|
|
85
|
+
const childRel = relPrefix === '' ? entry.name : path.posix.join(relPrefix, entry.name);
|
|
86
|
+
// Already-ignored directories don't need their nested .gitignores
|
|
87
|
+
// — git doesn't recurse past an ignored dir, neither should we.
|
|
88
|
+
if (ig.ignores(childRel + '/'))
|
|
89
|
+
continue;
|
|
90
|
+
scan(childDir, childRel);
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
scan(workspaceRoot, '');
|
|
94
|
+
// Layer `.rothunterignore` on top — rothunter-only path patterns
|
|
95
|
+
// using identical gitignore syntax. Used to hide fixtures, vendored
|
|
96
|
+
// code, and generated artifacts from scans without touching the
|
|
97
|
+
// operator's git rules.
|
|
98
|
+
const rhPath = path.join(workspaceRoot, '.rothunterignore');
|
|
99
|
+
if (fs.existsSync(rhPath)) {
|
|
100
|
+
try {
|
|
101
|
+
const raw = fs.readFileSync(rhPath, 'utf-8');
|
|
102
|
+
const patterns = raw
|
|
103
|
+
.split('\n')
|
|
104
|
+
.map((l) => l.trimEnd())
|
|
105
|
+
.filter((l) => l.length > 0 && !l.startsWith('#'));
|
|
106
|
+
if (patterns.length > 0)
|
|
107
|
+
ig.add(patterns);
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
// Unreadable — silently skip.
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return ig;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Filter a workspace-relative path list through the gitignore matcher,
|
|
117
|
+
* preserving order.
|
|
118
|
+
*/
|
|
119
|
+
export function filterGitignored(matcher, files) {
|
|
120
|
+
return files.filter((f) => !matcher.ignores(f.replace(/\\/g, '/')));
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=gitignore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gitignore.js","sourceRoot":"","sources":["../../src/utils/gitignore.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stable, short SHA-256 prefix used as a fingerprint suffix across
|
|
3
|
+
* every detector. Centralised here so the algorithm + truncation
|
|
4
|
+
* length live in ONE place — bumping from 16 to 24 hex chars only
|
|
5
|
+
* requires this file, not every detector.
|
|
6
|
+
*
|
|
7
|
+
* 16 hex chars = 64 bits of entropy → negligible collision risk for
|
|
8
|
+
* scan-sized populations (thousands of findings per scan).
|
|
9
|
+
*/
|
|
10
|
+
export declare function stableHash(input: string, length?: number): string;
|
|
11
|
+
//# sourceMappingURL=hash.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hash.d.ts","sourceRoot":"","sources":["../../src/utils/hash.ts"],"names":[],"mappings":"AAEA;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,SAAK,GAAG,MAAM,CAE7D"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import * as crypto from 'node:crypto';
|
|
2
|
+
/**
|
|
3
|
+
* Stable, short SHA-256 prefix used as a fingerprint suffix across
|
|
4
|
+
* every detector. Centralised here so the algorithm + truncation
|
|
5
|
+
* length live in ONE place — bumping from 16 to 24 hex chars only
|
|
6
|
+
* requires this file, not every detector.
|
|
7
|
+
*
|
|
8
|
+
* 16 hex chars = 64 bits of entropy → negligible collision risk for
|
|
9
|
+
* scan-sized populations (thousands of findings per scan).
|
|
10
|
+
*/
|
|
11
|
+
export function stableHash(input, length = 16) {
|
|
12
|
+
return crypto.createHash('sha256').update(input).digest('hex').slice(0, length);
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=hash.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hash.js","sourceRoot":"","sources":["../../src/utils/hash.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AAEtC;;;;;;;;GAQG;AACH,MAAM,UAAU,UAAU,CAAC,KAAa,EAAE,MAAM,GAAG,EAAE;IACnD,OAAO,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;AAClF,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-source suppression pragma — operators (and agents under operator
|
|
3
|
+
* supervision) can add a `// rothunter:ignore-<detectorId>` comment
|
|
4
|
+
* above a flagged line to silence the detector for that specific
|
|
5
|
+
* location. `// rothunter:ignore-all` silences every detector.
|
|
6
|
+
*
|
|
7
|
+
* Persistent + co-located with the code being suppressed — survives
|
|
8
|
+
* re-scans automatically. The convention mirrors
|
|
9
|
+
* `// eslint-disable-next-line <rule>`.
|
|
10
|
+
*
|
|
11
|
+
* Required shape (single line above target):
|
|
12
|
+
*
|
|
13
|
+
* // rothunter:ignore-<detectorId>
|
|
14
|
+
* // reason: <one short sentence explaining why this is intentional>
|
|
15
|
+
*
|
|
16
|
+
* Detectors call `hasIgnoreAnnotation(rawSource, line, detectorId)`
|
|
17
|
+
* BEFORE emitting a finding. The matcher walks the previous 5
|
|
18
|
+
* non-blank lines looking for the pragma — captures both styles:
|
|
19
|
+
*
|
|
20
|
+
* // rothunter:ignore-silent-catch ← matches detectorId
|
|
21
|
+
* // rothunter:ignore-all ← global ignore
|
|
22
|
+
*
|
|
23
|
+
* Strict matching: only line-comment `//` syntax (not block comments).
|
|
24
|
+
* Block comments confuse the regex on multi-line files; agents should
|
|
25
|
+
* stick to single-line line comments.
|
|
26
|
+
*/
|
|
27
|
+
export declare function hasIgnoreAnnotation(rawSource: string, targetLine: number, detectorId: string): boolean;
|
|
28
|
+
//# sourceMappingURL=ignore-annotation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ignore-annotation.d.ts","sourceRoot":"","sources":["../../src/utils/ignore-annotation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,GACjB,OAAO,CAeT"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-source suppression pragma — operators (and agents under operator
|
|
3
|
+
* supervision) can add a `// rothunter:ignore-<detectorId>` comment
|
|
4
|
+
* above a flagged line to silence the detector for that specific
|
|
5
|
+
* location. `// rothunter:ignore-all` silences every detector.
|
|
6
|
+
*
|
|
7
|
+
* Persistent + co-located with the code being suppressed — survives
|
|
8
|
+
* re-scans automatically. The convention mirrors
|
|
9
|
+
* `// eslint-disable-next-line <rule>`.
|
|
10
|
+
*
|
|
11
|
+
* Required shape (single line above target):
|
|
12
|
+
*
|
|
13
|
+
* // rothunter:ignore-<detectorId>
|
|
14
|
+
* // reason: <one short sentence explaining why this is intentional>
|
|
15
|
+
*
|
|
16
|
+
* Detectors call `hasIgnoreAnnotation(rawSource, line, detectorId)`
|
|
17
|
+
* BEFORE emitting a finding. The matcher walks the previous 5
|
|
18
|
+
* non-blank lines looking for the pragma — captures both styles:
|
|
19
|
+
*
|
|
20
|
+
* // rothunter:ignore-silent-catch ← matches detectorId
|
|
21
|
+
* // rothunter:ignore-all ← global ignore
|
|
22
|
+
*
|
|
23
|
+
* Strict matching: only line-comment `//` syntax (not block comments).
|
|
24
|
+
* Block comments confuse the regex on multi-line files; agents should
|
|
25
|
+
* stick to single-line line comments.
|
|
26
|
+
*/
|
|
27
|
+
export function hasIgnoreAnnotation(rawSource, targetLine, detectorId) {
|
|
28
|
+
if (!rawSource || targetLine < 1)
|
|
29
|
+
return false;
|
|
30
|
+
const lines = rawSource.split(/\r?\n/);
|
|
31
|
+
const start = Math.max(0, targetLine - 1 - 5);
|
|
32
|
+
const end = Math.min(lines.length, targetLine - 1);
|
|
33
|
+
const allTag = 'rothunter:ignore-all';
|
|
34
|
+
const detTag = `rothunter:ignore-${detectorId}`;
|
|
35
|
+
for (let i = start; i < end; i++) {
|
|
36
|
+
const line = lines[i] ?? '';
|
|
37
|
+
if (!line.includes('rothunter:ignore'))
|
|
38
|
+
continue;
|
|
39
|
+
// Cheap substring matches — avoid false positives from
|
|
40
|
+
// `rothunter:ignore-races-something` matching `ignore-race`.
|
|
41
|
+
if (line.includes(allTag) || line.includes(detTag))
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=ignore-annotation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ignore-annotation.js","sourceRoot":"","sources":["../../src/utils/ignore-annotation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,MAAM,UAAU,mBAAmB,CACjC,SAAiB,EACjB,UAAkB,EAClB,UAAkB;IAElB,IAAI,CAAC,SAAS,IAAI,UAAU,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC;IAC/C,MAAM,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACvC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,UAAU,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;IAC9C,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,EAAE,UAAU,GAAG,CAAC,CAAC,CAAC;IACnD,MAAM,MAAM,GAAG,sBAAsB,CAAC;IACtC,MAAM,MAAM,GAAG,oBAAoB,UAAU,EAAE,CAAC;IAChD,KAAK,IAAI,CAAC,GAAG,KAAK,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;QACjC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAC5B,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,kBAAkB,CAAC;YAAE,SAAS;QACjD,uDAAuD;QACvD,6DAA6D;QAC7D,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;YAAE,OAAO,IAAI,CAAC;IAClE,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"llm-json.d.ts","sourceRoot":"","sources":["../../src/utils/llm-json.ts"],"names":[],"mappings":"AAAA,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAWzD"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export function parseLlmJsonResponse(raw) {
|
|
2
|
+
const cleaned = stripCodeFence(raw).trim();
|
|
3
|
+
if (!cleaned)
|
|
4
|
+
throw new Error('Empty LLM response');
|
|
5
|
+
try {
|
|
6
|
+
return JSON.parse(cleaned);
|
|
7
|
+
}
|
|
8
|
+
catch {
|
|
9
|
+
const extracted = extractFirstJsonObject(cleaned);
|
|
10
|
+
if (extracted)
|
|
11
|
+
return JSON.parse(extracted);
|
|
12
|
+
throw new Error(`LLM response is not valid JSON: ${cleaned.slice(0, 200)}`);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
function stripCodeFence(s) {
|
|
16
|
+
const fence = /^```(?:json)?\s*([\s\S]*?)\s*```$/i;
|
|
17
|
+
const m = s.trim().match(fence);
|
|
18
|
+
return m?.[1] ?? s;
|
|
19
|
+
}
|
|
20
|
+
function extractFirstJsonObject(s) {
|
|
21
|
+
const start = s.indexOf('{');
|
|
22
|
+
if (start < 0)
|
|
23
|
+
return null;
|
|
24
|
+
let depth = 0;
|
|
25
|
+
let inString = false;
|
|
26
|
+
let escape = false;
|
|
27
|
+
for (let i = start; i < s.length; i++) {
|
|
28
|
+
const ch = s[i];
|
|
29
|
+
if (escape) {
|
|
30
|
+
escape = false;
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
if (ch === '\\') {
|
|
34
|
+
escape = true;
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
if (ch === '"') {
|
|
38
|
+
inString = !inString;
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
if (inString)
|
|
42
|
+
continue;
|
|
43
|
+
if (ch === '{')
|
|
44
|
+
depth++;
|
|
45
|
+
else if (ch === '}') {
|
|
46
|
+
depth--;
|
|
47
|
+
if (depth === 0)
|
|
48
|
+
return s.slice(start, i + 1);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=llm-json.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"llm-json.js","sourceRoot":"","sources":["../../src/utils/llm-json.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,oBAAoB,CAAC,GAAW;IAC9C,MAAM,OAAO,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IAC3C,IAAI,CAAC,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,oBAAoB,CAAC,CAAC;IAEpD,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC7B,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,SAAS,GAAG,sBAAsB,CAAC,OAAO,CAAC,CAAC;QAClD,IAAI,SAAS;YAAE,OAAO,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QAC5C,MAAM,IAAI,KAAK,CAAC,mCAAmC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;IAC9E,CAAC;AACH,CAAC;AAED,SAAS,cAAc,CAAC,CAAS;IAC/B,MAAM,KAAK,GAAG,oCAAoC,CAAC;IACnD,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAChC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;AACrB,CAAC;AAED,SAAS,sBAAsB,CAAC,CAAS;IACvC,MAAM,KAAK,GAAG,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC7B,IAAI,KAAK,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAC3B,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,IAAI,MAAM,GAAG,KAAK,CAAC;IACnB,KAAK,IAAI,CAAC,GAAG,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QAChB,IAAI,MAAM,EAAE,CAAC;YACX,MAAM,GAAG,KAAK,CAAC;YACf,SAAS;QACX,CAAC;QACD,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC;YAChB,MAAM,GAAG,IAAI,CAAC;YACd,SAAS;QACX,CAAC;QACD,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;YACf,QAAQ,GAAG,CAAC,QAAQ,CAAC;YACrB,SAAS;QACX,CAAC;QACD,IAAI,QAAQ;YAAE,SAAS;QACvB,IAAI,EAAE,KAAK,GAAG;YAAE,KAAK,EAAE,CAAC;aACnB,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;YACpB,KAAK,EAAE,CAAC;YACR,IAAI,KAAK,KAAK,CAAC;gBAAE,OAAO,CAAC,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;QAChD,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AAIxB,eAAO,MAAM,MAAM,6BAAkB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,mBAAmB,IAAI,OAAO,CAAC,GAAG,CAAC,SAAS,IAAI,MAAM,CAAC;AAEjF,MAAM,CAAC,MAAM,MAAM,GAAG,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-conventions.d.ts","sourceRoot":"","sources":["../../src/utils/project-conventions.ts"],"names":[],"mappings":"AAqDA,wBAAgB,sBAAsB,CACpC,aAAa,EAAE,MAAM,EACrB,YAAY,CAAC,EAAE,MAAM,GACpB,MAAM,GAAG,SAAS,CAyCpB"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
/**
|
|
4
|
+
* Read every project-conventions file in scope and return their
|
|
5
|
+
* concatenated body truncated to a prompt budget. Walks upward from
|
|
6
|
+
* the evidence file's directory so nested package rules layer over
|
|
7
|
+
* the workspace default. Cached per-path so a 100-finding scan reads
|
|
8
|
+
* each file once.
|
|
9
|
+
*
|
|
10
|
+
* Why this exists: detectors flag patterns ("duplicate-function",
|
|
11
|
+
* "long-function", "long-file", …) that are intentional in some
|
|
12
|
+
* codebases (Commander.js idiom, linear request handlers, recognizer
|
|
13
|
+
* tables). Encoding every project rule into the detector is impossible
|
|
14
|
+
* — but the project usually writes those rules in a conventions file
|
|
15
|
+
* (CLAUDE.md, AGENTS.md, .cursorrules, copilot-instructions.md, …).
|
|
16
|
+
* Feeding them into the LLM verdict makes triage project-aware without
|
|
17
|
+
* touching detector logic.
|
|
18
|
+
*
|
|
19
|
+
* Returns `undefined` when no recognised conventions file is found.
|
|
20
|
+
*/
|
|
21
|
+
const cache = new Map();
|
|
22
|
+
const MAX_LEN = 6000;
|
|
23
|
+
/**
|
|
24
|
+
* Filenames (relative to each directory we visit) that count as
|
|
25
|
+
* "project conventions". Ordered roughly by community adoption.
|
|
26
|
+
* Tools-specific files (`.cursorrules`, `.windsurfrules`) are kept in
|
|
27
|
+
* the same list because rules written for one agent usually apply to
|
|
28
|
+
* any agent — they describe project shape, not tool quirks.
|
|
29
|
+
*/
|
|
30
|
+
const CONVENTION_FILENAMES = [
|
|
31
|
+
'CLAUDE.md',
|
|
32
|
+
'AGENTS.md',
|
|
33
|
+
'AGENT.md',
|
|
34
|
+
'GEMINI.md',
|
|
35
|
+
'CODEX.md',
|
|
36
|
+
'.codex.md',
|
|
37
|
+
'COPILOT.md',
|
|
38
|
+
'AI.md',
|
|
39
|
+
'AI_GUIDELINES.md',
|
|
40
|
+
'AI_RULES.md',
|
|
41
|
+
'.cursorrules',
|
|
42
|
+
'.cursor/rules',
|
|
43
|
+
'.windsurfrules',
|
|
44
|
+
'.github/copilot-instructions.md',
|
|
45
|
+
'.continue/rules.md',
|
|
46
|
+
'CONVENTIONS.md',
|
|
47
|
+
'CODESTYLE.md',
|
|
48
|
+
'STYLEGUIDE.md',
|
|
49
|
+
'CONTRIBUTING.md',
|
|
50
|
+
];
|
|
51
|
+
export function readProjectConventions(workspaceRoot, evidenceFile) {
|
|
52
|
+
const dirs = [];
|
|
53
|
+
// Walk upward from the evidence file's directory to the workspace
|
|
54
|
+
// root, deepest first. Within each directory we read every matching
|
|
55
|
+
// conventions file. Nested rules appear before workspace defaults
|
|
56
|
+
// in the joined output so the LLM sees them as the more specific
|
|
57
|
+
// override.
|
|
58
|
+
if (evidenceFile) {
|
|
59
|
+
const wsAbs = path.resolve(workspaceRoot);
|
|
60
|
+
let dir = path.dirname(path.resolve(workspaceRoot, evidenceFile));
|
|
61
|
+
while (true) {
|
|
62
|
+
dirs.push(dir);
|
|
63
|
+
if (path.resolve(dir) === wsAbs)
|
|
64
|
+
break;
|
|
65
|
+
const parent = path.dirname(dir);
|
|
66
|
+
if (parent === dir)
|
|
67
|
+
break;
|
|
68
|
+
dir = parent;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
dirs.push(workspaceRoot);
|
|
73
|
+
}
|
|
74
|
+
const parts = [];
|
|
75
|
+
let budget = MAX_LEN;
|
|
76
|
+
for (const dir of dirs) {
|
|
77
|
+
for (const name of CONVENTION_FILENAMES) {
|
|
78
|
+
if (budget <= 0)
|
|
79
|
+
break;
|
|
80
|
+
const candidate = path.join(dir, name);
|
|
81
|
+
let body = cache.get(candidate);
|
|
82
|
+
if (body === undefined && !cache.has(candidate)) {
|
|
83
|
+
body = readFileSafe(candidate);
|
|
84
|
+
cache.set(candidate, body);
|
|
85
|
+
}
|
|
86
|
+
if (!body)
|
|
87
|
+
continue;
|
|
88
|
+
const wsRel = path.relative(workspaceRoot, candidate) || name;
|
|
89
|
+
const slice = body.length > budget ? body.slice(0, budget) + '\n…(truncated)' : body;
|
|
90
|
+
parts.push(`# ${wsRel}\n${slice}`);
|
|
91
|
+
budget -= slice.length + wsRel.length + 4;
|
|
92
|
+
}
|
|
93
|
+
if (budget <= 0)
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
if (parts.length === 0)
|
|
97
|
+
return undefined;
|
|
98
|
+
return parts.join('\n\n---\n\n');
|
|
99
|
+
}
|
|
100
|
+
function readFileSafe(p) {
|
|
101
|
+
try {
|
|
102
|
+
return fs.readFileSync(p, 'utf-8');
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
return undefined;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=project-conventions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-conventions.js","sourceRoot":"","sources":["../../src/utils/project-conventions.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Escape every regex metacharacter in `s` so it can be embedded as a
|
|
3
|
+
* literal inside a new RegExp. Centralised because four detectors
|
|
4
|
+
* shipped their own copy of the same one-liner — when a future bug
|
|
5
|
+
* (e.g. forgetting to escape `-` inside character classes) needs a
|
|
6
|
+
* fix, doing it here propagates everywhere.
|
|
7
|
+
*/
|
|
8
|
+
export declare function escapeForRegex(s: string): string;
|
|
9
|
+
//# sourceMappingURL=regex.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"regex.d.ts","sourceRoot":"","sources":["../../src/utils/regex.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAEhD"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Escape every regex metacharacter in `s` so it can be embedded as a
|
|
3
|
+
* literal inside a new RegExp. Centralised because four detectors
|
|
4
|
+
* shipped their own copy of the same one-liner — when a future bug
|
|
5
|
+
* (e.g. forgetting to escape `-` inside character classes) needs a
|
|
6
|
+
* fix, doing it here propagates everywhere.
|
|
7
|
+
*/
|
|
8
|
+
export function escapeForRegex(s) {
|
|
9
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=regex.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"regex.js","sourceRoot":"","sources":["../../src/utils/regex.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAAC,CAAS;IACtC,OAAO,CAAC,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;AAClD,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Snippet trimmers shared across the detectors. The cluster confirmers
|
|
3
|
+
* (api-race, shared-db-write, race-condition, …) embed `enclosingSource`
|
|
4
|
+
* blobs in each evidence note — those blobs eat the LLM prompt budget
|
|
5
|
+
* quickly when they're long real-world functions. We collapse + cap them
|
|
6
|
+
* here so every detector applies the same prompt-shape.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Collapse whitespace + truncate a single-line snippet. Used for
|
|
10
|
+
* preview strings in finding titles + evidence rows.
|
|
11
|
+
*/
|
|
12
|
+
export declare function trimSnippet(text: string, maxChars?: number): string;
|
|
13
|
+
/**
|
|
14
|
+
* Preserve the first/last lines of a long function body, ellipsise the
|
|
15
|
+
* middle. Used for the LLM prompt's `enclosingSource` block — gives the
|
|
16
|
+
* model enough context to see the signature + final return without
|
|
17
|
+
* blowing the token budget on a 200-line method.
|
|
18
|
+
*/
|
|
19
|
+
export declare function trimEnclosingSource(full: string, headLines?: number): string;
|
|
20
|
+
//# sourceMappingURL=snippet.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snippet.d.ts","sourceRoot":"","sources":["../../src/utils/snippet.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;GAGG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,SAAM,GAAG,MAAM,CAGhE;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,SAAK,GAAG,MAAM,CAIxE"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Snippet trimmers shared across the detectors. The cluster confirmers
|
|
3
|
+
* (api-race, shared-db-write, race-condition, …) embed `enclosingSource`
|
|
4
|
+
* blobs in each evidence note — those blobs eat the LLM prompt budget
|
|
5
|
+
* quickly when they're long real-world functions. We collapse + cap them
|
|
6
|
+
* here so every detector applies the same prompt-shape.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Collapse whitespace + truncate a single-line snippet. Used for
|
|
10
|
+
* preview strings in finding titles + evidence rows.
|
|
11
|
+
*/
|
|
12
|
+
export function trimSnippet(text, maxChars = 160) {
|
|
13
|
+
const collapsed = text.replace(/\s+/g, ' ').trim();
|
|
14
|
+
return collapsed.length > maxChars ? collapsed.slice(0, maxChars - 3) + '...' : collapsed;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Preserve the first/last lines of a long function body, ellipsise the
|
|
18
|
+
* middle. Used for the LLM prompt's `enclosingSource` block — gives the
|
|
19
|
+
* model enough context to see the signature + final return without
|
|
20
|
+
* blowing the token budget on a 200-line method.
|
|
21
|
+
*/
|
|
22
|
+
export function trimEnclosingSource(full, headLines = 40) {
|
|
23
|
+
const lines = full.split(/\r?\n/);
|
|
24
|
+
if (lines.length <= headLines + 2)
|
|
25
|
+
return full;
|
|
26
|
+
return [...lines.slice(0, headLines), ' // ...', lines[lines.length - 1] ?? ''].join('\n');
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=snippet.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snippet.js","sourceRoot":"","sources":["../../src/utils/snippet.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,IAAY,EAAE,QAAQ,GAAG,GAAG;IACtD,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IACnD,OAAO,SAAS,CAAC,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AAC5F,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAY,EAAE,SAAS,GAAG,EAAE;IAC9D,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAClC,IAAI,KAAK,CAAC,MAAM,IAAI,SAAS,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAC/C,OAAO,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,EAAE,UAAU,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC9F,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Project } from 'ts-morph';
|
|
2
|
+
/**
|
|
3
|
+
* Build a fast workspace-relative source reader. File-walking detectors
|
|
4
|
+
* (magic-numbers, console-log-prod, silent-catch, skip-tests, bad-config,
|
|
5
|
+
* long-file, mutable-globals, …) used to readFileSync every
|
|
6
|
+
* candidate file independently — the orchestrator already parsed those
|
|
7
|
+
* same files into a shared ts-morph Project, so the disk I/O was pure
|
|
8
|
+
* duplication. When a Project is passed, this reader serves text from
|
|
9
|
+
* ts-morph's in-memory SourceFile cache; otherwise it falls back to
|
|
10
|
+
* direct readFileSync so detectors keep working in their own tests
|
|
11
|
+
* (where there is no shared Project to inject).
|
|
12
|
+
*
|
|
13
|
+
* Returns `null` on read failure (missing file, encoding error, etc.) —
|
|
14
|
+
* callers should `continue` past missing files, same as the previous
|
|
15
|
+
* readFileSync/try-catch shape.
|
|
16
|
+
*/
|
|
17
|
+
export type SourceReader = (rel: string) => string | null;
|
|
18
|
+
export declare function makeSourceReader(workspaceRoot: string, project?: Project): SourceReader;
|
|
19
|
+
//# sourceMappingURL=source-reader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source-reader.d.ts","sourceRoot":"","sources":["../../src/utils/source-reader.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAExC;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;AAE1D,wBAAgB,gBAAgB,CAAC,aAAa,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,YAAY,CAWvF"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import * as path from 'node:path';
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
3
|
+
export function makeSourceReader(workspaceRoot, project) {
|
|
4
|
+
if (project) {
|
|
5
|
+
const byRel = new Map();
|
|
6
|
+
for (const sf of project.getSourceFiles()) {
|
|
7
|
+
const rel = path.relative(workspaceRoot, sf.getFilePath());
|
|
8
|
+
// Use POSIX separator so reads work the same across platforms.
|
|
9
|
+
byRel.set(rel.split(path.sep).join('/'), sf.getFullText());
|
|
10
|
+
}
|
|
11
|
+
return (rel) => byRel.get(rel.split(path.sep).join('/')) ?? readFromDisk(workspaceRoot, rel);
|
|
12
|
+
}
|
|
13
|
+
return (rel) => readFromDisk(workspaceRoot, rel);
|
|
14
|
+
}
|
|
15
|
+
function readFromDisk(workspaceRoot, rel) {
|
|
16
|
+
try {
|
|
17
|
+
const root = path.resolve(workspaceRoot);
|
|
18
|
+
const resolved = path.resolve(root, rel);
|
|
19
|
+
// Refuse anything that escapes the workspace root via `..` /
|
|
20
|
+
// absolute / symlink before it reaches `readFileSync`. Callers
|
|
21
|
+
// are internal today, but a defensive guard here keeps any
|
|
22
|
+
// future detector or test fixture from punching through.
|
|
23
|
+
if (resolved !== root && !resolved.startsWith(root + path.sep)) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
return readFileSync(resolved, 'utf-8');
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=source-reader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source-reader.js","sourceRoot":"","sources":["../../src/utils/source-reader.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAoBvC,MAAM,UAAU,gBAAgB,CAAC,aAAqB,EAAE,OAAiB;IACvE,IAAI,OAAO,EAAE,CAAC;QACZ,MAAM,KAAK,GAAG,IAAI,GAAG,EAAkB,CAAC;QACxC,KAAK,MAAM,EAAE,IAAI,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC;YAC1C,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,aAAa,EAAE,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC;YAC3D,+DAA+D;YAC/D,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC;QAC7D,CAAC;QACD,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,YAAY,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC;IAC/F,CAAC;IACD,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC,YAAY,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC;AACnD,CAAC;AAED,SAAS,YAAY,CAAC,aAAqB,EAAE,GAAW;IACtD,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;QACzC,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QACzC,6DAA6D;QAC7D,+DAA+D;QAC/D,2DAA2D;QAC3D,yDAAyD;QACzD,IAAI,QAAQ,KAAK,IAAI,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;YAC/D,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IACzC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
|
package/logo.png
ADDED
|
Binary file
|