ctxman 3.1.1 → 3.2.8

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 (55) hide show
  1. package/.ctxman/llm-profiles.json +6 -6
  2. package/README.md +16 -10
  3. package/bin/cli.js +109 -10
  4. package/bin/cm-ask.js +64 -6
  5. package/bin/cm-audit.js +76 -0
  6. package/bin/cm-count.js +45 -6
  7. package/bin/cm-diagnose.js +199 -0
  8. package/bin/cm-focus.js +322 -0
  9. package/bin/cm-map.js +71 -18
  10. package/bin/cm-pack.js +36 -5
  11. package/bin/cm-policy.js +107 -2
  12. package/bin/cm-pr.js +18 -3
  13. package/bin/cm-search.js +84 -19
  14. package/bin/cm-task.js +192 -0
  15. package/bin/cm-who.js +119 -0
  16. package/bin/cm-why.js +41 -5
  17. package/index.d.ts +940 -1
  18. package/index.js +105 -1
  19. package/lib/analyzers/token-calculator.js +76 -4
  20. package/lib/api/mcp/server.js +843 -29
  21. package/lib/cache/CacheManager.js +161 -47
  22. package/lib/core/Analyzer.js +61 -5
  23. package/lib/count/index.js +74 -14
  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/policy/audit.js +3 -1
  34. package/lib/policy/index.js +5 -0
  35. package/lib/policy/workspace.js +254 -0
  36. package/lib/pr/pr-context.js +292 -31
  37. package/lib/pr/pr-renderer.js +25 -10
  38. package/lib/rag/EmbeddingProvider.js +139 -2
  39. package/lib/rag/VectorStoreFactory.js +10 -4
  40. package/lib/rag/ask.js +186 -17
  41. package/lib/rag/stores/SqliteVectorStore.js +595 -0
  42. package/lib/search/hybrid.js +324 -0
  43. package/lib/search/symbols.js +75 -11
  44. package/lib/task/index.js +630 -0
  45. package/lib/utils/file-utils.js +1 -0
  46. package/lib/utils/multi-repo-manager.js +4 -1
  47. package/lib/watch/FileWatcher.js +121 -31
  48. package/lib/watch/IncrementalAnalyzer.js +964 -16
  49. package/lib/who/index.js +893 -0
  50. package/lib/why/analyzer-bridge.js +223 -0
  51. package/lib/why/discussions.js +314 -0
  52. package/lib/why/index.js +356 -30
  53. package/lib/why/render.js +154 -9
  54. package/lib/workspace/index.js +1274 -0
  55. package/package.json +1 -1
package/index.js CHANGED
@@ -7,6 +7,17 @@
7
7
  import TokenCalculator from './lib/analyzers/token-calculator.js';
8
8
  import MethodAnalyzer from './lib/analyzers/method-analyzer.js';
9
9
 
10
+ // Core v3 modular pipeline & cache
11
+ import { Scanner } from './lib/core/Scanner.js';
12
+ import { Analyzer } from './lib/core/Analyzer.js';
13
+ import { ContextBuilder } from './lib/core/ContextBuilder.js';
14
+ import { Reporter } from './lib/core/Reporter.js';
15
+ import { CacheManager, resolveCacheManager } from './lib/cache/CacheManager.js';
16
+
17
+ // Watch & incremental RAG/graph sync
18
+ import { FileWatcher } from './lib/watch/FileWatcher.js';
19
+ import { IncrementalAnalyzer } from './lib/watch/IncrementalAnalyzer.js';
20
+
10
21
  // Parsers
11
22
  import GitIgnoreParser from './lib/parsers/gitignore-parser.js';
12
23
  import MethodFilterParser from './lib/parsers/method-filter-parser.js';
@@ -34,18 +45,70 @@ import { buildPrContext } from './lib/pr/pr-context.js';
34
45
  import { generateDigestFromReport, generateDigestFromContext } from './ctxman.js';
35
46
 
36
47
  // Repository map (`ctxman map`)
