cntx-ui 3.1.0 β†’ 3.1.2

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.
@@ -39,7 +39,9 @@ export class AgentRuntime {
39
39
  let toolsReference = '';
40
40
  if (this.cntxServer.mcpServer) {
41
41
  const tools = this.cntxServer.mcpServer.getToolDefinitions();
42
- toolsReference = tools.map(t => {
42
+ toolsReference = tools
43
+ .filter(t => !t.name?.includes('activities'))
44
+ .map(t => {
43
45
  let params = [];
44
46
  if (t.inputSchema?.properties) {
45
47
  params = Object.entries(t.inputSchema.properties).map(([name, prop]) => {
@@ -50,6 +52,12 @@ export class AgentRuntime {
50
52
  return `### \`${t.name}\`\n${t.description}\n${params.length > 0 ? '**Parameters:**\n- ' + params.join('\n- ') : '*No parameters required*'}\n`;
51
53
  }).join('\n');
52
54
  }
55
+ // Find TOOLS.md template
56
+ let toolsMdPath = path.join(path.dirname(fileURLToPath(import.meta.url)), '../templates/TOOLS.md');
57
+ if (!fs.existsSync(toolsMdPath)) {
58
+ // Fallback for dist/lib/ context
59
+ toolsMdPath = path.join(path.dirname(fileURLToPath(import.meta.url)), '../../templates/TOOLS.md');
60
+ }
53
61
  const manifest = `# πŸ€– Agent Handshake: ${overview.projectPath.split('/').pop()}
54
62
 
55
63
  ## Project Overview
@@ -70,7 +78,7 @@ ${toolsReference || '*(MCP Server not yet initialized, tools will appear here)*'
70
78
  ## πŸ›  Complete Tool & API Reference
71
79
  Refer to the dynamic reference below for full parameter schemas and HTTP fallback endpoints.
72
80
 
73
- ${fs.readFileSync(path.join(path.dirname(fileURLToPath(import.meta.url)), '../templates/TOOLS.md'), 'utf8')}
81
+ ${fs.existsSync(toolsMdPath) ? fs.readFileSync(toolsMdPath, 'utf8') : '*(Tools documentation missing)*'}
74
82
 
75
83
  ## Working Memory
76
84
  This agent is **stateful**. All interactions in this directory are logged to a persistent SQLite database (\`.cntx/bundles.db\`), allowing for context retention across sessions.
@@ -9,6 +9,13 @@ import Parser from 'tree-sitter';
9
9
  import JavaScript from 'tree-sitter-javascript';
10
10
  import TypeScript from 'tree-sitter-typescript';
11
11
  import Rust from 'tree-sitter-rust';
12
+ import Json from 'tree-sitter-json';
13
+ import Css from 'tree-sitter-css';
14
+ import Html from 'tree-sitter-html';
15
+ import Sql from 'tree-sitter-sql';
16
+ import Markdown from 'tree-sitter-markdown';
17
+ import Toml from 'tree-sitter-toml';
18
+ import LegacyParser from 'tree-sitter-legacy';
12
19
  import HeuristicsManager from './heuristics-manager.js';
13
20
  export default class SemanticSplitter {
14
21
  options;
@@ -20,6 +27,7 @@ export default class SemanticSplitter {
20
27
  maxChunkSize: 3000, // Max chars per chunk
21
28
  includeContext: true, // Include imports/types needed
22
29
  minFunctionSize: 40, // Skip tiny functions
30
+ minStructureSize: 20, // Skip tiny structures
23
31
  ...options
24
32
  };
25
33
  // Initialize tree-sitter parsers
@@ -27,17 +35,37 @@ export default class SemanticSplitter {
27
35
  javascript: new Parser(),
28
36
  typescript: new Parser(),
29
37
  tsx: new Parser(),
30
- rust: new Parser()
38
+ rust: new Parser(),
39
+ json: new Parser(),
40
+ css: new Parser(),
41
+ html: new Parser(),
42
+ sql: new LegacyParser(),
43
+ markdown: new LegacyParser(),
44
+ toml: new LegacyParser()
31
45
  };
32
46
  this.parsers.javascript.setLanguage(JavaScript);
33
47
  this.parsers.typescript.setLanguage(TypeScript.typescript);
34
48
  this.parsers.tsx.setLanguage(TypeScript.tsx);
35
49
  this.parsers.rust.setLanguage(Rust);
50
+ this.parsers.json.setLanguage(Json);
51
+ this.parsers.css.setLanguage(Css);
52
+ this.parsers.html.setLanguage(Html);
53
+ this.parsers.sql.setLanguage(Sql);
54
+ this.parsers.markdown.setLanguage(Markdown);
55
+ this.parsers.toml.setLanguage(Toml);
36
56
  this.heuristicsManager = new HeuristicsManager();
37
57
  }
38
58
  getParser(filePath) {
39
59
  const ext = extname(filePath);
40
60
  switch (ext) {
61
+ case '.json': return this.parsers.json;
62
+ case '.css': return this.parsers.css;
63
+ case '.scss': return this.parsers.css;
64
+ case '.html': return this.parsers.html;
65
+ case '.sql': return this.parsers.sql;
66
+ case '.md': return this.parsers.markdown;
67
+ case '.toml': return this.parsers.toml;
68
+ case '.jsx': return this.parsers.javascript;
41
69
  case '.ts': return this.parsers.typescript;
42
70
  case '.tsx': return this.parsers.tsx;
43
71
  case '.rs': return this.parsers.rust;
@@ -81,13 +109,35 @@ export default class SemanticSplitter {
81
109
  const parser = this.getParser(relativePath);
82
110
  const tree = parser.parse(content);
83
111
  const root = tree.rootNode;
112
+ const ext = extname(relativePath).toLowerCase();
84
113
  const elements = {
85
114
  functions: [],
86
115
  types: [],
87
- imports: this.extractImports(root, content, relativePath)
116
+ imports: []
88
117
  };
89
- // Traverse AST for functions and types
90
- this.traverse(root, content, relativePath, elements);
118
+ if (['.js', '.jsx', '.ts', '.tsx', '.rs'].includes(ext)) {
119
+ elements.imports = this.extractImports(root, content, relativePath);
120
+ // Traverse AST for functions and types
121
+ this.traverse(root, content, relativePath, elements);
122
+ }
123
+ else if (ext === '.json') {
124
+ this.extractJsonStructures(root, content, relativePath, elements);
125
+ }
126
+ else if (ext === '.css' || ext === '.scss') {
127
+ this.extractCssStructures(root, content, relativePath, elements);
128
+ }
129
+ else if (ext === '.html') {
130
+ this.extractHtmlStructures(root, content, relativePath, elements);
131
+ }
132
+ else if (ext === '.sql') {
133
+ this.extractSqlStructures(root, content, relativePath, elements);
134
+ }
135
+ else if (ext === '.md') {
136
+ this.extractMarkdownStructures(root, content, relativePath, elements);
137
+ }
138
+ else if (ext === '.toml') {
139
+ this.extractTomlStructures(root, content, relativePath, elements);
140
+ }
91
141
  // Create chunks from elements
92
142
  return this.createChunks(elements, content, relativePath);
93
143
  }
@@ -176,9 +226,183 @@ export default class SemanticSplitter {
176
226
  startLine: node.startPosition.row + 1,
177
227
  code,
178
228
  isExported: this.isExported(node),
179
- isAsync: code.includes('async')
229
+ isAsync: code.includes('async'),
230
+ category: 'function'
231
+ };
232
+ }
233
+ mapStructureNode(name, node, content, filePath) {
234
+ return {
235
+ name,
236
+ type: node.type,
237
+ filePath,
238
+ startLine: node.startPosition.row + 1,
239
+ code: content.slice(node.startIndex, node.endIndex),
240
+ isExported: false,
241
+ isAsync: false,
242
+ category: 'structure'
180
243
  };
181
244
  }
245
+ extractJsonStructures(root, content, filePath, elements) {
246
+ const rootNode = root.namedChild(0);
247
+ if (!rootNode)
248
+ return;
249
+ if (rootNode.type === 'object') {
250
+ for (let i = 0; i < rootNode.namedChildCount; i++) {
251
+ const child = rootNode.namedChild(i);
252
+ if (child && child.type === 'pair') {
253
+ const keyNode = child.childForFieldName('key') || child.namedChild(0);
254
+ const name = keyNode ? content.slice(keyNode.startIndex, keyNode.endIndex).replace(/['"]/g, '') : 'pair';
255
+ const structure = this.mapStructureNode(name, child, content, filePath);
256
+ if (structure.code.length >= this.options.minStructureSize) {
257
+ elements.functions.push(structure);
258
+ }
259
+ }
260
+ }
261
+ }
262
+ else if (rootNode.type === 'array') {
263
+ for (let i = 0; i < rootNode.namedChildCount; i++) {
264
+ const child = rootNode.namedChild(i);
265
+ if (child) {
266
+ const structure = this.mapStructureNode(`item_${i + 1}`, child, content, filePath);
267
+ if (structure.code.length >= this.options.minStructureSize) {
268
+ elements.functions.push(structure);
269
+ }
270
+ }
271
+ }
272
+ }
273
+ }
274
+ extractCssStructures(root, content, filePath, elements) {
275
+ for (let i = 0; i < root.namedChildCount; i++) {
276
+ const node = root.namedChild(i);
277
+ if (!node)
278
+ continue;
279
+ if (node.type === 'rule_set' || node.type === 'at_rule') {
280
+ const name = this.getCssRuleName(node, content);
281
+ const structure = this.mapStructureNode(name, node, content, filePath);
282
+ if (structure.code.length >= this.options.minStructureSize) {
283
+ elements.functions.push(structure);
284
+ }
285
+ }
286
+ }
287
+ }
288
+ getCssRuleName(node, content) {
289
+ const selectorsNode = node.childForFieldName('selectors') || node.namedChild(0);
290
+ if (selectorsNode) {
291
+ return content.slice(selectorsNode.startIndex, selectorsNode.endIndex).trim();
292
+ }
293
+ const code = content.slice(node.startIndex, node.endIndex);
294
+ return code.split('{')[0].trim() || 'rule';
295
+ }
296
+ extractHtmlStructures(root, content, filePath, elements) {
297
+ for (let i = 0; i < root.namedChildCount; i++) {
298
+ const node = root.namedChild(i);
299
+ if (!node)
300
+ continue;
301
+ if (node.type === 'element' || node.type === 'script_element' || node.type === 'style_element') {
302
+ const name = this.getHtmlElementName(node, content);
303
+ const structure = this.mapStructureNode(name, node, content, filePath);
304
+ if (structure.code.length >= this.options.minStructureSize) {
305
+ elements.functions.push(structure);
306
+ }
307
+ }
308
+ }
309
+ }
310
+ getHtmlElementName(node, content) {
311
+ const startTag = node.childForFieldName('start_tag') || node.namedChild(0);
312
+ const tagNameNode = startTag?.childForFieldName('tag_name') || startTag?.namedChild(0);
313
+ if (tagNameNode) {
314
+ return content.slice(tagNameNode.startIndex, tagNameNode.endIndex);
315
+ }
316
+ return node.type;
317
+ }
318
+ extractSqlStructures(root, content, filePath, elements) {
319
+ for (let i = 0; i < root.namedChildCount; i++) {
320
+ const node = root.namedChild(i);
321
+ if (!node)
322
+ continue;
323
+ const name = this.getSqlStatementName(node, content);
324
+ const structure = this.mapStructureNode(name, node, content, filePath);
325
+ if (structure.code.length >= this.options.minStructureSize) {
326
+ elements.functions.push(structure);
327
+ }
328
+ }
329
+ }
330
+ getSqlStatementName(node, content) {
331
+ const code = content.slice(node.startIndex, node.endIndex).trim();
332
+ if (!code)
333
+ return node.type;
334
+ const firstLine = code.split('\n')[0];
335
+ const match = firstLine.match(/^\s*([A-Za-z_]+)/);
336
+ if (match)
337
+ return match[1].toUpperCase();
338
+ return node.type;
339
+ }
340
+ extractMarkdownStructures(root, content, filePath, elements) {
341
+ for (let i = 0; i < root.namedChildCount; i++) {
342
+ const node = root.namedChild(i);
343
+ if (!node)
344
+ continue;
345
+ if (this.isMarkdownStructureNode(node.type)) {
346
+ const name = this.getMarkdownNodeName(node, content);
347
+ const structure = this.mapStructureNode(name, node, content, filePath);
348
+ if (structure.code.length >= this.options.minStructureSize) {
349
+ elements.functions.push(structure);
350
+ }
351
+ }
352
+ }
353
+ }
354
+ isMarkdownStructureNode(type) {
355
+ return [
356
+ 'atx_heading',
357
+ 'setext_heading',
358
+ 'fenced_code_block',
359
+ 'indented_code_block',
360
+ 'tight_list',
361
+ 'loose_list',
362
+ 'list',
363
+ 'block_quote',
364
+ 'thematic_break'
365
+ ].includes(type);
366
+ }
367
+ getMarkdownNodeName(node, content) {
368
+ const text = content.slice(node.startIndex, node.endIndex).trim();
369
+ if (node.type === 'atx_heading') {
370
+ const withoutHashes = text.replace(/^#{1,6}\s*/, '').replace(/\s*#+\s*$/, '').trim();
371
+ return withoutHashes || 'heading';
372
+ }
373
+ if (node.type === 'setext_heading') {
374
+ const firstLine = text.split('\n')[0]?.trim();
375
+ return firstLine || 'heading';
376
+ }
377
+ if (node.type === 'fenced_code_block' || node.type === 'indented_code_block') {
378
+ return 'code_block';
379
+ }
380
+ if (node.type.includes('list')) {
381
+ return 'list';
382
+ }
383
+ if (node.type === 'block_quote') {
384
+ return 'blockquote';
385
+ }
386
+ if (node.type === 'thematic_break') {
387
+ return 'break';
388
+ }
389
+ return node.type;
390
+ }
391
+ extractTomlStructures(root, content, filePath, elements) {
392
+ for (let i = 0; i < root.namedChildCount; i++) {
393
+ const node = root.namedChild(i);
394
+ if (!node)
395
+ continue;
396
+ if (node.type === 'table' || node.type === 'table_array_element' || node.type === 'pair') {
397
+ const keyNode = node.childForFieldName('name') || node.childForFieldName('key') || node.namedChild(0);
398
+ const name = keyNode ? content.slice(keyNode.startIndex, keyNode.endIndex) : node.type;
399
+ const structure = this.mapStructureNode(name, node, content, filePath);
400
+ if (structure.code.length >= this.options.minStructureSize) {
401
+ elements.functions.push(structure);
402
+ }
403
+ }
404
+ }
405
+ }
182
406
  mapTypeNode(node, content, filePath) {
183
407
  const nameNode = node.childForFieldName('name');
184
408
  if (!nameNode)
@@ -251,7 +475,7 @@ export default class SemanticSplitter {
251
475
  id: `${filePath}:${func.name}:${func.startLine}`,
252
476
  name: func.name,
253
477
  filePath,
254
- type: 'function',
478
+ type: func.category === 'structure' ? 'structure' : 'function',
255
479
  subtype: func.type,
256
480
  code: chunkCode,
257
481
  startLine: func.startLine,
package/dist/server.js CHANGED
@@ -3,7 +3,7 @@
3
3
  * Lean orchestration layer using modular architecture
4
4
  */
5
5
  import { createServer } from 'http';
6
- import { join, dirname, extname } from 'path';
6
+ import { join, dirname, extname, basename } from 'path';
7
7
  import { fileURLToPath, parse } from 'url';
8
8
  import { existsSync, mkdirSync, readFileSync, writeFileSync, copyFileSync, cpSync } from 'fs';
9
9
  import { homedir } from 'os';
@@ -19,6 +19,21 @@ import SimpleVectorStore from './lib/simple-vector-store.js';
19
19
  import { MCPServer } from './lib/mcp-server.js';
20
20
  import AgentRuntime from './lib/agent-runtime.js';
21
21
  const __dirname = dirname(fileURLToPath(import.meta.url));
22
+ function getProjectName(cwd) {
23
+ const packageJsonPath = join(cwd, 'package.json');
24
+ if (existsSync(packageJsonPath)) {
25
+ try {
26
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
27
+ if (typeof packageJson?.name === 'string' && packageJson.name.trim()) {
28
+ return packageJson.name.trim();
29
+ }
30
+ }
31
+ catch {
32
+ // Fall through to directory name
33
+ }
34
+ }
35
+ return basename(cwd);
36
+ }
22
37
  export class CntxServer {
23
38
  CWD;
24
39
  CNTX_DIR;
@@ -121,7 +136,7 @@ export class CntxServer {
121
136
  catch (e) { }
122
137
  // Fresh analysis
123
138
  const files = this.fileSystemManager.getAllFiles().map(f => this.fileSystemManager.relativePath(f))
124
- .filter(f => ['.js', '.jsx', '.ts', '.tsx', '.rs'].includes(extname(f).toLowerCase()));
139
+ .filter(f => ['.js', '.jsx', '.ts', '.tsx', '.rs', '.json', '.css', '.scss', '.html', '.sql', '.md', '.toml'].includes(extname(f).toLowerCase()));
125
140
  let bundleConfig = null;
126
141
  if (existsSync(this.configManager.CONFIG_FILE)) {
127
142
  bundleConfig = JSON.parse(readFileSync(this.configManager.CONFIG_FILE, 'utf8'));
@@ -294,7 +309,12 @@ export async function initConfig(cwd = process.cwd()) {
294
309
  console.log('πŸ“„ Created .cntxignore with smart defaults');
295
310
  }
296
311
  console.log('βš™οΈ Basic configuration initialized');
297
- const templateDir = join(__dirname, 'templates');
312
+ let templateDir = join(__dirname, 'templates');
313
+ if (!existsSync(templateDir)) {
314
+ // Fallback for dist/ context
315
+ templateDir = join(__dirname, '..', 'templates');
316
+ }
317
+ const projectName = getProjectName(cwd);
298
318
  // Copy agent configuration files
299
319
  const agentFiles = [
300
320
  'agent-config.yaml',
@@ -304,7 +324,14 @@ export async function initConfig(cwd = process.cwd()) {
304
324
  const sourcePath = join(templateDir, file);
305
325
  const destPath = join(server.CNTX_DIR, file);
306
326
  if (existsSync(sourcePath) && !existsSync(destPath)) {
307
- copyFileSync(sourcePath, destPath);
327
+ if (file === 'agent-config.yaml') {
328
+ const template = readFileSync(sourcePath, 'utf8');
329
+ const updated = template.replace(/^project:\s*["'].*?["']\s*$/m, `project: "${projectName}"`);
330
+ writeFileSync(destPath, updated, 'utf8');
331
+ }
332
+ else {
333
+ copyFileSync(sourcePath, destPath);
334
+ }
308
335
  console.log(`πŸ“„ Created ${file}`);
309
336
  }
310
337
  }
@@ -315,39 +342,6 @@ export async function initConfig(cwd = process.cwd()) {
315
342
  cpSync(agentRulesSource, agentRulesDest, { recursive: true });
316
343
  console.log('πŸ“ Created agent-rules directory with templates');
317
344
  }
318
- // Copy activities framework
319
- const activitiesDir = join(server.CNTX_DIR, 'activities');
320
- if (!existsSync(activitiesDir)) {
321
- mkdirSync(activitiesDir, { recursive: true });
322
- }
323
- // Copy activities README
324
- const activitiesReadmeSource = join(templateDir, 'activities', 'README.md');
325
- const activitiesReadmeDest = join(activitiesDir, 'README.md');
326
- if (existsSync(activitiesReadmeSource) && !existsSync(activitiesReadmeDest)) {
327
- copyFileSync(activitiesReadmeSource, activitiesReadmeDest);
328
- console.log('πŸ“„ Created activities/README.md');
329
- }
330
- // Copy activities lib directory (MDC templates)
331
- const activitiesLibSource = join(templateDir, 'activities', 'lib');
332
- const activitiesLibDest = join(activitiesDir, 'lib');
333
- if (existsSync(activitiesLibSource) && !existsSync(activitiesLibDest)) {
334
- cpSync(activitiesLibSource, activitiesLibDest, { recursive: true });
335
- console.log('πŸ“ Created activities/lib with MDC templates');
336
- }
337
- // Copy activities.json from templates
338
- const activitiesJsonPath = join(activitiesDir, 'activities.json');
339
- const templateActivitiesJsonPath = join(templateDir, 'activities', 'activities.json');
340
- if (!existsSync(activitiesJsonPath) && existsSync(templateActivitiesJsonPath)) {
341
- copyFileSync(templateActivitiesJsonPath, activitiesJsonPath);
342
- console.log('πŸ“„ Created activities.json with bundle example activity');
343
- }
344
- // Copy example activity from templates
345
- const activitiesDestDir = join(activitiesDir, 'activities');
346
- const templateActivitiesDir = join(templateDir, 'activities', 'activities');
347
- if (!existsSync(activitiesDestDir) && existsSync(templateActivitiesDir)) {
348
- cpSync(templateActivitiesDir, activitiesDestDir, { recursive: true });
349
- console.log('πŸ“ Created example activity with templates');
350
- }
351
345
  return server.initMessages;
352
346
  }
353
347
  export async function generateBundle(name) {
@@ -360,17 +354,27 @@ export async function getStatus() {
360
354
  await server.init({ skipFileWatcher: true });
361
355
  const bundles = server.bundleManager.getAllBundleInfo();
362
356
  const totalFiles = server.fileSystemManager.getAllFiles().length;
357
+ // Resolve actual file counts from glob patterns (getAllBundleInfo only
358
+ // returns stored counts which are 0 until bundles are generated)
359
+ const bundlesWithCounts = await Promise.all(bundles.map(async (bundle) => {
360
+ if (bundle.fileCount === 0 && bundle.patterns?.length) {
361
+ const files = await server.bundleManager.resolveBundleFiles(bundle.name);
362
+ return { ...bundle, fileCount: files.length };
363
+ }
364
+ return bundle;
365
+ }));
363
366
  console.log('πŸ“Š cntx-ui Status');
364
367
  console.log('================');
365
368
  console.log(`Total files: ${totalFiles}`);
366
- console.log(`Bundles: ${bundles.length}`);
367
- bundles.forEach(bundle => {
368
- console.log(` β€’ ${bundle.name}: ${bundle.fileCount} files (${Math.round(bundle.size / 1024)}KB)`);
369
+ console.log(`Bundles: ${bundlesWithCounts.length}`);
370
+ bundlesWithCounts.forEach(bundle => {
371
+ const sizeStr = bundle.size > 0 ? ` (${Math.round(bundle.size / 1024)}KB)` : '';
372
+ console.log(` β€’ ${bundle.name}: ${bundle.fileCount} files${sizeStr}`);
369
373
  });
370
374
  return {
371
375
  totalFiles,
372
- bundles: bundles.length,
373
- bundleDetails: bundles
376
+ bundles: bundlesWithCounts.length,
377
+ bundleDetails: bundlesWithCounts
374
378
  };
375
379
  }
376
380
  export function setupMCP() {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "cntx-ui",
3
3
  "type": "module",
4
- "version": "3.1.0",
4
+ "version": "3.1.2",
5
5
  "description": "Autonomous Repository Intelligence engine with web UI and MCP server. Unified semantic code understanding, local RAG, and agent working memory.",
6
6
  "keywords": [
7
7
  "repository-intelligence",
@@ -49,8 +49,15 @@
49
49
  "better-sqlite3": "^12.2.0",
50
50
  "glob": "^9.0.0",
51
51
  "tree-sitter": "^0.21.1",
52
+ "tree-sitter-css": "^0.21.1",
53
+ "tree-sitter-html": "^0.20.4",
52
54
  "tree-sitter-javascript": "^0.23.1",
55
+ "tree-sitter-json": "^0.21.0",
56
+ "tree-sitter-legacy": "npm:tree-sitter@^0.20.4",
57
+ "tree-sitter-markdown": "^0.7.1",
53
58
  "tree-sitter-rust": "^0.21.0",
59
+ "tree-sitter-sql": "^0.1.0",
60
+ "tree-sitter-toml": "^0.5.1",
54
61
  "tree-sitter-typescript": "^0.23.2",
55
62
  "ws": "^8.13.0"
56
63
  },
@@ -21,20 +21,20 @@ You are an AI agent with access to a specialized "Repository Intelligence" engin
21
21
  ## Available Capabilities
22
22
 
23
23
  ### 1. Model Context Protocol (MCP) - PRIMARY
24
- You have direct access to surgical intelligence tools. Refer to the **Intelligence Interface** section in `.cntx/AGENT.md` for full parameter schemas.
24
+ Use MCP tools first: `agent/discover`, `agent/query`, `agent/investigate`, `agent/organize`.
25
25
 
26
26
  ### 2. HTTP API - FALLBACK
27
27
  If MCP is unavailable, use the HTTP endpoints documented in `.cntx/AGENT.md`.
28
28
 
29
29
  ## Performance Hierarchy (Use in this order):
30
30
 
31
- 1. **Semantic Search** (20ms, 90% token savings) - `agent/query` (MCP) or `POST /api/semantic-search` (HTTP)
31
+ 1. **Semantic Search** (20ms, 90% token savings) - `agent/query` (MCP), fallback: `POST /api/semantic-search`
32
32
  - Use for: code discovery, pattern matching, "find functions that..."
33
33
 
34
- 2. **Bundle System** (50ms) - `list_bundles` (MCP) or `GET /api/bundles` (HTTP)
34
+ 2. **Bundle System** (50ms) - `list_bundles` (MCP), fallback: `GET /api/bundles`
35
35
  - Use for: project structure, file organization, high-level overview
36
36
 
37
- 3. **Discovery Mode** - `agent/discover` (MCP) or `GET /api/status` (HTTP)
37
+ 3. **Discovery Mode** - `agent/discover` (MCP), fallback: `GET /api/status`
38
38
  - Use for: architectural overview and health check.
39
39
 
40
40
  4. **Traditional Search** (100ms+, high token cost) - `grep/rg/Read`
@@ -57,7 +57,7 @@ _"Tell me about this codebase"_
57
57
 
58
58
  _"Where is the user authentication handled?"_
59
59
 
60
- - **ALWAYS use vector database first** for semantic discovery (`POST /api/vector-db/search`)
60
+ - **ALWAYS use MCP `agent/query` first** for semantic discovery (fallback: `POST /api/semantic-search`)
61
61
  - Use precise queries like "user authentication login session"
62
62
  - Fallback to traditional search only if vector DB fails
63
63
  - Always provide specific file paths and line numbers from results
@@ -67,7 +67,7 @@ _"Where is the user authentication handled?"_
67
67
 
68
68
  _"I want to add dark modeβ€”what already exists?"_
69
69
 
70
- - **Vector search for related patterns** first: "theme dark mode styling colors"
70
+ - **Vector search for related patterns** first: `agent/investigate` (fallback: `POST /api/vector-db/search`)
71
71
  - Use the format: βœ… Existing, ⚠️ Partial, ❌ Missing
72
72
  - Cross-reference vector results with bundle organization
73
73
  - Identify integration points and patterns to follow
@@ -125,25 +125,11 @@ Would you like me to [specific follow-up options]?
125
125
 
126
126
  ## Efficiency Principles
127
127
 
128
- ### Performance Hierarchy (Use in this order):
129
-
130
- 1. **Vector Database** (20ms, 90% token savings) - `POST /api/vector-db/search`
131
- - Use for: code discovery, pattern matching, "find functions that..."
132
- - Query format: `{"query": "semantic description", "limit": 5, "minSimilarity": 0.2}`
133
-
134
- 2. **Bundle System** (50ms) - `GET /api/bundles`
135
- - Use for: project structure, file organization, high-level overview
136
-
137
- 3. **Traditional Search** (100ms+, high token cost) - `grep/rg/Read`
138
- - Use ONLY when: exact string matching needed, vector search fails
139
- - Examples: specific error messages, exact function names
140
-
141
128
  ### Token Optimization:
142
129
  - **Vector search**: ~5k tokens per query vs 50k+ for file reading
143
130
  - **Real-time updates**: Vector DB stays current with code changes
144
- - **Comprehensive coverage**: 315+ indexed code chunks across entire codebase
145
131
 
146
- ## Vector Search Examples
132
+ ## Vector Search Examples (HTTP fallback)
147
133
 
148
134
  ### Good Query Patterns:
149
135
  ```bash