ctxman 3.1.0 → 3.2.7

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.
Files changed (60) hide show
  1. package/.ctxman/llm-profiles.json +6 -6
  2. package/README.md +17 -10
  3. package/bin/cli.js +107 -8
  4. package/bin/cm-ask.js +74 -70
  5. package/bin/cm-audit.js +80 -7
  6. package/bin/cm-diagnose.js +199 -0
  7. package/bin/cm-focus.js +322 -0
  8. package/bin/cm-map.js +71 -18
  9. package/bin/cm-pack.js +36 -5
  10. package/bin/cm-policy.js +107 -2
  11. package/bin/cm-pr.js +18 -3
  12. package/bin/cm-search.js +84 -19
  13. package/bin/cm-task.js +192 -0
  14. package/bin/cm-who.js +119 -0
  15. package/bin/cm-why.js +41 -5
  16. package/examples/github-actions/ctxman-check.yml +1 -1
  17. package/index.d.ts +931 -1
  18. package/index.js +105 -1
  19. package/lib/analyzers/performance-dashboard.js +7 -3
  20. package/lib/analyzers/token-calculator.js +76 -4
  21. package/lib/api/mcp/server.js +713 -14
  22. package/lib/cache/CacheManager.js +161 -47
  23. package/lib/core/Analyzer.js +61 -5
  24. package/lib/diagnose/index.js +712 -0
  25. package/lib/focus/index.js +790 -0
  26. package/lib/map/call-graph.js +570 -0
  27. package/lib/map/graph.js +30 -5
  28. package/lib/map/index.js +12 -4
  29. package/lib/map/rank.js +45 -7
  30. package/lib/map/render.js +89 -28
  31. package/lib/pack/index.js +20 -1
  32. package/lib/pack/select.js +8 -1
  33. package/lib/parsers/gitignore-parser.js +4 -1
  34. package/lib/policy/audit.js +22 -2
  35. package/lib/policy/index.js +6 -0
  36. package/lib/policy/workspace.js +254 -0
  37. package/lib/pr/pr-context.js +292 -31
  38. package/lib/pr/pr-renderer.js +25 -10
  39. package/lib/rag/EmbeddingProvider.js +139 -2
  40. package/lib/rag/VectorStoreFactory.js +10 -4
  41. package/lib/rag/ask.js +291 -0
  42. package/lib/rag/sources/FileDataSource.js +6 -3
  43. package/lib/rag/stores/SqliteVectorStore.js +595 -0
  44. package/lib/search/hybrid.js +324 -0
  45. package/lib/search/symbols.js +75 -11
  46. package/lib/task/index.js +630 -0
  47. package/lib/utils/file-utils.js +1 -0
  48. package/lib/utils/logger.js +5 -2
  49. package/lib/utils/multi-repo-manager.js +4 -1
  50. package/lib/watch/FileWatcher.js +121 -31
  51. package/lib/watch/IncrementalAnalyzer.js +964 -16
  52. package/lib/who/index.js +893 -0
  53. package/lib/why/analyzer-bridge.js +223 -0
  54. package/lib/why/discussions.js +314 -0
  55. package/lib/why/index.js +356 -30
  56. package/lib/why/render.js +154 -9
  57. package/lib/wizards/config-generator.js +20 -18
  58. package/lib/wizards/project-detector.js +2 -1
  59. package/lib/workspace/index.js +1274 -0
  60. package/package.json +2 -2
package/index.d.ts CHANGED
@@ -20,6 +20,8 @@ export interface TokenCalculatorOptions {
20
20
  gitingest?: boolean;
21
21
  targetModel?: string;
22
22
  dashboard?: boolean;
23
+ cache?: boolean | 'memory' | 'disk' | CacheManagerOptions | CacheManager;
24
+ cachePath?: string;
23
25
  }
24
26
 
25
27
  /**
@@ -852,6 +854,21 @@ export interface RepoMapMethod {
852
854
  line: number;
853
855
  /** Declaration on one line, secrets redacted unless `redact: false` */
854
856
  signature: string;
857
+ /** Identifiers invoked inside this method body */
858
+ calls?: string[];
859
+ }
860
+
861
+ /**
862
+ * Cross-file symbol call edge, caller file/symbol -> callee file/symbol.
863
+ */
864
+ export interface RepoGraphCallEdge {
865
+ from: string;
866
+ caller: string;
867
+ callerLine: number;
868
+ to: string;
869
+ callee: string;
870
+ calleeLine: number;
871
+ weight: number;
855
872
  }
856
873
 
857
874
  /**
@@ -884,6 +901,7 @@ export interface RepoGraph {
884
901
  root: string;
885
902
  nodes: RepoGraphNode[];
886
903
  edges: RepoGraphEdge[];
904
+ callEdges?: RepoGraphCallEdge[];
887
905
  history: {
888
906
  available: boolean;
889
907
  requested: number;
@@ -896,6 +914,7 @@ export interface RepoGraph {
896
914
  scannedFiles: number;
897
915
  files: number;
898
916
  edges: number;
917
+ callEdges?: number;
899
918
  unresolvedImports: number;
900
919
  skipped: Array<{ path: string; reason: string }>;
901
920
  redactions: Record<string, number>;
@@ -919,12 +938,14 @@ export interface RankWeights {
919
938
  }
920
939
 
921
940
  export interface RankOptions {
922
- /** Files or folders (project-relative or absolute) to personalise PageRank toward */
941
+ /** Files, folders or file:symbol targets to personalise PageRank toward */
923
942
  focus?: string[];
924
943
  /** Blend of the normalised components (default 0.70 / 0.25 / 0.05) */
