@git.zone/tsdoc 1.5.1 → 1.6.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.
@@ -2,14 +2,27 @@ import { EnhancedContext } from './enhanced-context.js';
2
2
  import { TaskContextFactory } from './task-context-factory.js';
3
3
  import { ConfigManager } from './config-manager.js';
4
4
  import { ContextTrimmer } from './context-trimmer.js';
5
- import type {
6
- ContextMode,
7
- IContextConfig,
5
+ import { LazyFileLoader } from './lazy-file-loader.js';
6
+ import { ContextCache } from './context-cache.js';
7
+ import { ContextAnalyzer } from './context-analyzer.js';
8
+ import type {
9
+ ContextMode,
10
+ IContextConfig,
8
11
  IContextResult,
9
12
  IFileInfo,
10
13
  ITrimConfig,
11
14
  ITaskConfig,
12
- TaskType
15
+ TaskType,
16
+ ICacheConfig,
17
+ IAnalyzerConfig,
18
+ IPrioritizationWeights,
19
+ ITierConfig,
20
+ ITierSettings,
21
+ IFileMetadata,
22
+ ICacheEntry,
23
+ IFileDependencies,
24
+ IFileAnalysis,
25
+ IAnalysisResult
13
26
  } from './types.js';
14
27
 
15
28
  export {
@@ -18,6 +31,9 @@ export {
18
31
  TaskContextFactory,
19
32
  ConfigManager,
20
33
  ContextTrimmer,
34
+ LazyFileLoader,
35
+ ContextCache,
36
+ ContextAnalyzer,
21
37
  };
22
38
 
23
39
  // Types
@@ -28,5 +44,15 @@ export type {
28
44
  IFileInfo,
29
45
  ITrimConfig,
30
46
  ITaskConfig,
31
- TaskType
47
+ TaskType,
48
+ ICacheConfig,
49
+ IAnalyzerConfig,
50
+ IPrioritizationWeights,
51
+ ITierConfig,
52
+ ITierSettings,
53
+ IFileMetadata,
54
+ ICacheEntry,
55
+ IFileDependencies,
56
+ IFileAnalysis,
57
+ IAnalysisResult
32
58
  };
@@ -0,0 +1,191 @@
1
+ import * as plugins from '../plugins.js';
2
+ import * as fs from 'fs';
3
+ import type { IFileMetadata, IFileInfo } from './types.js';
4
+
5
+ /**
6
+ * LazyFileLoader handles efficient file loading by:
7
+ * - Scanning files for metadata without loading contents
8
+ * - Providing fast file size and token estimates
9
+ * - Loading contents only when requested
10
+ * - Parallel loading of selected files
11
+ */
12
+ export class LazyFileLoader {
13
+ private projectRoot: string;
14
+ private metadataCache: Map<string, IFileMetadata> = new Map();
15
+
16
+ /**
17
+ * Creates a new LazyFileLoader
18
+ * @param projectRoot - Root directory of the project
19
+ */
20
+ constructor(projectRoot: string) {
21
+ this.projectRoot = projectRoot;
22
+ }
23
+
24
+ /**
25
+ * Scans files in given globs and creates metadata without loading contents
26
+ * @param globs - File patterns to scan (e.g., ['ts/**\/*.ts', 'test/**\/*.ts'])
27
+ * @returns Array of file metadata
28
+ */
29
+ public async scanFiles(globs: string[]): Promise<IFileMetadata[]> {
30
+ const metadata: IFileMetadata[] = [];
31
+
32
+ for (const globPattern of globs) {
33
+ try {
34
+ const smartFiles = await plugins.smartfile.fs.fileTreeToObject(this.projectRoot, globPattern);
35
+ const fileArray = Array.isArray(smartFiles) ? smartFiles : [smartFiles];
36
+
37
+ for (const smartFile of fileArray) {
38
+ try {
39
+ const meta = await this.getMetadata(smartFile.path);
40
+ metadata.push(meta);
41
+ } catch (error) {
42
+ // Skip files that can't be read
43
+ console.warn(`Failed to get metadata for ${smartFile.path}:`, error.message);
44
+ }
45
+ }
46
+ } catch (error) {
47
+ // Skip patterns that don't match any files
48
+ console.warn(`No files found for pattern ${globPattern}`);
49
+ }
50
+ }
51
+
52
+ return metadata;
53
+ }
54
+
55
+ /**
56
+ * Gets metadata for a single file without loading contents
57
+ * @param filePath - Absolute path to the file
58
+ * @returns File metadata
59
+ */
60
+ public async getMetadata(filePath: string): Promise<IFileMetadata> {
61
+ // Check cache first
62
+ if (this.metadataCache.has(filePath)) {
63
+ const cached = this.metadataCache.get(filePath)!;
64
+ const currentStats = await fs.promises.stat(filePath);
65
+
66
+ // Return cached if file hasn't changed
67
+ if (cached.mtime === Math.floor(currentStats.mtimeMs)) {
68
+ return cached;
69
+ }
70
+ }
71
+
72
+ // Get file stats
73
+ const stats = await fs.promises.stat(filePath);
74
+ const relativePath = plugins.path.relative(this.projectRoot, filePath);
75
+
76
+ // Estimate tokens: rough estimate of ~4 characters per token
77
+ // This is faster than reading and tokenizing the entire file
78
+ const estimatedTokens = Math.ceil(stats.size / 4);
79
+
80
+ const metadata: IFileMetadata = {
81
+ path: filePath,
82
+ relativePath,
83
+ size: stats.size,
84
+ mtime: Math.floor(stats.mtimeMs),
85
+ estimatedTokens,
86
+ };
87
+
88
+ // Cache the metadata
89
+ this.metadataCache.set(filePath, metadata);
90
+
91
+ return metadata;
92
+ }
93
+
94
+ /**
95
+ * Loads file contents for selected files in parallel
96
+ * @param metadata - Array of file metadata to load
97
+ * @param tokenizer - Function to calculate accurate token count
98
+ * @returns Array of complete file info with contents
99
+ */
100
+ public async loadFiles(
101
+ metadata: IFileMetadata[],
102
+ tokenizer: (content: string) => number
103
+ ): Promise<IFileInfo[]> {
104
+ // Load files in parallel
105
+ const loadPromises = metadata.map(async (meta) => {
106
+ try {
107
+ const contents = await plugins.smartfile.fs.toStringSync(meta.path);
108
+ const tokenCount = tokenizer(contents);
109
+
110
+ const fileInfo: IFileInfo = {
111
+ path: meta.path,
112
+ relativePath: meta.relativePath,
113
+ contents,
114
+ tokenCount,
115
+ importanceScore: meta.importanceScore,
116
+ };
117
+
118
+ return fileInfo;
119
+ } catch (error) {
120
+ console.warn(`Failed to load file ${meta.path}:`, error.message);
121
+ return null;
122
+ }
123
+ });
124
+
125
+ // Wait for all loads to complete and filter out failures
126
+ const results = await Promise.all(loadPromises);
127
+ return results.filter((r): r is IFileInfo => r !== null);
128
+ }
129
+
130
+ /**
131
+ * Loads a single file with contents
132
+ * @param filePath - Absolute path to the file
133
+ * @param tokenizer - Function to calculate accurate token count
134
+ * @returns Complete file info with contents
135
+ */
136
+ public async loadFile(
137
+ filePath: string,
138
+ tokenizer: (content: string) => number
139
+ ): Promise<IFileInfo> {
140
+ const meta = await this.getMetadata(filePath);
141
+ const contents = await plugins.smartfile.fs.toStringSync(filePath);
142
+ const tokenCount = tokenizer(contents);
143
+ const relativePath = plugins.path.relative(this.projectRoot, filePath);
144
+
145
+ return {
146
+ path: filePath,
147
+ relativePath,
148
+ contents,
149
+ tokenCount,
150
+ importanceScore: meta.importanceScore,
151
+ };
152
+ }
153
+
154
+ /**
155
+ * Updates importance scores for metadata entries
156
+ * @param scores - Map of file paths to importance scores
157
+ */
158
+ public updateImportanceScores(scores: Map<string, number>): void {
159
+ for (const [path, score] of scores) {
160
+ const meta = this.metadataCache.get(path);
161
+ if (meta) {
162
+ meta.importanceScore = score;
163
+ }
164
+ }
165
+ }
166
+
167
+ /**
168
+ * Clears the metadata cache
169
+ */
170
+ public clearCache(): void {
171
+ this.metadataCache.clear();
172
+ }
173
+
174
+ /**
175
+ * Gets total estimated tokens for all cached metadata
176
+ */
177
+ public getTotalEstimatedTokens(): number {
178
+ let total = 0;
179
+ for (const meta of this.metadataCache.values()) {
180
+ total += meta.estimatedTokens;
181
+ }
182
+ return total;
183
+ }
184
+
185
+ /**
186
+ * Gets cached metadata entries
187
+ */
188
+ public getCachedMetadata(): IFileMetadata[] {
189
+ return Array.from(this.metadataCache.values());
190
+ }
191
+ }
@@ -58,6 +58,73 @@ export interface IContextConfig {
58
58
  };
59
59
  /** Trimming configuration */
60
60
  trimming?: ITrimConfig;
61
+ /** Cache configuration */
62
+ cache?: ICacheConfig;
63
+ /** Analyzer configuration */
64
+ analyzer?: IAnalyzerConfig;
65
+ /** Prioritization weights */
66
+ prioritization?: IPrioritizationWeights;
67
+ /** Tier configuration for adaptive trimming */
68
+ tiers?: ITierConfig;
69
+ }
70
+
71
+ /**
72
+ * Cache configuration
73
+ */
74
+ export interface ICacheConfig {
75
+ /** Whether caching is enabled */
76
+ enabled?: boolean;
77
+ /** Time-to-live in seconds */
78
+ ttl?: number;
79
+ /** Maximum cache size in MB */
80
+ maxSize?: number;
81
+ /** Cache directory path */
82
+ directory?: string;
83
+ }
84
+
85
+ /**
86
+ * Analyzer configuration
87
+ */
88
+ export interface IAnalyzerConfig {
89
+ /** Whether analyzer is enabled */
90
+ enabled?: boolean;
91
+ /** Whether to use AI refinement for selection */
92
+ useAIRefinement?: boolean;
93
+ /** AI model to use for refinement */
94
+ aiModel?: string;
95
+ }
96
+
97
+ /**
98
+ * Weights for file prioritization
99
+ */
100
+ export interface IPrioritizationWeights {
101
+ /** Weight for dependency centrality */
102
+ dependencyWeight?: number;
103
+ /** Weight for task relevance */
104
+ relevanceWeight?: number;
105
+ /** Weight for token efficiency */
106
+ efficiencyWeight?: number;
107
+ /** Weight for file recency */
108
+ recencyWeight?: number;
109
+ }
110
+
111
+ /**
112
+ * Tier configuration for adaptive trimming
113
+ */
114
+ export interface ITierConfig {
115
+ essential?: ITierSettings;
116
+ important?: ITierSettings;
117
+ optional?: ITierSettings;
118
+ }
119
+
120
+ /**
121
+ * Settings for a single tier
122
+ */
123
+ export interface ITierSettings {
124
+ /** Minimum score to qualify for this tier */
125
+ minScore: number;
126
+ /** Trimming level to apply */
127
+ trimLevel: 'none' | 'light' | 'aggressive';
61
128
  }
62
129
 
63
130
  /**
@@ -92,4 +159,90 @@ export interface IContextResult {
92
159
  excludedFiles: IFileInfo[];
93
160
  /** Token savings from trimming */
94
161
  tokenSavings: number;
162
+ }
163
+
164
+ /**
165
+ * File metadata without contents (for lazy loading)
166
+ */
167
+ export interface IFileMetadata {
168
+ /** The file path */
169
+ path: string;
170
+ /** The file's relative path from the project root */
171
+ relativePath: string;
172
+ /** File size in bytes */
173
+ size: number;
174
+ /** Last modified time (Unix timestamp) */
175
+ mtime: number;
176
+ /** Estimated token count (without loading full contents) */
177
+ estimatedTokens: number;
178
+ /** The file's importance score */
179
+ importanceScore?: number;
180
+ }
181
+
182
+ /**
183
+ * Cache entry for a file
184
+ */
185
+ export interface ICacheEntry {
186
+ /** File path */
187
+ path: string;
188
+ /** File contents */
189
+ contents: string;
190
+ /** Token count */
191
+ tokenCount: number;
192
+ /** Last modified time when cached */
193
+ mtime: number;
194
+ /** When this cache entry was created */
195
+ cachedAt: number;
196
+ }
197
+
198
+ /**
199
+ * Dependency information for a file
200
+ */
201
+ export interface IFileDependencies {
202
+ /** File path */
203
+ path: string;
204
+ /** Files this file imports */
205
+ imports: string[];
206
+ /** Files that import this file */
207
+ importedBy: string[];
208
+ /** Centrality score (0-1) - how central this file is in the dependency graph */
209
+ centrality: number;
210
+ }
211
+
212
+ /**
213
+ * Analysis result for a file
214
+ */
215
+ export interface IFileAnalysis {
216
+ /** File path */
217
+ path: string;
218
+ /** Task relevance score (0-1) */
219
+ relevanceScore: number;
220
+ /** Dependency centrality score (0-1) */
221
+ centralityScore: number;
222
+ /** Token efficiency score (0-1) */
223
+ efficiencyScore: number;
224
+ /** Recency score (0-1) */
225
+ recencyScore: number;
226
+ /** Combined importance score (0-1) */
227
+ importanceScore: number;
228
+ /** Assigned tier */
229
+ tier: 'essential' | 'important' | 'optional' | 'excluded';
230
+ /** Reason for the score */
231
+ reason?: string;
232
+ }
233
+
234
+ /**
235
+ * Result of context analysis
236
+ */
237
+ export interface IAnalysisResult {
238
+ /** Task type being analyzed */
239
+ taskType: TaskType;
240
+ /** Analyzed files with scores */
241
+ files: IFileAnalysis[];
242
+ /** Dependency graph */
243
+ dependencyGraph: Map<string, IFileDependencies>;
244
+ /** Total files analyzed */
245
+ totalFiles: number;
246
+ /** Analysis duration in ms */
247
+ analysisDuration: number;
95
248
  }