ctxman 3.0.0 → 3.1.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/llm-profiles.json +1067 -39
- package/.ctxman/profiles/backend-team.json +13 -0
- package/.ctxman/profiles/default.json +12 -0
- package/.ctxman/profiles/devops-team.json +21 -0
- package/.ctxman/profiles/frontend-team.json +20 -0
- package/.ctxman/templates/bug-fix.json +17 -0
- package/.ctxman/templates/code-review.json +9 -0
- package/.ctxman/templates/documentation.json +9 -0
- package/.ctxman/templates/feature.json +9 -0
- package/.ctxman/templates/full-context.json +9 -0
- package/.ctxman/templates/refactoring.json +18 -0
- package/.ctxman/templates/testing.json +9 -0
- package/.ctxman/wizard-profiles/security-audit/profile.json +7 -1
- package/LICENSE +21 -0
- package/README.md +380 -136
- package/bin/cli.js +1880 -529
- package/bin/cm-ask.js +18 -13
- package/bin/cm-audit.js +124 -0
- package/bin/cm-count.js +151 -0
- package/bin/cm-gitingest.js +128 -115
- package/bin/cm-map.js +242 -0
- package/bin/cm-mcp.js +293 -0
- package/bin/cm-pack.js +405 -0
- package/bin/cm-policy.js +170 -0
- package/bin/cm-pr.js +248 -0
- package/bin/cm-search.js +178 -0
- package/bin/cm-update.js +128 -128
- package/bin/cm-why.js +197 -0
- package/bin/mcp-server.js +8 -24
- package/ctxman.js +237 -183
- package/examples/README.md +19 -4
- package/examples/github-actions/ctxman-check.yml +49 -0
- package/examples/wizard-profiles/security-audit/profile.json +7 -1
- package/index.d.ts +1422 -0
- package/index.js +55 -31
- package/lib/agents/pr-failure-agent.js +48 -45
- package/lib/analyzers/ai-suggester.js +451 -0
- package/lib/analyzers/dependency-scanner.js +505 -0
- package/lib/analyzers/duplicate-detector.js +264 -0
- package/lib/analyzers/go-method-analyzer.js +151 -110
- package/lib/analyzers/import-tracker.js +431 -0
- package/lib/analyzers/method-analyzer.js +1055 -434
- package/lib/analyzers/performance-dashboard.js +683 -0
- package/lib/analyzers/token-calculator.js +895 -504
- package/lib/analyzers/unused-exports-detector.js +336 -0
- package/lib/api/mcp/install.js +424 -0
- package/lib/api/mcp/prompts.js +281 -281
- package/lib/api/mcp/resources.js +300 -241
- package/lib/api/mcp/sandbox.js +89 -0
- package/lib/api/mcp/server.js +675 -312
- package/lib/api/mcp/start.js +125 -0
- package/lib/api/mcp/transports/HttpSseTransport.js +60 -52
- package/lib/api/rest/server.js +105 -28
- package/lib/cache/CacheManager.js +9 -17
- package/lib/core/Analyzer.js +16 -10
- package/lib/core/ContextBuilder.js +19 -28
- package/lib/core/Reporter.js +15 -14
- package/lib/core/Scanner.js +81 -12
- package/lib/count/index.js +100 -0
- package/lib/formatters/format-registry.js +394 -372
- package/lib/formatters/gitingest-formatter.js +656 -621
- package/lib/formatters/toon-formatter-v1.3.js +637 -632
- package/lib/formatters/toon-formatter.js +239 -240
- package/lib/integrations/git/BlameTracker.js +4 -4
- package/lib/integrations/git/DiffAnalyzer.js +38 -31
- package/lib/integrations/git/GitClient.js +71 -53
- package/lib/lsp/server.js +311 -0
- package/lib/map/graph.js +299 -0
- package/lib/map/imports.js +233 -0
- package/lib/map/index.js +33 -0
- package/lib/map/rank.js +253 -0
- package/lib/map/render.js +346 -0
- package/lib/map/resolver.js +574 -0
- package/lib/pack/budget.js +111 -0
- package/lib/pack/code-lexer.js +360 -0
- package/lib/pack/compress.js +414 -0
- package/lib/pack/index.js +354 -0
- package/lib/pack/renderers/common.js +134 -0
- package/lib/pack/renderers/index.js +24 -0
- package/lib/pack/renderers/json.js +47 -0
- package/lib/pack/renderers/markdown.js +54 -0
- package/lib/pack/renderers/plain.js +38 -0
- package/lib/pack/renderers/xml.js +85 -0
- package/lib/pack/select.js +177 -0
- package/lib/pack/split.js +182 -0
- package/lib/pack/tree.js +35 -0
- package/lib/parsers/gitignore-parser.js +113 -107
- package/lib/parsers/method-filter-parser.js +50 -33
- package/lib/plugins/ExporterPlugin.js +3 -3
- package/lib/plugins/LanguagePlugin.js +3 -3
- package/lib/plugins/PluginManager.js +7 -13
- package/lib/policy/audit.js +294 -0
- package/lib/policy/enforce.js +125 -0
- package/lib/policy/ignore-files.js +169 -0
- package/lib/policy/index.js +52 -0
- package/lib/policy/policy.js +489 -0
- package/lib/pr/diff-parser.js +267 -0
- package/lib/pr/method-mapper.js +148 -0
- package/lib/pr/pr-context.js +829 -0
- package/lib/pr/pr-git.js +149 -0
- package/lib/pr/pr-renderer.js +644 -0
- package/lib/pr/related-tests.js +296 -0
- package/lib/rag/DataSourcePlugin.js +5 -5
- package/lib/rag/EmbeddingProvider.js +19 -8
- package/lib/rag/Indexer.js +14 -5
- package/lib/rag/VectorStoreFactory.js +3 -3
- package/lib/rag/sources/FileDataSource.js +6 -6
- package/lib/rag/stores/BaseVectorStore.js +5 -5
- package/lib/rag/stores/ChromaDBStore.js +10 -10
- package/lib/rag/stores/LanceDBStore.js +29 -19
- package/lib/rag/stores/PineconeStore.js +11 -9
- package/lib/search/bm25.js +433 -0
- package/lib/search/code-units.js +201 -0
- package/lib/search/symbols.js +189 -0
- package/lib/ui/dashboard.js +120 -89
- package/lib/ui/index.js +1 -6
- package/lib/ui/init-wizard-ui.js +550 -0
- package/lib/ui/progress-bar.js +94 -88
- package/lib/ui/select-input.js +40 -32
- package/lib/ui/template-selector.js +99 -0
- package/lib/ui/wizard.js +374 -329
- package/lib/utils/claude-token-counter.js +151 -0
- package/lib/utils/cli-output.js +17 -0
- package/lib/utils/clipboard-utils.js +49 -50
- package/lib/utils/config-utils.js +81 -66
- package/lib/utils/context-versioning.js +532 -0
- package/lib/utils/error-handler.js +112 -112
- package/lib/utils/file-utils.js +153 -69
- package/lib/utils/format-converter.js +356 -283
- package/lib/utils/git-utils.js +366 -294
- package/lib/utils/llm-cost-estimator.js +281 -0
- package/lib/utils/llm-detector.js +242 -78
- package/lib/utils/logger.js +243 -237
- package/lib/utils/messages.js +355 -0
- package/lib/utils/multi-repo-manager.js +364 -0
- package/lib/utils/profile-manager.js +380 -0
- package/lib/utils/safe-read.js +61 -0
- package/lib/utils/secret-redactor.js +61 -0
- package/lib/utils/snapshot-manager.js +679 -0
- package/lib/utils/template-manager.js +378 -0
- package/lib/utils/token-utils.js +201 -170
- package/lib/utils/tokenizer-adapter.js +421 -377
- package/lib/utils/updater.js +539 -506
- package/lib/watch/FileWatcher.js +12 -8
- package/lib/watch/IncrementalAnalyzer.js +7 -7
- package/lib/why/coupling.js +81 -0
- package/lib/why/errors.js +23 -0
- package/lib/why/git.js +169 -0
- package/lib/why/history.js +108 -0
- package/lib/why/index.js +720 -0
- package/lib/why/ownership.js +79 -0
- package/lib/why/paths.js +207 -0
- package/lib/why/references.js +259 -0
- package/lib/why/render.js +722 -0
- package/lib/why/search.js +358 -0
- package/lib/why/target.js +389 -0
- package/lib/wizards/config-generator.js +255 -0
- package/lib/wizards/project-detector.js +571 -0
- package/package.json +44 -10
- package/.ctxman/logs/ctxman-2026-07-02.log +0 -156
- 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/bin/cm-update.js
CHANGED
|
@@ -4,172 +4,172 @@
|
|
|
4
4
|
* Handles version updates and channel switching
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
import Updater from '../lib/utils/updater.js';
|
|
8
|
+
import { getLogger } from '../lib/utils/logger.js';
|
|
9
9
|
|
|
10
10
|
const logger = getLogger({ level: 'info' });
|
|
11
11
|
|
|
12
12
|
async function main() {
|
|
13
|
-
|
|
13
|
+
const args = process.argv.slice(2);
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
// Parse command
|
|
16
|
+
const command = args[0];
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
const updater = new Updater();
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
switch (command) {
|
|
21
|
+
case 'check':
|
|
22
|
+
await checkForUpdates(updater);
|
|
23
|
+
break;
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
case 'install':
|
|
26
|
+
await installUpdate(updater);
|
|
27
|
+
break;
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
case 'rollback':
|
|
30
|
+
await rollbackVersion(updater);
|
|
31
|
+
break;
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
case 'channel':
|
|
34
|
+
await switchChannel(updater, args[1]);
|
|
35
|
+
break;
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
case 'info':
|
|
38
|
+
showUpdateInfo(updater);
|
|
39
|
+
break;
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
41
|
+
default:
|
|
42
|
+
// Default: check and optionally install
|
|
43
|
+
await checkAndUpdate(updater);
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
async function checkForUpdates(updater) {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
49
|
+
console.log('🔍 Checking for updates...\n');
|
|
50
|
+
|
|
51
|
+
const updateInfo = await updater.checkForUpdates();
|
|
52
|
+
|
|
53
|
+
if (updateInfo.error) {
|
|
54
|
+
logger.error('Failed to check for updates', { error: updateInfo.error });
|
|
55
|
+
console.log(`\n❌ ${updateInfo.message}`);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (updateInfo.available) {
|
|
60
|
+
console.log('✨ Update available!\n');
|
|
61
|
+
console.log(` Current: ${updateInfo.currentVersion}`);
|
|
62
|
+
console.log(` Latest: ${updateInfo.latestVersion}`);
|
|
63
|
+
console.log(` Channel: ${updateInfo.channel}\n`);
|
|
64
|
+
|
|
65
|
+
if (updateInfo.releaseNotes) {
|
|
66
|
+
console.log('📝 Release Notes:');
|
|
67
|
+
console.log(' ' + updateInfo.releaseNotes.split('\n').join('\n '));
|
|
68
|
+
console.log('');
|
|
57
69
|
}
|
|
58
70
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
if (updateInfo.releaseNotes) {
|
|
66
|
-
console.log('📝 Release Notes:');
|
|
67
|
-
console.log(' ' + updateInfo.releaseNotes.split('\n').join('\n '));
|
|
68
|
-
console.log('');
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
console.log('To install: ctxman update install\n');
|
|
72
|
-
} else {
|
|
73
|
-
console.log('✓ You are using the latest version\n');
|
|
74
|
-
console.log(` Version: ${updateInfo.currentVersion}`);
|
|
75
|
-
console.log(` Channel: ${updater.channel}\n`);
|
|
76
|
-
}
|
|
71
|
+
console.log('To install: ctxman update install\n');
|
|
72
|
+
} else {
|
|
73
|
+
console.log('✓ You are using the latest version\n');
|
|
74
|
+
console.log(` Version: ${updateInfo.currentVersion}`);
|
|
75
|
+
console.log(` Channel: ${updater.channel}\n`);
|
|
76
|
+
}
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
async function installUpdate(updater) {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
80
|
+
console.log('📦 Installing update...\n');
|
|
81
|
+
|
|
82
|
+
const updateInfo = await updater.checkForUpdates();
|
|
83
|
+
|
|
84
|
+
if (!updateInfo.available) {
|
|
85
|
+
console.log('✓ Already using the latest version\n');
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const result = await updater.installUpdate(updateInfo);
|
|
90
|
+
|
|
91
|
+
if (result.success) {
|
|
92
|
+
console.log('\n✅ Update installed successfully!\n');
|
|
93
|
+
logger.info('Update installed', {
|
|
94
|
+
from: updateInfo.currentVersion,
|
|
95
|
+
to: updateInfo.latestVersion,
|
|
96
|
+
});
|
|
97
|
+
} else if (result.manualUpdateRequired) {
|
|
98
|
+
logger.warn('Manual update required');
|
|
99
|
+
} else {
|
|
100
|
+
console.log('\n❌ Update failed\n');
|
|
101
|
+
logger.error('Update failed', { error: result.error });
|
|
102
|
+
}
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
async function switchChannel(updater, newChannel) {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
106
|
+
if (!newChannel) {
|
|
107
|
+
console.log('❌ Please specify a channel: stable or insider\n');
|
|
108
|
+
console.log('Usage: ctxman update channel <stable|insider>\n');
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
111
|
|
|
112
|
-
|
|
113
|
-
|
|
112
|
+
try {
|
|
113
|
+
console.log(`🔄 Switching to ${newChannel} channel...\n`);
|
|
114
114
|
|
|
115
|
-
|
|
115
|
+
const updateInfo = await updater.switchChannel(newChannel);
|
|
116
116
|
|
|
117
|
-
|
|
117
|
+
logger.info('Channel switched', { channel: newChannel });
|
|
118
118
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
}
|
|
124
|
-
} catch (error) {
|
|
125
|
-
console.log(`\n❌ ${error.message}\n`);
|
|
126
|
-
logger.error('Failed to switch channel', { error: error.message });
|
|
119
|
+
if (updateInfo.available) {
|
|
120
|
+
console.log(`\n✨ Update available on ${newChannel} channel!\n`);
|
|
121
|
+
console.log(` Latest: ${updateInfo.latestVersion}\n`);
|
|
122
|
+
console.log('To install: ctxman update install\n');
|
|
127
123
|
}
|
|
124
|
+
} catch (error) {
|
|
125
|
+
console.log(`\n❌ ${error.message}\n`);
|
|
126
|
+
logger.error('Failed to switch channel', { error: error.message });
|
|
127
|
+
}
|
|
128
128
|
}
|
|
129
129
|
|
|
130
130
|
async function rollbackVersion(updater) {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
131
|
+
console.log('🔄 Rolling back to previous version...\n');
|
|
132
|
+
|
|
133
|
+
const result = await updater.rollback();
|
|
134
|
+
|
|
135
|
+
if (result.success) {
|
|
136
|
+
console.log(`\n✅ Successfully rolled back to v${result.restoredVersion}\n`);
|
|
137
|
+
logger.info('Version rolled back', {
|
|
138
|
+
restoredVersion: result.restoredVersion,
|
|
139
|
+
});
|
|
140
|
+
} else {
|
|
141
|
+
console.log(`\n❌ Rollback failed: ${result.error}\n`);
|
|
142
|
+
logger.error('Rollback failed', { error: result.error });
|
|
143
|
+
}
|
|
144
144
|
}
|
|
145
145
|
|
|
146
146
|
function showUpdateInfo(updater) {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
147
|
+
console.log('ℹ️ Update Configuration\n');
|
|
148
|
+
console.log(` Current version: ${updater.currentVersion}`);
|
|
149
|
+
console.log(` Update channel: ${updater.channel}`);
|
|
150
|
+
console.log(` Auto-update: ${updater.autoUpdate ? 'Enabled' : 'Disabled'}\n`);
|
|
151
|
+
|
|
152
|
+
console.log('Available commands:\n');
|
|
153
|
+
console.log(' ctxman update check - Check for updates');
|
|
154
|
+
console.log(' ctxman update install - Install latest update');
|
|
155
|
+
console.log(' ctxman update rollback - Rollback to previous version');
|
|
156
|
+
console.log(' ctxman update channel - Switch update channel');
|
|
157
|
+
console.log(' ctxman update info - Show this information\n');
|
|
158
|
+
|
|
159
|
+
console.log('Channels:\n');
|
|
160
|
+
console.log(' stable - Stable releases (recommended)');
|
|
161
|
+
console.log(' insider - Pre-release builds (bleeding edge)\n');
|
|
162
162
|
}
|
|
163
163
|
|
|
164
164
|
async function checkAndUpdate(updater) {
|
|
165
|
-
|
|
165
|
+
const updateInfo = await updater.checkAndNotify();
|
|
166
166
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
167
|
+
if (updateInfo && updateInfo.available) {
|
|
168
|
+
console.log('Run "ctxman update install" to update\n');
|
|
169
|
+
}
|
|
170
170
|
}
|
|
171
171
|
|
|
172
|
-
main().catch(error => {
|
|
173
|
-
|
|
174
|
-
|
|
172
|
+
main().catch((error) => {
|
|
173
|
+
console.error('Update command failed:', error.message);
|
|
174
|
+
process.exit(1);
|
|
175
175
|
});
|
package/bin/cm-why.js
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* ctxman why - "why is the code like this?" context from the local git history
|
|
4
|
+
*
|
|
5
|
+
* ctxman why <file> history of a whole file
|
|
6
|
+
* ctxman why <file>:<symbol> a function, method or class (Class.method works too)
|
|
7
|
+
* ctxman why <file>#L10-L40 a line range
|
|
8
|
+
*
|
|
9
|
+
* The document goes to stdout (or -o / --copy); the summary and warnings go to stderr.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { parseArgs } from 'node:util';
|
|
13
|
+
import fs from 'node:fs';
|
|
14
|
+
import path from 'node:path';
|
|
15
|
+
import { getLogger } from '../lib/utils/logger.js';
|
|
16
|
+
|
|
17
|
+
// stdout carries the document: the shared logger must neither print to it nor create log
|
|
18
|
+
// files in the user's repository, and notices printed with console.log go to stderr.
|
|
19
|
+
// The library is imported afterwards because its modules create the logger on import
|
|
20
|
+
getLogger({ level: 'error', logToFile: false });
|
|
21
|
+
const writeOut = (text) => process.stdout.write(text);
|
|
22
|
+
console.log = console.info = console.debug = console.error;
|
|
23
|
+
|
|
24
|
+
const { explainHistory, findRepositoryRoot, WhyError } = await import('../lib/why/index.js');
|
|
25
|
+
const { default: ClipboardUtils } = await import('../lib/utils/clipboard-utils.js');
|
|
26
|
+
|
|
27
|
+
const HELP = `Usage: ctxman why <target> [options]
|
|
28
|
+
|
|
29
|
+
Explain why code looks the way it does: the commits that shaped it (with their
|
|
30
|
+
messages), the issues, tickets and URLs they reference, blame ownership, files
|
|
31
|
+
that change together with it, docs / ADRs / changelog entries / comments that
|
|
32
|
+
mention it, TODO-style notes inside it and the tests that reference it. Built
|
|
33
|
+
from the local git history; no network access.
|
|
34
|
+
|
|
35
|
+
Targets (paths are relative to the current directory, inside the repository):
|
|
36
|
+
<file> The whole file (history follows renames)
|
|
37
|
+
<file>:<symbol> A function, method or class: total, Cart.total, Cart::total
|
|
38
|
+
<file>#L10-L40 A line range (also <file>#L10 and <file>:10-40)
|
|
39
|
+
|
|
40
|
+
Options:
|
|
41
|
+
-n, --limit <n> Commits to include (default: 20)
|
|
42
|
+
--since <date> Only commits after this date ("2024-01-01", "6 months ago")
|
|
43
|
+
--rev <ref> Explain the code as of this revision (default: working tree)
|
|
44
|
+
--patches Include each commit's diff of the target
|
|
45
|
+
--coupling-window <n> Commits of the file examined for co-changes (default: 500, 0 = off)
|
|
46
|
+
--budget <tokens> Keep the output within this many tokens (8000 or 8k); drops
|
|
47
|
+
patches, related text, tests, coupling details and older
|
|
48
|
+
commits first
|
|
49
|
+
--style <style> markdown (default), xml or json
|
|
50
|
+
--json Same as --style json
|
|
51
|
+
--ask <question> Append a question at the end of the document
|
|
52
|
+
-o, --output <file> Write the document to a file instead of stdout
|
|
53
|
+
--copy Copy the document to the clipboard instead of stdout
|
|
54
|
+
--no-redact Do not mask secrets (API keys, tokens, private keys)
|
|
55
|
+
-h, --help Show this help
|
|
56
|
+
|
|
57
|
+
Examples:
|
|
58
|
+
ctxman why src/cart.js:total
|
|
59
|
+
ctxman why src/cart.js#L40-L72 --patches --budget 8000
|
|
60
|
+
ctxman why lib/server.js --since "1 year ago" --style xml -o why.xml
|
|
61
|
+
ctxman why src/cart.js:total --ask "Why do we round here instead of in applyDiscount?" --copy
|
|
62
|
+
`;
|
|
63
|
+
|
|
64
|
+
const OPTIONS = {
|
|
65
|
+
limit: { type: 'string', short: 'n' },
|
|
66
|
+
since: { type: 'string' },
|
|
67
|
+
rev: { type: 'string' },
|
|
68
|
+
patches: { type: 'boolean' },
|
|
69
|
+
'coupling-window': { type: 'string' },
|
|
70
|
+
budget: { type: 'string' },
|
|
71
|
+
style: { type: 'string' },
|
|
72
|
+
json: { type: 'boolean' },
|
|
73
|
+
ask: { type: 'string' },
|
|
74
|
+
output: { type: 'string', short: 'o' },
|
|
75
|
+
copy: { type: 'boolean' },
|
|
76
|
+
'no-redact': { type: 'boolean' },
|
|
77
|
+
help: { type: 'boolean', short: 'h' },
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
function fail(message, exitCode = 1) {
|
|
81
|
+
process.stderr.write(`ctxman why: ${message}\n`);
|
|
82
|
+
process.exitCode = exitCode;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** "8000", "8k" or "1.5k" -> 8000 / 1500; anything else is left for the library to reject */
|
|
86
|
+
function parseTokenCount(text) {
|
|
87
|
+
if (text === undefined) return undefined;
|
|
88
|
+
const match = /^(\d+(?:\.\d+)?)k$/i.exec(text.trim());
|
|
89
|
+
return match ? Math.round(Number(match[1]) * 1000) : text;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function plural(count, word) {
|
|
93
|
+
return `${count} ${word}${count === 1 ? '' : 's'}`;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* One-line summary for stderr
|
|
98
|
+
*/
|
|
99
|
+
function summarize(result) {
|
|
100
|
+
const { data } = result;
|
|
101
|
+
const { target } = data;
|
|
102
|
+
const where = target.symbol ? `${target.path}:${target.symbol}` : target.path;
|
|
103
|
+
const span =
|
|
104
|
+
target.wholeFile || target.startLine === null
|
|
105
|
+
? ''
|
|
106
|
+
: ` (lines ${target.startLine}-${target.endLine})`;
|
|
107
|
+
const references = Object.values(data.references).reduce((sum, list) => sum + list.length, 0);
|
|
108
|
+
const parts = [
|
|
109
|
+
plural(data.history.commits.length, 'commit'),
|
|
110
|
+
plural(references, 'reference'),
|
|
111
|
+
plural(data.ownership?.authors.length ?? 0, 'author'),
|
|
112
|
+
plural(data.coupling?.files.length ?? 0, 'co-changed file'),
|
|
113
|
+
plural(data.related.length, 'related snippet'),
|
|
114
|
+
plural(data.notes.length, 'note'),
|
|
115
|
+
plural(data.tests.length, 'test file'),
|
|
116
|
+
];
|
|
117
|
+
const budget = result.budget ? ` of ${result.budget.toLocaleString('en-US')}` : '';
|
|
118
|
+
const omitted = Object.entries(result.omitted)
|
|
119
|
+
.map(([key, count]) => `${count} ${key}`)
|
|
120
|
+
.join(', ');
|
|
121
|
+
return (
|
|
122
|
+
`ctxman why: ${where}${span}: ${parts.join(', ')}; ` +
|
|
123
|
+
`${result.tokens.toLocaleString('en-US')}${budget} tokens` +
|
|
124
|
+
(omitted ? ` (omitted to fit: ${omitted})` : '')
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async function main() {
|
|
129
|
+
let parsed;
|
|
130
|
+
try {
|
|
131
|
+
parsed = parseArgs({ args: process.argv.slice(2), options: OPTIONS, allowPositionals: true });
|
|
132
|
+
} catch (error) {
|
|
133
|
+
fail(`${error.message}\nRun "ctxman why --help" for usage.`, 2);
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
const { values, positionals } = parsed;
|
|
137
|
+
|
|
138
|
+
if (values.help || positionals.length === 0) {
|
|
139
|
+
writeOut(HELP);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
if (positionals.length > 1) {
|
|
143
|
+
fail(
|
|
144
|
+
`expected one target, got ${positionals.length}: ${positionals.join(' ')} (quote the --ask question)`,
|
|
145
|
+
2
|
|
146
|
+
);
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const cwd = process.cwd();
|
|
151
|
+
let result;
|
|
152
|
+
try {
|
|
153
|
+
const root = findRepositoryRoot(cwd);
|
|
154
|
+
result = await explainHistory(root, positionals[0], {
|
|
155
|
+
cwd,
|
|
156
|
+
limit: values.limit,
|
|
157
|
+
since: values.since,
|
|
158
|
+
rev: values.rev,
|
|
159
|
+
patches: values.patches,
|
|
160
|
+
couplingWindow: values['coupling-window'],
|
|
161
|
+
budget: parseTokenCount(values.budget),
|
|
162
|
+
style: values.json ? 'json' : values.style,
|
|
163
|
+
ask: values.ask,
|
|
164
|
+
redact: !values['no-redact'],
|
|
165
|
+
});
|
|
166
|
+
} catch (error) {
|
|
167
|
+
if (error instanceof WhyError) {
|
|
168
|
+
const exitCode = { INVALID_OPTION: 2, INVALID_REF: 2, POLICY_DENIED: 3 }[error.code] ?? 1;
|
|
169
|
+
fail(error.message, exitCode);
|
|
170
|
+
} else {
|
|
171
|
+
fail(error.message);
|
|
172
|
+
}
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
let toStdout = !values.output && !values.copy;
|
|
177
|
+
if (values.output) {
|
|
178
|
+
const file = path.resolve(cwd, values.output);
|
|
179
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
180
|
+
fs.writeFileSync(file, result.text);
|
|
181
|
+
process.stderr.write(`Wrote ${path.relative(cwd, file) || file}\n`);
|
|
182
|
+
}
|
|
183
|
+
if (values.copy) {
|
|
184
|
+
if (ClipboardUtils.copy(result.text)) {
|
|
185
|
+
process.stderr.write('Copied to the clipboard\n');
|
|
186
|
+
} else if (!values.output) {
|
|
187
|
+
process.stderr.write('Clipboard unavailable; writing to stdout instead\n');
|
|
188
|
+
toStdout = true;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
if (toStdout) writeOut(result.text);
|
|
192
|
+
|
|
193
|
+
for (const warning of result.warnings) process.stderr.write(`warning: ${warning}\n`);
|
|
194
|
+
process.stderr.write(`${summarize(result)}\n`);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
await main();
|
package/bin/mcp-server.js
CHANGED
|
@@ -1,31 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
const args = process.argv.slice(2);
|
|
7
|
-
const transportType = args.includes("--transport=sse") ? "sse" : "stdio";
|
|
8
|
-
const port = args.find(arg => arg.startsWith("--port="))?.split("=")[1] || 3000;
|
|
9
|
-
const apiKey = args.find(arg => arg.startsWith("--api-key="))?.split("=")[1];
|
|
10
|
-
|
|
11
|
-
const server = new MCPServer();
|
|
3
|
+
// Kept for existing client configurations; `ctxman mcp` starts the same server
|
|
4
|
+
import { parseServerArgs, startMcpServer } from '../lib/api/mcp/start.js';
|
|
12
5
|
|
|
13
6
|
async function main() {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
await transport.start();
|
|
21
|
-
await server.start(transport.transport);
|
|
22
|
-
} else {
|
|
23
|
-
await server.start();
|
|
24
|
-
}
|
|
25
|
-
} catch (error) {
|
|
26
|
-
console.error("Fatal error running MCP server:", error);
|
|
27
|
-
process.exit(1);
|
|
28
|
-
}
|
|
7
|
+
try {
|
|
8
|
+
await startMcpServer(parseServerArgs(process.argv.slice(2), { strict: false }));
|
|
9
|
+
} catch (error) {
|
|
10
|
+
console.error('Fatal error running MCP server:', error);
|
|
11
|
+
process.exit(1);
|
|
12
|
+
}
|
|
29
13
|
}
|
|
30
14
|
|
|
31
15
|
main();
|