@kogu/context-check 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +213 -0
- package/dist/analyzer/engine.d.ts +27 -0
- package/dist/analyzer/engine.js +64 -0
- package/dist/analyzer/engine.js.map +1 -0
- package/dist/analyzer/rules/context-overhead.d.ts +21 -0
- package/dist/analyzer/rules/context-overhead.js +53 -0
- package/dist/analyzer/rules/context-overhead.js.map +1 -0
- package/dist/analyzer/rules/duplicate.d.ts +15 -0
- package/dist/analyzer/rules/duplicate.js +53 -0
- package/dist/analyzer/rules/duplicate.js.map +1 -0
- package/dist/analyzer/rules/high-stakes.d.ts +36 -0
- package/dist/analyzer/rules/high-stakes.js +96 -0
- package/dist/analyzer/rules/high-stakes.js.map +1 -0
- package/dist/analyzer/rules/large-file.d.ts +15 -0
- package/dist/analyzer/rules/large-file.js +41 -0
- package/dist/analyzer/rules/large-file.js.map +1 -0
- package/dist/analyzer/rules/mapping.d.ts +29 -0
- package/dist/analyzer/rules/mapping.js +51 -0
- package/dist/analyzer/rules/mapping.js.map +1 -0
- package/dist/analyzer/rules/repetition.d.ts +20 -0
- package/dist/analyzer/rules/repetition.js +80 -0
- package/dist/analyzer/rules/repetition.js.map +1 -0
- package/dist/analyzer/rules/scoped-no-match.d.ts +15 -0
- package/dist/analyzer/rules/scoped-no-match.js +46 -0
- package/dist/analyzer/rules/scoped-no-match.js.map +1 -0
- package/dist/analyzer/run.d.ts +25 -0
- package/dist/analyzer/run.js +59 -0
- package/dist/analyzer/run.js.map +1 -0
- package/dist/analyzer/severity.d.ts +21 -0
- package/dist/analyzer/severity.js +39 -0
- package/dist/analyzer/severity.js.map +1 -0
- package/dist/analyzer/types.d.ts +35 -0
- package/dist/analyzer/types.js +17 -0
- package/dist/analyzer/types.js.map +1 -0
- package/dist/cli/index.d.ts +23 -0
- package/dist/cli/index.js +159 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/config/load.d.ts +11 -0
- package/dist/config/load.js +38 -0
- package/dist/config/load.js.map +1 -0
- package/dist/discovery/adapters/agents.d.ts +8 -0
- package/dist/discovery/adapters/agents.js +25 -0
- package/dist/discovery/adapters/agents.js.map +1 -0
- package/dist/discovery/adapters/artifact.d.ts +21 -0
- package/dist/discovery/adapters/artifact.js +40 -0
- package/dist/discovery/adapters/artifact.js.map +1 -0
- package/dist/discovery/adapters/claude.d.ts +8 -0
- package/dist/discovery/adapters/claude.js +25 -0
- package/dist/discovery/adapters/claude.js.map +1 -0
- package/dist/discovery/adapters/cursor.d.ts +11 -0
- package/dist/discovery/adapters/cursor.js +32 -0
- package/dist/discovery/adapters/cursor.js.map +1 -0
- package/dist/discovery/adapters/index.d.ts +10 -0
- package/dist/discovery/adapters/index.js +18 -0
- package/dist/discovery/adapters/index.js.map +1 -0
- package/dist/discovery/adapters/skill.d.ts +9 -0
- package/dist/discovery/adapters/skill.js +26 -0
- package/dist/discovery/adapters/skill.js.map +1 -0
- package/dist/discovery/adapters/types.d.ts +22 -0
- package/dist/discovery/adapters/types.js +9 -0
- package/dist/discovery/adapters/types.js.map +1 -0
- package/dist/discovery/discover.d.ts +32 -0
- package/dist/discovery/discover.js +51 -0
- package/dist/discovery/discover.js.map +1 -0
- package/dist/discovery/exclusions.d.ts +19 -0
- package/dist/discovery/exclusions.js +40 -0
- package/dist/discovery/exclusions.js.map +1 -0
- package/dist/discovery/scanner.d.ts +31 -0
- package/dist/discovery/scanner.js +75 -0
- package/dist/discovery/scanner.js.map +1 -0
- package/dist/git/history.d.ts +25 -0
- package/dist/git/history.js +71 -0
- package/dist/git/history.js.map +1 -0
- package/dist/git/sufficiency.d.ts +38 -0
- package/dist/git/sufficiency.js +98 -0
- package/dist/git/sufficiency.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/output/json.d.ts +58 -0
- package/dist/output/json.js +52 -0
- package/dist/output/json.js.map +1 -0
- package/dist/output/terminal.d.ts +21 -0
- package/dist/output/terminal.js +163 -0
- package/dist/output/terminal.js.map +1 -0
- package/dist/parser/frontmatter.d.ts +26 -0
- package/dist/parser/frontmatter.js +63 -0
- package/dist/parser/frontmatter.js.map +1 -0
- package/dist/parser/markdown.d.ts +23 -0
- package/dist/parser/markdown.js +63 -0
- package/dist/parser/markdown.js.map +1 -0
- package/dist/parser/normalize.d.ts +20 -0
- package/dist/parser/normalize.js +35 -0
- package/dist/parser/normalize.js.map +1 -0
- package/dist/parser/parse.d.ts +34 -0
- package/dist/parser/parse.js +35 -0
- package/dist/parser/parse.js.map +1 -0
- package/dist/parser/plain-text.d.ts +30 -0
- package/dist/parser/plain-text.js +52 -0
- package/dist/parser/plain-text.js.map +1 -0
- package/dist/parser/scope.d.ts +20 -0
- package/dist/parser/scope.js +44 -0
- package/dist/parser/scope.js.map +1 -0
- package/dist/snapshot/diff.d.ts +62 -0
- package/dist/snapshot/diff.js +146 -0
- package/dist/snapshot/diff.js.map +1 -0
- package/dist/snapshot/manager.d.ts +54 -0
- package/dist/snapshot/manager.js +131 -0
- package/dist/snapshot/manager.js.map +1 -0
- package/dist/tokens/estimator.d.ts +12 -0
- package/dist/tokens/estimator.js +16 -0
- package/dist/tokens/estimator.js.map +1 -0
- package/dist/types/configuration.d.ts +47 -0
- package/dist/types/configuration.js +9 -0
- package/dist/types/configuration.js.map +1 -0
- package/dist/types/finding.d.ts +32 -0
- package/dist/types/finding.js +14 -0
- package/dist/types/finding.js.map +1 -0
- package/dist/types/report.d.ts +20 -0
- package/dist/types/report.js +9 -0
- package/dist/types/report.js.map +1 -0
- package/package.json +70 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 contextcheck
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
# contextcheck
|
|
2
|
+
|
|
3
|
+
> **See what your AI coding context contains, what changed, and what deserves review.**
|
|
4
|
+
|
|
5
|
+
> **Beta:** `contextcheck` şu anda beta kanalında yayınlanıyor. Kullanım:
|
|
6
|
+
> `npm install -g context-check@beta` veya `npx context-check@beta analyze`.
|
|
7
|
+
|
|
8
|
+
contextcheck, AI coding agent'ların kullandığı instructions, rules ve skills
|
|
9
|
+
dosyalarını (CLAUDE.md, AGENTS.md, .cursor/rules, SKILL.md) keşfeden,
|
|
10
|
+
deterministic olarak analiz eden ve context layer'ın zaman içindeki değişimini
|
|
11
|
+
takip etmeni sağlayan **local-first** bir developer tooludur.
|
|
12
|
+
|
|
13
|
+
- Local-first: içerik makineden çıkmaz
|
|
14
|
+
- Deterministic: LLM/embedding/API/DB yok
|
|
15
|
+
- Açıklanabilir: her finding ne/neden/hangi dosya açıklamalı
|
|
16
|
+
- Review, never remove: otomatik silme/düzeltme yok
|
|
17
|
+
|
|
18
|
+
## Hızlı Başlangıç (ilk 30 saniye)
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
# 1) Mevcut AI context'ini gör
|
|
22
|
+
npx context-check@beta analyze
|
|
23
|
+
|
|
24
|
+
# 2) Bir anlık görüntü al
|
|
25
|
+
npx context-check@beta snapshot
|
|
26
|
+
|
|
27
|
+
# 3) Kurallarında değişiklik yap, ne değiştiğini gör
|
|
28
|
+
npx context-check@beta diff
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Bunların hepsi hesap gerektirmez, hiçbir içerik cihazından çıkmaz.
|
|
32
|
+
|
|
33
|
+
Global kurulum tercih edersen:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npm install -g context-check@beta
|
|
37
|
+
context-check analyze
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Kullanım
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
# Tüm AI configuration dosyalarını keşfet + analiz et
|
|
44
|
+
context-check analyze
|
|
45
|
+
|
|
46
|
+
# Detaylı bulgular
|
|
47
|
+
context-check analyze --verbose
|
|
48
|
+
|
|
49
|
+
# Hızlı insan çıktısı (summary + findings)
|
|
50
|
+
context-check analyze --compact
|
|
51
|
+
|
|
52
|
+
# Makine tarafından okunabilir, privacy-safe JSON çıktısı
|
|
53
|
+
context-check analyze --json
|
|
54
|
+
|
|
55
|
+
# CI'da findings eşik üstüyse başarısız ol
|
|
56
|
+
context-check analyze --json --fail-on notice
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Üç çıktı kontratı:
|
|
60
|
+
|
|
61
|
+
```text
|
|
62
|
+
context-check analyze -> detaylı, varsayılan (spec §9)
|
|
63
|
+
context-check analyze --compact -> hızlı insan çıktısı
|
|
64
|
+
context-check analyze --json -> CI / automation (privacy-safe)
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Komutlar
|
|
68
|
+
|
|
69
|
+
- `context-check analyze` — keşfet ve analiz et
|
|
70
|
+
- `context-check analyze --verbose` — detaylı bulgular
|
|
71
|
+
- `context-check analyze --json` — privacy-safe JSON (raw content yok)
|
|
72
|
+
- `context-check analyze --compact` — hızlı/kompakt insan çıktısı
|
|
73
|
+
- `context-check analyze --fail-on <info|notice|warning>` — eşik üstü finding'de exit 1
|
|
74
|
+
- `context-check snapshot` — AI configuration snapshot oluştur
|
|
75
|
+
- `context-check snapshot list` — kayıtlı snapshot'ları listele
|
|
76
|
+
- `context-check diff` — en son snapshot ile mevcut durumu karşılaştır (artifact + token + finding değişimleri)
|
|
77
|
+
- `context-check diff <snapshot-id>` — belirtilen snapshot ile mevcut durumu karşılaştır
|
|
78
|
+
- `context-check diff --json` — privacy-safe JSON diff
|
|
79
|
+
- `context-check config` — yapılandırma (`.contextcheck.json`)
|
|
80
|
+
|
|
81
|
+
## Exit code politkası
|
|
82
|
+
|
|
83
|
+
Varsayılan (informational): findings CI'ı kırmaz — `no findings`, `info` ve
|
|
84
|
+
`notice` hepsi `exit 0`. `--fail-on` belirtilirse eşik (veya üstü) seviyesinde
|
|
85
|
+
finding varsa `exit 1`:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
# notice (REVIEW/CAUTION) findings → exit 1; sadece info → exit 0
|
|
89
|
+
context-check analyze --fail-on notice
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
`--fail-on` değeri finding **severity** kullanır (info < notice < warning).
|
|
93
|
+
Label→severity: INFO/CONTEXT → info, REVIEW/CAUTION → notice.
|
|
94
|
+
|
|
95
|
+
## Diff (snapshot → current)
|
|
96
|
+
|
|
97
|
+
`context-check diff`, context layer'ın zaman içindeki değişimini gösterir:
|
|
98
|
+
artifact (eklenen/silinmiş/değişen dosyalar), toplam context token değişimi ve
|
|
99
|
+
**analiz bulgularının değişimi** (yeni/çözülen/değişen finding):
|
|
100
|
+
|
|
101
|
+
```text
|
|
102
|
+
ContextCheck Diff
|
|
103
|
+
|
|
104
|
+
Snapshot: previous → current
|
|
105
|
+
|
|
106
|
+
Artifacts
|
|
107
|
+
~ CLAUDE.md
|
|
108
|
+
59 → 70 tokens
|
|
109
|
+
|
|
110
|
+
Context
|
|
111
|
+
Previous ~235 tokens
|
|
112
|
+
Current ~246 tokens
|
|
113
|
+
Change +11 tokens
|
|
114
|
+
|
|
115
|
+
Findings
|
|
116
|
+
+ 1 new
|
|
117
|
+
= 1 unchanged
|
|
118
|
+
|
|
119
|
+
Summary
|
|
120
|
+
0 added · 1 modified · 0 removed
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Bu, "context layer değişti ve bunun sonucunda hangi bulgular ortaya
|
|
124
|
+
çıktı/kayboldu?" sorusunu cevaplar — model performansıyla ilgili kausal iddia
|
|
125
|
+
**değildir** (Spec §28).
|
|
126
|
+
|
|
127
|
+
## Snapshot list ve seçimi (v2)
|
|
128
|
+
|
|
129
|
+
`context-check snapshot list` kayıtlı snapshot'ları gösterir:
|
|
130
|
+
|
|
131
|
+
```text
|
|
132
|
+
ContextCheck Snapshots
|
|
133
|
+
|
|
134
|
+
ID Created Files Tokens Findings
|
|
135
|
+
7ebv3k 2026-09-09 00:14:22 12 4,820 3
|
|
136
|
+
43d791 2026-09-08 12:03:44 10 3,980 1
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Belirli bir snapshot'a karşı diff:
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
context-check diff 7ebv3k
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
`diff --json` CI/automation için privacy-safe JSON üretir (artifact token
|
|
146
|
+
delta'ları, finding değişimleri, snapshot metadata — **asla raw content yok**).
|
|
147
|
+
|
|
148
|
+
## GitHub Actions (CI)
|
|
149
|
+
|
|
150
|
+
```yaml
|
|
151
|
+
name: ContextCheck
|
|
152
|
+
|
|
153
|
+
on:
|
|
154
|
+
pull_request:
|
|
155
|
+
push:
|
|
156
|
+
|
|
157
|
+
jobs:
|
|
158
|
+
contextcheck:
|
|
159
|
+
runs-on: ubuntu-latest
|
|
160
|
+
steps:
|
|
161
|
+
- uses: actions/checkout@v4
|
|
162
|
+
with:
|
|
163
|
+
fetch-depth: 0 # scoped-no-match Git history için tam geçmiş
|
|
164
|
+
- run: npx context-check analyze --fail-on notice # CI gate
|
|
165
|
+
- run: npx context-check analyze --compact # PR log
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
`fetch-depth: 0` önemli: shallow checkout'u ContextCheck tespit eder ve
|
|
169
|
+
`scoped-no-match`'i güvenli şekilde atlar (Rule 26).
|
|
170
|
+
|
|
171
|
+
İki kullanım modu:
|
|
172
|
+
|
|
173
|
+
```text
|
|
174
|
+
Developer workflow analyze → snapshot → diff
|
|
175
|
+
CI workflow analyze --fail-on notice
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
`--fail-on notice` REVIEW/CAUTION findings'de CI'ı kırar; yalnızca INFO
|
|
179
|
+
(duplicate/large-file) findings build'i kırmaz. Bu repo'da workflow
|
|
180
|
+
`.github/workflows/contextcheck.yml` mevcut ve paket yayınlanana kadar repo
|
|
181
|
+
içinden dogfooding modunda çalışır (yayın sonrası `npx`'e döner).
|
|
182
|
+
|
|
183
|
+
## Geliştirme
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
npm install
|
|
187
|
+
npm run build # tsc
|
|
188
|
+
npm test # vitest
|
|
189
|
+
npm run lint # eslint
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## Lisans
|
|
193
|
+
|
|
194
|
+
MIT — bkz. [LICENSE](LICENSE).
|
|
195
|
+
|
|
196
|
+
## Yayınlama (maintainer)
|
|
197
|
+
|
|
198
|
+
Beta kanalına yayın (kontrollü, mevcut `latest`'i korur):
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
npm login
|
|
202
|
+
npm publish --tag beta
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
Beta'yı test et:
|
|
206
|
+
|
|
207
|
+
```bash
|
|
208
|
+
npm install -g context-check@beta
|
|
209
|
+
npx context-check@beta analyze
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Stabil sürüm hazır olduğunda `--tag latest` ile (veya `npm dist-tag add context-check@<version> latest`)
|
|
213
|
+
mevcut sürümü `latest` kanalına taşı.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Analyzer engine (Spec §51).
|
|
3
|
+
*
|
|
4
|
+
* Runs each registered rule independently over the discovered artifacts and
|
|
5
|
+
* aggregates their findings into an `AnalysisReport`. Rules are isolation
|
|
6
|
+
* boundaries: a failure in one must not prevent the others from running
|
|
7
|
+
* (Spec §34), and presentation never affects analysis (Rule 15).
|
|
8
|
+
*/
|
|
9
|
+
import type { ConfigurationArtifact } from "../types/configuration.js";
|
|
10
|
+
import type { Finding } from "../types/finding.js";
|
|
11
|
+
import type { AnalysisReport } from "../types/report.js";
|
|
12
|
+
import type { AnalysisContext, AnalyzerRule } from "./types.js";
|
|
13
|
+
export interface AnalyzeInput {
|
|
14
|
+
artifacts: ConfigurationArtifact[];
|
|
15
|
+
context: AnalysisContext;
|
|
16
|
+
/** Rules to run; defaults to the full registered set when omitted. */
|
|
17
|
+
rules?: AnalyzerRule[];
|
|
18
|
+
}
|
|
19
|
+
/** Rules enabled for the current MVP build (grown across sprints). */
|
|
20
|
+
export declare const DEFAULT_RULES: AnalyzerRule[];
|
|
21
|
+
/**
|
|
22
|
+
* Runs all rules and returns an aggregated report. Rule failures are caught
|
|
23
|
+
* and logged instead of failing the whole analysis.
|
|
24
|
+
*/
|
|
25
|
+
export declare function analyze(input: AnalyzeInput): Promise<AnalysisReport>;
|
|
26
|
+
/** Builds the summary + report from artifacts and findings. */
|
|
27
|
+
export declare function aggregate(artifacts: ConfigurationArtifact[], findings: Finding[]): AnalysisReport;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Analyzer engine (Spec §51).
|
|
3
|
+
*
|
|
4
|
+
* Runs each registered rule independently over the discovered artifacts and
|
|
5
|
+
* aggregates their findings into an `AnalysisReport`. Rules are isolation
|
|
6
|
+
* boundaries: a failure in one must not prevent the others from running
|
|
7
|
+
* (Spec §34), and presentation never affects analysis (Rule 15).
|
|
8
|
+
*/
|
|
9
|
+
import { buildContextOverheadFinding, estimateOverhead, } from "./rules/context-overhead.js";
|
|
10
|
+
import { duplicateRule } from "./rules/duplicate.js";
|
|
11
|
+
import { highStakesRule } from "./rules/high-stakes.js";
|
|
12
|
+
import { largeFileRule } from "./rules/large-file.js";
|
|
13
|
+
import { repetitionRule } from "./rules/repetition.js";
|
|
14
|
+
import { scopedNoMatchRule } from "./rules/scoped-no-match.js";
|
|
15
|
+
/** Rules enabled for the current MVP build (grown across sprints). */
|
|
16
|
+
export const DEFAULT_RULES = [
|
|
17
|
+
duplicateRule,
|
|
18
|
+
highStakesRule,
|
|
19
|
+
largeFileRule,
|
|
20
|
+
repetitionRule,
|
|
21
|
+
scopedNoMatchRule,
|
|
22
|
+
];
|
|
23
|
+
/**
|
|
24
|
+
* Runs all rules and returns an aggregated report. Rule failures are caught
|
|
25
|
+
* and logged instead of failing the whole analysis.
|
|
26
|
+
*/
|
|
27
|
+
export async function analyze(input) {
|
|
28
|
+
const { artifacts, context, rules } = input;
|
|
29
|
+
const ruleSet = rules ?? DEFAULT_RULES;
|
|
30
|
+
const findings = [];
|
|
31
|
+
for (const rule of ruleSet) {
|
|
32
|
+
try {
|
|
33
|
+
const result = await rule.run(artifacts, context);
|
|
34
|
+
findings.push(...(Array.isArray(result) ? result : []));
|
|
35
|
+
}
|
|
36
|
+
catch (err) {
|
|
37
|
+
// eslint-disable-next-line no-console
|
|
38
|
+
console.error(`[contextcheck] Analyzer rule '${rule.name}' failed:`, err instanceof Error ? err.message : String(err));
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return aggregate(artifacts, findings);
|
|
42
|
+
}
|
|
43
|
+
/** Builds the summary + report from artifacts and findings. */
|
|
44
|
+
export function aggregate(artifacts, findings) {
|
|
45
|
+
const totalTokens = artifacts.reduce((sum, a) => sum + a.metadata.estimatedTokens, 0);
|
|
46
|
+
const skills = artifacts.filter((a) => a.type === "skill").length;
|
|
47
|
+
// Potential Context Overhead is derived from the other findings (Spec §22).
|
|
48
|
+
const contextOverhead = estimateOverhead(artifacts, findings);
|
|
49
|
+
const overheadFinding = buildContextOverheadFinding(contextOverhead, findings.flatMap((f) => f.filePaths));
|
|
50
|
+
const allFindings = overheadFinding
|
|
51
|
+
? [...findings, overheadFinding]
|
|
52
|
+
: findings;
|
|
53
|
+
return {
|
|
54
|
+
summary: {
|
|
55
|
+
configurationFiles: artifacts.length,
|
|
56
|
+
skills,
|
|
57
|
+
estimatedTokens: totalTokens,
|
|
58
|
+
potentialContextOverhead: contextOverhead,
|
|
59
|
+
},
|
|
60
|
+
artifacts,
|
|
61
|
+
findings: allFindings,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.js","sourceRoot":"","sources":["../../src/analyzer/engine.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,OAAO,EACL,2BAA2B,EAC3B,gBAAgB,GACjB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAS/D,sEAAsE;AACtE,MAAM,CAAC,MAAM,aAAa,GAAmB;IAC3C,aAAa;IACb,cAAc;IACd,aAAa;IACb,cAAc;IACd,iBAAiB;CAClB,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,KAAmB;IAC/C,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,KAAK,CAAC;IAC5C,MAAM,OAAO,GAAG,KAAK,IAAI,aAAa,CAAC;IAEvC,MAAM,QAAQ,GAAc,EAAE,CAAC;IAE/B,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;QAC3B,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;YAClD,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAC1D,CAAC;QAAC,OAAO,GAAY,EAAE,CAAC;YACtB,sCAAsC;YACtC,OAAO,CAAC,KAAK,CACX,iCAAiC,IAAI,CAAC,IAAI,WAAW,EACrD,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CACjD,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO,SAAS,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;AACxC,CAAC;AAED,+DAA+D;AAC/D,MAAM,UAAU,SAAS,CACvB,SAAkC,EAClC,QAAmB;IAEnB,MAAM,WAAW,GAAG,SAAS,CAAC,MAAM,CAClC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,QAAQ,CAAC,eAAe,EAC5C,CAAC,CACF,CAAC;IACF,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,MAAM,CAAC;IAElE,4EAA4E;IAC5E,MAAM,eAAe,GAAG,gBAAgB,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IAC9D,MAAM,eAAe,GAAG,2BAA2B,CACjD,eAAe,EACf,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CACrC,CAAC;IACF,MAAM,WAAW,GAAG,eAAe;QACjC,CAAC,CAAC,CAAC,GAAG,QAAQ,EAAE,eAAe,CAAC;QAChC,CAAC,CAAC,QAAQ,CAAC;IAEb,OAAO;QACL,OAAO,EAAE;YACP,kBAAkB,EAAE,SAAS,CAAC,MAAM;YACpC,MAAM;YACN,eAAe,EAAE,WAAW;YAC5B,wBAAwB,EAAE,eAAe;SAC1C;QACD,SAAS;QACT,QAAQ,EAAE,WAAW;KACtB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context overhead aggregation (Spec §22).
|
|
3
|
+
*
|
|
4
|
+
* Potential Context Overhead is a deterministic estimate of tokens that may
|
|
5
|
+
* be redundant, computed from signals already detected by other rules
|
|
6
|
+
* (duplicate content, repeated sections/blocks). The MVP does NOT detect
|
|
7
|
+
* semantic redundancy, and we never use "wasted tokens" — only "Potential
|
|
8
|
+
* Context Overhead" (Spec §22).
|
|
9
|
+
*/
|
|
10
|
+
import type { ConfigurationArtifact } from "../../types/configuration.js";
|
|
11
|
+
import type { Finding } from "../../types/finding.js";
|
|
12
|
+
/**
|
|
13
|
+
* Estimates redundant tokens from the duplicate/repetition findings. Uses
|
|
14
|
+
* only deterministic signals; bounds each category to avoid double counting.
|
|
15
|
+
*/
|
|
16
|
+
export declare function estimateOverhead(artifacts: ConfigurationArtifact[], findings: Finding[]): number;
|
|
17
|
+
/**
|
|
18
|
+
* Builds a `context-overhead` finding when overhead is meaningful.
|
|
19
|
+
* Returns `null` when overhead is negligible (avoids noisy output).
|
|
20
|
+
*/
|
|
21
|
+
export declare function buildContextOverheadFinding(overhead: number, sourcePaths: string[]): Finding | null;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context overhead aggregation (Spec §22).
|
|
3
|
+
*
|
|
4
|
+
* Potential Context Overhead is a deterministic estimate of tokens that may
|
|
5
|
+
* be redundant, computed from signals already detected by other rules
|
|
6
|
+
* (duplicate content, repeated sections/blocks). The MVP does NOT detect
|
|
7
|
+
* semantic redundancy, and we never use "wasted tokens" — only "Potential
|
|
8
|
+
* Context Overhead" (Spec §22).
|
|
9
|
+
*/
|
|
10
|
+
import { makeFinding } from "./mapping.js";
|
|
11
|
+
/**
|
|
12
|
+
* Estimates redundant tokens from the duplicate/repetition findings. Uses
|
|
13
|
+
* only deterministic signals; bounds each category to avoid double counting.
|
|
14
|
+
*/
|
|
15
|
+
export function estimateOverhead(artifacts, findings) {
|
|
16
|
+
let overhead = 0;
|
|
17
|
+
// Duplicate groups: each extra copy beyond the first is redundant.
|
|
18
|
+
for (const f of findings) {
|
|
19
|
+
if (f.type !== "duplicate" || f.filePaths.length <= 1)
|
|
20
|
+
continue;
|
|
21
|
+
const group = artifacts.filter((a) => f.filePaths.includes(a.path));
|
|
22
|
+
const avg = group.length
|
|
23
|
+
? Math.round(group.reduce((sum, a) => sum + a.metadata.estimatedTokens, 0) /
|
|
24
|
+
group.length)
|
|
25
|
+
: 0;
|
|
26
|
+
overhead += avg * (f.filePaths.length - 1);
|
|
27
|
+
}
|
|
28
|
+
// Repetitions: a bounded per-extra-occurrence contribution.
|
|
29
|
+
for (const f of findings) {
|
|
30
|
+
if (f.type !== "repetition")
|
|
31
|
+
continue;
|
|
32
|
+
overhead += Math.min(800, 200 * Math.max(0, f.filePaths.length));
|
|
33
|
+
}
|
|
34
|
+
return Math.round(overhead);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Builds a `context-overhead` finding when overhead is meaningful.
|
|
38
|
+
* Returns `null` when overhead is negligible (avoids noisy output).
|
|
39
|
+
*/
|
|
40
|
+
export function buildContextOverheadFinding(overhead, sourcePaths) {
|
|
41
|
+
if (overhead <= 0)
|
|
42
|
+
return null;
|
|
43
|
+
return makeFinding({
|
|
44
|
+
type: "context-overhead",
|
|
45
|
+
filePaths: sourcePaths.length > 0 ? sourcePaths : ["."],
|
|
46
|
+
title: "Potential context overhead",
|
|
47
|
+
description: `Potential Context Overhead ~${overhead.toLocaleString("en-US")} tokens ` +
|
|
48
|
+
`based on duplicate content and repeated sections.`,
|
|
49
|
+
recommendation: `Review the flagged duplicates and repetitions; consolidating them may ` +
|
|
50
|
+
`reduce how much of the model context your configuration uses.`,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=context-overhead.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context-overhead.js","sourceRoot":"","sources":["../../../src/analyzer/rules/context-overhead.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAE3C;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAC9B,SAAkC,EAClC,QAAmB;IAEnB,IAAI,QAAQ,GAAG,CAAC,CAAC;IAEjB,mEAAmE;IACnE,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,IAAI,CAAC,CAAC,IAAI,KAAK,WAAW,IAAI,CAAC,CAAC,SAAS,CAAC,MAAM,IAAI,CAAC;YAAE,SAAS;QAChE,MAAM,KAAK,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QACpE,MAAM,GAAG,GAAG,KAAK,CAAC,MAAM;YACtB,CAAC,CAAC,IAAI,CAAC,KAAK,CACR,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,QAAQ,CAAC,eAAe,EAAE,CAAC,CAAC;gBAC3D,KAAK,CAAC,MAAM,CACf;YACH,CAAC,CAAC,CAAC,CAAC;QACN,QAAQ,IAAI,GAAG,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC7C,CAAC;IAED,4DAA4D;IAC5D,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,IAAI,CAAC,CAAC,IAAI,KAAK,YAAY;YAAE,SAAS;QACtC,QAAQ,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;IACnE,CAAC;IAED,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;AAC9B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,2BAA2B,CACzC,QAAgB,EAChB,WAAqB;IAErB,IAAI,QAAQ,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IAE/B,OAAO,WAAW,CAAC;QACjB,IAAI,EAAE,kBAAkB;QACxB,SAAS,EAAE,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;QACvD,KAAK,EAAE,4BAA4B;QACnC,WAAW,EACT,+BAA+B,QAAQ,CAAC,cAAc,CAAC,OAAO,CAAC,UAAU;YACzE,mDAAmD;QACrD,cAAc,EACZ,wEAAwE;YACxE,+DAA+D;KAClE,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Exact duplicate detection (Spec §19, Rule 22).
|
|
3
|
+
*
|
|
4
|
+
* Compares document BODIES only, after stripping YAML frontmatter and
|
|
5
|
+
* normalizing. Two files with identical bodies but different frontmatter
|
|
6
|
+
* (description/globs/alwaysApply/metadata) are still duplicate candidates.
|
|
7
|
+
*
|
|
8
|
+
* Cross-file duplicates produce ONE finding listing every file path (Rule 16,
|
|
9
|
+
* Spec §12). This is a high-confidence, deterministic detection.
|
|
10
|
+
*/
|
|
11
|
+
import type { ConfigurationArtifact } from "../../types/configuration.js";
|
|
12
|
+
import type { AnalyzerRule } from "../types.js";
|
|
13
|
+
/** Normalized-body fingerprint (hash) for a single artifact. */
|
|
14
|
+
export declare function bodyFingerprint(artifact: ConfigurationArtifact): string;
|
|
15
|
+
export declare const duplicateRule: AnalyzerRule;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Exact duplicate detection (Spec §19, Rule 22).
|
|
3
|
+
*
|
|
4
|
+
* Compares document BODIES only, after stripping YAML frontmatter and
|
|
5
|
+
* normalizing. Two files with identical bodies but different frontmatter
|
|
6
|
+
* (description/globs/alwaysApply/metadata) are still duplicate candidates.
|
|
7
|
+
*
|
|
8
|
+
* Cross-file duplicates produce ONE finding listing every file path (Rule 16,
|
|
9
|
+
* Spec §12). This is a high-confidence, deterministic detection.
|
|
10
|
+
*/
|
|
11
|
+
import { createHash } from "node:crypto";
|
|
12
|
+
import { normalizeBody } from "../../parser/normalize.js";
|
|
13
|
+
import { makeFinding } from "./mapping.js";
|
|
14
|
+
/** Normalized-body fingerprint (hash) for a single artifact. */
|
|
15
|
+
export function bodyFingerprint(artifact) {
|
|
16
|
+
const normalized = normalizeBody(artifact.content);
|
|
17
|
+
return createHash("sha256").update(normalized).digest("hex");
|
|
18
|
+
}
|
|
19
|
+
export const duplicateRule = {
|
|
20
|
+
name: "duplicate",
|
|
21
|
+
run(artifacts) {
|
|
22
|
+
// Group artifacts by normalized-body fingerprint (bodies only).
|
|
23
|
+
const groups = new Map();
|
|
24
|
+
for (const artifact of artifacts) {
|
|
25
|
+
const fp = bodyFingerprint(artifact);
|
|
26
|
+
const existing = groups.get(fp);
|
|
27
|
+
if (existing) {
|
|
28
|
+
existing.push(artifact.path);
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
groups.set(fp, [artifact.path]);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
const findings = [];
|
|
35
|
+
for (const [, paths] of groups) {
|
|
36
|
+
if (paths.length < 2)
|
|
37
|
+
continue;
|
|
38
|
+
const sorted = [...paths].sort();
|
|
39
|
+
const sample = sorted.slice(0, 2).join(" and ");
|
|
40
|
+
findings.push(makeFinding({
|
|
41
|
+
type: "duplicate",
|
|
42
|
+
filePaths: sorted,
|
|
43
|
+
title: "Identical instructions in multiple files",
|
|
44
|
+
description: `Found ${paths.length} files with an identical instruction body ` +
|
|
45
|
+
`(frontmatter ignored): ${sample}.`,
|
|
46
|
+
recommendation: `Consider keeping a single source of truth and referencing it, ` +
|
|
47
|
+
`or removing the redundant copies after review.`,
|
|
48
|
+
}));
|
|
49
|
+
}
|
|
50
|
+
return findings;
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
//# sourceMappingURL=duplicate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"duplicate.js","sourceRoot":"","sources":["../../../src/analyzer/rules/duplicate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAG1D,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAE3C,gEAAgE;AAChE,MAAM,UAAU,eAAe,CAAC,QAA+B;IAC7D,MAAM,UAAU,GAAG,aAAa,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IACnD,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC/D,CAAC;AAED,MAAM,CAAC,MAAM,aAAa,GAAiB;IACzC,IAAI,EAAE,WAAW;IACjB,GAAG,CAAC,SAAS;QACX,gEAAgE;QAChE,MAAM,MAAM,GAAG,IAAI,GAAG,EAAoB,CAAC;QAE3C,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;YACjC,MAAM,EAAE,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAC;YACrC,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAChC,IAAI,QAAQ,EAAE,CAAC;gBACb,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;YAC/B,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;YAClC,CAAC;QACH,CAAC;QAED,MAAM,QAAQ,GAAG,EAAE,CAAC;QAEpB,KAAK,MAAM,CAAC,EAAE,KAAK,CAAC,IAAI,MAAM,EAAE,CAAC;YAC/B,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;gBAAE,SAAS;YAE/B,MAAM,MAAM,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC;YACjC,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAChD,QAAQ,CAAC,IAAI,CACX,WAAW,CAAC;gBACV,IAAI,EAAE,WAAW;gBACjB,SAAS,EAAE,MAAM;gBACjB,KAAK,EAAE,0CAA0C;gBACjD,WAAW,EACT,SAAS,KAAK,CAAC,MAAM,4CAA4C;oBACjE,0BAA0B,MAAM,GAAG;gBACrC,cAAc,EACZ,gEAAgE;oBAChE,gDAAgD;aACnD,CAAC,CACH,CAAC;QACJ,CAAC;QAED,OAAO,QAAQ,CAAC;IAClB,CAAC;CACF,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* High-stakes detection (Spec §26, §27, Rule 20) — REVISED after real-world
|
|
3
|
+
* pilot, two-tier model.
|
|
4
|
+
*
|
|
5
|
+
* Tier 1 (STRONG): a single strong term in CONTENT alone justifies CAUTION
|
|
6
|
+
* e.g. secret, credential, password, authentication, authorization,
|
|
7
|
+
* permission, payment, private key, api key, access token.
|
|
8
|
+
*
|
|
9
|
+
* Tier 2 (CONTEXTUAL): weaker terms (deploy, production, security, database,
|
|
10
|
+
* billing, infrastructure, migration) NEVER trigger a finding on their own.
|
|
11
|
+
* They may only reinforce a strong signal, never substitute for it. This
|
|
12
|
+
* removes the pilot's false positives ("docs deployed", "security gateways",
|
|
13
|
+
* "deployment scripts").
|
|
14
|
+
*
|
|
15
|
+
* Path/scope names are never the sole trigger: `.env.example`, `config/auth.ts`,
|
|
16
|
+
* `deployment.md` must not produce CAUTION by naming alone.
|
|
17
|
+
*
|
|
18
|
+
* Practically: a finding is produced iff a STRONG term is present in a rule's
|
|
19
|
+
* content. CONTEXTUAL entries are retained as reinforcement only.
|
|
20
|
+
*/
|
|
21
|
+
import type { ConfigurationArtifact } from "../../types/configuration.js";
|
|
22
|
+
import type { AnalyzerRule } from "../types.js";
|
|
23
|
+
/** Strong terms that alone justify a CAUTION finding (word-boundary match). */
|
|
24
|
+
export declare const STRONG_TERMS: readonly string[];
|
|
25
|
+
/** Contextual terms that NEVER trigger alone (Spec §26 weak categories). */
|
|
26
|
+
export declare const CONTEXTUAL_TERMS: readonly string[];
|
|
27
|
+
/** Returns the first STRONG term present in `text`, or undefined. */
|
|
28
|
+
export declare function matchStrongTerm(text: string): string | undefined;
|
|
29
|
+
/** Returns the first CONTEXTUAL term present in `text`, or undefined. */
|
|
30
|
+
export declare function matchContextualTerm(text: string): string | undefined;
|
|
31
|
+
export declare const highStakesRule: AnalyzerRule;
|
|
32
|
+
/**
|
|
33
|
+
* Returns the matched STRONG term, or undefined. Detection is content-driven
|
|
34
|
+
* (Rule 20 still reads the artifact, but path/scope names never trigger alone).
|
|
35
|
+
*/
|
|
36
|
+
export declare function detectHighStakes(artifact: ConfigurationArtifact): string | undefined;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* High-stakes detection (Spec §26, §27, Rule 20) — REVISED after real-world
|
|
3
|
+
* pilot, two-tier model.
|
|
4
|
+
*
|
|
5
|
+
* Tier 1 (STRONG): a single strong term in CONTENT alone justifies CAUTION
|
|
6
|
+
* e.g. secret, credential, password, authentication, authorization,
|
|
7
|
+
* permission, payment, private key, api key, access token.
|
|
8
|
+
*
|
|
9
|
+
* Tier 2 (CONTEXTUAL): weaker terms (deploy, production, security, database,
|
|
10
|
+
* billing, infrastructure, migration) NEVER trigger a finding on their own.
|
|
11
|
+
* They may only reinforce a strong signal, never substitute for it. This
|
|
12
|
+
* removes the pilot's false positives ("docs deployed", "security gateways",
|
|
13
|
+
* "deployment scripts").
|
|
14
|
+
*
|
|
15
|
+
* Path/scope names are never the sole trigger: `.env.example`, `config/auth.ts`,
|
|
16
|
+
* `deployment.md` must not produce CAUTION by naming alone.
|
|
17
|
+
*
|
|
18
|
+
* Practically: a finding is produced iff a STRONG term is present in a rule's
|
|
19
|
+
* content. CONTEXTUAL entries are retained as reinforcement only.
|
|
20
|
+
*/
|
|
21
|
+
import { makeFinding } from "./mapping.js";
|
|
22
|
+
/** Strong terms that alone justify a CAUTION finding (word-boundary match). */
|
|
23
|
+
export const STRONG_TERMS = [
|
|
24
|
+
"secret",
|
|
25
|
+
"credential",
|
|
26
|
+
"password",
|
|
27
|
+
"authentication",
|
|
28
|
+
"authorization",
|
|
29
|
+
"permission",
|
|
30
|
+
"payment",
|
|
31
|
+
"api key",
|
|
32
|
+
"private key",
|
|
33
|
+
"access token",
|
|
34
|
+
];
|
|
35
|
+
/** Contextual terms that NEVER trigger alone (Spec §26 weak categories). */
|
|
36
|
+
export const CONTEXTUAL_TERMS = [
|
|
37
|
+
"deploy",
|
|
38
|
+
"deployment",
|
|
39
|
+
"production",
|
|
40
|
+
"security",
|
|
41
|
+
"database",
|
|
42
|
+
"billing",
|
|
43
|
+
"infrastructure",
|
|
44
|
+
"migration",
|
|
45
|
+
];
|
|
46
|
+
/**
|
|
47
|
+
* Escapes a literal and anchors it to whole-word boundaries. Single-word terms
|
|
48
|
+
* accept an optional trailing `s` so (credential/credentials) both match.
|
|
49
|
+
*/
|
|
50
|
+
function termPattern(term) {
|
|
51
|
+
const escaped = term.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
52
|
+
const plural = term.includes(" ") ? escaped : `${escaped}(?:s|es)?`;
|
|
53
|
+
return new RegExp(`\\b${plural}\\b`, "i");
|
|
54
|
+
}
|
|
55
|
+
/** Returns the first STRONG term present in `text`, or undefined. */
|
|
56
|
+
export function matchStrongTerm(text) {
|
|
57
|
+
return STRONG_TERMS.find((term) => termPattern(term).test(text));
|
|
58
|
+
}
|
|
59
|
+
/** Returns the first CONTEXTUAL term present in `text`, or undefined. */
|
|
60
|
+
export function matchContextualTerm(text) {
|
|
61
|
+
return CONTEXTUAL_TERMS.find((term) => termPattern(term).test(text));
|
|
62
|
+
}
|
|
63
|
+
export const highStakesRule = {
|
|
64
|
+
name: "high-stakes",
|
|
65
|
+
run(artifacts) {
|
|
66
|
+
const findings = [];
|
|
67
|
+
for (const artifact of artifacts) {
|
|
68
|
+
const strong = matchStrongTerm(artifact.content);
|
|
69
|
+
if (!strong)
|
|
70
|
+
continue;
|
|
71
|
+
// A contextual term can only reinforce a strong signal; it is never sufficient.
|
|
72
|
+
const context = matchContextualTerm(artifact.content);
|
|
73
|
+
const why = context ? `(reinforced by "${context}")` : "";
|
|
74
|
+
findings.push(makeFinding({
|
|
75
|
+
type: "high-stakes",
|
|
76
|
+
filePaths: [artifact.path],
|
|
77
|
+
title: "Potentially high-stakes configuration",
|
|
78
|
+
description: `${artifact.path} contains a strong high-stakes signal related to ` +
|
|
79
|
+
`"${strong}" ${why}. Potentially high-stakes configuration detected: ` +
|
|
80
|
+
`this instruction may be low frequency but important. Review manually ` +
|
|
81
|
+
`before modifying or removing it.`,
|
|
82
|
+
recommendation: `Before changing or removing this rule, confirm its scope and ` +
|
|
83
|
+
`purpose with context of the related system.`,
|
|
84
|
+
}));
|
|
85
|
+
}
|
|
86
|
+
return findings;
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
/**
|
|
90
|
+
* Returns the matched STRONG term, or undefined. Detection is content-driven
|
|
91
|
+
* (Rule 20 still reads the artifact, but path/scope names never trigger alone).
|
|
92
|
+
*/
|
|
93
|
+
export function detectHighStakes(artifact) {
|
|
94
|
+
return matchStrongTerm(artifact.content);
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=high-stakes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"high-stakes.js","sourceRoot":"","sources":["../../../src/analyzer/rules/high-stakes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAE3C,+EAA+E;AAC/E,MAAM,CAAC,MAAM,YAAY,GAAsB;IAC7C,QAAQ;IACR,YAAY;IACZ,UAAU;IACV,gBAAgB;IAChB,eAAe;IACf,YAAY;IACZ,SAAS;IACT,SAAS;IACT,aAAa;IACb,cAAc;CACf,CAAC;AAEF,4EAA4E;AAC5E,MAAM,CAAC,MAAM,gBAAgB,GAAsB;IACjD,QAAQ;IACR,YAAY;IACZ,YAAY;IACZ,UAAU;IACV,UAAU;IACV,SAAS;IACT,gBAAgB;IAChB,WAAW;CACZ,CAAC;AAEF;;;GAGG;AACH,SAAS,WAAW,CAAC,IAAY;IAC/B,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;IAC5D,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,WAAW,CAAC;IACpE,OAAO,IAAI,MAAM,CAAC,MAAM,MAAM,KAAK,EAAE,GAAG,CAAC,CAAC;AAC5C,CAAC;AAED,qEAAqE;AACrE,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,OAAO,YAAY,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;AACnE,CAAC;AAED,yEAAyE;AACzE,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC9C,OAAO,gBAAgB,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;AACvE,CAAC;AAED,MAAM,CAAC,MAAM,cAAc,GAAiB;IAC1C,IAAI,EAAE,aAAa;IACnB,GAAG,CAAC,SAAS;QACX,MAAM,QAAQ,GAAG,EAAE,CAAC;QAEpB,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;YACjC,MAAM,MAAM,GAAG,eAAe,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;YACjD,IAAI,CAAC,MAAM;gBAAE,SAAS;YAEtB,gFAAgF;YAChF,MAAM,OAAO,GAAG,mBAAmB,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;YACtD,MAAM,GAAG,GAAG,OAAO,CAAC,CAAC,CAAC,mBAAmB,OAAO,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;YAE1D,QAAQ,CAAC,IAAI,CACX,WAAW,CAAC;gBACV,IAAI,EAAE,aAAa;gBACnB,SAAS,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC;gBAC1B,KAAK,EAAE,uCAAuC;gBAC9C,WAAW,EACT,GAAG,QAAQ,CAAC,IAAI,mDAAmD;oBACnE,IAAI,MAAM,KAAK,GAAG,oDAAoD;oBACtE,uEAAuE;oBACvE,kCAAkC;gBACpC,cAAc,EACZ,+DAA+D;oBAC/D,6CAA6C;aAChD,CAAC,CACH,CAAC;QACJ,CAAC;QAED,OAAO,QAAQ,CAAC;IAClB,CAAC;CACF,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAC9B,QAA+B;IAE/B,OAAO,eAAe,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;AAC3C,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Large instruction detection (Spec §20).
|
|
3
|
+
*
|
|
4
|
+
* A file being large is not inherently bad, but a file above the token
|
|
5
|
+
* threshold may contribute significantly to model context. In the MVP both
|
|
6
|
+
* the notice and warning thresholds map to `label=CONTEXT`, `severity=info`
|
|
7
|
+
* through the normative mapping; we only ever emit `large-file` findings.
|
|
8
|
+
*
|
|
9
|
+
* Note: the `warningFileTokens` threshold is reserved (Spec §11) and produces
|
|
10
|
+
* no additional severity in the MVP — the taxonomy keeps `info` here.
|
|
11
|
+
*/
|
|
12
|
+
import { type AnalyzerRule } from "../types.js";
|
|
13
|
+
export declare const largeFileRule: AnalyzerRule;
|
|
14
|
+
/** Formats a token count with thousands separators, e.g. 4800 -> "4,800". */
|
|
15
|
+
export declare function formatTokens(tokens: number): string;
|