k0ntext 3.2.1 → 3.3.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/README.md +15 -0
- package/dist/cli/index.js +28 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/repl/core/parser.d.ts +84 -0
- package/dist/cli/repl/core/parser.d.ts.map +1 -0
- package/dist/cli/repl/core/parser.js +309 -0
- package/dist/cli/repl/core/parser.js.map +1 -0
- package/dist/cli/repl/core/session.d.ts +124 -0
- package/dist/cli/repl/core/session.d.ts.map +1 -0
- package/dist/cli/repl/core/session.js +196 -0
- package/dist/cli/repl/core/session.js.map +1 -0
- package/dist/cli/repl/index.d.ts +59 -0
- package/dist/cli/repl/index.d.ts.map +1 -0
- package/dist/cli/repl/index.js +474 -0
- package/dist/cli/repl/index.js.map +1 -0
- package/dist/cli/repl/init/wizard.d.ts +61 -0
- package/dist/cli/repl/init/wizard.d.ts.map +1 -0
- package/dist/cli/repl/init/wizard.js +245 -0
- package/dist/cli/repl/init/wizard.js.map +1 -0
- package/dist/cli/repl/tui/panels/config.d.ts +71 -0
- package/dist/cli/repl/tui/panels/config.d.ts.map +1 -0
- package/dist/cli/repl/tui/panels/config.js +392 -0
- package/dist/cli/repl/tui/panels/config.js.map +1 -0
- package/dist/cli/repl/tui/panels/drift.d.ts +95 -0
- package/dist/cli/repl/tui/panels/drift.d.ts.map +1 -0
- package/dist/cli/repl/tui/panels/drift.js +353 -0
- package/dist/cli/repl/tui/panels/drift.js.map +1 -0
- package/dist/cli/repl/tui/panels/indexing.d.ts +86 -0
- package/dist/cli/repl/tui/panels/indexing.d.ts.map +1 -0
- package/dist/cli/repl/tui/panels/indexing.js +254 -0
- package/dist/cli/repl/tui/panels/indexing.js.map +1 -0
- package/dist/cli/repl/tui/panels/search.d.ts +66 -0
- package/dist/cli/repl/tui/panels/search.d.ts.map +1 -0
- package/dist/cli/repl/tui/panels/search.js +215 -0
- package/dist/cli/repl/tui/panels/search.js.map +1 -0
- package/dist/cli/repl/tui/theme.d.ts +109 -0
- package/dist/cli/repl/tui/theme.d.ts.map +1 -0
- package/dist/cli/repl/tui/theme.js +225 -0
- package/dist/cli/repl/tui/theme.js.map +1 -0
- package/dist/cli/repl/update/checker.d.ts +64 -0
- package/dist/cli/repl/update/checker.d.ts.map +1 -0
- package/dist/cli/repl/update/checker.js +166 -0
- package/dist/cli/repl/update/checker.js.map +1 -0
- package/dist/db/client.d.ts +1 -0
- package/dist/db/client.d.ts.map +1 -1
- package/dist/db/client.js +2 -1
- package/dist/db/client.js.map +1 -1
- package/package.json +4 -1
- package/src/cli/index.ts +28 -2
- package/src/cli/repl/core/parser.ts +373 -0
- package/src/cli/repl/core/session.ts +269 -0
- package/src/cli/repl/index.ts +557 -0
- package/src/cli/repl/init/wizard.ts +300 -0
- package/src/cli/repl/tui/panels/config.ts +457 -0
- package/src/cli/repl/tui/panels/drift.ts +458 -0
- package/src/cli/repl/tui/panels/indexing.ts +324 -0
- package/src/cli/repl/tui/panels/search.ts +272 -0
- package/src/cli/repl/tui/theme.ts +276 -0
- package/src/cli/repl/update/checker.ts +209 -0
- package/src/db/client.ts +9 -7
|
@@ -0,0 +1,557 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* K0ntext REPL Shell
|
|
3
|
+
*
|
|
4
|
+
* Interactive shell for managing k0ntext context
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import readline from 'readline';
|
|
8
|
+
import { REPLSessionManager, ProjectType } from './core/session.js';
|
|
9
|
+
import { REPLCommandParser } from './core/parser.js';
|
|
10
|
+
import { InitWizard } from './init/wizard.js';
|
|
11
|
+
import { UpdateChecker } from './update/checker.js';
|
|
12
|
+
import { K0NTEXT_THEME, terminal } from './tui/theme.js';
|
|
13
|
+
import { createIntelligentAnalyzer } from '../../analyzer/intelligent-analyzer.js';
|
|
14
|
+
import { DatabaseClient } from '../../db/client.js';
|
|
15
|
+
import { hasOpenRouterKey } from '../../embeddings/openrouter.js';
|
|
16
|
+
import { AdvancedSearchPanel, EnhancedSearchResult } from './tui/panels/search.js';
|
|
17
|
+
import { ConfigPanel } from './tui/panels/config.js';
|
|
18
|
+
import { IndexingProgressVisualizer } from './tui/panels/indexing.js';
|
|
19
|
+
import { DriftDetectionPanel } from './tui/panels/drift.js';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* REPL options
|
|
23
|
+
*/
|
|
24
|
+
export interface REPLOptions {
|
|
25
|
+
projectRoot: string;
|
|
26
|
+
version: string;
|
|
27
|
+
noTUI?: boolean;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* K0ntext REPL Shell
|
|
32
|
+
*/
|
|
33
|
+
export class REPLShell {
|
|
34
|
+
private session: REPLSessionManager;
|
|
35
|
+
private parser: REPLCommandParser;
|
|
36
|
+
private updateChecker: UpdateChecker;
|
|
37
|
+
private projectRoot: string;
|
|
38
|
+
private version: string;
|
|
39
|
+
private readline: readline.Interface;
|
|
40
|
+
private isActive: boolean = false;
|
|
41
|
+
private noTUI: boolean;
|
|
42
|
+
|
|
43
|
+
// Enhanced panels
|
|
44
|
+
private searchPanel: AdvancedSearchPanel;
|
|
45
|
+
private configPanel: ConfigPanel;
|
|
46
|
+
private driftPanel: DriftDetectionPanel;
|
|
47
|
+
|
|
48
|
+
constructor(options: REPLOptions) {
|
|
49
|
+
this.projectRoot = options.projectRoot;
|
|
50
|
+
this.version = options.version;
|
|
51
|
+
this.noTUI = options.noTUI || false;
|
|
52
|
+
|
|
53
|
+
this.session = new REPLSessionManager(this.projectRoot);
|
|
54
|
+
this.parser = new REPLCommandParser();
|
|
55
|
+
this.updateChecker = new UpdateChecker(options.version);
|
|
56
|
+
|
|
57
|
+
// Initialize enhanced panels
|
|
58
|
+
this.searchPanel = new AdvancedSearchPanel();
|
|
59
|
+
this.configPanel = new ConfigPanel(this.projectRoot, { ...this.session.getState().config } as Record<string, unknown>);
|
|
60
|
+
this.driftPanel = new DriftDetectionPanel(this.projectRoot);
|
|
61
|
+
|
|
62
|
+
// Create readline interface
|
|
63
|
+
this.readline = readline.createInterface({
|
|
64
|
+
input: process.stdin,
|
|
65
|
+
output: process.stdout,
|
|
66
|
+
prompt: this.getPrompt()
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
this.setupEventHandlers();
|
|
70
|
+
this.registerCommands();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Get the command prompt
|
|
75
|
+
*/
|
|
76
|
+
private getPrompt(): string {
|
|
77
|
+
const isInit = this.session.isInitialized();
|
|
78
|
+
const symbol = isInit ? '█' : '?';
|
|
79
|
+
return K0NTEXT_THEME.primary(`k0ntext${symbol} `);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Setup event handlers
|
|
84
|
+
*/
|
|
85
|
+
private setupEventHandlers(): void {
|
|
86
|
+
this.readline.on('line', async (input) => {
|
|
87
|
+
if (!this.isActive) return;
|
|
88
|
+
|
|
89
|
+
const trimmed = input.trim();
|
|
90
|
+
|
|
91
|
+
if (!trimmed) {
|
|
92
|
+
this.readline.prompt();
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Handle exit commands
|
|
97
|
+
if (trimmed.toLowerCase() === 'exit' || trimmed.toLowerCase() === 'quit') {
|
|
98
|
+
await this.stop();
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Parse and execute command
|
|
103
|
+
const parsed = this.parser.parse(trimmed);
|
|
104
|
+
if (parsed) {
|
|
105
|
+
const startTime = Date.now();
|
|
106
|
+
const result = await this.parser.execute(parsed);
|
|
107
|
+
const duration = Date.now() - startTime;
|
|
108
|
+
|
|
109
|
+
this.session.addCommand(trimmed, result.output, duration);
|
|
110
|
+
|
|
111
|
+
if (result.output) {
|
|
112
|
+
console.log('');
|
|
113
|
+
console.log(result.output);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (result.error) {
|
|
117
|
+
console.log('');
|
|
118
|
+
console.log(K0NTEXT_THEME.error(`✖ ${result.error}`));
|
|
119
|
+
}
|
|
120
|
+
} else {
|
|
121
|
+
console.log(K0NTEXT_THEME.warning('\n⚠ Invalid command. Type "help" for available commands.'));
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
this.readline.prompt();
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
this.readline.on('SIGINT', async () => {
|
|
128
|
+
console.log('');
|
|
129
|
+
console.log(K0NTEXT_THEME.warning('\n⚠ Use "exit" to quit the REPL.'));
|
|
130
|
+
this.readline.prompt();
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
this.readline.on('close', async () => {
|
|
134
|
+
await this.stop();
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Register REPL-specific commands
|
|
140
|
+
*/
|
|
141
|
+
private registerCommands(): void {
|
|
142
|
+
// Stats command
|
|
143
|
+
this.parser.registerCommand({
|
|
144
|
+
name: 'stats',
|
|
145
|
+
description: 'Show database and session statistics',
|
|
146
|
+
usage: 'stats',
|
|
147
|
+
examples: ['stats'],
|
|
148
|
+
handler: async () => {
|
|
149
|
+
const db = new DatabaseClient(this.projectRoot);
|
|
150
|
+
const dbStats = db.getStats();
|
|
151
|
+
const sessionStats = this.session.getStats();
|
|
152
|
+
const duration = this.session.getDuration();
|
|
153
|
+
|
|
154
|
+
const output = [
|
|
155
|
+
'',
|
|
156
|
+
K0NTEXT_THEME.header('━━━ Database Statistics ━━━'),
|
|
157
|
+
` ${K0NTEXT_THEME.cyan('•')} Context Items: ${dbStats.items}`,
|
|
158
|
+
` ${K0NTEXT_THEME.cyan('•')} Relations: ${dbStats.relations}`,
|
|
159
|
+
` ${K0NTEXT_THEME.cyan('•')} Git Commits: ${dbStats.commits}`,
|
|
160
|
+
` ${K0NTEXT_THEME.cyan('•')} Embeddings: ${dbStats.embeddings}`,
|
|
161
|
+
` ${K0NTEXT_THEME.cyan('•')} Tool Configs: ${dbStats.toolConfigs}`,
|
|
162
|
+
` ${K0NTEXT_THEME.cyan('•')} Database Path: ${dbStats.path || '.k0ntext.db'}`,
|
|
163
|
+
'',
|
|
164
|
+
K0NTEXT_THEME.header('━━━ Session Statistics ━━━'),
|
|
165
|
+
` ${K0NTEXT_THEME.cyan('•')} Duration: ${duration.human}`,
|
|
166
|
+
` ${K0NTEXT_THEME.cyan('•')} Commands Run: ${sessionStats.commandsExecuted}`,
|
|
167
|
+
` ${K0NTEXT_THEME.cyan('•')} Searches: ${sessionStats.searchesPerformed}`,
|
|
168
|
+
` ${K0NTEXT_THEME.cyan('•')} Files Indexed: ${sessionStats.filesIndexed}`,
|
|
169
|
+
` ${K0NTEXT_THEME.cyan('•')} Embeddings: ${sessionStats.embeddingsGenerated}`,
|
|
170
|
+
''
|
|
171
|
+
];
|
|
172
|
+
|
|
173
|
+
db.close();
|
|
174
|
+
|
|
175
|
+
return { success: true, output: output.join('\n') };
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
// Index command
|
|
180
|
+
this.parser.registerCommand({
|
|
181
|
+
name: 'index',
|
|
182
|
+
description: 'Index codebase into database',
|
|
183
|
+
usage: 'index [options]',
|
|
184
|
+
examples: ['index', 'index --code', 'index --all'],
|
|
185
|
+
handler: async (args, flags) => {
|
|
186
|
+
const analyzer = createIntelligentAnalyzer(this.projectRoot);
|
|
187
|
+
const db = new DatabaseClient(this.projectRoot);
|
|
188
|
+
const visualizer = new IndexingProgressVisualizer();
|
|
189
|
+
|
|
190
|
+
let indexedCount = 0;
|
|
191
|
+
let docsCount = 0;
|
|
192
|
+
let codeIndexedCount = 0;
|
|
193
|
+
let toolsIndexedCount = 0;
|
|
194
|
+
|
|
195
|
+
try {
|
|
196
|
+
const [docs, code, tools] = await Promise.all([
|
|
197
|
+
analyzer.discoverDocs(),
|
|
198
|
+
analyzer.discoverCode(),
|
|
199
|
+
analyzer.discoverToolConfigs()
|
|
200
|
+
]);
|
|
201
|
+
|
|
202
|
+
docsCount = docs.length;
|
|
203
|
+
const codeCount = Math.min(code.length, 500); // Limit for now
|
|
204
|
+
const toolsCount = tools.length;
|
|
205
|
+
const totalFiles = docsCount + codeCount + toolsCount;
|
|
206
|
+
|
|
207
|
+
visualizer.start(totalFiles);
|
|
208
|
+
|
|
209
|
+
// Index docs
|
|
210
|
+
for (const doc of docs) {
|
|
211
|
+
const content = require('fs').readFileSync(doc.path, 'utf-8').slice(0, 50000);
|
|
212
|
+
db.upsertItem({
|
|
213
|
+
type: 'doc',
|
|
214
|
+
name: require('path').basename(doc.relativePath),
|
|
215
|
+
content,
|
|
216
|
+
filePath: doc.relativePath,
|
|
217
|
+
metadata: { size: doc.size }
|
|
218
|
+
});
|
|
219
|
+
indexedCount++;
|
|
220
|
+
docsCount++;
|
|
221
|
+
visualizer.update('indexing_docs', { processed: indexedCount, currentFile: doc.relativePath });
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Index code
|
|
225
|
+
for (const codeFile of code.slice(0, 500)) {
|
|
226
|
+
const content = require('fs').existsSync(codeFile.path)
|
|
227
|
+
? require('fs').readFileSync(codeFile.path, 'utf-8').slice(0, 20000)
|
|
228
|
+
: '';
|
|
229
|
+
if (content) {
|
|
230
|
+
db.upsertItem({
|
|
231
|
+
type: 'code',
|
|
232
|
+
name: require('path').basename(codeFile.relativePath),
|
|
233
|
+
content,
|
|
234
|
+
filePath: codeFile.relativePath,
|
|
235
|
+
metadata: { size: codeFile.size }
|
|
236
|
+
});
|
|
237
|
+
indexedCount++;
|
|
238
|
+
codeIndexedCount++;
|
|
239
|
+
}
|
|
240
|
+
visualizer.update('indexing_code', { processed: indexedCount, currentFile: codeFile.relativePath });
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Index tools
|
|
244
|
+
for (const tool of tools) {
|
|
245
|
+
const content = require('fs').existsSync(tool.path)
|
|
246
|
+
? require('fs').readFileSync(tool.path, 'utf-8').slice(0, 50000)
|
|
247
|
+
: '';
|
|
248
|
+
if (content) {
|
|
249
|
+
db.upsertItem({
|
|
250
|
+
type: 'tool_config',
|
|
251
|
+
name: `${tool.tool}:${require('path').basename(tool.relativePath)}`,
|
|
252
|
+
content,
|
|
253
|
+
filePath: tool.relativePath,
|
|
254
|
+
metadata: { tool: tool.tool, size: tool.size }
|
|
255
|
+
});
|
|
256
|
+
indexedCount++;
|
|
257
|
+
toolsIndexedCount++;
|
|
258
|
+
}
|
|
259
|
+
visualizer.update('indexing_tools', { processed: indexedCount, currentFile: tool.relativePath });
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
this.session.updateStats({
|
|
263
|
+
filesIndexed: this.session.getStats().filesIndexed + indexedCount
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
visualizer.complete({ docsIndexed: docsCount, codeIndexed: codeIndexedCount, configsIndexed: toolsIndexedCount });
|
|
267
|
+
|
|
268
|
+
db.close();
|
|
269
|
+
|
|
270
|
+
return { success: true, output: '' };
|
|
271
|
+
} catch (error) {
|
|
272
|
+
visualizer.cancel();
|
|
273
|
+
db.close();
|
|
274
|
+
return {
|
|
275
|
+
success: false,
|
|
276
|
+
error: error instanceof Error ? error.message : String(error)
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
// Search command - Enhanced
|
|
283
|
+
this.parser.registerCommand({
|
|
284
|
+
name: 'search',
|
|
285
|
+
description: 'Search indexed content with advanced options',
|
|
286
|
+
usage: 'search <query> [options]',
|
|
287
|
+
examples: [
|
|
288
|
+
'search auth',
|
|
289
|
+
'search "user login"',
|
|
290
|
+
'search config --type code',
|
|
291
|
+
'search auth --sort date --limit 20'
|
|
292
|
+
],
|
|
293
|
+
handler: async (args, flags) => {
|
|
294
|
+
const query = args.join(' ');
|
|
295
|
+
if (!query) {
|
|
296
|
+
return {
|
|
297
|
+
success: false,
|
|
298
|
+
error: 'Please provide a search query'
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const db = new DatabaseClient(this.projectRoot);
|
|
303
|
+
const results = db.searchText(query);
|
|
304
|
+
|
|
305
|
+
this.session.updateStats({
|
|
306
|
+
searchesPerformed: this.session.getStats().searchesPerformed + 1
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
// Parse search flags
|
|
310
|
+
const filters = this.searchPanel.parseSearchFlags(args);
|
|
311
|
+
|
|
312
|
+
// Apply filters
|
|
313
|
+
let filteredResults: EnhancedSearchResult[] = results.map(r => ({ item: r, score: 0.5, highlights: [] }));
|
|
314
|
+
if (filters.type) {
|
|
315
|
+
filteredResults = this.searchPanel.filterByType(filteredResults, filters.type);
|
|
316
|
+
}
|
|
317
|
+
if (filters.sortBy) {
|
|
318
|
+
filteredResults = this.searchPanel.sortResults(filteredResults, filters.sortBy, filters.sortOrder || 'desc');
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// Display results
|
|
322
|
+
const output = this.searchPanel.displayResults(filteredResults, query, filters);
|
|
323
|
+
|
|
324
|
+
db.close();
|
|
325
|
+
|
|
326
|
+
return { success: true, output };
|
|
327
|
+
}
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
// Init command (re-run wizard)
|
|
331
|
+
this.parser.registerCommand({
|
|
332
|
+
name: 'init',
|
|
333
|
+
description: 'Re-run initialization wizard',
|
|
334
|
+
usage: 'init',
|
|
335
|
+
examples: ['init'],
|
|
336
|
+
handler: async () => {
|
|
337
|
+
const wizard = new InitWizard(this.projectRoot);
|
|
338
|
+
const config = await wizard.run();
|
|
339
|
+
|
|
340
|
+
if (config) {
|
|
341
|
+
this.session.setInitialized(config.apiKey, config.projectType);
|
|
342
|
+
this.session.updateConfig({
|
|
343
|
+
apiKey: config.apiKey,
|
|
344
|
+
projectType: config.projectType,
|
|
345
|
+
aiTools: config.aiTools,
|
|
346
|
+
features: config.features
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
wizard.showSuccess(config);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
return { success: true };
|
|
353
|
+
}
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
// Update command
|
|
357
|
+
this.parser.registerCommand({
|
|
358
|
+
name: 'update',
|
|
359
|
+
description: 'Check for k0ntext updates',
|
|
360
|
+
usage: 'update',
|
|
361
|
+
examples: ['update'],
|
|
362
|
+
handler: async () => {
|
|
363
|
+
const hasUpdate = await this.updateChecker.checkAndPrompt();
|
|
364
|
+
return {
|
|
365
|
+
success: true,
|
|
366
|
+
output: hasUpdate ? '' : K0NTEXT_THEME.success('\n✓ Already on latest version')
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
// Drift command
|
|
372
|
+
this.parser.registerCommand({
|
|
373
|
+
name: 'drift',
|
|
374
|
+
description: 'Check for documentation drift',
|
|
375
|
+
usage: 'drift',
|
|
376
|
+
examples: ['drift'],
|
|
377
|
+
handler: async () => {
|
|
378
|
+
const report = await this.driftPanel.analyze();
|
|
379
|
+
const output = this.driftPanel.displayReport(report);
|
|
380
|
+
return { success: true, output };
|
|
381
|
+
}
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
// Config command - Enhanced
|
|
385
|
+
this.parser.registerCommand({
|
|
386
|
+
name: 'config',
|
|
387
|
+
description: 'View or set configuration',
|
|
388
|
+
usage: 'config [get|set|list|edit|validate] [key] [value]',
|
|
389
|
+
examples: ['config list', 'config get projectType', 'config set projectType webapp', 'config edit', 'config validate'],
|
|
390
|
+
handler: async (args) => {
|
|
391
|
+
const action = args[0] || 'list';
|
|
392
|
+
|
|
393
|
+
if (action === 'edit') {
|
|
394
|
+
// Interactive configuration editor
|
|
395
|
+
const category = args[1]; // Optional category filter
|
|
396
|
+
await this.configPanel.interactiveConfig(category);
|
|
397
|
+
return { success: true, output: '' };
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
if (action === 'validate') {
|
|
401
|
+
const validation = this.configPanel.validateConfig();
|
|
402
|
+
const output = [
|
|
403
|
+
'',
|
|
404
|
+
K0NTEXT_THEME.header('━━━ Configuration Validation ━━━'),
|
|
405
|
+
''
|
|
406
|
+
];
|
|
407
|
+
|
|
408
|
+
if (validation.valid) {
|
|
409
|
+
output.push(K0NTEXT_THEME.success('✓ Configuration is valid'));
|
|
410
|
+
} else {
|
|
411
|
+
output.push(K0NTEXT_THEME.error('✗ Configuration errors found:'));
|
|
412
|
+
for (const error of validation.errors) {
|
|
413
|
+
output.push(` ${K0NTEXT_THEME.error('•')} ${error}`);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
if (validation.warnings.length > 0) {
|
|
418
|
+
output.push('');
|
|
419
|
+
output.push(K0NTEXT_THEME.warning('⚠ Warnings:'));
|
|
420
|
+
for (const warning of validation.warnings) {
|
|
421
|
+
output.push(` ${K0NTEXT_THEME.warning('•')} ${warning}`);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
output.push('');
|
|
425
|
+
|
|
426
|
+
return { success: validation.valid, output: output.join('\n') };
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
if (action === 'list') {
|
|
430
|
+
return { success: true, output: this.configPanel.displayConfig() };
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
if (action === 'get') {
|
|
434
|
+
const key = args[1];
|
|
435
|
+
if (!key) return { success: false, error: 'Please specify a key' };
|
|
436
|
+
const value = this.configPanel.getValue(key);
|
|
437
|
+
const formatted = this.configPanel.formatValue(value, {
|
|
438
|
+
name: key,
|
|
439
|
+
type: 'string',
|
|
440
|
+
description: '',
|
|
441
|
+
defaultValue: ''
|
|
442
|
+
});
|
|
443
|
+
return { success: true, output: `${key}: ${formatted}` };
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
if (action === 'set') {
|
|
447
|
+
const key = args[1];
|
|
448
|
+
const value = args.slice(2).join(' ');
|
|
449
|
+
if (!key || !value) return { success: false, error: 'Usage: config set <key> <value>' };
|
|
450
|
+
|
|
451
|
+
this.session.updateConfig({ [key]: value });
|
|
452
|
+
await this.configPanel.saveConfig();
|
|
453
|
+
return { success: true, output: K0NTEXT_THEME.success(`✓ Set ${key} = ${value}`) };
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
return { success: false, error: 'Unknown action. Use: list, get, set, edit, or validate' };
|
|
457
|
+
}
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* Start the REPL
|
|
463
|
+
*/
|
|
464
|
+
async start(): Promise<void> {
|
|
465
|
+
this.isActive = true;
|
|
466
|
+
|
|
467
|
+
// Show banner
|
|
468
|
+
this.showBanner();
|
|
469
|
+
|
|
470
|
+
// Check for updates
|
|
471
|
+
if (this.session.getState().config.autoUpdate) {
|
|
472
|
+
await this.updateChecker.showNotification({ showIfCurrent: false, checkInterval: 24 * 60 * 60 * 1000 });
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
// Check if initialized
|
|
476
|
+
if (!this.session.isInitialized()) {
|
|
477
|
+
console.log('');
|
|
478
|
+
console.log(K0NTEXT_THEME.info('🔧 First-time setup detected. Running initialization wizard...\n'));
|
|
479
|
+
|
|
480
|
+
const wizard = new InitWizard(this.projectRoot);
|
|
481
|
+
const config = await wizard.run();
|
|
482
|
+
|
|
483
|
+
if (config) {
|
|
484
|
+
this.session.setInitialized(config.apiKey, config.projectType);
|
|
485
|
+
this.session.updateConfig({
|
|
486
|
+
apiKey: config.apiKey,
|
|
487
|
+
projectType: config.projectType,
|
|
488
|
+
aiTools: config.aiTools,
|
|
489
|
+
features: config.features
|
|
490
|
+
});
|
|
491
|
+
|
|
492
|
+
wizard.showSuccess(config);
|
|
493
|
+
} else {
|
|
494
|
+
console.log(K0NTEXT_THEME.info('\n✓ Skipping initialization. You can run "init" later.\n'));
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
// Show project stats if initialized
|
|
499
|
+
if (this.session.isInitialized()) {
|
|
500
|
+
const analyzer = createIntelligentAnalyzer(this.projectRoot);
|
|
501
|
+
const analysis = await analyzer.analyze();
|
|
502
|
+
|
|
503
|
+
console.log('');
|
|
504
|
+
console.log(K0NTEXT_THEME.header('━━━ Project Overview ━━━'));
|
|
505
|
+
console.log(` ${K0NTEXT_THEME.primary('Type:')} ${this.session.getState().config.projectType}`);
|
|
506
|
+
console.log(` ${K0NTEXT_THEME.primary('Docs:')} ${analysis.existingContext.files.filter(f => f.type === 'doc').length}`);
|
|
507
|
+
console.log(` ${K0NTEXT_THEME.primary('Code:')} ${analysis.existingContext.files.filter(f => f.type === 'code').length}`);
|
|
508
|
+
console.log(` ${K0NTEXT_THEME.primary('Tech:')} ${analysis.techStack.languages.slice(0, 3).join(', ')}`);
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
console.log('');
|
|
512
|
+
console.log(K0NTEXT_THEME.dim('Type "help" for available commands, "exit" to quit.'));
|
|
513
|
+
|
|
514
|
+
// Start readline
|
|
515
|
+
this.readline.prompt();
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
/**
|
|
519
|
+
* Stop the REPL
|
|
520
|
+
*/
|
|
521
|
+
async stop(): Promise<void> {
|
|
522
|
+
this.isActive = false;
|
|
523
|
+
this.session.end();
|
|
524
|
+
this.readline.close();
|
|
525
|
+
console.log('');
|
|
526
|
+
console.log(K0NTEXT_THEME.success('✓ Session saved. Goodbye!'));
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
/**
|
|
530
|
+
* Show welcome banner
|
|
531
|
+
*/
|
|
532
|
+
private showBanner(): void {
|
|
533
|
+
const { supportsColor } = K0NTEXT_THEME.detectCapabilities();
|
|
534
|
+
|
|
535
|
+
if (!supportsColor) {
|
|
536
|
+
console.log(`\n K0ntext v${this.version}\n`);
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
console.log('');
|
|
541
|
+
console.log(K0NTEXT_THEME.box(
|
|
542
|
+
`K0NTEXT v${this.version}`,
|
|
543
|
+
`${K0NTEXT_THEME.dim('Interactive shell for AI context engineering')}
|
|
544
|
+
${K0NTEXT_THEME.dim('Type "help" for commands, "exit" to quit')}`,
|
|
545
|
+
'primary'
|
|
546
|
+
));
|
|
547
|
+
console.log('');
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
/**
|
|
552
|
+
* Create and start a REPL shell
|
|
553
|
+
*/
|
|
554
|
+
export async function startREPL(projectRoot: string = process.cwd(), version: string, noTUI = false): Promise<void> {
|
|
555
|
+
const shell = new REPLShell({ projectRoot, version, noTUI });
|
|
556
|
+
await shell.start();
|
|
557
|
+
}
|