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,565 @@
1
+ /**
2
+ * Auto-Update System
3
+ * Checks for updates and manages version channels (stable/insider)
4
+ * v2.3.6+
5
+ */
6
+
7
+ import fs from 'fs';
8
+ import path from 'path';
9
+ import https from 'https';
10
+ import { execSync } from 'child_process';
11
+
12
+ class Updater {
13
+ constructor(options = {}) {
14
+ this.currentVersion = this.getCurrentVersion();
15
+ this.channel = options.channel || 'stable'; // stable or insider
16
+ this.autoUpdate = options.autoUpdate !== false;
17
+ this.checkInterval = options.checkInterval || 86400000; // 24 hours
18
+ this.configDir = options.configDir || path.join(process.env.HOME || process.env.USERPROFILE, '.ctxman');
19
+ this.updateCacheFile = path.join(this.configDir, 'update-cache.json');
20
+
21
+ // Update endpoints (placeholder)
22
+ this.endpoints = {
23
+ stable: 'https://api.github.com/repos/hakkisagdic/ctxman/releases/latest',
24
+ insider: 'https://api.github.com/repos/hakkisagdic/ctxman/releases', // Get all releases
25
+ version: 'https://raw.githubusercontent.com/hakkisagdic/ctxman/main/package.json'
26
+ };
27
+ }
28
+
29
+ /**
30
+ * Get current version from package.json
31
+ */
32
+ getCurrentVersion() {
33
+ try {
34
+ const packagePath = path.join(__dirname, '../../package.json');
35
+ const pkg = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
36
+ return pkg.version;
37
+ } catch (error) {
38
+ return '0.0.0';
39
+ }
40
+ }
41
+
42
+ /**
43
+ * Fetch JSON from URL
44
+ */
45
+ async fetchJSON(url) {
46
+ return new Promise((resolve, reject) => {
47
+ https.get(url, {
48
+ headers: {
49
+ 'User-Agent': 'ctxman-updater'
50
+ }
51
+ }, (res) => {
52
+ let data = '';
53
+
54
+ res.on('data', (chunk) => {
55
+ data += chunk;
56
+ });
57
+
58
+ res.on('end', () => {
59
+ try {
60
+ resolve(JSON.parse(data));
61
+ } catch (error) {
62
+ reject(new Error('Failed to parse JSON response'));
63
+ }
64
+ });
65
+ }).on('error', reject);
66
+ });
67
+ }
68
+
69
+ /**
70
+ * Compare versions (returns: -1 if v1 < v2, 0 if equal, 1 if v1 > v2)
71
+ */
72
+ compareVersions(v1, v2) {
73
+ const parts1 = v1.split('.').map(Number);
74
+ const parts2 = v2.split('.').map(Number);
75
+
76
+ for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
77
+ const p1 = parts1[i] || 0;
78
+ const p2 = parts2[i] || 0;
79
+
80
+ if (p1 < p2) return -1;
81
+ if (p1 > p2) return 1;
82
+ }
83
+
84
+ return 0;
85
+ }
86
+
87
+ /**
88
+ * Check for updates
89
+ * @returns {Promise<Object>} Update information
90
+ */
91
+ async checkForUpdates() {
92
+ try {
93
+ // PLACEHOLDER: In production, this would fetch from actual API
94
+ console.log('🔍 Checking for updates...');
95
+ console.log(` Current version: ${this.currentVersion}`);
96
+ console.log(` Channel: ${this.channel}`);
97
+
98
+ // Simulate API call (placeholder)
99
+ const latestVersion = await this.getLatestVersion();
100
+
101
+ if (!latestVersion) {
102
+ return {
103
+ available: false,
104
+ message: 'Could not check for updates'
105
+ };
106
+ }
107
+
108
+ const comparison = this.compareVersions(latestVersion.version, this.currentVersion);
109
+
110
+ if (comparison > 0) {
111
+ return {
112
+ available: true,
113
+ currentVersion: this.currentVersion,
114
+ latestVersion: latestVersion.version,
115
+ channel: this.channel,
116
+ releaseNotes: latestVersion.releaseNotes || '',
117
+ downloadUrl: latestVersion.downloadUrl || '',
118
+ publishedAt: latestVersion.publishedAt || new Date().toISOString()
119
+ };
120
+ }
121
+
122
+ return {
123
+ available: false,
124
+ currentVersion: this.currentVersion,
125
+ latestVersion: latestVersion.version,
126
+ message: 'You are using the latest version'
127
+ };
128
+ } catch (error) {
129
+ return {
130
+ available: false,
131
+ error: error.message,
132
+ message: 'Failed to check for updates'
133
+ };
134
+ }
135
+ }
136
+
137
+ /**
138
+ * Get latest version info from GitHub API
139
+ */
140
+ async getLatestVersion() {
141
+ try {
142
+ if (this.channel === 'stable') {
143
+ // Stable channel - get latest stable release
144
+ const release = await this.fetchJSON(this.endpoints.stable);
145
+
146
+ return {
147
+ version: release.tag_name.replace(/^v/, ''), // Remove 'v' prefix
148
+ releaseNotes: release.body || 'No release notes available',
149
+ downloadUrl: release.html_url,
150
+ publishedAt: release.published_at,
151
+ assets: release.assets || []
152
+ };
153
+ } else {
154
+ // Insider channel - get latest pre-release
155
+ const releases = await this.fetchJSON(this.endpoints.insider);
156
+
157
+ // Find latest pre-release
158
+ const preRelease = releases.find(r => r.prerelease === true);
159
+
160
+ if (preRelease) {
161
+ return {
162
+ version: preRelease.tag_name.replace(/^v/, ''),
163
+ releaseNotes: preRelease.body || 'No release notes available',
164
+ downloadUrl: preRelease.html_url,
165
+ publishedAt: preRelease.published_at,
166
+ assets: preRelease.assets || []
167
+ };
168
+ }
169
+
170
+ // Fallback to latest stable if no pre-release
171
+ const latestRelease = releases[0];
172
+ return {
173
+ version: latestRelease.tag_name.replace(/^v/, ''),
174
+ releaseNotes: latestRelease.body || 'No release notes available',
175
+ downloadUrl: latestRelease.html_url,
176
+ publishedAt: latestRelease.published_at,
177
+ assets: latestRelease.assets || []
178
+ };
179
+ }
180
+ } catch (error) {
181
+ console.error('Failed to fetch latest version:', error.message);
182
+ return null;
183
+ }
184
+ }
185
+
186
+ /**
187
+ * Download and install update
188
+ */
189
+ async installUpdate(updateInfo) {
190
+
191
+ try {
192
+ console.log('📦 Installing update...');
193
+ console.log(` From: ${updateInfo.currentVersion}`);
194
+ console.log(` To: ${updateInfo.latestVersion}`);
195
+ console.log();
196
+
197
+ // Step 1: Detect installation type
198
+ const installType = this.detectInstallationType();
199
+ console.log(` Installation type: ${installType}`);
200
+
201
+ // Step 2: Create backup
202
+ console.log(' Creating backup...');
203
+ const backupPath = this.createBackup();
204
+ console.log(` ✓ Backup created: ${backupPath}`);
205
+
206
+ // Step 3: Install based on type
207
+ if (installType === 'global') {
208
+ console.log(' Updating global installation...');
209
+ const result = execSync('npm update -g ctxman', {
210
+ encoding: 'utf8',
211
+ stdio: 'pipe'
212
+ });
213
+ console.log(' ✓ Global update complete');
214
+ } else if (installType === 'local') {
215
+ console.log(' Updating local installation...');
216
+ const result = execSync('npm update ctxman', {
217
+ encoding: 'utf8',
218
+ stdio: 'pipe',
219
+ cwd: this.getInstallDir()
220
+ });
221
+ console.log(' ✓ Local update complete');
222
+ } else {
223
+ // Source installation - show manual instructions
224
+ console.log('\n⚠️ Source-based installation detected.');
225
+ console.log(' Please update manually:\n');
226
+ console.log(' 1. git pull origin main');
227
+ console.log(' 2. npm install');
228
+ console.log(` 3. Or visit: ${updateInfo.downloadUrl}\n`);
229
+
230
+ return {
231
+ success: false,
232
+ manualUpdateRequired: true,
233
+ instructions: 'Manual update required for source installation'
234
+ };
235
+ }
236
+
237
+ // Step 4: Verify installation
238
+ console.log(' Verifying update...');
239
+ const newVersion = this.getCurrentVersion();
240
+
241
+ if (this.compareVersions(newVersion, updateInfo.latestVersion) === 0) {
242
+ console.log(' ✓ Update verified');
243
+
244
+ // Update config
245
+ this.saveConfig({
246
+ channel: this.channel,
247
+ autoUpdate: this.autoUpdate
248
+ });
249
+
250
+ return {
251
+ success: true,
252
+ oldVersion: updateInfo.currentVersion,
253
+ newVersion: newVersion,
254
+ backupPath
255
+ };
256
+ } else {
257
+ console.log(' ⚠️ Version mismatch after update');
258
+ console.log(` Expected: ${updateInfo.latestVersion}, Got: ${newVersion}`);
259
+
260
+ return {
261
+ success: false,
262
+ error: 'Version verification failed'
263
+ };
264
+ }
265
+ } catch (error) {
266
+ console.error(' ✗ Update failed:', error.message);
267
+
268
+ return {
269
+ success: false,
270
+ error: error.message
271
+ };
272
+ }
273
+ }
274
+
275
+ /**
276
+ * Detect installation type (global, local, or source)
277
+ */
278
+ detectInstallationType() {
279
+ try {
280
+ // Check if installed globally
281
+ const globalList = execSync('npm list -g ctxman --depth=0', {
282
+ encoding: 'utf8',
283
+ stdio: 'pipe'
284
+ });
285
+
286
+ if (globalList.includes('ctxman')) {
287
+ return 'global';
288
+ }
289
+ } catch (error) {
290
+ // Not global
291
+ }
292
+
293
+ // Check if in node_modules (local npm install)
294
+ const nodeModulesPath = path.join(process.cwd(), 'node_modules', '@hakkisagdic', 'ctxman');
295
+ if (fs.existsSync(nodeModulesPath)) {
296
+ return 'local';
297
+ }
298
+
299
+ // Check if running from source (has .git directory)
300
+ const gitPath = path.join(__dirname, '../../.git');
301
+ if (fs.existsSync(gitPath)) {
302
+ return 'source';
303
+ }
304
+
305
+ return 'unknown';
306
+ }
307
+
308
+ /**
309
+ * Get installation directory
310
+ */
311
+ getInstallDir() {
312
+ const installType = this.detectInstallationType();
313
+
314
+ if (installType === 'global') {
315
+ try {
316
+ const globalRoot = execSync('npm root -g', { encoding: 'utf8' }).trim();
317
+ return path.join(globalRoot, 'ctxman');
318
+ } catch (error) {
319
+ return null;
320
+ }
321
+ }
322
+
323
+ if (installType === 'local') {
324
+ return path.join(process.cwd(), 'node_modules', '@hakkisagdic', 'ctxman');
325
+ }
326
+
327
+ return path.join(__dirname, '../..');
328
+ }
329
+
330
+ /**
331
+ * Create backup before update
332
+ */
333
+ createBackup() {
334
+
335
+ const installDir = this.getInstallDir();
336
+ const backupDir = path.join(this.configDir, 'backups');
337
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
338
+ const backupPath = path.join(backupDir, `backup-${this.currentVersion}-${timestamp}`);
339
+
340
+ if (!fs.existsSync(backupDir)) {
341
+ fs.mkdirSync(backupDir, { recursive: true });
342
+ }
343
+
344
+ // Create backup info file
345
+ const backupInfo = {
346
+ version: this.currentVersion,
347
+ timestamp: new Date().toISOString(),
348
+ installType: this.detectInstallationType(),
349
+ installDir: installDir
350
+ };
351
+
352
+ fs.writeFileSync(
353
+ path.join(backupPath, 'backup-info.json'),
354
+ JSON.stringify(backupInfo, null, 2),
355
+ 'utf8'
356
+ );
357
+
358
+ return backupPath;
359
+ }
360
+
361
+ /**
362
+ * Rollback to previous version
363
+ */
364
+ async rollback() {
365
+
366
+ try {
367
+ console.log('🔄 Rolling back to previous version...');
368
+
369
+ const backupDir = path.join(this.configDir, 'backups');
370
+ if (!fs.existsSync(backupDir)) {
371
+ throw new Error('No backups found');
372
+ }
373
+
374
+ // Get latest backup
375
+ const backups = fs.readdirSync(backupDir)
376
+ .filter(name => name.startsWith('backup-'))
377
+ .sort()
378
+ .reverse();
379
+
380
+ if (backups.length === 0) {
381
+ throw new Error('No backups available');
382
+ }
383
+
384
+ const latestBackup = backups[0];
385
+ const backupPath = path.join(backupDir, latestBackup);
386
+ const backupInfoPath = path.join(backupPath, 'backup-info.json');
387
+
388
+ if (!fs.existsSync(backupInfoPath)) {
389
+ throw new Error('Invalid backup: missing backup-info.json');
390
+ }
391
+
392
+ const backupInfo = JSON.parse(fs.readFileSync(backupInfoPath, 'utf8'));
393
+
394
+ console.log(` Restoring version: ${backupInfo.version}`);
395
+ console.log(` Backup date: ${backupInfo.timestamp}`);
396
+
397
+ // Install the backed up version
398
+ const installType = backupInfo.installType;
399
+
400
+ if (installType === 'global') {
401
+ execSync(`npm install -g ctxman@${backupInfo.version}`, {
402
+ stdio: 'inherit'
403
+ });
404
+ } else if (installType === 'local') {
405
+ execSync(`npm install ctxman@${backupInfo.version}`, {
406
+ stdio: 'inherit',
407
+ cwd: this.getInstallDir()
408
+ });
409
+ } else {
410
+ throw new Error('Cannot rollback source installation');
411
+ }
412
+
413
+ console.log('\n✅ Rollback successful!');
414
+ console.log(` Restored to version: ${backupInfo.version}`);
415
+
416
+ return {
417
+ success: true,
418
+ restoredVersion: backupInfo.version
419
+ };
420
+ } catch (error) {
421
+ console.error('✗ Rollback failed:', error.message);
422
+ return {
423
+ success: false,
424
+ error: error.message
425
+ };
426
+ }
427
+ }
428
+
429
+ /**
430
+ * Switch update channel
431
+ */
432
+ async switchChannel(newChannel) {
433
+ if (!['stable', 'insider'].includes(newChannel)) {
434
+ throw new Error('Invalid channel. Must be "stable" or "insider"');
435
+ }
436
+
437
+ this.channel = newChannel;
438
+
439
+ // Save to config
440
+ this.saveConfig({ channel: newChannel });
441
+
442
+ console.log(`✓ Switched to ${newChannel} channel`);
443
+ console.log(' Checking for updates on new channel...\n');
444
+
445
+ return await this.checkForUpdates();
446
+ }
447
+
448
+ /**
449
+ * Save updater config
450
+ */
451
+ saveConfig(config) {
452
+ try {
453
+ const configFile = path.join(this.configDir, 'config.json');
454
+ let existingConfig = {};
455
+
456
+ if (fs.existsSync(configFile)) {
457
+ existingConfig = JSON.parse(fs.readFileSync(configFile, 'utf8'));
458
+ }
459
+
460
+ const updatedConfig = {
461
+ ...existingConfig,
462
+ updateChannel: config.channel || this.channel,
463
+ autoUpdate: config.autoUpdate !== undefined ? config.autoUpdate : this.autoUpdate,
464
+ lastUpdateCheck: new Date().toISOString()
465
+ };
466
+
467
+ if (!fs.existsSync(this.configDir)) {
468
+ fs.mkdirSync(this.configDir, { recursive: true });
469
+ }
470
+
471
+ fs.writeFileSync(configFile, JSON.stringify(updatedConfig, null, 2), 'utf8');
472
+ } catch (error) {
473
+ console.error('Failed to save config:', error.message);
474
+ }
475
+ }
476
+
477
+ /**
478
+ * Get update cache
479
+ */
480
+ getUpdateCache() {
481
+ try {
482
+ if (fs.existsSync(this.updateCacheFile)) {
483
+ return JSON.parse(fs.readFileSync(this.updateCacheFile, 'utf8'));
484
+ }
485
+ } catch (error) {
486
+ // Return null if cache can't be read
487
+ }
488
+ return null;
489
+ }
490
+
491
+ /**
492
+ * Save update cache
493
+ */
494
+ saveUpdateCache(data) {
495
+ try {
496
+ if (!fs.existsSync(this.configDir)) {
497
+ fs.mkdirSync(this.configDir, { recursive: true });
498
+ }
499
+
500
+ fs.writeFileSync(this.updateCacheFile, JSON.stringify(data, null, 2), 'utf8');
501
+ } catch (error) {
502
+ // Fail silently
503
+ }
504
+ }
505
+
506
+ /**
507
+ * Should check for updates (based on interval)
508
+ */
509
+ shouldCheckForUpdates() {
510
+ const cache = this.getUpdateCache();
511
+ if (!cache || !cache.lastCheck) {
512
+ return true;
513
+ }
514
+
515
+ const lastCheck = new Date(cache.lastCheck).getTime();
516
+ const now = Date.now();
517
+
518
+ return (now - lastCheck) > this.checkInterval;
519
+ }
520
+
521
+ /**
522
+ * Check and notify about updates
523
+ */
524
+ async checkAndNotify() {
525
+ if (!this.shouldCheckForUpdates()) {
526
+ const cache = this.getUpdateCache();
527
+ if (cache && cache.available) {
528
+ this.showUpdateNotification(cache);
529
+ }
530
+ return cache;
531
+ }
532
+
533
+ const updateInfo = await this.checkForUpdates();
534
+
535
+ // Save to cache
536
+ this.saveUpdateCache({
537
+ ...updateInfo,
538
+ lastCheck: new Date().toISOString()
539
+ });
540
+
541
+ if (updateInfo.available) {
542
+ this.showUpdateNotification(updateInfo);
543
+ }
544
+
545
+ return updateInfo;
546
+ }
547
+
548
+ /**
549
+ * Show update notification
550
+ */
551
+ showUpdateNotification(updateInfo) {
552
+ console.log('\n╔════════════════════════════════════════════════════════╗');
553
+ console.log('║ 🎉 Update Available! ║');
554
+ console.log('╚════════════════════════════════════════════════════════╝\n');
555
+ console.log(` Current version: ${updateInfo.currentVersion}`);
556
+ console.log(` Latest version: ${updateInfo.latestVersion}`);
557
+ console.log(` Channel: ${updateInfo.channel}\n`);
558
+ console.log(' To update, run:');
559
+ console.log(' ctxman update\n');
560
+ console.log(' Or update manually:');
561
+ console.log(' npm update -g ctxman\n');
562
+ }
563
+ }
564
+
565
+ export default Updater;