k0ntext 3.3.0 → 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.
@@ -13,6 +13,10 @@ import { K0NTEXT_THEME, terminal } from './tui/theme.js';
13
13
  import { createIntelligentAnalyzer } from '../../analyzer/intelligent-analyzer.js';
14
14
  import { DatabaseClient } from '../../db/client.js';
15
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';
16
20
 
17
21
  /**
18
22
  * REPL options
@@ -36,6 +40,11 @@ export class REPLShell {
36
40
  private isActive: boolean = false;
37
41
  private noTUI: boolean;
38
42
 
43
+ // Enhanced panels
44
+ private searchPanel: AdvancedSearchPanel;
45
+ private configPanel: ConfigPanel;
46
+ private driftPanel: DriftDetectionPanel;
47
+
39
48
  constructor(options: REPLOptions) {
40
49
  this.projectRoot = options.projectRoot;
41
50
  this.version = options.version;
@@ -45,6 +54,11 @@ export class REPLShell {
45
54
  this.parser = new REPLCommandParser();
46
55
  this.updateChecker = new UpdateChecker(options.version);
47
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
+
48
62
  // Create readline interface
49
63
  this.readline = readline.createInterface({
50
64
  input: process.stdin,
@@ -171,8 +185,12 @@ export class REPLShell {
171
185
  handler: async (args, flags) => {
172
186
  const analyzer = createIntelligentAnalyzer(this.projectRoot);
173
187
  const db = new DatabaseClient(this.projectRoot);
188
+ const visualizer = new IndexingProgressVisualizer();
174
189
 
175
190
  let indexedCount = 0;
191
+ let docsCount = 0;
192
+ let codeIndexedCount = 0;
193
+ let toolsIndexedCount = 0;
176
194
 
177
195
  try {
178
196
  const [docs, code, tools] = await Promise.all([
@@ -181,9 +199,12 @@ export class REPLShell {
181
199
  analyzer.discoverToolConfigs()
182
200
  ]);
183
201
 
184
- const docsCount = docs.length;
202
+ docsCount = docs.length;
185
203
  const codeCount = Math.min(code.length, 500); // Limit for now
186
204
  const toolsCount = tools.length;
205
+ const totalFiles = docsCount + codeCount + toolsCount;
206
+
207
+ visualizer.start(totalFiles);
187
208
 
188
209
  // Index docs
189
210
  for (const doc of docs) {
@@ -196,6 +217,8 @@ export class REPLShell {
196
217
  metadata: { size: doc.size }
197
218
  });
198
219
  indexedCount++;
220
+ docsCount++;
221
+ visualizer.update('indexing_docs', { processed: indexedCount, currentFile: doc.relativePath });
199
222
  }
200
223
 
201
224
  // Index code
@@ -212,7 +235,9 @@ export class REPLShell {
212
235
  metadata: { size: codeFile.size }
213
236
  });
214
237
  indexedCount++;
238
+ codeIndexedCount++;
215
239
  }
240
+ visualizer.update('indexing_code', { processed: indexedCount, currentFile: codeFile.relativePath });
216
241
  }
217
242
 
218
243
  // Index tools
@@ -229,27 +254,22 @@ export class REPLShell {
229
254
  metadata: { tool: tool.tool, size: tool.size }
230
255
  });
231
256
  indexedCount++;
257
+ toolsIndexedCount++;
232
258
  }
259
+ visualizer.update('indexing_tools', { processed: indexedCount, currentFile: tool.relativePath });
233
260
  }
234
261
 
235
262
  this.session.updateStats({
236
263
  filesIndexed: this.session.getStats().filesIndexed + indexedCount
237
264
  });
238
265
 
239
- const output = [
240
- '',
241
- K0NTEXT_THEME.success('✓ Indexing complete'),
242
- ` ${K0NTEXT_THEME.cyan('•')} Documents: ${docsCount}`,
243
- ` ${K0NTEXT_THEME.cyan('•')} Code Files: ${codeCount}`,
244
- ` ${K0NTEXT_THEME.cyan('•')} Tool Configs: ${toolsCount}`,
245
- ` ${K0NTEXT_THEME.cyan('•')} Total Indexed: ${indexedCount}`,
246
- ''
247
- ];
266
+ visualizer.complete({ docsIndexed: docsCount, codeIndexed: codeIndexedCount, configsIndexed: toolsIndexedCount });
248
267
 
249
268
  db.close();
250
269
 
251
- return { success: true, output: output.join('\n') };
270
+ return { success: true, output: '' };
252
271
  } catch (error) {
272
+ visualizer.cancel();
253
273
  db.close();
254
274
  return {
255
275
  success: false,
@@ -259,14 +279,18 @@ export class REPLShell {
259
279
  }
260
280
  });
261
281
 
262
- // Search command
282
+ // Search command - Enhanced
263
283
  this.parser.registerCommand({
264
284
  name: 'search',
265
- description: 'Search indexed content',
266
- usage: 'search <query>',
267
- examples: ['search auth', 'search "user login"'],
268
- completions: () => [],
269
- handler: async (args) => {
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) => {
270
294
  const query = args.join(' ');
271
295
  if (!query) {
272
296
  return {
@@ -282,37 +306,24 @@ export class REPLShell {
282
306
  searchesPerformed: this.session.getStats().searchesPerformed + 1
283
307
  });
284
308
 
285
- if (results.length === 0) {
286
- db.close();
287
- return {
288
- success: true,
289
- output: K0NTEXT_THEME.dim('\nNo results found.')
290
- };
291
- }
292
-
293
- const output = [
294
- '',
295
- K0NTEXT_THEME.header(`━━━ Search Results: "${query}" ━━━`),
296
- ''
297
- ];
309
+ // Parse search flags
310
+ const filters = this.searchPanel.parseSearchFlags(args);
298
311
 
299
- for (let i = 0; i < Math.min(results.length, 10); i++) {
300
- const result = results[i];
301
- output.push(` ${K0NTEXT_THEME.primary(`${i + 1}.`)} ${result.name} [${result.type}]`);
302
- if (result.filePath) {
303
- output.push(` ${K0NTEXT_THEME.dim(result.filePath)}`);
304
- }
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);
305
316
  }
306
-
307
- if (results.length > 10) {
308
- output.push(` ${K0NTEXT_THEME.dim(`... and ${results.length - 10} more`)}`);
317
+ if (filters.sortBy) {
318
+ filteredResults = this.searchPanel.sortResults(filteredResults, filters.sortBy, filters.sortOrder || 'desc');
309
319
  }
310
320
 
311
- output.push('');
321
+ // Display results
322
+ const output = this.searchPanel.displayResults(filteredResults, query, filters);
312
323
 
313
324
  db.close();
314
325
 
315
- return { success: true, output: output.join('\n') };
326
+ return { success: true, output };
316
327
  }
317
328
  });
318
329
 
@@ -364,81 +375,72 @@ export class REPLShell {
364
375
  usage: 'drift',
365
376
  examples: ['drift'],
366
377
  handler: async () => {
367
- const db = new DatabaseClient(this.projectRoot);
368
- const items = db.getAllItems();
369
-
370
- const now = new Date();
371
- const driftDays = 7;
372
- const driftThreshold = new Date(now.getTime() - driftDays * 24 * 60 * 60 * 1000);
373
-
374
- const drifted = items.filter(item => {
375
- if (!item.updatedAt) return false;
376
- const updated = new Date(item.updatedAt);
377
- return updated < driftThreshold;
378
- });
379
-
380
- db.close();
381
-
382
- const output = [
383
- '',
384
- K0NTEXT_THEME.header('━━━ Documentation Drift Check ━━━'),
385
- ''
386
- ];
387
-
388
- if (drifted.length === 0) {
389
- output.push(K0NTEXT_THEME.success('✓ All context files are up to date'));
390
- } else {
391
- output.push(K0NTEXT_THEME.warning(`⚠ Found ${drifted.length} files that may be out of sync:`));
392
- output.push('');
393
-
394
- for (const item of drifted.slice(0, 10)) {
395
- output.push(` ${K0NTEXT_THEME.primary('•')} ${item.name} ${K0NTEXT_THEME.dim(`(${item.updatedAt})`)}`);
396
- }
397
-
398
- if (drifted.length > 10) {
399
- output.push(` ${K0NTEXT_THEME.dim(`... and ${drifted.length - 10} more`)}`);
400
- }
401
-
402
- output.push('');
403
- output.push(K0NTEXT_THEME.info('Run "index" to update your context.'));
404
- }
405
-
406
- output.push('');
407
-
408
- return { success: true, output: output.join('\n') };
378
+ const report = await this.driftPanel.analyze();
379
+ const output = this.driftPanel.displayReport(report);
380
+ return { success: true, output };
409
381
  }
410
382
  });
411
383
 
412
- // Config command
384
+ // Config command - Enhanced
413
385
  this.parser.registerCommand({
414
386
  name: 'config',
415
387
  description: 'View or set configuration',
416
- usage: 'config [get|set|list] [key] [value]',
417
- examples: ['config list', 'config get projectType', 'config set projectType webapp'],
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'],
418
390
  handler: async (args) => {
419
391
  const action = args[0] || 'list';
420
- const state = this.session.getState();
421
392
 
422
- if (action === 'list') {
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();
423
402
  const output = [
424
403
  '',
425
- K0NTEXT_THEME.header('━━━ Configuration ━━━'),
426
- '',
427
- ` ${K0NTEXT_THEME.cyan('projectType:')} ${state.config.projectType || 'not set'}`,
428
- ` ${K0NTEXT_THEME.cyan('apiKey:')} ${state.config.apiKey ? '✓ set' : '○ not set'}`,
429
- ` ${K0NTEXT_THEME.cyan('aiTools:')} ${state.config.aiTools.join(', ') || 'none'}`,
430
- ` ${K0NTEXT_THEME.cyan('features:')} ${state.config.features.join(', ') || 'none'}`,
431
- ` ${K0NTEXT_THEME.cyan('autoUpdate:')} ${state.config.autoUpdate}`,
404
+ K0NTEXT_THEME.header('━━━ Configuration Validation ━━━'),
432
405
  ''
433
406
  ];
434
- return { success: true, output: output.join('\n') };
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() };
435
431
  }
436
432
 
437
433
  if (action === 'get') {
438
434
  const key = args[1];
439
435
  if (!key) return { success: false, error: 'Please specify a key' };
440
- const value = (state.config as any)[key];
441
- return { success: true, output: `${key}: ${value || 'not set'}` };
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}` };
442
444
  }
443
445
 
444
446
  if (action === 'set') {
@@ -447,10 +449,11 @@ export class REPLShell {
447
449
  if (!key || !value) return { success: false, error: 'Usage: config set <key> <value>' };
448
450
 
449
451
  this.session.updateConfig({ [key]: value });
452
+ await this.configPanel.saveConfig();
450
453
  return { success: true, output: K0NTEXT_THEME.success(`✓ Set ${key} = ${value}`) };
451
454
  }
452
455
 
453
- return { success: false, error: 'Unknown action. Use: get, set, or list' };
456
+ return { success: false, error: 'Unknown action. Use: list, get, set, edit, or validate' };
454
457
  }
455
458
  });
456
459
  }