cntx-ui 3.0.4 → 3.0.5
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/lib/bundle-manager.js +118 -28
- package/package.json +1 -1
package/lib/bundle-manager.js
CHANGED
|
@@ -51,38 +51,87 @@ export default class BundleManager {
|
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
/**
|
|
54
|
-
* Generate Smart Bundle definitions from indexed semantic data
|
|
54
|
+
* Generate Smart Bundle definitions from indexed semantic data.
|
|
55
|
+
* Uses business domain, directory structure, and technical patterns
|
|
56
|
+
* instead of raw AST node types.
|
|
55
57
|
*/
|
|
56
58
|
generateSmartBundleDefinitions() {
|
|
57
59
|
const smartBundles = [];
|
|
60
|
+
const MIN_CHUNKS = 3; // Skip bundles with fewer than this many chunks
|
|
61
|
+
|
|
58
62
|
try {
|
|
59
|
-
// 1. Group by
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
63
|
+
// 1. Group by business domain (from metadata JSON)
|
|
64
|
+
const allRows = this.db.db.prepare('SELECT metadata FROM semantic_chunks WHERE metadata IS NOT NULL').all();
|
|
65
|
+
const domainCounts = new Map();
|
|
66
|
+
for (const row of allRows) {
|
|
67
|
+
try {
|
|
68
|
+
const meta = JSON.parse(row.metadata);
|
|
69
|
+
for (const domain of (meta.businessDomain || [])) {
|
|
70
|
+
domainCounts.set(domain, (domainCounts.get(domain) || 0) + 1);
|
|
71
|
+
}
|
|
72
|
+
} catch { /* skip malformed */ }
|
|
73
|
+
}
|
|
74
|
+
for (const [domain, count] of domainCounts) {
|
|
75
|
+
if (count < MIN_CHUNKS) continue;
|
|
64
76
|
smartBundles.push({
|
|
65
|
-
name
|
|
66
|
-
purpose:
|
|
67
|
-
fileCount:
|
|
77
|
+
name: `smart:${domain}`,
|
|
78
|
+
purpose: domain,
|
|
79
|
+
fileCount: count,
|
|
68
80
|
type: 'smart',
|
|
69
|
-
description:
|
|
81
|
+
description: `${count} chunks in the ${domain} domain`
|
|
70
82
|
});
|
|
71
|
-
}
|
|
83
|
+
}
|
|
72
84
|
|
|
73
|
-
// 2. Group by
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
85
|
+
// 2. Group by directory structure (components, hooks, pages, api, etc.)
|
|
86
|
+
const dirRows = this.db.db.prepare(`
|
|
87
|
+
SELECT
|
|
88
|
+
CASE
|
|
89
|
+
WHEN file_path LIKE '%/hooks/%' THEN 'hooks'
|
|
90
|
+
WHEN file_path LIKE '%/components/%' THEN 'components'
|
|
91
|
+
WHEN file_path LIKE '%/api/%' OR file_path LIKE '%/services/%' THEN 'api-services'
|
|
92
|
+
WHEN file_path LIKE '%/pages/%' OR file_path LIKE '%/routes/%' THEN 'pages'
|
|
93
|
+
WHEN file_path LIKE '%/stores/%' OR file_path LIKE '%/store/%' THEN 'state'
|
|
94
|
+
WHEN file_path LIKE '%/lib/%' OR file_path LIKE '%/utils/%' THEN 'lib-utils'
|
|
95
|
+
WHEN file_path LIKE '%/types/%' THEN 'types'
|
|
96
|
+
ELSE NULL
|
|
97
|
+
END as dir_group,
|
|
98
|
+
COUNT(DISTINCT file_path) as file_cnt
|
|
99
|
+
FROM semantic_chunks
|
|
100
|
+
GROUP BY dir_group
|
|
101
|
+
HAVING dir_group IS NOT NULL
|
|
102
|
+
`).all();
|
|
103
|
+
for (const row of dirRows) {
|
|
104
|
+
if (row.file_cnt < 2) continue;
|
|
78
105
|
smartBundles.push({
|
|
79
|
-
name
|
|
80
|
-
purpose: row.
|
|
81
|
-
fileCount: row.
|
|
106
|
+
name: `smart:dir-${row.dir_group}`,
|
|
107
|
+
purpose: row.dir_group,
|
|
108
|
+
fileCount: row.file_cnt,
|
|
82
109
|
type: 'smart',
|
|
83
|
-
description:
|
|
110
|
+
description: `${row.file_cnt} files in ${row.dir_group} directories`
|
|
84
111
|
});
|
|
85
|
-
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// 3. Group by technical pattern (react-hooks, async-io, event-driven)
|
|
115
|
+
const patternCounts = new Map();
|
|
116
|
+
for (const row of allRows) {
|
|
117
|
+
try {
|
|
118
|
+
const meta = JSON.parse(row.metadata);
|
|
119
|
+
for (const pattern of (meta.technicalPatterns || [])) {
|
|
120
|
+
if (pattern === 'public-api') continue; // Too generic
|
|
121
|
+
patternCounts.set(pattern, (patternCounts.get(pattern) || 0) + 1);
|
|
122
|
+
}
|
|
123
|
+
} catch { /* skip */ }
|
|
124
|
+
}
|
|
125
|
+
for (const [pattern, count] of patternCounts) {
|
|
126
|
+
if (count < MIN_CHUNKS) continue;
|
|
127
|
+
smartBundles.push({
|
|
128
|
+
name: `smart:pattern-${pattern}`,
|
|
129
|
+
purpose: pattern,
|
|
130
|
+
fileCount: count,
|
|
131
|
+
type: 'smart',
|
|
132
|
+
description: `${count} chunks using ${pattern} patterns`
|
|
133
|
+
});
|
|
134
|
+
}
|
|
86
135
|
} catch (e) {
|
|
87
136
|
if (this.verbose) console.warn('Smart bundle discovery failed:', e.message);
|
|
88
137
|
}
|
|
@@ -113,15 +162,56 @@ export default class BundleManager {
|
|
|
113
162
|
const query = bundleName.replace('smart:', '');
|
|
114
163
|
let rows = [];
|
|
115
164
|
|
|
116
|
-
if (query.startsWith('
|
|
117
|
-
|
|
118
|
-
|
|
165
|
+
if (query.startsWith('dir-')) {
|
|
166
|
+
// Directory-based bundle
|
|
167
|
+
const dirGroup = query.replace('dir-', '');
|
|
168
|
+
const dirPatterns = {
|
|
169
|
+
'hooks': '%/hooks/%',
|
|
170
|
+
'components': '%/components/%',
|
|
171
|
+
'api-services': null, // handled below
|
|
172
|
+
'pages': null,
|
|
173
|
+
'state': null,
|
|
174
|
+
'lib-utils': null,
|
|
175
|
+
'types': '%/types/%'
|
|
176
|
+
};
|
|
177
|
+
if (dirGroup === 'api-services') {
|
|
178
|
+
rows = this.db.db.prepare("SELECT DISTINCT file_path FROM semantic_chunks WHERE file_path LIKE '%/api/%' OR file_path LIKE '%/services/%'").all();
|
|
179
|
+
} else if (dirGroup === 'pages') {
|
|
180
|
+
rows = this.db.db.prepare("SELECT DISTINCT file_path FROM semantic_chunks WHERE file_path LIKE '%/pages/%' OR file_path LIKE '%/routes/%'").all();
|
|
181
|
+
} else if (dirGroup === 'state') {
|
|
182
|
+
rows = this.db.db.prepare("SELECT DISTINCT file_path FROM semantic_chunks WHERE file_path LIKE '%/stores/%' OR file_path LIKE '%/store/%'").all();
|
|
183
|
+
} else if (dirGroup === 'lib-utils') {
|
|
184
|
+
rows = this.db.db.prepare("SELECT DISTINCT file_path FROM semantic_chunks WHERE file_path LIKE '%/lib/%' OR file_path LIKE '%/utils/%'").all();
|
|
185
|
+
} else if (dirPatterns[dirGroup]) {
|
|
186
|
+
rows = this.db.db.prepare('SELECT DISTINCT file_path FROM semantic_chunks WHERE file_path LIKE ?').all(dirPatterns[dirGroup]);
|
|
187
|
+
}
|
|
188
|
+
} else if (query.startsWith('pattern-')) {
|
|
189
|
+
// Technical pattern bundle — search metadata JSON
|
|
190
|
+
const pattern = query.replace('pattern-', '');
|
|
191
|
+
const allRows = this.db.db.prepare('SELECT DISTINCT file_path, metadata FROM semantic_chunks WHERE metadata IS NOT NULL').all();
|
|
192
|
+
const files = new Set();
|
|
193
|
+
for (const row of allRows) {
|
|
194
|
+
try {
|
|
195
|
+
const meta = JSON.parse(row.metadata);
|
|
196
|
+
if ((meta.technicalPatterns || []).includes(pattern)) {
|
|
197
|
+
files.add(row.file_path);
|
|
198
|
+
}
|
|
199
|
+
} catch { /* skip */ }
|
|
200
|
+
}
|
|
201
|
+
return Array.from(files);
|
|
119
202
|
} else {
|
|
120
|
-
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
|
|
203
|
+
// Business domain bundle — search metadata JSON
|
|
204
|
+
const allRows = this.db.db.prepare('SELECT DISTINCT file_path, metadata FROM semantic_chunks WHERE metadata IS NOT NULL').all();
|
|
205
|
+
const files = new Set();
|
|
206
|
+
for (const row of allRows) {
|
|
207
|
+
try {
|
|
208
|
+
const meta = JSON.parse(row.metadata);
|
|
209
|
+
if ((meta.businessDomain || []).includes(query)) {
|
|
210
|
+
files.add(row.file_path);
|
|
211
|
+
}
|
|
212
|
+
} catch { /* skip */ }
|
|
124
213
|
}
|
|
214
|
+
return Array.from(files);
|
|
125
215
|
}
|
|
126
216
|
return rows.map(r => r.file_path);
|
|
127
217
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cntx-ui",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "3.0.
|
|
4
|
+
"version": "3.0.5",
|
|
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",
|