@git.zone/tsdoc 1.4.5 → 1.5.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.
Files changed (41) hide show
  1. package/dist_ts/00_commitinfo_data.js +3 -3
  2. package/dist_ts/aidocs_classes/commit.js +16 -10
  3. package/dist_ts/aidocs_classes/description.js +14 -6
  4. package/dist_ts/aidocs_classes/projectcontext.d.ts +22 -0
  5. package/dist_ts/aidocs_classes/projectcontext.js +41 -1
  6. package/dist_ts/aidocs_classes/readme.js +10 -4
  7. package/dist_ts/classes.aidoc.d.ts +22 -0
  8. package/dist_ts/classes.aidoc.js +31 -1
  9. package/dist_ts/cli.js +110 -1
  10. package/dist_ts/context/config-manager.d.ts +58 -0
  11. package/dist_ts/context/config-manager.js +183 -0
  12. package/dist_ts/context/context-trimmer.d.ts +52 -0
  13. package/dist_ts/context/context-trimmer.js +199 -0
  14. package/dist_ts/context/enhanced-context.d.ts +75 -0
  15. package/dist_ts/context/enhanced-context.js +272 -0
  16. package/dist_ts/context/index.d.ts +7 -0
  17. package/dist_ts/context/index.js +8 -0
  18. package/dist_ts/context/task-context-factory.d.ts +46 -0
  19. package/dist_ts/context/task-context-factory.js +109 -0
  20. package/dist_ts/context/types.d.ts +89 -0
  21. package/dist_ts/context/types.js +2 -0
  22. package/dist_ts/plugins.d.ts +2 -1
  23. package/dist_ts/plugins.js +3 -2
  24. package/npmextra.json +10 -9
  25. package/package.json +12 -10
  26. package/readme.md +588 -174
  27. package/readme.plan.md +314 -0
  28. package/ts/00_commitinfo_data.ts +2 -2
  29. package/ts/aidocs_classes/commit.ts +21 -9
  30. package/ts/aidocs_classes/description.ts +16 -5
  31. package/ts/aidocs_classes/projectcontext.ts +43 -0
  32. package/ts/aidocs_classes/readme.ts +11 -3
  33. package/ts/classes.aidoc.ts +33 -0
  34. package/ts/cli.ts +128 -0
  35. package/ts/context/config-manager.ts +209 -0
  36. package/ts/context/context-trimmer.ts +246 -0
  37. package/ts/context/enhanced-context.ts +343 -0
  38. package/ts/context/index.ts +32 -0
  39. package/ts/context/task-context-factory.ts +138 -0
  40. package/ts/context/types.ts +95 -0
  41. package/ts/plugins.ts +2 -1
