skannr 0.1.6 → 0.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/dist/languages/TreeSitterAdapter.d.ts +30 -0
- package/dist/languages/TreeSitterAdapter.d.ts.map +1 -0
- package/dist/languages/TreeSitterAdapter.js +466 -0
- package/dist/languages/TreeSitterAdapter.js.map +1 -0
- package/dist/languages/grammars/tree-sitter-go.wasm +0 -0
- package/dist/languages/grammars/tree-sitter-java.wasm +0 -0
- package/dist/languages/grammars/tree-sitter-python.wasm +0 -0
- package/dist/languages/grammars/tree-sitter-rust.wasm +0 -0
- package/dist/languages/lang-config.d.ts +38 -0
- package/dist/languages/lang-config.d.ts.map +1 -0
- package/dist/languages/lang-config.js +85 -0
- package/dist/languages/lang-config.js.map +1 -0
- package/dist/languages/registry.d.ts +6 -0
- package/dist/languages/registry.d.ts.map +1 -1
- package/dist/languages/registry.js +18 -4
- package/dist/languages/registry.js.map +1 -1
- package/package.json +4 -7
- package/src/languages/TreeSitterAdapter.ts +431 -0
- package/src/languages/lang-config.ts +113 -0
- package/src/languages/registry.ts +65 -49
- package/src/languages/PythonAdapter.ts +0 -302
- package/tests/agent.tools.test.ts +0 -81
- package/tests/benchmark.test.ts +0 -31
- package/tests/blast-radius.test.ts +0 -290
- package/tests/fixtures/sample.py +0 -17
- package/tests/fixtures/sample.ts +0 -13
- package/tests/fixtures/src/api/routes.ts +0 -1
- package/tests/fixtures/src/auth/permission.ts +0 -3
- package/tests/python-adapter.test.ts +0 -31
- package/tests/ranker-enhanced.test.ts +0 -70
- package/tests/ranker.test.ts +0 -79
- package/tests/scanner.scope.test.ts +0 -67
- package/tests/setup-fixtures.js +0 -29
- package/tests/skeletonizer.test.ts +0 -149
- package/uca-landing/index.html +0 -17
- package/uca-landing/package.json +0 -23
- package/uca-landing/postcss.config.js +0 -6
- package/uca-landing/src/App.jsx +0 -45
- package/uca-landing/src/components/AgentMode.jsx +0 -45
- package/uca-landing/src/components/CliReference.jsx +0 -89
- package/uca-landing/src/components/Features.jsx +0 -88
- package/uca-landing/src/components/Footer.jsx +0 -35
- package/uca-landing/src/components/Hero.jsx +0 -125
- package/uca-landing/src/components/HowItWorks.jsx +0 -65
- package/uca-landing/src/components/Install.jsx +0 -103
- package/uca-landing/src/components/LanguageSupport.jsx +0 -63
- package/uca-landing/src/components/Navbar.jsx +0 -86
- package/uca-landing/src/components/Problem.jsx +0 -51
- package/uca-landing/src/components/Reveal.jsx +0 -40
- package/uca-landing/src/components/TokenSavings.jsx +0 -126
- package/uca-landing/src/components/WorksWith.jsx +0 -59
- package/uca-landing/src/hooks/useScrollNav.js +0 -13
- package/uca-landing/src/hooks/useTypewriter.js +0 -41
- package/uca-landing/src/index.css +0 -13
- package/uca-landing/src/main.jsx +0 -10
- package/uca-landing/tailwind.config.js +0 -68
- package/uca-landing/vercel.json +0 -4
- package/uca-landing/vite.config.js +0 -6
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for skeleton generation
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import * as fs from 'fs';
|
|
6
|
-
import * as os from 'os';
|
|
7
|
-
import * as path from 'path';
|
|
8
|
-
import { buildSkeletonForFile } from '../src/skeletonizer';
|
|
9
|
-
|
|
10
|
-
describe('buildSkeletonForFile', () => {
|
|
11
|
-
let tempDir: string;
|
|
12
|
-
let testFilePath: string;
|
|
13
|
-
|
|
14
|
-
beforeEach(() => {
|
|
15
|
-
// Create a temporary directory for test files
|
|
16
|
-
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'skel-test-'));
|
|
17
|
-
testFilePath = path.join(tempDir, 'test.ts');
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
afterEach(() => {
|
|
21
|
-
// Clean up
|
|
22
|
-
if (fs.existsSync(tempDir)) {
|
|
23
|
-
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
24
|
-
}
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it('should generate skeleton for a simple class', () => {
|
|
28
|
-
const sourceCode = `
|
|
29
|
-
export class Calculator {
|
|
30
|
-
/**
|
|
31
|
-
* Adds two numbers
|
|
32
|
-
*/
|
|
33
|
-
add(a: number, b: number): number {
|
|
34
|
-
return a + b;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
multiply(x: number, y: number): number {
|
|
38
|
-
const result = x * y;
|
|
39
|
-
return result;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
`;
|
|
43
|
-
|
|
44
|
-
fs.writeFileSync(testFilePath, sourceCode);
|
|
45
|
-
const { skeleton, lineRanges } = buildSkeletonForFile(testFilePath, tempDir);
|
|
46
|
-
|
|
47
|
-
// Should contain class declaration
|
|
48
|
-
expect(skeleton).toContain('export class Calculator');
|
|
49
|
-
|
|
50
|
-
// Should contain method signatures
|
|
51
|
-
expect(skeleton).toContain('add(a: number, b: number): number');
|
|
52
|
-
expect(skeleton).toContain('multiply(x: number, y: number): number');
|
|
53
|
-
|
|
54
|
-
// Should not contain implementation details
|
|
55
|
-
expect(skeleton).toContain('/* trimmed */');
|
|
56
|
-
expect(skeleton).not.toContain('return a + b');
|
|
57
|
-
expect(skeleton).not.toContain('const result = x * y');
|
|
58
|
-
|
|
59
|
-
expect(skeleton).toMatch(/\/\/ test\.ts:\d+/);
|
|
60
|
-
expect(lineRanges?.some((r) => r.symbol === 'class Calculator')).toBe(true);
|
|
61
|
-
expect(lineRanges?.some((r) => r.symbol === 'Calculator.add')).toBe(true);
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
it('should keep interface definitions intact', () => {
|
|
65
|
-
const sourceCode = `
|
|
66
|
-
export interface User {
|
|
67
|
-
id: string;
|
|
68
|
-
name: string;
|
|
69
|
-
email: string;
|
|
70
|
-
}
|
|
71
|
-
`;
|
|
72
|
-
|
|
73
|
-
fs.writeFileSync(testFilePath, sourceCode);
|
|
74
|
-
const { skeleton, lineRanges } = buildSkeletonForFile(testFilePath, tempDir);
|
|
75
|
-
|
|
76
|
-
expect(skeleton).toContain('export interface User');
|
|
77
|
-
expect(skeleton).toContain('id: string');
|
|
78
|
-
expect(skeleton).toContain('name: string');
|
|
79
|
-
expect(skeleton).toContain('email: string');
|
|
80
|
-
expect(skeleton).toMatch(/\/\/ test\.ts:\d+/);
|
|
81
|
-
expect(lineRanges?.some((r) => r.symbol === 'interface User')).toBe(true);
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
it('should keep import statements', () => {
|
|
85
|
-
const sourceCode = `
|
|
86
|
-
import { Something } from './somewhere';
|
|
87
|
-
import * as fs from 'fs';
|
|
88
|
-
|
|
89
|
-
export function doWork(): void {
|
|
90
|
-
console.log('working');
|
|
91
|
-
}
|
|
92
|
-
`;
|
|
93
|
-
|
|
94
|
-
fs.writeFileSync(testFilePath, sourceCode);
|
|
95
|
-
const { skeleton } = buildSkeletonForFile(testFilePath, tempDir);
|
|
96
|
-
|
|
97
|
-
expect(skeleton).toContain("import { Something } from './somewhere'");
|
|
98
|
-
expect(skeleton).toContain("import * as fs from 'fs'");
|
|
99
|
-
expect(skeleton).toContain('export function doWork(): void');
|
|
100
|
-
expect(skeleton).not.toContain("console.log('working')");
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
it('should handle type aliases', () => {
|
|
104
|
-
const sourceCode = `
|
|
105
|
-
export type Status = 'active' | 'inactive' | 'pending';
|
|
106
|
-
export type UserId = string;
|
|
107
|
-
`;
|
|
108
|
-
|
|
109
|
-
fs.writeFileSync(testFilePath, sourceCode);
|
|
110
|
-
const { skeleton, lineRanges } = buildSkeletonForFile(testFilePath, tempDir);
|
|
111
|
-
|
|
112
|
-
expect(skeleton).toContain("export type Status = 'active' | 'inactive' | 'pending'");
|
|
113
|
-
expect(skeleton).toContain('export type UserId = string');
|
|
114
|
-
expect(lineRanges?.some((r) => r.symbol === 'type Status')).toBe(true);
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
it('should handle functions with JSDoc', () => {
|
|
118
|
-
const sourceCode = `
|
|
119
|
-
/**
|
|
120
|
-
* Calculates the sum of an array
|
|
121
|
-
* @param numbers Array of numbers
|
|
122
|
-
* @returns The sum
|
|
123
|
-
*/
|
|
124
|
-
export function sum(numbers: number[]): number {
|
|
125
|
-
return numbers.reduce((a, b) => a + b, 0);
|
|
126
|
-
}
|
|
127
|
-
`;
|
|
128
|
-
|
|
129
|
-
fs.writeFileSync(testFilePath, sourceCode);
|
|
130
|
-
const { skeleton } = buildSkeletonForFile(testFilePath, tempDir);
|
|
131
|
-
|
|
132
|
-
expect(skeleton).toContain('Calculates the sum');
|
|
133
|
-
expect(skeleton).toContain('export function sum(numbers: number[]): number');
|
|
134
|
-
expect(skeleton).toContain('/* trimmed */');
|
|
135
|
-
expect(skeleton).not.toContain('reduce');
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
it('should handle malformed TypeScript gracefully', () => {
|
|
139
|
-
const sourceCode = 'export const x = {{{';
|
|
140
|
-
|
|
141
|
-
fs.writeFileSync(testFilePath, sourceCode);
|
|
142
|
-
const { skeleton } = buildSkeletonForFile(testFilePath, tempDir);
|
|
143
|
-
|
|
144
|
-
// ts-morph is robust and may still parse partial content
|
|
145
|
-
// Just verify it returns some output without crashing
|
|
146
|
-
expect(skeleton).toBeDefined();
|
|
147
|
-
expect(skeleton.length).toBeGreaterThan(0);
|
|
148
|
-
});
|
|
149
|
-
});
|
package/uca-landing/index.html
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
<!doctype html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8" />
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
-
<title>Universal Code Analyzer</title>
|
|
7
|
-
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
8
|
-
<link
|
|
9
|
-
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap"
|
|
10
|
-
rel="stylesheet"
|
|
11
|
-
/>
|
|
12
|
-
</head>
|
|
13
|
-
<body>
|
|
14
|
-
<div id="root"></div>
|
|
15
|
-
<script type="module" src="/src/main.jsx"></script>
|
|
16
|
-
</body>
|
|
17
|
-
</html>
|
package/uca-landing/package.json
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "uca-landing",
|
|
3
|
-
"private": true,
|
|
4
|
-
"version": "0.0.0",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"scripts": {
|
|
7
|
-
"dev": "vite",
|
|
8
|
-
"build": "vite build",
|
|
9
|
-
"preview": "vite preview"
|
|
10
|
-
},
|
|
11
|
-
"dependencies": {
|
|
12
|
-
"lucide-react": "^0.525.0",
|
|
13
|
-
"react": "^18.3.1",
|
|
14
|
-
"react-dom": "^18.3.1"
|
|
15
|
-
},
|
|
16
|
-
"devDependencies": {
|
|
17
|
-
"@vitejs/plugin-react": "^4.3.1",
|
|
18
|
-
"autoprefixer": "^10.4.21",
|
|
19
|
-
"postcss": "^8.5.6",
|
|
20
|
-
"tailwindcss": "^3.4.17",
|
|
21
|
-
"vite": "^5.4.10"
|
|
22
|
-
}
|
|
23
|
-
}
|
package/uca-landing/src/App.jsx
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import Navbar from './components/Navbar'
|
|
2
|
-
import Hero from './components/Hero'
|
|
3
|
-
import Problem from './components/Problem'
|
|
4
|
-
import TokenSavings from './components/TokenSavings'
|
|
5
|
-
import HowItWorks from './components/HowItWorks'
|
|
6
|
-
import Features from './components/Features'
|
|
7
|
-
import LanguageSupport from './components/LanguageSupport'
|
|
8
|
-
import Install from './components/Install'
|
|
9
|
-
import CliReference from './components/CliReference'
|
|
10
|
-
import WorksWith from './components/WorksWith'
|
|
11
|
-
import AgentMode from './components/AgentMode'
|
|
12
|
-
import Footer from './components/Footer'
|
|
13
|
-
|
|
14
|
-
export default function App() {
|
|
15
|
-
return (
|
|
16
|
-
<div className="relative min-h-screen overflow-x-hidden bg-bg text-white font-sans">
|
|
17
|
-
<div
|
|
18
|
-
className="pointer-events-none fixed inset-0 -z-10"
|
|
19
|
-
aria-hidden
|
|
20
|
-
>
|
|
21
|
-
<div
|
|
22
|
-
className="absolute -left-[20%] top-[10%] h-[520px] w-[520px] rounded-full bg-accent/[0.07] blur-[120px] motion-safe:animate-drift"
|
|
23
|
-
/>
|
|
24
|
-
<div
|
|
25
|
-
className="absolute -right-[15%] top-[40%] h-[420px] w-[420px] rounded-full bg-purple/[0.08] blur-[100px] motion-safe:animate-drift motion-safe:[animation-delay:-7s] motion-safe:[animation-duration:28s]"
|
|
26
|
-
/>
|
|
27
|
-
<div
|
|
28
|
-
className="absolute bottom-0 left-1/3 h-[380px] w-[380px] rounded-full bg-green/[0.05] blur-[90px] motion-safe:animate-drift motion-safe:[animation-delay:-12s] motion-safe:[animation-duration:26s]"
|
|
29
|
-
/>
|
|
30
|
-
</div>
|
|
31
|
-
<Navbar />
|
|
32
|
-
<Hero />
|
|
33
|
-
<Problem />
|
|
34
|
-
<TokenSavings />
|
|
35
|
-
<HowItWorks />
|
|
36
|
-
<Features />
|
|
37
|
-
<LanguageSupport />
|
|
38
|
-
<Install />
|
|
39
|
-
<CliReference />
|
|
40
|
-
<WorksWith />
|
|
41
|
-
<AgentMode />
|
|
42
|
-
<Footer />
|
|
43
|
-
</div>
|
|
44
|
-
)
|
|
45
|
-
}
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import Reveal from './Reveal'
|
|
2
|
-
|
|
3
|
-
export default function AgentMode() {
|
|
4
|
-
return (
|
|
5
|
-
<section className="py-24 px-5">
|
|
6
|
-
<div className="max-w-5xl mx-auto">
|
|
7
|
-
<Reveal>
|
|
8
|
-
<h2 className="text-3xl md:text-5xl font-bold text-center tracking-tight">
|
|
9
|
-
Agent mode. For when you need to explore.
|
|
10
|
-
</h2>
|
|
11
|
-
</Reveal>
|
|
12
|
-
|
|
13
|
-
<Reveal delay={100} className="mt-12 block">
|
|
14
|
-
<div className="overflow-hidden rounded-xl border border-border bg-card transition-all duration-300 hover:border-white/10 hover:shadow-[0_0_40px_-12px_rgba(0,212,255,0.12)]">
|
|
15
|
-
<div className="flex items-center gap-2 border-b border-white/5 px-4 py-3">
|
|
16
|
-
<span className="h-3 w-3 rounded-full bg-[#ff5f57]" />
|
|
17
|
-
<span className="h-3 w-3 rounded-full bg-[#febc2e]" />
|
|
18
|
-
<span className="h-3 w-3 rounded-full bg-[#28c840]" />
|
|
19
|
-
<span className="ml-2 font-mono text-xs text-[#888]">bash</span>
|
|
20
|
-
</div>
|
|
21
|
-
<pre className="overflow-x-auto p-5 font-mono text-sm leading-7 text-[#e8e8e8]">
|
|
22
|
-
<span>$ skannr-agent --root ./my-project</span>
|
|
23
|
-
{'\n\n'}
|
|
24
|
-
<span className="text-accent">{'>'}</span> <span>/help</span>
|
|
25
|
-
{'\n'} <span className="text-white">/files</span> List currently retrieved files
|
|
26
|
-
{'\n'} <span className="text-white">/symbols <query></span> Search for symbols in the codebase
|
|
27
|
-
{'\n'} <span className="text-white">/symbol <id></span> Get full implementation of a symbol
|
|
28
|
-
{'\n'} <span className="text-white">/deps <filePath></span> Show imports and exports for a file
|
|
29
|
-
{'\n'} <span className="text-white">/refresh</span> Re-analyze project with new context
|
|
30
|
-
{'\n'} <span className="text-white">/stats</span> Show cache and mapping statistics
|
|
31
|
-
{'\n'} <span className="text-white">/exit</span> Quit
|
|
32
|
-
{'\n\n'}
|
|
33
|
-
<span className="text-accent">{'>'}</span> <span>/symbols auth</span>
|
|
34
|
-
{'\n'} Found 4 symbols:
|
|
35
|
-
{'\n'} <span className="text-accent">→</span> AuthManager (class) <span className="text-[#888]">src/auth/AuthManager.ts:12</span>
|
|
36
|
-
{'\n'} <span className="text-accent">→</span> validateToken (fn) <span className="text-[#888]">src/auth/AuthManager.ts:24</span>
|
|
37
|
-
{'\n'} <span className="text-accent">→</span> AuthMiddleware (class) <span className="text-[#888]">src/middleware/auth.ts:8</span>
|
|
38
|
-
{'\n'} <span className="text-accent">→</span> authRouter (const) <span className="text-[#888]">src/routes/auth.ts:3</span>
|
|
39
|
-
</pre>
|
|
40
|
-
</div>
|
|
41
|
-
</Reveal>
|
|
42
|
-
</div>
|
|
43
|
-
</section>
|
|
44
|
-
)
|
|
45
|
-
}
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
import Reveal from './Reveal'
|
|
2
|
-
|
|
3
|
-
const commands = [
|
|
4
|
-
['skannr "<question>"', 'Ask about the codebase (positional argument)'],
|
|
5
|
-
['skannr risk', 'Check downstream impact and risk of uncommitted changes'],
|
|
6
|
-
['skannr risk --diff <path>', 'Analyze a specific diff/patch file'],
|
|
7
|
-
['skannr risk -n <hops>', 'Set max traversal hops (default: 2)'],
|
|
8
|
-
['skannr risk --json', 'JSON output for CI pipelines'],
|
|
9
|
-
['skannr report', 'Print repository health summary as JSON'],
|
|
10
|
-
['skannr agent', 'Interactive exploration mode'],
|
|
11
|
-
['skannr cache stats', 'Show cache hit/miss statistics'],
|
|
12
|
-
['skannr cache clear', 'Clear all cached analysis results'],
|
|
13
|
-
]
|
|
14
|
-
|
|
15
|
-
const flags = [
|
|
16
|
-
['--root <path>', 'Project root (default: current directory)'],
|
|
17
|
-
['-n, --limit <number>', 'Number of top files to return (default: 10)'],
|
|
18
|
-
['--json', 'Shortcut for --format json'],
|
|
19
|
-
['--format <fmt>', 'human | markdown | json (default: human)'],
|
|
20
|
-
['--lang <mode>', 'typescript | javascript | python | auto (default: auto)'],
|
|
21
|
-
['--modules <keys>', 'Comma-separated module keys (auto-discovered when omitted)'],
|
|
22
|
-
['--watch', 'Watch the tree and re-run analysis when files change'],
|
|
23
|
-
['--skip-cache', 'Skip cache and force a full analysis'],
|
|
24
|
-
['--with-mapping', 'Generate symbol mapping for on-demand retrieval'],
|
|
25
|
-
['--mcp', 'Run as Model Context Protocol stdio server'],
|
|
26
|
-
['--telemetry-on / --telemetry-off', 'Toggle anonymous flag-only telemetry'],
|
|
27
|
-
]
|
|
28
|
-
|
|
29
|
-
export default function CliReference() {
|
|
30
|
-
return (
|
|
31
|
-
<section className="py-24 px-5">
|
|
32
|
-
<div className="max-w-5xl mx-auto">
|
|
33
|
-
<Reveal>
|
|
34
|
-
<h2 className="text-3xl md:text-5xl font-bold text-center tracking-tight">Commands & options.</h2>
|
|
35
|
-
</Reveal>
|
|
36
|
-
|
|
37
|
-
<Reveal delay={80}>
|
|
38
|
-
<h3 className="mt-12 mb-4 text-xl font-semibold text-white/80">Commands</h3>
|
|
39
|
-
<div className="overflow-hidden rounded-xl border border-border bg-card transition-colors duration-300 hover:border-white/10">
|
|
40
|
-
<table className="w-full text-left">
|
|
41
|
-
<thead className="border-b border-white/5">
|
|
42
|
-
<tr className="text-[#888] text-sm">
|
|
43
|
-
<th className="px-6 py-4 font-medium">Command</th>
|
|
44
|
-
<th className="px-6 py-4 font-medium">Description</th>
|
|
45
|
-
</tr>
|
|
46
|
-
</thead>
|
|
47
|
-
<tbody>
|
|
48
|
-
{commands.map(([cmd, description]) => (
|
|
49
|
-
<tr
|
|
50
|
-
key={cmd}
|
|
51
|
-
className="border-b border-white/5 transition-colors duration-200 last:border-b-0 hover:bg-white/[0.03]"
|
|
52
|
-
>
|
|
53
|
-
<td className="px-6 py-4 font-mono text-accent">{cmd}</td>
|
|
54
|
-
<td className="px-6 py-4 text-[#d0d0d0]">{description}</td>
|
|
55
|
-
</tr>
|
|
56
|
-
))}
|
|
57
|
-
</tbody>
|
|
58
|
-
</table>
|
|
59
|
-
</div>
|
|
60
|
-
</Reveal>
|
|
61
|
-
|
|
62
|
-
<Reveal delay={160}>
|
|
63
|
-
<h3 className="mt-10 mb-4 text-xl font-semibold text-white/80">Options</h3>
|
|
64
|
-
<div className="overflow-hidden rounded-xl border border-border bg-card transition-colors duration-300 hover:border-white/10">
|
|
65
|
-
<table className="w-full text-left">
|
|
66
|
-
<thead className="border-b border-white/5">
|
|
67
|
-
<tr className="text-[#888] text-sm">
|
|
68
|
-
<th className="px-6 py-4 font-medium">Flag</th>
|
|
69
|
-
<th className="px-6 py-4 font-medium">Description</th>
|
|
70
|
-
</tr>
|
|
71
|
-
</thead>
|
|
72
|
-
<tbody>
|
|
73
|
-
{flags.map(([flag, description]) => (
|
|
74
|
-
<tr
|
|
75
|
-
key={flag}
|
|
76
|
-
className="border-b border-white/5 transition-colors duration-200 last:border-b-0 hover:bg-white/[0.03]"
|
|
77
|
-
>
|
|
78
|
-
<td className="px-6 py-4 font-mono text-accent">{flag}</td>
|
|
79
|
-
<td className="px-6 py-4 text-[#d0d0d0]">{description}</td>
|
|
80
|
-
</tr>
|
|
81
|
-
))}
|
|
82
|
-
</tbody>
|
|
83
|
-
</table>
|
|
84
|
-
</div>
|
|
85
|
-
</Reveal>
|
|
86
|
-
</div>
|
|
87
|
-
</section>
|
|
88
|
-
)
|
|
89
|
-
}
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
AlertTriangle,
|
|
3
|
-
Code2,
|
|
4
|
-
Database,
|
|
5
|
-
FileSearch,
|
|
6
|
-
Network,
|
|
7
|
-
Plug,
|
|
8
|
-
RefreshCw,
|
|
9
|
-
ShieldCheck,
|
|
10
|
-
Zap,
|
|
11
|
-
} from 'lucide-react'
|
|
12
|
-
import Reveal from './Reveal'
|
|
13
|
-
|
|
14
|
-
const features = [
|
|
15
|
-
{
|
|
16
|
-
icon: Network,
|
|
17
|
-
title: 'Hybrid Retrieval',
|
|
18
|
-
body: 'Lexical + structural + dependency-graph ranking. Finds architecturally central files, not just keyword matches.',
|
|
19
|
-
},
|
|
20
|
-
{
|
|
21
|
-
icon: Zap,
|
|
22
|
-
title: '96.5% Token Reduction',
|
|
23
|
-
body: 'Skeleton generation strips bodies while preserving signatures and types. Feed entire codebases to your AI without hitting limits.',
|
|
24
|
-
},
|
|
25
|
-
{
|
|
26
|
-
icon: AlertTriangle,
|
|
27
|
-
title: 'Risk Analysis',
|
|
28
|
-
body: 'Run "skannr risk" before pushing. Traverses the dependency graph, finds downstream affected files, flags untested code, and scores risk 0-10. Deterministic and instant.',
|
|
29
|
-
},
|
|
30
|
-
{
|
|
31
|
-
icon: Code2,
|
|
32
|
-
title: 'Auto Language Detection',
|
|
33
|
-
body: "Detects the repo's dominant language automatically. TypeScript, JavaScript, and Python supported. Generic fallback for everything else.",
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
icon: Database,
|
|
37
|
-
title: 'Smart Caching',
|
|
38
|
-
body: 'MD5-based file hashing detects changes automatically. Repeated queries run in milliseconds. 24-hour TTL with manual controls.',
|
|
39
|
-
},
|
|
40
|
-
{
|
|
41
|
-
icon: FileSearch,
|
|
42
|
-
title: 'Grounded Citations',
|
|
43
|
-
body: 'Every response includes an Evidence section listing the exact files and symbols used. Verify AI answers by tracing to source.',
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
icon: Plug,
|
|
47
|
-
title: 'MCP Server Built In',
|
|
48
|
-
body: 'Runs as a Model Context Protocol server. Plug into Gemini CLI, Claude Code, Cursor, or any MCP-compatible tool.',
|
|
49
|
-
},
|
|
50
|
-
{
|
|
51
|
-
icon: RefreshCw,
|
|
52
|
-
title: 'Watch Mode',
|
|
53
|
-
body: 'Use --watch on large repos you touch daily. Skannr debounces file changes and re-analyzes with a fresh run (cache bypassed per pass) so output stays current.',
|
|
54
|
-
},
|
|
55
|
-
{
|
|
56
|
-
icon: ShieldCheck,
|
|
57
|
-
title: 'Opt-in Telemetry',
|
|
58
|
-
body: 'Anonymous, flag-only usage stats if you enable them. No code, questions, or paths leave your machine. Turn on or off anytime with --telemetry-on / --telemetry-off.',
|
|
59
|
-
},
|
|
60
|
-
]
|
|
61
|
-
|
|
62
|
-
export default function Features() {
|
|
63
|
-
return (
|
|
64
|
-
<section id="features" className="py-24 px-5">
|
|
65
|
-
<div className="max-w-6xl mx-auto">
|
|
66
|
-
<Reveal>
|
|
67
|
-
<h2 className="text-3xl md:text-5xl font-bold text-center tracking-tight">
|
|
68
|
-
Everything you need. Nothing you don't.
|
|
69
|
-
</h2>
|
|
70
|
-
</Reveal>
|
|
71
|
-
<div className="mt-14 grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-5">
|
|
72
|
-
{features.map(({ icon: Icon, title, body }, i) => (
|
|
73
|
-
<Reveal key={title} delay={i * 70} className="h-full">
|
|
74
|
-
<article className="group h-full rounded-xl border border-border bg-card p-6 transition-all duration-300 hover:-translate-y-1 hover:border-accent/40 hover:bg-card-hover hover:shadow-[0_0_36px_rgba(0,212,255,0.12)]">
|
|
75
|
-
<Icon
|
|
76
|
-
size={24}
|
|
77
|
-
className="mb-4 text-accent transition-transform duration-300 group-hover:scale-110 group-hover:rotate-3"
|
|
78
|
-
/>
|
|
79
|
-
<h3 className="mb-2 text-xl font-semibold">{title}</h3>
|
|
80
|
-
<p className="text-[#888]">{body}</p>
|
|
81
|
-
</article>
|
|
82
|
-
</Reveal>
|
|
83
|
-
))}
|
|
84
|
-
</div>
|
|
85
|
-
</div>
|
|
86
|
-
</section>
|
|
87
|
-
)
|
|
88
|
-
}
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import Reveal from './Reveal'
|
|
2
|
-
|
|
3
|
-
export default function Footer() {
|
|
4
|
-
return (
|
|
5
|
-
<footer className="border-t border-white/5 py-12 px-5">
|
|
6
|
-
<Reveal>
|
|
7
|
-
<div className="mx-auto grid max-w-6xl grid-cols-1 items-center gap-8 md:grid-cols-3">
|
|
8
|
-
<div>
|
|
9
|
-
<p className="font-mono text-lg">Skannr</p>
|
|
10
|
-
<p className="mt-1 text-[#888]">Open-source · MIT License</p>
|
|
11
|
-
</div>
|
|
12
|
-
|
|
13
|
-
<p className="text-[#888] md:text-center">
|
|
14
|
-
Built by Vignesh <span className="text-accent">♥</span>
|
|
15
|
-
</p>
|
|
16
|
-
|
|
17
|
-
<div className="flex gap-5 md:justify-self-end">
|
|
18
|
-
<a
|
|
19
|
-
href="https://github.com/7vignesh/code-analyzer"
|
|
20
|
-
className="text-[#888] transition-colors hover:text-accent"
|
|
21
|
-
>
|
|
22
|
-
GitHub
|
|
23
|
-
</a>
|
|
24
|
-
<a
|
|
25
|
-
href="https://npmjs.com/package/skannr"
|
|
26
|
-
className="text-[#888] transition-colors hover:text-accent"
|
|
27
|
-
>
|
|
28
|
-
npm
|
|
29
|
-
</a>
|
|
30
|
-
</div>
|
|
31
|
-
</div>
|
|
32
|
-
</Reveal>
|
|
33
|
-
</footer>
|
|
34
|
-
)
|
|
35
|
-
}
|
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
import { useEffect, useMemo, useState } from 'react'
|
|
2
|
-
import { useTypewriter } from '../hooks/useTypewriter'
|
|
3
|
-
|
|
4
|
-
function renderLine(line, showCursor) {
|
|
5
|
-
const baseClass = line.startsWith('✓')
|
|
6
|
-
? 'text-green'
|
|
7
|
-
: line.startsWith('───') || line.startsWith('────────────────')
|
|
8
|
-
? 'text-[#333]'
|
|
9
|
-
: line.startsWith('Evidence:')
|
|
10
|
-
? 'text-accent'
|
|
11
|
-
: 'text-[#e8e8e8]'
|
|
12
|
-
|
|
13
|
-
const parts = line.split('{ ... }')
|
|
14
|
-
|
|
15
|
-
return (
|
|
16
|
-
<span className={baseClass}>
|
|
17
|
-
{parts.map((part, idx) => (
|
|
18
|
-
<span key={`${part}-${idx}`}>
|
|
19
|
-
{part}
|
|
20
|
-
{idx < parts.length - 1 && <span className="text-[#777]">{'{ ... }'}</span>}
|
|
21
|
-
</span>
|
|
22
|
-
))}
|
|
23
|
-
{showCursor && <span className="animate-pulse text-white">|</span>}
|
|
24
|
-
</span>
|
|
25
|
-
)
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export default function Hero() {
|
|
29
|
-
const [start, setStart] = useState(false)
|
|
30
|
-
|
|
31
|
-
const lines = useMemo(
|
|
32
|
-
() => [
|
|
33
|
-
'$ skannr "how does authentication work?"',
|
|
34
|
-
'',
|
|
35
|
-
'✓ Detected language: TypeScript',
|
|
36
|
-
'✓ Discovered modules: auth, api, middleware, db',
|
|
37
|
-
'✓ Scanning 1,847 files...',
|
|
38
|
-
'',
|
|
39
|
-
' Ranked 8 relevant files in 1.1s',
|
|
40
|
-
' Token reduction: 96.5% vs full scan',
|
|
41
|
-
'',
|
|
42
|
-
'─── src/auth/AuthManager.ts ──────────────────',
|
|
43
|
-
' export class AuthManager {',
|
|
44
|
-
' constructor(private db: Database) {}',
|
|
45
|
-
' async validateToken(token: string): Promise<User | null> { ... }',
|
|
46
|
-
' async createSession(userId: string): Promise<Session> { ... }',
|
|
47
|
-
' }',
|
|
48
|
-
'──────────────────────────────────────────────',
|
|
49
|
-
'',
|
|
50
|
-
'Evidence: AuthManager.ts · SessionStore.ts · middleware/auth.ts',
|
|
51
|
-
],
|
|
52
|
-
[]
|
|
53
|
-
)
|
|
54
|
-
|
|
55
|
-
useEffect(() => {
|
|
56
|
-
const t = setTimeout(() => setStart(true), 800)
|
|
57
|
-
return () => clearTimeout(t)
|
|
58
|
-
}, [])
|
|
59
|
-
|
|
60
|
-
const { displayed, done } = useTypewriter(start ? lines : [], 30, 400)
|
|
61
|
-
|
|
62
|
-
return (
|
|
63
|
-
<section
|
|
64
|
-
className="relative px-5 pt-32 pb-24 min-h-screen flex flex-col items-center justify-center"
|
|
65
|
-
style={{
|
|
66
|
-
backgroundImage: 'radial-gradient(rgba(255,255,255,0.04) 1px, transparent 1px)',
|
|
67
|
-
backgroundSize: '32px 32px',
|
|
68
|
-
}}
|
|
69
|
-
>
|
|
70
|
-
<div
|
|
71
|
-
className="inline-flex items-center gap-2 border border-white/10 rounded-full px-4 py-1 text-sm text-gray-400 mb-8 opacity-0 motion-safe:animate-fade-in-up motion-reduce:opacity-100 motion-reduce:translate-y-0 [animation-delay:0ms]"
|
|
72
|
-
>
|
|
73
|
-
<span className="w-2 h-2 rounded-full bg-green motion-safe:animate-pulse" />
|
|
74
|
-
CLI + MCP · Watch mode · Open source
|
|
75
|
-
</div>
|
|
76
|
-
|
|
77
|
-
<h1 className="text-4xl md:text-6xl font-bold tracking-tight text-center leading-tight opacity-0 motion-safe:animate-fade-in-up motion-reduce:opacity-100 motion-reduce:translate-y-0 [animation-delay:80ms]">
|
|
78
|
-
Any repo. Any question.
|
|
79
|
-
<br />
|
|
80
|
-
<span className="text-accent">Instantly understood.</span>
|
|
81
|
-
</h1>
|
|
82
|
-
|
|
83
|
-
<p className="mt-6 max-w-xl text-center text-lg text-[#888] opacity-0 motion-safe:animate-fade-in-up motion-reduce:opacity-100 motion-reduce:translate-y-0 [animation-delay:180ms]">
|
|
84
|
-
Skannr helps AI assistants understand entire codebases using structural
|
|
85
|
-
skeletons and hybrid ranking. Human, Markdown, or JSON output; optional watch mode for
|
|
86
|
-
live re-analysis; MCP for IDE tools—all without shipping your source off the CLI.
|
|
87
|
-
</p>
|
|
88
|
-
|
|
89
|
-
<div className="flex flex-col sm:flex-row gap-4 mt-10 opacity-0 motion-safe:animate-fade-in-up motion-reduce:opacity-100 motion-reduce:translate-y-0 [animation-delay:280ms]">
|
|
90
|
-
<a
|
|
91
|
-
href="#install"
|
|
92
|
-
className="bg-accent text-black font-semibold px-6 py-3 rounded-lg shadow-[0_0_24px_-4px_rgba(0,212,255,0.5)] transition-all duration-300 hover:opacity-95 hover:shadow-[0_0_32px_-2px_rgba(0,212,255,0.65)] hover:-translate-y-0.5 active:translate-y-0"
|
|
93
|
-
>
|
|
94
|
-
Get started
|
|
95
|
-
</a>
|
|
96
|
-
<a
|
|
97
|
-
href="https://github.com/7vignesh/code-analyzer"
|
|
98
|
-
className="border border-white/20 px-6 py-3 rounded-lg transition-all duration-300 hover:border-accent/60 hover:text-accent hover:-translate-y-0.5 active:translate-y-0"
|
|
99
|
-
>
|
|
100
|
-
View on GitHub
|
|
101
|
-
</a>
|
|
102
|
-
</div>
|
|
103
|
-
|
|
104
|
-
<div className="w-full max-w-3xl mx-auto mt-16 rounded-xl border border-border bg-card overflow-hidden opacity-0 shadow-[0_0_40px_-16px_rgba(0,212,255,0.12)] motion-safe:animate-scale-in motion-safe:[animation-delay:420ms] motion-reduce:opacity-100 motion-reduce:translate-y-0 transition-[box-shadow,border-color] duration-500 hover:border-accent/25 hover:shadow-[0_0_48px_-12px_rgba(0,212,255,0.2)]">
|
|
105
|
-
<div className="flex items-center gap-2 px-4 py-3 border-b border-white/5">
|
|
106
|
-
<span className="w-3 h-3 rounded-full bg-[#ff5f57]" />
|
|
107
|
-
<span className="w-3 h-3 rounded-full bg-[#febc2e]" />
|
|
108
|
-
<span className="w-3 h-3 rounded-full bg-[#28c840]" />
|
|
109
|
-
<span className="ml-2 text-xs font-mono text-[#888]">bash</span>
|
|
110
|
-
</div>
|
|
111
|
-
<pre className="p-5 overflow-x-auto font-mono text-sm leading-7">
|
|
112
|
-
{lines.map((_, idx) => {
|
|
113
|
-
const current = displayed[idx] ?? ''
|
|
114
|
-
const showCursor = !done && idx === displayed.length - 1
|
|
115
|
-
return (
|
|
116
|
-
<div key={`line-${idx}`} className="min-h-6">
|
|
117
|
-
{renderLine(current, showCursor)}
|
|
118
|
-
</div>
|
|
119
|
-
)
|
|
120
|
-
})}
|
|
121
|
-
</pre>
|
|
122
|
-
</div>
|
|
123
|
-
</section>
|
|
124
|
-
)
|
|
125
|
-
}
|