skannr 0.2.3 → 0.2.4
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/ARCHITECTURE.md +229 -0
- package/README.md +209 -260
- package/package.json +1 -1
package/ARCHITECTURE.md
ADDED
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
Skannr: Architecture and Design Decisions
|
|
2
|
+
|
|
3
|
+
Overview
|
|
4
|
+
|
|
5
|
+
Skannr is a CLI tool, MCP server, and library that helps developers and AI assistants understand codebases without reading every file. It scans a project, generates compressed structural skeletons of the most relevant files, and returns them ranked by relevance to a natural language question. The output is 96.5% smaller than raw source while retaining all architectural information.
|
|
6
|
+
|
|
7
|
+
The tool also includes risk analysis (quantifying the downstream impact of a code change) and guard (AI code review against team rules with auto-fix capability).
|
|
8
|
+
|
|
9
|
+
Core Problem
|
|
10
|
+
|
|
11
|
+
AI coding assistants are limited by context windows. A typical repo has thousands of files. You cannot paste them all. The assistant sees only what you show it, and guesses the rest.
|
|
12
|
+
|
|
13
|
+
Existing approaches either dump full files (expensive, noisy) or do keyword search (misses architecturally central files that do not mention the search term directly). Neither approach understands the structural relationships between files.
|
|
14
|
+
|
|
15
|
+
Skannr solves this by combining three ranking signals with structural compression, so the AI gets the right files in the right format at minimal token cost.
|
|
16
|
+
|
|
17
|
+
System Architecture
|
|
18
|
+
|
|
19
|
+
The pipeline is linear and stateless. Each stage takes input from the previous stage and produces output for the next. There is no shared mutable state.
|
|
20
|
+
|
|
21
|
+
Input (question + root path)
|
|
22
|
+
> File Scanner (discovers all source files)
|
|
23
|
+
> Ranker (scores files by relevance using hybrid retrieval)
|
|
24
|
+
> Skeletonizer (strips function bodies, keeps signatures and types)
|
|
25
|
+
> Formatter (outputs human text, markdown, or JSON)
|
|
26
|
+
|
|
27
|
+
Each stage is a separate module. They communicate through typed interfaces, not through side effects.
|
|
28
|
+
|
|
29
|
+
File Layout
|
|
30
|
+
|
|
31
|
+
src/
|
|
32
|
+
cli.ts Command registration (Commander.js, subcommands)
|
|
33
|
+
index.ts Library entry point, orchestrates the pipeline
|
|
34
|
+
scanner.ts File discovery with gitignore awareness
|
|
35
|
+
ranker.ts Basic keyword relevance scoring
|
|
36
|
+
ranker-enhanced.ts Hybrid ranking with dependency graph analysis
|
|
37
|
+
skeletonizer.ts Routes files to the correct language adapter
|
|
38
|
+
formatter.ts Output formatting (human, markdown, JSON)
|
|
39
|
+
blast-radius.ts Risk analysis (diff parsing, graph traversal, scoring)
|
|
40
|
+
guard/ AI code review module (schema, provider, fix applier)
|
|
41
|
+
languages/ Language adapters (TypeScript via ts-morph, others via WASM)
|
|
42
|
+
mcp-server.ts MCP tool registration for AI assistant integration
|
|
43
|
+
types.ts Shared type definitions
|
|
44
|
+
|
|
45
|
+
Design Decisions
|
|
46
|
+
|
|
47
|
+
1. Hybrid Retrieval over Pure Keyword Search
|
|
48
|
+
|
|
49
|
+
Decision: Score files using three signals combined, not just keyword matching.
|
|
50
|
+
|
|
51
|
+
The three signals:
|
|
52
|
+
Lexical: keyword frequency in file path and content, with synonym expansion
|
|
53
|
+
Structural: export density, import relevance, symbol count
|
|
54
|
+
Dependency graph: how many other files import this file (centrality)
|
|
55
|
+
|
|
56
|
+
Why: A file named utils.ts might contain the most architecturally important auth logic but never mention the word "auth" in its filename. Pure keyword search misses it. Dependency centrality catches it because everything imports it.
|
|
57
|
+
|
|
58
|
+
The weights are: lexical 50%, structural 20%, enhanced score 30%, with a dependency boost of 15% blended in. These were tuned empirically against large TypeScript monorepos.
|
|
59
|
+
|
|
60
|
+
2. AST Skeleton Generation over Summarization
|
|
61
|
+
|
|
62
|
+
Decision: Generate skeletons by stripping function bodies from the AST, not by asking an LLM to summarize.
|
|
63
|
+
|
|
64
|
+
Why:
|
|
65
|
+
Deterministic (same input always produces same output)
|
|
66
|
+
Fast (milliseconds, not seconds)
|
|
67
|
+
Free (no API call)
|
|
68
|
+
Lossless for architecture (all types, signatures, imports, class hierarchies preserved)
|
|
69
|
+
Only lossy for implementation details (function bodies replaced with a placeholder)
|
|
70
|
+
|
|
71
|
+
The TypeScript adapter uses ts-morph (a TypeScript compiler API wrapper) which gives perfect AST access. For Python, Go, Rust, and Java, the tool uses web-tree-sitter (WASM compiled grammars) which gives equivalent structural parsing without requiring native C++ compilation.
|
|
72
|
+
|
|
73
|
+
3. WASM over Native Tree-sitter
|
|
74
|
+
|
|
75
|
+
Decision: Ship tree-sitter grammars as WASM files instead of native bindings.
|
|
76
|
+
|
|
77
|
+
Why: Native tree-sitter requires a C++ compiler (Visual Studio on Windows, Xcode on Mac). Most JavaScript developers do not have these installed. The npm install would fail for the majority of users.
|
|
78
|
+
|
|
79
|
+
WASM grammars are platform-independent. They run everywhere Node.js runs. The tradeoff is slightly slower parsing (roughly 2x), but since we only parse files the ranker already selected as relevant (typically 5-10 files), the total time remains under 2 seconds.
|
|
80
|
+
|
|
81
|
+
The WASM files add 2MB to the package. This is acceptable because it eliminates a class of installation failures entirely.
|
|
82
|
+
|
|
83
|
+
4. File-level Dependency Graph (not Symbol-level)
|
|
84
|
+
|
|
85
|
+
Decision: Track dependencies at file granularity (file A imports file B) not at symbol granularity (function X calls function Y).
|
|
86
|
+
|
|
87
|
+
Why: Symbol-level call graphs require type resolution, which in turn requires a full project compilation context. TypeScript's type checker can do this but only for TS/JS. Python, Go, Rust, and Java would each need their own type resolver. The engineering cost is disproportionate to the accuracy gain for the primary use case (ranking files by relevance).
|
|
88
|
+
|
|
89
|
+
File-level import graphs are trivially extractable from any language via regex or AST import node detection. They provide sufficient signal for ranking and risk analysis.
|
|
90
|
+
|
|
91
|
+
This is documented as a stated limitation. Symbol-level resolution is planned for a future version.
|
|
92
|
+
|
|
93
|
+
5. Deterministic Risk Scoring over LLM-based Assessment
|
|
94
|
+
|
|
95
|
+
Decision: Compute risk as a weighted formula with named constants, not by asking an LLM "how risky is this."
|
|
96
|
+
|
|
97
|
+
The formula:
|
|
98
|
+
risk = 2.5 * (affected files / total files)
|
|
99
|
+
+ 2.5 * (average centrality of affected files)
|
|
100
|
+
+ 3.5 * (fraction of affected files with no test coverage)
|
|
101
|
+
+ 1.5 * (max hop depth reached / max hops configured)
|
|
102
|
+
|
|
103
|
+
Why:
|
|
104
|
+
Deterministic: same diff always produces same score
|
|
105
|
+
Fast: runs in under 2 seconds with zero API calls
|
|
106
|
+
CI-gateable: you can block merges on a number, not on an LLM's mood
|
|
107
|
+
Transparent: the formula is visible, the weights are named constants, the inputs are shown
|
|
108
|
+
Reproducible: another developer running the same command gets the same result
|
|
109
|
+
|
|
110
|
+
The untested ratio has the highest weight (3.5 out of 10) because untested downstream code is the strongest signal that a merge is risky. This weighting was a deliberate choice: the number that should most influence whether someone trusts a merge is whether the things it might break have tests.
|
|
111
|
+
|
|
112
|
+
6. Guard: Structured JSON Contract over Text Parsing
|
|
113
|
+
|
|
114
|
+
Decision: Enforce schema-validated JSON responses from the LLM. Never parse free text looking for status keywords.
|
|
115
|
+
|
|
116
|
+
Why: This is a direct response to a known failure mode. Other tools in this space parse LLM responses as raw text, looking for a specific line (like "STATUS: PASSED") at a fixed position. This breaks when providers prepend acknowledgment text ("Sure! Here's my analysis:") before the actual response.
|
|
117
|
+
|
|
118
|
+
Skannr Guard uses:
|
|
119
|
+
Gemini API's responseMimeType: 'application/json' (forces JSON output)
|
|
120
|
+
OpenAI's response_format: { type: 'json_object' } (same)
|
|
121
|
+
Zod schema validation on every response before using it
|
|
122
|
+
A JSON extraction fallback that finds the first { to last } regardless of surrounding text
|
|
123
|
+
One retry with an explicit correction prompt on validation failure
|
|
124
|
+
|
|
125
|
+
The violation object has a fixed schema. The fixable field is set by the rule definition, not by the LLM. This prevents the LLM from deciding on its own what it can auto-fix.
|
|
126
|
+
|
|
127
|
+
7. Guard: CLI Auto-detection over Mandatory API Keys
|
|
128
|
+
|
|
129
|
+
Decision: If the user has Claude Code, Gemini CLI, Kiro, or Ollama installed, use it directly. Do not require a separate API key.
|
|
130
|
+
|
|
131
|
+
Detection priority:
|
|
132
|
+
1. claude CLI (uses existing Claude Code authenticated session)
|
|
133
|
+
2. gemini CLI (uses existing Gemini authenticated session)
|
|
134
|
+
3. kiro-cli (uses existing Kiro session)
|
|
135
|
+
4. ollama (local, no auth needed)
|
|
136
|
+
5. Gemini API (needs GEMINI_API_KEY)
|
|
137
|
+
6. OpenAI API (needs OPENAI_API_KEY)
|
|
138
|
+
|
|
139
|
+
Why: The biggest friction in AI-powered dev tools is API key setup. If someone already has a coding agent installed and authenticated, they should not need to configure anything else. The tool should use what is already there.
|
|
140
|
+
|
|
141
|
+
This mirrors how GGA (gentleman-guardian-angel) works: it shells out to existing CLI tools rather than making its own API calls. The difference is that Skannr Guard also supports direct API calls as a fallback for users who do not have any CLI tool installed.
|
|
142
|
+
|
|
143
|
+
8. Subcommands over Flags
|
|
144
|
+
|
|
145
|
+
Decision: Structure the CLI as subcommands (skannr risk, skannr guard, skannr agent) rather than a single command with 20+ flags.
|
|
146
|
+
|
|
147
|
+
Why: The original CLI had --question, --root, --report, --cache-clear, --cache-stats, --diff, --watch, --mcp, --telemetry-on, --telemetry-off all on one command. Users had to read --help every time to find which flags go together.
|
|
148
|
+
|
|
149
|
+
Subcommands group related functionality. Each subcommand has only the flags relevant to it. The most common operation (asking a question) takes a bare positional argument:
|
|
150
|
+
|
|
151
|
+
skannr "how does auth work?"
|
|
152
|
+
|
|
153
|
+
No flag needed. The tool should be typeable from memory after seeing it once.
|
|
154
|
+
|
|
155
|
+
Backward compatibility: all old flags still work. The new commands are aliases, not replacements.
|
|
156
|
+
|
|
157
|
+
9. Built-in Default Rules over Mandatory Configuration
|
|
158
|
+
|
|
159
|
+
Decision: Guard works without any rules file. Built-in defaults cover universal best practices.
|
|
160
|
+
|
|
161
|
+
Default rules: no-any-type, no-console-log, error-handling, no-hardcoded-secrets, function-complexity, unused-imports.
|
|
162
|
+
|
|
163
|
+
Why: Every extra file a tool requires before it works is friction that prevents adoption. If the user has to create .skannr/rules.json before they can try the tool, most will not try it.
|
|
164
|
+
|
|
165
|
+
The custom rules file is optional. When present, it overrides defaults. Teams that need domain-specific rules can add them. Everyone else gets reasonable defaults out of the box.
|
|
166
|
+
|
|
167
|
+
10. MCP as the Integration Layer
|
|
168
|
+
|
|
169
|
+
Decision: Expose all features as MCP tools (scan_codebase, blast_radius, guard_review) over stdio.
|
|
170
|
+
|
|
171
|
+
Why: MCP (Model Context Protocol) is the emerging standard for giving AI assistants access to external tools. One protocol works with Cursor, Claude Code, Gemini CLI, VS Code Copilot, and any future MCP-compatible tool.
|
|
172
|
+
|
|
173
|
+
The alternative would be building separate plugins for each editor. MCP avoids that entirely. One implementation, every tool.
|
|
174
|
+
|
|
175
|
+
The MCP server and CLI share the same internal functions. There is no code duplication between the two surfaces.
|
|
176
|
+
|
|
177
|
+
Technology Choices
|
|
178
|
+
|
|
179
|
+
TypeScript: The project itself is written in TypeScript. This aligns with the primary audience (JavaScript/TypeScript developers) and allows dogfooding the TypeScript adapter on the project itself.
|
|
180
|
+
|
|
181
|
+
Commander.js: CLI framework. Mature, well-typed, supports subcommands and positional arguments. Already a standard choice in the Node.js ecosystem.
|
|
182
|
+
|
|
183
|
+
ts-morph: TypeScript/JavaScript AST access. Wraps the TypeScript compiler API with a cleaner interface. Gives perfect type information, symbol extraction, and skeleton generation for TS/JS files.
|
|
184
|
+
|
|
185
|
+
web-tree-sitter: Multi-language parsing via WASM. No native compilation. Supports 40+ languages via grammar files. We bundle Python, Go, Rust, Java (2MB total).
|
|
186
|
+
|
|
187
|
+
parse-diff: Unified diff parsing. Small focused library, well-maintained, has types. Used by blast-radius to extract changed files and line ranges from git diffs.
|
|
188
|
+
|
|
189
|
+
zod: Schema validation for Guard's rules file and LLM response contract. Runtime type checking that integrates with TypeScript's type system.
|
|
190
|
+
|
|
191
|
+
@modelcontextprotocol/sdk: Official MCP SDK. Handles the stdio transport protocol so we only implement tool logic.
|
|
192
|
+
|
|
193
|
+
@google/generative-ai: Gemini SDK for Guard's direct API calls and the interactive agent mode.
|
|
194
|
+
|
|
195
|
+
What is Not Included (and Why)
|
|
196
|
+
|
|
197
|
+
No vector embeddings: Skannr does not use embeddings or a vector store. The hybrid ranking (lexical + structural + graph centrality) is sufficient for finding relevant files. Embeddings would add a model dependency, increase latency, and require a build step to generate the index.
|
|
198
|
+
|
|
199
|
+
No persistent index: Each run scans fresh. The cache is keyed by file hash (MD5) and invalidates automatically when files change. A persistent index would be faster for repeated queries on unchanged codebases, but adds complexity around invalidation and stale results.
|
|
200
|
+
|
|
201
|
+
No UI: Skannr is a CLI tool and MCP server. There is no web dashboard or VS Code extension. The landing page is marketing only. The tool's value is in the terminal and in the AI assistant context window.
|
|
202
|
+
|
|
203
|
+
No custom language grammars: Adding a new language means adding one config entry (which AST node types represent functions, classes, imports) and one WASM grammar file. We do not write custom parsers per language.
|
|
204
|
+
|
|
205
|
+
Testing Approach
|
|
206
|
+
|
|
207
|
+
Jest with ts-jest. Tests are in a flat tests/ directory, named *.test.ts.
|
|
208
|
+
|
|
209
|
+
Test patterns:
|
|
210
|
+
Temp directories with real files (not mocks of the filesystem)
|
|
211
|
+
beforeEach creates fixtures, afterEach cleans up
|
|
212
|
+
WASM dependencies mocked in tests to avoid loading .wasm files in CI
|
|
213
|
+
Integration tests run git commands in real temp repos
|
|
214
|
+
|
|
215
|
+
Coverage areas:
|
|
216
|
+
Schema validation (valid input accepted, invalid input rejected)
|
|
217
|
+
Graph traversal (correct hop distances, cycle handling, max-hop limits)
|
|
218
|
+
Risk scoring (known input/output pairs)
|
|
219
|
+
Fix scoping (never touches non-fixable violations)
|
|
220
|
+
Hook installation (append-safe, idempotent, clean uninstall)
|
|
221
|
+
|
|
222
|
+
Performance Characteristics
|
|
223
|
+
|
|
224
|
+
Typical query: 1-2 seconds end to end on a 2000-file TypeScript repo
|
|
225
|
+
Risk analysis: under 2 seconds (no API call)
|
|
226
|
+
Guard review: 5-15 seconds (LLM round-trip)
|
|
227
|
+
Package size: 450KB compressed, 2.8MB unpacked
|
|
228
|
+
Memory: under 100MB for typical repos
|
|
229
|
+
No background processes: runs, outputs, exits
|
package/README.md
CHANGED
|
@@ -1,260 +1,209 @@
|
|
|
1
|
-
# Skannr
|
|
2
|
-
|
|
3
|
-
A CLI tool and MCP server that helps AI
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
skannr
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
skannr
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
skannr
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
{
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
Changed symbols:
|
|
212
|
-
function validateToken (src/auth/session.ts)
|
|
213
|
-
|
|
214
|
-
Hop 1 (2 files):
|
|
215
|
-
src/middleware/auth-guard.ts centrality=0.85 [NO TEST]
|
|
216
|
-
src/api/login.ts centrality=0.60
|
|
217
|
-
|
|
218
|
-
Hop 2 (2 files):
|
|
219
|
-
src/routes/index.ts centrality=0.45 [NO TEST]
|
|
220
|
-
src/api/admin.ts centrality=0.30 [NO TEST]
|
|
221
|
-
|
|
222
|
-
Formula inputs:
|
|
223
|
-
affected_count (normalized): 0.080
|
|
224
|
-
avg_centrality: 0.550
|
|
225
|
-
untested_ratio: 0.750
|
|
226
|
-
hop_spread (normalized): 1.000
|
|
227
|
-
```
|
|
228
|
-
|
|
229
|
-
### MCP
|
|
230
|
-
|
|
231
|
-
The `blast_radius` tool is exposed via the same MCP server (`skannr --mcp`).
|
|
232
|
-
|
|
233
|
-
Input schema:
|
|
234
|
-
|
|
235
|
-
```json
|
|
236
|
-
{
|
|
237
|
-
"root": "/path/to/repo",
|
|
238
|
-
"diff": "<optional unified diff content>",
|
|
239
|
-
"hops": 2
|
|
240
|
-
}
|
|
241
|
-
```
|
|
242
|
-
|
|
243
|
-
If `diff` is omitted, the tool runs `git diff HEAD` in the given root.
|
|
244
|
-
The output is the same JSON structure as `skannr blast-radius --json`.
|
|
245
|
-
|
|
246
|
-
### Risk Score Formula
|
|
247
|
-
|
|
248
|
-
```
|
|
249
|
-
risk = 2.5 × normalizedAffectedCount
|
|
250
|
-
+ 2.5 × avgCentrality
|
|
251
|
-
+ 3.5 × untestedRatio
|
|
252
|
-
+ 1.5 × normalizedMaxHopSpread
|
|
253
|
-
```
|
|
254
|
-
|
|
255
|
-
| Input | Range | Meaning |
|
|
256
|
-
|-------|-------|---------|
|
|
257
|
-
| normalizedAffectedCount | 0–1 | Affected files / total project files |
|
|
258
|
-
| avgCentrality | 0–1 | Mean in-degree centrality of affected files |
|
|
259
|
-
| untestedRatio | 0–1 | Fraction of affected files with no test file |
|
|
260
|
-
| normalizedMaxHopSpread | 0–1 | Deepest hop reached / max configured hops |
|
|
1
|
+
# Skannr
|
|
2
|
+
|
|
3
|
+
A CLI tool and MCP server that helps developers and AI assistants understand any codebase. Scans files, generates compressed structural skeletons, and ranks results using hybrid retrieval. Reduces token usage by 96.5% vs full file reads.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g skannr
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Or use without installing:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npx skannr "how does auth work?"
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
### Ask about any codebase
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
skannr "how does authentication work?"
|
|
23
|
+
skannr "database queries" -n 5
|
|
24
|
+
skannr "API endpoints" --json
|
|
25
|
+
skannr "class structure" --lang python
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
No config needed. Auto-detects language and module structure.
|
|
29
|
+
|
|
30
|
+
### Risk analysis
|
|
31
|
+
|
|
32
|
+
Check the downstream impact of your changes before pushing:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
skannr risk
|
|
36
|
+
skannr risk --json
|
|
37
|
+
skannr risk -n 3
|
|
38
|
+
skannr risk --diff feature.patch
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Walks the dependency graph, finds affected files, flags untested code, gives you a 0-10 score. No LLM, no API key, runs in under 2 seconds.
|
|
42
|
+
|
|
43
|
+
### Guard (AI code review)
|
|
44
|
+
|
|
45
|
+
Reviews staged changes against coding rules at the symbol level:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
git add .
|
|
49
|
+
skannr guard
|
|
50
|
+
skannr guard --fix
|
|
51
|
+
skannr guard --fix --dry-run
|
|
52
|
+
skannr guard --pr-mode
|
|
53
|
+
skannr guard --json
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Auto-detects your coding agent (Claude, Gemini, Kiro, Ollama). No API key needed if you already have one installed. Works out of the box with built-in rules.
|
|
57
|
+
|
|
58
|
+
Install as a pre-commit hook:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
skannr guard install
|
|
62
|
+
skannr guard uninstall
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Custom rules (optional):
|
|
66
|
+
|
|
67
|
+
```json
|
|
68
|
+
// .skannr/rules.json
|
|
69
|
+
{
|
|
70
|
+
"rules": [
|
|
71
|
+
{
|
|
72
|
+
"id": "no-mutations",
|
|
73
|
+
"description": "Do not mutate function arguments",
|
|
74
|
+
"severity": "high",
|
|
75
|
+
"fixable": false,
|
|
76
|
+
"category": "functional"
|
|
77
|
+
}
|
|
78
|
+
]
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Other commands
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
skannr report # repo health summary (JSON)
|
|
86
|
+
skannr agent # interactive exploration mode
|
|
87
|
+
skannr cache stats # cache hit rate
|
|
88
|
+
skannr cache clear # wipe cache
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Supported Languages
|
|
92
|
+
|
|
93
|
+
| Language | Support | Method |
|
|
94
|
+
|---|---|---|
|
|
95
|
+
| TypeScript / TSX | Full AST | ts-morph |
|
|
96
|
+
| JavaScript / JSX | Full AST | ts-morph |
|
|
97
|
+
| Python | Full AST | tree-sitter WASM |
|
|
98
|
+
| Go | Full AST | tree-sitter WASM |
|
|
99
|
+
| Rust | Full AST | tree-sitter WASM |
|
|
100
|
+
| Java | Full AST | tree-sitter WASM |
|
|
101
|
+
| Others | Basic | First 50 lines fallback |
|
|
102
|
+
|
|
103
|
+
No native build tools required. WASM grammars run everywhere npm runs.
|
|
104
|
+
|
|
105
|
+
## MCP Server
|
|
106
|
+
|
|
107
|
+
Exposes three tools: `scan_codebase`, `blast_radius`, `guard_review`.
|
|
108
|
+
|
|
109
|
+
One-time setup for your AI tool:
|
|
110
|
+
|
|
111
|
+
```json
|
|
112
|
+
{
|
|
113
|
+
"mcpServers": {
|
|
114
|
+
"skannr": {
|
|
115
|
+
"command": "npx",
|
|
116
|
+
"args": ["-y", "skannr", "--mcp"]
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Works with Cursor, Claude Code, Gemini CLI, Kiro, VS Code Copilot, and any MCP-compatible tool.
|
|
123
|
+
|
|
124
|
+
After setup, just ask your assistant:
|
|
125
|
+
- "How does auth work in this repo?"
|
|
126
|
+
- "Review my staged changes"
|
|
127
|
+
- "What's the risk of my current diff?"
|
|
128
|
+
|
|
129
|
+
## Risk Score Formula
|
|
130
|
+
|
|
131
|
+
```
|
|
132
|
+
risk = 2.5 x (affected files / total files)
|
|
133
|
+
+ 2.5 x (avg centrality of affected files)
|
|
134
|
+
+ 3.5 x (% of affected files with no tests)
|
|
135
|
+
+ 1.5 x (max hop reached / max hops configured)
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Deterministic. Same diff always produces the same score. CI-gateable:
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
RISK=$(skannr risk --json | jq '.riskScore')
|
|
142
|
+
if [ $(echo "$RISK > 7" | bc -l) -eq 1 ]; then exit 1; fi
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Guard Providers
|
|
146
|
+
|
|
147
|
+
Guard auto-detects in this order (no config needed):
|
|
148
|
+
|
|
149
|
+
1. `claude` CLI (uses your existing session)
|
|
150
|
+
2. `gemini` CLI (uses your existing session)
|
|
151
|
+
3. `kiro-cli` (uses your existing session)
|
|
152
|
+
4. `ollama` (local, no auth)
|
|
153
|
+
5. Gemini API (set `GEMINI_API_KEY`)
|
|
154
|
+
6. OpenAI API (set `OPENAI_API_KEY`)
|
|
155
|
+
|
|
156
|
+
Override with `SKANNR_GUARD_PROVIDER` env var or `.skannr/guard.json`.
|
|
157
|
+
|
|
158
|
+
## Config File (optional)
|
|
159
|
+
|
|
160
|
+
```json
|
|
161
|
+
// code-analyzer.config.json (project root)
|
|
162
|
+
{
|
|
163
|
+
"modules": {
|
|
164
|
+
"auth": ["src/auth", "lib/auth"],
|
|
165
|
+
"api": ["src/api", "src/routes"]
|
|
166
|
+
},
|
|
167
|
+
"exclude": ["**/generated/**", "**/migrations/**"],
|
|
168
|
+
"extensions": [".ts", ".js"],
|
|
169
|
+
"defaultLimit": 10
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## Performance
|
|
174
|
+
|
|
175
|
+
| Metric | Result |
|
|
176
|
+
|---|---|
|
|
177
|
+
| Token reduction vs full scan | 96.5% |
|
|
178
|
+
| Typical query time | 1-2 seconds |
|
|
179
|
+
| Risk analysis | under 2 seconds (no API call) |
|
|
180
|
+
| Guard review | 5-15 seconds (LLM round-trip) |
|
|
181
|
+
| Package size | 450KB compressed |
|
|
182
|
+
|
|
183
|
+
## Interactive Agent
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
skannr agent
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Commands inside the agent:
|
|
190
|
+
|
|
191
|
+
```
|
|
192
|
+
/files List retrieved files
|
|
193
|
+
/symbols <query> Search for symbols
|
|
194
|
+
/symbol <id> Get full implementation
|
|
195
|
+
/deps <file> Show imports for a file
|
|
196
|
+
/refresh Re-analyze with new context
|
|
197
|
+
/stats Show mapping statistics
|
|
198
|
+
/exit Quit
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
Requires `GEMINI_API_KEY` for the interactive agent mode.
|
|
202
|
+
|
|
203
|
+
## Architecture
|
|
204
|
+
|
|
205
|
+
See [ARCHITECTURE.md](./ARCHITECTURE.md) for design decisions, technology choices, and the reasoning behind each one.
|
|
206
|
+
|
|
207
|
+
## License
|
|
208
|
+
|
|
209
|
+
MIT
|