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.
- package/.ctxman/custom-profiles.example.json +20 -0
- package/.ctxman/llm-profiles.json +131 -0
- package/.ctxman/logs/ctxman-2026-07-02.log +156 -0
- package/.ctxman/self-knowledge/documents.lance/_transactions/0-b3dd1558-f95b-44b9-a154-18e8c330ae7a.txn +0 -0
- package/.ctxman/self-knowledge/documents.lance/_transactions/1-44bff6d4-e552-4010-a769-e09ad822698d.txn +0 -0
- package/.ctxman/self-knowledge/documents.lance/_transactions/10-37260794-5947-4ecb-b4c8-d732f58f40f1.txn +0 -0
- package/.ctxman/self-knowledge/documents.lance/_transactions/11-0d52a883-5518-463c-9b6d-986c3af89dfd.txn +0 -0
- package/.ctxman/self-knowledge/documents.lance/_transactions/12-ae354646-52f2-49c9-90fb-2e6ed8932663.txn +0 -0
- package/.ctxman/self-knowledge/documents.lance/_transactions/13-5d7d5e57-27da-498b-b734-091b9d38da13.txn +0 -0
- package/.ctxman/self-knowledge/documents.lance/_transactions/14-558ce928-810b-4252-a364-fdd1ee6f8a25.txn +0 -0
- package/.ctxman/self-knowledge/documents.lance/_transactions/15-27e3a58f-8b64-43b8-a9f4-e3369446968b.txn +0 -0
- package/.ctxman/self-knowledge/documents.lance/_transactions/16-a110c390-e5c8-4464-b976-bc7f5195fa15.txn +0 -0
- package/.ctxman/self-knowledge/documents.lance/_transactions/17-97513225-53de-4216-b61a-6337bd0cc4ad.txn +0 -0
- package/.ctxman/self-knowledge/documents.lance/_transactions/18-5ba450f0-c527-459c-bf4f-6d79b5e4285f.txn +0 -0
- package/.ctxman/self-knowledge/documents.lance/_transactions/19-600d3a63-5225-49e9-b8b7-30e037b564ab.txn +0 -0
- package/.ctxman/self-knowledge/documents.lance/_transactions/2-48a61b4e-92cf-4342-8376-914c1b99a9f9.txn +0 -0
- package/.ctxman/self-knowledge/documents.lance/_transactions/20-121dd912-5410-410e-84ff-82c7a23604a0.txn +0 -0
- package/.ctxman/self-knowledge/documents.lance/_transactions/3-bc6844ab-7ea3-4bb3-9ee1-538ce5664a9c.txn +0 -0
- package/.ctxman/self-knowledge/documents.lance/_transactions/4-d5e1754f-dbb5-454e-a9be-2a7da8d7bcf9.txn +0 -0
- package/.ctxman/self-knowledge/documents.lance/_transactions/5-4fae93df-0568-4b6f-8acf-8bef4bba6047.txn +0 -0
- package/.ctxman/self-knowledge/documents.lance/_transactions/6-4b1b8b87-068e-49a3-ba2b-d8dae628b252.txn +0 -0
- package/.ctxman/self-knowledge/documents.lance/_transactions/7-63d040fb-d2fa-4c0f-bed0-53411918a7b0.txn +0 -0
- package/.ctxman/self-knowledge/documents.lance/_transactions/8-c5628cf6-264c-420f-aab8-bc96a9083591.txn +0 -0
- package/.ctxman/self-knowledge/documents.lance/_transactions/9-c94570aa-5e53-4fcb-971f-cecc1d1d5492.txn +0 -0
- package/.ctxman/self-knowledge/documents.lance/_versions/1.manifest +0 -0
- package/.ctxman/self-knowledge/documents.lance/_versions/10.manifest +0 -0
- package/.ctxman/self-knowledge/documents.lance/_versions/11.manifest +0 -0
- package/.ctxman/self-knowledge/documents.lance/_versions/12.manifest +0 -0
- package/.ctxman/self-knowledge/documents.lance/_versions/13.manifest +0 -0
- package/.ctxman/self-knowledge/documents.lance/_versions/14.manifest +0 -0
- package/.ctxman/self-knowledge/documents.lance/_versions/15.manifest +0 -0
- package/.ctxman/self-knowledge/documents.lance/_versions/16.manifest +0 -0
- package/.ctxman/self-knowledge/documents.lance/_versions/17.manifest +0 -0
- package/.ctxman/self-knowledge/documents.lance/_versions/18.manifest +0 -0
- package/.ctxman/self-knowledge/documents.lance/_versions/19.manifest +0 -0
- package/.ctxman/self-knowledge/documents.lance/_versions/2.manifest +0 -0
- package/.ctxman/self-knowledge/documents.lance/_versions/20.manifest +0 -0
- package/.ctxman/self-knowledge/documents.lance/_versions/21.manifest +0 -0
- package/.ctxman/self-knowledge/documents.lance/_versions/3.manifest +0 -0
- package/.ctxman/self-knowledge/documents.lance/_versions/4.manifest +0 -0
- package/.ctxman/self-knowledge/documents.lance/_versions/5.manifest +0 -0
- package/.ctxman/self-knowledge/documents.lance/_versions/6.manifest +0 -0
- package/.ctxman/self-knowledge/documents.lance/_versions/7.manifest +0 -0
- package/.ctxman/self-knowledge/documents.lance/_versions/8.manifest +0 -0
- package/.ctxman/self-knowledge/documents.lance/_versions/9.manifest +0 -0
- package/.ctxman/self-knowledge/documents.lance/data/000010010011011101000010de8621405d9d5cb06540dbf02c.lance +0 -0
- package/.ctxman/self-knowledge/documents.lance/data/0000101001011100011000003fb62a4aa8978974075c144f1c.lance +0 -0
- package/.ctxman/self-knowledge/documents.lance/data/000011010000100111001011c6cc824d628f3bb1f4e04cd5d0.lance +0 -0
- package/.ctxman/self-knowledge/documents.lance/data/001000001000011001011100d8f74349ada090b332cd1df669.lance +0 -0
- package/.ctxman/self-knowledge/documents.lance/data/01000001111010001000010045ab44467bb1e7e4d8ae06bd58.lance +0 -0
- package/.ctxman/self-knowledge/documents.lance/data/01000101101110111110011131ef904241a89e1ffb6ddaf8e3.lance +0 -0
- package/.ctxman/self-knowledge/documents.lance/data/010010110000010000011010fbfcc145c6a8a827b28057a07e.lance +0 -0
- package/.ctxman/self-knowledge/documents.lance/data/1000011000101010001110003fb4f045c3835982efa1405324.lance +0 -0
- package/.ctxman/self-knowledge/documents.lance/data/100111011101110111101011f4396b410fa06af3b028d204e6.lance +0 -0
- package/.ctxman/self-knowledge/documents.lance/data/100111100111101000001100596b174e00995d6cc2969cc8dc.lance +0 -0
- package/.ctxman/self-knowledge/documents.lance/data/1010001100011000110010102ea16b484d887c7c9725feffd6.lance +0 -0
- package/.ctxman/self-knowledge/documents.lance/data/101101100010101111110111c5230944d9b3af7301b6392dda.lance +0 -0
- package/.ctxman/self-knowledge/documents.lance/data/1011011100100111111110105892bf41b5a530fedd7cf516f1.lance +0 -0
- package/.ctxman/self-knowledge/documents.lance/data/10111110000010001011110164e8774742bade87b483c10f36.lance +0 -0
- package/.ctxman/self-knowledge/documents.lance/data/1100000100010110001110103ffaca40a2a42c85067ebaf9ae.lance +0 -0
- package/.ctxman/self-knowledge/documents.lance/data/110000010001110110101110664bde4c76a78dfc14b05e83b8.lance +0 -0
- package/.ctxman/self-knowledge/documents.lance/data/11001100111000010100110050645c4281b1f35b2022234f7f.lance +0 -0
- package/.ctxman/self-knowledge/documents.lance/data/1101111011001100110111017214924a368f5719eab2246619.lance +0 -0
- package/.ctxman/self-knowledge/documents.lance/data/1110000111001010001000002150c24dcab653d39d07032e63.lance +0 -0
- package/.ctxman/self-knowledge/documents.lance/data/1110100000000100000011014d20534967a18b5c87721659ff.lance +0 -0
- package/.ctxman/self-knowledge/documents.lance/data/11110101111010000101011135a0114ef2b35dc6f1297a3381.lance +0 -0
- package/.ctxman/wizard-profiles/code-review/.contextignore +41 -0
- package/.ctxman/wizard-profiles/code-review/.contextinclude +30 -0
- package/.ctxman/wizard-profiles/code-review/.methodignore +26 -0
- package/.ctxman/wizard-profiles/code-review/.methodinclude +47 -0
- package/.ctxman/wizard-profiles/code-review/profile.json +26 -0
- package/.ctxman/wizard-profiles/documentation/.contextignore +21 -0
- package/.ctxman/wizard-profiles/documentation/.contextinclude +24 -0
- package/.ctxman/wizard-profiles/documentation/.methodignore +19 -0
- package/.ctxman/wizard-profiles/documentation/.methodinclude +44 -0
- package/.ctxman/wizard-profiles/documentation/profile.json +27 -0
- package/.ctxman/wizard-profiles/full/.contextignore +23 -0
- package/.ctxman/wizard-profiles/full/.contextinclude +38 -0
- package/.ctxman/wizard-profiles/full/.methodignore +5 -0
- package/.ctxman/wizard-profiles/full/.methodinclude +5 -0
- package/.ctxman/wizard-profiles/full/profile.json +28 -0
- package/.ctxman/wizard-profiles/llm-explain/.contextignore +25 -0
- package/.ctxman/wizard-profiles/llm-explain/.contextinclude +33 -0
- package/.ctxman/wizard-profiles/llm-explain/.methodignore +19 -0
- package/.ctxman/wizard-profiles/llm-explain/.methodinclude +48 -0
- package/.ctxman/wizard-profiles/llm-explain/profile.json +27 -0
- package/.ctxman/wizard-profiles/minimal/.contextignore +27 -0
- package/.ctxman/wizard-profiles/minimal/.contextinclude +14 -0
- package/.ctxman/wizard-profiles/minimal/.methodignore +22 -0
- package/.ctxman/wizard-profiles/minimal/.methodinclude +18 -0
- package/.ctxman/wizard-profiles/minimal/profile.json +26 -0
- package/.ctxman/wizard-profiles/security-audit/.contextignore +36 -0
- package/.ctxman/wizard-profiles/security-audit/.contextinclude +37 -0
- package/.ctxman/wizard-profiles/security-audit/.methodignore +29 -0
- package/.ctxman/wizard-profiles/security-audit/.methodinclude +60 -0
- package/.ctxman/wizard-profiles/security-audit/profile.json +34 -0
- package/README.md +1167 -0
- package/bin/cli.js +650 -0
- package/bin/cm-ask.js +95 -0
- package/bin/cm-gitingest.js +134 -0
- package/bin/cm-update.js +175 -0
- package/bin/mcp-server.js +31 -0
- package/ctxman.js +236 -0
- package/examples/README.md +298 -0
- package/examples/custom-llm-profiles.example.json +19 -0
- package/examples/wizard-profiles/code-review/.contextignore +41 -0
- package/examples/wizard-profiles/code-review/.contextinclude +30 -0
- package/examples/wizard-profiles/code-review/.methodignore +26 -0
- package/examples/wizard-profiles/code-review/.methodinclude +47 -0
- package/examples/wizard-profiles/code-review/profile.json +26 -0
- package/examples/wizard-profiles/documentation/.contextignore +21 -0
- package/examples/wizard-profiles/documentation/.contextinclude +24 -0
- package/examples/wizard-profiles/documentation/.methodignore +19 -0
- package/examples/wizard-profiles/documentation/.methodinclude +44 -0
- package/examples/wizard-profiles/documentation/profile.json +27 -0
- package/examples/wizard-profiles/full/.contextignore +23 -0
- package/examples/wizard-profiles/full/.contextinclude +38 -0
- package/examples/wizard-profiles/full/.methodignore +5 -0
- package/examples/wizard-profiles/full/.methodinclude +5 -0
- package/examples/wizard-profiles/full/profile.json +28 -0
- package/examples/wizard-profiles/llm-explain/.contextignore +25 -0
- package/examples/wizard-profiles/llm-explain/.contextinclude +33 -0
- package/examples/wizard-profiles/llm-explain/.methodignore +19 -0
- package/examples/wizard-profiles/llm-explain/.methodinclude +48 -0
- package/examples/wizard-profiles/llm-explain/profile.json +27 -0
- package/examples/wizard-profiles/minimal/.contextignore +27 -0
- package/examples/wizard-profiles/minimal/.contextinclude +14 -0
- package/examples/wizard-profiles/minimal/.methodignore +22 -0
- package/examples/wizard-profiles/minimal/.methodinclude +18 -0
- package/examples/wizard-profiles/minimal/profile.json +26 -0
- package/examples/wizard-profiles/security-audit/.contextignore +36 -0
- package/examples/wizard-profiles/security-audit/.contextinclude +37 -0
- package/examples/wizard-profiles/security-audit/.methodignore +29 -0
- package/examples/wizard-profiles/security-audit/.methodinclude +60 -0
- package/examples/wizard-profiles/security-audit/profile.json +34 -0
- package/index.js +68 -0
- package/lib/agents/pr-failure-agent.js +201 -0
- package/lib/analyzers/go-method-analyzer.js +126 -0
- package/lib/analyzers/method-analyzer.js +457 -0
- package/lib/analyzers/token-calculator.js +577 -0
- package/lib/api/mcp/prompts.js +319 -0
- package/lib/api/mcp/resources.js +269 -0
- package/lib/api/mcp/server.js +339 -0
- package/lib/api/mcp/transports/HttpSseTransport.js +63 -0
- package/lib/api/rest/server.js +405 -0
- package/lib/cache/CacheManager.js +388 -0
- package/lib/core/Analyzer.js +180 -0
- package/lib/core/ContextBuilder.js +325 -0
- package/lib/core/Reporter.js +270 -0
- package/lib/core/Scanner.js +129 -0
- package/lib/formatters/format-registry.js +419 -0
- package/lib/formatters/gitingest-formatter.js +746 -0
- package/lib/formatters/toon-formatter-v1.3.js +731 -0
- package/lib/formatters/toon-formatter.js +291 -0
- package/lib/integrations/git/BlameTracker.js +142 -0
- package/lib/integrations/git/DiffAnalyzer.js +260 -0
- package/lib/integrations/git/GitClient.js +336 -0
- package/lib/parsers/gitignore-parser.js +127 -0
- package/lib/parsers/method-filter-parser.js +50 -0
- package/lib/plugins/ExporterPlugin.js +102 -0
- package/lib/plugins/LanguagePlugin.js +100 -0
- package/lib/plugins/PluginManager.js +302 -0
- package/lib/rag/DataSourcePlugin.js +31 -0
- package/lib/rag/EmbeddingProvider.js +75 -0
- package/lib/rag/Indexer.js +83 -0
- package/lib/rag/VectorStoreFactory.js +24 -0
- package/lib/rag/sources/FileDataSource.js +70 -0
- package/lib/rag/stores/BaseVectorStore.js +43 -0
- package/lib/rag/stores/ChromaDBStore.js +85 -0
- package/lib/rag/stores/LanceDBStore.js +109 -0
- package/lib/rag/stores/PineconeStore.js +74 -0
- package/lib/ui/dashboard.js +109 -0
- package/lib/ui/index.js +15 -0
- package/lib/ui/progress-bar.js +110 -0
- package/lib/ui/select-input.js +51 -0
- package/lib/ui/wizard.js +359 -0
- package/lib/utils/clipboard-utils.js +66 -0
- package/lib/utils/config-utils.js +94 -0
- package/lib/utils/error-handler.js +145 -0
- package/lib/utils/file-utils.js +89 -0
- package/lib/utils/format-converter.js +325 -0
- package/lib/utils/git-utils.js +337 -0
- package/lib/utils/llm-detector.js +409 -0
- package/lib/utils/logger.js +278 -0
- package/lib/utils/token-utils.js +200 -0
- package/lib/utils/tokenizer-adapter.js +454 -0
- package/lib/utils/updater.js +565 -0
- package/lib/watch/FileWatcher.js +208 -0
- package/lib/watch/IncrementalAnalyzer.js +206 -0
- 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;
|