925
944
  weights?: RankWeights;
926
945
  /** PageRank damping (default 0.85) */
927
946
  damping?: number;
947
+ /** Include cross-file AST symbol call edges in PageRank and focus expansion */
948
+ callGraph?: boolean;
928
949
  }
929
950
 
930
951
  export interface RankedFile {
@@ -953,6 +974,8 @@ export interface RepoMapOptions extends RepoGraphOptions, RankOptions {
953
974
  style?: 'text' | 'markdown' | 'json' | 'dot';
954
975
  /** Render the dependency edges instead of the map */
955
976
  graph?: boolean;
977
+ /** Annotate methods with cross-file call targets and include call edges in graph/ranking */
978
+ callGraph?: boolean;
956
979
  }
957
980
 
958
981
  export interface RepoMapFile {
@@ -987,6 +1010,15 @@ export interface RepoMapResult {
987
1010
  */
988
1011
  export function buildRepoGraph(root: string, options?: RepoGraphOptions): RepoGraph;
989
1012
 
1013
+ /**
1014
+ * Build cross-file AST symbol call edges across mapped nodes and import edges
1015
+ */
1016
+ export function buildCallGraph(
1017
+ nodes: RepoGraphNode[],
1018
+ edges: RepoGraphEdge[],
1019
+ contentsByPath?: Map<string, string>
1020
+ ): RepoGraphCallEdge[];
1021
+
990
1022
  /**
991
1023
  * Rank the files of a graph, best first: PageRank blended with git change frequency and
992
1024
  * symbol references
@@ -1035,6 +1067,8 @@ export interface PrContextOptions {
1035
1067
  maxFileBytes?: number;
1036
1068
  /** Most related tests to list (default: 50) */
1037
1069
  maxRelatedTests?: number;
1070
+ /** Additional repositories for multi-repo PR review context */
1071
+ repos?: WorkspaceRepoInput[];
1038
1072
  }
1039
1073
 
1040
1074
  export interface PrRevision {
@@ -1072,6 +1106,7 @@ export interface PrChangedMethod extends PrMethod {
1072
1106
  }
1073
1107
 
1074
1108
  export interface PrCommit {
1109
+ repo?: string;
1075
1110
  sha: string;
1076
1111
  short: string;
1077
1112
  author: string;
@@ -1088,6 +1123,8 @@ export interface PrRelatedTest {
1088
1123
  }
1089
1124
 
1090
1125
  export interface PrContextResult {
1126
+ /** Participating repositories when `repos` was supplied */
1127
+ repos?: Array<{ alias: string; root: string }>;
1091
1128
  /** The rendered context */
1092
1129
  output: string;
1093
1130
  /** Tokens of `output` */
@@ -1153,6 +1190,20 @@ export interface ExplainHistoryOptions {
1153
1190
  rev?: string;
1154
1191
  /** Include each commit's diff of the target */
1155
1192
  patches?: boolean;
1193
+ /** Fetch referenced GitHub/GitLab issue, PR and MR discussions (default false) */
1194
+ discussions?: boolean;
1195
+ /** Maximum remote discussions fetched (default 10) */
1196
+ maxDiscussions?: number;
1197
+ /** Maximum comments fetched per discussion (default 5) */
1198
+ maxComments?: number;
1199
+ /** Per-request timeout in milliseconds (default 5000) */
1200
+ discussionTimeoutMs?: number;
1201
+ /** Explicit GitHub/GitLab API token override */
1202
+ token?: string;
1203
+ /** Custom fetch implementation */
1204
+ fetch?: typeof globalThis.fetch;
1205
+ /** Environment override for token lookup */
1206
+ env?: NodeJS.ProcessEnv;
1156
1207
  /** Commits of the file examined for co-changes (default 500, 0 = off) */
1157
1208
  couplingWindow?: number;
1158
1209
  /** Co-changed files listed (default 10) */
@@ -1172,6 +1223,8 @@ export interface ExplainHistoryOptions {
1172
1223
  redact?: boolean;
1173
1224
  /** Directory relative target paths start from (default: root) */
1174
1225
  cwd?: string;
1226
+ /** Additional repositories for multi-repo history & cross-repo ADR/doc/test lookup */
1227
+ repos?: WorkspaceRepoInput[];
1175
1228
  }
1176
1229
 
1177
1230
  export interface WhyCommit {
@@ -1204,6 +1257,7 @@ export interface WhyIssueReference {
1204
1257
  }
1205
1258
 
1206
1259
  export interface ExplainHistoryData {
1260
+ repos?: Array<{ alias: string; root: string }>;
1207
1261
  target: {
1208
1262
  path: string;
1209
1263
  symbol: string | null;
@@ -1240,6 +1294,26 @@ export interface ExplainHistoryData {
1240
1294
  coAuthors: Array<{ value: string; commits: string[] }>;
1241
1295
  reviewers: Array<{ value: string; commits: string[] }>;
1242
1296
  };
1297
+ discussions: Array<{
1298
+ id: string;
1299
+ number: number;
1300
+ repo: string;
1301
+ provider: 'github' | 'gitlab';
1302
+ kind: 'issue' | 'pr' | 'mr';
1303
+ title: string;
1304
+ state: string;
1305
+ author: string;
1306
+ createdAt: string | null;
1307
+ url: string | null;
1308
+ labels: string[];
1309
+ body: string;
1310
+ commits: string[];
1311
+ comments: Array<{
1312
+ author: string;
1313
+ createdAt: string | null;
1314
+ body: string;
1315
+ }>;
1316
+ }>;
1243
1317
  ownership: {
1244
1318
  startLine: number | null;
1245
1319
  endLine: number | null;
@@ -1282,6 +1356,8 @@ export interface ExplainHistoryData {
1282
1356
  }
1283
1357
 
1284
1358
  export interface ExplainHistoryResult {
1359
+ /** Participating repositories when `repos` was supplied */
1360
+ repos?: Array<{ alias: string; root: string }>;
1285
1361
  /** The rendered document */
1286
1362
  text: string;
1287
1363
  style: 'markdown' | 'xml' | 'json';
@@ -1420,3 +1496,857 @@ export interface PackResult {
1420
1496
  * BUDGET_TOO_SMALL, SPLIT_TOO_SMALL).
1421
1497
  */
1422
1498
  export function packContext(root: string, options?: PackOptions): Promise<PackResult>;
1499
+
1500
+ // ============================================
1501
+ // Core v3 Modular Pipeline & Cache
1502
+ // ============================================
1503
+
1504
+ export interface CacheManagerOptions {
1505
+ enabled?: boolean;
1506
+ strategy?: 'disk' | 'memory';
1507
+ ttl?: number;
1508
+ path?: string;
1509
+ maxEntries?: number;
1510
+ maxSize?: number;
1511
+ }
1512
+
1513
+ export interface CacheMeta {
1514
+ size?: number;
1515
+ namespace?: string;
1516
+ encoding?: string;
1517
+ methodLevel?: boolean;
1518
+ extraKey?: string;
1519
+ }
1520
+
1521
+ export class CacheManager {
1522
+ constructor(options?: CacheManagerOptions);
1523
+ options: Required<CacheManagerOptions>;
1524
+ stats: {
1525
+ hits: number;
1526
+ misses: number;
1527
+ writes: number;
1528
+ evictions: number;
1529
+ errors: number;
1530
+ };
1531
+ get(
1532
+ filePath: string,
1533
+ modifiedTime: number | Date | null,
1534
+ meta?: CacheMeta
1535
+ ): Record<string, unknown> | null;
1536
+ set(
1537
+ filePath: string,
1538
+ analysis: Record<string, unknown>,
1539
+ modifiedTime: number | Date | null,
1540
+ meta?: CacheMeta
1541
+ ): void;
1542
+ isExpired(
1543
+ cacheTimestamp: number,
1544
+ cacheModifiedTime: number | null,
1545
+ currentModifiedTime: number | Date | null,
1546
+ cachedSize?: number | null,
1547
+ currentSize?: number | null
1548
+ ): boolean;
1549
+ getCacheKey(filePath: string, meta?: CacheMeta): string;
1550
+ clear(): void;
1551
+ prune(): void;
1552
+ getStats(): {
1553
+ hits: number;
1554
+ misses: number;
1555
+ writes: number;
1556
+ evictions: number;
1557
+ errors: number;
1558
+ totalRequests: number;
1559
+ hitRate: number;
1560
+ cacheSize: number;
1561
+ };
1562
+ }
1563
+
1564
+ export function resolveCacheManager(
1565
+ cacheOption: boolean | 'memory' | 'disk' | CacheManagerOptions | CacheManager | null | undefined,
1566
+ defaults?: CacheManagerOptions
1567
+ ): CacheManager | null;
1568
+
1569
+ export interface ScannedFile {
1570
+ path: string;
1571
+ relativePath: string;
1572
+ name: string;
1573
+ extension: string;
1574
+ size: number;
1575
+ modified: Date;
1576
+ created: Date;
1577
+ }
1578
+
1579
+ export interface ScannerOptions {
1580
+ respectGitignore?: boolean;
1581
+ followSymlinks?: boolean;
1582
+ maxDepth?: number;
1583
+ policy?: Policy | false | null;
1584
+ respectIgnoreFiles?: string | string[];
1585
+ env?: NodeJS.ProcessEnv;
1586
+ }
1587
+
1588
+ export class Scanner {
1589
+ constructor(rootPath: string, options?: ScannerOptions);
1590
+ rootPath: string;
1591
+ scan(): ScannedFile[];
1592
+ isDenied(relativePath: string): boolean;
1593
+ shouldIgnore(relativePath: string, fullPath?: string): boolean;
1594
+ getPolicy(): Policy | null;
1595
+ getStats(): {
1596
+ filesScanned: number;
1597
+ directoriesTraversed: number;
1598
+ filesIgnored: number;
1599
+ filesDenied: number;
1600
+ errors: number;
1601
+ };
1602
+ reset(): void;
1603
+ }
1604
+
1605
+ export interface AnalyzerOptions {
1606
+ methodLevel?: boolean;
1607
+ parallel?: boolean;
1608
+ maxWorkers?: number;
1609
+ targetModel?: string | null;
1610
+ encoding?: string;
1611
+ cache?: boolean | 'memory' | 'disk' | CacheManagerOptions | CacheManager;
1612
+ cachePath?: string;
1613
+ }
1614
+
1615
+ export interface AnalyzedFile {
1616
+ path: string;
1617
+ relativePath: string;
1618
+ name: string;
1619
+ extension: string;
1620
+ size: number;
1621
+ modified: Date | number;
1622
+ tokens: number;
1623
+ lines: number;
1624
+ language: string;
1625
+ methods?: MethodInfo[];
1626
+ methodCount?: number;
1627
+ }
1628
+
1629
+ export interface AnalyzerStats {
1630
+ totalFiles: number;
1631
+ totalTokens: number;
1632
+ totalSize: number;
1633
+ totalMethods: number;
1634
+ totalLines: number;
1635
+ byLanguage: Record<string, { files: number; tokens: number; size: number }>;
1636
+ largestFiles: Array<{ path: string; tokens: number; size: number }>;
1637
+ analysisTime: number;
1638
+ cacheHits?: number;
1639
+ cacheMisses?: number;
1640
+ }
1641
+
1642
+ export class Analyzer {
1643
+ constructor(options?: AnalyzerOptions);
1644
+ analyze(files: ScannedFile[]): Promise<{ files: AnalyzedFile[]; stats: AnalyzerStats }>;
1645
+ analyzeFile(fileInfo: ScannedFile): Promise<AnalyzedFile | null>;
1646
+ getStats(): AnalyzerStats;
1647
+ reset(): void;
1648
+ getLanguageDistribution(): Array<{
1649
+ language: string;
1650
+ files: number;
1651
+ tokens: number;
1652
+ size: number;
1653
+ percentage: number;
1654
+ }>;
1655
+ }
1656
+
1657
+ export interface ContextBuilderOptions {
1658
+ targetModel?: string | null;
1659
+ targetTokens?: number | null;
1660
+ useCase?: string;
1661
+ includeTests?: boolean;
1662
+ includeDocumentation?: boolean;
1663
+ methodLevel?: boolean;
1664
+ priorityStrategy?: 'balanced' | 'changed-first' | 'core-first';
1665
+ }
1666
+
1667
+ export class ContextBuilder {
1668
+ constructor(options?: ContextBuilderOptions);
1669
+ build(analysisResult: { files: AnalyzedFile[]; stats: AnalyzerStats }): Record<string, unknown>;
1670
+ getSummary(context: Record<string, unknown>): string;
1671
+ }
1672
+
1673
+ export interface ReporterOptions {
1674
+ format?: string;
1675
+ outputPath?: string | null;
1676
+ clipboard?: boolean;
1677
+ verbose?: boolean;
1678
+ }
1679
+
1680
+ export class Reporter {
1681
+ constructor(options?: ReporterOptions);
1682
+ report(context: Record<string, unknown>, stats: AnalyzerStats): Promise<void>;
1683
+ exportToFile(context: Record<string, unknown>, outputPath: string): Promise<void>;
1684
+ exportToClipboard(context: Record<string, unknown>): Promise<void>;
1685
+ generateSummary(context: Record<string, unknown>): string;
1686
+ }
1687
+
1688
+ // ============================================
1689
+ // Search, Symbol Lookup, Token Counting, RAG & Policy
1690
+ // ============================================
1691
+
1692
+ export interface SearchProjectOptions {
1693
+ limit?: number;
1694
+ maxTokens?: number;
1695
+ redact?: boolean;
1696
+ windowLines?: number;
1697
+ }
1698
+
1699
+ export interface SearchHit {
1700
+ file: string;
1701
+ name: string | null;
1702
+ kind: string;
1703
+ parent?: string;
1704
+ startLine: number;
1705
+ endLine: number;
1706
+ score: number;
1707
+ tokens: number;
1708
+ text: string | null;
1709
+ truncated?: boolean;
1710
+ }
1711
+
1712
+ export interface SearchProjectResult {
1713
+ query: string;
1714
+ root: string;
1715
+ files: number;
1716
+ chunks: number;
1717
+ tokens: number;
1718
+ results: SearchHit[];
1719
+ }
1720
+
1721
+ export function searchProject(
1722
+ root: string,
1723
+ query: string,
1724
+ options?: SearchProjectOptions
1725
+ ): SearchProjectResult;
1726
+
1727
+ export interface GetSymbolOptions {
1728
+ file?: string;
1729
+ limit?: number;
1730
+ maxTokens?: number;
1731
+ redact?: boolean;
1732
+ repos?: WorkspaceRepoInput[];
1733
+ }
1734
+
1735
+ export interface SymbolMatch {
1736
+ name: string;
1737
+ kind: string;
1738
+ parent?: string;
1739
+ repo?: string;
1740
+ file: string;
1741
+ startLine: number;
1742
+ endLine: number;
1743
+ tokens: number;
1744
+ source: string;
1745
+ truncated?: boolean;
1746
+ }
1747
+
1748
+ export interface GetSymbolResult {
1749
+ query: string;
1750
+ match: 'exact' | 'case-insensitive' | 'none';
1751
+ total: number;
1752
+ repos?: Array<{ alias: string; root: string }>;
1753
+ symbols: SymbolMatch[];
1754
+ suggestions?: Array<{
1755
+ name: string;
1756
+ kind: string;
1757
+ parent?: string;
1758
+ file: string;
1759
+ startLine: number;
1760
+ }>;
1761
+ }
1762
+
1763
+ export function getSymbol(root: string, query: string, options?: GetSymbolOptions): GetSymbolResult;
1764
+
1765
+ export interface CountForModelOptions {
1766
+ model?: string;
1767
+ text?: string;
1768
+ api?: boolean;
1769
+ client?: unknown;
1770
+ onProgress?: (done: number, total: number) => void;
1771
+ }
1772
+
1773
+ export interface CountForModelResult {
1774
+ model: string;
1775
+ modelName: string;
1776
+ vendor: string;
1777
+ files: number;
1778
+ encoding: string;
1779
+ localTokens: number;
1780
+ tokens: number;
1781
+ method: 'local' | 'anthropic-api';
1782
+ exact: boolean;
1783
+ contextWindow: number;
1784
+ fits: boolean;
1785
+ percentOfWindow: number;
1786
+ requests?: number;
1787
+ ratio?: number | null;
1788
+ largestFiles: Array<{ path: string; tokens: number }>;
1789
+ }
1790
+
1791
+ export function countForModel(
1792
+ root: string,
1793
+ options?: CountForModelOptions
1794
+ ): Promise<CountForModelResult>;
1795
+
1796
+ export interface AskCodebaseOptions {
1797
+ limit?: number;
1798
+ redact?: boolean;
1799
+ maxChars?: number;
1800
+ repos?: WorkspaceRepoInput[];
1801
+ onProgress?: (event: 'indexing' | 'indexed') => void;
1802
+ createStore?: (storePath: string, repo?: ResolvedWorkspaceRepo) => unknown;
1803
+ }
1804
+
1805
+ export interface AskCodebaseResult {
1806
+ query: string;
1807
+ indexed: boolean;
1808
+ repos?: Array<{ alias: string; root: string }>;
1809
+ results: Array<{
1810
+ repo?: string;
1811
+ file: string;
1812
+ score: number | null;
1813
+ text: string;
1814
+ truncated: boolean;
1815
+ }>;
1816
+ removedByPolicy: number;
1817
+ redactions: number;
1818
+ }
1819
+
1820
+ export function askCodebase(
1821
+ root: string,
1822
+ query: string,
1823
+ options?: AskCodebaseOptions
1824
+ ): Promise<AskCodebaseResult>;
1825
+
1826
+ export class Policy {
1827
+ active: boolean;
1828
+ requireRedaction: boolean;
1829
+ maxExportTokens: number | null;
1830
+ allowedModels: string[] | null;
1831
+ ignoreFiles: string[];
1832
+ audit: { enabled: boolean; path: string };
1833
+ sources: Array<{ scope: string; file: string }>;
1834
+ isDenied(relativePath: string): boolean;
1835
+ isAuditLog(relativePath: string): boolean;
1836
+ toJSON(): Record<string, unknown>;
1837
+ }
1838
+
1839
+ export class PolicyConfigError extends Error {
1840
+ file: string;
1841
+ errors: string[];
1842
+ exitCode: number;
1843
+ }
1844
+
1845
+ export class PolicyViolationError extends Error {
1846
+ violations: Array<Record<string, unknown>>;
1847
+ exitCode: number;
1848
+ }
1849
+
1850
+ export function loadPolicy(rootPath: string, options?: { env?: NodeJS.ProcessEnv }): Policy;
1851
+
1852
+ export interface WorkspacePolicySummary {
1853
+ valid: boolean;
1854
+ active: boolean;
1855
+ requireRedaction: boolean;
1856
+ maxExportTokens: number | null;
1857
+ allowedTargetModels: Array<{
1858
+ repo: string;
1859
+ scope: string;
1860
+ file: string;
1861
+ patterns: string[];
1862
+ }> | null;
1863
+ ignoreFiles: string[];
1864
+ repos: Array<Record<string, unknown>>;
1865
+ }
1866
+
1867
+ export function checkWorkspacePolicies(
1868
+ primaryRoot: string,
1869
+ extraRepos?: WorkspaceRepoInput[],
1870
+ options?: { env?: NodeJS.ProcessEnv }
1871
+ ): WorkspacePolicySummary;
1872
+
1873
+ export interface WorkspaceAuditResult {
1874
+ repos: Array<{ alias: string; root: string }>;
1875
+ files: string[];
1876
+ allFiles: string[];
1877
+ missing: string[];
1878
+ total: number;
1879
+ events: Array<Record<string, unknown>>;
1880
+ skippedLines: number;
1881
+ policyErrors: Array<{ repo: string; root: string; message: string }>;
1882
+ }
1883
+
1884
+ export function readWorkspaceAuditLogs(
1885
+ primaryRoot: string,
1886
+ extraRepos?: WorkspaceRepoInput[],
1887
+ options?: { since?: Date | null; limit?: number | null; env?: NodeJS.ProcessEnv }
1888
+ ): WorkspaceAuditResult;
1889
+
1890
+ // ============================================
1891
+ // Focused Neighborhood Context (`ctxman focus`)
1892
+ // ============================================
1893
+
1894
+ export type FocusDirection = 'both' | 'imports' | 'importers' | 'calls' | 'callers';
1895
+
1896
+ export interface FocusOptions extends Omit<PackOptions, 'files'> {
1897
+ /** Target function or method name for symbol-level call graph traversal */
1898
+ symbol?: string;
1899
+ /** Graph traversal hops (default: 1; 0 = target file only) */
1900
+ depth?: number;
1901
+ /** Traversal direction (default: 'both') */
1902
+ direction?: FocusDirection;
1903
+ /** Include outgoing imports (default: true) */
1904
+ imports?: boolean;
1905
+ /** Include incoming importers (default: true) */
1906
+ importers?: boolean;
1907
+ /** Include cross-file AST symbol call graph edges (default: true) */
1908
+ callGraph?: boolean;
1909
+ /** Include outgoing cross-file calls (default: true) */
1910
+ calls?: boolean;
1911
+ /** Include incoming cross-file callers (default: true) */
1912
+ callers?: boolean;
1913
+ /** Include test files in neighborhood (default: true) */
1914
+ includeTests?: boolean;
1915
+ /** Git commits mined for tie-breaking PageRank (default: 0) */
1916
+ history?: number;
1917
+ /** Explicit rank order override */
1918
+ rankOrder?: string[];
1919
+ /** Additional repositories for multi-repo workspace focus */
1920
+ repos?: WorkspaceRepoInput[];
1921
+ }
1922
+
1923
+ export interface FocusNeighbor {
1924
+ path: string;
1925
+ distance: number;
1926
+ relation: 'import' | 'importer' | 'call' | 'caller' | 'both';
1927
+ score: number;
1928
+ }
1929
+
1930
+ export interface FocusMetadata {
1931
+ target: string;
1932
+ symbol?: string | null;
1933
+ depth: number;
1934
+ direction: FocusDirection;
1935
+ callGraph?: boolean;
1936
+ inGraph: boolean;
1937
+ neighbors: FocusNeighbor[];
1938
+ callEdges?: RepoGraphCallEdge[];
1939
+ repos?: Array<{ alias: string; root: string }>;
1940
+ }
1941
+
1942
+ export interface FocusResult extends PackResult {
1943
+ focus: FocusMetadata;
1944
+ repos?: Array<{ alias: string; root: string }>;
1945
+ }
1946
+
1947
+ export class FocusError extends Error {
1948
+ code: string;
1949
+ constructor(code: string, message: string);
1950
+ }
1951
+
1952
+ export function focusContext(
1953
+ root: string,
1954
+ target: string,
1955
+ options?: FocusOptions
1956
+ ): Promise<FocusResult>;
1957
+
1958
+ export function describeFocus(result: FocusResult): string;
1959
+
1960
+ // ============================================
1961
+ // Hybrid Search (BM25 + Semantic RRF)
1962
+ // ============================================
1963
+
1964
+ export type HybridSearchMode = 'bm25' | 'semantic' | 'hybrid';
1965
+
1966
+ export interface HybridSearchOptions extends SearchProjectOptions {
1967
+ mode?: HybridSearchMode;
1968
+ rrfK?: number;
1969
+ bm25Weight?: number;
1970
+ semanticWeight?: number;
1971
+ fallbackToBm25?: boolean;
1972
+ onProgress?: (event: 'indexing' | 'indexed') => void;
1973
+ createStore?: (storePath: string) => unknown;
1974
+ }
1975
+
1976
+ export interface HybridSearchHit extends SearchHit {
1977
+ bm25Rank: number | null;
1978
+ semanticRank: number | null;
1979
+ bm25Score: number | null;
1980
+ semanticScore: number | null;
1981
+ }
1982
+
1983
+ export interface HybridSearchResult {
1984
+ query: string;
1985
+ mode: HybridSearchMode;
1986
+ effectiveMode: HybridSearchMode;
1987
+ semanticAvailable: boolean;
1988
+ indexed: boolean;
1989
+ root: string;
1990
+ files: number;
1991
+ chunks: number;
1992
+ tokens: number;
1993
+ warnings: string[];
1994
+ results: HybridSearchHit[];
1995
+ }
1996
+
1997
+ export function hybridSearch(
1998
+ root: string,
1999
+ query: string,
2000
+ options?: HybridSearchOptions
2001
+ ): Promise<HybridSearchResult>;
2002
+
2003
+ // ============================================
2004
+ // Multi-Repository Workspace (`--repo`)
2005
+ // ============================================
2006
+
2007
+ export type WorkspaceRepoInput = string | { path: string; alias?: string };
2008
+
2009
+ export interface ResolvedWorkspaceRepo {
2010
+ alias: string;
2011
+ root: string;
2012
+ realRoot?: string;
2013
+ policy?: Policy;
2014
+ }
2015
+
2016
+ export function resolveWorkspaceRepos(repos: WorkspaceRepoInput[]): ResolvedWorkspaceRepo[];
2017
+
2018
+ export interface WorkspaceGraph extends RepoGraph {
2019
+ repos: Array<{ alias: string; root: string }>;
2020
+ }
2021
+
2022
+ export function buildWorkspaceGraph(
2023
+ repos: WorkspaceRepoInput[],
2024
+ options?: RepoGraphOptions & { callGraph?: boolean }
2025
+ ): WorkspaceGraph;
2026
+
2027
+ export interface WorkspaceMapResult extends RepoMapResult {
2028
+ repos: Array<{ alias: string; root: string }>;
2029
+ }
2030
+
2031
+ export function mapWorkspace(
2032
+ repos: WorkspaceRepoInput[],
2033
+ options?: RepoMapOptions
2034
+ ): WorkspaceMapResult;
2035
+
2036
+ export interface WorkspaceSearchHit extends SearchHit {
2037
+ repo: string;
2038
+ }
2039
+
2040
+ export interface WorkspaceSearchResult extends Omit<SearchProjectResult, 'results'> {
2041
+ repos: Array<{ alias: string; root: string }>;
2042
+ results: WorkspaceSearchHit[];
2043
+ }
2044
+
2045
+ export function searchWorkspace(
2046
+ repos: WorkspaceRepoInput[],
2047
+ query: string,
2048
+ options?: SearchProjectOptions
2049
+ ): WorkspaceSearchResult;
2050
+
2051
+ export interface WorkspacePackResult extends PackResult {
2052
+ repos: Array<{ alias: string; root: string }>;
2053
+ }
2054
+
2055
+ export function packWorkspace(
2056
+ repos: WorkspaceRepoInput[],
2057
+ options?: Omit<PackOptions, 'files' | 'cwd'>
2058
+ ): Promise<WorkspacePackResult>;
2059
+
2060
+ // ============================================
2061
+ // RAG Vector Stores & Embedding Providers (v3.2.0)
2062
+ // ============================================
2063
+
2064
+ export class LocalHashEmbeddingProvider {
2065
+ dimension: number;
2066
+ constructor(dimension?: number);
2067
+ embed(text: string): Promise<number[]>;
2068
+ }
2069
+
2070
+ export class AutoEmbeddingProvider {
2071
+ modelName?: string;
2072
+ constructor(options?: { modelName?: string });
2073
+ init(): Promise<void>;
2074
+ embed(text: string): Promise<number[]>;
2075
+ }
2076
+
2077
+ export class SqliteVectorStore {
2078
+ storagePath: string;
2079
+ dbPath: string;
2080
+ constructor(embeddingProvider: any, storagePath?: string);
2081
+ init(): Promise<void>;
2082
+ addDocument(
2083
+ text: string,
2084
+ metadata?: Record<string, any>
2085
+ ): Promise<{ upserted: number; skipped: number }>;
2086
+ addDocuments(
2087
+ docs: Array<{ text: string; id?: string; metadata?: Record<string, any> }>
2088
+ ): Promise<{ upserted: number; skipped: number }>;
2089
+ removeDocument(idOrFilePath: string): Promise<number>;
2090
+ upsertGraphNode(node: Record<string, any>): Promise<void>;
2091
+ replaceGraphEdges(
2092
+ fromPath: string,
2093
+ edges?: Array<{ to: string; weight?: number }>
2094
+ ): Promise<void>;
2095
+ removeGraphNode(filePath: string): Promise<{ removedNode: number; removedEdges: number }>;
2096
+ syncGraph(graph: {
2097
+ nodes?: Array<Record<string, any>>;
2098
+ edges?: Array<{ from: string; to: string; weight?: number }>;
2099
+ replaceAll?: boolean;
2100
+ }): Promise<void>;
2101
+ getGraph(): Promise<{
2102
+ nodes: Array<Record<string, any>>;
2103
+ edges: Array<{ from: string; to: string; weight: number }>;
2104
+ }>;
2105
+ graphCounts(): { nodes: number; edges: number };
2106
+ search(
2107
+ queryText: string,
2108
+ limit?: number
2109
+ ): Promise<Array<{ id?: string; text: string; score: number; metadata: Record<string, any> }>>;
2110
+ deleteTable(): Promise<void>;
2111
+ count(): number;
2112
+ close(): void;
2113
+ }
2114
+
2115
+ export interface FileWatcherOptions {
2116
+ debounce?: number;
2117
+ recursive?: boolean;
2118
+ ignorePatterns?: Array<string | RegExp>;
2119
+ repos?: string[];
2120
+ }
2121
+
2122
+ export class FileWatcher {
2123
+ rootPath: string;
2124
+ options: FileWatcherOptions;
2125
+ isWatching: boolean;
2126
+ isMultiRepo: boolean;
2127
+ repos: Array<{ root: string; alias: string | null }>;
2128
+ constructor(rootPath: string, options?: FileWatcherOptions);
2129
+ start(): void;
2130
+ stop(): void;
2131
+ shouldIgnore(relativePath: string, repoRoot?: string): boolean;
2132
+ getStats(): {
2133
+ totalChanges: number;
2134
+ ignoredChanges: number;
2135
+ errors: number;
2136
+ isWatching: boolean;
2137
+ activeWatchers: number;
2138
+ pendingDebounce: number;
2139
+ };
2140
+ resetStats(): void;
2141
+ on(event: string, listener: (...args: any[]) => void): this;
2142
+ }
2143
+
2144
+ export interface IncrementalAnalyzerOptions extends AnalyzerOptions {
2145
+ projectRoot?: string;
2146
+ repos?: string[];
2147
+ sync?: boolean;
2148
+ syncRag?: boolean;
2149
+ syncGraph?: boolean;
2150
+ storePath?: string;
2151
+ vectorStore?: SqliteVectorStore;
2152
+ embeddingProvider?: any;
2153
+ redactSecrets?: boolean;
2154
+ maxFileBytes?: number;
2155
+ hydrateGraphOnStart?: boolean;
2156
+ }
2157
+
2158
+ export class IncrementalAnalyzer {
2159
+ projectRoot: string | null;
2160
+ vectorStore: SqliteVectorStore | null;
2161
+ constructor(options?: IncrementalAnalyzerOptions);
2162
+ isMultiRepo: boolean;
2163
+ repos: Array<{ root: string; alias: string | null }>;
2164
+ isPathDenied(graphPath: string): boolean;
2165
+ ensureSyncReady(rootOverride?: string | null): Promise<boolean>;
2166
+ bootstrapSync(
2167
+ root?: string,
2168
+ options?: { repos?: string[] }
2169
+ ): Promise<{
2170
+ files: number;
2171
+ ragUpserted: number;
2172
+ ragSkipped: number;
2173
+ graphNodes: number;
2174
+ graphEdges: number;
2175
+ repos?: string[];
2176
+ }>;
2177
+ syncFileChange(changeEvent: Record<string, any>): Promise<{
2178
+ rag: Record<string, any> | null;
2179
+ graph: Record<string, any> | null;
2180
+ }>;
2181
+ analyzeChange(changeEvent: Record<string, any>): Promise<void>;
2182
+ handleFileDeleted(changeEvent: Record<string, any>): Promise<void>;
2183
+ getGraph(): Promise<{
2184
+ nodes: Array<Record<string, any>>;
2185
+ edges: Array<{ from: string; to: string; weight: number }>;
2186
+ }>;
2187
+ getStats(): AnalyzerStats;
2188
+ getCachedAnalysis(filePath: string): AnalyzedFile | null;
2189
+ clearCache(): void;
2190
+ close(): void;
2191
+ getCacheStats(): {
2192
+ cachedFiles: number;
2193
+ cacheSize: number;
2194
+ graphNodes: number;
2195
+ graphEdges: number;
2196
+ vectorDocuments: number;
2197
+ };
2198
+ on(event: string, listener: (...args: any[]) => void): this;
2199
+ }
2200
+
2201
+ // ============================================
2202
+ // Task-Driven Auto Context (v3.2.0)
2203
+ // ============================================
2204
+
2205
+ export interface TaskContextOptions {
2206
+ budget?: number | string;
2207
+ format?: 'markdown' | 'json' | 'xml';
2208
+ searchMode?: 'hybrid' | 'bm25' | 'semantic';
2209
+ callGraph?: boolean;
2210
+ redact?: boolean;
2211
+ output?: string;
2212
+ model?: string;
2213
+ limit?: number;
2214
+ repos?: WorkspaceRepoInput[];
2215
+ }
2216
+
2217
+ export interface TaskContextResult {
2218
+ query: string;
2219
+ format: string;
2220
+ budget: number;
2221
+ totalTokens: number;
2222
+ text: string;
2223
+ seeds: Array<{ file: string; score: number; kind?: string; name?: string }>;
2224
+ files: Array<{ path: string; hop: number; compressed: boolean; tokens: number; lines: number }>;
2225
+ callEdges?: RepoGraphCallEdge[];
2226
+ omitted: Array<{ path: string; reason: string }>;
2227
+ redactions: { total: number };
2228
+ repos?: Array<{ alias: string; root: string }>;
2229
+ }
2230
+
2231
+ export function buildTaskContext(
2232
+ root: string,
2233
+ taskQuery: string,
2234
+ options?: TaskContextOptions
2235
+ ): Promise<TaskContextResult>;
2236
+
2237
+ // ============================================
2238
+ // Failure Diagnosis (v3.2.0)
2239
+ // ============================================
2240
+
2241
+ export interface DiagnoseFailureOptions {
2242
+ budget?: number | string;
2243
+ format?: 'markdown' | 'json' | 'xml';
2244
+ callGraph?: boolean;
2245
+ redact?: boolean;
2246
+ windowLines?: number;
2247
+ output?: string;
2248
+ repos?: WorkspaceRepoInput[];
2249
+ }
2250
+
2251
+ export interface DiagnoseFailureResult {
2252
+ frames: Array<any>;
2253
+ implicatedFiles: string[];
2254
+ totalTokens: number;
2255
+ budget: number;
2256
+ text: string;
2257
+ history: Array<{ file: string; text: string; tokens: number }>;
2258
+ neighbors: string[];
2259
+ callEdges?: RepoGraphCallEdge[];
2260
+ redactions: { total: number };
2261
+ repos?: Array<{ alias: string; root: string }>;
2262
+ }
2263
+
2264
+ export function diagnoseFailure(
2265
+ root: string,
2266
+ traceOrLog: string,
2267
+ options?: DiagnoseFailureOptions
2268
+ ): Promise<DiagnoseFailureResult>;
2269
+
2270
+ // ============================================
2271
+ // Institutional Memory Bridge (v3.2.0)
2272
+ // ============================================
2273
+
2274
+ export interface AnalyzerCitation {
2275
+ id: string;
2276
+ kind: string;
2277
+ title: string;
2278
+ snippet: string;
2279
+ url: string | null;
2280
+ author: string | null;
2281
+ timestamp: string | null;
2282
+ refs: string[];
2283
+ score: number;
2284
+ }
2285
+
2286
+ export interface QueryAnalyzerOptions {
2287
+ file?: string;
2288
+ symbol?: string;
2289
+ commits?: string[];
2290
+ limit?: number;
2291
+ redact?: boolean;
2292
+ }
2293
+
2294
+ export function queryAnalyzerCitations(
2295
+ rootOrDbPath: string,
2296
+ options?: QueryAnalyzerOptions
2297
+ ): AnalyzerCitation[];
2298
+
2299
+ // ============================================
2300
+ // Expert Finder ("Who knows about this?")
2301
+ // ============================================
2302
+
2303
+ export class WhoError extends Error {
2304
+ constructor(message: string);
2305
+ }
2306
+
2307
+ export interface FindExpertsOptions {
2308
+ limit?: number;
2309
+ commitWindow?: number;
2310
+ halfLifeDays?: number;
2311
+ since?: string;
2312
+ style?: 'markdown' | 'xml' | 'json';
2313
+ nowMs?: number;
2314
+ repos?: WorkspaceRepoInput[];
2315
+ }
2316
+
2317
+ export interface ExpertEntry {
2318
+ name: string;
2319
+ email: string | null;
2320
+ score: number;
2321
+ blameLines: number;
2322
+ blameShare: number;
2323
+ commits: number;
2324
+ commitShare: number;
2325
+ recencyShare: number;
2326
+ additions: number;
2327
+ deletions: number;
2328
+ lastActive: string | null;
2329
+ isCodeOwner: boolean;
2330
+ codeOwnerHandles: string[];
2331
+ filesTouched: number;
2332
+ recentCommits: Array<{ sha: string; date: string | null; subject: string }>;
2333
+ }
2334
+
2335
+ export interface FindExpertsResult {
2336
+ repos?: Array<{ alias: string; root: string }>;
2337
+ data: {
2338
+ repos?: Array<{ alias: string; root: string }>;
2339
+ target: { query: string; mode: string; label: string; files: string[] };
2340
+ codeOwners: { file: string | null; owners: string[] };
2341
+ totalAuthors: number;
2342
+ experts: ExpertEntry[];
2343
+ };
2344
+ experts: ExpertEntry[];
2345
+ text: string;
2346
+ }
2347
+
2348
+ export function findExperts(
2349
+ projectDir: string,
2350
+ target: string,
2351
+ options?: FindExpertsOptions
2352
+ ): Promise<FindExpertsResult>;