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.
Files changed (223) hide show
  1. package/.ctxman/llm-profiles.json +1067 -39
  2. package/.ctxman/profiles/backend-team.json +13 -0
  3. package/.ctxman/profiles/default.json +12 -0
  4. package/.ctxman/profiles/devops-team.json +21 -0
  5. package/.ctxman/profiles/frontend-team.json +20 -0
  6. package/.ctxman/templates/bug-fix.json +17 -0
  7. package/.ctxman/templates/code-review.json +9 -0
  8. package/.ctxman/templates/documentation.json +9 -0
  9. package/.ctxman/templates/feature.json +9 -0
  10. package/.ctxman/templates/full-context.json +9 -0
  11. package/.ctxman/templates/refactoring.json +18 -0
  12. package/.ctxman/templates/testing.json +9 -0
  13. package/.ctxman/wizard-profiles/security-audit/profile.json +7 -1
  14. package/LICENSE +21 -0
  15. package/README.md +380 -136
  16. package/bin/cli.js +1880 -529
  17. package/bin/cm-ask.js +18 -13
  18. package/bin/cm-audit.js +124 -0
  19. package/bin/cm-count.js +151 -0
  20. package/bin/cm-gitingest.js +128 -115
  21. package/bin/cm-map.js +242 -0
  22. package/bin/cm-mcp.js +293 -0
  23. package/bin/cm-pack.js +405 -0
  24. package/bin/cm-policy.js +170 -0
  25. package/bin/cm-pr.js +248 -0
  26. package/bin/cm-search.js +178 -0
  27. package/bin/cm-update.js +128 -128
  28. package/bin/cm-why.js +197 -0
  29. package/bin/mcp-server.js +8 -24
  30. package/ctxman.js +237 -183
  31. package/examples/README.md +19 -4
  32. package/examples/github-actions/ctxman-check.yml +49 -0
  33. package/examples/wizard-profiles/security-audit/profile.json +7 -1
  34. package/index.d.ts +1422 -0
  35. package/index.js +55 -31
  36. package/lib/agents/pr-failure-agent.js +48 -45
  37. package/lib/analyzers/ai-suggester.js +451 -0
  38. package/lib/analyzers/dependency-scanner.js +505 -0
  39. package/lib/analyzers/duplicate-detector.js +264 -0
  40. package/lib/analyzers/go-method-analyzer.js +151 -110
  41. package/lib/analyzers/import-tracker.js +431 -0
  42. package/lib/analyzers/method-analyzer.js +1055 -434
  43. package/lib/analyzers/performance-dashboard.js +683 -0
  44. package/lib/analyzers/token-calculator.js +895 -504
  45. package/lib/analyzers/unused-exports-detector.js +336 -0
  46. package/lib/api/mcp/install.js +424 -0
  47. package/lib/api/mcp/prompts.js +281 -281
  48. package/lib/api/mcp/resources.js +300 -241
  49. package/lib/api/mcp/sandbox.js +89 -0
  50. package/lib/api/mcp/server.js +675 -312
  51. package/lib/api/mcp/start.js +125 -0
  52. package/lib/api/mcp/transports/HttpSseTransport.js +60 -52
  53. package/lib/api/rest/server.js +105 -28
  54. package/lib/cache/CacheManager.js +9 -17
  55. package/lib/core/Analyzer.js +16 -10
  56. package/lib/core/ContextBuilder.js +19 -28
  57. package/lib/core/Reporter.js +15 -14
  58. package/lib/core/Scanner.js +81 -12
  59. package/lib/count/index.js +100 -0
  60. package/lib/formatters/format-registry.js +394 -372
  61. package/lib/formatters/gitingest-formatter.js +656 -621
  62. package/lib/formatters/toon-formatter-v1.3.js +637 -632
  63. package/lib/formatters/toon-formatter.js +239 -240
  64. package/lib/integrations/git/BlameTracker.js +4 -4
  65. package/lib/integrations/git/DiffAnalyzer.js +38 -31
  66. package/lib/integrations/git/GitClient.js +71 -53
  67. package/lib/lsp/server.js +311 -0
  68. package/lib/map/graph.js +299 -0
  69. package/lib/map/imports.js +233 -0
  70. package/lib/map/index.js +33 -0
  71. package/lib/map/rank.js +253 -0
  72. package/lib/map/render.js +346 -0
  73. package/lib/map/resolver.js +574 -0
  74. package/lib/pack/budget.js +111 -0
  75. package/lib/pack/code-lexer.js +360 -0
  76. package/lib/pack/compress.js +414 -0
  77. package/lib/pack/index.js +354 -0
  78. package/lib/pack/renderers/common.js +134 -0
  79. package/lib/pack/renderers/index.js +24 -0
  80. package/lib/pack/renderers/json.js +47 -0
  81. package/lib/pack/renderers/markdown.js +54 -0
  82. package/lib/pack/renderers/plain.js +38 -0
  83. package/lib/pack/renderers/xml.js +85 -0
  84. package/lib/pack/select.js +177 -0
  85. package/lib/pack/split.js +182 -0
  86. package/lib/pack/tree.js +35 -0
  87. package/lib/parsers/gitignore-parser.js +113 -107
  88. package/lib/parsers/method-filter-parser.js +50 -33
  89. package/lib/plugins/ExporterPlugin.js +3 -3
  90. package/lib/plugins/LanguagePlugin.js +3 -3
  91. package/lib/plugins/PluginManager.js +7 -13
  92. package/lib/policy/audit.js +294 -0
  93. package/lib/policy/enforce.js +125 -0
  94. package/lib/policy/ignore-files.js +169 -0
  95. package/lib/policy/index.js +52 -0
  96. package/lib/policy/policy.js +489 -0
  97. package/lib/pr/diff-parser.js +267 -0
  98. package/lib/pr/method-mapper.js +148 -0
  99. package/lib/pr/pr-context.js +829 -0
  100. package/lib/pr/pr-git.js +149 -0
  101. package/lib/pr/pr-renderer.js +644 -0
  102. package/lib/pr/related-tests.js +296 -0
  103. package/lib/rag/DataSourcePlugin.js +5 -5
  104. package/lib/rag/EmbeddingProvider.js +19 -8
  105. package/lib/rag/Indexer.js +14 -5
  106. package/lib/rag/VectorStoreFactory.js +3 -3
  107. package/lib/rag/sources/FileDataSource.js +6 -6
  108. package/lib/rag/stores/BaseVectorStore.js +5 -5
  109. package/lib/rag/stores/ChromaDBStore.js +10 -10
  110. package/lib/rag/stores/LanceDBStore.js +29 -19
  111. package/lib/rag/stores/PineconeStore.js +11 -9
  112. package/lib/search/bm25.js +433 -0
  113. package/lib/search/code-units.js +201 -0
  114. package/lib/search/symbols.js +189 -0
  115. package/lib/ui/dashboard.js +120 -89
  116. package/lib/ui/index.js +1 -6
  117. package/lib/ui/init-wizard-ui.js +550 -0
  118. package/lib/ui/progress-bar.js +94 -88
  119. package/lib/ui/select-input.js +40 -32
  120. package/lib/ui/template-selector.js +99 -0
  121. package/lib/ui/wizard.js +374 -329
  122. package/lib/utils/claude-token-counter.js +151 -0
  123. package/lib/utils/cli-output.js +17 -0
  124. package/lib/utils/clipboard-utils.js +49 -50
  125. package/lib/utils/config-utils.js +81 -66
  126. package/lib/utils/context-versioning.js +532 -0
  127. package/lib/utils/error-handler.js +112 -112
  128. package/lib/utils/file-utils.js +153 -69
  129. package/lib/utils/format-converter.js +356 -283
  130. package/lib/utils/git-utils.js +366 -294
  131. package/lib/utils/llm-cost-estimator.js +281 -0
  132. package/lib/utils/llm-detector.js +242 -78
  133. package/lib/utils/logger.js +243 -237
  134. package/lib/utils/messages.js +355 -0
  135. package/lib/utils/multi-repo-manager.js +364 -0
  136. package/lib/utils/profile-manager.js +380 -0
  137. package/lib/utils/safe-read.js +61 -0
  138. package/lib/utils/secret-redactor.js +61 -0
  139. package/lib/utils/snapshot-manager.js +679 -0
  140. package/lib/utils/template-manager.js +378 -0
  141. package/lib/utils/token-utils.js +201 -170
  142. package/lib/utils/tokenizer-adapter.js +421 -377
  143. package/lib/utils/updater.js +539 -506
  144. package/lib/watch/FileWatcher.js +12 -8
  145. package/lib/watch/IncrementalAnalyzer.js +7 -7
  146. package/lib/why/coupling.js +81 -0
  147. package/lib/why/errors.js +23 -0
  148. package/lib/why/git.js +169 -0
  149. package/lib/why/history.js +108 -0
  150. package/lib/why/index.js +720 -0
  151. package/lib/why/ownership.js +79 -0
  152. package/lib/why/paths.js +207 -0
  153. package/lib/why/references.js +259 -0
  154. package/lib/why/render.js +722 -0
  155. package/lib/why/search.js +358 -0
  156. package/lib/why/target.js +389 -0
  157. package/lib/wizards/config-generator.js +255 -0
  158. package/lib/wizards/project-detector.js +571 -0
  159. package/package.json +44 -10
  160. package/.ctxman/logs/ctxman-2026-07-02.log +0 -156
  161. package/.ctxman/self-knowledge/documents.lance/_transactions/0-b3dd1558-f95b-44b9-a154-18e8c330ae7a.txn +0 -0
  162. package/.ctxman/self-knowledge/documents.lance/_transactions/1-44bff6d4-e552-4010-a769-e09ad822698d.txn +0 -0
  163. package/.ctxman/self-knowledge/documents.lance/_transactions/10-37260794-5947-4ecb-b4c8-d732f58f40f1.txn +0 -0
  164. package/.ctxman/self-knowledge/documents.lance/_transactions/11-0d52a883-5518-463c-9b6d-986c3af89dfd.txn +0 -0
  165. package/.ctxman/self-knowledge/documents.lance/_transactions/12-ae354646-52f2-49c9-90fb-2e6ed8932663.txn +0 -0
  166. package/.ctxman/self-knowledge/documents.lance/_transactions/13-5d7d5e57-27da-498b-b734-091b9d38da13.txn +0 -0
  167. package/.ctxman/self-knowledge/documents.lance/_transactions/14-558ce928-810b-4252-a364-fdd1ee6f8a25.txn +0 -0
  168. package/.ctxman/self-knowledge/documents.lance/_transactions/15-27e3a58f-8b64-43b8-a9f4-e3369446968b.txn +0 -0
  169. package/.ctxman/self-knowledge/documents.lance/_transactions/16-a110c390-e5c8-4464-b976-bc7f5195fa15.txn +0 -0
  170. package/.ctxman/self-knowledge/documents.lance/_transactions/17-97513225-53de-4216-b61a-6337bd0cc4ad.txn +0 -0
  171. package/.ctxman/self-knowledge/documents.lance/_transactions/18-5ba450f0-c527-459c-bf4f-6d79b5e4285f.txn +0 -0
  172. package/.ctxman/self-knowledge/documents.lance/_transactions/19-600d3a63-5225-49e9-b8b7-30e037b564ab.txn +0 -0
  173. package/.ctxman/self-knowledge/documents.lance/_transactions/2-48a61b4e-92cf-4342-8376-914c1b99a9f9.txn +0 -0
  174. package/.ctxman/self-knowledge/documents.lance/_transactions/20-121dd912-5410-410e-84ff-82c7a23604a0.txn +0 -0
  175. package/.ctxman/self-knowledge/documents.lance/_transactions/3-bc6844ab-7ea3-4bb3-9ee1-538ce5664a9c.txn +0 -0
  176. package/.ctxman/self-knowledge/documents.lance/_transactions/4-d5e1754f-dbb5-454e-a9be-2a7da8d7bcf9.txn +0 -0
  177. package/.ctxman/self-knowledge/documents.lance/_transactions/5-4fae93df-0568-4b6f-8acf-8bef4bba6047.txn +0 -0
  178. package/.ctxman/self-knowledge/documents.lance/_transactions/6-4b1b8b87-068e-49a3-ba2b-d8dae628b252.txn +0 -0
  179. package/.ctxman/self-knowledge/documents.lance/_transactions/7-63d040fb-d2fa-4c0f-bed0-53411918a7b0.txn +0 -0
  180. package/.ctxman/self-knowledge/documents.lance/_transactions/8-c5628cf6-264c-420f-aab8-bc96a9083591.txn +0 -0
  181. package/.ctxman/self-knowledge/documents.lance/_transactions/9-c94570aa-5e53-4fcb-971f-cecc1d1d5492.txn +0 -0
  182. package/.ctxman/self-knowledge/documents.lance/_versions/1.manifest +0 -0
  183. package/.ctxman/self-knowledge/documents.lance/_versions/10.manifest +0 -0
  184. package/.ctxman/self-knowledge/documents.lance/_versions/11.manifest +0 -0
  185. package/.ctxman/self-knowledge/documents.lance/_versions/12.manifest +0 -0
  186. package/.ctxman/self-knowledge/documents.lance/_versions/13.manifest +0 -0
  187. package/.ctxman/self-knowledge/documents.lance/_versions/14.manifest +0 -0
  188. package/.ctxman/self-knowledge/documents.lance/_versions/15.manifest +0 -0
  189. package/.ctxman/self-knowledge/documents.lance/_versions/16.manifest +0 -0
  190. package/.ctxman/self-knowledge/documents.lance/_versions/17.manifest +0 -0
  191. package/.ctxman/self-knowledge/documents.lance/_versions/18.manifest +0 -0
  192. package/.ctxman/self-knowledge/documents.lance/_versions/19.manifest +0 -0
  193. package/.ctxman/self-knowledge/documents.lance/_versions/2.manifest +0 -0
  194. package/.ctxman/self-knowledge/documents.lance/_versions/20.manifest +0 -0
  195. package/.ctxman/self-knowledge/documents.lance/_versions/21.manifest +0 -0
  196. package/.ctxman/self-knowledge/documents.lance/_versions/3.manifest +0 -0
  197. package/.ctxman/self-knowledge/documents.lance/_versions/4.manifest +0 -0
  198. package/.ctxman/self-knowledge/documents.lance/_versions/5.manifest +0 -0
  199. package/.ctxman/self-knowledge/documents.lance/_versions/6.manifest +0 -0
  200. package/.ctxman/self-knowledge/documents.lance/_versions/7.manifest +0 -0
  201. package/.ctxman/self-knowledge/documents.lance/_versions/8.manifest +0 -0
  202. package/.ctxman/self-knowledge/documents.lance/_versions/9.manifest +0 -0
  203. package/.ctxman/self-knowledge/documents.lance/data/000010010011011101000010de8621405d9d5cb06540dbf02c.lance +0 -0
  204. package/.ctxman/self-knowledge/documents.lance/data/0000101001011100011000003fb62a4aa8978974075c144f1c.lance +0 -0
  205. package/.ctxman/self-knowledge/documents.lance/data/000011010000100111001011c6cc824d628f3bb1f4e04cd5d0.lance +0 -0
  206. package/.ctxman/self-knowledge/documents.lance/data/001000001000011001011100d8f74349ada090b332cd1df669.lance +0 -0
  207. package/.ctxman/self-knowledge/documents.lance/data/01000001111010001000010045ab44467bb1e7e4d8ae06bd58.lance +0 -0
  208. package/.ctxman/self-knowledge/documents.lance/data/01000101101110111110011131ef904241a89e1ffb6ddaf8e3.lance +0 -0
  209. package/.ctxman/self-knowledge/documents.lance/data/010010110000010000011010fbfcc145c6a8a827b28057a07e.lance +0 -0
  210. package/.ctxman/self-knowledge/documents.lance/data/1000011000101010001110003fb4f045c3835982efa1405324.lance +0 -0
  211. package/.ctxman/self-knowledge/documents.lance/data/100111011101110111101011f4396b410fa06af3b028d204e6.lance +0 -0
  212. package/.ctxman/self-knowledge/documents.lance/data/100111100111101000001100596b174e00995d6cc2969cc8dc.lance +0 -0
  213. package/.ctxman/self-knowledge/documents.lance/data/1010001100011000110010102ea16b484d887c7c9725feffd6.lance +0 -0
  214. package/.ctxman/self-knowledge/documents.lance/data/101101100010101111110111c5230944d9b3af7301b6392dda.lance +0 -0
  215. package/.ctxman/self-knowledge/documents.lance/data/1011011100100111111110105892bf41b5a530fedd7cf516f1.lance +0 -0
  216. package/.ctxman/self-knowledge/documents.lance/data/10111110000010001011110164e8774742bade87b483c10f36.lance +0 -0
  217. package/.ctxman/self-knowledge/documents.lance/data/1100000100010110001110103ffaca40a2a42c85067ebaf9ae.lance +0 -0
  218. package/.ctxman/self-knowledge/documents.lance/data/110000010001110110101110664bde4c76a78dfc14b05e83b8.lance +0 -0
  219. package/.ctxman/self-knowledge/documents.lance/data/11001100111000010100110050645c4281b1f35b2022234f7f.lance +0 -0
  220. package/.ctxman/self-knowledge/documents.lance/data/1101111011001100110111017214924a368f5719eab2246619.lance +0 -0
  221. package/.ctxman/self-knowledge/documents.lance/data/1110000111001010001000002150c24dcab653d39d07032e63.lance +0 -0
  222. package/.ctxman/self-knowledge/documents.lance/data/1110100000000100000011014d20534967a18b5c87721659ff.lance +0 -0
  223. 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
