skannr 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +22 -0
- package/README.md +109 -0
- package/dist/agent-cli.d.ts +7 -0
- package/dist/agent-cli.d.ts.map +1 -0
- package/dist/agent-cli.js +364 -0
- package/dist/agent-cli.js.map +1 -0
- package/dist/agent.d.ts +115 -0
- package/dist/agent.d.ts.map +1 -0
- package/dist/agent.js +340 -0
- package/dist/agent.js.map +1 -0
- package/dist/benchmark.d.ts +53 -0
- package/dist/benchmark.d.ts.map +1 -0
- package/dist/benchmark.js +307 -0
- package/dist/benchmark.js.map +1 -0
- package/dist/cache.d.ts +97 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +284 -0
- package/dist/cache.js.map +1 -0
- package/dist/cli.d.ts +6 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +238 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +8 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +52 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +176 -0
- package/dist/index.js.map +1 -0
- package/dist/languages/GenericAdapter.d.ts +10 -0
- package/dist/languages/GenericAdapter.d.ts.map +1 -0
- package/dist/languages/GenericAdapter.js +41 -0
- package/dist/languages/GenericAdapter.js.map +1 -0
- package/dist/languages/LanguageAdapter.d.ts +14 -0
- package/dist/languages/LanguageAdapter.d.ts.map +1 -0
- package/dist/languages/LanguageAdapter.js +3 -0
- package/dist/languages/LanguageAdapter.js.map +1 -0
- package/dist/languages/PythonAdapter.d.ts +10 -0
- package/dist/languages/PythonAdapter.d.ts.map +1 -0
- package/dist/languages/PythonAdapter.js +98 -0
- package/dist/languages/PythonAdapter.js.map +1 -0
- package/dist/languages/TypeScriptAdapter.d.ts +17 -0
- package/dist/languages/TypeScriptAdapter.d.ts.map +1 -0
- package/dist/languages/TypeScriptAdapter.js +321 -0
- package/dist/languages/TypeScriptAdapter.js.map +1 -0
- package/dist/languages/registry.d.ts +4 -0
- package/dist/languages/registry.d.ts.map +1 -0
- package/dist/languages/registry.js +86 -0
- package/dist/languages/registry.js.map +1 -0
- package/dist/mapper.d.ts +49 -0
- package/dist/mapper.d.ts.map +1 -0
- package/dist/mapper.js +386 -0
- package/dist/mapper.js.map +1 -0
- package/dist/ranker-enhanced.d.ts +37 -0
- package/dist/ranker-enhanced.d.ts.map +1 -0
- package/dist/ranker-enhanced.js +395 -0
- package/dist/ranker-enhanced.js.map +1 -0
- package/dist/ranker.d.ts +14 -0
- package/dist/ranker.d.ts.map +1 -0
- package/dist/ranker.js +105 -0
- package/dist/ranker.js.map +1 -0
- package/dist/rocket-chat-scope.d.ts +7 -0
- package/dist/rocket-chat-scope.d.ts.map +1 -0
- package/dist/rocket-chat-scope.js +95 -0
- package/dist/rocket-chat-scope.js.map +1 -0
- package/dist/scanner.d.ts +16 -0
- package/dist/scanner.d.ts.map +1 -0
- package/dist/scanner.js +228 -0
- package/dist/scanner.js.map +1 -0
- package/dist/skeletonizer.d.ts +5 -0
- package/dist/skeletonizer.d.ts.map +1 -0
- package/dist/skeletonizer.js +52 -0
- package/dist/skeletonizer.js.map +1 -0
- package/dist/tokenizer.d.ts +9 -0
- package/dist/tokenizer.d.ts.map +1 -0
- package/dist/tokenizer.js +21 -0
- package/dist/tokenizer.js.map +1 -0
- package/dist/types.d.ts +68 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/gemini-extension/GEMINI.md +26 -0
- package/gemini-extension/gemini-extension.json +12 -0
- package/gemini-extension/package.json +14 -0
- package/gemini-extension/src/server.ts +63 -0
- package/gemini-extension/tsconfig.json +14 -0
- package/jest.config.js +5 -0
- package/package.json +46 -0
- package/src/agent-cli.ts +383 -0
- package/src/agent.ts +344 -0
- package/src/benchmark.ts +389 -0
- package/src/cache.ts +317 -0
- package/src/cli.ts +223 -0
- package/src/config.ts +22 -0
- package/src/index.ts +215 -0
- package/src/languages/GenericAdapter.ts +44 -0
- package/src/languages/LanguageAdapter.ts +14 -0
- package/src/languages/PythonAdapter.ts +74 -0
- package/src/languages/TypeScriptAdapter.ts +338 -0
- package/src/languages/registry.ts +49 -0
- package/src/mapper.ts +448 -0
- package/src/ranker-enhanced.ts +460 -0
- package/src/ranker.ts +92 -0
- package/src/scanner.ts +201 -0
- package/src/skeletonizer.ts +16 -0
- package/src/tokenizer.ts +20 -0
- package/src/types.ts +71 -0
- package/tests/agent.tools.test.ts +81 -0
- package/tests/benchmark.test.ts +31 -0
- package/tests/fixtures/sample.py +17 -0
- package/tests/fixtures/sample.ts +13 -0
- package/tests/fixtures/src/api/routes.ts +1 -0
- package/tests/fixtures/src/auth/permission.ts +3 -0
- package/tests/ranker-enhanced.test.ts +68 -0
- package/tests/ranker.test.ts +75 -0
- package/tests/scanner.scope.test.ts +41 -0
- package/tests/setup-fixtures.js +29 -0
- package/tests/skeletonizer.test.ts +142 -0
- package/tsconfig.json +21 -0
- package/uca-landing/index.html +17 -0
- package/uca-landing/package.json +23 -0
- package/uca-landing/postcss.config.js +6 -0
- package/uca-landing/src/App.jsx +43 -0
- package/uca-landing/src/components/AgentMode.jsx +45 -0
- package/uca-landing/src/components/CliReference.jsx +49 -0
- package/uca-landing/src/components/Features.jsx +63 -0
- package/uca-landing/src/components/Footer.jsx +35 -0
- package/uca-landing/src/components/Hero.jsx +124 -0
- package/uca-landing/src/components/HowItWorks.jsx +60 -0
- package/uca-landing/src/components/Install.jsx +90 -0
- package/uca-landing/src/components/LanguageSupport.jsx +63 -0
- package/uca-landing/src/components/Navbar.jsx +86 -0
- package/uca-landing/src/components/Problem.jsx +51 -0
- package/uca-landing/src/components/Reveal.jsx +40 -0
- package/uca-landing/src/components/WorksWith.jsx +59 -0
- package/uca-landing/src/hooks/useScrollNav.js +13 -0
- package/uca-landing/src/hooks/useTypewriter.js +41 -0
- package/uca-landing/src/index.css +13 -0
- package/uca-landing/src/main.jsx +10 -0
- package/uca-landing/tailwind.config.js +68 -0
- package/uca-landing/vite.config.js +6 -0
package/src/agent-cli.ts
ADDED
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Agent CLI - Interactive code analysis with Gemini AI
|
|
5
|
+
* This demonstrates the agentic code analysis workflow
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { createCodeAnalysisAgent } from './agent';
|
|
9
|
+
import { analyzeProject } from './index';
|
|
10
|
+
import type { AnalysisResult } from './types';
|
|
11
|
+
import * as readline from 'readline';
|
|
12
|
+
import * as path from 'path';
|
|
13
|
+
|
|
14
|
+
interface AgentCliArgs {
|
|
15
|
+
root: string;
|
|
16
|
+
question: string;
|
|
17
|
+
limit: number;
|
|
18
|
+
apiKey?: string;
|
|
19
|
+
model?: string;
|
|
20
|
+
interactive?: boolean;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface InteractiveState {
|
|
24
|
+
result: AnalysisResult;
|
|
25
|
+
skeletonContext: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function buildSkeletonContext(result: AnalysisResult, includeScores: boolean): string {
|
|
29
|
+
return result.files
|
|
30
|
+
.map((file) => {
|
|
31
|
+
if (includeScores) {
|
|
32
|
+
return `=== ${file.path} (score: ${file.score.toFixed(2)}) ===\n${file.skeleton}`;
|
|
33
|
+
}
|
|
34
|
+
return `=== ${file.path} ===\n${file.skeleton}`;
|
|
35
|
+
})
|
|
36
|
+
.join('\n\n');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function printEvidence(result: AnalysisResult): void {
|
|
40
|
+
console.log('π Retrieval Evidence:');
|
|
41
|
+
for (const file of result.files.slice(0, 10)) {
|
|
42
|
+
console.log(`- ${file.path} (score: ${file.score.toFixed(2)})`);
|
|
43
|
+
}
|
|
44
|
+
console.log('');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function printInteractiveHelp(): void {
|
|
48
|
+
console.log('Commands:');
|
|
49
|
+
console.log(' /help Show this help');
|
|
50
|
+
console.log(' /files List currently retrieved files');
|
|
51
|
+
console.log(' /symbols <query> Search symbols in loaded mapping');
|
|
52
|
+
console.log(' /symbol <symbolId> Show full implementation for a symbol');
|
|
53
|
+
console.log(' /deps <filePath> Show imports and symbols for a file');
|
|
54
|
+
console.log(' /refresh Re-analyze project and refresh context');
|
|
55
|
+
console.log(' /stats Show mapping statistics');
|
|
56
|
+
console.log(' /exit Quit interactive mode');
|
|
57
|
+
console.log('');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Parse command line arguments
|
|
62
|
+
*/
|
|
63
|
+
function parseArgs(): AgentCliArgs | null {
|
|
64
|
+
const args = process.argv.slice(2);
|
|
65
|
+
|
|
66
|
+
let root = process.cwd();
|
|
67
|
+
let question = '';
|
|
68
|
+
let limit = 10;
|
|
69
|
+
let apiKey = process.env.GEMINI_API_KEY;
|
|
70
|
+
let model = 'gemini-2.0-flash-exp';
|
|
71
|
+
let interactive = false;
|
|
72
|
+
|
|
73
|
+
for (let i = 0; i < args.length; i++) {
|
|
74
|
+
const arg = args[i];
|
|
75
|
+
|
|
76
|
+
if (arg === '--root' && i + 1 < args.length) {
|
|
77
|
+
root = args[i + 1];
|
|
78
|
+
i++;
|
|
79
|
+
} else if (arg === '--question' && i + 1 < args.length) {
|
|
80
|
+
question = args[i + 1];
|
|
81
|
+
i++;
|
|
82
|
+
} else if (arg === '--limit' && i + 1 < args.length) {
|
|
83
|
+
limit = parseInt(args[i + 1], 10);
|
|
84
|
+
if (isNaN(limit) || limit < 1) {
|
|
85
|
+
console.error('Error: --limit must be a positive number');
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
i++;
|
|
89
|
+
} else if (arg === '--api-key' && i + 1 < args.length) {
|
|
90
|
+
apiKey = args[i + 1];
|
|
91
|
+
i++;
|
|
92
|
+
} else if (arg === '--model' && i + 1 < args.length) {
|
|
93
|
+
model = args[i + 1];
|
|
94
|
+
i++;
|
|
95
|
+
} else if (arg === '--interactive' || arg === '-i') {
|
|
96
|
+
interactive = true;
|
|
97
|
+
} else if (arg === '--help' || arg === '-h') {
|
|
98
|
+
printHelp();
|
|
99
|
+
return null;
|
|
100
|
+
} else {
|
|
101
|
+
console.error(`Unknown argument: ${arg}`);
|
|
102
|
+
printHelp();
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (!apiKey) {
|
|
108
|
+
console.error('Error: GEMINI_API_KEY environment variable or --api-key is required\n');
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (!question && !interactive) {
|
|
113
|
+
console.error('Error: --question is required (or use --interactive for chat mode)\n');
|
|
114
|
+
printHelp();
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return { root, question, limit, apiKey, model, interactive };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Print help message
|
|
123
|
+
*/
|
|
124
|
+
function printHelp(): void {
|
|
125
|
+
console.log(`
|
|
126
|
+
uca-agent - AI-Powered Code Analysis Agent
|
|
127
|
+
|
|
128
|
+
Usage:
|
|
129
|
+
uca-agent --question "<text>" [options]
|
|
130
|
+
uca-agent --interactive [options]
|
|
131
|
+
|
|
132
|
+
Options:
|
|
133
|
+
--root <path> Project root directory (default: current directory)
|
|
134
|
+
--question "<text>" Question to analyze (required unless --interactive)
|
|
135
|
+
--limit <number> Number of top files to analyze (default: 10)
|
|
136
|
+
--api-key <key> Gemini API key (or set GEMINI_API_KEY env var)
|
|
137
|
+
--model <name> Gemini model name (default: gemini-2.0-flash-exp)
|
|
138
|
+
--interactive, -i Start interactive chat mode
|
|
139
|
+
--help, -h Show this help message
|
|
140
|
+
|
|
141
|
+
Example:
|
|
142
|
+
uca-agent --question "How does authentication work?"
|
|
143
|
+
uca-agent --interactive --root ./my-project
|
|
144
|
+
uca-agent --question "Find all API endpoints" --limit 15
|
|
145
|
+
|
|
146
|
+
Environment Variables:
|
|
147
|
+
GEMINI_API_KEY Gemini API key (alternative to --api-key)
|
|
148
|
+
`);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Run single question mode
|
|
153
|
+
*/
|
|
154
|
+
async function runSingleQuestion(args: AgentCliArgs): Promise<void> {
|
|
155
|
+
console.log('π Analyzing codebase...\n');
|
|
156
|
+
|
|
157
|
+
// Step 1: Generate skeletons with mapping
|
|
158
|
+
const result = await analyzeProject({
|
|
159
|
+
root: args.root,
|
|
160
|
+
question: args.question,
|
|
161
|
+
limit: args.limit,
|
|
162
|
+
generateMapping: true,
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
if (result.files.length === 0) {
|
|
166
|
+
console.log('No files found for analysis.');
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
console.log(`π Found ${result.files.length} relevant files\n`);
|
|
171
|
+
|
|
172
|
+
// Prepare skeleton context
|
|
173
|
+
const skeletonContext = buildSkeletonContext(result, true);
|
|
174
|
+
|
|
175
|
+
// Step 2: Create agent and load mapping
|
|
176
|
+
const agent = createCodeAnalysisAgent(args.apiKey!, args.model);
|
|
177
|
+
const mappingPath = path.join(result.root, 'code-analyzer.mapping.json');
|
|
178
|
+
agent.loadMapping(mappingPath);
|
|
179
|
+
|
|
180
|
+
const stats = agent.getStats();
|
|
181
|
+
console.log(`π Loaded mapping: ${stats.totalFiles} files, ${stats.totalSymbols} symbols\n`);
|
|
182
|
+
console.log(`π€ Agent is analyzing...\n`);
|
|
183
|
+
|
|
184
|
+
// Step 3: Send to agent
|
|
185
|
+
const response = await agent.chat(args.question, skeletonContext);
|
|
186
|
+
|
|
187
|
+
console.log('π Agent Response:\n');
|
|
188
|
+
console.log(response);
|
|
189
|
+
console.log('\n');
|
|
190
|
+
printEvidence(result);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Run interactive mode
|
|
195
|
+
*/
|
|
196
|
+
async function runInteractive(args: AgentCliArgs): Promise<void> {
|
|
197
|
+
console.log('π Starting interactive code analysis agent...\n');
|
|
198
|
+
console.log('First, let me analyze your codebase.\n');
|
|
199
|
+
|
|
200
|
+
const buildState = async (): Promise<InteractiveState> => {
|
|
201
|
+
const result = await analyzeProject({
|
|
202
|
+
root: args.root,
|
|
203
|
+
question: 'code structure and organization',
|
|
204
|
+
limit: args.limit,
|
|
205
|
+
generateMapping: true,
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
if (result.files.length === 0) {
|
|
209
|
+
return {
|
|
210
|
+
result,
|
|
211
|
+
skeletonContext: '',
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return {
|
|
216
|
+
result,
|
|
217
|
+
skeletonContext: buildSkeletonContext(result, false),
|
|
218
|
+
};
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
// Step 1: Generate skeletons with mapping
|
|
222
|
+
let state = await buildState();
|
|
223
|
+
|
|
224
|
+
if (state.result.files.length === 0) {
|
|
225
|
+
console.log('No files found for analysis.');
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
console.log(`π Analyzed ${state.result.files.length} files\n`);
|
|
230
|
+
|
|
231
|
+
// Step 2: Create agent and load mapping
|
|
232
|
+
const agent = createCodeAnalysisAgent(args.apiKey!, args.model);
|
|
233
|
+
const mappingPath = path.join(state.result.root, 'code-analyzer.mapping.json');
|
|
234
|
+
agent.loadMapping(mappingPath);
|
|
235
|
+
|
|
236
|
+
const stats = agent.getStats();
|
|
237
|
+
console.log(`π Mapping: ${stats.totalFiles} files, ${stats.totalSymbols} symbols\n`);
|
|
238
|
+
console.log('π¬ Ready for questions! Type /help for commands.\n');
|
|
239
|
+
|
|
240
|
+
// Step 3: Interactive loop
|
|
241
|
+
const rl = readline.createInterface({
|
|
242
|
+
input: process.stdin,
|
|
243
|
+
output: process.stdout,
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
const askQuestion = () => {
|
|
247
|
+
rl.question('You: ', async (input) => {
|
|
248
|
+
const question = input.trim();
|
|
249
|
+
|
|
250
|
+
if (question.toLowerCase() === 'exit' || question.toLowerCase() === '/exit') {
|
|
251
|
+
console.log('\nπ Goodbye!');
|
|
252
|
+
rl.close();
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
if (question.toLowerCase() === 'stats' || question.toLowerCase() === '/stats') {
|
|
257
|
+
console.log('\nπ Statistics:');
|
|
258
|
+
console.log(JSON.stringify(agent.getStats(), null, 2));
|
|
259
|
+
console.log('');
|
|
260
|
+
askQuestion();
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if (question.toLowerCase() === '/help') {
|
|
265
|
+
console.log('');
|
|
266
|
+
printInteractiveHelp();
|
|
267
|
+
askQuestion();
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if (question.toLowerCase() === '/files') {
|
|
272
|
+
console.log('');
|
|
273
|
+
printEvidence(state.result);
|
|
274
|
+
askQuestion();
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
if (question.toLowerCase() === '/refresh') {
|
|
279
|
+
console.log('\nπ Refreshing project analysis...');
|
|
280
|
+
state = await buildState();
|
|
281
|
+
const refreshedMappingPath = path.join(state.result.root, 'code-analyzer.mapping.json');
|
|
282
|
+
agent.loadMapping(refreshedMappingPath);
|
|
283
|
+
console.log(`β
Refreshed ${state.result.files.length} files\n`);
|
|
284
|
+
askQuestion();
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
if (question.startsWith('/symbols ')) {
|
|
289
|
+
const query = question.slice('/symbols '.length).trim();
|
|
290
|
+
if (!query) {
|
|
291
|
+
console.log('\nUsage: /symbols <query>\n');
|
|
292
|
+
askQuestion();
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
const symbols = agent.searchSymbols(query);
|
|
296
|
+
console.log('\nπ Symbol Search Result:');
|
|
297
|
+
console.log(JSON.stringify(symbols, null, 2));
|
|
298
|
+
console.log('');
|
|
299
|
+
askQuestion();
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
if (question.startsWith('/symbol ')) {
|
|
304
|
+
const symbolId = question.slice('/symbol '.length).trim();
|
|
305
|
+
if (!symbolId) {
|
|
306
|
+
console.log('\nUsage: /symbol <symbolId>\n');
|
|
307
|
+
askQuestion();
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
const symbol = agent.getSymbolDetails(symbolId);
|
|
311
|
+
console.log('\nπ§© Symbol Details:');
|
|
312
|
+
console.log(JSON.stringify(symbol, null, 2));
|
|
313
|
+
console.log('');
|
|
314
|
+
askQuestion();
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
if (question.startsWith('/deps ')) {
|
|
319
|
+
const filePath = question.slice('/deps '.length).trim();
|
|
320
|
+
if (!filePath) {
|
|
321
|
+
console.log('\nUsage: /deps <filePath>\n');
|
|
322
|
+
askQuestion();
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
const deps = agent.analyzeFileDependencies(filePath);
|
|
326
|
+
console.log('\nπΈοΈ File Dependencies:');
|
|
327
|
+
console.log(JSON.stringify(deps, null, 2));
|
|
328
|
+
console.log('');
|
|
329
|
+
askQuestion();
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
if (!question) {
|
|
334
|
+
askQuestion();
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
console.log('\nπ€ Agent: ');
|
|
339
|
+
|
|
340
|
+
try {
|
|
341
|
+
const response = await agent.chat(question, state.skeletonContext);
|
|
342
|
+
console.log(response);
|
|
343
|
+
console.log('');
|
|
344
|
+
printEvidence(state.result);
|
|
345
|
+
} catch (error) {
|
|
346
|
+
console.error('Error:', error instanceof Error ? error.message : error);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
console.log('');
|
|
350
|
+
askQuestion();
|
|
351
|
+
});
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
askQuestion();
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Main CLI function
|
|
359
|
+
*/
|
|
360
|
+
async function main(): Promise<void> {
|
|
361
|
+
const args = parseArgs();
|
|
362
|
+
|
|
363
|
+
if (!args) {
|
|
364
|
+
process.exit(1);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
try {
|
|
368
|
+
if (args.interactive) {
|
|
369
|
+
await runInteractive(args);
|
|
370
|
+
} else {
|
|
371
|
+
await runSingleQuestion(args);
|
|
372
|
+
}
|
|
373
|
+
} catch (error) {
|
|
374
|
+
console.error('Error:', error instanceof Error ? error.message : error);
|
|
375
|
+
process.exit(1);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// Run CLI
|
|
380
|
+
main().catch((error) => {
|
|
381
|
+
console.error('Unexpected error:', error);
|
|
382
|
+
process.exit(1);
|
|
383
|
+
});
|