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.
Files changed (2) hide show
  1. package/lib/bundle-manager.js +118 -28
  2. package/package.json +1 -1
@@ -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 Purpose (Heuristics)
60
- const purposeRows = this.db.db.prepare('SELECT DISTINCT purpose, COUNT(*) as count FROM semantic_chunks GROUP BY purpose').all();
61
- purposeRows.forEach(row => {
62
- if (!row.purpose) return;
63
- const name = `smart:${row.purpose.toLowerCase().replace(/\s+/g, '-')}`;
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: row.purpose,
67
- fileCount: row.count,
77
+ name: `smart:${domain}`,
78
+ purpose: domain,
79
+ fileCount: count,
68
80
  type: 'smart',
69
- description: `Automatically grouped by purpose: ${row.purpose}`
81
+ description: `${count} chunks in the ${domain} domain`
70
82
  });
71
- });
83
+ }
72
84
 
73
- // 2. Group by Component Types (Subtypes)
74
- const subtypeRows = this.db.db.prepare('SELECT DISTINCT subtype, COUNT(*) as count FROM semantic_chunks GROUP BY subtype').all();
75
- subtypeRows.forEach(row => {
76
- if (!row.subtype) return;
77
- const name = `smart:type-${row.subtype.toLowerCase().replace(/_/g, '-')}`;
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.subtype,
81
- fileCount: row.count,
106
+ name: `smart:dir-${row.dir_group}`,
107
+ purpose: row.dir_group,
108
+ fileCount: row.file_cnt,
82
109
  type: 'smart',
83
- description: `All ${row.subtype} elements across the codebase`
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('type-')) {
117
- const type = query.replace('type-', '').replace(/-/g, '_');
118
- rows = this.db.db.prepare('SELECT DISTINCT file_path FROM semantic_chunks WHERE LOWER(subtype) = ?').all(type);
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
- const purposeRows = this.db.db.prepare('SELECT DISTINCT purpose FROM semantic_chunks').all();
121
- const matched = purposeRows.find(r => r.purpose?.toLowerCase().replace(/\s+/g, '-') === query);
122
- if (matched) {
123
- rows = this.db.db.prepare('SELECT DISTINCT file_path FROM semantic_chunks WHERE purpose = ?').all(matched.purpose);
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",
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",