cto-ai-cli 5.2.0 → 7.1.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 +169 -316
- package/dist/cli/index.js +6306 -5691
- package/dist/engine/index.d.ts +690 -730
- package/dist/engine/index.js +2415 -4721
- package/dist/mcp/index.js +3313 -15036
- package/package.json +9 -43
- package/DOCS.md +0 -902
- package/dist/action/index.js +0 -26395
- package/dist/api/dashboard.js +0 -2276
- package/dist/api/dashboard.js.map +0 -1
- package/dist/api/server.js +0 -3663
- package/dist/api/server.js.map +0 -1
- package/dist/cli/gateway.js +0 -3054
- package/dist/cli/index.d.ts +0 -2
- package/dist/cli/index.js.map +0 -1
- package/dist/cli/score.js +0 -6352
- package/dist/cli/v2/index.d.ts +0 -2
- package/dist/cli/v2/index.js +0 -3695
- package/dist/cli/v2/index.js.map +0 -1
- package/dist/engine/index.js.map +0 -1
- package/dist/fsevents-X6WP4TKM.node +0 -0
- package/dist/gateway/index.d.ts +0 -281
- package/dist/gateway/index.js +0 -2932
- package/dist/gateway/index.js.map +0 -1
- package/dist/govern/index.d.ts +0 -325
- package/dist/govern/index.js +0 -1101
- package/dist/govern/index.js.map +0 -1
- package/dist/interact/index.d.ts +0 -234
- package/dist/interact/index.js +0 -1542
- package/dist/interact/index.js.map +0 -1
- package/dist/mcp/index.d.ts +0 -2
- package/dist/mcp/index.js.map +0 -1
- package/dist/mcp/v2.d.ts +0 -2
- package/dist/mcp/v2.js +0 -18492
- package/dist/mcp/v2.js.map +0 -1
package/dist/engine/index.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Project } from 'ts-morph';
|
|
2
|
-
import { EventEmitter } from 'node:events';
|
|
3
2
|
|
|
4
3
|
interface AnalyzedFile {
|
|
5
4
|
path: string;
|
|
@@ -203,7 +202,6 @@ interface AdjacencyList {
|
|
|
203
202
|
}
|
|
204
203
|
declare function buildAdjacencyList(edges: GraphEdge[]): AdjacencyList;
|
|
205
204
|
declare function bfsBidirectional(seeds: string[], adj: AdjacencyList, depth: number): Set<string>;
|
|
206
|
-
declare function matchGlob(path: string, pattern: string): boolean;
|
|
207
205
|
|
|
208
206
|
interface PolicySet {
|
|
209
207
|
version: string;
|
|
@@ -220,634 +218,733 @@ interface PolicyRule {
|
|
|
220
218
|
enabled: boolean;
|
|
221
219
|
}
|
|
222
220
|
type PolicyRuleType = 'include-always' | 'exclude-always' | 'budget-limit' | 'coverage-minimum' | 'risk-maximum' | 'secret-block';
|
|
221
|
+
interface SecretFinding {
|
|
222
|
+
type: SecretType;
|
|
223
|
+
file: string;
|
|
224
|
+
line: number;
|
|
225
|
+
match: string;
|
|
226
|
+
redacted: string;
|
|
227
|
+
severity: 'critical' | 'high' | 'medium' | 'low';
|
|
228
|
+
}
|
|
229
|
+
type SecretType = 'api-key' | 'aws-key' | 'private-key' | 'password' | 'token' | 'connection-string' | 'env-variable' | 'pii' | 'high-entropy' | 'custom';
|
|
223
230
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
declare function loadConfig(projectPath: string): Promise<CTOConfig>;
|
|
228
|
-
declare function saveConfig(projectPath: string, config: CTOConfig): Promise<string>;
|
|
229
|
-
declare function initProjectConfig(projectPath: string): Promise<{
|
|
230
|
-
configPath: string;
|
|
231
|
-
policyPath: string;
|
|
232
|
-
created: string[];
|
|
233
|
-
}>;
|
|
234
|
-
declare function loadPolicyFromYAML(projectPath: string): Promise<PolicySet | null>;
|
|
235
|
-
|
|
236
|
-
interface CacheOptions {
|
|
237
|
-
maxAgeMs: number;
|
|
238
|
-
maxEntries: number;
|
|
239
|
-
enabled: boolean;
|
|
231
|
+
interface SemanticScore {
|
|
232
|
+
filePath: string;
|
|
233
|
+
score: number;
|
|
240
234
|
}
|
|
235
|
+
interface LearnerBoostInput {
|
|
236
|
+
filePath: string;
|
|
237
|
+
boost: number;
|
|
238
|
+
}
|
|
239
|
+
interface SelectionInput {
|
|
240
|
+
task: string;
|
|
241
|
+
analysis: ProjectAnalysis;
|
|
242
|
+
budget: number;
|
|
243
|
+
policies?: PolicySet;
|
|
244
|
+
depth?: number;
|
|
245
|
+
semanticScores?: SemanticScore[];
|
|
246
|
+
learnerBoosts?: LearnerBoostInput[];
|
|
247
|
+
}
|
|
248
|
+
declare function selectContext(input: SelectionInput): Promise<ContextSelection>;
|
|
249
|
+
|
|
241
250
|
/**
|
|
242
|
-
*
|
|
251
|
+
* TF-IDF Semantic Matching Engine
|
|
243
252
|
*
|
|
244
|
-
*
|
|
245
|
-
*
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
*
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
*
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
}[];
|
|
263
|
-
};
|
|
264
|
-
/**
|
|
265
|
-
* Configure cache behavior.
|
|
253
|
+
* Zero dependencies. Pure math.
|
|
254
|
+
*
|
|
255
|
+
* Computes term frequency–inverse document frequency vectors for all files
|
|
256
|
+
* in a project and matches task descriptions against file content semantically.
|
|
257
|
+
* This replaces naive keyword matching with real information retrieval.
|
|
258
|
+
*
|
|
259
|
+
* How it works:
|
|
260
|
+
* 1. Tokenize each file into terms (identifiers, words)
|
|
261
|
+
* 2. Compute TF (term frequency) per document
|
|
262
|
+
* 3. Compute IDF (inverse document frequency) across corpus
|
|
263
|
+
* 4. Score query against each document using cosine similarity
|
|
264
|
+
*
|
|
265
|
+
* Why this matters:
|
|
266
|
+
* - "fix authentication" matches `auth.ts` even though "authentication" ≠ "auth"
|
|
267
|
+
* because stemming normalizes both to "auth"
|
|
268
|
+
* - Files that mention rare, task-specific terms rank higher than files
|
|
269
|
+
* full of common terms like "import", "export", "function"
|
|
270
|
+
* - BM25 variant avoids over-weighting long files
|
|
266
271
|
*/
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
+
interface TfIdfIndex {
|
|
273
|
+
documents: Map<string, DocumentVector>;
|
|
274
|
+
idf: Map<string, number>;
|
|
275
|
+
avgDocLength: number;
|
|
276
|
+
totalDocs: number;
|
|
272
277
|
}
|
|
273
|
-
interface
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
timestamp: Date;
|
|
278
|
+
interface DocumentVector {
|
|
279
|
+
terms: Map<string, number>;
|
|
280
|
+
length: number;
|
|
277
281
|
}
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
private config;
|
|
283
|
-
private debounceTimer;
|
|
284
|
-
private pendingChanges;
|
|
285
|
-
private running;
|
|
286
|
-
constructor(projectPath: string, options?: WatcherOptions);
|
|
287
|
-
start(): Promise<void>;
|
|
288
|
-
stop(): Promise<void>;
|
|
289
|
-
isRunning(): boolean;
|
|
290
|
-
getProjectPath(): string;
|
|
291
|
-
private handleEvent;
|
|
292
|
-
private flush;
|
|
282
|
+
interface SemanticMatch {
|
|
283
|
+
filePath: string;
|
|
284
|
+
score: number;
|
|
285
|
+
matchedTerms: string[];
|
|
293
286
|
}
|
|
294
287
|
/**
|
|
295
|
-
*
|
|
296
|
-
*
|
|
288
|
+
* Build a TF-IDF index from file contents.
|
|
289
|
+
* Call this once per project analysis, then query multiple times.
|
|
297
290
|
*/
|
|
298
|
-
declare function
|
|
291
|
+
declare function buildIndex(files: {
|
|
292
|
+
relativePath: string;
|
|
293
|
+
content: string;
|
|
294
|
+
}[]): TfIdfIndex;
|
|
299
295
|
/**
|
|
300
|
-
*
|
|
296
|
+
* Query the index with a task description.
|
|
297
|
+
* Returns files ranked by semantic relevance (BM25 scoring).
|
|
301
298
|
*/
|
|
302
|
-
declare function
|
|
299
|
+
declare function query(index: TfIdfIndex, taskDescription: string, maxResults?: number): SemanticMatch[];
|
|
303
300
|
/**
|
|
304
|
-
*
|
|
301
|
+
* Compute pairwise similarity between two documents in the index.
|
|
302
|
+
* Useful for finding related files (e.g., "what other files are similar to auth.ts?")
|
|
305
303
|
*/
|
|
306
|
-
declare function
|
|
304
|
+
declare function similarity(index: TfIdfIndex, pathA: string, pathB: string): number;
|
|
307
305
|
/**
|
|
308
|
-
*
|
|
306
|
+
* Tokenize source code into meaningful terms.
|
|
307
|
+
* Handles camelCase, snake_case, PascalCase splitting.
|
|
308
|
+
* Applies stemming and stop word removal.
|
|
309
309
|
*/
|
|
310
|
-
declare function
|
|
311
|
-
path: string;
|
|
312
|
-
running: boolean;
|
|
313
|
-
}[];
|
|
314
|
-
|
|
315
|
-
type ChangeType = 'added' | 'modified' | 'deleted' | 'renamed';
|
|
316
|
-
interface ChangedFile {
|
|
317
|
-
relativePath: string;
|
|
318
|
-
changeType: ChangeType;
|
|
319
|
-
linesAdded: number;
|
|
320
|
-
linesRemoved: number;
|
|
321
|
-
}
|
|
322
|
-
interface PRContextResult {
|
|
323
|
-
baseBranch: string;
|
|
324
|
-
currentBranch: string;
|
|
325
|
-
isGitRepo: boolean;
|
|
326
|
-
changedFiles: ChangedFile[];
|
|
327
|
-
dependencyFiles: string[];
|
|
328
|
-
allRelevantFiles: AnalyzedFile[];
|
|
329
|
-
totalChangedTokens: number;
|
|
330
|
-
totalContextTokens: number;
|
|
331
|
-
riskSummary: {
|
|
332
|
-
critical: number;
|
|
333
|
-
high: number;
|
|
334
|
-
medium: number;
|
|
335
|
-
low: number;
|
|
336
|
-
maxRiskFile: string;
|
|
337
|
-
maxRiskScore: number;
|
|
338
|
-
};
|
|
339
|
-
renderedSummary: string;
|
|
340
|
-
}
|
|
341
|
-
interface PRContextOptions {
|
|
342
|
-
baseBranch?: string;
|
|
343
|
-
depth?: number;
|
|
344
|
-
includeTests?: boolean;
|
|
345
|
-
}
|
|
310
|
+
declare function tokenize(text: string): string[];
|
|
346
311
|
/**
|
|
347
|
-
*
|
|
348
|
-
*
|
|
349
|
-
*
|
|
350
|
-
* @param options - PR context options (baseBranch, depth, includeTests)
|
|
351
|
-
* @returns Structured PR context with changed files, dependencies, risk summary
|
|
312
|
+
* Boost TF-IDF scores based on file path relevance to the task.
|
|
313
|
+
* This catches cases where the file content doesn't mention the task terms
|
|
314
|
+
* but the file path does (e.g., task "fix auth" → src/auth/middleware.ts).
|
|
352
315
|
*/
|
|
353
|
-
declare function
|
|
316
|
+
declare function boostByPath(matches: SemanticMatch[], allFiles: string[], taskDescription: string): SemanticMatch[];
|
|
354
317
|
|
|
355
|
-
type Grade = 'A+' | 'A' | 'A-' | 'B+' | 'B' | 'B-' | 'C+' | 'C' | 'C-' | 'D' | 'F';
|
|
356
|
-
interface ContextScore {
|
|
357
|
-
overall: number;
|
|
358
|
-
grade: Grade;
|
|
359
|
-
dimensions: {
|
|
360
|
-
efficiency: DimensionScore;
|
|
361
|
-
coverage: DimensionScore;
|
|
362
|
-
riskControl: DimensionScore;
|
|
363
|
-
structure: DimensionScore;
|
|
364
|
-
governance: DimensionScore;
|
|
365
|
-
};
|
|
366
|
-
insights: ScoreInsight[];
|
|
367
|
-
comparison: {
|
|
368
|
-
naiveTokens: number;
|
|
369
|
-
optimizedTokens: number;
|
|
370
|
-
savedTokens: number;
|
|
371
|
-
savedPercent: number;
|
|
372
|
-
monthlySavingsUSD: number;
|
|
373
|
-
};
|
|
374
|
-
meta: {
|
|
375
|
-
projectName: string;
|
|
376
|
-
totalFiles: number;
|
|
377
|
-
totalTokens: number;
|
|
378
|
-
analyzedAt: Date;
|
|
379
|
-
};
|
|
380
|
-
}
|
|
381
|
-
interface DimensionScore {
|
|
382
|
-
score: number;
|
|
383
|
-
weight: number;
|
|
384
|
-
weighted: number;
|
|
385
|
-
detail: string;
|
|
386
|
-
}
|
|
387
|
-
interface ScoreInsight {
|
|
388
|
-
type: 'strength' | 'weakness' | 'opportunity';
|
|
389
|
-
title: string;
|
|
390
|
-
detail: string;
|
|
391
|
-
impact: 'high' | 'medium' | 'low';
|
|
392
|
-
}
|
|
393
318
|
/**
|
|
394
|
-
*
|
|
319
|
+
* Persistent TF-IDF Index Cache
|
|
395
320
|
*
|
|
396
|
-
*
|
|
397
|
-
*
|
|
398
|
-
*
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
*
|
|
321
|
+
* Problem: Building a TF-IDF index reads every source file and tokenizes it.
|
|
322
|
+
* For a 50K-file repo, that's 5-10 seconds per query. With 20K devs running
|
|
323
|
+
* queries concurrently, re-indexing on every call is unacceptable.
|
|
324
|
+
*
|
|
325
|
+
* Solution: Persist the index to disk with per-file mtime tracking.
|
|
326
|
+
* On subsequent queries, only re-index files that changed since last build.
|
|
327
|
+
*
|
|
328
|
+
* Storage: .cto/index-cache.json
|
|
329
|
+
* {
|
|
330
|
+
* version: 2,
|
|
331
|
+
* builtAt: ISO timestamp,
|
|
332
|
+
* files: { [relativePath]: { mtime: number, terms: { [term]: count }, length: number } },
|
|
333
|
+
* idf: { [term]: number },
|
|
334
|
+
* avgDocLength: number,
|
|
335
|
+
* totalDocs: number,
|
|
336
|
+
* }
|
|
337
|
+
*
|
|
338
|
+
* Invalidation:
|
|
339
|
+
* - Per-file: mtime changed → re-tokenize that file
|
|
340
|
+
* - New files: not in cache → tokenize and add
|
|
341
|
+
* - Deleted files: in cache but not on disk → remove
|
|
342
|
+
* - Version bump: cache format changed → full rebuild
|
|
343
|
+
*
|
|
344
|
+
* The IDF values are recomputed after any incremental update because
|
|
345
|
+
* document frequency changes affect all terms globally.
|
|
403
346
|
*/
|
|
404
|
-
declare function renderContextScore(score: ContextScore): string;
|
|
405
347
|
|
|
406
|
-
interface
|
|
407
|
-
|
|
348
|
+
interface IndexCacheStats {
|
|
349
|
+
/** Total files in the index */
|
|
408
350
|
totalFiles: number;
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
vsNaiveTokensSaved: number;
|
|
420
|
-
vsNaiveTokensSavedPercent: number;
|
|
421
|
-
vsRandomCoverageGain: number;
|
|
422
|
-
vsNaiveCostSavedMonthlyUSD: number;
|
|
423
|
-
};
|
|
424
|
-
}
|
|
425
|
-
interface StrategyResult {
|
|
426
|
-
filesSelected: number;
|
|
427
|
-
tokensUsed: number;
|
|
428
|
-
coverageScore: number;
|
|
429
|
-
criticalFilesCovered: number;
|
|
430
|
-
criticalFilesTotal: number;
|
|
431
|
-
highRiskCovered: number;
|
|
432
|
-
highRiskTotal: number;
|
|
433
|
-
costPerInteractionUSD: number;
|
|
434
|
-
timeMs: number;
|
|
351
|
+
/** Files that were re-indexed (changed or new) */
|
|
352
|
+
updatedFiles: number;
|
|
353
|
+
/** Files removed from cache (deleted from disk) */
|
|
354
|
+
removedFiles: number;
|
|
355
|
+
/** Files reused from cache (unchanged) */
|
|
356
|
+
cachedFiles: number;
|
|
357
|
+
/** Whether the cache existed before this build */
|
|
358
|
+
cacheHit: boolean;
|
|
359
|
+
/** Time to build/update the index (ms) */
|
|
360
|
+
buildTimeMs: number;
|
|
435
361
|
}
|
|
436
362
|
/**
|
|
437
|
-
*
|
|
363
|
+
* Build or update a TF-IDF index with disk caching.
|
|
364
|
+
*
|
|
365
|
+
* First call: builds full index and writes cache to .cto/index-cache.json
|
|
366
|
+
* Subsequent calls: reads cache, updates only changed files, rewrites cache
|
|
367
|
+
*
|
|
368
|
+
* @param projectPath - Root of the project (for .cto/ directory)
|
|
369
|
+
* @param files - All files to index: { relativePath, absolutePath, content? }
|
|
370
|
+
* If content is provided, it's used directly. Otherwise, the file is read from disk.
|
|
371
|
+
* @returns The TF-IDF index + stats about cache hits/misses
|
|
438
372
|
*/
|
|
439
|
-
declare function
|
|
373
|
+
declare function buildIndexCached(projectPath: string, files: {
|
|
374
|
+
relativePath: string;
|
|
375
|
+
absolutePath: string;
|
|
376
|
+
content?: string;
|
|
377
|
+
}[]): {
|
|
378
|
+
index: TfIdfIndex;
|
|
379
|
+
stats: IndexCacheStats;
|
|
380
|
+
};
|
|
440
381
|
/**
|
|
441
|
-
*
|
|
382
|
+
* Invalidate the entire cache (force full rebuild on next call).
|
|
442
383
|
*/
|
|
443
|
-
declare function
|
|
444
|
-
|
|
445
|
-
type TaskType = 'debug' | 'review' | 'refactor' | 'test' | 'docs' | 'feature' | 'architecture' | 'simple-edit';
|
|
446
|
-
|
|
447
|
-
interface QualityBenchmarkResult {
|
|
448
|
-
project: string;
|
|
449
|
-
task: string;
|
|
450
|
-
taskType: TaskType;
|
|
451
|
-
budget: number;
|
|
452
|
-
strategies: {
|
|
453
|
-
cto: QualityMetrics;
|
|
454
|
-
naive: QualityMetrics;
|
|
455
|
-
random: QualityMetrics;
|
|
456
|
-
};
|
|
457
|
-
comparison: {
|
|
458
|
-
ctoVsNaiveRelevance: number;
|
|
459
|
-
ctoVsRandomRelevance: number;
|
|
460
|
-
ctoVsNaiveCompleteness: number;
|
|
461
|
-
ctoVsRandomCompleteness: number;
|
|
462
|
-
ctoNoiseReduction: number;
|
|
463
|
-
};
|
|
464
|
-
prompts: {
|
|
465
|
-
cto: {
|
|
466
|
-
rendered: string;
|
|
467
|
-
tokens: number;
|
|
468
|
-
};
|
|
469
|
-
naive: {
|
|
470
|
-
rendered: string;
|
|
471
|
-
tokens: number;
|
|
472
|
-
};
|
|
473
|
-
};
|
|
474
|
-
verdict: string;
|
|
475
|
-
}
|
|
476
|
-
interface QualityMetrics {
|
|
477
|
-
filesSelected: number;
|
|
478
|
-
tokensUsed: number;
|
|
479
|
-
relevanceScore: number;
|
|
480
|
-
completenessScore: number;
|
|
481
|
-
noiseRatio: number;
|
|
482
|
-
typeCoverage: number;
|
|
483
|
-
dependencyClosure: number;
|
|
484
|
-
relevantFilesIncluded: number;
|
|
485
|
-
relevantFilesTotal: number;
|
|
486
|
-
typeFilesIncluded: number;
|
|
487
|
-
typeFilesNeeded: number;
|
|
488
|
-
depsIncluded: number;
|
|
489
|
-
depsNeeded: number;
|
|
490
|
-
}
|
|
384
|
+
declare function invalidateCache(projectPath: string): void;
|
|
491
385
|
/**
|
|
492
|
-
*
|
|
493
|
-
* Measures relevance, completeness, noise, and dependency closure.
|
|
386
|
+
* Get cache stats without rebuilding.
|
|
494
387
|
*/
|
|
495
|
-
declare function
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
version: number;
|
|
500
|
-
trainedAt: string;
|
|
501
|
-
totalObservations: number;
|
|
502
|
-
taskTypeFrequency: Record<string, Record<string, number>>;
|
|
503
|
-
keywordFrequency: Record<string, Record<string, number>>;
|
|
504
|
-
fileStats: Record<string, {
|
|
505
|
-
totalSelections: number;
|
|
506
|
-
avgRiskScore: number;
|
|
507
|
-
avgTokens: number;
|
|
508
|
-
lastSelected: string;
|
|
509
|
-
}>;
|
|
510
|
-
coSelection: Record<string, Record<string, number>>;
|
|
511
|
-
}
|
|
512
|
-
interface PredictionResult {
|
|
513
|
-
filePath: string;
|
|
514
|
-
predictedScore: number;
|
|
515
|
-
reasons: string[];
|
|
516
|
-
}
|
|
517
|
-
interface PredictorConfig {
|
|
518
|
-
maxCoSelectionPairs: number;
|
|
519
|
-
decayFactor: number;
|
|
520
|
-
minObservations: number;
|
|
521
|
-
}
|
|
522
|
-
declare function loadModel(projectPath: string): Promise<PredictorModel>;
|
|
523
|
-
declare function recordSelection(projectPath: string, task: string, selectedFiles: {
|
|
524
|
-
relativePath: string;
|
|
525
|
-
riskScore: number;
|
|
526
|
-
tokens: number;
|
|
527
|
-
}[]): Promise<PredictorModel>;
|
|
528
|
-
declare function predictRelevantFiles(projectPath: string, task: string, analysis: ProjectAnalysis, config?: Partial<PredictorConfig>): Promise<PredictionResult[]>;
|
|
529
|
-
declare function getPredictorBoosts(projectPath: string, task: string, analysis: ProjectAnalysis): Promise<Map<string, number>>;
|
|
530
|
-
declare function getModelStats(model: PredictorModel): {
|
|
531
|
-
observations: number;
|
|
532
|
-
taskTypes: number;
|
|
533
|
-
keywords: number;
|
|
534
|
-
trackedFiles: number;
|
|
535
|
-
coSelectionPairs: number;
|
|
536
|
-
trainedAt: string;
|
|
388
|
+
declare function getCacheInfo(projectPath: string): {
|
|
389
|
+
exists: boolean;
|
|
390
|
+
fileCount: number;
|
|
391
|
+
builtAt: string | null;
|
|
537
392
|
};
|
|
538
393
|
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
394
|
+
/**
|
|
395
|
+
* Usage Learner — Gets smarter with every use.
|
|
396
|
+
*
|
|
397
|
+
* Zero dependencies. Bayesian priors updated from real usage.
|
|
398
|
+
*
|
|
399
|
+
* How it works:
|
|
400
|
+
* 1. Every time context is selected, log what was included/excluded
|
|
401
|
+
* 2. When user accepts/rejects files, update file relevance priors
|
|
402
|
+
* 3. Next selection uses priors as boost signals alongside TF-IDF + risk
|
|
403
|
+
*
|
|
404
|
+
* Storage: .cto/learner.json — portable, versioned, <10KB typical
|
|
405
|
+
*
|
|
406
|
+
* The model is intentionally simple (no neural nets, no embeddings model):
|
|
407
|
+
* - Beta distribution per file pattern (alpha=accepted, beta=rejected)
|
|
408
|
+
* - Exponential decay so recent feedback weighs more
|
|
409
|
+
* - Pattern-based: learns "test files matter for debug tasks", not
|
|
410
|
+
* specific file paths (so it works across branches/renames)
|
|
411
|
+
*/
|
|
412
|
+
interface LearnerModel {
|
|
413
|
+
version: 2;
|
|
550
414
|
updatedAt: string;
|
|
551
|
-
|
|
552
|
-
totalObservations: number;
|
|
553
|
-
archetypes: Record<string, ArchetypeProfile>;
|
|
554
|
-
universalPatterns: PatternStats[];
|
|
555
|
-
}
|
|
556
|
-
interface ArchetypeProfile {
|
|
557
|
-
name: string;
|
|
558
|
-
fingerprint: ProjectFingerprint;
|
|
559
|
-
projectCount: number;
|
|
560
|
-
observationCount: number;
|
|
415
|
+
patterns: Record<string, PatternStats>;
|
|
561
416
|
taskPatterns: Record<string, Record<string, PatternStats>>;
|
|
562
|
-
|
|
563
|
-
kind: string;
|
|
564
|
-
importance: number;
|
|
565
|
-
}[];
|
|
566
|
-
criticalDirs: {
|
|
567
|
-
pattern: string;
|
|
568
|
-
importance: number;
|
|
569
|
-
}[];
|
|
417
|
+
totalSelections: number;
|
|
570
418
|
}
|
|
571
419
|
interface PatternStats {
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
hitRate: number;
|
|
576
|
-
avgRelevanceBoost: number;
|
|
420
|
+
alpha: number;
|
|
421
|
+
beta: number;
|
|
422
|
+
lastSeen: string;
|
|
577
423
|
}
|
|
578
|
-
interface
|
|
424
|
+
interface LearnerBoost {
|
|
579
425
|
filePath: string;
|
|
580
426
|
boost: number;
|
|
581
|
-
reason: string;
|
|
582
427
|
confidence: number;
|
|
428
|
+
reason: string;
|
|
583
429
|
}
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
declare function
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
outcome: FeedbackOutcome;
|
|
613
|
-
model?: string;
|
|
614
|
-
promptTokens?: number;
|
|
615
|
-
sessionId?: string;
|
|
616
|
-
strategy?: string;
|
|
617
|
-
}
|
|
618
|
-
interface FeedbackOutcome {
|
|
619
|
-
accepted: boolean;
|
|
620
|
-
compilable?: boolean;
|
|
621
|
-
testsPassed?: number;
|
|
622
|
-
testsTotal?: number;
|
|
623
|
-
linesGenerated?: number;
|
|
624
|
-
linesAccepted?: number;
|
|
625
|
-
timeToAcceptMs?: number;
|
|
626
|
-
userRating?: 1 | 2 | 3 | 4 | 5;
|
|
627
|
-
notes?: string;
|
|
628
|
-
}
|
|
629
|
-
interface FeedbackModel {
|
|
630
|
-
version: number;
|
|
631
|
-
updatedAt: string;
|
|
632
|
-
totalFeedback: number;
|
|
633
|
-
acceptRate: number;
|
|
634
|
-
fileAcceptance: Record<string, {
|
|
635
|
-
includedCount: number;
|
|
636
|
-
acceptedCount: number;
|
|
637
|
-
acceptRate: number;
|
|
638
|
-
ewmaAcceptRate: number;
|
|
639
|
-
avgTimeToAccept: number;
|
|
640
|
-
lastSeen: string;
|
|
641
|
-
bayesianLower: number;
|
|
642
|
-
}>;
|
|
643
|
-
taskTypeAcceptance: Record<string, {
|
|
644
|
-
totalCount: number;
|
|
645
|
-
acceptedCount: number;
|
|
646
|
-
acceptRate: number;
|
|
647
|
-
avgCompilable: number;
|
|
648
|
-
ewmaAcceptRate: number;
|
|
649
|
-
}>;
|
|
650
|
-
pairAcceptance: Record<string, {
|
|
651
|
-
count: number;
|
|
652
|
-
acceptedCount: number;
|
|
653
|
-
acceptRate: number;
|
|
654
|
-
}>;
|
|
655
|
-
sessions: Record<string, {
|
|
656
|
-
strategy: string;
|
|
657
|
-
entries: number;
|
|
658
|
-
acceptRate: number;
|
|
659
|
-
}>;
|
|
660
|
-
strategyComparison: Record<string, {
|
|
661
|
-
totalCount: number;
|
|
662
|
-
acceptedCount: number;
|
|
430
|
+
/**
|
|
431
|
+
* Load the learner model from .cto/learner.json.
|
|
432
|
+
* Returns empty model if none exists.
|
|
433
|
+
*/
|
|
434
|
+
declare function loadLearner(projectPath: string): Promise<LearnerModel>;
|
|
435
|
+
/**
|
|
436
|
+
* Save the learner model.
|
|
437
|
+
*/
|
|
438
|
+
declare function saveLearner(projectPath: string, model: LearnerModel): Promise<void>;
|
|
439
|
+
/**
|
|
440
|
+
* Record a selection event — which files were included and which were excluded.
|
|
441
|
+
* Updates Bayesian priors with exponential decay.
|
|
442
|
+
*/
|
|
443
|
+
declare function recordSelection(model: LearnerModel, taskType: string, selectedFiles: string[], excludedFiles: string[]): LearnerModel;
|
|
444
|
+
/**
|
|
445
|
+
* Get boost signals for files based on learned patterns.
|
|
446
|
+
* Returns boosts that should be applied during context selection.
|
|
447
|
+
*/
|
|
448
|
+
declare function getLearnerBoosts(model: LearnerModel, taskType: string, files: string[]): LearnerBoost[];
|
|
449
|
+
/**
|
|
450
|
+
* Get learner stats for display.
|
|
451
|
+
*/
|
|
452
|
+
declare function getLearnerStats(model: LearnerModel): {
|
|
453
|
+
totalSelections: number;
|
|
454
|
+
patternsLearned: number;
|
|
455
|
+
taskTypes: string[];
|
|
456
|
+
topPatterns: {
|
|
457
|
+
pattern: string;
|
|
663
458
|
acceptRate: number;
|
|
664
|
-
avgTimeToAccept: number;
|
|
665
|
-
}>;
|
|
666
|
-
insights: FeedbackInsight[];
|
|
667
|
-
}
|
|
668
|
-
interface FeedbackInsight {
|
|
669
|
-
type: 'positive' | 'negative' | 'opportunity';
|
|
670
|
-
title: string;
|
|
671
|
-
detail: string;
|
|
672
|
-
impact: number;
|
|
673
|
-
}
|
|
674
|
-
declare function loadFeedbackModel(projectPath: string): Promise<FeedbackModel>;
|
|
675
|
-
declare function wilsonLowerBound(successes: number, total: number, z?: number): number;
|
|
676
|
-
declare function recordFeedback(projectPath: string, entry: Omit<FeedbackEntry, 'id' | 'timestamp' | 'taskType'>): Promise<FeedbackModel>;
|
|
677
|
-
declare function getFeedbackBoosts(projectPath: string, task: string): Promise<Map<string, number>>;
|
|
678
|
-
interface TeamFeedbackExport {
|
|
679
|
-
version: number;
|
|
680
|
-
exportedAt: string;
|
|
681
|
-
projectName: string;
|
|
682
|
-
model: FeedbackModel;
|
|
683
|
-
entrySummary: {
|
|
684
|
-
total: number;
|
|
685
|
-
accepted: number;
|
|
686
|
-
sessions: number;
|
|
687
|
-
};
|
|
688
|
-
}
|
|
689
|
-
declare function exportFeedbackForTeam(projectPath: string, projectName: string): Promise<TeamFeedbackExport>;
|
|
690
|
-
declare function importTeamFeedback(projectPath: string, teamExport: TeamFeedbackExport): Promise<FeedbackModel>;
|
|
691
|
-
declare function renderFeedbackReport(model: FeedbackModel): string;
|
|
692
|
-
declare function renderCrossRepoReport(stats: {
|
|
693
|
-
totalProjects: number;
|
|
694
|
-
totalObservations: number;
|
|
695
|
-
archetypes: {
|
|
696
|
-
name: string;
|
|
697
|
-
projects: number;
|
|
698
459
|
observations: number;
|
|
699
460
|
}[];
|
|
700
|
-
|
|
701
|
-
|
|
461
|
+
};
|
|
462
|
+
/**
|
|
463
|
+
* Extract a generalizable pattern from a file path.
|
|
464
|
+
* Examples: "src/engine/analyzer.ts" becomes "engine/(star).ts",
|
|
465
|
+
* "tests/unit/auth.test.ts" becomes "tests/(star)(star)/(star).test.ts".
|
|
466
|
+
* This way the learner generalizes across similar files,
|
|
467
|
+
* not just memorize specific paths.
|
|
468
|
+
*/
|
|
469
|
+
declare function extractPattern(filePath: string): string;
|
|
702
470
|
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
471
|
+
/**
|
|
472
|
+
* Multi-Repo Context Selection
|
|
473
|
+
*
|
|
474
|
+
* Discovers sibling repositories in a workspace and queries them
|
|
475
|
+
* for relevant files when selecting context for a task.
|
|
476
|
+
*
|
|
477
|
+
* How it works:
|
|
478
|
+
* 1. Discover sibling repos (scan parent dir or use explicit paths)
|
|
479
|
+
* 2. For each sibling: list source files, read contents, build TF-IDF index
|
|
480
|
+
* 3. Query each sibling's index with the task description
|
|
481
|
+
* 4. Return ranked matches with repo attribution
|
|
482
|
+
*
|
|
483
|
+
* This is NOT the cross-repo learning system (cross-repo.ts).
|
|
484
|
+
* This is actual multi-repo file discovery and querying.
|
|
485
|
+
*/
|
|
486
|
+
interface SiblingRepo {
|
|
487
|
+
/** Absolute path to the repo root */
|
|
488
|
+
path: string;
|
|
489
|
+
/** Short name (directory name) */
|
|
712
490
|
name: string;
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
declare function analyzeSemantics(analysis: ProjectAnalysis): SemanticAnalysis;
|
|
734
|
-
declare function semanticBoosts(semantics: SemanticAnalysis, task: string): Map<string, number>;
|
|
735
|
-
declare function renderSemanticAnalysis(semantics: SemanticAnalysis): string;
|
|
736
|
-
|
|
737
|
-
interface CompilabilityResult {
|
|
738
|
-
project: string;
|
|
739
|
-
task: string;
|
|
740
|
-
budget: number;
|
|
741
|
-
strategies: {
|
|
742
|
-
cto: CompilabilityMetrics;
|
|
743
|
-
naive: CompilabilityMetrics;
|
|
744
|
-
random: CompilabilityMetrics;
|
|
745
|
-
};
|
|
746
|
-
comparison: {
|
|
747
|
-
ctoVsNaiveTypeAvailability: number;
|
|
748
|
-
ctoVsRandomTypeAvailability: number;
|
|
749
|
-
ctoVsNaivePredictedErrors: number;
|
|
750
|
-
naiveMissingTypes: string[];
|
|
751
|
-
randomMissingTypes: string[];
|
|
752
|
-
};
|
|
753
|
-
verdict: string;
|
|
491
|
+
/** Detected stack (from package.json, tsconfig, etc.) */
|
|
492
|
+
stack: string[];
|
|
493
|
+
/** Number of source files found */
|
|
494
|
+
fileCount: number;
|
|
495
|
+
}
|
|
496
|
+
interface SiblingMatch {
|
|
497
|
+
/** Which sibling repo this file belongs to */
|
|
498
|
+
repoName: string;
|
|
499
|
+
/** Absolute path to the repo */
|
|
500
|
+
repoPath: string;
|
|
501
|
+
/** Relative path within the sibling repo */
|
|
502
|
+
relativePath: string;
|
|
503
|
+
/** Absolute path to the file */
|
|
504
|
+
absolutePath: string;
|
|
505
|
+
/** Semantic relevance score (0-1) */
|
|
506
|
+
score: number;
|
|
507
|
+
/** File content */
|
|
508
|
+
content: string;
|
|
509
|
+
/** Estimated token count */
|
|
510
|
+
tokens: number;
|
|
754
511
|
}
|
|
755
|
-
interface
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
missingTypeFiles: string[];
|
|
763
|
-
missingDependencies: string[];
|
|
764
|
-
predictedTypeErrors: number;
|
|
765
|
-
predictedImportErrors: number;
|
|
766
|
-
predictedTotalErrors: number;
|
|
767
|
-
compilabilityScore: number;
|
|
768
|
-
filesSelected: number;
|
|
769
|
-
tokensUsed: number;
|
|
512
|
+
interface MultiRepoResult {
|
|
513
|
+
/** Sibling repos that were discovered/used */
|
|
514
|
+
siblings: SiblingRepo[];
|
|
515
|
+
/** Top matches from sibling repos, ranked by score */
|
|
516
|
+
matches: SiblingMatch[];
|
|
517
|
+
/** Total time spent indexing + querying (ms) */
|
|
518
|
+
timeMs: number;
|
|
770
519
|
}
|
|
771
|
-
|
|
772
|
-
|
|
520
|
+
/**
|
|
521
|
+
* Discover sibling repositories by scanning the parent directory.
|
|
522
|
+
* A directory is a "repo" if it contains a known project marker file.
|
|
523
|
+
*/
|
|
524
|
+
declare function discoverSiblingRepos(projectPath: string): SiblingRepo[];
|
|
525
|
+
/**
|
|
526
|
+
* Query sibling repos for files relevant to a task.
|
|
527
|
+
*
|
|
528
|
+
* For each sibling:
|
|
529
|
+
* 1. List source files
|
|
530
|
+
* 2. Build TF-IDF index from file contents
|
|
531
|
+
* 3. Query with task description
|
|
532
|
+
* 4. Return top matches with content
|
|
533
|
+
*
|
|
534
|
+
* @param siblings - Sibling repos to query (from discoverSiblingRepos or explicit paths)
|
|
535
|
+
* @param task - Task description to match against
|
|
536
|
+
* @param maxPerRepo - Max matches per repo (default 5)
|
|
537
|
+
* @param minScore - Minimum semantic score to include (default 0.3)
|
|
538
|
+
*/
|
|
539
|
+
declare function querySiblingRepos(siblings: SiblingRepo[], task: string, maxPerRepo?: number, minScore?: number): MultiRepoResult;
|
|
540
|
+
/**
|
|
541
|
+
* Parse explicit repo paths from a comma-separated string.
|
|
542
|
+
* Resolves relative paths against the current project's parent directory.
|
|
543
|
+
*/
|
|
544
|
+
declare function parseSiblingPaths(pathsStr: string, projectPath: string): SiblingRepo[];
|
|
545
|
+
/**
|
|
546
|
+
* Render multi-repo results for CLI output.
|
|
547
|
+
*/
|
|
548
|
+
declare function renderMultiRepoSummary(result: MultiRepoResult): string;
|
|
773
549
|
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
details: string;
|
|
783
|
-
}
|
|
784
|
-
interface CompileProofStrategy {
|
|
785
|
-
name: string;
|
|
786
|
-
filesIncluded: number;
|
|
787
|
-
tokensUsed: number;
|
|
788
|
-
typeFilesIncluded: string[];
|
|
789
|
-
typeFilesMissing: string[];
|
|
790
|
-
compileErrors: number;
|
|
791
|
-
errorMessages: string[];
|
|
792
|
-
compiles: boolean;
|
|
793
|
-
}
|
|
794
|
-
declare function runCompileProof(analysis: ProjectAnalysis, task: string, budget?: number): Promise<CompileProofResult>;
|
|
795
|
-
declare function renderCompileProof(result: CompileProofResult): string;
|
|
550
|
+
/**
|
|
551
|
+
* Shared Context Pipeline
|
|
552
|
+
*
|
|
553
|
+
* Single function that runs the full context selection pipeline:
|
|
554
|
+
* read files → build TF-IDF index → query → boost → load learner → selectContext
|
|
555
|
+
*
|
|
556
|
+
* Used by both CLI and MCP server. No duplication.
|
|
557
|
+
*/
|
|
796
558
|
|
|
797
|
-
interface
|
|
798
|
-
|
|
799
|
-
name: string;
|
|
800
|
-
provider: 'openai' | 'anthropic' | 'google' | 'mistral' | 'meta' | 'custom';
|
|
801
|
-
contextWindow: number;
|
|
802
|
-
maxOutput: number;
|
|
803
|
-
costPer1MInput: number;
|
|
804
|
-
costPer1MOutput: number;
|
|
805
|
-
strengths: ModelStrength[];
|
|
806
|
-
recommendedBudgetPercent: number;
|
|
807
|
-
}
|
|
808
|
-
type ModelStrength = 'code-generation' | 'analysis' | 'refactoring' | 'debugging' | 'documentation' | 'testing' | 'general';
|
|
809
|
-
interface MultiModelResult {
|
|
559
|
+
interface ContextPipelineInput {
|
|
560
|
+
projectPath: string;
|
|
810
561
|
task: string;
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
bestSpeed: string;
|
|
816
|
-
reasoning: string;
|
|
817
|
-
};
|
|
562
|
+
analysis: ProjectAnalysis;
|
|
563
|
+
budget?: number;
|
|
564
|
+
/** Optional sibling repos for cross-repo context */
|
|
565
|
+
siblingRepos?: SiblingRepo[];
|
|
818
566
|
}
|
|
819
|
-
interface
|
|
820
|
-
model: ModelProfile;
|
|
821
|
-
budget: number;
|
|
567
|
+
interface ContextPipelineResult {
|
|
822
568
|
selection: ContextSelection;
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
569
|
+
taskType: string;
|
|
570
|
+
fileContentMap: Map<string, string>;
|
|
571
|
+
semanticMap: Map<string, SemanticMatch>;
|
|
572
|
+
learnerMap: Map<string, LearnerBoost>;
|
|
573
|
+
/** Cross-repo results (only present if siblingRepos were provided) */
|
|
574
|
+
multiRepo?: MultiRepoResult;
|
|
575
|
+
/** Index cache stats (how many files were cached vs rebuilt) */
|
|
576
|
+
indexCacheStats?: IndexCacheStats;
|
|
826
577
|
}
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
578
|
+
/**
|
|
579
|
+
* Run the full context selection pipeline.
|
|
580
|
+
* One function, used everywhere. No copy-paste.
|
|
581
|
+
*/
|
|
582
|
+
declare function runContextPipeline(input: ContextPipelineInput): Promise<ContextPipelineResult>;
|
|
830
583
|
|
|
831
584
|
declare function scoreAllFiles(files: AnalyzedFile[], graph: ProjectGraph, weights?: RiskWeights): void;
|
|
832
585
|
declare function scoreFile(file: AnalyzedFile, graph: ProjectGraph, weights?: RiskWeights): number;
|
|
833
586
|
|
|
834
587
|
declare function calculateCoverage(targetPaths: string[], includedPaths: string[], allFiles: AnalyzedFile[], graph: ProjectGraph, depth?: number): CoverageResult;
|
|
835
588
|
|
|
836
|
-
interface SelectionInput {
|
|
837
|
-
task: string;
|
|
838
|
-
analysis: ProjectAnalysis;
|
|
839
|
-
budget: number;
|
|
840
|
-
policies?: PolicySet;
|
|
841
|
-
depth?: number;
|
|
842
|
-
}
|
|
843
|
-
declare function selectContext(input: SelectionInput): Promise<ContextSelection>;
|
|
844
|
-
|
|
845
589
|
declare function getPruneLevelForRisk(riskScore: number): PruneLevel;
|
|
846
590
|
declare function optimizeBudget(files: AnalyzedFile[], budget: number): Promise<BudgetPlan>;
|
|
847
591
|
|
|
848
592
|
declare function pruneFile(file: AnalyzedFile, level: PruneLevel): Promise<PrunedContent>;
|
|
849
593
|
declare function pruneFiles(files: AnalyzedFile[], levelFn: (file: AnalyzedFile) => PruneLevel): Promise<PrunedContent[]>;
|
|
850
594
|
|
|
595
|
+
/**
|
|
596
|
+
* Closed-Loop A/B Testing Engine
|
|
597
|
+
*
|
|
598
|
+
* The missing piece: the feedback system records data but never closes the loop.
|
|
599
|
+
* This module adds real experimentation:
|
|
600
|
+
*
|
|
601
|
+
* 1. Define experiments with control + variant strategies
|
|
602
|
+
* 2. Assign requests to groups (deterministic hashing for consistency)
|
|
603
|
+
* 3. Collect outcomes per group
|
|
604
|
+
* 4. Compute statistical significance (z-test for proportions)
|
|
605
|
+
* 5. Auto-promote winning variants when significance threshold met
|
|
606
|
+
*
|
|
607
|
+
* Example experiment:
|
|
608
|
+
* - Control: default composite scoring (semantic 0.55, risk 0.25, learner 0.20)
|
|
609
|
+
* - Variant: reranker-heavy scoring (reranker 0.70, risk 0.15, learner 0.15)
|
|
610
|
+
* - Metric: acceptance rate
|
|
611
|
+
* - Significance: p < 0.05
|
|
612
|
+
*
|
|
613
|
+
* Storage: .cto/experiments.json
|
|
614
|
+
* Design: Pure functions. No external deps. Deterministic assignment.
|
|
615
|
+
*/
|
|
616
|
+
interface Experiment {
|
|
617
|
+
/** Unique experiment ID */
|
|
618
|
+
id: string;
|
|
619
|
+
/** Human-readable name */
|
|
620
|
+
name: string;
|
|
621
|
+
/** What we're testing */
|
|
622
|
+
description: string;
|
|
623
|
+
/** Current status */
|
|
624
|
+
status: 'running' | 'concluded' | 'paused';
|
|
625
|
+
/** When the experiment started */
|
|
626
|
+
startedAt: string;
|
|
627
|
+
/** When it concluded (if applicable) */
|
|
628
|
+
concludedAt?: string;
|
|
629
|
+
/** Traffic split: 0.5 = 50/50 */
|
|
630
|
+
trafficSplit: number;
|
|
631
|
+
/** Minimum observations per group before significance test */
|
|
632
|
+
minObservations: number;
|
|
633
|
+
/** P-value threshold for significance */
|
|
634
|
+
significanceThreshold: number;
|
|
635
|
+
/** Control group config */
|
|
636
|
+
control: ExperimentGroup;
|
|
637
|
+
/** Variant group config */
|
|
638
|
+
variant: ExperimentGroup;
|
|
639
|
+
/** Conclusion (when experiment ends) */
|
|
640
|
+
conclusion?: ExperimentConclusion;
|
|
641
|
+
}
|
|
642
|
+
interface ExperimentGroup {
|
|
643
|
+
/** Group name */
|
|
644
|
+
name: string;
|
|
645
|
+
/** Strategy parameters (passed to the engine) */
|
|
646
|
+
params: Record<string, unknown>;
|
|
647
|
+
/** Collected metrics */
|
|
648
|
+
metrics: GroupMetrics;
|
|
649
|
+
}
|
|
650
|
+
interface GroupMetrics {
|
|
651
|
+
/** Total observations */
|
|
652
|
+
total: number;
|
|
653
|
+
/** Successful outcomes (accepted) */
|
|
654
|
+
successes: number;
|
|
655
|
+
/** Accept rate = successes / total */
|
|
656
|
+
acceptRate: number;
|
|
657
|
+
/** Average time to accept (ms) */
|
|
658
|
+
avgTimeToAccept: number;
|
|
659
|
+
/** Compilable rate */
|
|
660
|
+
compilableRate: number;
|
|
661
|
+
/** Sum of time values (for running average) */
|
|
662
|
+
timeSum: number;
|
|
663
|
+
/** Count of compilable results */
|
|
664
|
+
compilableCount: number;
|
|
665
|
+
}
|
|
666
|
+
interface ExperimentConclusion {
|
|
667
|
+
/** Which group won */
|
|
668
|
+
winner: 'control' | 'variant' | 'no_difference';
|
|
669
|
+
/** Observed p-value */
|
|
670
|
+
pValue: number;
|
|
671
|
+
/** Effect size (difference in accept rates) */
|
|
672
|
+
effectSize: number;
|
|
673
|
+
/** Confidence interval for effect size */
|
|
674
|
+
confidenceInterval: [number, number];
|
|
675
|
+
/** Human-readable summary */
|
|
676
|
+
summary: string;
|
|
677
|
+
}
|
|
678
|
+
interface AssignmentResult {
|
|
679
|
+
/** Which group the request was assigned to */
|
|
680
|
+
group: 'control' | 'variant';
|
|
681
|
+
/** The strategy params for this group */
|
|
682
|
+
params: Record<string, unknown>;
|
|
683
|
+
/** Experiment ID for tracking */
|
|
684
|
+
experimentId: string;
|
|
685
|
+
}
|
|
686
|
+
declare function loadExperiments(projectPath: string): Experiment[];
|
|
687
|
+
declare function saveExperiments(projectPath: string, experiments: Experiment[]): void;
|
|
688
|
+
declare function createExperiment(id: string, name: string, description: string, controlParams: Record<string, unknown>, variantParams: Record<string, unknown>, options?: {
|
|
689
|
+
trafficSplit?: number;
|
|
690
|
+
minObservations?: number;
|
|
691
|
+
significanceThreshold?: number;
|
|
692
|
+
}): Experiment;
|
|
693
|
+
/**
|
|
694
|
+
* Assign a request to control or variant group.
|
|
695
|
+
* Uses deterministic hashing: same (experiment_id, task) → same group.
|
|
696
|
+
* This ensures consistency (retries get the same group).
|
|
697
|
+
*/
|
|
698
|
+
declare function assignGroup(experiment: Experiment, task: string): AssignmentResult | null;
|
|
699
|
+
/**
|
|
700
|
+
* Record an outcome for an experiment group.
|
|
701
|
+
* Updates running statistics and checks for significance.
|
|
702
|
+
*/
|
|
703
|
+
declare function recordOutcome(experiment: Experiment, group: 'control' | 'variant', outcome: {
|
|
704
|
+
accepted: boolean;
|
|
705
|
+
compilable?: boolean;
|
|
706
|
+
timeToAcceptMs?: number;
|
|
707
|
+
}): Experiment;
|
|
708
|
+
interface SignificanceResult {
|
|
709
|
+
/** Two-sided p-value */
|
|
710
|
+
pValue: number;
|
|
711
|
+
/** Z-score */
|
|
712
|
+
zScore: number;
|
|
713
|
+
/** Effect size: variant rate - control rate */
|
|
714
|
+
effectSize: number;
|
|
715
|
+
/** 95% confidence interval for effect size */
|
|
716
|
+
confidenceInterval: [number, number];
|
|
717
|
+
/** Whether the result is significant at the experiment's threshold */
|
|
718
|
+
significant: boolean;
|
|
719
|
+
}
|
|
720
|
+
/**
|
|
721
|
+
* Two-proportion z-test for A/B testing.
|
|
722
|
+
*
|
|
723
|
+
* H0: p_control = p_variant
|
|
724
|
+
* H1: p_control ≠ p_variant (two-sided)
|
|
725
|
+
*
|
|
726
|
+
* This is the standard test for comparing conversion rates.
|
|
727
|
+
*/
|
|
728
|
+
declare function testSignificance(experiment: Experiment): SignificanceResult;
|
|
729
|
+
/**
|
|
730
|
+
* Get the active experiment for this project (if any).
|
|
731
|
+
*/
|
|
732
|
+
declare function getActiveExperiment(experiments: Experiment[]): Experiment | null;
|
|
733
|
+
/**
|
|
734
|
+
* Get all concluded experiments with their results.
|
|
735
|
+
*/
|
|
736
|
+
declare function getConcludedExperiments(experiments: Experiment[]): Experiment[];
|
|
737
|
+
/**
|
|
738
|
+
* Render experiment summary for CLI/dashboard.
|
|
739
|
+
*/
|
|
740
|
+
declare function renderExperimentSummary(experiment: Experiment): string;
|
|
741
|
+
|
|
742
|
+
/**
|
|
743
|
+
* Polyglot Dependency Graph — Import Parsing for Python, Go, Java, Rust
|
|
744
|
+
*
|
|
745
|
+
* Problem: The existing graph.ts uses ts-morph (AST) which only handles TS/JS.
|
|
746
|
+
* For a 20K-dev org with Java, Python, Go, Rust — the dependency graph is empty.
|
|
747
|
+
* No graph → no hub detection → no risk scoring → useless context selection.
|
|
748
|
+
*
|
|
749
|
+
* Solution: Regex-based import parsers for each language. Not AST-accurate, but
|
|
750
|
+
* good enough for dependency graph construction. We don't need perfect resolution;
|
|
751
|
+
* we need to know "file A probably depends on file B" for hub/risk scoring.
|
|
752
|
+
*
|
|
753
|
+
* Each parser:
|
|
754
|
+
* 1. Extracts import specifiers from file content using regex
|
|
755
|
+
* 2. Resolves specifiers to relative file paths within the project
|
|
756
|
+
* 3. Returns edges: { from: relativePath, to: relativePath }
|
|
757
|
+
*
|
|
758
|
+
* Supported languages:
|
|
759
|
+
* - Python: import x, from x import y, relative imports
|
|
760
|
+
* - Go: import "pkg", import ( "pkg" ... )
|
|
761
|
+
* - Java: import com.example.Foo, package declaration
|
|
762
|
+
* - Rust: use crate::x, mod x, use super::x
|
|
763
|
+
*
|
|
764
|
+
* Design: Pure functions. No external deps. Deterministic.
|
|
765
|
+
*/
|
|
766
|
+
|
|
767
|
+
type SupportedLanguage = 'python' | 'go' | 'java' | 'rust' | 'typescript';
|
|
768
|
+
interface ImportSpec {
|
|
769
|
+
/** The raw import specifier as written in the source */
|
|
770
|
+
raw: string;
|
|
771
|
+
/** Whether this is a relative import */
|
|
772
|
+
isRelative: boolean;
|
|
773
|
+
}
|
|
774
|
+
declare function detectLanguage(filePath: string): SupportedLanguage | null;
|
|
775
|
+
/**
|
|
776
|
+
* Parse imports from a non-TS file and resolve to project-relative paths.
|
|
777
|
+
* Returns dependency edges for the project graph.
|
|
778
|
+
*
|
|
779
|
+
* @param filePath - Absolute path to the source file
|
|
780
|
+
* @param relativePath - Project-relative path (e.g., "src/auth/login.py")
|
|
781
|
+
* @param projectPath - Absolute path to the project root
|
|
782
|
+
* @param allRelativePaths - Set of all file paths in the project (for resolution)
|
|
783
|
+
* @param content - Optional file content (read from disk if not provided)
|
|
784
|
+
*/
|
|
785
|
+
declare function parseImports(filePath: string, relativePath: string, projectPath: string, allRelativePaths: Set<string>, content?: string): GraphEdge[];
|
|
786
|
+
/**
|
|
787
|
+
* Parse imports for ALL non-TS files in a project.
|
|
788
|
+
* Call this alongside ts-morph's buildProjectGraph for TS files.
|
|
789
|
+
*/
|
|
790
|
+
declare function parseAllPolyglotImports(files: {
|
|
791
|
+
relativePath: string;
|
|
792
|
+
absolutePath: string;
|
|
793
|
+
content?: string;
|
|
794
|
+
}[], projectPath: string): GraphEdge[];
|
|
795
|
+
/**
|
|
796
|
+
* Estimate cyclomatic complexity from source code using regex.
|
|
797
|
+
* Not AST-accurate but good enough for risk scoring.
|
|
798
|
+
*/
|
|
799
|
+
declare function estimateComplexity(content: string, lang: SupportedLanguage): number;
|
|
800
|
+
|
|
801
|
+
/**
|
|
802
|
+
* Multi-Stage Reranker
|
|
803
|
+
*
|
|
804
|
+
* The problem: BM25 retrieval gets 54% precision. Adding risk scoring drops it
|
|
805
|
+
* to 33% because high-risk irrelevant files fill the budget.
|
|
806
|
+
*
|
|
807
|
+
* The solution: a 3-stage pipeline that turns BM25 candidates into a precision-
|
|
808
|
+
* optimized selection:
|
|
809
|
+
*
|
|
810
|
+
* Stage 1: RETRIEVE (BM25 top-K) — already done by tfidf.ts
|
|
811
|
+
* Stage 2: RERANK (multi-signal rescoring)
|
|
812
|
+
* - Term coverage: what fraction of UNIQUE query terms does the file match?
|
|
813
|
+
* - Term specificity: are the matched terms rare (high IDF) or generic?
|
|
814
|
+
* - Bigram proximity: do query terms appear near each other in the file?
|
|
815
|
+
* - Dependency signal: is this file in the dependency cone of a top match?
|
|
816
|
+
* - Path relevance: does the file path match query terms?
|
|
817
|
+
* Stage 3: QUALITY GATE (adaptive cutoff)
|
|
818
|
+
* - Hard floor: files below absolute threshold are excluded
|
|
819
|
+
* - Elbow detection: find the natural drop-off point in scores
|
|
820
|
+
* - Don't fill budget with noise — stop when quality degrades
|
|
821
|
+
*
|
|
822
|
+
* This is a cross-encoder-like approach using hand-crafted features instead
|
|
823
|
+
* of a neural model. No ML dependencies. Deterministic.
|
|
824
|
+
*/
|
|
825
|
+
|
|
826
|
+
interface RerankInput {
|
|
827
|
+
/** Task description */
|
|
828
|
+
task: string;
|
|
829
|
+
/** BM25 candidates from tfidf.query() */
|
|
830
|
+
candidates: SemanticMatch[];
|
|
831
|
+
/** The TF-IDF index (for IDF weights) */
|
|
832
|
+
index: TfIdfIndex;
|
|
833
|
+
/** File contents for bigram proximity analysis */
|
|
834
|
+
fileContents: Map<string, string>;
|
|
835
|
+
/** Dependency edges: from → to[] */
|
|
836
|
+
dependencies: Map<string, string[]>;
|
|
837
|
+
/** All file paths in the project */
|
|
838
|
+
allFilePaths: string[];
|
|
839
|
+
}
|
|
840
|
+
interface RerankResult {
|
|
841
|
+
/** Reranked and filtered files — only high-quality matches */
|
|
842
|
+
files: RerankedFile[];
|
|
843
|
+
/** Files that were cut by the quality gate */
|
|
844
|
+
filtered: FilteredFile[];
|
|
845
|
+
/** The quality threshold used */
|
|
846
|
+
qualityThreshold: number;
|
|
847
|
+
/** Telemetry data for observability and debugging */
|
|
848
|
+
telemetry: RerankTelemetry;
|
|
849
|
+
}
|
|
850
|
+
interface RerankTelemetry {
|
|
851
|
+
/** Total candidates received from BM25 */
|
|
852
|
+
candidatesIn: number;
|
|
853
|
+
/** Files that passed the quality gate */
|
|
854
|
+
candidatesOut: number;
|
|
855
|
+
/** Files filtered out */
|
|
856
|
+
candidatesFiltered: number;
|
|
857
|
+
/** Timing in milliseconds */
|
|
858
|
+
durationMs: number;
|
|
859
|
+
/** Signal weight configuration used */
|
|
860
|
+
weights: typeof WEIGHTS;
|
|
861
|
+
/** Quality gate thresholds used */
|
|
862
|
+
gateConfig: {
|
|
863
|
+
absoluteFloor: number;
|
|
864
|
+
elbowDropRatio: number;
|
|
865
|
+
minTermCoverage: number;
|
|
866
|
+
};
|
|
867
|
+
/** Aggregate signal statistics across all candidates (before gate) */
|
|
868
|
+
signalStats: {
|
|
869
|
+
termCoverage: {
|
|
870
|
+
min: number;
|
|
871
|
+
max: number;
|
|
872
|
+
mean: number;
|
|
873
|
+
median: number;
|
|
874
|
+
};
|
|
875
|
+
termSpecificity: {
|
|
876
|
+
min: number;
|
|
877
|
+
max: number;
|
|
878
|
+
mean: number;
|
|
879
|
+
median: number;
|
|
880
|
+
};
|
|
881
|
+
bigramProximity: {
|
|
882
|
+
min: number;
|
|
883
|
+
max: number;
|
|
884
|
+
mean: number;
|
|
885
|
+
median: number;
|
|
886
|
+
};
|
|
887
|
+
dependencySignal: {
|
|
888
|
+
min: number;
|
|
889
|
+
max: number;
|
|
890
|
+
mean: number;
|
|
891
|
+
median: number;
|
|
892
|
+
};
|
|
893
|
+
pathRelevance: {
|
|
894
|
+
min: number;
|
|
895
|
+
max: number;
|
|
896
|
+
mean: number;
|
|
897
|
+
median: number;
|
|
898
|
+
};
|
|
899
|
+
};
|
|
900
|
+
/** Filter reason breakdown: reason → count */
|
|
901
|
+
filterReasons: Record<string, number>;
|
|
902
|
+
/** Score distribution: [min, p25, p50, p75, max] across all scored candidates */
|
|
903
|
+
scoreDistribution: [number, number, number, number, number];
|
|
904
|
+
/** Number of unique query terms */
|
|
905
|
+
queryTermCount: number;
|
|
906
|
+
/** Size of the dependency relevance cone */
|
|
907
|
+
relevanceConeSize: number;
|
|
908
|
+
}
|
|
909
|
+
interface RerankedFile {
|
|
910
|
+
filePath: string;
|
|
911
|
+
/** Final reranked score (0-1) */
|
|
912
|
+
score: number;
|
|
913
|
+
/** Original BM25 score */
|
|
914
|
+
bm25Score: number;
|
|
915
|
+
/** Individual signal scores */
|
|
916
|
+
signals: {
|
|
917
|
+
termCoverage: number;
|
|
918
|
+
termSpecificity: number;
|
|
919
|
+
bigramProximity: number;
|
|
920
|
+
dependencySignal: number;
|
|
921
|
+
pathRelevance: number;
|
|
922
|
+
};
|
|
923
|
+
}
|
|
924
|
+
interface FilteredFile {
|
|
925
|
+
filePath: string;
|
|
926
|
+
score: number;
|
|
927
|
+
reason: string;
|
|
928
|
+
}
|
|
929
|
+
declare const WEIGHTS: {
|
|
930
|
+
termCoverage: number;
|
|
931
|
+
termSpecificity: number;
|
|
932
|
+
bigramProximity: number;
|
|
933
|
+
dependencySignal: number;
|
|
934
|
+
pathRelevance: number;
|
|
935
|
+
};
|
|
936
|
+
/**
|
|
937
|
+
* Rerank BM25 candidates using multi-signal scoring + quality gate.
|
|
938
|
+
* Returns only files that pass the quality threshold.
|
|
939
|
+
*/
|
|
940
|
+
declare function rerank(input: RerankInput): RerankResult;
|
|
941
|
+
|
|
942
|
+
declare function countTokensTiktoken(text: string): number;
|
|
943
|
+
declare function countTokensChars4(sizeInBytes: number): number;
|
|
944
|
+
declare function estimateTokens(content: string, sizeInBytes: number, method?: 'chars4' | 'tiktoken'): number;
|
|
945
|
+
declare function estimateFileTokens(filePath: string, method?: 'chars4' | 'tiktoken'): Promise<number>;
|
|
946
|
+
declare function freeEncoder(): void;
|
|
947
|
+
|
|
851
948
|
type LogLevel = 'debug' | 'info' | 'warn' | 'error';
|
|
852
949
|
interface LogEntry {
|
|
853
950
|
level: LogLevel;
|
|
@@ -877,173 +974,36 @@ declare class CtoError extends Error {
|
|
|
877
974
|
declare function isCtoError(err: unknown): err is CtoError;
|
|
878
975
|
declare function wrapError(err: unknown, code: CtoErrorCode, module: string, context?: Record<string, unknown>): CtoError;
|
|
879
976
|
|
|
880
|
-
declare function
|
|
881
|
-
declare function
|
|
882
|
-
declare function
|
|
883
|
-
declare function
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
to: string;
|
|
901
|
-
files: number;
|
|
902
|
-
type: 'dependency' | 'devDependency' | 'peer';
|
|
903
|
-
}
|
|
904
|
-
interface MonorepoAnalysis {
|
|
905
|
-
detected: boolean;
|
|
906
|
-
tool: MonorepoTool;
|
|
907
|
-
rootPath: string;
|
|
908
|
-
packages: PackageInfo[];
|
|
909
|
-
sharedPackages: PackageInfo[];
|
|
910
|
-
crossPackageEdges: CrossPackageEdge[];
|
|
911
|
-
isolationScore: number;
|
|
912
|
-
totalTokens: number;
|
|
913
|
-
packageTokenMap: Record<string, number>;
|
|
914
|
-
}
|
|
915
|
-
interface PackageContextResult {
|
|
916
|
-
targetPackage: string;
|
|
917
|
-
includedPackages: string[];
|
|
918
|
-
excludedPackages: string[];
|
|
919
|
-
originalTokens: number;
|
|
920
|
-
optimizedTokens: number;
|
|
921
|
-
savedTokens: number;
|
|
922
|
-
savedPercent: number;
|
|
923
|
-
}
|
|
924
|
-
declare function detectMonorepoTool(rootPath: string): Promise<MonorepoTool>;
|
|
925
|
-
declare function analyzeMonorepo(rootPath: string, analysis?: ProjectAnalysis): Promise<MonorepoAnalysis>;
|
|
926
|
-
declare function selectPackageContext(monorepo: MonorepoAnalysis, targetPackage: string): PackageContextResult;
|
|
927
|
-
declare function renderMonorepoAnalysis(mono: MonorepoAnalysis): string;
|
|
928
|
-
declare function renderPackageContext(result: PackageContextResult): string;
|
|
929
|
-
|
|
930
|
-
interface QualityGateConfig {
|
|
931
|
-
threshold: number;
|
|
932
|
-
failOnSecrets: boolean;
|
|
933
|
-
failOnRegression: boolean;
|
|
934
|
-
regressionLimit: number;
|
|
935
|
-
baselinePath: string;
|
|
936
|
-
secretSeverities: string[];
|
|
937
|
-
}
|
|
938
|
-
interface QualityGateCheck {
|
|
939
|
-
name: string;
|
|
940
|
-
passed: boolean;
|
|
941
|
-
detail: string;
|
|
942
|
-
severity: 'error' | 'warning' | 'info';
|
|
943
|
-
}
|
|
944
|
-
interface Baseline {
|
|
945
|
-
score: number;
|
|
946
|
-
grade: string;
|
|
947
|
-
timestamp: string;
|
|
948
|
-
commit?: string;
|
|
949
|
-
branch?: string;
|
|
950
|
-
dimensions: Record<string, number>;
|
|
951
|
-
}
|
|
952
|
-
interface QualityGateResult {
|
|
953
|
-
passed: boolean;
|
|
954
|
-
score: number;
|
|
955
|
-
grade: string;
|
|
956
|
-
previousScore: number | null;
|
|
957
|
-
delta: number | null;
|
|
958
|
-
checks: QualityGateCheck[];
|
|
959
|
-
baseline: Baseline | null;
|
|
960
|
-
prComment: string;
|
|
961
|
-
summary: string;
|
|
962
|
-
}
|
|
963
|
-
declare const DEFAULT_GATE_CONFIG: QualityGateConfig;
|
|
964
|
-
declare function loadBaseline(projectPath: string, baselinePath?: string): Promise<Baseline | null>;
|
|
965
|
-
declare function saveBaseline(projectPath: string, score: ContextScore, commit?: string, branch?: string, baselinePath?: string): Promise<void>;
|
|
966
|
-
declare function runQualityGate(score: ContextScore, analysis: ProjectAnalysis, secretFindings: {
|
|
967
|
-
severity: string;
|
|
968
|
-
}[], config?: Partial<QualityGateConfig>): Promise<QualityGateResult>;
|
|
969
|
-
|
|
970
|
-
interface ReviewResult {
|
|
971
|
-
branch: string;
|
|
972
|
-
baseBranch: string;
|
|
973
|
-
isGitRepo: boolean;
|
|
974
|
-
changedFiles: ReviewFile[];
|
|
975
|
-
totalLinesChanged: number;
|
|
976
|
-
breakingChanges: BreakingChange[];
|
|
977
|
-
missingFiles: MissingFile[];
|
|
978
|
-
impactRadius: ImpactRadius;
|
|
979
|
-
reviewQuality: ReviewQuality;
|
|
980
|
-
reviewPrompt: string;
|
|
981
|
-
renderedSummary: string;
|
|
982
|
-
}
|
|
983
|
-
interface ReviewFile {
|
|
984
|
-
relativePath: string;
|
|
985
|
-
changeType: 'added' | 'modified' | 'deleted' | 'renamed';
|
|
986
|
-
linesAdded: number;
|
|
987
|
-
linesRemoved: number;
|
|
988
|
-
riskScore: number;
|
|
989
|
-
kind: string;
|
|
990
|
-
hunks: DiffHunk[];
|
|
991
|
-
hasExportChanges: boolean;
|
|
992
|
-
hasTypeChanges: boolean;
|
|
993
|
-
}
|
|
994
|
-
interface DiffHunk {
|
|
995
|
-
startLine: number;
|
|
996
|
-
endLine: number;
|
|
997
|
-
header: string;
|
|
998
|
-
additions: string[];
|
|
999
|
-
deletions: string[];
|
|
1000
|
-
}
|
|
1001
|
-
interface BreakingChange {
|
|
1002
|
-
file: string;
|
|
1003
|
-
type: 'export-removed' | 'export-renamed' | 'type-changed' | 'interface-changed' | 'function-signature' | 'enum-modified' | 'default-export-changed';
|
|
1004
|
-
severity: 'critical' | 'high' | 'medium';
|
|
1005
|
-
description: string;
|
|
1006
|
-
affectedFiles: string[];
|
|
1007
|
-
line?: number;
|
|
1008
|
-
}
|
|
1009
|
-
interface MissingFile {
|
|
1010
|
-
file: string;
|
|
1011
|
-
reason: string;
|
|
1012
|
-
severity: 'high' | 'medium' | 'low';
|
|
1013
|
-
relatedChangedFile: string;
|
|
1014
|
-
relationship: 'imports' | 'imported-by' | 'sibling-type' | 'test' | 'co-located';
|
|
1015
|
-
}
|
|
1016
|
-
interface ImpactRadius {
|
|
1017
|
-
directlyAffected: number;
|
|
1018
|
-
transitivelyAffected: number;
|
|
1019
|
-
totalAffected: number;
|
|
1020
|
-
affectedTests: number;
|
|
1021
|
-
riskScore: number;
|
|
1022
|
-
hotspots: {
|
|
1023
|
-
file: string;
|
|
1024
|
-
dependents: number;
|
|
1025
|
-
riskScore: number;
|
|
1026
|
-
}[];
|
|
1027
|
-
}
|
|
1028
|
-
interface ReviewQuality {
|
|
1029
|
-
score: number;
|
|
1030
|
-
grade: string;
|
|
1031
|
-
factors: ReviewFactor[];
|
|
1032
|
-
}
|
|
1033
|
-
interface ReviewFactor {
|
|
1034
|
-
name: string;
|
|
1035
|
-
score: number;
|
|
1036
|
-
weight: number;
|
|
1037
|
-
detail: string;
|
|
977
|
+
declare function scanContentForSecrets(content: string, filePath: string, customPatterns?: string[], extraPiiSafeDomains?: Set<string>): SecretFinding[];
|
|
978
|
+
declare function scanFileForSecrets(filePath: string, projectPath: string, customPatterns?: string[]): Promise<SecretFinding[]>;
|
|
979
|
+
declare function scanProjectForSecrets(projectPath: string, filePaths: string[], customPatterns?: string[]): Promise<SecretFinding[]>;
|
|
980
|
+
declare function sanitizeContent(content: string, customPatterns?: string[]): string;
|
|
981
|
+
interface AuditResult {
|
|
982
|
+
findings: SecretFinding[];
|
|
983
|
+
summary: {
|
|
984
|
+
totalFiles: number;
|
|
985
|
+
filesScanned: number;
|
|
986
|
+
filesWithSecrets: number;
|
|
987
|
+
totalFindings: number;
|
|
988
|
+
bySeverity: {
|
|
989
|
+
critical: number;
|
|
990
|
+
high: number;
|
|
991
|
+
medium: number;
|
|
992
|
+
low: number;
|
|
993
|
+
};
|
|
994
|
+
byType: Record<string, number>;
|
|
995
|
+
};
|
|
996
|
+
recommendations: string[];
|
|
1038
997
|
}
|
|
1039
|
-
interface
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
998
|
+
interface AuditOptions {
|
|
999
|
+
customPatterns?: string[];
|
|
1000
|
+
entropyThreshold?: number;
|
|
1001
|
+
includePII?: boolean;
|
|
1002
|
+
useAllowlist?: boolean;
|
|
1003
|
+
incrementalScan?: boolean;
|
|
1004
|
+
severityOverrides?: Partial<Record<SecretType, SecretFinding['severity']>>;
|
|
1005
|
+
piiSafeDomains?: string[];
|
|
1045
1006
|
}
|
|
1046
|
-
declare function
|
|
1047
|
-
declare function renderReviewSummary(branch: string, baseBranch: string, changedFiles: ReviewFile[], breakingChanges: BreakingChange[], missingFiles: MissingFile[], impactRadius: ImpactRadius, reviewQuality: ReviewQuality): string;
|
|
1007
|
+
declare function auditProject(projectPath: string, filePaths: string[], options?: AuditOptions): Promise<AuditResult>;
|
|
1048
1008
|
|
|
1049
|
-
export { type
|
|
1009
|
+
export { type AssignmentResult, type ContextPipelineInput, type ContextPipelineResult, CtoError, type CtoErrorCode, type DocumentVector, type Experiment, type ExperimentConclusion, type ExperimentGroup, type FilteredFile, type GroupMetrics, type ImportSpec, type IndexCacheStats, type LearnerBoost, type LearnerBoostInput, type LearnerModel, type LogEntry, type LogLevel, type Logger, type MultiRepoResult, type PatternStats, type RerankInput, type RerankResult, type RerankedFile, type SecretFinding, type SecretType, type SelectionInput, type SemanticMatch, type SemanticScore, type SiblingMatch, type SiblingRepo, type SignificanceResult, type SupportedLanguage, type TfIdfIndex, analyzeProject, assignGroup, auditProject, bfsBidirectional, boostByPath, buildAdjacencyList, buildIndex, buildIndexCached, buildProjectGraph, calculateCoverage, classifyFileKind, countTokensChars4, countTokensTiktoken, createExperiment, createLogger, createProject, detectLanguage, detectStack, discoverSiblingRepos, estimateComplexity, estimateFileTokens, estimateTokens, extractPattern, freeEncoder, getActiveExperiment, getCacheInfo, getConcludedExperiments, getLearnerBoosts, getLearnerStats, getPruneLevelForRisk, invalidateCache, isCtoError, loadExperiments, loadLearner, optimizeBudget, parseAllPolyglotImports, parseImports, parseSiblingPaths, pruneFile, pruneFiles, query, querySiblingRepos, recordOutcome, recordSelection, renderExperimentSummary, renderMultiRepoSummary, rerank, runContextPipeline, sanitizeContent, saveExperiments, saveLearner, scanContentForSecrets, scanFileForSecrets, scanProjectForSecrets, scoreAllFiles, scoreFile, selectContext, setJsonLogging, setLogLevel, similarity, testSignificance, tokenize, walkProject, wrapError };
|