ctxman 3.0.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 (190) hide show
  1. package/.ctxman/custom-profiles.example.json +20 -0
  2. package/.ctxman/llm-profiles.json +131 -0
  3. package/.ctxman/logs/ctxman-2026-07-02.log +156 -0
  4. package/.ctxman/self-knowledge/documents.lance/_transactions/0-b3dd1558-f95b-44b9-a154-18e8c330ae7a.txn +0 -0
  5. package/.ctxman/self-knowledge/documents.lance/_transactions/1-44bff6d4-e552-4010-a769-e09ad822698d.txn +0 -0
  6. package/.ctxman/self-knowledge/documents.lance/_transactions/10-37260794-5947-4ecb-b4c8-d732f58f40f1.txn +0 -0
  7. package/.ctxman/self-knowledge/documents.lance/_transactions/11-0d52a883-5518-463c-9b6d-986c3af89dfd.txn +0 -0
  8. package/.ctxman/self-knowledge/documents.lance/_transactions/12-ae354646-52f2-49c9-90fb-2e6ed8932663.txn +0 -0
  9. package/.ctxman/self-knowledge/documents.lance/_transactions/13-5d7d5e57-27da-498b-b734-091b9d38da13.txn +0 -0
  10. package/.ctxman/self-knowledge/documents.lance/_transactions/14-558ce928-810b-4252-a364-fdd1ee6f8a25.txn +0 -0
  11. package/.ctxman/self-knowledge/documents.lance/_transactions/15-27e3a58f-8b64-43b8-a9f4-e3369446968b.txn +0 -0
  12. package/.ctxman/self-knowledge/documents.lance/_transactions/16-a110c390-e5c8-4464-b976-bc7f5195fa15.txn +0 -0
  13. package/.ctxman/self-knowledge/documents.lance/_transactions/17-97513225-53de-4216-b61a-6337bd0cc4ad.txn +0 -0
  14. package/.ctxman/self-knowledge/documents.lance/_transactions/18-5ba450f0-c527-459c-bf4f-6d79b5e4285f.txn +0 -0
  15. package/.ctxman/self-knowledge/documents.lance/_transactions/19-600d3a63-5225-49e9-b8b7-30e037b564ab.txn +0 -0
  16. package/.ctxman/self-knowledge/documents.lance/_transactions/2-48a61b4e-92cf-4342-8376-914c1b99a9f9.txn +0 -0
  17. package/.ctxman/self-knowledge/documents.lance/_transactions/20-121dd912-5410-410e-84ff-82c7a23604a0.txn +0 -0
  18. package/.ctxman/self-knowledge/documents.lance/_transactions/3-bc6844ab-7ea3-4bb3-9ee1-538ce5664a9c.txn +0 -0
  19. package/.ctxman/self-knowledge/documents.lance/_transactions/4-d5e1754f-dbb5-454e-a9be-2a7da8d7bcf9.txn +0 -0
  20. package/.ctxman/self-knowledge/documents.lance/_transactions/5-4fae93df-0568-4b6f-8acf-8bef4bba6047.txn +0 -0
  21. package/.ctxman/self-knowledge/documents.lance/_transactions/6-4b1b8b87-068e-49a3-ba2b-d8dae628b252.txn +0 -0
  22. package/.ctxman/self-knowledge/documents.lance/_transactions/7-63d040fb-d2fa-4c0f-bed0-53411918a7b0.txn +0 -0
  23. package/.ctxman/self-knowledge/documents.lance/_transactions/8-c5628cf6-264c-420f-aab8-bc96a9083591.txn +0 -0
  24. package/.ctxman/self-knowledge/documents.lance/_transactions/9-c94570aa-5e53-4fcb-971f-cecc1d1d5492.txn +0 -0
  25. package/.ctxman/self-knowledge/documents.lance/_versions/1.manifest +0 -0
  26. package/.ctxman/self-knowledge/documents.lance/_versions/10.manifest +0 -0
  27. package/.ctxman/self-knowledge/documents.lance/_versions/11.manifest +0 -0
  28. package/.ctxman/self-knowledge/documents.lance/_versions/12.manifest +0 -0
  29. package/.ctxman/self-knowledge/documents.lance/_versions/13.manifest +0 -0
  30. package/.ctxman/self-knowledge/documents.lance/_versions/14.manifest +0 -0
  31. package/.ctxman/self-knowledge/documents.lance/_versions/15.manifest +0 -0
  32. package/.ctxman/self-knowledge/documents.lance/_versions/16.manifest +0 -0
  33. package/.ctxman/self-knowledge/documents.lance/_versions/17.manifest +0 -0
  34. package/.ctxman/self-knowledge/documents.lance/_versions/18.manifest +0 -0
  35. package/.ctxman/self-knowledge/documents.lance/_versions/19.manifest +0 -0
  36. package/.ctxman/self-knowledge/documents.lance/_versions/2.manifest +0 -0
  37. package/.ctxman/self-knowledge/documents.lance/_versions/20.manifest +0 -0
  38. package/.ctxman/self-knowledge/documents.lance/_versions/21.manifest +0 -0
  39. package/.ctxman/self-knowledge/documents.lance/_versions/3.manifest +0 -0
  40. package/.ctxman/self-knowledge/documents.lance/_versions/4.manifest +0 -0
  41. package/.ctxman/self-knowledge/documents.lance/_versions/5.manifest +0 -0
  42. package/.ctxman/self-knowledge/documents.lance/_versions/6.manifest +0 -0
  43. package/.ctxman/self-knowledge/documents.lance/_versions/7.manifest +0 -0
  44. package/.ctxman/self-knowledge/documents.lance/_versions/8.manifest +0 -0
  45. package/.ctxman/self-knowledge/documents.lance/_versions/9.manifest +0 -0
  46. package/.ctxman/self-knowledge/documents.lance/data/000010010011011101000010de8621405d9d5cb06540dbf02c.lance +0 -0
  47. package/.ctxman/self-knowledge/documents.lance/data/0000101001011100011000003fb62a4aa8978974075c144f1c.lance +0 -0
  48. package/.ctxman/self-knowledge/documents.lance/data/000011010000100111001011c6cc824d628f3bb1f4e04cd5d0.lance +0 -0
  49. package/.ctxman/self-knowledge/documents.lance/data/001000001000011001011100d8f74349ada090b332cd1df669.lance +0 -0
  50. package/.ctxman/self-knowledge/documents.lance/data/01000001111010001000010045ab44467bb1e7e4d8ae06bd58.lance +0 -0
  51. package/.ctxman/self-knowledge/documents.lance/data/01000101101110111110011131ef904241a89e1ffb6ddaf8e3.lance +0 -0
  52. package/.ctxman/self-knowledge/documents.lance/data/010010110000010000011010fbfcc145c6a8a827b28057a07e.lance +0 -0
  53. package/.ctxman/self-knowledge/documents.lance/data/1000011000101010001110003fb4f045c3835982efa1405324.lance +0 -0
  54. package/.ctxman/self-knowledge/documents.lance/data/100111011101110111101011f4396b410fa06af3b028d204e6.lance +0 -0
  55. package/.ctxman/self-knowledge/documents.lance/data/100111100111101000001100596b174e00995d6cc2969cc8dc.lance +0 -0
  56. package/.ctxman/self-knowledge/documents.lance/data/1010001100011000110010102ea16b484d887c7c9725feffd6.lance +0 -0
  57. package/.ctxman/self-knowledge/documents.lance/data/101101100010101111110111c5230944d9b3af7301b6392dda.lance +0 -0
  58. package/.ctxman/self-knowledge/documents.lance/data/1011011100100111111110105892bf41b5a530fedd7cf516f1.lance +0 -0
  59. package/.ctxman/self-knowledge/documents.lance/data/10111110000010001011110164e8774742bade87b483c10f36.lance +0 -0
  60. package/.ctxman/self-knowledge/documents.lance/data/1100000100010110001110103ffaca40a2a42c85067ebaf9ae.lance +0 -0
  61. package/.ctxman/self-knowledge/documents.lance/data/110000010001110110101110664bde4c76a78dfc14b05e83b8.lance +0 -0
  62. package/.ctxman/self-knowledge/documents.lance/data/11001100111000010100110050645c4281b1f35b2022234f7f.lance +0 -0
  63. package/.ctxman/self-knowledge/documents.lance/data/1101111011001100110111017214924a368f5719eab2246619.lance +0 -0
  64. package/.ctxman/self-knowledge/documents.lance/data/1110000111001010001000002150c24dcab653d39d07032e63.lance +0 -0
  65. package/.ctxman/self-knowledge/documents.lance/data/1110100000000100000011014d20534967a18b5c87721659ff.lance +0 -0
  66. package/.ctxman/self-knowledge/documents.lance/data/11110101111010000101011135a0114ef2b35dc6f1297a3381.lance +0 -0
  67. package/.ctxman/wizard-profiles/code-review/.contextignore +41 -0
  68. package/.ctxman/wizard-profiles/code-review/.contextinclude +30 -0
  69. package/.ctxman/wizard-profiles/code-review/.methodignore +26 -0
  70. package/.ctxman/wizard-profiles/code-review/.methodinclude +47 -0
  71. package/.ctxman/wizard-profiles/code-review/profile.json +26 -0
  72. package/.ctxman/wizard-profiles/documentation/.contextignore +21 -0
  73. package/.ctxman/wizard-profiles/documentation/.contextinclude +24 -0
  74. package/.ctxman/wizard-profiles/documentation/.methodignore +19 -0
  75. package/.ctxman/wizard-profiles/documentation/.methodinclude +44 -0
  76. package/.ctxman/wizard-profiles/documentation/profile.json +27 -0
  77. package/.ctxman/wizard-profiles/full/.contextignore +23 -0
  78. package/.ctxman/wizard-profiles/full/.contextinclude +38 -0
  79. package/.ctxman/wizard-profiles/full/.methodignore +5 -0
  80. package/.ctxman/wizard-profiles/full/.methodinclude +5 -0
  81. package/.ctxman/wizard-profiles/full/profile.json +28 -0
  82. package/.ctxman/wizard-profiles/llm-explain/.contextignore +25 -0
  83. package/.ctxman/wizard-profiles/llm-explain/.contextinclude +33 -0
  84. package/.ctxman/wizard-profiles/llm-explain/.methodignore +19 -0
  85. package/.ctxman/wizard-profiles/llm-explain/.methodinclude +48 -0
  86. package/.ctxman/wizard-profiles/llm-explain/profile.json +27 -0
  87. package/.ctxman/wizard-profiles/minimal/.contextignore +27 -0
  88. package/.ctxman/wizard-profiles/minimal/.contextinclude +14 -0
  89. package/.ctxman/wizard-profiles/minimal/.methodignore +22 -0
  90. package/.ctxman/wizard-profiles/minimal/.methodinclude +18 -0
  91. package/.ctxman/wizard-profiles/minimal/profile.json +26 -0
  92. package/.ctxman/wizard-profiles/security-audit/.contextignore +36 -0
  93. package/.ctxman/wizard-profiles/security-audit/.contextinclude +37 -0
  94. package/.ctxman/wizard-profiles/security-audit/.methodignore +29 -0
  95. package/.ctxman/wizard-profiles/security-audit/.methodinclude +60 -0
  96. package/.ctxman/wizard-profiles/security-audit/profile.json +34 -0
  97. package/README.md +1167 -0
  98. package/bin/cli.js +650 -0
  99. package/bin/cm-ask.js +95 -0
  100. package/bin/cm-gitingest.js +134 -0
  101. package/bin/cm-update.js +175 -0
  102. package/bin/mcp-server.js +31 -0
  103. package/ctxman.js +236 -0
  104. package/examples/README.md +298 -0
  105. package/examples/custom-llm-profiles.example.json +19 -0
  106. package/examples/wizard-profiles/code-review/.contextignore +41 -0
  107. package/examples/wizard-profiles/code-review/.contextinclude +30 -0
  108. package/examples/wizard-profiles/code-review/.methodignore +26 -0
  109. package/examples/wizard-profiles/code-review/.methodinclude +47 -0
  110. package/examples/wizard-profiles/code-review/profile.json +26 -0
  111. package/examples/wizard-profiles/documentation/.contextignore +21 -0
  112. package/examples/wizard-profiles/documentation/.contextinclude +24 -0
  113. package/examples/wizard-profiles/documentation/.methodignore +19 -0
  114. package/examples/wizard-profiles/documentation/.methodinclude +44 -0
  115. package/examples/wizard-profiles/documentation/profile.json +27 -0
  116. package/examples/wizard-profiles/full/.contextignore +23 -0
  117. package/examples/wizard-profiles/full/.contextinclude +38 -0
  118. package/examples/wizard-profiles/full/.methodignore +5 -0
  119. package/examples/wizard-profiles/full/.methodinclude +5 -0
  120. package/examples/wizard-profiles/full/profile.json +28 -0
  121. package/examples/wizard-profiles/llm-explain/.contextignore +25 -0
  122. package/examples/wizard-profiles/llm-explain/.contextinclude +33 -0
  123. package/examples/wizard-profiles/llm-explain/.methodignore +19 -0
  124. package/examples/wizard-profiles/llm-explain/.methodinclude +48 -0
  125. package/examples/wizard-profiles/llm-explain/profile.json +27 -0
  126. package/examples/wizard-profiles/minimal/.contextignore +27 -0
  127. package/examples/wizard-profiles/minimal/.contextinclude +14 -0
  128. package/examples/wizard-profiles/minimal/.methodignore +22 -0
  129. package/examples/wizard-profiles/minimal/.methodinclude +18 -0
  130. package/examples/wizard-profiles/minimal/profile.json +26 -0
  131. package/examples/wizard-profiles/security-audit/.contextignore +36 -0
  132. package/examples/wizard-profiles/security-audit/.contextinclude +37 -0
  133. package/examples/wizard-profiles/security-audit/.methodignore +29 -0
  134. package/examples/wizard-profiles/security-audit/.methodinclude +60 -0
  135. package/examples/wizard-profiles/security-audit/profile.json +34 -0
  136. package/index.js +68 -0
  137. package/lib/agents/pr-failure-agent.js +201 -0
  138. package/lib/analyzers/go-method-analyzer.js +126 -0
  139. package/lib/analyzers/method-analyzer.js +457 -0
  140. package/lib/analyzers/token-calculator.js +577 -0
  141. package/lib/api/mcp/prompts.js +319 -0
  142. package/lib/api/mcp/resources.js +269 -0
  143. package/lib/api/mcp/server.js +339 -0
  144. package/lib/api/mcp/transports/HttpSseTransport.js +63 -0
  145. package/lib/api/rest/server.js +405 -0
  146. package/lib/cache/CacheManager.js +388 -0
  147. package/lib/core/Analyzer.js +180 -0
  148. package/lib/core/ContextBuilder.js +325 -0
  149. package/lib/core/Reporter.js +270 -0
  150. package/lib/core/Scanner.js +129 -0
  151. package/lib/formatters/format-registry.js +419 -0
  152. package/lib/formatters/gitingest-formatter.js +746 -0
  153. package/lib/formatters/toon-formatter-v1.3.js +731 -0
  154. package/lib/formatters/toon-formatter.js +291 -0
  155. package/lib/integrations/git/BlameTracker.js +142 -0
  156. package/lib/integrations/git/DiffAnalyzer.js +260 -0
  157. package/lib/integrations/git/GitClient.js +336 -0
  158. package/lib/parsers/gitignore-parser.js +127 -0
  159. package/lib/parsers/method-filter-parser.js +50 -0
  160. package/lib/plugins/ExporterPlugin.js +102 -0
  161. package/lib/plugins/LanguagePlugin.js +100 -0
  162. package/lib/plugins/PluginManager.js +302 -0
  163. package/lib/rag/DataSourcePlugin.js +31 -0
  164. package/lib/rag/EmbeddingProvider.js +75 -0
  165. package/lib/rag/Indexer.js +83 -0
  166. package/lib/rag/VectorStoreFactory.js +24 -0
  167. package/lib/rag/sources/FileDataSource.js +70 -0
  168. package/lib/rag/stores/BaseVectorStore.js +43 -0
  169. package/lib/rag/stores/ChromaDBStore.js +85 -0
  170. package/lib/rag/stores/LanceDBStore.js +109 -0
  171. package/lib/rag/stores/PineconeStore.js +74 -0
  172. package/lib/ui/dashboard.js +109 -0
  173. package/lib/ui/index.js +15 -0
  174. package/lib/ui/progress-bar.js +110 -0
  175. package/lib/ui/select-input.js +51 -0
  176. package/lib/ui/wizard.js +359 -0
  177. package/lib/utils/clipboard-utils.js +66 -0
  178. package/lib/utils/config-utils.js +94 -0
  179. package/lib/utils/error-handler.js +145 -0
  180. package/lib/utils/file-utils.js +89 -0
  181. package/lib/utils/format-converter.js +325 -0
  182. package/lib/utils/git-utils.js +337 -0
  183. package/lib/utils/llm-detector.js +409 -0
  184. package/lib/utils/logger.js +278 -0
  185. package/lib/utils/token-utils.js +200 -0
  186. package/lib/utils/tokenizer-adapter.js +454 -0
  187. package/lib/utils/updater.js +565 -0
  188. package/lib/watch/FileWatcher.js +208 -0
  189. package/lib/watch/IncrementalAnalyzer.js +206 -0
  190. package/package.json +118 -0
