cntx-ui 3.1.0 → 3.1.3
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 +9 -0
- package/dist/bin/cntx-ui.js +1 -1
- package/dist/lib/agent-runtime.js +10 -2
- package/dist/lib/api-router.js +19 -0
- package/dist/lib/artifact-manager.js +147 -0
- package/dist/lib/bundle-manager.js +4 -0
- package/dist/lib/mcp-server.js +42 -0
- package/dist/lib/semantic-splitter.js +238 -6
- package/dist/lib/simple-vector-store.js +3 -2
- package/dist/server.js +78 -50
- package/package.json +8 -1
- package/templates/agent-instructions.md +7 -21
- package/web/dist/assets/{index-D2RTcdqV.js → index-8QXMnXVq.js} +3 -3
- package/web/dist/index.html +1 -1
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';
|
|
@@ -13,12 +13,28 @@ import FileSystemManager from './lib/file-system-manager.js';
|
|
|
13
13
|
import BundleManager from './lib/bundle-manager.js';
|
|
14
14
|
import APIRouter from './lib/api-router.js';
|
|
15
15
|
import WebSocketManager from './lib/websocket-manager.js';
|
|
16
|
+
import ArtifactManager from './lib/artifact-manager.js';
|
|
16
17
|
// Import existing lib modules
|
|
17
18
|
import SemanticSplitter from './lib/semantic-splitter.js';
|
|
18
19
|
import SimpleVectorStore from './lib/simple-vector-store.js';
|
|
19
20
|
import { MCPServer } from './lib/mcp-server.js';
|
|
20
21
|
import AgentRuntime from './lib/agent-runtime.js';
|
|
21
22
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
23
|
+
function getProjectName(cwd) {
|
|
24
|
+
const packageJsonPath = join(cwd, 'package.json');
|
|
25
|
+
if (existsSync(packageJsonPath)) {
|
|
26
|
+
try {
|
|
27
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
|
|
28
|
+
if (typeof packageJson?.name === 'string' && packageJson.name.trim()) {
|
|
29
|
+
return packageJson.name.trim();
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
// Fall through to directory name
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return basename(cwd);
|
|
37
|
+
}
|
|
22
38
|
export class CntxServer {
|
|
23
39
|
CWD;
|
|
24
40
|
CNTX_DIR;
|
|
@@ -32,6 +48,7 @@ export class CntxServer {
|
|
|
32
48
|
bundleManager;
|
|
33
49
|
webSocketManager;
|
|
34
50
|
apiRouter;
|
|
51
|
+
artifactManager;
|
|
35
52
|
semanticSplitter;
|
|
36
53
|
vectorStore;
|
|
37
54
|
agentRuntime;
|
|
@@ -56,6 +73,7 @@ export class CntxServer {
|
|
|
56
73
|
this.fileSystemManager = new FileSystemManager(cwd, { verbose: this.verbose });
|
|
57
74
|
this.bundleManager = new BundleManager(this.configManager, this.fileSystemManager, this.verbose);
|
|
58
75
|
this.webSocketManager = new WebSocketManager(this.bundleManager, this.configManager, { verbose: this.verbose });
|
|
76
|
+
this.artifactManager = new ArtifactManager(cwd);
|
|
59
77
|
// AI Components
|
|
60
78
|
this.semanticSplitter = new SemanticSplitter({
|
|
61
79
|
maxChunkSize: 2000,
|
|
@@ -97,6 +115,7 @@ export class CntxServer {
|
|
|
97
115
|
}
|
|
98
116
|
// Load reasoning/manifest
|
|
99
117
|
await this.agentRuntime.generateAgentManifest();
|
|
118
|
+
this.artifactManager.refresh();
|
|
100
119
|
}
|
|
101
120
|
startWatching() {
|
|
102
121
|
this.fileSystemManager.startWatching((eventType, filename) => {
|
|
@@ -121,7 +140,7 @@ export class CntxServer {
|
|
|
121
140
|
catch (e) { }
|
|
122
141
|
// Fresh analysis
|
|
123
142
|
const files = this.fileSystemManager.getAllFiles().map(f => this.fileSystemManager.relativePath(f))
|
|
124
|
-
.filter(f => ['.js', '.jsx', '.ts', '.tsx', '.rs'].includes(extname(f).toLowerCase()));
|
|
143
|
+
.filter(f => ['.js', '.jsx', '.ts', '.tsx', '.rs', '.json', '.css', '.scss', '.html', '.sql', '.md', '.toml'].includes(extname(f).toLowerCase()));
|
|
125
144
|
let bundleConfig = null;
|
|
126
145
|
if (existsSync(this.configManager.CONFIG_FILE)) {
|
|
127
146
|
bundleConfig = JSON.parse(readFileSync(this.configManager.CONFIG_FILE, 'utf8'));
|
|
@@ -137,15 +156,31 @@ export class CntxServer {
|
|
|
137
156
|
async enhanceSemanticChunksIfNeeded(analysis) {
|
|
138
157
|
if (!analysis || !analysis.chunks)
|
|
139
158
|
return;
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
159
|
+
try {
|
|
160
|
+
if (!this.vectorStoreInitialized) {
|
|
161
|
+
await this.vectorStore.init();
|
|
162
|
+
this.vectorStoreInitialized = true;
|
|
163
|
+
}
|
|
164
|
+
let embedded = 0;
|
|
165
|
+
let skipped = 0;
|
|
166
|
+
for (const chunk of analysis.chunks) {
|
|
167
|
+
if (!this.databaseManager.getEmbedding(chunk.id)) {
|
|
168
|
+
try {
|
|
169
|
+
await this.vectorStore.upsertChunk(chunk);
|
|
170
|
+
embedded++;
|
|
171
|
+
}
|
|
172
|
+
catch (e) {
|
|
173
|
+
skipped++;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
if (skipped > 0) {
|
|
178
|
+
console.warn(`⚠️ Embedded ${embedded} chunks, skipped ${skipped} (too large or invalid)`);
|
|
147
179
|
}
|
|
148
180
|
}
|
|
181
|
+
catch (e) {
|
|
182
|
+
console.error(`⚠️ Vector embedding failed: ${e.message}. Semantic search will be unavailable.`);
|
|
183
|
+
}
|
|
149
184
|
}
|
|
150
185
|
async refreshSemanticAnalysis() {
|
|
151
186
|
this.databaseManager.db.prepare('DELETE FROM semantic_chunks').run();
|
|
@@ -178,7 +213,11 @@ export class CntxServer {
|
|
|
178
213
|
});
|
|
179
214
|
}
|
|
180
215
|
handleStaticFile(req, res, url) {
|
|
181
|
-
|
|
216
|
+
let webDir = join(__dirname, 'web', 'dist');
|
|
217
|
+
if (!existsSync(webDir)) {
|
|
218
|
+
// Fallback for dist/ context — web/dist is at package root
|
|
219
|
+
webDir = join(__dirname, '..', 'web', 'dist');
|
|
220
|
+
}
|
|
182
221
|
let filePath = join(webDir, url.pathname === '/' ? 'index.html' : url.pathname);
|
|
183
222
|
if (!existsSync(filePath)) {
|
|
184
223
|
filePath = join(webDir, 'index.html');
|
|
@@ -294,7 +333,12 @@ export async function initConfig(cwd = process.cwd()) {
|
|
|
294
333
|
console.log('📄 Created .cntxignore with smart defaults');
|
|
295
334
|
}
|
|
296
335
|
console.log('⚙️ Basic configuration initialized');
|
|
297
|
-
|
|
336
|
+
let templateDir = join(__dirname, 'templates');
|
|
337
|
+
if (!existsSync(templateDir)) {
|
|
338
|
+
// Fallback for dist/ context
|
|
339
|
+
templateDir = join(__dirname, '..', 'templates');
|
|
340
|
+
}
|
|
341
|
+
const projectName = getProjectName(cwd);
|
|
298
342
|
// Copy agent configuration files
|
|
299
343
|
const agentFiles = [
|
|
300
344
|
'agent-config.yaml',
|
|
@@ -304,7 +348,14 @@ export async function initConfig(cwd = process.cwd()) {
|
|
|
304
348
|
const sourcePath = join(templateDir, file);
|
|
305
349
|
const destPath = join(server.CNTX_DIR, file);
|
|
306
350
|
if (existsSync(sourcePath) && !existsSync(destPath)) {
|
|
307
|
-
|
|
351
|
+
if (file === 'agent-config.yaml') {
|
|
352
|
+
const template = readFileSync(sourcePath, 'utf8');
|
|
353
|
+
const updated = template.replace(/^project:\s*["'].*?["']\s*$/m, `project: "${projectName}"`);
|
|
354
|
+
writeFileSync(destPath, updated, 'utf8');
|
|
355
|
+
}
|
|
356
|
+
else {
|
|
357
|
+
copyFileSync(sourcePath, destPath);
|
|
358
|
+
}
|
|
308
359
|
console.log(`📄 Created ${file}`);
|
|
309
360
|
}
|
|
310
361
|
}
|
|
@@ -315,39 +366,6 @@ export async function initConfig(cwd = process.cwd()) {
|
|
|
315
366
|
cpSync(agentRulesSource, agentRulesDest, { recursive: true });
|
|
316
367
|
console.log('📁 Created agent-rules directory with templates');
|
|
317
368
|
}
|
|
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
369
|
return server.initMessages;
|
|
352
370
|
}
|
|
353
371
|
export async function generateBundle(name) {
|
|
@@ -360,17 +378,27 @@ export async function getStatus() {
|
|
|
360
378
|
await server.init({ skipFileWatcher: true });
|
|
361
379
|
const bundles = server.bundleManager.getAllBundleInfo();
|
|
362
380
|
const totalFiles = server.fileSystemManager.getAllFiles().length;
|
|
381
|
+
// Resolve actual file counts from glob patterns (getAllBundleInfo only
|
|
382
|
+
// returns stored counts which are 0 until bundles are generated)
|
|
383
|
+
const bundlesWithCounts = await Promise.all(bundles.map(async (bundle) => {
|
|
384
|
+
if (bundle.fileCount === 0 && bundle.patterns?.length) {
|
|
385
|
+
const files = await server.bundleManager.resolveBundleFiles(bundle.name);
|
|
386
|
+
return { ...bundle, fileCount: files.length };
|
|
387
|
+
}
|
|
388
|
+
return bundle;
|
|
389
|
+
}));
|
|
363
390
|
console.log('📊 cntx-ui Status');
|
|
364
391
|
console.log('================');
|
|
365
392
|
console.log(`Total files: ${totalFiles}`);
|
|
366
|
-
console.log(`Bundles: ${
|
|
367
|
-
|
|
368
|
-
|
|
393
|
+
console.log(`Bundles: ${bundlesWithCounts.length}`);
|
|
394
|
+
bundlesWithCounts.forEach(bundle => {
|
|
395
|
+
const sizeStr = bundle.size > 0 ? ` (${Math.round(bundle.size / 1024)}KB)` : '';
|
|
396
|
+
console.log(` • ${bundle.name}: ${bundle.fileCount} files${sizeStr}`);
|
|
369
397
|
});
|
|
370
398
|
return {
|
|
371
399
|
totalFiles,
|
|
372
|
-
bundles:
|
|
373
|
-
bundleDetails:
|
|
400
|
+
bundles: bundlesWithCounts.length,
|
|
401
|
+
bundleDetails: bundlesWithCounts
|
|
374
402
|
};
|
|
375
403
|
}
|
|
376
404
|
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.
|
|
4
|
+
"version": "3.1.3",
|
|
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
|
-
|
|
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)
|
|
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)
|
|
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)
|
|
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
|
|
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:
|
|
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
|