- const Updater = require('../lib/utils/updater');
8
- const { getLogger } = require('../lib/utils/logger');
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
- const args = process.argv.slice(2);
13
+ const args = process.argv.slice(2);
14
14
 
15
- // Parse command
16
- const command = args[0];
15
+ // Parse command
16
+ const command = args[0];
17
17
 
18
- const updater = new Updater();
18
+ const updater = new Updater();
19
19
 
20
- switch (command) {
21
- case 'check':
22
- await checkForUpdates(updater);
23
- break;
20
+ switch (command) {
21
+ case 'check':
22
+ await checkForUpdates(updater);
23
+ break;
24
24
 
25
- case 'install':
26
- await installUpdate(updater);
27
- break;
25
+ case 'install':
26
+ await installUpdate(updater);
27
+ break;
28
28
 
29
- case 'rollback':
30
- await rollbackVersion(updater);
31
- break;
29
+ case 'rollback':
30
+ await rollbackVersion(updater);
31
+ break;
32
32
 
33
- case 'channel':
34
- await switchChannel(updater, args[1]);
35
- break;
33
+ case 'channel':
34
+ await switchChannel(updater, args[1]);
35
+ break;
36
36
 
37
- case 'info':
38
- showUpdateInfo(updater);
39
- break;
37
+ case 'info':
38
+ showUpdateInfo(updater);
39
+ break;
40
40
 
41
- default:
42
- // Default: check and optionally install
43
- await checkAndUpdate(updater);
44
- break;
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
- 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;
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
- 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('');
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
- 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
- }
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
- 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
- }
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
- try {
113
- console.log(`🔄 Switching to ${newChannel} channel...\n`);
112
+ try {
113
+ console.log(`🔄 Switching to ${newChannel} channel...\n`);
114
114
 
115
- const updateInfo = await updater.switchChannel(newChannel);
115
+ const updateInfo = await updater.switchChannel(newChannel);
116
116
 
117
- logger.info('Channel switched', { channel: newChannel });
117
+ logger.info('Channel switched', { channel: newChannel });
118
118
 
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');
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
- 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
- }
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
- 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');
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
- const updateInfo = await updater.checkAndNotify();
165
+ const updateInfo = await updater.checkAndNotify();
166
166
 
167
- if (updateInfo && updateInfo.available) {
168
- console.log('Run "ctxman update install" to update\n');
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
- console.error('Update command failed:', error.message);
174
- process.exit(1);
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
- import MCPServer from "../lib/api/mcp/server.js";
4
- import { HttpSseTransport } from "../lib/api/mcp/transports/HttpSseTransport.js";
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
- try {
15
- if (transportType === "sse") {
16
- const transport = new HttpSseTransport({
17
- port: parseInt(port),
18
- auth: apiKey ? { apiKey } : undefined
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();