@@ -0,0 +1,337 @@
1
+ /**
2
+ * Git Integration Utilities
3
+ * Handles GitHub URL parsing, repository cloning, and GitIngest generation
4
+ * v2.3.6+
5
+ */
6
+
7
+ import { execSync } from 'child_process';
8
+ import fs from 'fs';
9
+ import path from 'path';
10
+ import https from 'https';
11
+ import TokenCalculator from '../analyzers/token-calculator.js';
12
+ import GitIngestFormatter from '../formatters/gitingest-formatter.js';
13
+
14
+ class GitUtils {
15
+ constructor(options = {}) {
16
+ this.tempDir = options.tempDir || path.join(process.cwd(), '.ctxman', 'temp');
17
+ this.outputDir = options.outputDir || path.join(process.cwd(), 'docs');
18
+ this.verbose = options.verbose || false;
19
+ }
20
+
21
+ /**
22
+ * Parse GitHub URL and extract repo info
23
+ */
24
+ parseGitHubURL(url) {
25
+ // Support various GitHub URL formats:
26
+ // - https://github.com/owner/repo
27
+ // - https://github.com/owner/repo.git
28
+ // - git@github.com:owner/repo.git
29
+ // - github.com/owner/repo
30
+ // - owner/repo
31
+
32
+ let cleanUrl = url.trim();
33
+
34
+ // Remove git@ prefix
35
+ if (cleanUrl.startsWith('git@')) {
36
+ cleanUrl = cleanUrl.replace('git@github.com:', 'https://github.com/');
37
+ }
38
+
39
+ // Add https if missing
40
+ if (!cleanUrl.startsWith('http')) {
41
+ if (cleanUrl.includes('/')) {
42
+ cleanUrl = `https://github.com/${cleanUrl}`;
43
+ }
44
+ }
45
+
46
+ // Remove .git suffix
47
+ cleanUrl = cleanUrl.replace(/\.git$/, '');
48
+
49
+ // Parse URL
50
+ try {
51
+ const urlObj = new URL(cleanUrl);
52
+ const pathParts = urlObj.pathname.split('/').filter(p => p);
53
+
54
+ if (pathParts.length < 2) {
55
+ throw new Error('Invalid GitHub URL: must include owner and repo');
56
+ }
57
+
58
+ const owner = pathParts[0];
59
+ const repo = pathParts[1];
60
+ const branch = pathParts[3] || 'main'; // If /tree/branch specified
61
+
62
+ return {
63
+ owner,
64
+ repo,
65
+ branch,
66
+ fullName: `${owner}/${repo}`,
67
+ cloneUrl: `https://github.com/${owner}/${repo}.git`,
68
+ apiUrl: `https://api.github.com/repos/${owner}/${repo}`,
69
+ rawUrl: `https://raw.githubusercontent.com/${owner}/${repo}/${branch}`,
70
+ originalUrl: url
71
+ };
72
+ } catch (error) {
73
+ throw new Error(`Failed to parse GitHub URL: ${error.message}`);
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Check if git is installed
79
+ */
80
+ isGitInstalled() {
81
+ try {
82
+ execSync('git --version', { stdio: 'pipe' });
83
+ return true;
84
+ } catch (error) {
85
+ return false;
86
+ }
87
+ }
88
+
89
+ /**
90
+ * Clone repository to temp directory
91
+ */
92
+ cloneRepository(repoInfo, options = {}) {
93
+ if (!this.isGitInstalled()) {
94
+ throw new Error('Git is not installed. Please install git to use this feature.');
95
+ }
96
+
97
+ const { shallow = true, depth = 1 } = options;
98
+ const repoPath = path.join(this.tempDir, repoInfo.fullName.replace('/', '-'));
99
+
100
+ // Create temp directory
101
+ if (!fs.existsSync(this.tempDir)) {
102
+ fs.mkdirSync(this.tempDir, { recursive: true });
103
+ }
104
+
105
+ // Remove existing clone if present
106
+ if (fs.existsSync(repoPath)) {
107
+ if (this.verbose) {
108
+ console.log(`♻️ Removing existing clone: ${repoPath}`);
109
+ }
110
+ fs.rmSync(repoPath, { recursive: true, force: true });
111
+ }
112
+
113
+ // Clone repository
114
+ if (this.verbose) {
115
+ console.log(`📥 Cloning ${repoInfo.fullName}...`);
116
+ }
117
+
118
+ const cloneCommand = shallow
119
+ ? `git clone --depth ${depth} --single-branch --branch ${repoInfo.branch} ${repoInfo.cloneUrl} "${repoPath}"`
120
+ : `git clone ${repoInfo.cloneUrl} "${repoPath}"`;
121
+
122
+ try {
123
+ execSync(cloneCommand, {
124
+ stdio: this.verbose ? 'inherit' : 'pipe'
125
+ });
126
+
127
+ if (this.verbose) {
128
+ console.log(`✅ Repository cloned to: ${repoPath}`);
129
+ }
130
+
131
+ return repoPath;
132
+ } catch (error) {
133
+ throw new Error(`Failed to clone repository: ${error.message}`);
134
+ }
135
+ }
136
+
137
+ /**
138
+ * Generate GitIngest digest from GitHub URL
139
+ */
140
+ async generateFromGitHub(url, options = {}) {
141
+ const {
142
+ outputFile,
143
+ cleanup = true,
144
+ shallow = true,
145
+ analyzerOptions = {}
146
+ } = options;
147
+
148
+ // Parse GitHub URL
149
+ const repoInfo = this.parseGitHubURL(url);
150
+ console.log(`\n📦 Processing GitHub Repository: ${repoInfo.fullName}`);
151
+ console.log(` Branch: ${repoInfo.branch}`);
152
+
153
+ // Clone repository
154
+ console.log(`\n📥 Cloning repository...`);
155
+ const repoPath = this.cloneRepository(repoInfo, { shallow });
156
+
157
+ // Generate GitIngest digest
158
+ console.log(`\n🔍 Analyzing repository...`);
159
+
160
+ const analyzer = new TokenCalculator(repoPath, {
161
+ verbose: this.verbose,
162
+ ...analyzerOptions
163
+ });
164
+
165
+ // Run analysis
166
+ const results = analyzer.analyze();
167
+
168
+ // Generate digest
169
+ const formatter = new GitIngestFormatter(
170
+ repoPath,
171
+ analyzer.stats,
172
+ results,
173
+ options.formatterOptions || {}
174
+ );
175
+
176
+ const digest = formatter.generateDigest();
177
+
178
+ // Determine output file
179
+ const outputFileName = outputFile ||
180
+ `${repoInfo.fullName.replace('/', '-')}-gitingest-${Date.now().toString(36)}.txt`;
181
+
182
+ const outputPath = path.isAbsolute(outputFileName)
183
+ ? outputFileName
184
+ : path.join(this.outputDir, outputFileName);
185
+
186
+ // Ensure output directory exists
187
+ const outputDirPath = path.dirname(outputPath);
188
+ if (!fs.existsSync(outputDirPath)) {
189
+ fs.mkdirSync(outputDirPath, { recursive: true });
190
+ }
191
+
192
+ // Save digest
193
+ fs.writeFileSync(outputPath, digest, 'utf8');
194
+
195
+ const stats = {
196
+ repository: repoInfo.fullName,
197
+ branch: repoInfo.branch,
198
+ url: repoInfo.originalUrl,
199
+ localPath: repoPath,
200
+ outputFile: outputPath,
201
+ digestSize: digest.length,
202
+ digestSizeKB: (digest.length / 1024).toFixed(2),
203
+ files: analyzer.stats.totalFiles,
204
+ tokens: analyzer.stats.totalTokens,
205
+ lines: analyzer.stats.totalLines
206
+ };
207
+
208
+ // Cleanup temp directory if requested
209
+ if (cleanup) {
210
+ console.log(`\n🧹 Cleaning up temporary files...`);
211
+ fs.rmSync(repoPath, { recursive: true, force: true });
212
+ }
213
+
214
+ console.log(`\n✅ GitIngest digest generated!`);
215
+ console.log(` Output: ${outputPath}`);
216
+ console.log(` Size: ${stats.digestSizeKB} KB`);
217
+ console.log(` Files: ${stats.files}`);
218
+ console.log(` Tokens: ${stats.tokens.toLocaleString()}`);
219
+
220
+ return stats;
221
+ }
222
+
223
+ /**
224
+ * Fetch file from GitHub raw URL (without cloning)
225
+ */
226
+ async fetchFileFromGitHub(url) {
227
+ return new Promise((resolve, reject) => {
228
+ https.get(url, (res) => {
229
+ let data = '';
230
+
231
+ res.on('data', (chunk) => {
232
+ data += chunk;
233
+ });
234
+
235
+ res.on('end', () => {
236
+ if (res.statusCode === 200) {
237
+ resolve(data);
238
+ } else {
239
+ reject(new Error(`HTTP ${res.statusCode}: ${data}`));
240
+ }
241
+ });
242
+ }).on('error', reject);
243
+ });
244
+ }
245
+
246
+ /**
247
+ * Get repository info from GitHub API
248
+ */
249
+ async getRepositoryInfo(repoInfo) {
250
+ return new Promise((resolve, reject) => {
251
+ const options = {
252
+ headers: {
253
+ 'User-Agent': 'ctxman-git-utils'
254
+ }
255
+ };
256
+
257
+ https.get(repoInfo.apiUrl, options, (res) => {
258
+ let data = '';
259
+
260
+ res.on('data', (chunk) => {
261
+ data += chunk;
262
+ });
263
+
264
+ res.on('end', () => {
265
+ try {
266
+ const repo = JSON.parse(data);
267
+ resolve({
268
+ name: repo.name,
269
+ fullName: repo.full_name,
270
+ description: repo.description,
271
+ stars: repo.stargazers_count,
272
+ forks: repo.forks_count,
273
+ defaultBranch: repo.default_branch,
274
+ size: repo.size,
275
+ language: repo.language,
276
+ updatedAt: repo.updated_at
277
+ });
278
+ } catch (error) {
279
+ reject(new Error(`Failed to parse GitHub API response: ${error.message}`));
280
+ }
281
+ });
282
+ }).on('error', reject);
283
+ });
284
+ }
285
+
286
+ /**
287
+ * Cleanup temp directory
288
+ */
289
+ cleanupTemp() {
290
+ if (fs.existsSync(this.tempDir)) {
291
+ fs.rmSync(this.tempDir, { recursive: true, force: true });
292
+ if (this.verbose) {
293
+ console.log(`🧹 Cleaned up: ${this.tempDir}`);
294
+ }
295
+ }
296
+ }
297
+
298
+ /**
299
+ * List cached repositories
300
+ */
301
+ listCachedRepos() {
302
+ if (!fs.existsSync(this.tempDir)) {
303
+ return [];
304
+ }
305
+
306
+ return fs.readdirSync(this.tempDir)
307
+ .filter(name => fs.statSync(path.join(this.tempDir, name)).isDirectory())
308
+ .map(name => ({
309
+ name,
310
+ path: path.join(this.tempDir, name),
311
+ size: this.getDirectorySize(path.join(this.tempDir, name))
312
+ }));
313
+ }
314
+
315
+ /**
316
+ * Get directory size recursively
317
+ */
318
+ getDirectorySize(dir) {
319
+ let size = 0;
320
+
321
+ const files = fs.readdirSync(dir);
322
+ for (const file of files) {
323
+ const filePath = path.join(dir, file);
324
+ const stats = fs.statSync(filePath);
325
+
326
+ if (stats.isDirectory()) {
327
+ size += this.getDirectorySize(filePath);
328
+ } else {
329
+ size += stats.size;
330
+ }
331
+ }
332
+
333
+ return size;
334
+ }
335
+ }
336
+
337
+ export default GitUtils;