@@ -0,0 +1,343 @@
1
+ import * as plugins from '../plugins.js';
2
+ import type { ContextMode, IContextResult, IFileInfo, TaskType } from './types.js';
3
+ import { ContextTrimmer } from './context-trimmer.js';
4
+ import { ConfigManager } from './config-manager.js';
5
+
6
+ /**
7
+ * Enhanced ProjectContext that supports context optimization strategies
8
+ */
9
+ export class EnhancedContext {
10
+ private projectDir: string;
11
+ private trimmer: ContextTrimmer;
12
+ private configManager: ConfigManager;
13
+ private contextMode: ContextMode = 'trimmed';
14
+ private tokenBudget: number = 190000; // Default for o4-mini
15
+ private contextResult: IContextResult = {
16
+ context: '',
17
+ tokenCount: 0,
18
+ includedFiles: [],
19
+ trimmedFiles: [],
20
+ excludedFiles: [],
21
+ tokenSavings: 0
22
+ };
23
+
24
+ /**
25
+ * Create a new EnhancedContext
26
+ * @param projectDirArg The project directory
27
+ */
28
+ constructor(projectDirArg: string) {
29
+ this.projectDir = projectDirArg;
30
+ this.configManager = ConfigManager.getInstance();
31
+ this.trimmer = new ContextTrimmer(this.configManager.getTrimConfig());
32
+ }
33
+
34
+ /**
35
+ * Initialize the context builder
36
+ */
37
+ public async initialize(): Promise<void> {
38
+ await this.configManager.initialize(this.projectDir);
39
+ this.tokenBudget = this.configManager.getMaxTokens();
40
+ this.trimmer.updateConfig(this.configManager.getTrimConfig());
41
+ }
42
+
43
+ /**
44
+ * Set the context mode
45
+ * @param mode The context mode to use
46
+ */
47
+ public setContextMode(mode: ContextMode): void {
48
+ this.contextMode = mode;
49
+ }
50
+
51
+ /**
52
+ * Set the token budget
53
+ * @param maxTokens The maximum tokens to use
54
+ */
55
+ public setTokenBudget(maxTokens: number): void {
56
+ this.tokenBudget = maxTokens;
57
+ }
58
+
59
+ /**
60
+ * Gather files from the project
61
+ * @param includePaths Optional paths to include
62
+ * @param excludePaths Optional paths to exclude
63
+ */
64
+ public async gatherFiles(includePaths?: string[], excludePaths?: string[]): Promise<Record<string, plugins.smartfile.SmartFile | plugins.smartfile.SmartFile[]>> {
65
+ const smartfilePackageJSON = await plugins.smartfile.SmartFile.fromFilePath(
66
+ plugins.path.join(this.projectDir, 'package.json'),
67
+ this.projectDir,
68
+ );
69
+
70
+ const smartfilesReadme = await plugins.smartfile.SmartFile.fromFilePath(
71
+ plugins.path.join(this.projectDir, 'readme.md'),
72
+ this.projectDir,
73
+ );
74
+
75
+ const smartfilesReadmeHints = await plugins.smartfile.SmartFile.fromFilePath(
76
+ plugins.path.join(this.projectDir, 'readme.hints.md'),
77
+ this.projectDir,
78
+ );
79
+
80
+ const smartfilesNpmextraJSON = await plugins.smartfile.SmartFile.fromFilePath(
81
+ plugins.path.join(this.projectDir, 'npmextra.json'),
82
+ this.projectDir,
83
+ );
84
+
85
+ // Use provided include paths or default to all TypeScript files
86
+ const includeGlobs = includePaths?.map(path => `${path}/**/*.ts`) || ['ts*/**/*.ts'];
87
+
88
+ // Get TypeScript files
89
+ const smartfilesModPromises = includeGlobs.map(glob =>
90
+ plugins.smartfile.fs.fileTreeToObject(this.projectDir, glob)
91
+ );
92
+
93
+ const smartfilesModArrays = await Promise.all(smartfilesModPromises);
94
+
95
+ // Flatten the arrays
96
+ const smartfilesMod: plugins.smartfile.SmartFile[] = [];
97
+ smartfilesModArrays.forEach(array => {
98
+ smartfilesMod.push(...array);
99
+ });
100
+
101
+ // Get test files if not excluded
102
+ let smartfilesTest: plugins.smartfile.SmartFile[] = [];
103
+ if (!excludePaths?.includes('test/')) {
104
+ smartfilesTest = await plugins.smartfile.fs.fileTreeToObject(
105
+ this.projectDir,
106
+ 'test/**/*.ts',
107
+ );
108
+ }
109
+
110
+ return {
111
+ smartfilePackageJSON,
112
+ smartfilesReadme,
113
+ smartfilesReadmeHints,
114
+ smartfilesNpmextraJSON,
115
+ smartfilesMod,
116
+ smartfilesTest,
117
+ };
118
+ }
119
+
120
+ /**
121
+ * Convert files to context string
122
+ * @param files The files to convert
123
+ * @param mode The context mode to use
124
+ */
125
+ public async convertFilesToContext(
126
+ files: plugins.smartfile.SmartFile[],
127
+ mode: ContextMode = this.contextMode
128
+ ): Promise<string> {
129
+ // Reset context result
130
+ this.contextResult = {
131
+ context: '',
132
+ tokenCount: 0,
133
+ includedFiles: [],
134
+ trimmedFiles: [],
135
+ excludedFiles: [],
136
+ tokenSavings: 0
137
+ };
138
+
139
+ let totalTokenCount = 0;
140
+ let totalOriginalTokens = 0;
141
+
142
+ // Sort files by importance (for now just a simple alphabetical sort)
143
+ // Later this could be enhanced with more sophisticated prioritization
144
+ const sortedFiles = [...files].sort((a, b) => a.relative.localeCompare(b.relative));
145
+
146
+ const processedFiles: string[] = [];
147
+
148
+ for (const smartfile of sortedFiles) {
149
+ // Calculate original token count
150
+ const originalContent = smartfile.contents.toString();
151
+ const originalTokenCount = this.countTokens(originalContent);
152
+ totalOriginalTokens += originalTokenCount;
153
+
154
+ // Apply trimming based on mode
155
+ let processedContent = originalContent;
156
+
157
+ if (mode !== 'full') {
158
+ processedContent = this.trimmer.trimFile(
159
+ smartfile.relative,
160
+ originalContent,
161
+ mode
162
+ );
163
+ }
164
+
165
+ // Calculate new token count
166
+ const processedTokenCount = this.countTokens(processedContent);
167
+
168
+ // Check if we have budget for this file
169
+ if (totalTokenCount + processedTokenCount > this.tokenBudget) {
170
+ // We don't have budget for this file
171
+ this.contextResult.excludedFiles.push({
172
+ path: smartfile.path,
173
+ contents: originalContent,
174
+ relativePath: smartfile.relative,
175
+ tokenCount: originalTokenCount
176
+ });
177
+ continue;
178
+ }
179
+
180
+ // Format the file for context
181
+ const formattedContent = `
182
+ ====== START OF FILE ${smartfile.relative} ======
183
+
184
+ ${processedContent}
185
+
186
+ ====== END OF FILE ${smartfile.relative} ======
187
+ `;
188
+
189
+ processedFiles.push(formattedContent);
190
+ totalTokenCount += processedTokenCount;
191
+
192
+ // Track file in appropriate list
193
+ const fileInfo: IFileInfo = {
194
+ path: smartfile.path,
195
+ contents: processedContent,
196
+ relativePath: smartfile.relative,
197
+ tokenCount: processedTokenCount
198
+ };
199
+
200
+ if (mode === 'full' || processedContent === originalContent) {
201
+ this.contextResult.includedFiles.push(fileInfo);
202
+ } else {
203
+ this.contextResult.trimmedFiles.push(fileInfo);
204
+ this.contextResult.tokenSavings += (originalTokenCount - processedTokenCount);
205
+ }
206
+ }
207
+
208
+ // Join all processed files
209
+ const context = processedFiles.join('\n');
210
+
211
+ // Update context result
212
+ this.contextResult.context = context;
213
+ this.contextResult.tokenCount = totalTokenCount;
214
+
215
+ return context;
216
+ }
217
+
218
+ /**
219
+ * Build context for the project
220
+ * @param taskType Optional task type for task-specific context
221
+ */
222
+ public async buildContext(taskType?: TaskType): Promise<IContextResult> {
223
+ // Initialize if needed
224
+ if (this.tokenBudget === 0) {
225
+ await this.initialize();
226
+ }
227
+
228
+ // Get task-specific configuration if a task type is provided
229
+ if (taskType) {
230
+ const taskConfig = this.configManager.getTaskConfig(taskType);
231
+ if (taskConfig.mode) {
232
+ this.setContextMode(taskConfig.mode);
233
+ }
234
+ }
235
+
236
+ // Gather files
237
+ const taskConfig = taskType ? this.configManager.getTaskConfig(taskType) : undefined;
238
+ const files = await this.gatherFiles(
239
+ taskConfig?.includePaths,
240
+ taskConfig?.excludePaths
241
+ );
242
+
243
+ // Convert files to context
244
+ // Create an array of all files to process
245
+ const allFiles: plugins.smartfile.SmartFile[] = [];
246
+
247
+ // Add individual files
248
+ if (files.smartfilePackageJSON) allFiles.push(files.smartfilePackageJSON as plugins.smartfile.SmartFile);
249
+ if (files.smartfilesReadme) allFiles.push(files.smartfilesReadme as plugins.smartfile.SmartFile);
250
+ if (files.smartfilesReadmeHints) allFiles.push(files.smartfilesReadmeHints as plugins.smartfile.SmartFile);
251
+ if (files.smartfilesNpmextraJSON) allFiles.push(files.smartfilesNpmextraJSON as plugins.smartfile.SmartFile);
252
+
253
+ // Add arrays of files
254
+ if (files.smartfilesMod) {
255
+ if (Array.isArray(files.smartfilesMod)) {
256
+ allFiles.push(...files.smartfilesMod);
257
+ } else {
258
+ allFiles.push(files.smartfilesMod);
259
+ }
260
+ }
261
+
262
+ if (files.smartfilesTest) {
263
+ if (Array.isArray(files.smartfilesTest)) {
264
+ allFiles.push(...files.smartfilesTest);
265
+ } else {
266
+ allFiles.push(files.smartfilesTest);
267
+ }
268
+ }
269
+
270
+ const context = await this.convertFilesToContext(allFiles);
271
+
272
+ return this.contextResult;
273
+ }
274
+
275
+ /**
276
+ * Update the context with git diff information for commit tasks
277
+ * @param gitDiff The git diff to include
278
+ */
279
+ public updateWithGitDiff(gitDiff: string): IContextResult {
280
+ // If we don't have a context yet, return empty result
281
+ if (!this.contextResult.context) {
282
+ return this.contextResult;
283
+ }
284
+
285
+ // Add git diff to context
286
+ const diffSection = `
287
+ ====== GIT DIFF ======
288
+
289
+ ${gitDiff}
290
+
291
+ ====== END GIT DIFF ======
292
+ `;
293
+
294
+ const diffTokenCount = this.countTokens(diffSection);
295
+
296
+ // Update context and token count
297
+ this.contextResult.context += diffSection;
298
+ this.contextResult.tokenCount += diffTokenCount;
299
+
300
+ return this.contextResult;
301
+ }
302
+
303
+ /**
304
+ * Count tokens in a string
305
+ * @param text The text to count tokens for
306
+ * @param model The model to use for token counting
307
+ */
308
+ public countTokens(text: string, model: string = 'gpt-3.5-turbo'): number {
309
+ try {
310
+ // Use the gpt-tokenizer library to count tokens
311
+ const tokens = plugins.gptTokenizer.encode(text);
312
+ return tokens.length;
313
+ } catch (error) {
314
+ console.error('Error counting tokens:', error);
315
+ // Provide a rough estimate if tokenization fails
316
+ return Math.ceil(text.length / 4);
317
+ }
318
+ }
319
+
320
+ /**
321
+ * Get the context result
322
+ */
323
+ public getContextResult(): IContextResult {
324
+ return this.contextResult;
325
+ }
326
+
327
+ /**
328
+ * Get the token count for the current context
329
+ */
330
+ public getTokenCount(): number {
331
+ return this.contextResult.tokenCount;
332
+ }
333
+
334
+ /**
335
+ * Get both the context string and its token count
336
+ */
337
+ public getContextWithTokenCount(): { context: string; tokenCount: number } {
338
+ return {
339
+ context: this.contextResult.context,
340
+ tokenCount: this.contextResult.tokenCount
341
+ };
342
+ }
343
+ }
@@ -0,0 +1,32 @@
1
+ import { EnhancedContext } from './enhanced-context.js';
2
+ import { TaskContextFactory } from './task-context-factory.js';
3
+ import { ConfigManager } from './config-manager.js';
4
+ import { ContextTrimmer } from './context-trimmer.js';
5
+ import type {
6
+ ContextMode,
7
+ IContextConfig,
8
+ IContextResult,
9
+ IFileInfo,
10
+ ITrimConfig,
11
+ ITaskConfig,
12
+ TaskType
13
+ } from './types.js';
14
+
15
+ export {
16
+ // Classes
17
+ EnhancedContext,
18
+ TaskContextFactory,
19
+ ConfigManager,
20
+ ContextTrimmer,
21
+ };
22
+
23
+ // Types
24
+ export type {
25
+ ContextMode,
26
+ IContextConfig,
27
+ IContextResult,
28
+ IFileInfo,
29
+ ITrimConfig,
30
+ ITaskConfig,
31
+ TaskType
32
+ };
@@ -0,0 +1,138 @@
1
+ import * as plugins from '../plugins.js';
2
+ import { EnhancedContext } from './enhanced-context.js';
3
+ import { ConfigManager } from './config-manager.js';
4
+ import type { IContextResult, TaskType } from './types.js';
5
+
6
+ /**
7
+ * Factory class for creating task-specific context
8
+ */
9
+ export class TaskContextFactory {
10
+ private projectDir: string;
11
+ private configManager: ConfigManager;
12
+
13
+ /**
14
+ * Create a new TaskContextFactory
15
+ * @param projectDirArg The project directory
16
+ */
17
+ constructor(projectDirArg: string) {
18
+ this.projectDir = projectDirArg;
19
+ this.configManager = ConfigManager.getInstance();
20
+ }
21
+
22
+ /**
23
+ * Initialize the factory
24
+ */
25
+ public async initialize(): Promise<void> {
26
+ await this.configManager.initialize(this.projectDir);
27
+ }
28
+
29
+ /**
30
+ * Create context for README generation
31
+ */
32
+ public async createContextForReadme(): Promise<IContextResult> {
33
+ const contextBuilder = new EnhancedContext(this.projectDir);
34
+ await contextBuilder.initialize();
35
+
36
+ // Get README-specific configuration
37
+ const taskConfig = this.configManager.getTaskConfig('readme');
38
+ if (taskConfig.mode) {
39
+ contextBuilder.setContextMode(taskConfig.mode);
40
+ }
41
+
42
+ // Build the context for README task
43
+ return await contextBuilder.buildContext('readme');
44
+ }
45
+
46
+ /**
47
+ * Create context for description generation
48
+ */
49
+ public async createContextForDescription(): Promise<IContextResult> {
50
+ const contextBuilder = new EnhancedContext(this.projectDir);
51
+ await contextBuilder.initialize();
52
+
53
+ // Get description-specific configuration
54
+ const taskConfig = this.configManager.getTaskConfig('description');
55
+ if (taskConfig.mode) {
56
+ contextBuilder.setContextMode(taskConfig.mode);
57
+ }
58
+
59
+ // Build the context for description task
60
+ return await contextBuilder.buildContext('description');
61
+ }
62
+
63
+ /**
64
+ * Create context for commit message generation
65
+ * @param gitDiff Optional git diff to include
66
+ */
67
+ public async createContextForCommit(gitDiff?: string): Promise<IContextResult> {
68
+ const contextBuilder = new EnhancedContext(this.projectDir);
69
+ await contextBuilder.initialize();
70
+
71
+ // Get commit-specific configuration
72
+ const taskConfig = this.configManager.getTaskConfig('commit');
73
+ if (taskConfig.mode) {
74
+ contextBuilder.setContextMode(taskConfig.mode);
75
+ }
76
+
77
+ // Build the context for commit task
78
+ const contextResult = await contextBuilder.buildContext('commit');
79
+
80
+ // If git diff is provided, add it to the context
81
+ if (gitDiff) {
82
+ contextBuilder.updateWithGitDiff(gitDiff);
83
+ }
84
+
85
+ return contextBuilder.getContextResult();
86
+ }
87
+
88
+ /**
89
+ * Create context for any task type
90
+ * @param taskType The task type to create context for
91
+ * @param additionalContent Optional additional content to include
92
+ */
93
+ public async createContextForTask(
94
+ taskType: TaskType,
95
+ additionalContent?: string
96
+ ): Promise<IContextResult> {
97
+ switch (taskType) {
98
+ case 'readme':
99
+ return this.createContextForReadme();
100
+ case 'description':
101
+ return this.createContextForDescription();
102
+ case 'commit':
103
+ return this.createContextForCommit(additionalContent);
104
+ default:
105
+ // Generic context for unknown task types
106
+ const contextBuilder = new EnhancedContext(this.projectDir);
107
+ await contextBuilder.initialize();
108
+ return await contextBuilder.buildContext();
109
+ }
110
+ }
111
+
112
+ /**
113
+ * Get token stats for all task types
114
+ */
115
+ public async getTokenStats(): Promise<Record<TaskType, {
116
+ tokenCount: number;
117
+ savings: number;
118
+ includedFiles: number;
119
+ trimmedFiles: number;
120
+ excludedFiles: number;
121
+ }>> {
122
+ const taskTypes: TaskType[] = ['readme', 'description', 'commit'];
123
+ const stats: Record<TaskType, any> = {} as any;
124
+
125
+ for (const taskType of taskTypes) {
126
+ const result = await this.createContextForTask(taskType);
127
+ stats[taskType] = {
128
+ tokenCount: result.tokenCount,
129
+ savings: result.tokenSavings,
130
+ includedFiles: result.includedFiles.length,
131
+ trimmedFiles: result.trimmedFiles.length,
132
+ excludedFiles: result.excludedFiles.length
133
+ };
134
+ }
135
+
136
+ return stats;
137
+ }
138
+ }
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Context processing mode to control how context is built
3
+ */
4
+ export type ContextMode = 'full' | 'trimmed' | 'summarized';
5
+
6
+ /**
7
+ * Configuration for context trimming
8
+ */
9
+ export interface ITrimConfig {
10
+ /** Whether to remove function implementations */
11
+ removeImplementations?: boolean;
12
+ /** Whether to preserve interface definitions */
13
+ preserveInterfaces?: boolean;
14
+ /** Whether to preserve type definitions */
15
+ preserveTypeDefs?: boolean;
16
+ /** Whether to preserve JSDoc comments */
17
+ preserveJSDoc?: boolean;
18
+ /** Maximum lines to keep for function bodies (if not removing completely) */
19
+ maxFunctionLines?: number;
20
+ /** Whether to remove normal comments (non-JSDoc) */
21
+ removeComments?: boolean;
22
+ /** Whether to remove blank lines */
23
+ removeBlankLines?: boolean;
24
+ }
25
+
26
+ /**
27
+ * Task types that require different context optimization
28
+ */
29
+ export type TaskType = 'readme' | 'commit' | 'description';
30
+
31
+ /**
32
+ * Configuration for different tasks
33
+ */
34
+ export interface ITaskConfig {
35
+ /** The context mode to use for this task */
36
+ mode?: ContextMode;
37
+ /** File paths to include for this task */
38
+ includePaths?: string[];
39
+ /** File paths to exclude for this task */
40
+ excludePaths?: string[];
41
+ /** For commit tasks, whether to focus on changed files */
42
+ focusOnChangedFiles?: boolean;
43
+ /** For description tasks, whether to include package info */
44
+ includePackageInfo?: boolean;
45
+ }
46
+
47
+ /**
48
+ * Complete context configuration
49
+ */
50
+ export interface IContextConfig {
51
+ /** Maximum tokens to use for context */
52
+ maxTokens?: number;
53
+ /** Default context mode */
54
+ defaultMode?: ContextMode;
55
+ /** Task-specific settings */
56
+ taskSpecificSettings?: {
57
+ [key in TaskType]?: ITaskConfig;
58
+ };
59
+ /** Trimming configuration */
60
+ trimming?: ITrimConfig;
61
+ }
62
+
63
+ /**
64
+ * Basic file information interface
65
+ */
66
+ export interface IFileInfo {
67
+ /** The file path */
68
+ path: string;
69
+ /** The file contents */
70
+ contents: string;
71
+ /** The file's relative path from the project root */
72
+ relativePath: string;
73
+ /** The estimated token count of the file */
74
+ tokenCount?: number;
75
+ /** The file's importance score (higher is more important) */
76
+ importanceScore?: number;
77
+ }
78
+
79
+ /**
80
+ * Result of context building
81
+ */
82
+ export interface IContextResult {
83
+ /** The generated context string */
84
+ context: string;
85
+ /** The total token count of the context */
86
+ tokenCount: number;
87
+ /** Files included in the context */
88
+ includedFiles: IFileInfo[];
89
+ /** Files that were trimmed */
90
+ trimmedFiles: IFileInfo[];
91
+ /** Files that were excluded */
92
+ excludedFiles: IFileInfo[];
93
+ /** Token savings from trimming */
94
+ tokenSavings: number;
95
+ }
package/ts/plugins.ts CHANGED
@@ -41,5 +41,6 @@ export { tspublish };
41
41
 
42
42
  // third party scope
43
43
  import * as typedoc from 'typedoc';
44
+ import * as gptTokenizer from 'gpt-tokenizer';
44
45
 
45
- export { typedoc };
46
+ export { typedoc, gptTokenizer };