aios-core 4.2.4 → 4.2.6
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/.aios-core/core/code-intel/code-intel-client.js +280 -0
- package/.aios-core/core/code-intel/code-intel-enricher.js +159 -0
- package/.aios-core/core/code-intel/index.js +137 -0
- package/.aios-core/core/code-intel/providers/code-graph-provider.js +201 -0
- package/.aios-core/core/code-intel/providers/provider-interface.js +108 -0
- package/.aios-core/data/entity-registry.yaml +1330 -1086
- package/.aios-core/data/registry-update-log.jsonl +6 -0
- package/.aios-core/install-manifest.yaml +25 -5
- package/package.json +2 -1
- package/packages/installer/src/wizard/pro-setup.js +8 -7
- package/pro/README.md +66 -0
- package/pro/license/degradation.js +220 -0
- package/pro/license/errors.js +450 -0
- package/pro/license/feature-gate.js +354 -0
- package/pro/license/index.js +181 -0
- package/pro/license/license-api.js +617 -0
- package/pro/license/license-cache.js +523 -0
- package/pro/license/license-crypto.js +303 -0
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { CodeGraphProvider } = require('./providers/code-graph-provider');
|
|
4
|
+
|
|
5
|
+
// --- Constants (adjustable, not hardcoded magic numbers) ---
|
|
6
|
+
const CIRCUIT_BREAKER_THRESHOLD = 3;
|
|
7
|
+
const CIRCUIT_BREAKER_RESET_MS = 60000;
|
|
8
|
+
const CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes
|
|
9
|
+
|
|
10
|
+
// Circuit breaker states
|
|
11
|
+
const CB_CLOSED = 'CLOSED';
|
|
12
|
+
const CB_OPEN = 'OPEN';
|
|
13
|
+
const CB_HALF_OPEN = 'HALF-OPEN';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* CodeIntelClient — Central entry point for code intelligence.
|
|
17
|
+
*
|
|
18
|
+
* Features:
|
|
19
|
+
* - Provider auto-detection and registry
|
|
20
|
+
* - Circuit breaker pattern (threshold 3, reset 60s)
|
|
21
|
+
* - Session cache (Map-based, TTL 5min)
|
|
22
|
+
* - Graceful fallback (returns null without throw when no provider available)
|
|
23
|
+
* - Latency logging per capability
|
|
24
|
+
* - Cache hit/miss counters
|
|
25
|
+
*/
|
|
26
|
+
class CodeIntelClient {
|
|
27
|
+
constructor(options = {}) {
|
|
28
|
+
this._providers = [];
|
|
29
|
+
this._activeProvider = null;
|
|
30
|
+
this._options = options;
|
|
31
|
+
|
|
32
|
+
// Circuit breaker state
|
|
33
|
+
this._cbState = CB_CLOSED;
|
|
34
|
+
this._cbFailures = 0;
|
|
35
|
+
this._cbOpenedAt = null;
|
|
36
|
+
|
|
37
|
+
// Session cache
|
|
38
|
+
this._cache = new Map();
|
|
39
|
+
|
|
40
|
+
// Metrics
|
|
41
|
+
this._cacheHits = 0;
|
|
42
|
+
this._cacheMisses = 0;
|
|
43
|
+
this._latencyLog = [];
|
|
44
|
+
|
|
45
|
+
// Warning dedup
|
|
46
|
+
this._noProviderWarned = false;
|
|
47
|
+
|
|
48
|
+
// Auto-register default providers
|
|
49
|
+
this._registerDefaultProviders(options);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Register default providers based on configuration.
|
|
54
|
+
* @private
|
|
55
|
+
*/
|
|
56
|
+
_registerDefaultProviders(options) {
|
|
57
|
+
// Code Graph MCP is the primary (and currently only) provider
|
|
58
|
+
const codeGraphProvider = new CodeGraphProvider({
|
|
59
|
+
mcpServerName: options.mcpServerName || 'code-graph',
|
|
60
|
+
mcpCallFn: options.mcpCallFn || null,
|
|
61
|
+
});
|
|
62
|
+
this._providers.push(codeGraphProvider);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Register an additional provider.
|
|
67
|
+
* @param {import('./providers/provider-interface').CodeIntelProvider} provider
|
|
68
|
+
*/
|
|
69
|
+
registerProvider(provider) {
|
|
70
|
+
this._providers.push(provider);
|
|
71
|
+
// Reset active provider so next call re-detects
|
|
72
|
+
this._activeProvider = null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Detect and return the first available provider.
|
|
77
|
+
* @returns {import('./providers/provider-interface').CodeIntelProvider|null}
|
|
78
|
+
* @private
|
|
79
|
+
*/
|
|
80
|
+
_detectProvider() {
|
|
81
|
+
if (this._activeProvider) return this._activeProvider;
|
|
82
|
+
|
|
83
|
+
for (const provider of this._providers) {
|
|
84
|
+
// A provider is considered "available" if it has a configured mcpCallFn
|
|
85
|
+
if (provider.options && typeof provider.options.mcpCallFn === 'function') {
|
|
86
|
+
this._activeProvider = provider;
|
|
87
|
+
return provider;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Check if code intelligence is available.
|
|
96
|
+
* @returns {boolean}
|
|
97
|
+
*/
|
|
98
|
+
isCodeIntelAvailable() {
|
|
99
|
+
return this._detectProvider() !== null;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Execute a capability with circuit breaker, cache, and fallback.
|
|
104
|
+
* @param {string} capability - One of the 8 primitive capability names
|
|
105
|
+
* @param {Array} args - Arguments to pass to the capability
|
|
106
|
+
* @returns {Promise<*>} Result or null on fallback
|
|
107
|
+
*/
|
|
108
|
+
async _executeCapability(capability, args) {
|
|
109
|
+
const startTime = Date.now();
|
|
110
|
+
|
|
111
|
+
// Check provider availability
|
|
112
|
+
const provider = this._detectProvider();
|
|
113
|
+
if (!provider) {
|
|
114
|
+
if (!this._noProviderWarned) {
|
|
115
|
+
console.warn('[code-intel] No provider available. Code intelligence features disabled.');
|
|
116
|
+
this._noProviderWarned = true;
|
|
117
|
+
}
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Check cache
|
|
122
|
+
const cacheKey = `${capability}:${JSON.stringify(args)}`;
|
|
123
|
+
const cached = this._getFromCache(cacheKey);
|
|
124
|
+
if (cached !== undefined) {
|
|
125
|
+
this._cacheHits++;
|
|
126
|
+
this._logLatency(capability, Date.now() - startTime, true);
|
|
127
|
+
return cached;
|
|
128
|
+
}
|
|
129
|
+
this._cacheMisses++;
|
|
130
|
+
|
|
131
|
+
// Check circuit breaker
|
|
132
|
+
if (this._cbState === CB_OPEN) {
|
|
133
|
+
if (Date.now() - this._cbOpenedAt >= CIRCUIT_BREAKER_RESET_MS) {
|
|
134
|
+
this._cbState = CB_HALF_OPEN;
|
|
135
|
+
} else {
|
|
136
|
+
this._logLatency(capability, Date.now() - startTime, false);
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Execute capability
|
|
142
|
+
try {
|
|
143
|
+
const result = await provider[capability](...args);
|
|
144
|
+
this._onSuccess();
|
|
145
|
+
this._putInCache(cacheKey, result);
|
|
146
|
+
this._logLatency(capability, Date.now() - startTime, false);
|
|
147
|
+
return result;
|
|
148
|
+
} catch (_error) {
|
|
149
|
+
this._onFailure();
|
|
150
|
+
this._logLatency(capability, Date.now() - startTime, false);
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// --- Circuit breaker helpers ---
|
|
156
|
+
|
|
157
|
+
_onSuccess() {
|
|
158
|
+
this._cbFailures = 0;
|
|
159
|
+
if (this._cbState === CB_HALF_OPEN) {
|
|
160
|
+
this._cbState = CB_CLOSED;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
_onFailure() {
|
|
165
|
+
this._cbFailures++;
|
|
166
|
+
if (this._cbFailures >= CIRCUIT_BREAKER_THRESHOLD) {
|
|
167
|
+
this._cbState = CB_OPEN;
|
|
168
|
+
this._cbOpenedAt = Date.now();
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
getCircuitBreakerState() {
|
|
173
|
+
// Re-check if open timer expired
|
|
174
|
+
if (this._cbState === CB_OPEN && Date.now() - this._cbOpenedAt >= CIRCUIT_BREAKER_RESET_MS) {
|
|
175
|
+
this._cbState = CB_HALF_OPEN;
|
|
176
|
+
}
|
|
177
|
+
return this._cbState;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// --- Cache helpers ---
|
|
181
|
+
|
|
182
|
+
_getFromCache(key) {
|
|
183
|
+
const entry = this._cache.get(key);
|
|
184
|
+
if (!entry) return undefined;
|
|
185
|
+
if (Date.now() - entry.timestamp > CACHE_TTL_MS) {
|
|
186
|
+
this._cache.delete(key);
|
|
187
|
+
return undefined;
|
|
188
|
+
}
|
|
189
|
+
return entry.value;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
_putInCache(key, value) {
|
|
193
|
+
// Evict expired entries periodically (every 50 puts)
|
|
194
|
+
if (this._cache.size > 0 && this._cache.size % 50 === 0) {
|
|
195
|
+
this._evictExpired();
|
|
196
|
+
}
|
|
197
|
+
this._cache.set(key, { value, timestamp: Date.now() });
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
_evictExpired() {
|
|
201
|
+
const now = Date.now();
|
|
202
|
+
for (const [key, entry] of this._cache) {
|
|
203
|
+
if (now - entry.timestamp > CACHE_TTL_MS) {
|
|
204
|
+
this._cache.delete(key);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// --- Latency logging ---
|
|
210
|
+
|
|
211
|
+
_logLatency(capability, durationMs, isCacheHit) {
|
|
212
|
+
this._latencyLog.push({
|
|
213
|
+
capability,
|
|
214
|
+
durationMs,
|
|
215
|
+
isCacheHit,
|
|
216
|
+
timestamp: Date.now(),
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// --- Metrics ---
|
|
221
|
+
|
|
222
|
+
getMetrics() {
|
|
223
|
+
return {
|
|
224
|
+
cacheHits: this._cacheHits,
|
|
225
|
+
cacheMisses: this._cacheMisses,
|
|
226
|
+
cacheHitRate:
|
|
227
|
+
this._cacheHits + this._cacheMisses > 0
|
|
228
|
+
? this._cacheHits / (this._cacheHits + this._cacheMisses)
|
|
229
|
+
: 0,
|
|
230
|
+
circuitBreakerState: this.getCircuitBreakerState(),
|
|
231
|
+
latencyLog: this._latencyLog,
|
|
232
|
+
providerAvailable: this.isCodeIntelAvailable(),
|
|
233
|
+
activeProvider: this._activeProvider ? this._activeProvider.name : null,
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// --- 8 Primitive Capabilities (public API) ---
|
|
238
|
+
|
|
239
|
+
async findDefinition(symbol, options) {
|
|
240
|
+
return this._executeCapability('findDefinition', [symbol, options]);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
async findReferences(symbol, options) {
|
|
244
|
+
return this._executeCapability('findReferences', [symbol, options]);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
async findCallers(symbol, options) {
|
|
248
|
+
return this._executeCapability('findCallers', [symbol, options]);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
async findCallees(symbol, options) {
|
|
252
|
+
return this._executeCapability('findCallees', [symbol, options]);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
async analyzeDependencies(path, options) {
|
|
256
|
+
return this._executeCapability('analyzeDependencies', [path, options]);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
async analyzeComplexity(path, options) {
|
|
260
|
+
return this._executeCapability('analyzeComplexity', [path, options]);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
async analyzeCodebase(path, options) {
|
|
264
|
+
return this._executeCapability('analyzeCodebase', [path, options]);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
async getProjectStats(options) {
|
|
268
|
+
return this._executeCapability('getProjectStats', [options]);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
module.exports = {
|
|
273
|
+
CodeIntelClient,
|
|
274
|
+
CIRCUIT_BREAKER_THRESHOLD,
|
|
275
|
+
CIRCUIT_BREAKER_RESET_MS,
|
|
276
|
+
CACHE_TTL_MS,
|
|
277
|
+
CB_CLOSED,
|
|
278
|
+
CB_OPEN,
|
|
279
|
+
CB_HALF_OPEN,
|
|
280
|
+
};
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* CodeIntelEnricher — Composite capabilities built on top of primitive capabilities.
|
|
5
|
+
*
|
|
6
|
+
* Each method composes multiple primitive capabilities from CodeIntelClient
|
|
7
|
+
* to provide higher-level analysis functions used by AIOS tasks.
|
|
8
|
+
*/
|
|
9
|
+
class CodeIntelEnricher {
|
|
10
|
+
/**
|
|
11
|
+
* @param {import('./code-intel-client').CodeIntelClient} client
|
|
12
|
+
*/
|
|
13
|
+
constructor(client) {
|
|
14
|
+
this._client = client;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Assess impact of changes to given files.
|
|
19
|
+
* Composition: findReferences + analyzeComplexity
|
|
20
|
+
*
|
|
21
|
+
* @param {string[]} files - Files to assess impact for
|
|
22
|
+
* @returns {Promise<{references: Array, complexity: Object, blastRadius: number}|null>}
|
|
23
|
+
*/
|
|
24
|
+
async assessImpact(files) {
|
|
25
|
+
if (!files || files.length === 0) return null;
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
const results = await Promise.all(
|
|
29
|
+
files.map(async (file) => {
|
|
30
|
+
try {
|
|
31
|
+
const [refs, complexity] = await Promise.all([
|
|
32
|
+
this._client.findReferences(file),
|
|
33
|
+
this._client.analyzeComplexity(file),
|
|
34
|
+
]);
|
|
35
|
+
return { file, references: refs, complexity };
|
|
36
|
+
} catch {
|
|
37
|
+
return { file, references: null, complexity: null };
|
|
38
|
+
}
|
|
39
|
+
}),
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
const allRefs = results.flatMap((r) => r.references || []);
|
|
43
|
+
const avgComplexity =
|
|
44
|
+
results.reduce((sum, r) => sum + ((r.complexity && r.complexity.score) || 0), 0) /
|
|
45
|
+
(results.length || 1);
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
references: allRefs,
|
|
49
|
+
complexity: { average: avgComplexity, perFile: results },
|
|
50
|
+
blastRadius: allRefs.length,
|
|
51
|
+
};
|
|
52
|
+
} catch {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Detect potential duplicates for a description/concept.
|
|
59
|
+
* Composition: findReferences + analyzeCodebase
|
|
60
|
+
*
|
|
61
|
+
* @param {string} description - Description of the capability to check
|
|
62
|
+
* @param {Object} [options] - Search options
|
|
63
|
+
* @returns {Promise<{matches: Array, codebaseOverview: Object}|null>}
|
|
64
|
+
*/
|
|
65
|
+
async detectDuplicates(description, options = {}) {
|
|
66
|
+
try {
|
|
67
|
+
const [refs, codebase] = await Promise.all([
|
|
68
|
+
this._client.findReferences(description, options),
|
|
69
|
+
this._client.analyzeCodebase(options.path || '.', options),
|
|
70
|
+
]);
|
|
71
|
+
|
|
72
|
+
if (!refs && !codebase) return null;
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
matches: refs || [],
|
|
76
|
+
codebaseOverview: codebase || {},
|
|
77
|
+
};
|
|
78
|
+
} catch {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Get coding conventions for a path.
|
|
85
|
+
* Composition: analyzeCodebase + getProjectStats
|
|
86
|
+
*
|
|
87
|
+
* @param {string} path - Path to analyze conventions for
|
|
88
|
+
* @returns {Promise<{patterns: Array, stats: Object}|null>}
|
|
89
|
+
*/
|
|
90
|
+
async getConventions(path) {
|
|
91
|
+
try {
|
|
92
|
+
const [codebase, stats] = await Promise.all([
|
|
93
|
+
this._client.analyzeCodebase(path),
|
|
94
|
+
this._client.getProjectStats(),
|
|
95
|
+
]);
|
|
96
|
+
|
|
97
|
+
if (!codebase && !stats) return null;
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
patterns: (codebase && codebase.patterns) || [],
|
|
101
|
+
stats: stats || {},
|
|
102
|
+
};
|
|
103
|
+
} catch {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Find tests related to a symbol.
|
|
110
|
+
* Composition: findReferences (filtered for test/spec files)
|
|
111
|
+
*
|
|
112
|
+
* @param {string} symbol - Symbol to find tests for
|
|
113
|
+
* @returns {Promise<Array<{file: string, line: number, context: string}>|null>}
|
|
114
|
+
*/
|
|
115
|
+
async findTests(symbol) {
|
|
116
|
+
try {
|
|
117
|
+
const refs = await this._client.findReferences(symbol);
|
|
118
|
+
if (!refs) return null;
|
|
119
|
+
|
|
120
|
+
return refs.filter((ref) => {
|
|
121
|
+
const file = (ref.file || '').toLowerCase();
|
|
122
|
+
return (
|
|
123
|
+
file.includes('test') ||
|
|
124
|
+
file.includes('spec') ||
|
|
125
|
+
file.includes('__tests__')
|
|
126
|
+
);
|
|
127
|
+
});
|
|
128
|
+
} catch {
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Describe the project overview.
|
|
135
|
+
* Composition: analyzeCodebase + getProjectStats
|
|
136
|
+
*
|
|
137
|
+
* @param {string} [path] - Root path (defaults to '.')
|
|
138
|
+
* @returns {Promise<{codebase: Object, stats: Object}|null>}
|
|
139
|
+
*/
|
|
140
|
+
async describeProject(path = '.') {
|
|
141
|
+
try {
|
|
142
|
+
const [codebase, stats] = await Promise.all([
|
|
143
|
+
this._client.analyzeCodebase(path),
|
|
144
|
+
this._client.getProjectStats(),
|
|
145
|
+
]);
|
|
146
|
+
|
|
147
|
+
if (!codebase && !stats) return null;
|
|
148
|
+
|
|
149
|
+
return {
|
|
150
|
+
codebase: codebase || {},
|
|
151
|
+
stats: stats || {},
|
|
152
|
+
};
|
|
153
|
+
} catch {
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
module.exports = { CodeIntelEnricher };
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { CodeIntelClient } = require('./code-intel-client');
|
|
4
|
+
const { CodeIntelEnricher } = require('./code-intel-enricher');
|
|
5
|
+
const { CodeIntelProvider, CAPABILITIES } = require('./providers/provider-interface');
|
|
6
|
+
const { CodeGraphProvider, TOOL_MAP } = require('./providers/code-graph-provider');
|
|
7
|
+
|
|
8
|
+
// Singleton client instance (lazily initialized)
|
|
9
|
+
let _defaultClient = null;
|
|
10
|
+
let _defaultEnricher = null;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Get the default CodeIntelClient singleton.
|
|
14
|
+
* @param {Object} [options] - Options to pass on first creation
|
|
15
|
+
* @returns {CodeIntelClient}
|
|
16
|
+
*/
|
|
17
|
+
function getClient(options) {
|
|
18
|
+
if (!_defaultClient) {
|
|
19
|
+
_defaultClient = new CodeIntelClient(options);
|
|
20
|
+
}
|
|
21
|
+
return _defaultClient;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Get the default CodeIntelEnricher singleton.
|
|
26
|
+
* @param {Object} [options] - Options to pass to client on first creation
|
|
27
|
+
* @returns {CodeIntelEnricher}
|
|
28
|
+
*/
|
|
29
|
+
function getEnricher(options) {
|
|
30
|
+
if (!_defaultEnricher) {
|
|
31
|
+
_defaultEnricher = new CodeIntelEnricher(getClient(options));
|
|
32
|
+
}
|
|
33
|
+
return _defaultEnricher;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Check if any code intelligence provider is available.
|
|
38
|
+
* @returns {boolean}
|
|
39
|
+
*/
|
|
40
|
+
function isCodeIntelAvailable() {
|
|
41
|
+
if (_defaultClient) {
|
|
42
|
+
return _defaultClient.isCodeIntelAvailable();
|
|
43
|
+
}
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Enrich a base result with code intelligence data.
|
|
49
|
+
* Graceful — never throws, returns baseResult unchanged on failure.
|
|
50
|
+
*
|
|
51
|
+
* @param {*} baseResult - The base result to enrich
|
|
52
|
+
* @param {Object} options - Enrichment options
|
|
53
|
+
* @param {string[]} options.capabilities - List of enricher capabilities to use
|
|
54
|
+
* @param {number} [options.timeout=5000] - Timeout in ms
|
|
55
|
+
* @param {string} [options.fallbackBehavior='warn-and-continue'] - Fallback strategy
|
|
56
|
+
* @returns {Promise<*>} Enriched result or baseResult on failure
|
|
57
|
+
*/
|
|
58
|
+
async function enrichWithCodeIntel(baseResult, options = {}) {
|
|
59
|
+
if (!isCodeIntelAvailable()) {
|
|
60
|
+
return baseResult;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const enricher = getEnricher();
|
|
64
|
+
const enrichments = {};
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
const timeout = options.timeout ?? 5000;
|
|
68
|
+
const capabilities = options.capabilities || [];
|
|
69
|
+
|
|
70
|
+
const capabilityArgs = {
|
|
71
|
+
assessImpact: () => [Array.isArray(options.files) ? options.files : []],
|
|
72
|
+
detectDuplicates: () => [options.description || '', options],
|
|
73
|
+
findTests: () => [options.symbol || ''],
|
|
74
|
+
getConventions: () => [options.target || '.'],
|
|
75
|
+
describeProject: () => [options.target || '.'],
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const promises = capabilities.map(async (cap) => {
|
|
79
|
+
if (typeof enricher[cap] === 'function') {
|
|
80
|
+
let timer;
|
|
81
|
+
try {
|
|
82
|
+
const args = capabilityArgs[cap] ? capabilityArgs[cap]() : [options.target || '.'];
|
|
83
|
+
const result = await Promise.race([
|
|
84
|
+
enricher[cap](...args),
|
|
85
|
+
new Promise((_, reject) => {
|
|
86
|
+
timer = setTimeout(() => reject(new Error('timeout')), timeout);
|
|
87
|
+
}),
|
|
88
|
+
]);
|
|
89
|
+
enrichments[cap] = result;
|
|
90
|
+
} finally {
|
|
91
|
+
clearTimeout(timer);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
await Promise.allSettled(promises);
|
|
97
|
+
} catch (error) {
|
|
98
|
+
// Graceful — never throws, returns baseResult unchanged on failure
|
|
99
|
+
if (options.fallbackBehavior !== 'silent') {
|
|
100
|
+
console.warn('[code-intel] Enrichment failed, returning base result:', error.message);
|
|
101
|
+
}
|
|
102
|
+
return baseResult;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return { ...baseResult, _codeIntel: enrichments };
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Reset singletons (for testing).
|
|
110
|
+
*/
|
|
111
|
+
function _resetForTesting() {
|
|
112
|
+
_defaultClient = null;
|
|
113
|
+
_defaultEnricher = null;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
module.exports = {
|
|
117
|
+
// Singletons
|
|
118
|
+
getClient,
|
|
119
|
+
getEnricher,
|
|
120
|
+
|
|
121
|
+
// Convenience
|
|
122
|
+
isCodeIntelAvailable,
|
|
123
|
+
enrichWithCodeIntel,
|
|
124
|
+
|
|
125
|
+
// Classes (for custom instances)
|
|
126
|
+
CodeIntelClient,
|
|
127
|
+
CodeIntelEnricher,
|
|
128
|
+
CodeIntelProvider,
|
|
129
|
+
CodeGraphProvider,
|
|
130
|
+
|
|
131
|
+
// Constants
|
|
132
|
+
CAPABILITIES,
|
|
133
|
+
TOOL_MAP,
|
|
134
|
+
|
|
135
|
+
// Testing
|
|
136
|
+
_resetForTesting,
|
|
137
|
+
};
|