@loxia-labs/loxia-autopilot-one 1.0.1
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/LICENSE +267 -0
- package/README.md +509 -0
- package/bin/cli.js +117 -0
- package/package.json +94 -0
- package/scripts/install-scanners.js +236 -0
- package/src/analyzers/CSSAnalyzer.js +297 -0
- package/src/analyzers/ConfigValidator.js +690 -0
- package/src/analyzers/ESLintAnalyzer.js +320 -0
- package/src/analyzers/JavaScriptAnalyzer.js +261 -0
- package/src/analyzers/PrettierFormatter.js +247 -0
- package/src/analyzers/PythonAnalyzer.js +266 -0
- package/src/analyzers/SecurityAnalyzer.js +729 -0
- package/src/analyzers/TypeScriptAnalyzer.js +247 -0
- package/src/analyzers/codeCloneDetector/analyzer.js +344 -0
- package/src/analyzers/codeCloneDetector/detector.js +203 -0
- package/src/analyzers/codeCloneDetector/index.js +160 -0
- package/src/analyzers/codeCloneDetector/parser.js +199 -0
- package/src/analyzers/codeCloneDetector/reporter.js +148 -0
- package/src/analyzers/codeCloneDetector/scanner.js +59 -0
- package/src/core/agentPool.js +1474 -0
- package/src/core/agentScheduler.js +2147 -0
- package/src/core/contextManager.js +709 -0
- package/src/core/messageProcessor.js +732 -0
- package/src/core/orchestrator.js +548 -0
- package/src/core/stateManager.js +877 -0
- package/src/index.js +631 -0
- package/src/interfaces/cli.js +549 -0
- package/src/interfaces/webServer.js +2162 -0
- package/src/modules/fileExplorer/controller.js +280 -0
- package/src/modules/fileExplorer/index.js +37 -0
- package/src/modules/fileExplorer/middleware.js +92 -0
- package/src/modules/fileExplorer/routes.js +125 -0
- package/src/modules/fileExplorer/types.js +44 -0
- package/src/services/aiService.js +1232 -0
- package/src/services/apiKeyManager.js +164 -0
- package/src/services/benchmarkService.js +366 -0
- package/src/services/budgetService.js +539 -0
- package/src/services/contextInjectionService.js +247 -0
- package/src/services/conversationCompactionService.js +637 -0
- package/src/services/errorHandler.js +810 -0
- package/src/services/fileAttachmentService.js +544 -0
- package/src/services/modelRouterService.js +366 -0
- package/src/services/modelsService.js +322 -0
- package/src/services/qualityInspector.js +796 -0
- package/src/services/tokenCountingService.js +536 -0
- package/src/tools/agentCommunicationTool.js +1344 -0
- package/src/tools/agentDelayTool.js +485 -0
- package/src/tools/asyncToolManager.js +604 -0
- package/src/tools/baseTool.js +800 -0
- package/src/tools/browserTool.js +920 -0
- package/src/tools/cloneDetectionTool.js +621 -0
- package/src/tools/dependencyResolverTool.js +1215 -0
- package/src/tools/fileContentReplaceTool.js +875 -0
- package/src/tools/fileSystemTool.js +1107 -0
- package/src/tools/fileTreeTool.js +853 -0
- package/src/tools/imageTool.js +901 -0
- package/src/tools/importAnalyzerTool.js +1060 -0
- package/src/tools/jobDoneTool.js +248 -0
- package/src/tools/seekTool.js +956 -0
- package/src/tools/staticAnalysisTool.js +1778 -0
- package/src/tools/taskManagerTool.js +2873 -0
- package/src/tools/terminalTool.js +2304 -0
- package/src/tools/webTool.js +1430 -0
- package/src/types/agent.js +519 -0
- package/src/types/contextReference.js +972 -0
- package/src/types/conversation.js +730 -0
- package/src/types/toolCommand.js +747 -0
- package/src/utilities/attachmentValidator.js +292 -0
- package/src/utilities/configManager.js +582 -0
- package/src/utilities/constants.js +722 -0
- package/src/utilities/directoryAccessManager.js +535 -0
- package/src/utilities/fileProcessor.js +307 -0
- package/src/utilities/logger.js +436 -0
- package/src/utilities/tagParser.js +1246 -0
- package/src/utilities/toolConstants.js +317 -0
- package/web-ui/build/index.html +15 -0
- package/web-ui/build/logo.png +0 -0
- package/web-ui/build/logo2.png +0 -0
- package/web-ui/build/static/index-CjkkcnFA.js +344 -0
- package/web-ui/build/static/index-Dy2bYbOa.css +1 -0
|
@@ -0,0 +1,972 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context Reference Data Model - Type definitions and validation for context references
|
|
3
|
+
*
|
|
4
|
+
* Purpose:
|
|
5
|
+
* - Define the structure and properties of context references
|
|
6
|
+
* - Provide validation functions for context reference data
|
|
7
|
+
* - Handle context reference resolution and management
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { CONTEXT_REFERENCE_TYPES, FILE_EXTENSIONS, CONTEXT_ICONS } from '../utilities/constants.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Context Reference data model
|
|
14
|
+
* @typedef {Object} ContextReference
|
|
15
|
+
* @property {string} id - Unique reference identifier
|
|
16
|
+
* @property {string} type - Reference type (file, component, selection, directory)
|
|
17
|
+
* @property {string} path - Path or identifier of referenced item
|
|
18
|
+
* @property {string} name - Human-readable name
|
|
19
|
+
* @property {string} [content] - Referenced content (if loaded)
|
|
20
|
+
* @property {ReferenceMetadata} metadata - Reference metadata
|
|
21
|
+
* @property {ReferenceScope} scope - Reference scope and boundaries
|
|
22
|
+
* @property {string} createdAt - ISO timestamp of creation
|
|
23
|
+
* @property {string} [lastAccessed] - ISO timestamp of last access
|
|
24
|
+
* @property {string} [lastModified] - ISO timestamp of last modification
|
|
25
|
+
* @property {boolean} isValid - Whether reference is still valid
|
|
26
|
+
* @property {string} [invalidReason] - Reason for invalidity
|
|
27
|
+
* @property {number} accessCount - Number of times accessed
|
|
28
|
+
* @property {Object} tags - Reference tags and labels
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Reference metadata
|
|
33
|
+
* @typedef {Object} ReferenceMetadata
|
|
34
|
+
* @property {string} [language] - Programming language (for code files)
|
|
35
|
+
* @property {string} [encoding] - File encoding
|
|
36
|
+
* @property {number} [size] - Content size in bytes
|
|
37
|
+
* @property {string} [mimeType] - MIME type
|
|
38
|
+
* @property {string} [checksum] - Content checksum for integrity
|
|
39
|
+
* @property {string[]} [keywords] - Extracted keywords
|
|
40
|
+
* @property {string} [description] - Reference description
|
|
41
|
+
* @property {Object} [customFields] - Custom metadata fields
|
|
42
|
+
* @property {string} [icon] - Display icon identifier
|
|
43
|
+
* @property {Object} [permissions] - Access permissions
|
|
44
|
+
*/
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Reference scope and boundaries
|
|
48
|
+
* @typedef {Object} ReferenceScope
|
|
49
|
+
* @property {number} [startLine] - Start line number (for selections)
|
|
50
|
+
* @property {number} [endLine] - End line number (for selections)
|
|
51
|
+
* @property {number} [startColumn] - Start column number
|
|
52
|
+
* @property {number} [endColumn] - End column number
|
|
53
|
+
* @property {string} [functionName] - Function/method name (for code)
|
|
54
|
+
* @property {string} [className] - Class name (for code)
|
|
55
|
+
* @property {string} [namespace] - Namespace or module (for code)
|
|
56
|
+
* @property {string[]} [includePaths] - Included sub-paths (for directories)
|
|
57
|
+
* @property {string[]} [excludePaths] - Excluded sub-paths (for directories)
|
|
58
|
+
* @property {number} [maxDepth] - Maximum directory depth
|
|
59
|
+
*/
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* File Reference (extends ContextReference)
|
|
63
|
+
* @typedef {Object} FileReference
|
|
64
|
+
* @property {string} absolutePath - Absolute file path
|
|
65
|
+
* @property {string} relativePath - Relative file path from workspace
|
|
66
|
+
* @property {string} extension - File extension
|
|
67
|
+
* @property {boolean} exists - Whether file exists on filesystem
|
|
68
|
+
* @property {FileStats} [stats] - File system statistics
|
|
69
|
+
* @property {string} [gitStatus] - Git status of file
|
|
70
|
+
* @property {DependencyInfo} [dependencies] - File dependencies
|
|
71
|
+
*/
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Directory Reference (extends ContextReference)
|
|
75
|
+
* @typedef {Object} DirectoryReference
|
|
76
|
+
* @property {string} absolutePath - Absolute directory path
|
|
77
|
+
* @property {string} relativePath - Relative directory path from workspace
|
|
78
|
+
* @property {FileSystemTree} [tree] - Directory tree structure
|
|
79
|
+
* @property {number} [fileCount] - Number of files in directory
|
|
80
|
+
* @property {number} [totalSize] - Total size of directory contents
|
|
81
|
+
* @property {string[]} [fileTypes] - File types present in directory
|
|
82
|
+
*/
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Selection Reference (extends ContextReference)
|
|
86
|
+
* @typedef {Object} SelectionReference
|
|
87
|
+
* @property {string} sourceFile - Source file path
|
|
88
|
+
* @property {string} selectedText - Selected text content
|
|
89
|
+
* @property {SyntaxInfo} [syntax] - Syntax information
|
|
90
|
+
* @property {ContextInfo} [context] - Surrounding context
|
|
91
|
+
* @property {string} [purpose] - Purpose of selection
|
|
92
|
+
*/
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Component Reference (extends ContextReference)
|
|
96
|
+
* @typedef {Object} ComponentReference
|
|
97
|
+
* @property {string} componentType - Type of component
|
|
98
|
+
* @property {string} [sourceFile] - Source file containing component
|
|
99
|
+
* @property {Object} [properties] - Component properties
|
|
100
|
+
* @property {string[]} [dependencies] - Component dependencies
|
|
101
|
+
* @property {string} [documentation] - Component documentation
|
|
102
|
+
*/
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* File system statistics
|
|
106
|
+
* @typedef {Object} FileStats
|
|
107
|
+
* @property {number} size - File size in bytes
|
|
108
|
+
* @property {string} created - Creation timestamp
|
|
109
|
+
* @property {string} modified - Last modification timestamp
|
|
110
|
+
* @property {string} accessed - Last access timestamp
|
|
111
|
+
* @property {boolean} isDirectory - Whether item is directory
|
|
112
|
+
* @property {boolean} isFile - Whether item is file
|
|
113
|
+
* @property {number} mode - File permissions mode
|
|
114
|
+
*/
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* File system tree structure
|
|
118
|
+
* @typedef {Object} FileSystemTree
|
|
119
|
+
* @property {string} name - Item name
|
|
120
|
+
* @property {string} path - Item path
|
|
121
|
+
* @property {string} type - Item type (file, directory)
|
|
122
|
+
* @property {FileSystemTree[]} [children] - Child items (for directories)
|
|
123
|
+
* @property {number} [size] - Item size
|
|
124
|
+
* @property {string} [extension] - File extension
|
|
125
|
+
*/
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Syntax information
|
|
129
|
+
* @typedef {Object} SyntaxInfo
|
|
130
|
+
* @property {string} language - Programming language
|
|
131
|
+
* @property {string[]} [symbols] - Identified symbols
|
|
132
|
+
* @property {string[]} [imports] - Import statements
|
|
133
|
+
* @property {string[]} [functions] - Function definitions
|
|
134
|
+
* @property {string[]} [classes] - Class definitions
|
|
135
|
+
* @property {string[]} [variables] - Variable definitions
|
|
136
|
+
*/
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Context information
|
|
140
|
+
* @typedef {Object} ContextInfo
|
|
141
|
+
* @property {string} [beforeText] - Text before selection
|
|
142
|
+
* @property {string} [afterText] - Text after selection
|
|
143
|
+
* @property {number} [indentLevel] - Indentation level
|
|
144
|
+
* @property {string[]} [surroundingFunctions] - Surrounding function names
|
|
145
|
+
* @property {string[]} [surroundingClasses] - Surrounding class names
|
|
146
|
+
*/
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Dependency information
|
|
150
|
+
* @typedef {Object} DependencyInfo
|
|
151
|
+
* @property {string[]} imports - Imported modules/files
|
|
152
|
+
* @property {string[]} exports - Exported items
|
|
153
|
+
* @property {string[]} dependencies - External dependencies
|
|
154
|
+
* @property {string[]} dependents - Files that depend on this file
|
|
155
|
+
*/
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Context Reference validation functions
|
|
159
|
+
*/
|
|
160
|
+
export class ContextReferenceValidator {
|
|
161
|
+
/**
|
|
162
|
+
* Validate context reference data structure
|
|
163
|
+
* @param {Object} reference - Context reference to validate
|
|
164
|
+
* @returns {Object} Validation result
|
|
165
|
+
*/
|
|
166
|
+
static validate(reference) {
|
|
167
|
+
const errors = [];
|
|
168
|
+
const warnings = [];
|
|
169
|
+
|
|
170
|
+
// Required fields
|
|
171
|
+
if (!reference.id || typeof reference.id !== 'string') {
|
|
172
|
+
errors.push('Reference ID is required and must be a string');
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (!reference.type || typeof reference.type !== 'string') {
|
|
176
|
+
errors.push('Reference type is required and must be a string');
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (!reference.path || typeof reference.path !== 'string') {
|
|
180
|
+
errors.push('Reference path is required and must be a string');
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (!reference.name || typeof reference.name !== 'string') {
|
|
184
|
+
errors.push('Reference name is required and must be a string');
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Type validation
|
|
188
|
+
if (reference.type && !Object.values(CONTEXT_REFERENCE_TYPES).includes(reference.type)) {
|
|
189
|
+
errors.push(`Invalid reference type: ${reference.type}`);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Content validation
|
|
193
|
+
if (reference.content && typeof reference.content !== 'string') {
|
|
194
|
+
errors.push('Reference content must be a string');
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (reference.content && reference.content.length > 1000000) { // 1MB
|
|
198
|
+
warnings.push('Reference content is very large (>1MB)');
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Metadata validation
|
|
202
|
+
if (reference.metadata) {
|
|
203
|
+
const metadataValidation = this.validateMetadata(reference.metadata);
|
|
204
|
+
errors.push(...metadataValidation.errors);
|
|
205
|
+
warnings.push(...metadataValidation.warnings);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Scope validation
|
|
209
|
+
if (reference.scope) {
|
|
210
|
+
const scopeValidation = this.validateScope(reference.scope);
|
|
211
|
+
errors.push(...scopeValidation.errors);
|
|
212
|
+
warnings.push(...scopeValidation.warnings);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Access count validation
|
|
216
|
+
if (reference.accessCount !== undefined && typeof reference.accessCount !== 'number') {
|
|
217
|
+
errors.push('Access count must be a number');
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Timestamp validation
|
|
221
|
+
const timestampFields = ['createdAt', 'lastAccessed', 'lastModified'];
|
|
222
|
+
timestampFields.forEach(field => {
|
|
223
|
+
if (reference[field] && !this.isValidTimestamp(reference[field])) {
|
|
224
|
+
errors.push(`Invalid timestamp for ${field}: ${reference[field]}`);
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
// Type-specific validation
|
|
229
|
+
switch (reference.type) {
|
|
230
|
+
case CONTEXT_REFERENCE_TYPES.FILE:
|
|
231
|
+
const fileValidation = this.validateFileReference(reference);
|
|
232
|
+
errors.push(...fileValidation.errors);
|
|
233
|
+
warnings.push(...fileValidation.warnings);
|
|
234
|
+
break;
|
|
235
|
+
case CONTEXT_REFERENCE_TYPES.SELECTION:
|
|
236
|
+
const selectionValidation = this.validateSelectionReference(reference);
|
|
237
|
+
errors.push(...selectionValidation.errors);
|
|
238
|
+
warnings.push(...selectionValidation.warnings);
|
|
239
|
+
break;
|
|
240
|
+
case CONTEXT_REFERENCE_TYPES.DIRECTORY:
|
|
241
|
+
const directoryValidation = this.validateDirectoryReference(reference);
|
|
242
|
+
errors.push(...directoryValidation.errors);
|
|
243
|
+
warnings.push(...directoryValidation.warnings);
|
|
244
|
+
break;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
return {
|
|
248
|
+
isValid: errors.length === 0,
|
|
249
|
+
errors,
|
|
250
|
+
warnings
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Validate reference metadata
|
|
256
|
+
* @param {Object} metadata - Metadata to validate
|
|
257
|
+
* @returns {Object} Validation result
|
|
258
|
+
*/
|
|
259
|
+
static validateMetadata(metadata) {
|
|
260
|
+
const errors = [];
|
|
261
|
+
const warnings = [];
|
|
262
|
+
|
|
263
|
+
if (metadata.size !== undefined && (typeof metadata.size !== 'number' || metadata.size < 0)) {
|
|
264
|
+
errors.push('Metadata size must be a non-negative number');
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
if (metadata.language && typeof metadata.language !== 'string') {
|
|
268
|
+
errors.push('Metadata language must be a string');
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if (metadata.encoding && typeof metadata.encoding !== 'string') {
|
|
272
|
+
errors.push('Metadata encoding must be a string');
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
if (metadata.keywords && !Array.isArray(metadata.keywords)) {
|
|
276
|
+
errors.push('Metadata keywords must be an array');
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
if (metadata.mimeType && typeof metadata.mimeType !== 'string') {
|
|
280
|
+
errors.push('Metadata MIME type must be a string');
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
return { errors, warnings };
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Validate reference scope
|
|
288
|
+
* @param {Object} scope - Scope to validate
|
|
289
|
+
* @returns {Object} Validation result
|
|
290
|
+
*/
|
|
291
|
+
static validateScope(scope) {
|
|
292
|
+
const errors = [];
|
|
293
|
+
const warnings = [];
|
|
294
|
+
|
|
295
|
+
const numericFields = ['startLine', 'endLine', 'startColumn', 'endColumn', 'maxDepth'];
|
|
296
|
+
numericFields.forEach(field => {
|
|
297
|
+
if (scope[field] !== undefined && (typeof scope[field] !== 'number' || scope[field] < 0)) {
|
|
298
|
+
errors.push(`Scope ${field} must be a non-negative number`);
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
// Line range validation
|
|
303
|
+
if (scope.startLine !== undefined && scope.endLine !== undefined) {
|
|
304
|
+
if (scope.startLine > scope.endLine) {
|
|
305
|
+
errors.push('Start line must be <= end line');
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// Column range validation
|
|
310
|
+
if (scope.startColumn !== undefined && scope.endColumn !== undefined) {
|
|
311
|
+
if (scope.startColumn > scope.endColumn) {
|
|
312
|
+
errors.push('Start column must be <= end column');
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
const arrayFields = ['includePaths', 'excludePaths'];
|
|
317
|
+
arrayFields.forEach(field => {
|
|
318
|
+
if (scope[field] && !Array.isArray(scope[field])) {
|
|
319
|
+
errors.push(`Scope ${field} must be an array`);
|
|
320
|
+
}
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
return { errors, warnings };
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Validate file reference specific fields
|
|
328
|
+
* @param {Object} reference - File reference to validate
|
|
329
|
+
* @returns {Object} Validation result
|
|
330
|
+
*/
|
|
331
|
+
static validateFileReference(reference) {
|
|
332
|
+
const errors = [];
|
|
333
|
+
const warnings = [];
|
|
334
|
+
|
|
335
|
+
if (reference.absolutePath && typeof reference.absolutePath !== 'string') {
|
|
336
|
+
errors.push('Absolute path must be a string');
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
if (reference.relativePath && typeof reference.relativePath !== 'string') {
|
|
340
|
+
errors.push('Relative path must be a string');
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
if (reference.extension && typeof reference.extension !== 'string') {
|
|
344
|
+
errors.push('File extension must be a string');
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
if (reference.exists !== undefined && typeof reference.exists !== 'boolean') {
|
|
348
|
+
errors.push('File exists flag must be a boolean');
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
return { errors, warnings };
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Validate selection reference specific fields
|
|
356
|
+
* @param {Object} reference - Selection reference to validate
|
|
357
|
+
* @returns {Object} Validation result
|
|
358
|
+
*/
|
|
359
|
+
static validateSelectionReference(reference) {
|
|
360
|
+
const errors = [];
|
|
361
|
+
const warnings = [];
|
|
362
|
+
|
|
363
|
+
if (!reference.scope || (!reference.scope.startLine && !reference.scope.endLine)) {
|
|
364
|
+
warnings.push('Selection reference should have line scope defined');
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
if (reference.selectedText && typeof reference.selectedText !== 'string') {
|
|
368
|
+
errors.push('Selected text must be a string');
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
if (reference.sourceFile && typeof reference.sourceFile !== 'string') {
|
|
372
|
+
errors.push('Source file must be a string');
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
return { errors, warnings };
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* Validate directory reference specific fields
|
|
380
|
+
* @param {Object} reference - Directory reference to validate
|
|
381
|
+
* @returns {Object} Validation result
|
|
382
|
+
*/
|
|
383
|
+
static validateDirectoryReference(reference) {
|
|
384
|
+
const errors = [];
|
|
385
|
+
const warnings = [];
|
|
386
|
+
|
|
387
|
+
if (reference.fileCount !== undefined && (typeof reference.fileCount !== 'number' || reference.fileCount < 0)) {
|
|
388
|
+
errors.push('File count must be a non-negative number');
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
if (reference.totalSize !== undefined && (typeof reference.totalSize !== 'number' || reference.totalSize < 0)) {
|
|
392
|
+
errors.push('Total size must be a non-negative number');
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
if (reference.fileTypes && !Array.isArray(reference.fileTypes)) {
|
|
396
|
+
errors.push('File types must be an array');
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
return { errors, warnings };
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* Check if a timestamp is valid ISO string
|
|
404
|
+
* @param {string} timestamp - Timestamp to validate
|
|
405
|
+
* @returns {boolean} True if valid
|
|
406
|
+
*/
|
|
407
|
+
static isValidTimestamp(timestamp) {
|
|
408
|
+
if (typeof timestamp !== 'string') return false;
|
|
409
|
+
const date = new Date(timestamp);
|
|
410
|
+
return date instanceof Date && !isNaN(date.getTime());
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* Context Reference factory functions
|
|
416
|
+
*/
|
|
417
|
+
export class ContextReferenceFactory {
|
|
418
|
+
/**
|
|
419
|
+
* Create a new context reference
|
|
420
|
+
* @param {string} type - Reference type
|
|
421
|
+
* @param {string} path - Reference path
|
|
422
|
+
* @param {string} name - Reference name
|
|
423
|
+
* @param {Object} options - Additional options
|
|
424
|
+
* @returns {ContextReference} New context reference
|
|
425
|
+
*/
|
|
426
|
+
static create(type, path, name, options = {}) {
|
|
427
|
+
const now = new Date().toISOString();
|
|
428
|
+
const referenceId = this.generateReferenceId();
|
|
429
|
+
|
|
430
|
+
return {
|
|
431
|
+
id: referenceId,
|
|
432
|
+
type,
|
|
433
|
+
path,
|
|
434
|
+
name,
|
|
435
|
+
content: options.content || null,
|
|
436
|
+
metadata: this.createDefaultMetadata(type, path, options.metadata),
|
|
437
|
+
scope: options.scope || {},
|
|
438
|
+
createdAt: now,
|
|
439
|
+
lastAccessed: null,
|
|
440
|
+
lastModified: options.lastModified || null,
|
|
441
|
+
isValid: true,
|
|
442
|
+
invalidReason: null,
|
|
443
|
+
accessCount: 0,
|
|
444
|
+
tags: options.tags || {}
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* Create a file reference
|
|
450
|
+
* @param {string} absolutePath - Absolute file path
|
|
451
|
+
* @param {string} relativePath - Relative file path
|
|
452
|
+
* @param {Object} options - Additional options
|
|
453
|
+
* @returns {FileReference} File reference
|
|
454
|
+
*/
|
|
455
|
+
static createFileReference(absolutePath, relativePath, options = {}) {
|
|
456
|
+
const name = options.name || this.extractFileName(absolutePath);
|
|
457
|
+
const extension = this.extractFileExtension(absolutePath);
|
|
458
|
+
|
|
459
|
+
const reference = this.create(CONTEXT_REFERENCE_TYPES.FILE, relativePath, name, options);
|
|
460
|
+
|
|
461
|
+
return {
|
|
462
|
+
...reference,
|
|
463
|
+
absolutePath,
|
|
464
|
+
relativePath,
|
|
465
|
+
extension,
|
|
466
|
+
exists: options.exists !== undefined ? options.exists : true,
|
|
467
|
+
stats: options.stats || null,
|
|
468
|
+
gitStatus: options.gitStatus || null,
|
|
469
|
+
dependencies: options.dependencies || null
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* Create a selection reference
|
|
475
|
+
* @param {string} sourceFile - Source file path
|
|
476
|
+
* @param {string} selectedText - Selected text
|
|
477
|
+
* @param {Object} scope - Selection scope
|
|
478
|
+
* @param {Object} options - Additional options
|
|
479
|
+
* @returns {SelectionReference} Selection reference
|
|
480
|
+
*/
|
|
481
|
+
static createSelectionReference(sourceFile, selectedText, scope, options = {}) {
|
|
482
|
+
const name = options.name || this.generateSelectionName(sourceFile, scope);
|
|
483
|
+
|
|
484
|
+
const reference = this.create(CONTEXT_REFERENCE_TYPES.SELECTION, sourceFile, name, {
|
|
485
|
+
...options,
|
|
486
|
+
scope,
|
|
487
|
+
content: selectedText
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
return {
|
|
491
|
+
...reference,
|
|
492
|
+
sourceFile,
|
|
493
|
+
selectedText,
|
|
494
|
+
syntax: options.syntax || null,
|
|
495
|
+
context: options.context || null,
|
|
496
|
+
purpose: options.purpose || null
|
|
497
|
+
};
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
/**
|
|
501
|
+
* Create a directory reference
|
|
502
|
+
* @param {string} absolutePath - Absolute directory path
|
|
503
|
+
* @param {string} relativePath - Relative directory path
|
|
504
|
+
* @param {Object} options - Additional options
|
|
505
|
+
* @returns {DirectoryReference} Directory reference
|
|
506
|
+
*/
|
|
507
|
+
static createDirectoryReference(absolutePath, relativePath, options = {}) {
|
|
508
|
+
const name = options.name || this.extractDirectoryName(absolutePath);
|
|
509
|
+
|
|
510
|
+
const reference = this.create(CONTEXT_REFERENCE_TYPES.DIRECTORY, relativePath, name, options);
|
|
511
|
+
|
|
512
|
+
return {
|
|
513
|
+
...reference,
|
|
514
|
+
absolutePath,
|
|
515
|
+
relativePath,
|
|
516
|
+
tree: options.tree || null,
|
|
517
|
+
fileCount: options.fileCount || null,
|
|
518
|
+
totalSize: options.totalSize || null,
|
|
519
|
+
fileTypes: options.fileTypes || null
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
/**
|
|
524
|
+
* Create a component reference
|
|
525
|
+
* @param {string} componentType - Component type
|
|
526
|
+
* @param {string} path - Component path/identifier
|
|
527
|
+
* @param {string} name - Component name
|
|
528
|
+
* @param {Object} options - Additional options
|
|
529
|
+
* @returns {ComponentReference} Component reference
|
|
530
|
+
*/
|
|
531
|
+
static createComponentReference(componentType, path, name, options = {}) {
|
|
532
|
+
const reference = this.create(CONTEXT_REFERENCE_TYPES.COMPONENT, path, name, options);
|
|
533
|
+
|
|
534
|
+
return {
|
|
535
|
+
...reference,
|
|
536
|
+
componentType,
|
|
537
|
+
sourceFile: options.sourceFile || null,
|
|
538
|
+
properties: options.properties || null,
|
|
539
|
+
dependencies: options.dependencies || null,
|
|
540
|
+
documentation: options.documentation || null
|
|
541
|
+
};
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
/**
|
|
545
|
+
* Create default metadata for reference type
|
|
546
|
+
* @param {string} type - Reference type
|
|
547
|
+
* @param {string} path - Reference path
|
|
548
|
+
* @param {Object} overrides - Metadata overrides
|
|
549
|
+
* @returns {ReferenceMetadata} Default metadata
|
|
550
|
+
*/
|
|
551
|
+
static createDefaultMetadata(type, path, overrides = {}) {
|
|
552
|
+
const metadata = {
|
|
553
|
+
language: null,
|
|
554
|
+
encoding: 'utf-8',
|
|
555
|
+
size: null,
|
|
556
|
+
mimeType: null,
|
|
557
|
+
checksum: null,
|
|
558
|
+
keywords: [],
|
|
559
|
+
description: '',
|
|
560
|
+
customFields: {},
|
|
561
|
+
icon: this.getDefaultIcon(type, path),
|
|
562
|
+
permissions: {},
|
|
563
|
+
...overrides
|
|
564
|
+
};
|
|
565
|
+
|
|
566
|
+
// Set language for file references
|
|
567
|
+
if (type === CONTEXT_REFERENCE_TYPES.FILE) {
|
|
568
|
+
const extension = this.extractFileExtension(path);
|
|
569
|
+
metadata.language = this.getLanguageFromExtension(extension);
|
|
570
|
+
metadata.mimeType = this.getMimeTypeFromExtension(extension);
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
return metadata;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
/**
|
|
577
|
+
* Generate unique reference ID
|
|
578
|
+
* @returns {string} Unique reference ID
|
|
579
|
+
*/
|
|
580
|
+
static generateReferenceId() {
|
|
581
|
+
const timestamp = Date.now().toString(36);
|
|
582
|
+
const random = Math.random().toString(36).substr(2, 9);
|
|
583
|
+
return `ref_${timestamp}_${random}`;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
/**
|
|
587
|
+
* Extract file name from path
|
|
588
|
+
* @param {string} path - File path
|
|
589
|
+
* @returns {string} File name
|
|
590
|
+
*/
|
|
591
|
+
static extractFileName(path) {
|
|
592
|
+
return path.split(/[/\\]/).pop() || path;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
/**
|
|
596
|
+
* Extract directory name from path
|
|
597
|
+
* @param {string} path - Directory path
|
|
598
|
+
* @returns {string} Directory name
|
|
599
|
+
*/
|
|
600
|
+
static extractDirectoryName(path) {
|
|
601
|
+
const parts = path.split(/[/\\]/).filter(Boolean);
|
|
602
|
+
return parts[parts.length - 1] || 'Root';
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
/**
|
|
606
|
+
* Extract file extension from path
|
|
607
|
+
* @param {string} path - File path
|
|
608
|
+
* @returns {string} File extension
|
|
609
|
+
*/
|
|
610
|
+
static extractFileExtension(path) {
|
|
611
|
+
const fileName = this.extractFileName(path);
|
|
612
|
+
const lastDot = fileName.lastIndexOf('.');
|
|
613
|
+
return lastDot !== -1 ? fileName.substring(lastDot) : '';
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
/**
|
|
617
|
+
* Generate selection name from file and scope
|
|
618
|
+
* @param {string} sourceFile - Source file path
|
|
619
|
+
* @param {Object} scope - Selection scope
|
|
620
|
+
* @returns {string} Selection name
|
|
621
|
+
*/
|
|
622
|
+
static generateSelectionName(sourceFile, scope) {
|
|
623
|
+
const fileName = this.extractFileName(sourceFile);
|
|
624
|
+
|
|
625
|
+
if (scope.startLine && scope.endLine) {
|
|
626
|
+
if (scope.startLine === scope.endLine) {
|
|
627
|
+
return `${fileName}:${scope.startLine}`;
|
|
628
|
+
} else {
|
|
629
|
+
return `${fileName}:${scope.startLine}-${scope.endLine}`;
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
if (scope.functionName) {
|
|
634
|
+
return `${fileName}:${scope.functionName}()`;
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
if (scope.className) {
|
|
638
|
+
return `${fileName}:${scope.className}`;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
return `${fileName} (selection)`;
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
/**
|
|
645
|
+
* Get default icon for reference type and path
|
|
646
|
+
* @param {string} type - Reference type
|
|
647
|
+
* @param {string} path - Reference path
|
|
648
|
+
* @returns {string} Icon identifier
|
|
649
|
+
*/
|
|
650
|
+
static getDefaultIcon(type, path) {
|
|
651
|
+
if (type === CONTEXT_REFERENCE_TYPES.FILE) {
|
|
652
|
+
const extension = this.extractFileExtension(path);
|
|
653
|
+
return FILE_ICONS[extension] || CONTEXT_ICONS.DEFAULT;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
return CONTEXT_ICONS[type] || CONTEXT_ICONS.DEFAULT;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
/**
|
|
660
|
+
* Get programming language from file extension
|
|
661
|
+
* @param {string} extension - File extension
|
|
662
|
+
* @returns {string|null} Programming language
|
|
663
|
+
*/
|
|
664
|
+
static getLanguageFromExtension(extension) {
|
|
665
|
+
const languageMap = {
|
|
666
|
+
'.js': 'javascript',
|
|
667
|
+
'.jsx': 'javascript',
|
|
668
|
+
'.ts': 'typescript',
|
|
669
|
+
'.tsx': 'typescript',
|
|
670
|
+
'.py': 'python',
|
|
671
|
+
'.java': 'java',
|
|
672
|
+
'.cpp': 'cpp',
|
|
673
|
+
'.c': 'c',
|
|
674
|
+
'.cs': 'csharp',
|
|
675
|
+
'.php': 'php',
|
|
676
|
+
'.rb': 'ruby',
|
|
677
|
+
'.go': 'go',
|
|
678
|
+
'.rs': 'rust',
|
|
679
|
+
'.html': 'html',
|
|
680
|
+
'.css': 'css',
|
|
681
|
+
'.scss': 'scss',
|
|
682
|
+
'.json': 'json',
|
|
683
|
+
'.yml': 'yaml',
|
|
684
|
+
'.yaml': 'yaml',
|
|
685
|
+
'.xml': 'xml',
|
|
686
|
+
'.sql': 'sql',
|
|
687
|
+
'.md': 'markdown'
|
|
688
|
+
};
|
|
689
|
+
|
|
690
|
+
return languageMap[extension.toLowerCase()] || null;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
/**
|
|
694
|
+
* Get MIME type from file extension
|
|
695
|
+
* @param {string} extension - File extension
|
|
696
|
+
* @returns {string|null} MIME type
|
|
697
|
+
*/
|
|
698
|
+
static getMimeTypeFromExtension(extension) {
|
|
699
|
+
const mimeMap = {
|
|
700
|
+
'.js': 'application/javascript',
|
|
701
|
+
'.jsx': 'application/javascript',
|
|
702
|
+
'.ts': 'application/typescript',
|
|
703
|
+
'.tsx': 'application/typescript',
|
|
704
|
+
'.py': 'text/x-python',
|
|
705
|
+
'.java': 'text/x-java-source',
|
|
706
|
+
'.cpp': 'text/x-c++src',
|
|
707
|
+
'.c': 'text/x-csrc',
|
|
708
|
+
'.cs': 'text/x-csharp',
|
|
709
|
+
'.php': 'application/x-php',
|
|
710
|
+
'.rb': 'application/x-ruby',
|
|
711
|
+
'.go': 'text/x-go',
|
|
712
|
+
'.rs': 'text/x-rust',
|
|
713
|
+
'.html': 'text/html',
|
|
714
|
+
'.css': 'text/css',
|
|
715
|
+
'.scss': 'text/x-scss',
|
|
716
|
+
'.json': 'application/json',
|
|
717
|
+
'.yml': 'application/x-yaml',
|
|
718
|
+
'.yaml': 'application/x-yaml',
|
|
719
|
+
'.xml': 'application/xml',
|
|
720
|
+
'.sql': 'application/sql',
|
|
721
|
+
'.md': 'text/markdown',
|
|
722
|
+
'.txt': 'text/plain'
|
|
723
|
+
};
|
|
724
|
+
|
|
725
|
+
return mimeMap[extension.toLowerCase()] || 'text/plain';
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
/**
|
|
730
|
+
* Context Reference utility functions
|
|
731
|
+
*/
|
|
732
|
+
export class ContextReferenceUtils {
|
|
733
|
+
/**
|
|
734
|
+
* Check if reference is still valid
|
|
735
|
+
* @param {ContextReference} reference - Reference to check
|
|
736
|
+
* @returns {boolean} True if valid
|
|
737
|
+
*/
|
|
738
|
+
static isValid(reference) {
|
|
739
|
+
return reference.isValid && !reference.invalidReason;
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
/**
|
|
743
|
+
* Mark reference as accessed
|
|
744
|
+
* @param {ContextReference} reference - Reference to mark
|
|
745
|
+
* @returns {ContextReference} Updated reference
|
|
746
|
+
*/
|
|
747
|
+
static markAccessed(reference) {
|
|
748
|
+
return {
|
|
749
|
+
...reference,
|
|
750
|
+
lastAccessed: new Date().toISOString(),
|
|
751
|
+
accessCount: reference.accessCount + 1
|
|
752
|
+
};
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
/**
|
|
756
|
+
* Mark reference as invalid
|
|
757
|
+
* @param {ContextReference} reference - Reference to invalidate
|
|
758
|
+
* @param {string} reason - Reason for invalidation
|
|
759
|
+
* @returns {ContextReference} Updated reference
|
|
760
|
+
*/
|
|
761
|
+
static markInvalid(reference, reason) {
|
|
762
|
+
return {
|
|
763
|
+
...reference,
|
|
764
|
+
isValid: false,
|
|
765
|
+
invalidReason: reason
|
|
766
|
+
};
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
/**
|
|
770
|
+
* Get reference display name with context
|
|
771
|
+
* @param {ContextReference} reference - Reference to format
|
|
772
|
+
* @returns {string} Display name
|
|
773
|
+
*/
|
|
774
|
+
static getDisplayName(reference) {
|
|
775
|
+
if (reference.type === CONTEXT_REFERENCE_TYPES.SELECTION && reference.scope) {
|
|
776
|
+
return ContextReferenceFactory.generateSelectionName(reference.path, reference.scope);
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
return reference.name;
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
/**
|
|
783
|
+
* Get reference description
|
|
784
|
+
* @param {ContextReference} reference - Reference to describe
|
|
785
|
+
* @returns {string} Description
|
|
786
|
+
*/
|
|
787
|
+
static getDescription(reference) {
|
|
788
|
+
if (reference.metadata?.description) {
|
|
789
|
+
return reference.metadata.description;
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
switch (reference.type) {
|
|
793
|
+
case CONTEXT_REFERENCE_TYPES.FILE:
|
|
794
|
+
return `File: ${reference.path}`;
|
|
795
|
+
case CONTEXT_REFERENCE_TYPES.DIRECTORY:
|
|
796
|
+
return `Directory: ${reference.path}`;
|
|
797
|
+
case CONTEXT_REFERENCE_TYPES.SELECTION:
|
|
798
|
+
return `Selection from ${reference.path}`;
|
|
799
|
+
case CONTEXT_REFERENCE_TYPES.COMPONENT:
|
|
800
|
+
return `Component: ${reference.name}`;
|
|
801
|
+
default:
|
|
802
|
+
return reference.path;
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
/**
|
|
807
|
+
* Calculate reference relevance score
|
|
808
|
+
* @param {ContextReference} reference - Reference to score
|
|
809
|
+
* @param {Object} context - Scoring context
|
|
810
|
+
* @returns {number} Relevance score (0-1)
|
|
811
|
+
*/
|
|
812
|
+
static calculateRelevance(reference, context = {}) {
|
|
813
|
+
let score = 0.5; // Base score
|
|
814
|
+
|
|
815
|
+
// Recent access bonus
|
|
816
|
+
if (reference.lastAccessed) {
|
|
817
|
+
const daysSinceAccess = (Date.now() - new Date(reference.lastAccessed)) / (1000 * 60 * 60 * 24);
|
|
818
|
+
score += Math.max(0, 0.2 * (1 - daysSinceAccess / 30)); // Decay over 30 days
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
// Access frequency bonus
|
|
822
|
+
if (reference.accessCount > 0) {
|
|
823
|
+
score += Math.min(0.2, reference.accessCount * 0.01);
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
// Type-specific scoring
|
|
827
|
+
switch (reference.type) {
|
|
828
|
+
case CONTEXT_REFERENCE_TYPES.FILE:
|
|
829
|
+
if (context.fileTypes && reference.metadata?.language) {
|
|
830
|
+
if (context.fileTypes.includes(reference.metadata.language)) {
|
|
831
|
+
score += 0.2;
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
break;
|
|
835
|
+
case CONTEXT_REFERENCE_TYPES.SELECTION:
|
|
836
|
+
score += 0.1; // Selections are often more relevant
|
|
837
|
+
break;
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
// Keyword matching
|
|
841
|
+
if (context.keywords && reference.metadata?.keywords) {
|
|
842
|
+
const matches = context.keywords.filter(k =>
|
|
843
|
+
reference.metadata.keywords.includes(k)
|
|
844
|
+
).length;
|
|
845
|
+
score += Math.min(0.3, matches * 0.1);
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
// Invalid references get heavy penalty
|
|
849
|
+
if (!reference.isValid) {
|
|
850
|
+
score *= 0.1;
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
return Math.min(1, Math.max(0, score));
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
/**
|
|
857
|
+
* Group references by type
|
|
858
|
+
* @param {ContextReference[]} references - References to group
|
|
859
|
+
* @returns {Object} Grouped references
|
|
860
|
+
*/
|
|
861
|
+
static groupByType(references) {
|
|
862
|
+
return references.reduce((groups, reference) => {
|
|
863
|
+
const type = reference.type;
|
|
864
|
+
if (!groups[type]) {
|
|
865
|
+
groups[type] = [];
|
|
866
|
+
}
|
|
867
|
+
groups[type].push(reference);
|
|
868
|
+
return groups;
|
|
869
|
+
}, {});
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
/**
|
|
873
|
+
* Sort references by relevance
|
|
874
|
+
* @param {ContextReference[]} references - References to sort
|
|
875
|
+
* @param {Object} context - Sorting context
|
|
876
|
+
* @returns {ContextReference[]} Sorted references
|
|
877
|
+
*/
|
|
878
|
+
static sortByRelevance(references, context = {}) {
|
|
879
|
+
return [...references].sort((a, b) => {
|
|
880
|
+
const scoreA = this.calculateRelevance(a, context);
|
|
881
|
+
const scoreB = this.calculateRelevance(b, context);
|
|
882
|
+
return scoreB - scoreA;
|
|
883
|
+
});
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
/**
|
|
887
|
+
* Filter references by criteria
|
|
888
|
+
* @param {ContextReference[]} references - References to filter
|
|
889
|
+
* @param {Object} criteria - Filter criteria
|
|
890
|
+
* @returns {ContextReference[]} Filtered references
|
|
891
|
+
*/
|
|
892
|
+
static filter(references, criteria = {}) {
|
|
893
|
+
return references.filter(reference => {
|
|
894
|
+
// Type filter
|
|
895
|
+
if (criteria.types && !criteria.types.includes(reference.type)) {
|
|
896
|
+
return false;
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
// Valid filter
|
|
900
|
+
if (criteria.validOnly && !reference.isValid) {
|
|
901
|
+
return false;
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
// Language filter
|
|
905
|
+
if (criteria.languages && reference.metadata?.language) {
|
|
906
|
+
if (!criteria.languages.includes(reference.metadata.language)) {
|
|
907
|
+
return false;
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
// Path filter
|
|
912
|
+
if (criteria.pathPattern) {
|
|
913
|
+
const regex = new RegExp(criteria.pathPattern, 'i');
|
|
914
|
+
if (!regex.test(reference.path)) {
|
|
915
|
+
return false;
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
// Keyword filter
|
|
920
|
+
if (criteria.keywords && reference.metadata?.keywords) {
|
|
921
|
+
const hasKeyword = criteria.keywords.some(keyword =>
|
|
922
|
+
reference.metadata.keywords.includes(keyword)
|
|
923
|
+
);
|
|
924
|
+
if (!hasKeyword) {
|
|
925
|
+
return false;
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
// Date range filter
|
|
930
|
+
if (criteria.createdAfter) {
|
|
931
|
+
if (new Date(reference.createdAt) < new Date(criteria.createdAfter)) {
|
|
932
|
+
return false;
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
if (criteria.createdBefore) {
|
|
937
|
+
if (new Date(reference.createdAt) > new Date(criteria.createdBefore)) {
|
|
938
|
+
return false;
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
return true;
|
|
943
|
+
});
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
/**
|
|
947
|
+
* Format reference for display
|
|
948
|
+
* @param {ContextReference} reference - Reference to format
|
|
949
|
+
* @returns {Object} Formatted reference
|
|
950
|
+
*/
|
|
951
|
+
static formatForDisplay(reference) {
|
|
952
|
+
return {
|
|
953
|
+
id: reference.id,
|
|
954
|
+
type: reference.type,
|
|
955
|
+
name: this.getDisplayName(reference),
|
|
956
|
+
description: this.getDescription(reference),
|
|
957
|
+
path: reference.path,
|
|
958
|
+
icon: reference.metadata?.icon || CONTEXT_ICONS[reference.type] || CONTEXT_ICONS.DEFAULT,
|
|
959
|
+
isValid: reference.isValid,
|
|
960
|
+
lastAccessed: reference.lastAccessed,
|
|
961
|
+
accessCount: reference.accessCount,
|
|
962
|
+
size: reference.metadata?.size || null,
|
|
963
|
+
language: reference.metadata?.language || null
|
|
964
|
+
};
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
export default {
|
|
969
|
+
ContextReferenceValidator,
|
|
970
|
+
ContextReferenceFactory,
|
|
971
|
+
ContextReferenceUtils
|
|
972
|
+
};
|