@sharc-code/mcp 0.2.0
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 +134 -0
- package/dist/backend-client.d.ts +251 -0
- package/dist/backend-client.d.ts.map +1 -0
- package/dist/backend-client.js +269 -0
- package/dist/backend-client.js.map +1 -0
- package/dist/backend-handlers.d.ts +243 -0
- package/dist/backend-handlers.d.ts.map +1 -0
- package/dist/backend-handlers.js +1453 -0
- package/dist/backend-handlers.js.map +1 -0
- package/dist/config.d.ts +47 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +94 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +344 -0
- package/dist/index.js.map +1 -0
- package/dist/project-detector.d.ts +42 -0
- package/dist/project-detector.d.ts.map +1 -0
- package/dist/project-detector.js +135 -0
- package/dist/project-detector.js.map +1 -0
- package/dist/utils/env-manager.d.ts +19 -0
- package/dist/utils/env-manager.d.ts.map +1 -0
- package/dist/utils/env-manager.js +99 -0
- package/dist/utils/env-manager.js.map +1 -0
- package/dist/utils.d.ts +10 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +27 -0
- package/dist/utils.js.map +1 -0
- package/dist/watcher/file-watcher.d.ts +64 -0
- package/dist/watcher/file-watcher.d.ts.map +1 -0
- package/dist/watcher/file-watcher.js +263 -0
- package/dist/watcher/file-watcher.js.map +1 -0
- package/dist/watcher/incremental-indexer.d.ts +68 -0
- package/dist/watcher/incremental-indexer.d.ts.map +1 -0
- package/dist/watcher/incremental-indexer.js +254 -0
- package/dist/watcher/incremental-indexer.js.map +1 -0
- package/dist/watcher/index.d.ts +10 -0
- package/dist/watcher/index.d.ts.map +1 -0
- package/dist/watcher/index.js +10 -0
- package/dist/watcher/index.js.map +1 -0
- package/dist/watcher/processing-queue.d.ts +79 -0
- package/dist/watcher/processing-queue.d.ts.map +1 -0
- package/dist/watcher/processing-queue.js +150 -0
- package/dist/watcher/processing-queue.js.map +1 -0
- package/dist/watcher/syntax-guard.d.ts +59 -0
- package/dist/watcher/syntax-guard.d.ts.map +1 -0
- package/dist/watcher/syntax-guard.js +136 -0
- package/dist/watcher/syntax-guard.js.map +1 -0
- package/package.json +52 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Processing Queue for File Changes
|
|
3
|
+
*
|
|
4
|
+
* Handles race conditions when files change rapidly:
|
|
5
|
+
* - Deduplicates by file path (latest event wins)
|
|
6
|
+
* - Processes one file at a time per queue
|
|
7
|
+
* - Allows concurrent processing across different codebases
|
|
8
|
+
*/
|
|
9
|
+
export type FileChangeType = 'add' | 'modify' | 'delete';
|
|
10
|
+
export interface FileChange {
|
|
11
|
+
type: FileChangeType;
|
|
12
|
+
relativePath: string;
|
|
13
|
+
absolutePath: string;
|
|
14
|
+
}
|
|
15
|
+
export interface QueuedChange {
|
|
16
|
+
change: FileChange;
|
|
17
|
+
timestamp: number;
|
|
18
|
+
}
|
|
19
|
+
export interface ProcessingResult {
|
|
20
|
+
success: boolean;
|
|
21
|
+
chunksDeleted: number;
|
|
22
|
+
chunksIndexed: number;
|
|
23
|
+
skipped?: boolean;
|
|
24
|
+
error?: string;
|
|
25
|
+
}
|
|
26
|
+
export type ChangeProcessor = (change: FileChange) => Promise<ProcessingResult>;
|
|
27
|
+
/**
|
|
28
|
+
* Processing queue that deduplicates file changes by path
|
|
29
|
+
*
|
|
30
|
+
* When a file changes multiple times before processing:
|
|
31
|
+
* - Old events are discarded
|
|
32
|
+
* - Only the latest event is processed
|
|
33
|
+
* - This prevents wasted work and race conditions
|
|
34
|
+
*/
|
|
35
|
+
export declare class ProcessingQueue {
|
|
36
|
+
private queue;
|
|
37
|
+
private isProcessing;
|
|
38
|
+
private processingPath;
|
|
39
|
+
private pendingReprocess;
|
|
40
|
+
private processor;
|
|
41
|
+
private verbose;
|
|
42
|
+
constructor(processor: ChangeProcessor, verbose?: boolean);
|
|
43
|
+
/**
|
|
44
|
+
* Add a file change to the queue
|
|
45
|
+
* If the file is already queued, replaces with the new event
|
|
46
|
+
*/
|
|
47
|
+
enqueue(change: FileChange): void;
|
|
48
|
+
/**
|
|
49
|
+
* Get the number of pending changes
|
|
50
|
+
*/
|
|
51
|
+
getPendingCount(): number;
|
|
52
|
+
/**
|
|
53
|
+
* Check if the queue is idle (not processing and no pending)
|
|
54
|
+
*/
|
|
55
|
+
isIdle(): boolean;
|
|
56
|
+
/**
|
|
57
|
+
* Get the currently processing path
|
|
58
|
+
*/
|
|
59
|
+
getCurrentlyProcessing(): string | null;
|
|
60
|
+
/**
|
|
61
|
+
* Process the next item in the queue
|
|
62
|
+
* Only one item is processed at a time
|
|
63
|
+
*/
|
|
64
|
+
private tryProcessNext;
|
|
65
|
+
/**
|
|
66
|
+
* Get the oldest entry from the queue (FIFO order)
|
|
67
|
+
*/
|
|
68
|
+
private getOldestEntry;
|
|
69
|
+
/**
|
|
70
|
+
* Clear the queue (for shutdown)
|
|
71
|
+
*/
|
|
72
|
+
clear(): void;
|
|
73
|
+
/**
|
|
74
|
+
* Wait for the queue to become idle
|
|
75
|
+
* Useful for graceful shutdown
|
|
76
|
+
*/
|
|
77
|
+
waitForIdle(timeoutMs?: number): Promise<boolean>;
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=processing-queue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"processing-queue.d.ts","sourceRoot":"","sources":["../../src/watcher/processing-queue.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAEzD,MAAM,WAAW,UAAU;IACvB,IAAI,EAAE,cAAc,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,YAAY;IACzB,MAAM,EAAE,UAAU,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,MAAM,eAAe,GAAG,CAAC,MAAM,EAAE,UAAU,KAAK,OAAO,CAAC,gBAAgB,CAAC,CAAC;AAEhF;;;;;;;GAOG;AACH,qBAAa,eAAe;IACxB,OAAO,CAAC,KAAK,CAAwC;IACrD,OAAO,CAAC,YAAY,CAAkB;IACtC,OAAO,CAAC,cAAc,CAAuB;IAC7C,OAAO,CAAC,gBAAgB,CAA0B;IAClD,OAAO,CAAC,SAAS,CAAkB;IACnC,OAAO,CAAC,OAAO,CAAU;gBAEb,SAAS,EAAE,eAAe,EAAE,OAAO,GAAE,OAAe;IAKhE;;;OAGG;IACH,OAAO,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;IAyBjC;;OAEG;IACH,eAAe,IAAI,MAAM;IAIzB;;OAEG;IACH,MAAM,IAAI,OAAO;IAIjB;;OAEG;IACH,sBAAsB,IAAI,MAAM,GAAG,IAAI;IAIvC;;;OAGG;YACW,cAAc;IA8C5B;;OAEG;IACH,OAAO,CAAC,cAAc;IAgBtB;;OAEG;IACH,KAAK,IAAI,IAAI;IAKb;;;OAGG;IACG,WAAW,CAAC,SAAS,GAAE,MAAc,GAAG,OAAO,CAAC,OAAO,CAAC;CAYjE"}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Processing Queue for File Changes
|
|
3
|
+
*
|
|
4
|
+
* Handles race conditions when files change rapidly:
|
|
5
|
+
* - Deduplicates by file path (latest event wins)
|
|
6
|
+
* - Processes one file at a time per queue
|
|
7
|
+
* - Allows concurrent processing across different codebases
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Processing queue that deduplicates file changes by path
|
|
11
|
+
*
|
|
12
|
+
* When a file changes multiple times before processing:
|
|
13
|
+
* - Old events are discarded
|
|
14
|
+
* - Only the latest event is processed
|
|
15
|
+
* - This prevents wasted work and race conditions
|
|
16
|
+
*/
|
|
17
|
+
export class ProcessingQueue {
|
|
18
|
+
constructor(processor, verbose = false) {
|
|
19
|
+
this.queue = new Map();
|
|
20
|
+
this.isProcessing = false;
|
|
21
|
+
this.processingPath = null;
|
|
22
|
+
this.pendingReprocess = new Set();
|
|
23
|
+
this.processor = processor;
|
|
24
|
+
this.verbose = verbose;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Add a file change to the queue
|
|
28
|
+
* If the file is already queued, replaces with the new event
|
|
29
|
+
*/
|
|
30
|
+
enqueue(change) {
|
|
31
|
+
const { relativePath } = change;
|
|
32
|
+
// If this file is currently being processed, mark for reprocess
|
|
33
|
+
if (this.processingPath === relativePath) {
|
|
34
|
+
this.pendingReprocess.add(relativePath);
|
|
35
|
+
if (this.verbose) {
|
|
36
|
+
console.log(`[Queue] File ${relativePath} changed during processing, will reprocess`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
// Always replace existing - latest event wins
|
|
40
|
+
this.queue.set(relativePath, {
|
|
41
|
+
change,
|
|
42
|
+
timestamp: Date.now(),
|
|
43
|
+
});
|
|
44
|
+
if (this.verbose) {
|
|
45
|
+
console.log(`[Queue] Enqueued ${change.type}: ${relativePath} (queue size: ${this.queue.size})`);
|
|
46
|
+
}
|
|
47
|
+
// Try to process next item
|
|
48
|
+
this.tryProcessNext();
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Get the number of pending changes
|
|
52
|
+
*/
|
|
53
|
+
getPendingCount() {
|
|
54
|
+
return this.queue.size;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Check if the queue is idle (not processing and no pending)
|
|
58
|
+
*/
|
|
59
|
+
isIdle() {
|
|
60
|
+
return !this.isProcessing && this.queue.size === 0;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Get the currently processing path
|
|
64
|
+
*/
|
|
65
|
+
getCurrentlyProcessing() {
|
|
66
|
+
return this.processingPath;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Process the next item in the queue
|
|
70
|
+
* Only one item is processed at a time
|
|
71
|
+
*/
|
|
72
|
+
async tryProcessNext() {
|
|
73
|
+
if (this.isProcessing || this.queue.size === 0) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
this.isProcessing = true;
|
|
77
|
+
// Get oldest entry (FIFO within deduplication)
|
|
78
|
+
const [path, queued] = this.getOldestEntry();
|
|
79
|
+
this.queue.delete(path);
|
|
80
|
+
this.processingPath = path;
|
|
81
|
+
try {
|
|
82
|
+
if (this.verbose) {
|
|
83
|
+
console.log(`[Queue] Processing ${queued.change.type}: ${path}`);
|
|
84
|
+
}
|
|
85
|
+
const result = await this.processor(queued.change);
|
|
86
|
+
if (this.verbose) {
|
|
87
|
+
if (result.success) {
|
|
88
|
+
console.log(`[Queue] Completed ${path}: deleted=${result.chunksDeleted}, indexed=${result.chunksIndexed}`);
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
console.log(`[Queue] Failed ${path}: ${result.error}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
console.error(`[Queue] Error processing ${path}:`, error);
|
|
97
|
+
}
|
|
98
|
+
finally {
|
|
99
|
+
this.processingPath = null;
|
|
100
|
+
this.isProcessing = false;
|
|
101
|
+
// Check if this file needs reprocessing
|
|
102
|
+
if (this.pendingReprocess.has(path)) {
|
|
103
|
+
this.pendingReprocess.delete(path);
|
|
104
|
+
// The file should already be in queue with updated content
|
|
105
|
+
if (this.verbose) {
|
|
106
|
+
console.log(`[Queue] File ${path} changed during processing, continuing with updated content`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
// Process next item
|
|
110
|
+
this.tryProcessNext();
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Get the oldest entry from the queue (FIFO order)
|
|
115
|
+
*/
|
|
116
|
+
getOldestEntry() {
|
|
117
|
+
let oldest = null;
|
|
118
|
+
for (const entry of this.queue.entries()) {
|
|
119
|
+
if (!oldest || entry[1].timestamp < oldest[1].timestamp) {
|
|
120
|
+
oldest = entry;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
if (!oldest) {
|
|
124
|
+
throw new Error('Queue is empty');
|
|
125
|
+
}
|
|
126
|
+
return oldest;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Clear the queue (for shutdown)
|
|
130
|
+
*/
|
|
131
|
+
clear() {
|
|
132
|
+
this.queue.clear();
|
|
133
|
+
this.pendingReprocess.clear();
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Wait for the queue to become idle
|
|
137
|
+
* Useful for graceful shutdown
|
|
138
|
+
*/
|
|
139
|
+
async waitForIdle(timeoutMs = 30000) {
|
|
140
|
+
const startTime = Date.now();
|
|
141
|
+
while (!this.isIdle()) {
|
|
142
|
+
if (Date.now() - startTime > timeoutMs) {
|
|
143
|
+
return false; // Timeout
|
|
144
|
+
}
|
|
145
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
146
|
+
}
|
|
147
|
+
return true;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
//# sourceMappingURL=processing-queue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"processing-queue.js","sourceRoot":"","sources":["../../src/watcher/processing-queue.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Syntax Guard
|
|
3
|
+
*
|
|
4
|
+
* Protects vector database from incomplete/broken code.
|
|
5
|
+
* When a file has syntax errors, we skip re-indexing and keep old vectors.
|
|
6
|
+
*
|
|
7
|
+
* This prevents the scenario where:
|
|
8
|
+
* 1. User saves file mid-typing (incomplete code)
|
|
9
|
+
* 2. Old vectors get deleted
|
|
10
|
+
* 3. New code fails to parse → no new vectors
|
|
11
|
+
* 4. Search results disappear until next successful save
|
|
12
|
+
*/
|
|
13
|
+
export interface SyntaxCheckResult {
|
|
14
|
+
hasErrors: boolean;
|
|
15
|
+
errorCount: number;
|
|
16
|
+
language: string;
|
|
17
|
+
supported: boolean;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Syntax Guard class
|
|
21
|
+
*
|
|
22
|
+
* Uses tree-sitter via AST splitter to detect syntax errors.
|
|
23
|
+
* Only checks languages that have tree-sitter grammars.
|
|
24
|
+
*/
|
|
25
|
+
export declare class SyntaxGuard {
|
|
26
|
+
private splitter;
|
|
27
|
+
constructor();
|
|
28
|
+
/**
|
|
29
|
+
* Get language from file extension
|
|
30
|
+
*/
|
|
31
|
+
getLanguageFromExtension(ext: string): string;
|
|
32
|
+
/**
|
|
33
|
+
* Check if a language is supported for syntax checking
|
|
34
|
+
*/
|
|
35
|
+
isLanguageSupported(language: string): boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Check file content for syntax errors
|
|
38
|
+
*
|
|
39
|
+
* @param content - File content to check
|
|
40
|
+
* @param language - Language identifier (e.g., 'typescript', 'python')
|
|
41
|
+
* @returns SyntaxCheckResult with error information
|
|
42
|
+
*/
|
|
43
|
+
checkSyntax(content: string, language: string): SyntaxCheckResult;
|
|
44
|
+
/**
|
|
45
|
+
* Check file by extension
|
|
46
|
+
*
|
|
47
|
+
* Convenience method that determines language from extension.
|
|
48
|
+
*
|
|
49
|
+
* @param content - File content to check
|
|
50
|
+
* @param ext - File extension (e.g., '.ts', '.py')
|
|
51
|
+
* @returns SyntaxCheckResult with error information
|
|
52
|
+
*/
|
|
53
|
+
checkSyntaxByExtension(content: string, ext: string): SyntaxCheckResult;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Get shared SyntaxGuard instance
|
|
57
|
+
*/
|
|
58
|
+
export declare function getSyntaxGuard(): SyntaxGuard;
|
|
59
|
+
//# sourceMappingURL=syntax-guard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"syntax-guard.d.ts","sourceRoot":"","sources":["../../src/watcher/syntax-guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AA2CH,MAAM,WAAW,iBAAiB;IAC9B,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;CACtB;AAED;;;;;GAKG;AACH,qBAAa,WAAW;IACpB,OAAO,CAAC,QAAQ,CAAkB;;IAMlC;;OAEG;IACH,wBAAwB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM;IAI7C;;OAEG;IACH,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAI9C;;;;;;OAMG;IACH,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,iBAAiB;IAgCjE;;;;;;;;OAQG;IACH,sBAAsB,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,iBAAiB;CAI1E;AAKD;;GAEG;AACH,wBAAgB,cAAc,IAAI,WAAW,CAK5C"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Syntax Guard
|
|
3
|
+
*
|
|
4
|
+
* Protects vector database from incomplete/broken code.
|
|
5
|
+
* When a file has syntax errors, we skip re-indexing and keep old vectors.
|
|
6
|
+
*
|
|
7
|
+
* This prevents the scenario where:
|
|
8
|
+
* 1. User saves file mid-typing (incomplete code)
|
|
9
|
+
* 2. Old vectors get deleted
|
|
10
|
+
* 3. New code fails to parse → no new vectors
|
|
11
|
+
* 4. Search results disappear until next successful save
|
|
12
|
+
*/
|
|
13
|
+
// Import from @sharc-code/splitter package
|
|
14
|
+
import { AstCodeSplitter } from '@sharc-code/splitter';
|
|
15
|
+
// Languages supported by tree-sitter AST parsing
|
|
16
|
+
const SUPPORTED_LANGUAGES = new Set([
|
|
17
|
+
'typescript',
|
|
18
|
+
'javascript',
|
|
19
|
+
'python',
|
|
20
|
+
'go',
|
|
21
|
+
'rust',
|
|
22
|
+
'java',
|
|
23
|
+
'c',
|
|
24
|
+
'cpp',
|
|
25
|
+
'c_sharp',
|
|
26
|
+
'scala',
|
|
27
|
+
]);
|
|
28
|
+
// Extension to language mapping
|
|
29
|
+
const EXTENSION_TO_LANGUAGE = {
|
|
30
|
+
'.ts': 'typescript',
|
|
31
|
+
'.tsx': 'typescript',
|
|
32
|
+
'.js': 'javascript',
|
|
33
|
+
'.jsx': 'javascript',
|
|
34
|
+
'.mjs': 'javascript',
|
|
35
|
+
'.cjs': 'javascript',
|
|
36
|
+
'.py': 'python',
|
|
37
|
+
'.go': 'go',
|
|
38
|
+
'.rs': 'rust',
|
|
39
|
+
'.java': 'java',
|
|
40
|
+
'.c': 'c',
|
|
41
|
+
'.h': 'c',
|
|
42
|
+
'.cpp': 'cpp',
|
|
43
|
+
'.cc': 'cpp',
|
|
44
|
+
'.cxx': 'cpp',
|
|
45
|
+
'.hpp': 'cpp',
|
|
46
|
+
'.hxx': 'cpp',
|
|
47
|
+
'.cs': 'c_sharp',
|
|
48
|
+
'.scala': 'scala',
|
|
49
|
+
'.sc': 'scala',
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* Syntax Guard class
|
|
53
|
+
*
|
|
54
|
+
* Uses tree-sitter via AST splitter to detect syntax errors.
|
|
55
|
+
* Only checks languages that have tree-sitter grammars.
|
|
56
|
+
*/
|
|
57
|
+
export class SyntaxGuard {
|
|
58
|
+
constructor() {
|
|
59
|
+
this.splitter = new AstCodeSplitter();
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Get language from file extension
|
|
63
|
+
*/
|
|
64
|
+
getLanguageFromExtension(ext) {
|
|
65
|
+
return EXTENSION_TO_LANGUAGE[ext.toLowerCase()] || 'unknown';
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Check if a language is supported for syntax checking
|
|
69
|
+
*/
|
|
70
|
+
isLanguageSupported(language) {
|
|
71
|
+
return SUPPORTED_LANGUAGES.has(language);
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Check file content for syntax errors
|
|
75
|
+
*
|
|
76
|
+
* @param content - File content to check
|
|
77
|
+
* @param language - Language identifier (e.g., 'typescript', 'python')
|
|
78
|
+
* @returns SyntaxCheckResult with error information
|
|
79
|
+
*/
|
|
80
|
+
checkSyntax(content, language) {
|
|
81
|
+
// If language is not supported by tree-sitter, allow indexing
|
|
82
|
+
if (!this.isLanguageSupported(language)) {
|
|
83
|
+
return {
|
|
84
|
+
hasErrors: false,
|
|
85
|
+
errorCount: 0,
|
|
86
|
+
language,
|
|
87
|
+
supported: false,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
try {
|
|
91
|
+
const result = this.splitter.checkSyntaxErrors(content, language);
|
|
92
|
+
return {
|
|
93
|
+
hasErrors: result.hasErrors,
|
|
94
|
+
errorCount: result.errorCount,
|
|
95
|
+
language,
|
|
96
|
+
supported: true,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
// If parsing fails completely, treat as having errors
|
|
101
|
+
// This is conservative - better to keep old vectors than risk losing them
|
|
102
|
+
console.error(`[SyntaxGuard] Error checking syntax for ${language}:`, error);
|
|
103
|
+
return {
|
|
104
|
+
hasErrors: true,
|
|
105
|
+
errorCount: 1,
|
|
106
|
+
language,
|
|
107
|
+
supported: true,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Check file by extension
|
|
113
|
+
*
|
|
114
|
+
* Convenience method that determines language from extension.
|
|
115
|
+
*
|
|
116
|
+
* @param content - File content to check
|
|
117
|
+
* @param ext - File extension (e.g., '.ts', '.py')
|
|
118
|
+
* @returns SyntaxCheckResult with error information
|
|
119
|
+
*/
|
|
120
|
+
checkSyntaxByExtension(content, ext) {
|
|
121
|
+
const language = this.getLanguageFromExtension(ext);
|
|
122
|
+
return this.checkSyntax(content, language);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
// Singleton instance for shared use
|
|
126
|
+
let sharedInstance = null;
|
|
127
|
+
/**
|
|
128
|
+
* Get shared SyntaxGuard instance
|
|
129
|
+
*/
|
|
130
|
+
export function getSyntaxGuard() {
|
|
131
|
+
if (!sharedInstance) {
|
|
132
|
+
sharedInstance = new SyntaxGuard();
|
|
133
|
+
}
|
|
134
|
+
return sharedInstance;
|
|
135
|
+
}
|
|
136
|
+
//# sourceMappingURL=syntax-guard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"syntax-guard.js","sourceRoot":"","sources":["../../src/watcher/syntax-guard.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@sharc-code/mcp",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "SHARC MCP Server - Semantic code search for AI assistants",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"bin": "dist/index.js",
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "bun run clean && tsc --build --force",
|
|
11
|
+
"dev": "tsx --watch src/index.ts",
|
|
12
|
+
"clean": "rimraf dist",
|
|
13
|
+
"lint": "eslint src --ext .ts",
|
|
14
|
+
"lint:fix": "eslint src --ext .ts --fix",
|
|
15
|
+
"typecheck": "tsc --noEmit",
|
|
16
|
+
"start": "tsx src/index.ts",
|
|
17
|
+
"start:with-env": "OPENAI_API_KEY=${OPENAI_API_KEY:your-api-key-here} MILVUS_ADDRESS=${MILVUS_ADDRESS:localhost:19530} tsx src/index.ts",
|
|
18
|
+
"prepublishOnly": "bun run build"
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
22
|
+
"@sharc-code/splitter": "^0.2.0",
|
|
23
|
+
"chokidar": "^3.6.0",
|
|
24
|
+
"dotenv": "^17.2.3",
|
|
25
|
+
"zod": "^4.1.0"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@types/node": "^20.0.0",
|
|
29
|
+
"tsx": "^4.19.4",
|
|
30
|
+
"typescript": "^5.0.0"
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"dist",
|
|
34
|
+
"README.md"
|
|
35
|
+
],
|
|
36
|
+
"keywords": [
|
|
37
|
+
"code-search",
|
|
38
|
+
"semantic-search",
|
|
39
|
+
"mcp",
|
|
40
|
+
"model-context-protocol",
|
|
41
|
+
"ai",
|
|
42
|
+
"embeddings",
|
|
43
|
+
"tree-sitter",
|
|
44
|
+
"ast"
|
|
45
|
+
],
|
|
46
|
+
"author": "SHARC <npm@sharc.co>",
|
|
47
|
+
"homepage": "https://sharc.co",
|
|
48
|
+
"license": "MIT",
|
|
49
|
+
"publishConfig": {
|
|
50
|
+
"access": "public"
|
|
51
|
+
}
|
|
52
|
+
}
|