cntx-ui 3.0.9 → 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.
@@ -2,7 +2,7 @@
2
2
  import { readFileSync, existsSync } from 'fs';
3
3
  import { dirname, join } from 'path';
4
4
  import { fileURLToPath } from 'url';
5
- import { startServer, initConfig } from '../server.js';
5
+ import { startServer, initConfig, getStatus, setupMCP, generateBundle } from '../server.js';
6
6
  const __dirname = dirname(fileURLToPath(import.meta.url));
7
7
  let packagePath = join(__dirname, '..', 'package.json');
8
8
  if (!existsSync(packagePath)) {
@@ -30,11 +30,27 @@ async function main() {
30
30
  case 'init':
31
31
  console.log('🚀 Initializing cntx-ui...');
32
32
  await initConfig();
33
- console.log('🎉 cntx-ui initialized with full scaffolding!');
34
33
  break;
35
34
  case 'mcp':
36
35
  await startServer({ withMcp: true, skipFileWatcher: true, skipBundleGeneration: true });
37
36
  break;
37
+ case 'bundle':
38
+ const bundleName = args[1] || 'master';
39
+ try {
40
+ await generateBundle(bundleName);
41
+ console.log(`✅ Bundle '${bundleName}' generated successfully`);
42
+ }
43
+ catch (error) {
44
+ console.error(`❌ Failed to generate bundle '${bundleName}': ${error.message}`);
45
+ process.exit(1);
46
+ }
47
+ break;
48
+ case 'status':
49
+ await getStatus();
50
+ break;
51
+ case 'setup-mcp':
52
+ setupMCP();
53
+ break;
38
54
  case 'version':
39
55
  case '-v':
40
56
  case '--version':
@@ -49,6 +65,9 @@ Usage:
49
65
  cntx-ui watch [port] Start the visual dashboard and intelligence engine (default: 3333)
50
66
  cntx-ui init Initialize cntx-ui configuration in the current directory
51
67
  cntx-ui mcp Start the Model Context Protocol (MCP) server on stdio
68
+ cntx-ui bundle [name] Generate specific bundle (default: master)
69
+ cntx-ui status Show current project status
70
+ cntx-ui setup-mcp Add this project to Claude Desktop MCP config
52
71
  cntx-ui version Show current version
53
72
  cntx-ui help Show this help information
54
73
 
@@ -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,9 +3,10 @@
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
- import { existsSync, mkdirSync, readFileSync } from 'fs';
8
+ import { existsSync, mkdirSync, readFileSync, writeFileSync, copyFileSync, cpSync } from 'fs';
9
+ import { homedir } from 'os';
9
10
  // Import our modular components
10
11
  import ConfigurationManager from './lib/configuration-manager.js';
11
12
  import FileSystemManager from './lib/file-system-manager.js';
@@ -18,6 +19,21 @@ import SimpleVectorStore from './lib/simple-vector-store.js';
18
19
  import { MCPServer } from './lib/mcp-server.js';
19
20
  import AgentRuntime from './lib/agent-runtime.js';
20
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
+ }
21
37
  export class CntxServer {
22
38
  CWD;
23
39
  CNTX_DIR;
@@ -120,7 +136,7 @@ export class CntxServer {
120
136
  catch (e) { }
121
137
  // Fresh analysis
122
138
  const files = this.fileSystemManager.getAllFiles().map(f => this.fileSystemManager.relativePath(f))
123
- .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()));
124
140
  let bundleConfig = null;
125
141
  if (existsSync(this.configManager.CONFIG_FILE)) {
126
142
  bundleConfig = JSON.parse(readFileSync(this.configManager.CONFIG_FILE, 'utf8'));
@@ -218,8 +234,184 @@ export async function startServer(options = {}) {
218
234
  server.startMCPServer();
219
235
  return await server.listen(options.port, options.host);
220
236
  }
237
+ // Initialize project configuration
221
238
  export async function initConfig(cwd = process.cwd()) {
222
239
  const server = new CntxServer(cwd);
223
- // Implementation matches previous init logic
224
- return [];
240
+ // 1. Initialize directory structure
241
+ if (!existsSync(server.CNTX_DIR)) {
242
+ mkdirSync(server.CNTX_DIR, { recursive: true });
243
+ console.log('📁 Created .cntx directory');
244
+ }
245
+ // 2. Create .mcp.json for Claude Code discovery
246
+ const mcpConfigPath = join(cwd, '.mcp.json');
247
+ const mcpConfig = {
248
+ mcpServers: {
249
+ "cntx-ui": {
250
+ command: "cntx-ui",
251
+ args: ["mcp"],
252
+ cwd: "."
253
+ }
254
+ }
255
+ };
256
+ writeFileSync(mcpConfigPath, JSON.stringify(mcpConfig, null, 2), 'utf8');
257
+ console.log('📄 Created .mcp.json for agent auto-discovery');
258
+ // 3. Initialize basic configuration with better defaults and auto-suggestions
259
+ server.configManager.loadConfig();
260
+ const suggestedBundles = {
261
+ master: ['**/*']
262
+ };
263
+ // Directory-based auto-suggestions
264
+ const commonDirs = [
265
+ { dir: 'src/components', name: 'ui-components' },
266
+ { dir: 'src/services', name: 'services' },
267
+ { dir: 'src/lib', name: 'libraries' },
268
+ { dir: 'src/hooks', name: 'react-hooks' },
269
+ { dir: 'server', name: 'backend-api' },
270
+ { dir: 'tests', name: 'test-suite' }
271
+ ];
272
+ commonDirs.forEach(d => {
273
+ if (existsSync(join(cwd, d.dir))) {
274
+ suggestedBundles[d.name] = [`${d.dir}/**`];
275
+ console.log(`💡 Suggested bundle: ${d.name} (${d.dir}/**)`);
276
+ }
277
+ });
278
+ server.configManager.saveConfig({
279
+ bundles: suggestedBundles
280
+ });
281
+ // 4. Create robust default .cntxignore
282
+ const ignorePath = join(cwd, '.cntxignore');
283
+ if (!existsSync(ignorePath)) {
284
+ const defaultIgnore = `# Binary files
285
+ *.db
286
+ *.db-journal
287
+ *.png
288
+ *.jpg
289
+ *.jpeg
290
+ *.ico
291
+ *.icns
292
+ *.gif
293
+ *.zip
294
+ *.tar.gz
295
+
296
+ # Generated files
297
+ **/gen/**
298
+ **/dist/**
299
+ **/build/**
300
+ **/node_modules/**
301
+ **/.next/**
302
+ **/.cache/**
303
+
304
+ # cntx-ui internals
305
+ .cntx/**
306
+ .mcp.json
307
+ `;
308
+ writeFileSync(ignorePath, defaultIgnore, 'utf8');
309
+ console.log('📄 Created .cntxignore with smart defaults');
310
+ }
311
+ console.log('⚙️ Basic configuration initialized');
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);
318
+ // Copy agent configuration files
319
+ const agentFiles = [
320
+ 'agent-config.yaml',
321
+ 'agent-instructions.md'
322
+ ];
323
+ for (const file of agentFiles) {
324
+ const sourcePath = join(templateDir, file);
325
+ const destPath = join(server.CNTX_DIR, file);
326
+ if (existsSync(sourcePath) && !existsSync(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
+ }
335
+ console.log(`📄 Created ${file}`);
336
+ }
337
+ }
338
+ // Copy agent-rules directory structure
339
+ const agentRulesSource = join(templateDir, 'agent-rules');
340
+ const agentRulesDest = join(server.CNTX_DIR, 'agent-rules');
341
+ if (existsSync(agentRulesSource) && !existsSync(agentRulesDest)) {
342
+ cpSync(agentRulesSource, agentRulesDest, { recursive: true });
343
+ console.log('📁 Created agent-rules directory with templates');
344
+ }
345
+ return server.initMessages;
346
+ }
347
+ export async function generateBundle(name) {
348
+ const server = new CntxServer(process.cwd());
349
+ await server.init({ skipFileWatcher: true });
350
+ return await server.bundleManager.regenerateBundle(name);
351
+ }
352
+ export async function getStatus() {
353
+ const server = new CntxServer(process.cwd());
354
+ await server.init({ skipFileWatcher: true });
355
+ const bundles = server.bundleManager.getAllBundleInfo();
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
+ }));
366
+ console.log('📊 cntx-ui Status');
367
+ console.log('================');
368
+ console.log(`Total files: ${totalFiles}`);
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}`);
373
+ });
374
+ return {
375
+ totalFiles,
376
+ bundles: bundlesWithCounts.length,
377
+ bundleDetails: bundlesWithCounts
378
+ };
379
+ }
380
+ export function setupMCP() {
381
+ const configPath = join(homedir(), 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json');
382
+ const projectPath = process.cwd();
383
+ console.log('🔧 Setting up MCP integration...');
384
+ console.log(`Project: ${projectPath}`);
385
+ console.log(`Claude config: ${configPath}`);
386
+ try {
387
+ let config = {};
388
+ if (existsSync(configPath)) {
389
+ config = JSON.parse(readFileSync(configPath, 'utf8'));
390
+ }
391
+ if (!config.mcpServers) {
392
+ config.mcpServers = {};
393
+ }
394
+ config.mcpServers['cntx-ui'] = {
395
+ command: 'npx',
396
+ args: ['cntx-ui', 'mcp'],
397
+ cwd: projectPath
398
+ };
399
+ // Ensure directory exists
400
+ mkdirSync(dirname(configPath), { recursive: true });
401
+ writeFileSync(configPath, JSON.stringify(config, null, 2));
402
+ console.log('✅ MCP integration configured');
403
+ console.log('💡 Restart Claude Desktop to apply changes');
404
+ }
405
+ catch (error) {
406
+ console.error('❌ Failed to setup MCP:', error.message);
407
+ console.log('💡 You may need to manually add the configuration to Claude Desktop');
408
+ }
409
+ }
410
+ // Auto-start server when run directly
411
+ const isMainModule = import.meta.url === `file://${process.argv[1]}`;
412
+ if (isMainModule) {
413
+ console.log('🚀 Starting cntx-ui server...');
414
+ const server = new CntxServer();
415
+ server.init();
416
+ server.listen(3333, 'localhost');
225
417
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "cntx-ui",
3
3
  "type": "module",
4
- "version": "3.0.9",
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",
@@ -42,15 +42,22 @@
42
42
  "check:version-sync": "node scripts/check-version-sync.mjs",
43
43
  "release:ci": "node scripts/release-from-package.mjs",
44
44
  "prepublishOnly": "npm run build",
45
- "test:local": "npm pack && npm install -g ./cntx-ui-3.0.8.tgz"
45
+ "test:local": "npm pack && npm install -g ./cntx-ui-3.1.0.tgz"
46
46
  },
47
47
  "dependencies": {
48
48
  "@xenova/transformers": "^2.17.2",
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
  },