37
- import { buildRepoGraph, rankFiles, renderRepoMap } from './lib/map/index.js';
48
+ import { buildRepoGraph, rankFiles, renderRepoMap, buildCallGraph } from './lib/map/index.js';
38
49
 
39
50
  // Code history explanation (`ctxman why`)
40
51
  import { explainHistory } from './lib/why/index.js';
52
+ import { findExperts, WhoError } from './lib/who/index.js';
53
+
41
54
  // File contents in one document (`ctxman pack`)
42
55
  import { packContext } from './lib/pack/index.js';
43
56
 
57
+ // Import/importer neighborhood context (`ctxman focus`)
58
+ import { focusContext, describeFocus, FocusError } from './lib/focus/index.js';
59
+
60
+ // Multi-repository workspace (`--repo`)
61
+ import {
62
+ resolveWorkspaceRepos,
63
+ buildWorkspaceGraph,
64
+ mapWorkspace,
65
+ searchWorkspace,
66
+ packWorkspace,
67
+ } from './lib/workspace/index.js';
68
+
69
+ // RAG Stores & Embedding Providers (v3.2.0)
70
+ import { SqliteVectorStore } from './lib/rag/stores/SqliteVectorStore.js';
71
+ import { LocalHashEmbeddingProvider, AutoEmbeddingProvider } from './lib/rag/EmbeddingProvider.js';
72
+
73
+ // Task-Driven Auto Context & Failure Diagnosis (v3.2.0)
74
+ import { buildTaskContext } from './lib/task/index.js';
75
+ import { diagnoseFailure } from './lib/diagnose/index.js';
76
+
77
+ // Institutional Memory Bridge (v3.2.0)
78
+ import { queryAnalyzerCitations } from './lib/why/analyzer-bridge.js';
79
+
80
+ // Search, symbol lookup, token counting, RAG & export policy
81
+ import { searchProject } from './lib/search/bm25.js';
82
+ import { hybridSearch } from './lib/search/hybrid.js';
83
+ import { getSymbol } from './lib/search/symbols.js';
84
+ import { countForModel } from './lib/count/index.js';
85
+ import { askCodebase } from './lib/rag/ask.js';
86
+ import {
87
+ loadPolicy,
88
+ Policy,
89
+ PolicyConfigError,
90
+ PolicyViolationError,
91
+ checkWorkspacePolicies,
92
+ readWorkspaceAuditLogs,
93
+ } from './lib/policy/index.js';
94
+
44
95
  export {
45
96
  // Analyzers
46
97
  TokenCalculator,
47
98
  MethodAnalyzer,
48
99
 
100
+ // Core v3 pipeline & cache
101
+ Scanner,
102
+ Analyzer,
103
+ ContextBuilder,
104
+ Reporter,
105
+ CacheManager,
106
+ resolveCacheManager,
107
+
108
+ // Watch & incremental RAG/graph sync
109
+ FileWatcher,
110
+ IncrementalAnalyzer,
111
+
49
112
  // Parsers
50
113
  GitIgnoreParser,
51
114
  MethodFilterParser,
@@ -78,14 +141,55 @@ export {
78
141
  buildRepoGraph,
79
142
  rankFiles,
80
143
  renderRepoMap,
144
+ buildCallGraph,
81
145
 
82
146
  // Pull-request context
83
147
  buildPrContext,
84
148
 
85
149
  // Code history explanation (`ctxman why`)
86
150
  explainHistory,
151
+ findExperts,
152
+ WhoError,
153
+
87
154
  // Packed file contents
88
155
  packContext,
156
+
157
+ // Focused neighborhood context (`ctxman focus`)
158
+ focusContext,
159
+ describeFocus,
160
+ FocusError,
161
+
162
+ // Multi-repository workspace
163
+ resolveWorkspaceRepos,
164
+ buildWorkspaceGraph,
165
+ mapWorkspace,
166
+ searchWorkspace,
167
+ packWorkspace,
168
+
169
+ // RAG Stores & Embedding Providers (v3.2.0)
170
+ SqliteVectorStore,
171
+ LocalHashEmbeddingProvider,
172
+ AutoEmbeddingProvider,
173
+
174
+ // Task-Driven Auto Context & Failure Diagnosis (v3.2.0)
175
+ buildTaskContext,
176
+ diagnoseFailure,
177
+
178
+ // Institutional Memory Bridge (v3.2.0)
179
+ queryAnalyzerCitations,
180
+
181
+ // Search, symbols, counting, RAG & policy
182
+ searchProject,
183
+ hybridSearch,
184
+ getSymbol,
185
+ countForModel,
186
+ askCodebase,
187
+ loadPolicy,
188
+ Policy,
189
+ PolicyConfigError,
190
+ PolicyViolationError,
191
+ checkWorkspacePolicies,
192
+ readWorkspaceAuditLogs,
89
193
  };
90
194
 
91
195
  // Alias for backward compatibility
@@ -8,6 +8,7 @@ import ConfigUtils from '../utils/config-utils.js';
8
8
  import MethodAnalyzer from './method-analyzer.js';
9
9
  import GitIngestFormatter from '../formatters/gitingest-formatter.js';
10
10
  import FormatRegistry from '../formatters/format-registry.js';
11
+ import { resolveCacheManager } from '../cache/CacheManager.js';
11
12
  import { formatRedactionSummary, redactSecrets } from '../utils/secret-redactor.js';
12
13
  import { LLMDetector } from '../utils/llm-detector.js';
13
14
  import { POLICY_FILE } from '../policy/policy.js';
@@ -23,19 +24,30 @@ const OWN_OUTPUT_PATHS = [
23
24
  /^digest(-chunk-\d+-of-\d+)?\.txt$/,
24
25
  /^(ai-suggestions-report|dependency-context|restored-context)\.json$/,
25
26
  /^performance-report\.txt$/,
27
+ /^\.context-cache(?:[/\\]|$)/,
26
28
  /^\.ctxman[/\\]audit\.log$/,
27
- /^\.ctxman[/\\](versions|snapshots|metrics|logs|temp|self-knowledge|rag-store)$/,
29
+ /^\.ctxman[/\\](versions|snapshots|metrics|logs|temp|self-knowledge|rag-store|cache)(?:[/\\]|$)/,
28
30
  ];
29
31
 
30
32
  class TokenCalculator {
31
33
  constructor(projectRoot, options = {}) {
32
34
  this.projectRoot = projectRoot;
33
- this.options = { verbose: false, compactContext: true, methodLevel: false, ...options };
35
+ this.options = {
36
+ verbose: false,
37
+ compactContext: true,
38
+ methodLevel: false,
39
+ cache: false,
40
+ ...options,
41
+ };
42
+ this.cache = resolveCacheManager(this.options.cache, {
43
+ path: this.options.cachePath || path.join(projectRoot, '.ctxman', 'cache'),
44
+ });
34
45
  this.stats = this.initStats();
35
46
  this.gitIgnore = this.initGitIgnore();
36
47
  this.patternFilters = this.initPatternFilters();
37
48
  this.methodAnalyzer = new MethodAnalyzer();
38
49
  this.methodFilter = this.options.methodLevel ? this.initMethodFilter() : null;
50
+ this.methodFilterSignature = this.buildMethodFilterSignature();
39
51
  this.methodStats = { totalMethods: 0, includedMethods: 0, methodTokens: {} };
40
52
  // o200k_base for OpenAI models since GPT-4o, cl100k_base otherwise
41
53
  this.encoding = TokenUtils.encodingFor(this.options.targetModel);
@@ -67,8 +79,15 @@ class TokenCalculator {
67
79
  return ConfigUtils.initMethodFilter(this.projectRoot);
68
80
  }
69
81
 
82
+ buildMethodFilterSignature() {
83
+ if (!this.methodFilter) return '';
84
+ const inc = (this.methodFilter.includePatterns || []).map((p) => p.pattern).join(',');
85
+ const ign = (this.methodFilter.ignorePatterns || []).map((p) => p.pattern).join(',');
86
+ return `${this.methodFilter.hasIncludeFile ? '1' : '0'}:${inc}|${ign}`;
87
+ }
88
+
70
89
  initStats() {
71
- return {
90
+ const base = {
72
91
  totalFiles: 0,
73
92
  totalTokens: 0,
74
93
  totalBytes: 0,
@@ -79,6 +98,11 @@ class TokenCalculator {
79
98
  byDirectory: {},
80
99
  largestFiles: [],
81
100
  };
101
+ if (this.cache) {
102
+ base.cacheHits = 0;
103
+ base.cacheMisses = 0;
104
+ }
105
+ return base;
82
106
  }
83
107
 
84
108
  initGitIgnore() {
@@ -123,8 +147,39 @@ class TokenCalculator {
123
147
 
124
148
  analyzeFile(filePath) {
125
149
  try {
126
- const content = fs.readFileSync(filePath, 'utf8');
127
150
  const stats = fs.statSync(filePath);
151
+ const cacheMeta = {
152
+ namespace: 'token-calculator',
153
+ size: stats.size,
154
+ encoding: this.encoding,
155
+ methodLevel: Boolean(this.options.methodLevel),
156
+ extraKey: this.methodFilterSignature,
157
+ };
158
+
159
+ if (this.cache) {
160
+ const cached = this.cache.get(filePath, stats.mtimeMs, cacheMeta);
161
+ if (cached) {
162
+ this.stats.cacheHits++;
163
+ const { _rawMethodCount, ...rest } = cached;
164
+ const fileInfo = {
165
+ ...rest,
166
+ path: filePath,
167
+ relativePath: path.relative(this.projectRoot, filePath),
168
+ };
169
+ if (this.options.methodLevel && Array.isArray(fileInfo.methods)) {
170
+ const fileName = path.basename(filePath, path.extname(filePath));
171
+ this.methodStats.totalMethods += _rawMethodCount ?? fileInfo.methods.length;
172
+ this.methodStats.includedMethods += fileInfo.methods.length;
173
+ for (const method of fileInfo.methods) {
174
+ this.methodStats.methodTokens[`${fileName}.${method.name}`] = method.tokens;
175
+ }
176
+ }
177
+ return fileInfo;
178
+ }
179
+ this.stats.cacheMisses++;
180
+ }
181
+
182
+ const content = fs.readFileSync(filePath, 'utf8');
128
183
 
129
184
  const fileInfo = {
130
185
  path: filePath,
@@ -136,8 +191,20 @@ class TokenCalculator {
136
191
  };
137
192
 
138
193
  // Method-level analysis if enabled
194
+ let rawMethodCount = 0;
139
195
  if (this.options.methodLevel && this.isCodeFile(filePath)) {
196
+ const beforeTotal = this.methodStats.totalMethods;
140
197
  fileInfo.methods = this.analyzeFileMethods(content, filePath);
198
+ rawMethodCount = this.methodStats.totalMethods - beforeTotal;
199
+ }
200
+
201
+ if (this.cache) {
202
+ this.cache.set(
203
+ filePath,
204
+ this.options.methodLevel ? { ...fileInfo, _rawMethodCount: rawMethodCount } : fileInfo,
205
+ stats.mtimeMs,
206
+ cacheMeta
207
+ );
141
208
  }
142
209
 
143
210
  return fileInfo;
@@ -709,6 +776,11 @@ class TokenCalculator {
709
776
  `📋 Files ignored by context rules: ${this.stats.calculatorIgnoredFiles.toLocaleString()}`
710
777
  );
711
778
  }
779
+ if (this.stats.cacheHits !== undefined) {
780
+ const total = this.stats.cacheHits + this.stats.cacheMisses;
781
+ const rate = total > 0 ? ((this.stats.cacheHits / total) * 100).toFixed(1) : '0.0';
782
+ console.log(`⚡ Cache hits: ${this.stats.cacheHits}/${total} (${rate}%)`);
783
+ }
712
784
  console.log();
713
785
 
714
786
  this.printExtensionStats();