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.
Files changed (60) hide show
  1. package/README.md +15 -0
  2. package/dist/cli/index.js +28 -2
  3. package/dist/cli/index.js.map +1 -1
  4. package/dist/cli/repl/core/parser.d.ts +84 -0
  5. package/dist/cli/repl/core/parser.d.ts.map +1 -0
  6. package/dist/cli/repl/core/parser.js +309 -0
  7. package/dist/cli/repl/core/parser.js.map +1 -0
  8. package/dist/cli/repl/core/session.d.ts +124 -0
  9. package/dist/cli/repl/core/session.d.ts.map +1 -0
  10. package/dist/cli/repl/core/session.js +196 -0
  11. package/dist/cli/repl/core/session.js.map +1 -0
  12. package/dist/cli/repl/index.d.ts +59 -0
  13. package/dist/cli/repl/index.d.ts.map +1 -0
  14. package/dist/cli/repl/index.js +474 -0
  15. package/dist/cli/repl/index.js.map +1 -0
  16. package/dist/cli/repl/init/wizard.d.ts +61 -0
  17. package/dist/cli/repl/init/wizard.d.ts.map +1 -0
  18. package/dist/cli/repl/init/wizard.js +245 -0
  19. package/dist/cli/repl/init/wizard.js.map +1 -0
  20. package/dist/cli/repl/tui/panels/config.d.ts +71 -0
  21. package/dist/cli/repl/tui/panels/config.d.ts.map +1 -0
  22. package/dist/cli/repl/tui/panels/config.js +392 -0
  23. package/dist/cli/repl/tui/panels/config.js.map +1 -0
  24. package/dist/cli/repl/tui/panels/drift.d.ts +95 -0
  25. package/dist/cli/repl/tui/panels/drift.d.ts.map +1 -0
  26. package/dist/cli/repl/tui/panels/drift.js +353 -0
  27. package/dist/cli/repl/tui/panels/drift.js.map +1 -0
  28. package/dist/cli/repl/tui/panels/indexing.d.ts +86 -0
  29. package/dist/cli/repl/tui/panels/indexing.d.ts.map +1 -0
  30. package/dist/cli/repl/tui/panels/indexing.js +254 -0
  31. package/dist/cli/repl/tui/panels/indexing.js.map +1 -0
  32. package/dist/cli/repl/tui/panels/search.d.ts +66 -0
  33. package/dist/cli/repl/tui/panels/search.d.ts.map +1 -0
  34. package/dist/cli/repl/tui/panels/search.js +215 -0
  35. package/dist/cli/repl/tui/panels/search.js.map +1 -0
  36. package/dist/cli/repl/tui/theme.d.ts +109 -0
  37. package/dist/cli/repl/tui/theme.d.ts.map +1 -0
  38. package/dist/cli/repl/tui/theme.js +225 -0
  39. package/dist/cli/repl/tui/theme.js.map +1 -0
  40. package/dist/cli/repl/update/checker.d.ts +64 -0
  41. package/dist/cli/repl/update/checker.d.ts.map +1 -0
  42. package/dist/cli/repl/update/checker.js +166 -0
  43. package/dist/cli/repl/update/checker.js.map +1 -0
  44. package/dist/db/client.d.ts +1 -0
  45. package/dist/db/client.d.ts.map +1 -1
  46. package/dist/db/client.js +2 -1
  47. package/dist/db/client.js.map +1 -1
  48. package/package.json +4 -1
  49. package/src/cli/index.ts +28 -2
  50. package/src/cli/repl/core/parser.ts +373 -0
  51. package/src/cli/repl/core/session.ts +269 -0
  52. package/src/cli/repl/index.ts +557 -0
  53. package/src/cli/repl/init/wizard.ts +300 -0
  54. package/src/cli/repl/tui/panels/config.ts +457 -0
  55. package/src/cli/repl/tui/panels/drift.ts +458 -0
  56. package/src/cli/repl/tui/panels/indexing.ts +324 -0
  57. package/src/cli/repl/tui/panels/search.ts +272 -0
  58. package/src/cli/repl/tui/theme.ts +276 -0
  59. package/src/cli/repl/update/checker.ts +209 -0
  60. 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
+ }