@softerist/heuristic-mcp 3.2.2 → 3.2.4
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 +387 -376
- package/config.jsonc +800 -800
- package/features/ann-config.js +102 -110
- package/features/clear-cache.js +81 -84
- package/features/find-similar-code.js +265 -286
- package/features/hybrid-search.js +487 -536
- package/features/index-codebase.js +3139 -3270
- package/features/lifecycle.js +1041 -1063
- package/features/package-version.js +277 -291
- package/features/register.js +351 -370
- package/features/resources.js +115 -130
- package/features/set-workspace.js +214 -240
- package/index.js +742 -762
- package/lib/cache-ops.js +22 -22
- package/lib/cache-utils.js +465 -519
- package/lib/cache.js +1699 -1767
- package/lib/call-graph.js +396 -396
- package/lib/cli.js +232 -226
- package/lib/config.js +1483 -1495
- package/lib/constants.js +511 -492
- package/lib/embed-query-process.js +206 -212
- package/lib/embedding-process.js +434 -451
- package/lib/embedding-worker.js +862 -934
- package/lib/ignore-patterns.js +276 -316
- package/lib/json-worker.js +14 -14
- package/lib/json-writer.js +302 -310
- package/lib/logging.js +116 -127
- package/lib/memory-logger.js +13 -13
- package/lib/onnx-backend.js +188 -193
- package/lib/path-utils.js +18 -23
- package/lib/project-detector.js +82 -84
- package/lib/server-lifecycle.js +133 -145
- package/lib/settings-editor.js +738 -739
- package/lib/slice-normalize.js +25 -31
- package/lib/tokenizer.js +168 -203
- package/lib/utils.js +364 -409
- package/lib/vector-store-binary.js +811 -591
- package/lib/vector-store-sqlite.js +377 -414
- package/lib/workspace-env.js +32 -34
- package/mcp_config.json +9 -9
- package/package.json +86 -86
- package/scripts/clear-cache.js +20 -20
- package/scripts/download-model.js +43 -43
- package/scripts/mcp-launcher.js +49 -49
- package/scripts/postinstall.js +12 -12
- package/search-configs.js +36 -36
package/lib/onnx-backend.js
CHANGED
|
@@ -1,193 +1,188 @@
|
|
|
1
|
-
import { env } from '@huggingface/transformers';
|
|
2
|
-
import { createRequire } from 'module';
|
|
3
|
-
|
|
4
|
-
const require = createRequire(import.meta.url);
|
|
5
|
-
const IS_TEST_ENV = process.env.VITEST === 'true' || process.env.NODE_ENV === 'test';
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
let
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
let
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
transformersPkg?.
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
const
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
if (typeof threads
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
if (
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
if (
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
if (
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
log(`${label ? `${label} ` : ''}ONNX backend set to onnxruntime-node (cpu)`);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
return true;
|
|
193
|
-
}
|
|
1
|
+
import { env } from '@huggingface/transformers';
|
|
2
|
+
import { createRequire } from 'module';
|
|
3
|
+
|
|
4
|
+
const require = createRequire(import.meta.url);
|
|
5
|
+
const IS_TEST_ENV = process.env.VITEST === 'true' || process.env.NODE_ENV === 'test';
|
|
6
|
+
|
|
7
|
+
let ort = null;
|
|
8
|
+
let ortLoadError = null;
|
|
9
|
+
try {
|
|
10
|
+
ort = await import('onnxruntime-node');
|
|
11
|
+
} catch (e) {
|
|
12
|
+
ortLoadError = e;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let ortVersion = null;
|
|
16
|
+
let expectedOrtVersion = null;
|
|
17
|
+
try {
|
|
18
|
+
ortVersion = require('onnxruntime-node/package.json')?.version || null;
|
|
19
|
+
} catch {
|
|
20
|
+
ortVersion = null;
|
|
21
|
+
}
|
|
22
|
+
try {
|
|
23
|
+
const transformersPkg = require('@huggingface/transformers/package.json');
|
|
24
|
+
expectedOrtVersion =
|
|
25
|
+
transformersPkg?.optionalDependencies?.['onnxruntime-node'] ||
|
|
26
|
+
transformersPkg?.dependencies?.['onnxruntime-node'] ||
|
|
27
|
+
null;
|
|
28
|
+
} catch {
|
|
29
|
+
expectedOrtVersion = null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
let executionProviders = null;
|
|
33
|
+
let ONNX = null;
|
|
34
|
+
if (!IS_TEST_ENV) {
|
|
35
|
+
try {
|
|
36
|
+
const dynamicImport = new Function('specifier', 'return import(specifier)');
|
|
37
|
+
const backend = await dynamicImport('@huggingface/transformers/src/backends/onnx.js');
|
|
38
|
+
executionProviders = backend.executionProviders;
|
|
39
|
+
ONNX = backend.ONNX;
|
|
40
|
+
} catch {}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
let sessionThreadOptions = null;
|
|
44
|
+
let originalCreate = null;
|
|
45
|
+
let createPatched = false;
|
|
46
|
+
|
|
47
|
+
export function getNativeOnnxStatus() {
|
|
48
|
+
if (typeof process === 'undefined' || process?.release?.name !== 'node') {
|
|
49
|
+
return { available: false, reason: 'not_node' };
|
|
50
|
+
}
|
|
51
|
+
if (ortVersion && expectedOrtVersion) {
|
|
52
|
+
const actualParts = String(ortVersion).split('.');
|
|
53
|
+
const expectedParts = String(expectedOrtVersion)
|
|
54
|
+
.replace(/^[^0-9]*/, '')
|
|
55
|
+
.split('.');
|
|
56
|
+
const actualMajor = Number(actualParts[0] || 0);
|
|
57
|
+
const actualMinor = Number(actualParts[1] || 0);
|
|
58
|
+
const expectedMajor = Number(expectedParts[0] || 0);
|
|
59
|
+
const expectedMinor = Number(expectedParts[1] || 0);
|
|
60
|
+
if (
|
|
61
|
+
Number.isFinite(actualMajor) &&
|
|
62
|
+
Number.isFinite(actualMinor) &&
|
|
63
|
+
Number.isFinite(expectedMajor) &&
|
|
64
|
+
Number.isFinite(expectedMinor) &&
|
|
65
|
+
(actualMajor !== expectedMajor || actualMinor !== expectedMinor)
|
|
66
|
+
) {
|
|
67
|
+
return {
|
|
68
|
+
available: false,
|
|
69
|
+
reason: 'version_mismatch',
|
|
70
|
+
message: `onnxruntime-node ${ortVersion} incompatible with transformers.js expectation ${expectedOrtVersion}`,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (!ort?.InferenceSession) {
|
|
75
|
+
return {
|
|
76
|
+
available: false,
|
|
77
|
+
reason: 'unavailable',
|
|
78
|
+
error: ortLoadError,
|
|
79
|
+
message: ortLoadError?.message || 'onnxruntime-node not available',
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
return { available: true };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function normalizeThreadOptions(threads) {
|
|
86
|
+
if (threads == null) return null;
|
|
87
|
+
if (typeof threads === 'number') {
|
|
88
|
+
const value = Number.isFinite(threads) ? Math.floor(threads) : null;
|
|
89
|
+
if (value && value > 0) return { intraOpNumThreads: value };
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
if (typeof threads !== 'object') return null;
|
|
93
|
+
const intra = Number.isFinite(threads.intraOpNumThreads)
|
|
94
|
+
? Math.floor(threads.intraOpNumThreads)
|
|
95
|
+
: null;
|
|
96
|
+
const inter = Number.isFinite(threads.interOpNumThreads)
|
|
97
|
+
? Math.floor(threads.interOpNumThreads)
|
|
98
|
+
: null;
|
|
99
|
+
const normalized = {};
|
|
100
|
+
if (intra && intra > 0) normalized.intraOpNumThreads = intra;
|
|
101
|
+
if (inter && inter > 0) normalized.interOpNumThreads = inter;
|
|
102
|
+
return Object.keys(normalized).length ? normalized : null;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function isOptionsCandidate(value) {
|
|
106
|
+
if (!value || typeof value !== 'object') return false;
|
|
107
|
+
if (ArrayBuffer.isView(value)) return false;
|
|
108
|
+
if (value instanceof ArrayBuffer) return false;
|
|
109
|
+
if (typeof SharedArrayBuffer !== 'undefined' && value instanceof SharedArrayBuffer) return false;
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function findOptionsIndex(args) {
|
|
114
|
+
if (isOptionsCandidate(args[1])) return 1;
|
|
115
|
+
if (typeof args[1] === 'number') {
|
|
116
|
+
if (isOptionsCandidate(args[2])) return 2;
|
|
117
|
+
if (typeof args[2] === 'number' && isOptionsCandidate(args[3])) return 3;
|
|
118
|
+
}
|
|
119
|
+
return -1;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function patchInferenceSessionCreate() {
|
|
123
|
+
if (createPatched || !ONNX?.InferenceSession?.create) return false;
|
|
124
|
+
originalCreate = ONNX.InferenceSession.create.bind(ONNX.InferenceSession);
|
|
125
|
+
ONNX.InferenceSession.create = (...args) => {
|
|
126
|
+
if (sessionThreadOptions) {
|
|
127
|
+
const optionsIndex = findOptionsIndex(args);
|
|
128
|
+
if (optionsIndex >= 0) {
|
|
129
|
+
const merged = { ...args[optionsIndex] };
|
|
130
|
+
if (merged.intraOpNumThreads == null && sessionThreadOptions.intraOpNumThreads != null) {
|
|
131
|
+
merged.intraOpNumThreads = sessionThreadOptions.intraOpNumThreads;
|
|
132
|
+
}
|
|
133
|
+
if (merged.interOpNumThreads == null && sessionThreadOptions.interOpNumThreads != null) {
|
|
134
|
+
merged.interOpNumThreads = sessionThreadOptions.interOpNumThreads;
|
|
135
|
+
}
|
|
136
|
+
args[optionsIndex] = merged;
|
|
137
|
+
} else {
|
|
138
|
+
args.push({ ...sessionThreadOptions });
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return originalCreate(...args);
|
|
142
|
+
};
|
|
143
|
+
createPatched = true;
|
|
144
|
+
return true;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export function configureNativeOnnxBackend({ log, label, threads } = {}) {
|
|
148
|
+
const status = getNativeOnnxStatus();
|
|
149
|
+
if (!status.available) {
|
|
150
|
+
if (log) {
|
|
151
|
+
const msg = status.message || status.reason || 'onnxruntime-node not available';
|
|
152
|
+
log(
|
|
153
|
+
`${label ? `${label} ` : ''}Native ONNX backend unavailable: ${msg}. Falling back to WASM.`
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (Array.isArray(executionProviders)) {
|
|
160
|
+
if (executionProviders.length !== 1 || executionProviders[0] !== 'cpu') {
|
|
161
|
+
executionProviders.length = 0;
|
|
162
|
+
executionProviders.push('cpu');
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (env.backends?.onnx?.wasm && typeof env.backends.onnx.wasm === 'object') {
|
|
167
|
+
env.backends.onnx.wasm.proxy = false;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const normalizedThreads = normalizeThreadOptions(threads);
|
|
171
|
+
if (normalizedThreads) {
|
|
172
|
+
sessionThreadOptions = normalizedThreads;
|
|
173
|
+
patchInferenceSessionCreate();
|
|
174
|
+
if (log) {
|
|
175
|
+
const intra = normalizedThreads.intraOpNumThreads ?? 'default';
|
|
176
|
+
const inter = normalizedThreads.interOpNumThreads ?? 'default';
|
|
177
|
+
log(
|
|
178
|
+
`${label ? `${label} ` : ''}ONNX session threads set (intraOp=${intra}, interOp=${inter})`
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (log) {
|
|
184
|
+
log(`${label ? `${label} ` : ''}ONNX backend set to onnxruntime-node (cpu)`);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return true;
|
|
188
|
+
}
|
package/lib/path-utils.js
CHANGED
|
@@ -1,23 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
return
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
export function normalizePathKey(filePath) {
|
|
22
|
-
return normalizePath(filePath);
|
|
23
|
-
}
|
|
1
|
+
import path from 'path';
|
|
2
|
+
|
|
3
|
+
export function normalizePath(value) {
|
|
4
|
+
if (typeof value !== 'string') return '';
|
|
5
|
+
const normalized = value.replace(/\\/g, '/');
|
|
6
|
+
return process.platform === 'win32' ? normalized.toLowerCase() : normalized;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function isPathInside(basePath, targetPath) {
|
|
10
|
+
const normalizedBase = normalizePath(basePath);
|
|
11
|
+
const normalizedTarget = normalizePath(targetPath);
|
|
12
|
+
const relative = path.relative(normalizedBase, normalizedTarget);
|
|
13
|
+
return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function normalizePathKey(filePath) {
|
|
17
|
+
return normalizePath(filePath);
|
|
18
|
+
}
|
package/lib/project-detector.js
CHANGED
|
@@ -1,84 +1,82 @@
|
|
|
1
|
-
import fs from 'fs/promises';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import { FILE_TYPE_MAP, IGNORE_PATTERNS, SKIP_DIRECTORIES } from './ignore-patterns.js';
|
|
4
|
-
|
|
5
|
-
export class ProjectDetector {
|
|
6
|
-
constructor(searchDirectory) {
|
|
7
|
-
this.searchDirectory = searchDirectory;
|
|
8
|
-
this.detectedTypes = new Set();
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
async detectProjectTypes(options = {}) {
|
|
12
|
-
const maxDepth = typeof options.maxDepth === 'number' ? options.maxDepth : 2;
|
|
13
|
-
const startDepth = typeof options.startDepth === 'number' ? options.startDepth : 0;
|
|
14
|
-
const markerFiles = Object.keys(FILE_TYPE_MAP);
|
|
15
|
-
const discoveredTypes = new Map();
|
|
16
|
-
|
|
17
|
-
const checkDir = async (dir, depth) => {
|
|
18
|
-
if (depth > maxDepth) return;
|
|
19
|
-
|
|
20
|
-
const items = await fs.readdir(dir, { withFileTypes: true }).catch(() => []);
|
|
21
|
-
const itemNames = items.map((i) => i.name);
|
|
22
|
-
const itemSet = new Set(itemNames);
|
|
23
|
-
|
|
24
|
-
for (const marker of markerFiles) {
|
|
25
|
-
let found = false;
|
|
26
|
-
if (marker.includes('*')) {
|
|
27
|
-
const regex = new RegExp('^' + marker.replace('*', '.*') + '$');
|
|
28
|
-
found = itemNames.some((file) => regex.test(file));
|
|
29
|
-
} else {
|
|
30
|
-
found = itemSet.has(marker);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
if (found) {
|
|
34
|
-
const type = FILE_TYPE_MAP[marker];
|
|
35
|
-
if (!discoveredTypes.has(type)) {
|
|
36
|
-
discoveredTypes.set(type, path.relative(this.searchDirectory, path.join(dir, marker)));
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}
|
|
84
|
-
}
|
|
1
|
+
import fs from 'fs/promises';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { FILE_TYPE_MAP, IGNORE_PATTERNS, SKIP_DIRECTORIES } from './ignore-patterns.js';
|
|
4
|
+
|
|
5
|
+
export class ProjectDetector {
|
|
6
|
+
constructor(searchDirectory) {
|
|
7
|
+
this.searchDirectory = searchDirectory;
|
|
8
|
+
this.detectedTypes = new Set();
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async detectProjectTypes(options = {}) {
|
|
12
|
+
const maxDepth = typeof options.maxDepth === 'number' ? options.maxDepth : 2;
|
|
13
|
+
const startDepth = typeof options.startDepth === 'number' ? options.startDepth : 0;
|
|
14
|
+
const markerFiles = Object.keys(FILE_TYPE_MAP);
|
|
15
|
+
const discoveredTypes = new Map();
|
|
16
|
+
|
|
17
|
+
const checkDir = async (dir, depth) => {
|
|
18
|
+
if (depth > maxDepth) return;
|
|
19
|
+
|
|
20
|
+
const items = await fs.readdir(dir, { withFileTypes: true }).catch(() => []);
|
|
21
|
+
const itemNames = items.map((i) => i.name);
|
|
22
|
+
const itemSet = new Set(itemNames);
|
|
23
|
+
|
|
24
|
+
for (const marker of markerFiles) {
|
|
25
|
+
let found = false;
|
|
26
|
+
if (marker.includes('*')) {
|
|
27
|
+
const regex = new RegExp('^' + marker.replace('*', '.*') + '$');
|
|
28
|
+
found = itemNames.some((file) => regex.test(file));
|
|
29
|
+
} else {
|
|
30
|
+
found = itemSet.has(marker);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (found) {
|
|
34
|
+
const type = FILE_TYPE_MAP[marker];
|
|
35
|
+
if (!discoveredTypes.has(type)) {
|
|
36
|
+
discoveredTypes.set(type, path.relative(this.searchDirectory, path.join(dir, marker)));
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (depth < maxDepth) {
|
|
42
|
+
for (const item of items) {
|
|
43
|
+
if (item.isDirectory()) {
|
|
44
|
+
const name = item.name;
|
|
45
|
+
if (name.startsWith('.') || SKIP_DIRECTORIES.includes(name)) {
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
await checkDir(path.join(dir, name), depth + 1);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
await checkDir(this.searchDirectory, startDepth);
|
|
55
|
+
|
|
56
|
+
for (const [type, marker] of discoveredTypes) {
|
|
57
|
+
this.detectedTypes.add(type);
|
|
58
|
+
console.info(`[Detector] Detected ${type} project (${marker})`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return Array.from(this.detectedTypes);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
getSmartIgnorePatterns() {
|
|
65
|
+
const patterns = [...IGNORE_PATTERNS.common];
|
|
66
|
+
|
|
67
|
+
for (const type of this.detectedTypes) {
|
|
68
|
+
if (IGNORE_PATTERNS[type]) {
|
|
69
|
+
patterns.push(...IGNORE_PATTERNS[type]);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return [...new Set(patterns)];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
getSummary() {
|
|
77
|
+
return {
|
|
78
|
+
detectedTypes: Array.from(this.detectedTypes),
|
|
79
|
+
patternCount: this.getSmartIgnorePatterns().length,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
}
|