cntx-ui 3.0.7 → 3.0.9
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/dist/bin/cntx-ui.js +70 -0
- package/dist/lib/agent-runtime.js +269 -0
- package/dist/lib/agent-tools.js +162 -0
- package/dist/lib/api-router.js +387 -0
- package/dist/lib/bundle-manager.js +236 -0
- package/dist/lib/configuration-manager.js +230 -0
- package/dist/lib/database-manager.js +277 -0
- package/dist/lib/file-system-manager.js +305 -0
- package/dist/lib/function-level-chunker.js +144 -0
- package/dist/lib/heuristics-manager.js +491 -0
- package/dist/lib/mcp-server.js +159 -0
- package/dist/lib/mcp-transport.js +10 -0
- package/dist/lib/semantic-splitter.js +335 -0
- package/dist/lib/simple-vector-store.js +98 -0
- package/dist/lib/treesitter-semantic-chunker.js +277 -0
- package/dist/lib/websocket-manager.js +268 -0
- package/dist/server.js +225 -0
- package/package.json +18 -8
- package/bin/cntx-ui-mcp.sh +0 -3
- package/bin/cntx-ui.js +0 -123
- package/lib/agent-runtime.js +0 -371
- package/lib/agent-tools.js +0 -370
- package/lib/api-router.js +0 -1026
- package/lib/bundle-manager.js +0 -326
- package/lib/configuration-manager.js +0 -760
- package/lib/database-manager.js +0 -397
- package/lib/file-system-manager.js +0 -489
- package/lib/function-level-chunker.js +0 -406
- package/lib/heuristics-manager.js +0 -529
- package/lib/mcp-server.js +0 -1380
- package/lib/mcp-transport.js +0 -97
- package/lib/semantic-splitter.js +0 -304
- package/lib/simple-vector-store.js +0 -108
- package/lib/treesitter-semantic-chunker.js +0 -1485
- package/lib/websocket-manager.js +0 -470
- package/server.js +0 -687
|
@@ -1,760 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Configuration Manager for cntx-ui
|
|
3
|
-
* Handles all configuration files, settings, and persistence
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { readFileSync, writeFileSync, existsSync, unlinkSync } from 'fs';
|
|
7
|
-
import { join, relative } from 'path';
|
|
8
|
-
import DatabaseManager from './database-manager.js';
|
|
9
|
-
|
|
10
|
-
export default class ConfigurationManager {
|
|
11
|
-
constructor(cwd = process.cwd(), options = {}) {
|
|
12
|
-
this.CWD = cwd;
|
|
13
|
-
this.CNTX_DIR = join(cwd, '.cntx');
|
|
14
|
-
this.verbose = options.verbose || false;
|
|
15
|
-
|
|
16
|
-
// Configuration files
|
|
17
|
-
this.CONFIG_FILE = join(this.CNTX_DIR, 'config.json');
|
|
18
|
-
this.BUNDLE_STATES_FILE = join(this.CNTX_DIR, 'bundle-states.json');
|
|
19
|
-
this.HIDDEN_FILES_CONFIG_FILE = join(this.CNTX_DIR, 'hidden-files.json');
|
|
20
|
-
this.IGNORE_FILE = join(this.CWD, '.cntxignore');
|
|
21
|
-
this.GITIGNORE_FILE = join(this.CWD, '.gitignore');
|
|
22
|
-
this.CURSOR_RULES_FILE = join(this.CWD, '.cursorrules');
|
|
23
|
-
this.CLAUDE_MD_FILE = join(this.CWD, 'CLAUDE.md');
|
|
24
|
-
this.HEURISTICS_CONFIG_FILE = join(this.CNTX_DIR, 'heuristics-config.json');
|
|
25
|
-
this.SEMANTIC_CACHE_FILE = join(this.CNTX_DIR, 'semantic-cache.json');
|
|
26
|
-
|
|
27
|
-
// Configuration data
|
|
28
|
-
this.config = {};
|
|
29
|
-
this.bundleStates = new Map();
|
|
30
|
-
this.hiddenFilesConfig = {
|
|
31
|
-
hiddenFiles: new Map(),
|
|
32
|
-
userIgnorePatterns: []
|
|
33
|
-
};
|
|
34
|
-
this.ignorePatterns = [];
|
|
35
|
-
|
|
36
|
-
// Initialize database manager
|
|
37
|
-
this.dbManager = new DatabaseManager(this.CNTX_DIR, { verbose: this.verbose });
|
|
38
|
-
|
|
39
|
-
// Load initial configuration
|
|
40
|
-
this.loadConfig();
|
|
41
|
-
|
|
42
|
-
// Load bundles (try SQLite first, fallback to JSON)
|
|
43
|
-
this.loadBundleStates();
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// === Bundle Configuration ===
|
|
47
|
-
|
|
48
|
-
loadConfig() {
|
|
49
|
-
if (existsSync(this.CONFIG_FILE)) {
|
|
50
|
-
const config = JSON.parse(readFileSync(this.CONFIG_FILE, 'utf8'));
|
|
51
|
-
|
|
52
|
-
// Load non-bundle settings only
|
|
53
|
-
this.editor = config.editor || 'code';
|
|
54
|
-
|
|
55
|
-
// Store full config for other non-bundle settings
|
|
56
|
-
this.config = config;
|
|
57
|
-
} else {
|
|
58
|
-
// Set defaults
|
|
59
|
-
this.editor = 'code';
|
|
60
|
-
this.config = {};
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// Bundle loading is now handled by loadBundleStates()
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
saveConfig(config) {
|
|
67
|
-
writeFileSync(this.CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// === Bundle States Persistence ===
|
|
71
|
-
|
|
72
|
-
loadBundleStates() {
|
|
73
|
-
// Try loading from SQLite first
|
|
74
|
-
try {
|
|
75
|
-
this.bundleStates = this.dbManager.loadBundles();
|
|
76
|
-
|
|
77
|
-
if (this.bundleStates.size > 0) {
|
|
78
|
-
if (this.verbose) {
|
|
79
|
-
console.log('✅ Loaded bundles from SQLite database');
|
|
80
|
-
}
|
|
81
|
-
return;
|
|
82
|
-
}
|
|
83
|
-
} catch (error) {
|
|
84
|
-
if (this.verbose) {
|
|
85
|
-
console.log('⚠️ SQLite load failed, trying JSON fallback:', error.message);
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// Fallback to JSON file
|
|
90
|
-
this.bundleStates.clear();
|
|
91
|
-
|
|
92
|
-
if (existsSync(this.BUNDLE_STATES_FILE)) {
|
|
93
|
-
try {
|
|
94
|
-
const bundleStates = JSON.parse(readFileSync(this.BUNDLE_STATES_FILE, 'utf8'));
|
|
95
|
-
|
|
96
|
-
bundleStates.forEach(state => {
|
|
97
|
-
this.bundleStates.set(state.name, {
|
|
98
|
-
patterns: state.patterns || [],
|
|
99
|
-
files: (state.files || []).map(file => {
|
|
100
|
-
if (file.startsWith('/')) {
|
|
101
|
-
const relativePath = relative(this.CWD, file);
|
|
102
|
-
return relativePath;
|
|
103
|
-
} else {
|
|
104
|
-
return file;
|
|
105
|
-
}
|
|
106
|
-
}),
|
|
107
|
-
content: state.content || '',
|
|
108
|
-
size: state.size || 0,
|
|
109
|
-
generated: state.generated || null,
|
|
110
|
-
changed: false
|
|
111
|
-
});
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
// Migrate JSON data to SQLite
|
|
115
|
-
this.dbManager.saveBundles(this.bundleStates);
|
|
116
|
-
if (this.verbose) {
|
|
117
|
-
console.log('📦 Migrated bundle data from JSON to SQLite');
|
|
118
|
-
}
|
|
119
|
-
} catch (error) {
|
|
120
|
-
console.error('Failed to load bundle states from JSON:', error.message);
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// Ensure 'master' bundle exists
|
|
125
|
-
if (!this.bundleStates.has('master')) {
|
|
126
|
-
this.bundleStates.set('master', {
|
|
127
|
-
patterns: ['**/*'],
|
|
128
|
-
files: [],
|
|
129
|
-
content: '',
|
|
130
|
-
changed: false,
|
|
131
|
-
size: 0,
|
|
132
|
-
generated: null
|
|
133
|
-
});
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
saveBundleStates() {
|
|
138
|
-
try {
|
|
139
|
-
// Save to SQLite database (primary)
|
|
140
|
-
this.dbManager.saveBundles(this.bundleStates);
|
|
141
|
-
|
|
142
|
-
// Also save to JSON for compatibility/backup
|
|
143
|
-
const bundleStates = Array.from(this.bundleStates.entries()).map(([name, bundle]) => ({
|
|
144
|
-
name,
|
|
145
|
-
contentPreview: bundle.content ? bundle.content.substring(0, 200) + '...' : '',
|
|
146
|
-
size: bundle.size,
|
|
147
|
-
fileCount: bundle.files ? bundle.files.length : 0,
|
|
148
|
-
generated: bundle.generated,
|
|
149
|
-
changed: bundle.changed,
|
|
150
|
-
patterns: bundle.patterns,
|
|
151
|
-
files: (bundle.files || []).map(file => {
|
|
152
|
-
if (file.startsWith('/')) {
|
|
153
|
-
return relative(this.CWD, file);
|
|
154
|
-
} else {
|
|
155
|
-
return file;
|
|
156
|
-
}
|
|
157
|
-
})
|
|
158
|
-
}));
|
|
159
|
-
|
|
160
|
-
writeFileSync(this.BUNDLE_STATES_FILE, JSON.stringify(bundleStates, null, 2));
|
|
161
|
-
if (this.verbose) {
|
|
162
|
-
console.log(`💾 Saved ${bundleStates.length} bundles to SQLite + JSON`);
|
|
163
|
-
}
|
|
164
|
-
} catch (error) {
|
|
165
|
-
if (this.verbose) {
|
|
166
|
-
console.warn('⚠️ Failed to save bundle states:', error.message);
|
|
167
|
-
}
|
|
168
|
-
// Try to save just the essential info
|
|
169
|
-
try {
|
|
170
|
-
const minimalStates = Array.from(this.bundleStates.entries()).map(([name, bundle]) => ({
|
|
171
|
-
name,
|
|
172
|
-
size: bundle.size || 0,
|
|
173
|
-
fileCount: bundle.files ? bundle.files.length : 0,
|
|
174
|
-
generated: bundle.generated || new Date().toISOString()
|
|
175
|
-
}));
|
|
176
|
-
writeFileSync(this.BUNDLE_STATES_FILE, JSON.stringify(minimalStates, null, 2));
|
|
177
|
-
if (this.verbose) {
|
|
178
|
-
console.log(`💾 Saved minimal bundle states for ${minimalStates.length} bundles`);
|
|
179
|
-
}
|
|
180
|
-
} catch (fallbackError) {
|
|
181
|
-
if (this.verbose) {
|
|
182
|
-
console.error('❌ Failed to save even minimal bundle states:', fallbackError.message);
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
// === Hidden Files Configuration ===
|
|
189
|
-
|
|
190
|
-
loadHiddenFilesConfig() {
|
|
191
|
-
if (existsSync(this.HIDDEN_FILES_CONFIG_FILE)) {
|
|
192
|
-
try {
|
|
193
|
-
this.hiddenFilesConfig = JSON.parse(readFileSync(this.HIDDEN_FILES_CONFIG_FILE, 'utf8'));
|
|
194
|
-
} catch (error) {
|
|
195
|
-
console.error('Failed to load hidden files config:', error.message);
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
saveHiddenFilesConfig() {
|
|
201
|
-
writeFileSync(this.HIDDEN_FILES_CONFIG_FILE, JSON.stringify(this.hiddenFilesConfig, null, 2));
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
isFileHidden(filePath, bundleName = null) {
|
|
205
|
-
// Check global hidden files
|
|
206
|
-
if (this.hiddenFilesConfig.globalHidden.includes(filePath)) {
|
|
207
|
-
return true;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
// Check bundle-specific hidden files
|
|
211
|
-
if (bundleName && this.hiddenFilesConfig.bundleSpecific[bundleName]?.includes(filePath)) {
|
|
212
|
-
return true;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
return false;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
toggleFileVisibility(filePath, bundleName = null, forceHide = null) {
|
|
219
|
-
if (bundleName) {
|
|
220
|
-
// Bundle-specific hiding
|
|
221
|
-
if (!this.hiddenFilesConfig.bundleSpecific[bundleName]) {
|
|
222
|
-
this.hiddenFilesConfig.bundleSpecific[bundleName] = [];
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
const bundleHidden = this.hiddenFilesConfig.bundleSpecific[bundleName];
|
|
226
|
-
const isCurrentlyHidden = bundleHidden.includes(filePath);
|
|
227
|
-
|
|
228
|
-
if (forceHide === true || (forceHide === null && !isCurrentlyHidden)) {
|
|
229
|
-
if (!isCurrentlyHidden) {
|
|
230
|
-
bundleHidden.push(filePath);
|
|
231
|
-
}
|
|
232
|
-
} else {
|
|
233
|
-
const index = bundleHidden.indexOf(filePath);
|
|
234
|
-
if (index > -1) {
|
|
235
|
-
bundleHidden.splice(index, 1);
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
} else {
|
|
239
|
-
// Global hiding
|
|
240
|
-
const isCurrentlyHidden = this.hiddenFilesConfig.globalHidden.includes(filePath);
|
|
241
|
-
|
|
242
|
-
if (forceHide === true || (forceHide === null && !isCurrentlyHidden)) {
|
|
243
|
-
if (!isCurrentlyHidden) {
|
|
244
|
-
this.hiddenFilesConfig.globalHidden.push(filePath);
|
|
245
|
-
}
|
|
246
|
-
} else {
|
|
247
|
-
const index = this.hiddenFilesConfig.globalHidden.indexOf(filePath);
|
|
248
|
-
if (index > -1) {
|
|
249
|
-
this.hiddenFilesConfig.globalHidden.splice(index, 1);
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
this.saveHiddenFilesConfig();
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
bulkToggleFileVisibility(filePaths, bundleName = null, forceHide = null) {
|
|
258
|
-
filePaths.forEach(filePath => {
|
|
259
|
-
this.toggleFileVisibility(filePath, bundleName, forceHide);
|
|
260
|
-
});
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
// === Ignore Patterns Management ===
|
|
264
|
-
|
|
265
|
-
addUserIgnorePattern(pattern) {
|
|
266
|
-
if (!this.hiddenFilesConfig.userIgnorePatterns.some(p => p.pattern === pattern)) {
|
|
267
|
-
this.hiddenFilesConfig.userIgnorePatterns.push({
|
|
268
|
-
pattern,
|
|
269
|
-
enabled: true,
|
|
270
|
-
addedAt: new Date().toISOString()
|
|
271
|
-
});
|
|
272
|
-
this.saveHiddenFilesConfig();
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
removeUserIgnorePattern(pattern) {
|
|
277
|
-
const index = this.hiddenFilesConfig.userIgnorePatterns.findIndex(p => p.pattern === pattern);
|
|
278
|
-
if (index > -1) {
|
|
279
|
-
this.hiddenFilesConfig.userIgnorePatterns.splice(index, 1);
|
|
280
|
-
this.saveHiddenFilesConfig();
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
toggleSystemIgnorePattern(pattern) {
|
|
285
|
-
const isDisabled = this.hiddenFilesConfig.disabledSystemPatterns.includes(pattern);
|
|
286
|
-
|
|
287
|
-
if (isDisabled) {
|
|
288
|
-
const index = this.hiddenFilesConfig.disabledSystemPatterns.indexOf(pattern);
|
|
289
|
-
this.hiddenFilesConfig.disabledSystemPatterns.splice(index, 1);
|
|
290
|
-
} else {
|
|
291
|
-
this.hiddenFilesConfig.disabledSystemPatterns.push(pattern);
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
this.saveHiddenFilesConfig();
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
loadIgnorePatterns() {
|
|
298
|
-
this.ignorePatterns = [];
|
|
299
|
-
|
|
300
|
-
// System patterns - common files to ignore
|
|
301
|
-
const systemPatterns = [
|
|
302
|
-
'node_modules/**/*', '.git/**/*', '.svn/**/*', '.hg/**/*',
|
|
303
|
-
'*.log', '*.tmp', '*.temp', '*.cache', '*.pid',
|
|
304
|
-
'.DS_Store', 'Thumbs.db', '.env', '.env.*',
|
|
305
|
-
'dist/**/*', 'build/**/*', 'coverage/**/*',
|
|
306
|
-
'*.min.js', '*.min.css', '*.map',
|
|
307
|
-
'**/*.lock', 'yarn.lock', 'package-lock.json',
|
|
308
|
-
'.vscode/**/*', '.idea/**/*', '*.swp', '*.swo',
|
|
309
|
-
'__pycache__/**/*', '*.pyc', '*.pyo',
|
|
310
|
-
'.pytest_cache/**/*', '.coverage',
|
|
311
|
-
'target/**/*', '*.class', '*.jar',
|
|
312
|
-
'bin/**/*', 'obj/**/*', '*.exe', '*.dll',
|
|
313
|
-
'.next/**/*', '.nuxt/**/*', '.vite/**/*',
|
|
314
|
-
'public/build/**/*', 'static/build/**/*'
|
|
315
|
-
];
|
|
316
|
-
|
|
317
|
-
// Add all system patterns (always enabled now)
|
|
318
|
-
this.ignorePatterns.push(...systemPatterns);
|
|
319
|
-
|
|
320
|
-
// File-based patterns (.cntxignore)
|
|
321
|
-
if (existsSync(this.IGNORE_FILE)) {
|
|
322
|
-
try {
|
|
323
|
-
const content = readFileSync(this.IGNORE_FILE, 'utf8');
|
|
324
|
-
content.split('\n')
|
|
325
|
-
.map(line => line.trim())
|
|
326
|
-
.filter(line => line && !line.startsWith('#'))
|
|
327
|
-
.forEach(pattern => this.ignorePatterns.push(pattern));
|
|
328
|
-
} catch (error) {
|
|
329
|
-
console.error('Failed to load .cntxignore:', error.message);
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
if (this.verbose) {
|
|
334
|
-
console.log(`🚫 Loaded ${this.ignorePatterns.length} ignore patterns`);
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
updateIgnoreFile() {
|
|
339
|
-
const header = '# cntx-ui ignore patterns\n# This file is auto-generated. User patterns are managed via the UI.\n\n';
|
|
340
|
-
const userPatterns = this.hiddenFilesConfig.userIgnorePatterns
|
|
341
|
-
.filter(p => p.enabled)
|
|
342
|
-
.map(p => p.pattern)
|
|
343
|
-
.join('\n');
|
|
344
|
-
|
|
345
|
-
writeFileSync(this.IGNORE_FILE, header + userPatterns);
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
loadCntxignore() {
|
|
349
|
-
if (existsSync(this.IGNORE_FILE)) {
|
|
350
|
-
return readFileSync(this.IGNORE_FILE, 'utf8');
|
|
351
|
-
}
|
|
352
|
-
return '';
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
loadGitignore() {
|
|
356
|
-
if (existsSync(this.GITIGNORE_FILE)) {
|
|
357
|
-
return readFileSync(this.GITIGNORE_FILE, 'utf8');
|
|
358
|
-
}
|
|
359
|
-
return '';
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
saveGitignore(content) {
|
|
363
|
-
writeFileSync(this.GITIGNORE_FILE, content);
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
// === Cursor Rules Management ===
|
|
367
|
-
|
|
368
|
-
loadCursorRules() {
|
|
369
|
-
if (existsSync(this.CURSOR_RULES_FILE)) {
|
|
370
|
-
return readFileSync(this.CURSOR_RULES_FILE, 'utf8');
|
|
371
|
-
}
|
|
372
|
-
return this.getDefaultCursorRules();
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
saveCursorRules(content) {
|
|
376
|
-
writeFileSync(this.CURSOR_RULES_FILE, content);
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
getDefaultCursorRules() {
|
|
380
|
-
let pkg = {};
|
|
381
|
-
try {
|
|
382
|
-
const packagePath = join(this.CWD, 'package.json');
|
|
383
|
-
if (existsSync(packagePath)) {
|
|
384
|
-
pkg = JSON.parse(readFileSync(packagePath, 'utf8'));
|
|
385
|
-
}
|
|
386
|
-
} catch (error) {
|
|
387
|
-
console.error('Failed to read package.json:', error.message);
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
const projectType = this.detectProjectType(pkg);
|
|
391
|
-
return this.generateCursorRulesTemplate({ projectType, name: pkg.name });
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
detectProjectType(pkg) {
|
|
395
|
-
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
396
|
-
|
|
397
|
-
if (deps.react || deps['@types/react']) return 'react';
|
|
398
|
-
if (deps.vue || deps['@vue/cli']) return 'vue';
|
|
399
|
-
if (deps.angular || deps['@angular/core']) return 'angular';
|
|
400
|
-
if (deps.next || deps['next']) return 'next';
|
|
401
|
-
if (deps.express || deps.fastify) return 'node-backend';
|
|
402
|
-
|
|
403
|
-
return 'javascript';
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
generateCursorRulesTemplate(projectInfo) {
|
|
407
|
-
const { projectType, name } = projectInfo;
|
|
408
|
-
|
|
409
|
-
const baseRules = `# Cursor Rules for ${name || 'Project'}
|
|
410
|
-
|
|
411
|
-
## Project Overview
|
|
412
|
-
This is a ${projectType} project. Please follow these guidelines when making changes:
|
|
413
|
-
|
|
414
|
-
## Code Style
|
|
415
|
-
- Use consistent indentation (2 spaces for JS/TS, 4 for Python)
|
|
416
|
-
- Follow existing naming conventions
|
|
417
|
-
- Add comments for complex logic
|
|
418
|
-
- Keep functions small and focused
|
|
419
|
-
|
|
420
|
-
## Architecture
|
|
421
|
-
- Maintain separation of concerns
|
|
422
|
-
- Follow established patterns in the codebase
|
|
423
|
-
- Consider performance implications
|
|
424
|
-
- Ensure code is testable
|
|
425
|
-
|
|
426
|
-
## Testing
|
|
427
|
-
- Write tests for new functionality
|
|
428
|
-
- Update existing tests when modifying code
|
|
429
|
-
- Aim for good test coverage
|
|
430
|
-
- Use descriptive test names`;
|
|
431
|
-
|
|
432
|
-
const typeSpecificRules = {
|
|
433
|
-
react: `
|
|
434
|
-
## React Specific
|
|
435
|
-
- Use functional components with hooks
|
|
436
|
-
- Follow component composition patterns
|
|
437
|
-
- Keep components focused on single responsibility
|
|
438
|
-
- Use TypeScript for type safety
|
|
439
|
-
- Handle loading and error states properly`,
|
|
440
|
-
|
|
441
|
-
vue: `
|
|
442
|
-
## Vue Specific
|
|
443
|
-
- Use Composition API for new components
|
|
444
|
-
- Follow single-file component structure
|
|
445
|
-
- Use reactive refs appropriately
|
|
446
|
-
- Implement proper event handling
|
|
447
|
-
- Follow Vue style guide conventions`,
|
|
448
|
-
|
|
449
|
-
angular: `
|
|
450
|
-
## Angular Specific
|
|
451
|
-
- Follow Angular style guide
|
|
452
|
-
- Use dependency injection properly
|
|
453
|
-
- Implement proper lifecycle hooks
|
|
454
|
-
- Use reactive forms for complex forms
|
|
455
|
-
- Follow module organization patterns`,
|
|
456
|
-
|
|
457
|
-
'node-backend': `
|
|
458
|
-
## Backend Specific
|
|
459
|
-
- Implement proper error handling
|
|
460
|
-
- Use middleware for common functionality
|
|
461
|
-
- Validate input data
|
|
462
|
-
- Follow REST API conventions
|
|
463
|
-
- Implement proper logging`,
|
|
464
|
-
|
|
465
|
-
javascript: `
|
|
466
|
-
## JavaScript Specific
|
|
467
|
-
- Use modern ES6+ features appropriately
|
|
468
|
-
- Handle promises and async operations properly
|
|
469
|
-
- Implement error handling
|
|
470
|
-
- Follow functional programming principles where applicable
|
|
471
|
-
- Use appropriate data structures`
|
|
472
|
-
};
|
|
473
|
-
|
|
474
|
-
return baseRules + (typeSpecificRules[projectType] || typeSpecificRules.javascript) + `
|
|
475
|
-
|
|
476
|
-
## File Organization
|
|
477
|
-
- Keep related files together
|
|
478
|
-
- Use descriptive file names
|
|
479
|
-
- Maintain consistent directory structure
|
|
480
|
-
- Avoid deeply nested directories
|
|
481
|
-
|
|
482
|
-
## Documentation
|
|
483
|
-
- Update README when adding features
|
|
484
|
-
- Document complex algorithms
|
|
485
|
-
- Keep inline comments current
|
|
486
|
-
- Use JSDoc for function documentation`;
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
// === Claude.md Management ===
|
|
490
|
-
|
|
491
|
-
loadClaudeMd() {
|
|
492
|
-
if (existsSync(this.CLAUDE_MD_FILE)) {
|
|
493
|
-
return readFileSync(this.CLAUDE_MD_FILE, 'utf8');
|
|
494
|
-
}
|
|
495
|
-
return this.getDefaultClaudeMd();
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
saveClaudeMd(content) {
|
|
499
|
-
writeFileSync(this.CLAUDE_MD_FILE, content);
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
getDefaultClaudeMd() {
|
|
503
|
-
let projectInfo = {};
|
|
504
|
-
try {
|
|
505
|
-
const packagePath = join(this.CWD, 'package.json');
|
|
506
|
-
if (existsSync(packagePath)) {
|
|
507
|
-
const pkg = JSON.parse(readFileSync(packagePath, 'utf8'));
|
|
508
|
-
projectInfo = {
|
|
509
|
-
name: pkg.name,
|
|
510
|
-
description: pkg.description,
|
|
511
|
-
projectType: this.detectProjectType(pkg)
|
|
512
|
-
};
|
|
513
|
-
}
|
|
514
|
-
} catch (error) {
|
|
515
|
-
projectInfo = { name: 'Project', description: '', projectType: 'javascript' };
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
return this.generateClaudeMdTemplate(projectInfo);
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
generateClaudeMdTemplate(projectInfo) {
|
|
522
|
-
const { name, description, projectType } = projectInfo;
|
|
523
|
-
|
|
524
|
-
return `# ${name || 'Project'} - Claude Context
|
|
525
|
-
|
|
526
|
-
## Project Overview
|
|
527
|
-
${description || 'A ' + projectType + ' project'}
|
|
528
|
-
|
|
529
|
-
## Architecture
|
|
530
|
-
Please describe your project architecture here:
|
|
531
|
-
- Main components/modules
|
|
532
|
-
- Data flow patterns
|
|
533
|
-
- Key dependencies
|
|
534
|
-
- Design decisions
|
|
535
|
-
|
|
536
|
-
## Development Guidelines
|
|
537
|
-
- Code style preferences
|
|
538
|
-
- Testing approach
|
|
539
|
-
- Deployment process
|
|
540
|
-
- Known limitations or considerations
|
|
541
|
-
|
|
542
|
-
## Context for AI
|
|
543
|
-
When working on this project, please:
|
|
544
|
-
- Follow existing patterns and conventions
|
|
545
|
-
- Consider the overall architecture
|
|
546
|
-
- Maintain code quality and consistency
|
|
547
|
-
- Ask for clarification on complex requirements
|
|
548
|
-
|
|
549
|
-
## Current Focus
|
|
550
|
-
<!-- Update this section with current development priorities -->
|
|
551
|
-
- Feature development
|
|
552
|
-
- Bug fixes
|
|
553
|
-
- Performance improvements
|
|
554
|
-
- Documentation updates`;
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
// === Heuristics Configuration ===
|
|
558
|
-
|
|
559
|
-
loadHeuristicsConfig() {
|
|
560
|
-
if (existsSync(this.HEURISTICS_CONFIG_FILE)) {
|
|
561
|
-
try {
|
|
562
|
-
return JSON.parse(readFileSync(this.HEURISTICS_CONFIG_FILE, 'utf8'));
|
|
563
|
-
} catch (error) {
|
|
564
|
-
console.error('Failed to load heuristics config:', error.message);
|
|
565
|
-
}
|
|
566
|
-
}
|
|
567
|
-
return this.getDefaultHeuristicsConfig();
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
saveHeuristicsConfig(config) {
|
|
571
|
-
writeFileSync(this.HEURISTICS_CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
getDefaultHeuristicsConfig() {
|
|
575
|
-
return {
|
|
576
|
-
bundlePurposePatterns: {
|
|
577
|
-
'frontend': {
|
|
578
|
-
patterns: ['src/components/**', 'src/pages/**', 'src/views/**', 'public/**', 'assets/**', 'styles/**'],
|
|
579
|
-
description: 'User interface components and styling'
|
|
580
|
-
},
|
|
581
|
-
'backend': {
|
|
582
|
-
patterns: ['src/api/**', 'src/server/**', 'src/routes/**', 'src/controllers/**', 'src/middleware/**'],
|
|
583
|
-
description: 'Server-side logic and API endpoints'
|
|
584
|
-
},
|
|
585
|
-
'database': {
|
|
586
|
-
patterns: ['src/models/**', 'src/schemas/**', 'src/migrations/**', 'prisma/**', 'db/**'],
|
|
587
|
-
description: 'Database models and migrations'
|
|
588
|
-
},
|
|
589
|
-
'utilities': {
|
|
590
|
-
patterns: ['src/utils/**', 'src/helpers/**', 'src/lib/**', 'lib/**'],
|
|
591
|
-
description: 'Utility functions and shared libraries'
|
|
592
|
-
},
|
|
593
|
-
'configuration': {
|
|
594
|
-
patterns: ['config/**', '*.config.js', '*.config.ts', '.env*', 'docker*', 'webpack*'],
|
|
595
|
-
description: 'Configuration files and environment setup'
|
|
596
|
-
},
|
|
597
|
-
'testing': {
|
|
598
|
-
patterns: ['test/**', 'tests/**', 'spec/**', '**/*.test.*', '**/*.spec.*', '__tests__/**'],
|
|
599
|
-
description: 'Test files and testing utilities'
|
|
600
|
-
},
|
|
601
|
-
'documentation': {
|
|
602
|
-
patterns: ['docs/**', '*.md', 'README*', 'CHANGELOG*', 'LICENSE*'],
|
|
603
|
-
description: 'Documentation and readme files'
|
|
604
|
-
},
|
|
605
|
-
'build': {
|
|
606
|
-
patterns: ['build/**', 'dist/**', 'out/**', 'target/**', 'bin/**'],
|
|
607
|
-
description: 'Built/compiled output files'
|
|
608
|
-
}
|
|
609
|
-
},
|
|
610
|
-
fileTypeHeuristics: {
|
|
611
|
-
'javascript': {
|
|
612
|
-
extensions: ['.js', '.jsx', '.mjs', '.cjs'],
|
|
613
|
-
role: 'Implementation files containing business logic'
|
|
614
|
-
},
|
|
615
|
-
'typescript': {
|
|
616
|
-
extensions: ['.ts', '.tsx'],
|
|
617
|
-
role: 'Type-safe implementation files'
|
|
618
|
-
},
|
|
619
|
-
'configuration': {
|
|
620
|
-
extensions: ['.json', '.yaml', '.yml', '.toml', '.ini'],
|
|
621
|
-
role: 'Configuration and data files'
|
|
622
|
-
},
|
|
623
|
-
'styling': {
|
|
624
|
-
extensions: ['.css', '.scss', '.sass', '.less', '.styl'],
|
|
625
|
-
role: 'Styling and presentation files'
|
|
626
|
-
},
|
|
627
|
-
'markup': {
|
|
628
|
-
extensions: ['.html', '.htm', '.xml', '.svg'],
|
|
629
|
-
role: 'Markup and template files'
|
|
630
|
-
},
|
|
631
|
-
'documentation': {
|
|
632
|
-
extensions: ['.md', '.txt', '.rst'],
|
|
633
|
-
role: 'Documentation and text files'
|
|
634
|
-
}
|
|
635
|
-
}
|
|
636
|
-
};
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
// === Semantic Cache Management ===
|
|
640
|
-
|
|
641
|
-
loadSemanticCache() {
|
|
642
|
-
try {
|
|
643
|
-
if (!existsSync(this.SEMANTIC_CACHE_FILE)) {
|
|
644
|
-
console.log('🔍 No semantic cache file found');
|
|
645
|
-
return null;
|
|
646
|
-
}
|
|
647
|
-
|
|
648
|
-
const cacheData = JSON.parse(readFileSync(this.SEMANTIC_CACHE_FILE, 'utf8'));
|
|
649
|
-
|
|
650
|
-
// Simple cache validation - could be enhanced with file modification time checks
|
|
651
|
-
const cacheAge = Date.now() - cacheData.timestamp;
|
|
652
|
-
const maxAge = 24 * 60 * 60 * 1000; // 24 hours
|
|
653
|
-
|
|
654
|
-
if (cacheAge > maxAge) {
|
|
655
|
-
console.log('🔍 Semantic cache is too old, will regenerate');
|
|
656
|
-
return null;
|
|
657
|
-
}
|
|
658
|
-
|
|
659
|
-
// Validate that chunks have embeddings
|
|
660
|
-
const hasEmbeddings = cacheData.analysis?.chunks?.some(chunk => chunk.embedding);
|
|
661
|
-
if (!hasEmbeddings) {
|
|
662
|
-
console.log('🔍 Cached chunks missing embeddings, will regenerate');
|
|
663
|
-
return null;
|
|
664
|
-
}
|
|
665
|
-
|
|
666
|
-
if (this.verbose) {
|
|
667
|
-
console.log(`✅ Loaded semantic cache from disk (${cacheData.analysis.chunks.length} chunks with embeddings)`);
|
|
668
|
-
}
|
|
669
|
-
return { analysis: cacheData.analysis, timestamp: cacheData.timestamp };
|
|
670
|
-
} catch (error) {
|
|
671
|
-
console.error('❌ Failed to load semantic cache:', error.message);
|
|
672
|
-
return null;
|
|
673
|
-
}
|
|
674
|
-
}
|
|
675
|
-
|
|
676
|
-
saveSemanticCache(analysis) {
|
|
677
|
-
try {
|
|
678
|
-
const cacheData = {
|
|
679
|
-
timestamp: Date.now(),
|
|
680
|
-
analysis: analysis,
|
|
681
|
-
version: '1.0'
|
|
682
|
-
};
|
|
683
|
-
|
|
684
|
-
writeFileSync(this.SEMANTIC_CACHE_FILE, JSON.stringify(cacheData, null, 2));
|
|
685
|
-
console.log('💾 Saved semantic cache with embeddings to disk');
|
|
686
|
-
} catch (error) {
|
|
687
|
-
console.error('❌ Failed to save semantic cache:', error.message);
|
|
688
|
-
}
|
|
689
|
-
}
|
|
690
|
-
|
|
691
|
-
invalidateSemanticCache() {
|
|
692
|
-
try {
|
|
693
|
-
if (existsSync(this.SEMANTIC_CACHE_FILE)) {
|
|
694
|
-
unlinkSync(this.SEMANTIC_CACHE_FILE);
|
|
695
|
-
console.log('🗑️ Cleared semantic cache file');
|
|
696
|
-
}
|
|
697
|
-
} catch (error) {
|
|
698
|
-
console.error('❌ Failed to clear cache file:', error.message);
|
|
699
|
-
}
|
|
700
|
-
}
|
|
701
|
-
|
|
702
|
-
// === Bundle Creation from Chunks ===
|
|
703
|
-
|
|
704
|
-
createBundleFromChunk(chunkName, files) {
|
|
705
|
-
const bundleName = `chunk-${chunkName.toLowerCase().replace(/[^a-z0-9]/g, '-')}`;
|
|
706
|
-
|
|
707
|
-
// Create bundle configuration
|
|
708
|
-
const patterns = files.map(file => file.replace(/\\/g, '/'));
|
|
709
|
-
|
|
710
|
-
this.bundleStates.set(bundleName, {
|
|
711
|
-
patterns: patterns,
|
|
712
|
-
files: [],
|
|
713
|
-
content: '',
|
|
714
|
-
changed: false,
|
|
715
|
-
size: 0,
|
|
716
|
-
generated: null
|
|
717
|
-
});
|
|
718
|
-
|
|
719
|
-
// Save to bundle-states.json (single source of truth)
|
|
720
|
-
this.saveBundleStates();
|
|
721
|
-
|
|
722
|
-
return bundleName;
|
|
723
|
-
}
|
|
724
|
-
|
|
725
|
-
// === Getters ===
|
|
726
|
-
|
|
727
|
-
// === Ignore File Management ===
|
|
728
|
-
|
|
729
|
-
saveCntxignore(content) {
|
|
730
|
-
try {
|
|
731
|
-
writeFileSync(this.IGNORE_FILE, content, 'utf8');
|
|
732
|
-
|
|
733
|
-
// Update ignore patterns in memory immediately
|
|
734
|
-
this.ignorePatterns = content.split('\n')
|
|
735
|
-
.map(line => line.trim())
|
|
736
|
-
.filter(line => line && !line.startsWith('#'));
|
|
737
|
-
|
|
738
|
-
return true;
|
|
739
|
-
} catch (error) {
|
|
740
|
-
console.error('Failed to save .cntxignore:', error.message);
|
|
741
|
-
return false;
|
|
742
|
-
}
|
|
743
|
-
}
|
|
744
|
-
|
|
745
|
-
getBundles() {
|
|
746
|
-
return this.bundleStates;
|
|
747
|
-
}
|
|
748
|
-
|
|
749
|
-
getIgnorePatterns() {
|
|
750
|
-
return this.ignorePatterns;
|
|
751
|
-
}
|
|
752
|
-
|
|
753
|
-
getHiddenFilesConfig() {
|
|
754
|
-
return this.hiddenFilesConfig;
|
|
755
|
-
}
|
|
756
|
-
|
|
757
|
-
getEditor() {
|
|
758
|
-
return this.editor;
|
|
759
|
-
}
|
|
760
|
-
}
|