@kevinrabun/judges 3.1.0 → 3.2.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/README.md +102 -7
- package/dist/api.d.ts +1 -0
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +2 -0
- package/dist/api.js.map +1 -1
- package/dist/ast/index.d.ts +12 -1
- package/dist/ast/index.d.ts.map +1 -1
- package/dist/ast/index.js +72 -3
- package/dist/ast/index.js.map +1 -1
- package/dist/ast/tree-sitter-ast.d.ts +34 -0
- package/dist/ast/tree-sitter-ast.d.ts.map +1 -0
- package/dist/ast/tree-sitter-ast.js +747 -0
- package/dist/ast/tree-sitter-ast.js.map +1 -0
- package/dist/cli.d.ts +16 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +317 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +13 -15
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +44 -36
- package/dist/index.js.map +1 -1
- package/grammars/tree-sitter-c_sharp.wasm +0 -0
- package/grammars/tree-sitter-go.wasm +0 -0
- package/grammars/tree-sitter-java.wasm +0 -0
- package/grammars/tree-sitter-python.wasm +0 -0
- package/grammars/tree-sitter-rust.wasm +0 -0
- package/package.json +11 -2
- package/server.json +2 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Judges Panel
|
|
2
2
|
|
|
3
|
-
An MCP (Model Context Protocol) server that provides a panel of **35 specialized judges** to evaluate AI-generated code — acting as an independent quality gate regardless of which project is being reviewed.
|
|
3
|
+
An MCP (Model Context Protocol) server that provides a panel of **35 specialized judges** to evaluate AI-generated code — acting as an independent quality gate regardless of which project is being reviewed. Combines **deterministic pattern matching & AST analysis** (instant, offline, zero LLM calls) with **LLM-powered deep-review prompts** that let your AI assistant perform expert-persona analysis across all 35 domains.
|
|
4
4
|
|
|
5
5
|
**Highlights:**
|
|
6
6
|
- Includes an **App Builder Workflow (3-step)** demo for release decisions, plain-language risk summaries, and prioritized fixes — see [Try the Demo](#2-try-the-demo).
|
|
@@ -9,12 +9,100 @@ An MCP (Model Context Protocol) server that provides a panel of **35 specialized
|
|
|
9
9
|
|
|
10
10
|
[](https://github.com/KevinRabun/judges/actions/workflows/ci.yml)
|
|
11
11
|
[](https://www.npmjs.com/package/@kevinrabun/judges)
|
|
12
|
+
[](https://www.npmjs.com/package/@kevinrabun/judges)
|
|
12
13
|
[](https://opensource.org/licenses/MIT)
|
|
14
|
+
[](https://github.com/KevinRabun/judges/actions)
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Why Judges?
|
|
19
|
+
|
|
20
|
+
AI code generators (Copilot, Cursor, Claude, ChatGPT, etc.) write code fast — but they routinely produce **insecure defaults, missing auth, hardcoded secrets, and poor error handling**. Human reviewers catch some of this, but nobody reviews 35 dimensions consistently.
|
|
21
|
+
|
|
22
|
+
| | ESLint / Biome | SonarQube | Semgrep / CodeQL | **Judges** |
|
|
23
|
+
|---|---|---|---|---|
|
|
24
|
+
| **Scope** | Style + some bugs | Bugs + code smells | Security patterns | **35 domains**: security, cost, compliance, a11y, API design, cloud, UX, … |
|
|
25
|
+
| **AI-generated code focus** | No | No | Partial | **Purpose-built** for AI output failure modes |
|
|
26
|
+
| **Setup** | Config per project | Server + scanner | Cloud or local | **One command**: `npx @kevinrabun/judges eval file.ts` |
|
|
27
|
+
| **Auto-fix patches** | Some | No | No | **47 deterministic patches** — instant, offline |
|
|
28
|
+
| **Non-technical output** | No | Dashboard | No | **Plain-language findings** with What/Why/Next |
|
|
29
|
+
| **MCP native** | No | No | No | **Yes** — works inside Copilot, Claude, Cursor |
|
|
30
|
+
| **SARIF output** | No | Yes | Yes | **Yes** — upload to GitHub Code Scanning |
|
|
31
|
+
| **Cost** | Free | $$$$ | Free/paid | **Free / MIT** |
|
|
32
|
+
|
|
33
|
+
**Judges doesn't replace linters** — it covers the dimensions linters don't: authentication strategy, data sovereignty, cost patterns, accessibility, framework-specific anti-patterns, and architectural issues across multiple files.
|
|
13
34
|
|
|
14
35
|
---
|
|
15
36
|
|
|
16
37
|
## Quick Start
|
|
17
38
|
|
|
39
|
+
### Try it now (no clone needed)
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
# Install globally
|
|
43
|
+
npm install -g @kevinrabun/judges
|
|
44
|
+
|
|
45
|
+
# Evaluate any file
|
|
46
|
+
judges eval src/app.ts
|
|
47
|
+
|
|
48
|
+
# Pipe from stdin
|
|
49
|
+
cat api.py | judges eval --language python
|
|
50
|
+
|
|
51
|
+
# Single judge
|
|
52
|
+
judges eval --judge cybersecurity server.ts
|
|
53
|
+
|
|
54
|
+
# SARIF output for CI
|
|
55
|
+
judges eval --file app.ts --format sarif > results.sarif
|
|
56
|
+
|
|
57
|
+
# List all 35 judges
|
|
58
|
+
judges list
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Use in GitHub Actions
|
|
62
|
+
|
|
63
|
+
Add Judges to your CI pipeline with zero configuration:
|
|
64
|
+
|
|
65
|
+
```yaml
|
|
66
|
+
# .github/workflows/judges.yml
|
|
67
|
+
name: Judges Code Review
|
|
68
|
+
on: [pull_request]
|
|
69
|
+
|
|
70
|
+
jobs:
|
|
71
|
+
judges:
|
|
72
|
+
runs-on: ubuntu-latest
|
|
73
|
+
permissions:
|
|
74
|
+
contents: read
|
|
75
|
+
security-events: write # only if using upload-sarif
|
|
76
|
+
steps:
|
|
77
|
+
- uses: actions/checkout@v4
|
|
78
|
+
- uses: KevinRabun/judges@main
|
|
79
|
+
with:
|
|
80
|
+
path: src/api.ts # file or directory
|
|
81
|
+
format: text # text | json | sarif | markdown
|
|
82
|
+
upload-sarif: true # upload to GitHub Code Scanning
|
|
83
|
+
fail-on-findings: true # fail CI on critical/high findings
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
**Outputs** available for downstream steps: `verdict`, `score`, `findings`, `critical`, `high`, `sarif-file`.
|
|
87
|
+
|
|
88
|
+
### Use with Docker (no Node.js required)
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
# Build the image
|
|
92
|
+
docker build -t judges .
|
|
93
|
+
|
|
94
|
+
# Evaluate a local file
|
|
95
|
+
docker run --rm -v $(pwd):/code judges eval --file /code/app.ts
|
|
96
|
+
|
|
97
|
+
# Pipe from stdin
|
|
98
|
+
cat api.py | docker run --rm -i judges eval --language python
|
|
99
|
+
|
|
100
|
+
# List judges
|
|
101
|
+
docker run --rm judges list
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Or use as an MCP server
|
|
105
|
+
|
|
18
106
|
### 1. Install and Build
|
|
19
107
|
|
|
20
108
|
```bash
|
|
@@ -279,24 +367,24 @@ This helps keep Copilot feedback aligned with Judges findings.
|
|
|
279
367
|
|
|
280
368
|
The tribunal operates in three layers:
|
|
281
369
|
|
|
282
|
-
1. **Pattern-Based Analysis** — All tools (`evaluate_code`, `evaluate_code_single_judge`, `evaluate_project`, `evaluate_diff`) perform heuristic analysis using regex pattern matching to catch common anti-patterns. This
|
|
370
|
+
1. **Pattern-Based Analysis** — All tools (`evaluate_code`, `evaluate_code_single_judge`, `evaluate_project`, `evaluate_diff`) perform heuristic analysis using regex pattern matching to catch common anti-patterns. This layer is instant, deterministic, and runs entirely offline with zero external API calls.
|
|
283
371
|
|
|
284
|
-
2. **AST-Based Structural Analysis** — The Code Structure judge (`STRUCT-*` rules) uses real Abstract Syntax Tree parsing to measure cyclomatic complexity, nesting depth, function length, parameter count, dead code, and type safety with precision that regex cannot achieve. JavaScript/TypeScript files are parsed via the TypeScript Compiler API
|
|
372
|
+
2. **AST-Based Structural Analysis** — The Code Structure judge (`STRUCT-*` rules) uses real Abstract Syntax Tree parsing to measure cyclomatic complexity, nesting depth, function length, parameter count, dead code, and type safety with precision that regex cannot achieve. JavaScript/TypeScript files are parsed via the TypeScript Compiler API. Python, Rust, Go, Java, and C# are parsed via **tree-sitter WASM grammars** — real syntax trees compiled to WebAssembly that run in-process with zero native dependencies. A scope-tracking structural parser is kept as a fallback when WASM grammars are unavailable. No external AST server required.
|
|
285
373
|
|
|
286
|
-
3. **LLM-Powered Deep Analysis (Prompts)** — The server exposes MCP prompts (e.g., `judge-data-security`, `full-tribunal`) that provide each judge's expert persona as a system prompt. When used by an LLM-based client,
|
|
374
|
+
3. **LLM-Powered Deep Analysis (Prompts)** — The server exposes MCP prompts (e.g., `judge-data-security`, `full-tribunal`) that provide each judge's expert persona as a system prompt. When used by an LLM-based client (Copilot, Claude, Cursor, etc.), the host LLM performs deeper, context-aware probabilistic analysis beyond what static patterns can detect. This is where the `systemPrompt` on each judge comes alive — Judges itself makes no LLM calls, but it provides the expert criteria so your AI assistant can act as 35 specialized reviewers.
|
|
287
375
|
|
|
288
376
|
---
|
|
289
377
|
|
|
290
378
|
## Composable by Design
|
|
291
379
|
|
|
292
|
-
Judges Panel
|
|
380
|
+
Judges Panel is a **dual-layer** review system: instant **deterministic tools** (offline, no API keys) for pattern and AST analysis, plus **35 expert-persona MCP prompts** that unlock LLM-powered deep analysis when connected to an AI client. It does not try to be a CVE scanner or a linter. Those capabilities belong in dedicated MCP servers that an AI agent can orchestrate alongside Judges.
|
|
293
381
|
|
|
294
382
|
### Built-in AST Analysis (v2.0.0)
|
|
295
383
|
|
|
296
384
|
Unlike earlier versions that recommended a separate AST MCP server, Judges Panel now includes **real AST-based structural analysis** out of the box:
|
|
297
385
|
|
|
298
386
|
- **JavaScript / TypeScript** — Parsed with the TypeScript Compiler API (`ts.createSourceFile`) for full-fidelity AST
|
|
299
|
-
- **Python, Rust, Go, Java, C#** —
|
|
387
|
+
- **Python, Rust, Go, Java, C#** — Parsed with **tree-sitter WASM grammars** for full syntax-tree analysis (functions, complexity, nesting, dead code, type safety). Falls back to a scope-tracking structural parser when WASM grammars are unavailable
|
|
300
388
|
|
|
301
389
|
The Code Structure judge (`STRUCT-*`) uses these parsers to accurately measure:
|
|
302
390
|
|
|
@@ -723,7 +811,8 @@ judges/
|
|
|
723
811
|
│ │ ├── index.ts # analyzeStructure() — routes to correct parser
|
|
724
812
|
│ │ ├── types.ts # FunctionInfo, CodeStructure interfaces
|
|
725
813
|
│ │ ├── typescript-ast.ts # TypeScript Compiler API parser (JS/TS)
|
|
726
|
-
│ │
|
|
814
|
+
│ │ ├── tree-sitter-ast.ts # Tree-sitter WASM parser (Python/Rust/Go/Java/C#)
|
|
815
|
+
│ │ └── structural-parser.ts # Fallback scope-tracking parser
|
|
727
816
|
│ ├── evaluators/ # Analysis engine for each judge
|
|
728
817
|
│ │ ├── index.ts # evaluateWithJudge(), evaluateWithTribunal(), evaluateProject(), etc.
|
|
729
818
|
│ │ ├── shared.ts # Scoring, verdict logic, markdown formatters
|
|
@@ -741,6 +830,12 @@ judges/
|
|
|
741
830
|
│ └── demo.ts # Run: npm run demo
|
|
742
831
|
├── tests/
|
|
743
832
|
│ └── judges.test.ts # Run: npm test
|
|
833
|
+
├── grammars/ # Tree-sitter WASM grammar files
|
|
834
|
+
│ ├── tree-sitter-python.wasm
|
|
835
|
+
│ ├── tree-sitter-go.wasm
|
|
836
|
+
│ ├── tree-sitter-rust.wasm
|
|
837
|
+
│ ├── tree-sitter-java.wasm
|
|
838
|
+
│ └── tree-sitter-c_sharp.wasm
|
|
744
839
|
├── server.json # MCP Registry manifest
|
|
745
840
|
├── package.json
|
|
746
841
|
├── tsconfig.json
|
package/dist/api.d.ts
CHANGED
|
@@ -19,6 +19,7 @@ export { LRUCache, contentHash } from "./cache.js";
|
|
|
19
19
|
export { clearProjectCache } from "./evaluators/project.js";
|
|
20
20
|
export { findingsToSarif, evaluationToSarif, verdictToSarif, validateSarifLog } from "./formatters/sarif.js";
|
|
21
21
|
export type { SarifValidationError } from "./formatters/sarif.js";
|
|
22
|
+
export { runCli } from "./cli.js";
|
|
22
23
|
import type { EvaluationOptions } from "./evaluators/index.js";
|
|
23
24
|
import type { JudgeEvaluation, TribunalVerdict } from "./types.js";
|
|
24
25
|
/**
|
package/dist/api.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,YAAY,EACV,QAAQ,EACR,OAAO,EACP,OAAO,EACP,KAAK,EACL,UAAU,EACV,YAAY,EACZ,YAAY,EACZ,WAAW,EACX,cAAc,EACd,WAAW,EACX,eAAe,EACf,iBAAiB,EACjB,eAAe,EACf,eAAe,EACf,eAAe,EACf,mBAAmB,EACnB,gBAAgB,EAChB,oBAAoB,EACpB,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,EACjB,wBAAwB,EACxB,oBAAoB,EACpB,YAAY,EACZ,aAAa,GACd,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAGpF,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAGzD,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAIxE,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,eAAe,EACf,YAAY,EACZ,mBAAmB,EACnB,iBAAiB,EACjB,mBAAmB,EACnB,uBAAuB,EACvB,qBAAqB,EACrB,uBAAuB,EACvB,0BAA0B,EAC1B,qBAAqB,GACtB,MAAM,uBAAuB,CAAC;AAG/B,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,0BAA0B,EAAE,MAAM,oBAAoB,CAAC;AAGnG,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAGlE,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAG5D,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAC7G,YAAY,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,YAAY,EACV,QAAQ,EACR,OAAO,EACP,OAAO,EACP,KAAK,EACL,UAAU,EACV,YAAY,EACZ,YAAY,EACZ,WAAW,EACX,cAAc,EACd,WAAW,EACX,eAAe,EACf,iBAAiB,EACjB,eAAe,EACf,eAAe,EACf,eAAe,EACf,mBAAmB,EACnB,gBAAgB,EAChB,oBAAoB,EACpB,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,EACjB,wBAAwB,EACxB,oBAAoB,EACpB,YAAY,EACZ,aAAa,GACd,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAGpF,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAGzD,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAIxE,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,eAAe,EACf,YAAY,EACZ,mBAAmB,EACnB,iBAAiB,EACjB,mBAAmB,EACnB,uBAAuB,EACvB,qBAAqB,EACrB,uBAAuB,EACvB,0BAA0B,EAC1B,qBAAqB,GACtB,MAAM,uBAAuB,CAAC;AAG/B,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,0BAA0B,EAAE,MAAM,oBAAoB,CAAC;AAGnG,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAGlE,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAG5D,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAC7G,YAAY,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAGlE,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAKlC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,KAAK,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAInE;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,eAAe,CAEzG;AAED;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,iBAAiB,GAC1B,eAAe,CAMjB"}
|
package/dist/api.js
CHANGED
|
@@ -25,6 +25,8 @@ export { LRUCache, contentHash } from "./cache.js";
|
|
|
25
25
|
export { clearProjectCache } from "./evaluators/project.js";
|
|
26
26
|
// ─── Formatters ──────────────────────────────────────────────────────────────
|
|
27
27
|
export { findingsToSarif, evaluationToSarif, verdictToSarif, validateSarifLog } from "./formatters/sarif.js";
|
|
28
|
+
// ─── CLI ─────────────────────────────────────────────────────────────────────
|
|
29
|
+
export { runCli } from "./cli.js";
|
|
28
30
|
// ─── Convenience Aliases ─────────────────────────────────────────────────────
|
|
29
31
|
import { evaluateWithTribunal, evaluateWithJudge } from "./evaluators/index.js";
|
|
30
32
|
import { getJudge } from "./judges/index.js";
|
package/dist/api.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AA+BH,gFAAgF;AAChF,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEpF,gFAAgF;AAChF,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEzD,gFAAgF;AAChF,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAExE,gFAAgF;AAEhF,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,eAAe,EACf,YAAY,EACZ,mBAAmB,EACnB,iBAAiB,EACjB,mBAAmB,EACnB,uBAAuB,EACvB,qBAAqB,EACrB,uBAAuB,EACvB,0BAA0B,EAC1B,qBAAqB,GACtB,MAAM,uBAAuB,CAAC;AAE/B,+EAA+E;AAC/E,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,0BAA0B,EAAE,MAAM,oBAAoB,CAAC;AAEnG,gFAAgF;AAChF,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAElE,gFAAgF;AAChF,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAE5D,gFAAgF;AAChF,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAG7G,gFAAgF;AAEhF,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAGhF,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE9C;;;;;;;GAOG;AACH,MAAM,UAAU,YAAY,CAAC,IAAY,EAAE,QAAgB,EAAE,OAA2B;IACtF,OAAO,oBAAoB,CAAC,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;AAClE,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,uBAAuB,CACrC,OAAe,EACf,IAAY,EACZ,QAAgB,EAChB,OAA2B;IAE3B,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;IAChC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,eAAe,CAAC,mBAAmB,OAAO,GAAG,EAAE,OAAO,CAAC,CAAC;IACpE,CAAC;IACD,OAAO,iBAAiB,CAAC,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;AACtE,CAAC"}
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AA+BH,gFAAgF;AAChF,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEpF,gFAAgF;AAChF,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEzD,gFAAgF;AAChF,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAExE,gFAAgF;AAEhF,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,eAAe,EACf,YAAY,EACZ,mBAAmB,EACnB,iBAAiB,EACjB,mBAAmB,EACnB,uBAAuB,EACvB,qBAAqB,EACrB,uBAAuB,EACvB,0BAA0B,EAC1B,qBAAqB,GACtB,MAAM,uBAAuB,CAAC;AAE/B,+EAA+E;AAC/E,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,0BAA0B,EAAE,MAAM,oBAAoB,CAAC;AAEnG,gFAAgF;AAChF,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAElE,gFAAgF;AAChF,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAE5D,gFAAgF;AAChF,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAG7G,gFAAgF;AAChF,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAElC,gFAAgF;AAEhF,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAGhF,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE9C;;;;;;;GAOG;AACH,MAAM,UAAU,YAAY,CAAC,IAAY,EAAE,QAAgB,EAAE,OAA2B;IACtF,OAAO,oBAAoB,CAAC,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;AAClE,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,uBAAuB,CACrC,OAAe,EACf,IAAY,EACZ,QAAgB,EAChB,OAA2B;IAE3B,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;IAChC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,eAAe,CAAC,mBAAmB,OAAO,GAAG,EAAE,OAAO,CAAC,CAAC;IACpE,CAAC;IACD,OAAO,iBAAiB,CAAC,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;AACtE,CAAC"}
|
package/dist/ast/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { CodeStructure, FunctionInfo } from "./types.js";
|
|
2
2
|
export type { CodeStructure, FunctionInfo };
|
|
3
|
+
export { isTreeSitterAvailable, isTreeSitterReadySync } from "./tree-sitter-ast.js";
|
|
3
4
|
export { analyzeTaintFlows } from "./taint-tracker.js";
|
|
4
5
|
export type { TaintFlow, TaintSourceKind, TaintSinkKind } from "./taint-tracker.js";
|
|
5
6
|
export { analyzeCrossFileTaint } from "./cross-file-taint.js";
|
|
@@ -7,10 +8,20 @@ export type { CrossFileTaintFlow } from "./cross-file-taint.js";
|
|
|
7
8
|
/**
|
|
8
9
|
* Analyse source code structurally. For JavaScript/TypeScript this uses the
|
|
9
10
|
* TypeScript compiler API (full AST). For Python, Rust, Go, Java, and C# it
|
|
10
|
-
* uses
|
|
11
|
+
* uses tree-sitter (real AST via WASM) when available, falling back to the
|
|
12
|
+
* lightweight scope-tracking parser.
|
|
11
13
|
*
|
|
12
14
|
* Returns function metrics (complexity, nesting, length, params), dead code
|
|
13
15
|
* locations, deep-nesting locations, and type-safety issues.
|
|
14
16
|
*/
|
|
15
17
|
export declare function analyzeStructure(code: string, language: string): CodeStructure;
|
|
18
|
+
/**
|
|
19
|
+
* Async version of analyzeStructure that uses tree-sitter (real AST) for
|
|
20
|
+
* Python, Rust, Go, Java, and C# when WASM grammars are available.
|
|
21
|
+
* Falls back to the structural parser if tree-sitter is not available.
|
|
22
|
+
*
|
|
23
|
+
* Prefer this over analyzeStructure() for Tier 2 languages — it provides
|
|
24
|
+
* the same depth of analysis that the TS Compiler API gives JS/TS.
|
|
25
|
+
*/
|
|
26
|
+
export declare function analyzeStructureAsync(code: string, language: string): Promise<CodeStructure>;
|
|
16
27
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/ast/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ast/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ast/index.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE9D,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,CAAC;AAG5C,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAGpF,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACvD,YAAY,EAAE,SAAS,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAGpF,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,YAAY,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAahE;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,aAAa,CAuC9E;AAED;;;;;;;GAOG;AACH,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAsClG"}
|
package/dist/ast/index.js
CHANGED
|
@@ -1,20 +1,33 @@
|
|
|
1
1
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
2
2
|
// AST Analysis — Unified Entry Point
|
|
3
3
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
4
|
-
// Routes to the TypeScript compiler-based parser for JS/TS
|
|
5
|
-
//
|
|
4
|
+
// Routes to the TypeScript compiler-based parser for JS/TS, the tree-sitter
|
|
5
|
+
// real-AST parser for Python, Rust, Go, Java, and C# (when WASM grammars are
|
|
6
|
+
// available), or the lightweight structural parser as a fallback.
|
|
6
7
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
7
8
|
import { normalizeLanguage } from "../language-patterns.js";
|
|
8
9
|
import { analyzeTypeScript } from "./typescript-ast.js";
|
|
9
10
|
import { analyzeStructurally } from "./structural-parser.js";
|
|
11
|
+
import { isTreeSitterAvailable, isTreeSitterReadySync, analyzeWithTreeSitter, analyzeWithTreeSitterSync, } from "./tree-sitter-ast.js";
|
|
12
|
+
// Re-export tree-sitter availability checks
|
|
13
|
+
export { isTreeSitterAvailable, isTreeSitterReadySync } from "./tree-sitter-ast.js";
|
|
10
14
|
// Re-export taint analysis
|
|
11
15
|
export { analyzeTaintFlows } from "./taint-tracker.js";
|
|
12
16
|
// Re-export cross-file taint analysis
|
|
13
17
|
export { analyzeCrossFileTaint } from "./cross-file-taint.js";
|
|
18
|
+
// ─── Tree-sitter Warm-up ────────────────────────────────────────────────────
|
|
19
|
+
// Pre-initialize tree-sitter on module load so it's ready when needed.
|
|
20
|
+
// This is fire-and-forget; if it fails, analyzeStructure falls back silently.
|
|
21
|
+
const TREE_SITTER_LANGS = ["python", "rust", "go", "java", "csharp"];
|
|
22
|
+
const treeSitterReady = new Map();
|
|
23
|
+
for (const lang of TREE_SITTER_LANGS) {
|
|
24
|
+
treeSitterReady.set(lang, isTreeSitterAvailable(lang));
|
|
25
|
+
}
|
|
14
26
|
/**
|
|
15
27
|
* Analyse source code structurally. For JavaScript/TypeScript this uses the
|
|
16
28
|
* TypeScript compiler API (full AST). For Python, Rust, Go, Java, and C# it
|
|
17
|
-
* uses
|
|
29
|
+
* uses tree-sitter (real AST via WASM) when available, falling back to the
|
|
30
|
+
* lightweight scope-tracking parser.
|
|
18
31
|
*
|
|
19
32
|
* Returns function metrics (complexity, nesting, length, params), dead code
|
|
20
33
|
* locations, deep-nesting locations, and type-safety issues.
|
|
@@ -30,6 +43,17 @@ export function analyzeStructure(code, language) {
|
|
|
30
43
|
case "go":
|
|
31
44
|
case "java":
|
|
32
45
|
case "csharp":
|
|
46
|
+
// Use tree-sitter (real AST) if WASM runtime + grammar already loaded,
|
|
47
|
+
// otherwise fall back to the lightweight structural parser.
|
|
48
|
+
// parser.parse() is synchronous in web-tree-sitter once initialized.
|
|
49
|
+
if (isTreeSitterReadySync(lang)) {
|
|
50
|
+
try {
|
|
51
|
+
return analyzeWithTreeSitterSync(code, lang);
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
// Tree-sitter failed at runtime — fall back silently
|
|
55
|
+
}
|
|
56
|
+
}
|
|
33
57
|
return analyzeStructurally(code, lang);
|
|
34
58
|
default:
|
|
35
59
|
// Unknown language — return a minimal structure
|
|
@@ -46,4 +70,49 @@ export function analyzeStructure(code, language) {
|
|
|
46
70
|
};
|
|
47
71
|
}
|
|
48
72
|
}
|
|
73
|
+
/**
|
|
74
|
+
* Async version of analyzeStructure that uses tree-sitter (real AST) for
|
|
75
|
+
* Python, Rust, Go, Java, and C# when WASM grammars are available.
|
|
76
|
+
* Falls back to the structural parser if tree-sitter is not available.
|
|
77
|
+
*
|
|
78
|
+
* Prefer this over analyzeStructure() for Tier 2 languages — it provides
|
|
79
|
+
* the same depth of analysis that the TS Compiler API gives JS/TS.
|
|
80
|
+
*/
|
|
81
|
+
export async function analyzeStructureAsync(code, language) {
|
|
82
|
+
const lang = normalizeLanguage(language);
|
|
83
|
+
switch (lang) {
|
|
84
|
+
case "javascript":
|
|
85
|
+
case "typescript":
|
|
86
|
+
return analyzeTypeScript(code, lang);
|
|
87
|
+
case "python":
|
|
88
|
+
case "rust":
|
|
89
|
+
case "go":
|
|
90
|
+
case "java":
|
|
91
|
+
case "csharp": {
|
|
92
|
+
// Try tree-sitter first (real AST), fall back to structural parser
|
|
93
|
+
const available = await (treeSitterReady.get(lang) ?? Promise.resolve(false));
|
|
94
|
+
if (available) {
|
|
95
|
+
try {
|
|
96
|
+
return await analyzeWithTreeSitter(code, lang);
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
// Tree-sitter failed at runtime — fall back silently
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return analyzeStructurally(code, lang);
|
|
103
|
+
}
|
|
104
|
+
default:
|
|
105
|
+
return {
|
|
106
|
+
language: lang,
|
|
107
|
+
totalLines: code.split("\n").length,
|
|
108
|
+
functions: [],
|
|
109
|
+
fileCyclomaticComplexity: 1,
|
|
110
|
+
maxNestingDepth: 0,
|
|
111
|
+
deadCodeLines: [],
|
|
112
|
+
deepNestLines: [],
|
|
113
|
+
typeAnyLines: [],
|
|
114
|
+
imports: [],
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
}
|
|
49
118
|
//# sourceMappingURL=index.js.map
|
package/dist/ast/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/ast/index.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,qCAAqC;AACrC,gFAAgF;AAChF,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/ast/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { CodeStructure } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Check whether tree-sitter analysis is available for a given language.
|
|
4
|
+
* Must be called (and awaited) before analyzeWithTreeSitter.
|
|
5
|
+
*/
|
|
6
|
+
export declare function isTreeSitterAvailable(lang: string): Promise<boolean>;
|
|
7
|
+
/**
|
|
8
|
+
* Synchronous readiness check — returns true only if tree-sitter's WASM
|
|
9
|
+
* runtime AND the grammar for `lang` have already been loaded into memory.
|
|
10
|
+
* This is safe to call from synchronous code paths; if the async init
|
|
11
|
+
* hasn't finished yet, it simply returns false and the caller falls back
|
|
12
|
+
* to the structural parser.
|
|
13
|
+
*/
|
|
14
|
+
export declare function isTreeSitterReadySync(lang: string): boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Synchronous tree-sitter analysis. Can ONLY be called when
|
|
17
|
+
* isTreeSitterReadySync(lang) returns true (i.e. parser module and grammar
|
|
18
|
+
* are already loaded). parser.parse() is synchronous in web-tree-sitter
|
|
19
|
+
* once the WASM runtime and grammar are in memory.
|
|
20
|
+
*
|
|
21
|
+
* Returns the same CodeStructure interface as analyzeWithTreeSitter.
|
|
22
|
+
* Throws if preconditions are not met.
|
|
23
|
+
*/
|
|
24
|
+
export declare function analyzeWithTreeSitterSync(code: string, language: string): CodeStructure;
|
|
25
|
+
/**
|
|
26
|
+
* Analyse source code using tree-sitter's real syntax tree.
|
|
27
|
+
* Returns the same CodeStructure interface as the TypeScript and
|
|
28
|
+
* structural parsers — but with much higher precision for non-JS/TS languages.
|
|
29
|
+
*
|
|
30
|
+
* IMPORTANT: Call isTreeSitterAvailable(lang) first. If it returns false,
|
|
31
|
+
* fall back to analyzeStructurally().
|
|
32
|
+
*/
|
|
33
|
+
export declare function analyzeWithTreeSitter(code: string, language: string): Promise<CodeStructure>;
|
|
34
|
+
//# sourceMappingURL=tree-sitter-ast.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tree-sitter-ast.d.ts","sourceRoot":"","sources":["../../src/ast/tree-sitter-ast.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,aAAa,EAAgB,MAAM,YAAY,CAAC;AAkG9D;;;GAGG;AACH,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAK1E;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE3D;AAED;;;;;;;;GAQG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,aAAa,CAKvF;AAED;;;;;;;GAOG;AACH,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAKlG"}
|