cto-ai-cli 5.2.0 → 6.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.
@@ -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,633 +218,196 @@ 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
- declare function getConfigPath(projectPath: string): string;
225
- declare function getPolicyPath(projectPath: string): string;
226
- declare function getCTODir(projectPath: string): string;
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;
234
+ }
235
+ interface LearnerBoostInput {
236
+ filePath: string;
237
+ boost: number;
240
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
+
250
+ declare function scoreAllFiles(files: AnalyzedFile[], graph: ProjectGraph, weights?: RiskWeights): void;
251
+ declare function scoreFile(file: AnalyzedFile, graph: ProjectGraph, weights?: RiskWeights): number;
252
+
253
+ declare function calculateCoverage(targetPaths: string[], includedPaths: string[], allFiles: AnalyzedFile[], graph: ProjectGraph, depth?: number): CoverageResult;
254
+
255
+ declare function getPruneLevelForRisk(riskScore: number): PruneLevel;
256
+ declare function optimizeBudget(files: AnalyzedFile[], budget: number): Promise<BudgetPlan>;
257
+
258
+ declare function pruneFile(file: AnalyzedFile, level: PruneLevel): Promise<PrunedContent>;
259
+ declare function pruneFiles(files: AnalyzedFile[], levelFn: (file: AnalyzedFile) => PruneLevel): Promise<PrunedContent[]>;
260
+
241
261
  /**
242
- * Get a project analysis, using cache when possible.
262
+ * TF-IDF Semantic Matching Engine
243
263
  *
244
- * Cache hit: ~1-5ms (fingerprint check only)
245
- * Cache miss: full analyzeProject() + cache update
246
- */
247
- declare function getCachedAnalysis(projectPath: string, config?: Partial<CTOConfig>): Promise<ProjectAnalysis>;
248
- /**
249
- * Invalidate cache for a specific project (e.g., after a known file change).
250
- */
251
- declare function invalidateCache(projectPath?: string): void;
252
- /**
253
- * Get cache statistics for debugging/monitoring.
254
- */
255
- declare function getCacheStats(): {
256
- entries: number;
257
- totalHits: number;
258
- projects: {
259
- path: string;
260
- hits: number;
261
- ageMs: number;
262
- }[];
263
- };
264
- /**
265
- * Configure cache behavior.
264
+ * Zero dependencies. Pure math.
265
+ *
266
+ * Computes term frequency–inverse document frequency vectors for all files
267
+ * in a project and matches task descriptions against file content semantically.
268
+ * This replaces naive keyword matching with real information retrieval.
269
+ *
270
+ * How it works:
271
+ * 1. Tokenize each file into terms (identifiers, words)
272
+ * 2. Compute TF (term frequency) per document
273
+ * 3. Compute IDF (inverse document frequency) across corpus
274
+ * 4. Score query against each document using cosine similarity
275
+ *
276
+ * Why this matters:
277
+ * - "fix authentication" matches `auth.ts` even though "authentication" ≠ "auth"
278
+ * because stemming normalizes both to "auth"
279
+ * - Files that mention rare, task-specific terms rank higher than files
280
+ * full of common terms like "import", "export", "function"
281
+ * - BM25 variant avoids over-weighting long files
266
282
  */
267
- declare function configureCache(options: Partial<CacheOptions>): void;
268
-
269
- interface WatcherOptions {
270
- debounceMs?: number;
271
- config?: Partial<CTOConfig>;
283
+ interface TfIdfIndex {
284
+ documents: Map<string, DocumentVector>;
285
+ idf: Map<string, number>;
286
+ avgDocLength: number;
287
+ totalDocs: number;
272
288
  }
273
- interface FileChangeEvent {
274
- type: 'add' | 'change' | 'unlink';
275
- path: string;
276
- timestamp: Date;
289
+ interface DocumentVector {
290
+ terms: Map<string, number>;
291
+ length: number;
277
292
  }
278
- declare class ProjectWatcher extends EventEmitter {
279
- private watcher;
280
- private projectPath;
281
- private debounceMs;
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;
293
+ interface SemanticMatch {
294
+ filePath: string;
295
+ score: number;
296
+ matchedTerms: string[];
293
297
  }
294
298
  /**
295
- * Start watching a project. Returns the watcher instance.
296
- * If already watching, returns the existing watcher.
299
+ * Build a TF-IDF index from file contents.
300
+ * Call this once per project analysis, then query multiple times.
301
+ */
302
+ declare function buildIndex(files: {
303
+ relativePath: string;
304
+ content: string;
305
+ }[]): TfIdfIndex;
306
+ /**
307
+ * Query the index with a task description.
308
+ * Returns files ranked by semantic relevance (BM25 scoring).
297
309
  */
298
- declare function watchProject(projectPath: string, options?: WatcherOptions): Promise<ProjectWatcher>;
310
+ declare function query(index: TfIdfIndex, taskDescription: string, maxResults?: number): SemanticMatch[];
299
311
  /**
300
- * Stop watching a specific project.
312
+ * Compute pairwise similarity between two documents in the index.
313
+ * Useful for finding related files (e.g., "what other files are similar to auth.ts?")
301
314
  */
302
- declare function unwatchProject(projectPath: string): Promise<void>;
315
+ declare function similarity(index: TfIdfIndex, pathA: string, pathB: string): number;
303
316
  /**
304
- * Stop all active watchers. Call on process shutdown.
317
+ * Tokenize source code into meaningful terms.
318
+ * Handles camelCase, snake_case, PascalCase splitting.
319
+ * Applies stemming and stop word removal.
305
320
  */
306
- declare function unwatchAll(): Promise<void>;
321
+ declare function tokenize(text: string): string[];
307
322
  /**
308
- * Get all actively watched projects.
323
+ * Boost TF-IDF scores based on file path relevance to the task.
324
+ * This catches cases where the file content doesn't mention the task terms
325
+ * but the file path does (e.g., task "fix auth" → src/auth/middleware.ts).
309
326
  */
310
- declare function getActiveWatchers(): {
311
- path: string;
312
- running: boolean;
313
- }[];
327
+ declare function boostByPath(matches: SemanticMatch[], allFiles: string[], taskDescription: string): SemanticMatch[];
314
328
 
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
- }
346
329
  /**
347
- * Generate PR-focused context by analyzing git changes and expanding dependencies.
330
+ * Usage Learner Gets smarter with every use.
331
+ *
332
+ * Zero dependencies. Bayesian priors updated from real usage.
333
+ *
334
+ * How it works:
335
+ * 1. Every time context is selected, log what was included/excluded
336
+ * 2. When user accepts/rejects files, update file relevance priors
337
+ * 3. Next selection uses priors as boost signals alongside TF-IDF + risk
338
+ *
339
+ * Storage: .cto/learner.json — portable, versioned, <10KB typical
348
340
  *
349
- * @param analysis - Project analysis (from analyzeProject or getCachedAnalysis)
350
- * @param options - PR context options (baseBranch, depth, includeTests)
351
- * @returns Structured PR context with changed files, dependencies, risk summary
341
+ * The model is intentionally simple (no neural nets, no embeddings model):
342
+ * - Beta distribution per file pattern (alpha=accepted, beta=rejected)
343
+ * - Exponential decay so recent feedback weighs more
344
+ * - Pattern-based: learns "test files matter for debug tasks", not
345
+ * specific file paths (so it works across branches/renames)
352
346
  */
353
- declare function generatePRContext(analysis: ProjectAnalysis, options?: PRContextOptions): Promise<PRContextResult>;
354
-
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
- };
347
+ interface LearnerModel {
348
+ version: 2;
349
+ updatedAt: string;
350
+ patterns: Record<string, PatternStats>;
351
+ taskPatterns: Record<string, Record<string, PatternStats>>;
352
+ totalSelections: number;
380
353
  }
381
- interface DimensionScore {
382
- score: number;
383
- weight: number;
384
- weighted: number;
385
- detail: string;
354
+ interface PatternStats {
355
+ alpha: number;
356
+ beta: number;
357
+ lastSeen: string;
386
358
  }
387
- interface ScoreInsight {
388
- type: 'strength' | 'weakness' | 'opportunity';
389
- title: string;
390
- detail: string;
391
- impact: 'high' | 'medium' | 'low';
359
+ interface LearnerBoost {
360
+ filePath: string;
361
+ boost: number;
362
+ confidence: number;
363
+ reason: string;
392
364
  }
393
365
  /**
394
- * Compute the Context Score™ for a project.
395
- *
396
- * @param analysis - Project analysis (from analyzeProject or getCachedAnalysis)
397
- * @param task - Representative task (default: "general code review")
398
- * @param budget - Token budget (default: 50000)
366
+ * Load the learner model from .cto/learner.json.
367
+ * Returns empty model if none exists.
399
368
  */
400
- declare function computeContextScore(analysis: ProjectAnalysis, task?: string, budget?: number): Promise<ContextScore>;
369
+ declare function loadLearner(projectPath: string): Promise<LearnerModel>;
401
370
  /**
402
- * Render the Context Score as a beautiful terminal-friendly string.
371
+ * Save the learner model.
403
372
  */
404
- declare function renderContextScore(score: ContextScore): string;
405
-
406
- interface BenchmarkResult {
407
- project: string;
408
- totalFiles: number;
409
- totalTokens: number;
410
- budget: number;
411
- task: string;
412
- strategies: {
413
- cto: StrategyResult;
414
- naive: StrategyResult;
415
- random: StrategyResult;
416
- };
417
- winner: 'cto' | 'naive' | 'random';
418
- ctoAdvantage: {
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;
435
- }
373
+ declare function saveLearner(projectPath: string, model: LearnerModel): Promise<void>;
436
374
  /**
437
- * Run a full benchmark comparing CTO vs naive vs random selection.
375
+ * Record a selection event which files were included and which were excluded.
376
+ * Updates Bayesian priors with exponential decay.
438
377
  */
439
- declare function runBenchmark(analysis: ProjectAnalysis, task?: string, budget?: number): Promise<BenchmarkResult>;
378
+ declare function recordSelection(model: LearnerModel, taskType: string, selectedFiles: string[], excludedFiles: string[]): LearnerModel;
440
379
  /**
441
- * Render benchmark results as a formatted comparison table.
380
+ * Get boost signals for files based on learned patterns.
381
+ * Returns boosts that should be applied during context selection.
442
382
  */
443
- declare function renderBenchmark(result: BenchmarkResult): string;
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
- }
383
+ declare function getLearnerBoosts(model: LearnerModel, taskType: string, files: string[]): LearnerBoost[];
491
384
  /**
492
- * Run a quality benchmark comparing CTO vs naive vs random context selection.
493
- * Measures relevance, completeness, noise, and dependency closure.
385
+ * Get learner stats for display.
494
386
  */
495
- declare function runQualityBenchmark(analysis: ProjectAnalysis, task: string, budget?: number): Promise<QualityBenchmarkResult>;
496
- declare function renderQualityBenchmark(result: QualityBenchmarkResult): string;
497
-
498
- interface PredictorModel {
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;
537
- };
538
-
539
- interface ProjectFingerprint {
540
- stack: string[];
541
- sizeClass: 'tiny' | 'small' | 'medium' | 'large' | 'huge';
542
- hasTypes: boolean;
543
- hasTests: boolean;
544
- isMonorepo: boolean;
545
- entryPointPatterns: string[];
546
- dominantLanguage: string;
547
- }
548
- interface CrossRepoModel {
549
- version: number;
550
- updatedAt: string;
551
- totalProjects: number;
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;
561
- taskPatterns: Record<string, Record<string, PatternStats>>;
562
- criticalKinds: {
563
- kind: string;
564
- importance: number;
565
- }[];
566
- criticalDirs: {
567
- pattern: string;
568
- importance: number;
569
- }[];
570
- }
571
- interface PatternStats {
572
- pattern: string;
573
- hitCount: number;
387
+ declare function getLearnerStats(model: LearnerModel): {
574
388
  totalSelections: number;
575
- hitRate: number;
576
- avgRelevanceBoost: number;
577
- }
578
- interface CrossRepoPrediction {
579
- filePath: string;
580
- boost: number;
581
- reason: string;
582
- confidence: number;
583
- }
584
- declare function computeFingerprint(analysis: ProjectAnalysis): ProjectFingerprint;
585
- declare function loadGlobalModel(): Promise<CrossRepoModel>;
586
- declare function recordCrossRepoSelection(analysis: ProjectAnalysis, task: string, selectedFiles: {
587
- relativePath: string;
588
- riskScore: number;
589
- tokens: number;
590
- }[]): Promise<CrossRepoModel>;
591
- declare function predictFromCrossRepo(analysis: ProjectAnalysis, task: string): Promise<CrossRepoPrediction[]>;
592
- declare function getCrossRepoStats(model: CrossRepoModel): {
593
- totalProjects: number;
594
- totalObservations: number;
595
- archetypes: {
596
- name: string;
597
- projects: number;
598
- observations: number;
599
- }[];
600
- universalPatterns: number;
601
- };
602
-
603
- interface FeedbackEntry {
604
- id: string;
605
- timestamp: string;
606
- task: string;
607
- taskType: TaskType;
608
- contextHash: string;
609
- filesIncluded: string[];
610
- tokensUsed: number;
611
- budget: number;
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;
389
+ patternsLearned: number;
390
+ taskTypes: string[];
391
+ topPatterns: {
392
+ pattern: string;
663
393
  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
394
  observations: number;
699
395
  }[];
700
- universalPatterns: number;
701
- }): string;
702
-
703
- interface SemanticFingerprint {
704
- filePath: string;
705
- domains: SemanticDomain[];
706
- exports: string[];
707
- concepts: string[];
708
- patterns: CodePattern[];
709
- intentScore: Map<string, number>;
710
- }
711
- interface SemanticDomain {
712
- name: string;
713
- confidence: number;
714
- signals: string[];
715
- }
716
- interface CodePattern {
717
- type: 'route' | 'model' | 'middleware' | 'config' | 'test' | 'type' | 'util' | 'entry' | 'event' | 'error';
718
- evidence: string;
719
- }
720
- interface SemanticAnalysis {
721
- files: SemanticFingerprint[];
722
- domainGraph: {
723
- from: string;
724
- to: string;
725
- strength: number;
726
- }[];
727
- domainClusters: {
728
- domain: string;
729
- files: string[];
730
- tokenBudget: number;
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;
754
- }
755
- interface CompilabilityMetrics {
756
- typeFilesAvailable: number;
757
- typeFilesTotal: number;
758
- typeAvailabilityPercent: number;
759
- importChainsComplete: number;
760
- importChainsTotal: number;
761
- importCompletenessPercent: number;
762
- missingTypeFiles: string[];
763
- missingDependencies: string[];
764
- predictedTypeErrors: number;
765
- predictedImportErrors: number;
766
- predictedTotalErrors: number;
767
- compilabilityScore: number;
768
- filesSelected: number;
769
- tokensUsed: number;
770
- }
771
- declare function runCompilabilityBenchmark(analysis: ProjectAnalysis, task: string, budget?: number): Promise<CompilabilityResult>;
772
- declare function renderCompilabilityBenchmark(result: CompilabilityResult): string;
773
-
774
- interface CompileProofResult {
775
- project: string;
776
- task: string;
777
- budget: number;
778
- cto: CompileProofStrategy;
779
- naive: CompileProofStrategy;
780
- random: CompileProofStrategy;
781
- headline: string;
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;
796
-
797
- interface ModelProfile {
798
- id: string;
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 {
810
- task: string;
811
- models: ModelOptimization[];
812
- recommendation: {
813
- bestValue: string;
814
- bestQuality: string;
815
- bestSpeed: string;
816
- reasoning: string;
817
- };
818
- }
819
- interface ModelOptimization {
820
- model: ModelProfile;
821
- budget: number;
822
- selection: ContextSelection;
823
- estimatedCost: number;
824
- qualityScore: number;
825
- recommendation: string;
826
- }
827
- declare const MODEL_REGISTRY: ModelProfile[];
828
- declare function optimizeForModels(analysis: ProjectAnalysis, task: string, models?: string[]): Promise<MultiModelResult>;
829
- declare function renderMultiModelResult(result: MultiModelResult): string;
830
-
831
- declare function scoreAllFiles(files: AnalyzedFile[], graph: ProjectGraph, weights?: RiskWeights): void;
832
- declare function scoreFile(file: AnalyzedFile, graph: ProjectGraph, weights?: RiskWeights): number;
833
-
834
- declare function calculateCoverage(targetPaths: string[], includedPaths: string[], allFiles: AnalyzedFile[], graph: ProjectGraph, depth?: number): CoverageResult;
835
-
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
- declare function getPruneLevelForRisk(riskScore: number): PruneLevel;
846
- declare function optimizeBudget(files: AnalyzedFile[], budget: number): Promise<BudgetPlan>;
396
+ };
397
+ /**
398
+ * Extract a generalizable pattern from a file path.
399
+ * Examples: "src/engine/analyzer.ts" becomes "engine/(star).ts",
400
+ * "tests/unit/auth.test.ts" becomes "tests/(star)(star)/(star).test.ts".
401
+ * This way the learner generalizes across similar files,
402
+ * not just memorize specific paths.
403
+ */
404
+ declare function extractPattern(filePath: string): string;
847
405
 
848
- declare function pruneFile(file: AnalyzedFile, level: PruneLevel): Promise<PrunedContent>;
849
- declare function pruneFiles(files: AnalyzedFile[], levelFn: (file: AnalyzedFile) => PruneLevel): Promise<PrunedContent[]>;
406
+ declare function countTokensTiktoken(text: string): number;
407
+ declare function countTokensChars4(sizeInBytes: number): number;
408
+ declare function estimateTokens(content: string, sizeInBytes: number, method?: 'chars4' | 'tiktoken'): number;
409
+ declare function estimateFileTokens(filePath: string, method?: 'chars4' | 'tiktoken'): Promise<number>;
410
+ declare function freeEncoder(): void;
850
411
 
851
412
  type LogLevel = 'debug' | 'info' | 'warn' | 'error';
852
413
  interface LogEntry {
@@ -877,173 +438,36 @@ declare class CtoError extends Error {
877
438
  declare function isCtoError(err: unknown): err is CtoError;
878
439
  declare function wrapError(err: unknown, code: CtoErrorCode, module: string, context?: Record<string, unknown>): CtoError;
879
440
 
880
- declare function countTokensTiktoken(text: string): number;
881
- declare function countTokensChars4(sizeInBytes: number): number;
882
- declare function estimateTokens(content: string, sizeInBytes: number, method?: 'chars4' | 'tiktoken'): number;
883
- declare function estimateFileTokens(filePath: string, method?: 'chars4' | 'tiktoken'): Promise<number>;
884
- declare function freeEncoder(): void;
885
-
886
- type MonorepoTool = 'npm-workspaces' | 'yarn-workspaces' | 'pnpm-workspaces' | 'turborepo' | 'nx' | 'lerna' | 'none';
887
- interface PackageInfo {
888
- name: string;
889
- path: string;
890
- relativePath: string;
891
- files: number;
892
- tokens: number;
893
- dependencies: string[];
894
- dependents: string[];
895
- isShared: boolean;
896
- entryPoints: string[];
897
- }
898
- interface CrossPackageEdge {
899
- from: string;
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;
441
+ declare function scanContentForSecrets(content: string, filePath: string, customPatterns?: string[], extraPiiSafeDomains?: Set<string>): SecretFinding[];
442
+ declare function scanFileForSecrets(filePath: string, projectPath: string, customPatterns?: string[]): Promise<SecretFinding[]>;
443
+ declare function scanProjectForSecrets(projectPath: string, filePaths: string[], customPatterns?: string[]): Promise<SecretFinding[]>;
444
+ declare function sanitizeContent(content: string, customPatterns?: string[]): string;
445
+ interface AuditResult {
446
+ findings: SecretFinding[];
447
+ summary: {
448
+ totalFiles: number;
449
+ filesScanned: number;
450
+ filesWithSecrets: number;
451
+ totalFindings: number;
452
+ bySeverity: {
453
+ critical: number;
454
+ high: number;
455
+ medium: number;
456
+ low: number;
457
+ };
458
+ byType: Record<string, number>;
459
+ };
460
+ recommendations: string[];
1038
461
  }
1039
- interface ReviewOptions {
1040
- baseBranch?: string;
1041
- depth?: number;
1042
- includeTests?: boolean;
1043
- maxPromptFiles?: number;
1044
- maxPromptTokens?: number;
462
+ interface AuditOptions {
463
+ customPatterns?: string[];
464
+ entropyThreshold?: number;
465
+ includePII?: boolean;
466
+ useAllowlist?: boolean;
467
+ incrementalScan?: boolean;
468
+ severityOverrides?: Partial<Record<SecretType, SecretFinding['severity']>>;
469
+ piiSafeDomains?: string[];
1045
470
  }
1046
- declare function analyzeForReview(analysis: ProjectAnalysis, options?: ReviewOptions): Promise<ReviewResult>;
1047
- declare function renderReviewSummary(branch: string, baseBranch: string, changedFiles: ReviewFile[], breakingChanges: BreakingChange[], missingFiles: MissingFile[], impactRadius: ImpactRadius, reviewQuality: ReviewQuality): string;
471
+ declare function auditProject(projectPath: string, filePaths: string[], options?: AuditOptions): Promise<AuditResult>;
1048
472
 
1049
- export { type Baseline, type BenchmarkResult, type BreakingChange, type ChangeType, type ChangedFile, type CompilabilityMetrics, type CompilabilityResult, type CompileProofResult, type CompileProofStrategy, type ContextScore, type CrossPackageEdge, type CrossRepoModel, type CrossRepoPrediction, CtoError, type CtoErrorCode, DEFAULT_GATE_CONFIG, type DiffHunk, type DimensionScore, type FeedbackEntry, type FeedbackInsight, type FeedbackModel, type FeedbackOutcome, type FileChangeEvent, type Grade, type ImpactRadius, type LogEntry, type LogLevel, type Logger, MODEL_REGISTRY, type MissingFile, type ModelOptimization, type ModelProfile, type MonorepoAnalysis, type MonorepoTool, type MultiModelResult, type PRContextOptions, type PRContextResult, type PackageContextResult, type PackageInfo, type PredictionResult, type PredictorModel, type ProjectFingerprint, ProjectWatcher, type QualityBenchmarkResult, type QualityGateCheck, type QualityGateConfig, type QualityGateResult, type QualityMetrics, type ReviewFactor, type ReviewFile, type ReviewOptions, type ReviewQuality, type ReviewResult, type ScoreInsight, type SelectionInput, type SemanticAnalysis, type SemanticDomain, type SemanticFingerprint, type StrategyResult, type TeamFeedbackExport, type WatcherOptions, analyzeForReview, analyzeMonorepo, analyzeProject, analyzeSemantics, bfsBidirectional, buildAdjacencyList, buildProjectGraph, calculateCoverage, classifyFileKind, computeContextScore, computeFingerprint, configureCache, countTokensChars4, countTokensTiktoken, createLogger, createProject, detectMonorepoTool, detectStack, estimateFileTokens, estimateTokens, exportFeedbackForTeam, freeEncoder, generatePRContext, getActiveWatchers, getCTODir, getCacheStats, getCachedAnalysis, getConfigPath, getCrossRepoStats, getFeedbackBoosts, getModelStats, getPolicyPath, getPredictorBoosts, getPruneLevelForRisk, importTeamFeedback, initProjectConfig, invalidateCache, isCtoError, loadBaseline, loadConfig, loadFeedbackModel, loadGlobalModel, loadModel, loadPolicyFromYAML, matchGlob, optimizeBudget, optimizeForModels, predictFromCrossRepo, predictRelevantFiles, pruneFile, pruneFiles, recordCrossRepoSelection, recordFeedback, recordSelection, renderBenchmark, renderCompilabilityBenchmark, renderCompileProof, renderContextScore, renderCrossRepoReport, renderFeedbackReport, renderMonorepoAnalysis, renderMultiModelResult, renderPackageContext, renderQualityBenchmark, renderReviewSummary, renderSemanticAnalysis, runBenchmark, runCompilabilityBenchmark, runCompileProof, runQualityBenchmark, runQualityGate, saveBaseline, saveConfig, scoreAllFiles, scoreFile, selectContext, selectPackageContext, semanticBoosts, setJsonLogging, setLogLevel, unwatchAll, unwatchProject, walkProject, watchProject, wilsonLowerBound, wrapError };
473
+ export { CtoError, type CtoErrorCode, type DocumentVector, type LearnerBoost, type LearnerBoostInput, type LearnerModel, type LogEntry, type LogLevel, type Logger, type PatternStats, type SecretFinding, type SecretType, type SelectionInput, type SemanticMatch, type SemanticScore, type TfIdfIndex, analyzeProject, auditProject, bfsBidirectional, boostByPath, buildAdjacencyList, buildIndex, buildProjectGraph, calculateCoverage, classifyFileKind, countTokensChars4, countTokensTiktoken, createLogger, createProject, detectStack, estimateFileTokens, estimateTokens, extractPattern, freeEncoder, getLearnerBoosts, getLearnerStats, getPruneLevelForRisk, isCtoError, loadLearner, optimizeBudget, pruneFile, pruneFiles, query, recordSelection, sanitizeContent, saveLearner, scanContentForSecrets, scanFileForSecrets, scanProjectForSecrets, scoreAllFiles, scoreFile, selectContext, setJsonLogging, setLogLevel, similarity, tokenize, walkProject, wrapError };