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
@@ -0,0 +1,355 @@
1
+ /**
2
+ * Bilingual Messages Utility
3
+ * v3.0.0 - Internationalization support for CLI messages
4
+ *
5
+ * Supports English (en) and Turkish (tr) messages.
6
+ * Language is detected from LANG environment variable or can be set explicitly.
7
+ */
8
+
9
+ // Message codes and their translations
10
+ const messages = {
11
+ // CLI Help Messages
12
+ 'cli.help.usage': {
13
+ en: 'Usage',
14
+ tr: 'Kullanım',
15
+ },
16
+ 'cli.help.options': {
17
+ en: 'Options',
18
+ tr: 'Seçenekler',
19
+ },
20
+ 'cli.help.examples': {
21
+ en: 'Examples',
22
+ tr: 'Örnekler',
23
+ },
24
+ 'cli.help.description': {
25
+ en: 'Description',
26
+ tr: 'Açıklama',
27
+ },
28
+
29
+ // Analysis Messages
30
+ 'analysis.start': {
31
+ en: 'Starting analysis...',
32
+ tr: 'Analiz başlatılıyor...',
33
+ },
34
+ 'analysis.complete': {
35
+ en: 'Analysis complete',
36
+ tr: 'Analiz tamamlandı',
37
+ },
38
+ 'analysis.files_found': {
39
+ en: 'Files found',
40
+ tr: 'Dosya bulundu',
41
+ },
42
+ 'analysis.total_tokens': {
43
+ en: 'Total tokens',
44
+ tr: 'Toplam token',
45
+ },
46
+ 'analysis.method_count': {
47
+ en: 'Methods found',
48
+ tr: 'Method bulundu',
49
+ },
50
+ 'analysis.no_files': {
51
+ en: 'No files found to analyze',
52
+ tr: 'Analiz edilecek dosya bulunamadı',
53
+ },
54
+
55
+ // Export Messages
56
+ 'export.success': {
57
+ en: 'Export successful',
58
+ tr: 'Dışa aktarma başarılı',
59
+ },
60
+ 'export.copied': {
61
+ en: 'Copied to clipboard',
62
+ tr: 'Panoya kopyalandı',
63
+ },
64
+ 'export.saved': {
65
+ en: 'Saved to file',
66
+ tr: 'Dosyaya kaydedildi',
67
+ },
68
+ 'export.failed': {
69
+ en: 'Export failed',
70
+ tr: 'Dışa aktarma başarısız',
71
+ },
72
+
73
+ // Error Messages
74
+ 'error.file_not_found': {
75
+ en: 'File not found',
76
+ tr: 'Dosya bulunamadı',
77
+ },
78
+ 'error.permission_denied': {
79
+ en: 'Permission denied',
80
+ tr: 'İzin reddedildi',
81
+ },
82
+ 'error.invalid_format': {
83
+ en: 'Invalid format',
84
+ tr: 'Geçersiz format',
85
+ },
86
+ 'error.missing_param': {
87
+ en: 'Missing required parameter',
88
+ tr: 'Gerekli parametre eksik',
89
+ },
90
+ 'error.unsupported_lang': {
91
+ en: 'Unsupported language',
92
+ tr: 'Desteklenmeyen dil',
93
+ },
94
+ 'error.git_not_found': {
95
+ en: 'Git repository not found',
96
+ tr: 'Git deposu bulunamadı',
97
+ },
98
+ 'error.tiktoken_unavailable': {
99
+ en: 'tiktoken not available, using estimation mode',
100
+ tr: 'tiktoken mevcut değil, tahmin modu kullanılıyor',
101
+ },
102
+
103
+ // Success Messages
104
+ 'success.server_started': {
105
+ en: 'API server started',
106
+ tr: 'API sunucusu başlatıldı',
107
+ },
108
+ 'success.server_stopped': {
109
+ en: 'API server stopped',
110
+ tr: 'API sunucusu durduruldu',
111
+ },
112
+ 'success.cache_cleared': {
113
+ en: 'Cache cleared successfully',
114
+ tr: 'Önbellek başarıyla temizlendi',
115
+ },
116
+ 'success.config_saved': {
117
+ en: 'Configuration saved',
118
+ tr: 'Yapılandırma kaydedildi',
119
+ },
120
+
121
+ // Warning Messages
122
+ 'warning.large_project': {
123
+ en: 'Large project detected, analysis may take longer',
124
+ tr: 'Büyük proje tespit edildi, analiz daha uzun sürebilir',
125
+ },
126
+ 'warning.token_limit': {
127
+ en: 'Token limit exceeded, some files may be excluded',
128
+ tr: 'Token limiti aşıldı, bazı dosyalar hariç tutulabilir',
129
+ },
130
+ 'warning.no_methods': {
131
+ en: 'No methods found in the specified files',
132
+ tr: 'Belirtilen dosyalarda method bulunamadı',
133
+ },
134
+
135
+ // Info Messages
136
+ 'info.watch_mode': {
137
+ en: 'Watching for file changes...',
138
+ tr: 'Dosya değişiklikleri izleniyor...',
139
+ },
140
+ 'info.press_exit': {
141
+ en: 'Press Ctrl+C to exit',
142
+ tr: 'Çıkmak için Ctrl+C tuşlarına basın',
143
+ },
144
+ 'info.using_cache': {
145
+ en: 'Using cached results',
146
+ tr: 'Önbelleğe alınmış sonuçlar kullanılıyor',
147
+ },
148
+ 'info.checking_updates': {
149
+ en: 'Checking for updates...',
150
+ tr: 'Güncellemeler kontrol ediliyor...',
151
+ },
152
+
153
+ // Git Integration Messages
154
+ 'git.no_changes': {
155
+ en: 'No changes detected',
156
+ tr: 'Değişiklik tespit edilmedi',
157
+ },
158
+ 'git.changes_found': {
159
+ en: 'Changes found in {count} files',
160
+ tr: '{count} dosyada değişiklik bulundu',
161
+ },
162
+ 'git.branch_switch': {
163
+ en: 'Switched to branch',
164
+ tr: 'Branch değiştirildi',
165
+ },
166
+
167
+ // API Messages
168
+ 'api.listening': {
169
+ en: 'Listening on',
170
+ tr: 'Dinleniyor',
171
+ },
172
+ 'api.docs_available': {
173
+ en: 'API documentation available at',
174
+ tr: 'API dokümantasyonu şu adreste mevcut',
175
+ },
176
+ 'api.auth_required': {
177
+ en: 'Authentication required',
178
+ tr: 'Kimlik doğrulama gerekli',
179
+ },
180
+
181
+ // Progress Messages
182
+ 'progress.scanning': {
183
+ en: 'Scanning files...',
184
+ tr: 'Dosyalar taranıyor...',
185
+ },
186
+ 'progress.analyzing': {
187
+ en: 'Analyzing tokens...',
188
+ tr: "Token'lar analiz ediliyor...",
189
+ },
190
+ 'progress.generating': {
191
+ en: 'Generating report...',
192
+ tr: 'Rapor oluşturuluyor...',
193
+ },
194
+ 'progress.uploading': {
195
+ en: 'Uploading...',
196
+ tr: 'Yükleniyor...',
197
+ },
198
+
199
+ // Configuration Messages
200
+ 'config.created': {
201
+ en: 'Configuration file created',
202
+ tr: 'Yapılandırma dosyası oluşturuldu',
203
+ },
204
+ 'config.updated': {
205
+ en: 'Configuration updated',
206
+ tr: 'Yapılandırma güncellendi',
207
+ },
208
+ 'config.not_found': {
209
+ en: 'Configuration file not found, using defaults',
210
+ tr: 'Yapılandırma dosyası bulunamadı, varsayılanlar kullanılıyor',
211
+ },
212
+
213
+ // Plugin Messages
214
+ 'plugin.loaded': {
215
+ en: 'Plugin loaded',
216
+ tr: 'Plugin yüklendi',
217
+ },
218
+ 'plugin.not_found': {
219
+ en: 'Plugin not found',
220
+ tr: 'Plugin bulunamadı',
221
+ },
222
+ 'plugin.error': {
223
+ en: 'Plugin error',
224
+ tr: 'Plugin hatası',
225
+ },
226
+ };
227
+
228
+ // Default language (can be overridden)
229
+ let currentLanguage = 'en';
230
+
231
+ /**
232
+ * Detect language from environment
233
+ * @returns {string} Language code ('en' or 'tr')
234
+ */
235
+ function detectLanguage() {
236
+ const lang = process.env.LANG || process.env.LANGUAGE || process.env.LC_ALL || '';
237
+
238
+ // Check for Turkish
239
+ if (lang.toLowerCase().includes('tr') || lang.toLowerCase().includes('tur')) {
240
+ return 'tr';
241
+ }
242
+
243
+ // Default to English
244
+ return 'en';
245
+ }
246
+
247
+ /**
248
+ * Set the current language
249
+ * @param {string} lang - Language code ('en' or 'tr')
250
+ */
251
+ export function setLanguage(lang) {
252
+ if (lang === 'en' || lang === 'tr') {
253
+ currentLanguage = lang;
254
+ } else {
255
+ console.warn(`Unsupported language: ${lang}, defaulting to English`);
256
+ currentLanguage = 'en';
257
+ }
258
+ }
259
+
260
+ /**
261
+ * Get the current language
262
+ * @returns {string} Current language code
263
+ */
264
+ export function getLanguage() {
265
+ return currentLanguage;
266
+ }
267
+
268
+ /**
269
+ * Get a message by code
270
+ * @param {string} code - Message code (e.g., 'cli.help.usage')
271
+ * @param {object} params - Optional parameters for message interpolation
272
+ * @returns {string} Translated message
273
+ */
274
+ export function getMessage(code, params = {}) {
275
+ const message = messages[code];
276
+
277
+ if (!message) {
278
+ console.warn(`Message code not found: ${code}`);
279
+ return code;
280
+ }
281
+
282
+ let text = message[currentLanguage] || message.en;
283
+
284
+ // Interpolate parameters
285
+ for (const [key, value] of Object.entries(params)) {
286
+ text = text.replace(new RegExp(`\\{${key}\\}`, 'g'), value);
287
+ }
288
+
289
+ return text;
290
+ }
291
+
292
+ /**
293
+ * Get all messages for a language
294
+ * @param {string} lang - Language code
295
+ * @returns {object} All messages in the specified language
296
+ */
297
+ export function getAllMessages(lang = currentLanguage) {
298
+ const result = {};
299
+
300
+ for (const [code, translations] of Object.entries(messages)) {
301
+ result[code] = translations[lang] || translations.en;
302
+ }
303
+
304
+ return result;
305
+ }
306
+
307
+ /**
308
+ * Check if a message code exists
309
+ * @param {string} code - Message code
310
+ * @returns {boolean} True if code exists
311
+ */
312
+ export function hasMessage(code) {
313
+ return code in messages;
314
+ }
315
+
316
+ /**
317
+ * Get message codes by prefix
318
+ * @param {string} prefix - Message code prefix (e.g., 'error')
319
+ * @returns {string[]} Array of matching message codes
320
+ */
321
+ export function getMessagesByPrefix(prefix) {
322
+ return Object.keys(messages).filter((code) => code.startsWith(prefix));
323
+ }
324
+
325
+ /**
326
+ * Format a message with emoji prefix
327
+ * @param {string} code - Message code
328
+ * @param {string} emoji - Emoji to prepend
329
+ * @param {object} params - Optional parameters
330
+ * @returns {string} Formatted message
331
+ */
332
+ export function formatMessage(code, emoji = '', params = {}) {
333
+ const message = getMessage(code, params);
334
+ return emoji ? `${emoji} ${message}` : message;
335
+ }
336
+
337
+ // Initialize language on module load
338
+ currentLanguage = detectLanguage();
339
+
340
+ // Export message codes for external use
341
+ export const MessageCodes = Object.keys(messages).reduce((acc, code) => {
342
+ acc[code.toUpperCase().replace(/\./g, '_')] = code;
343
+ return acc;
344
+ }, {});
345
+
346
+ export default {
347
+ getMessage,
348
+ getAllMessages,
349
+ setLanguage,
350
+ getLanguage,
351
+ hasMessage,
352
+ getMessagesByPrefix,
353
+ formatMessage,
354
+ MessageCodes,
355
+ };
@@ -0,0 +1,364 @@
1
+ /**
2
+ * Multi-Repository Manager
3
+ * FEAT-006: Multi-Repository Context
4
+ *
5
+ * Manages multiple repositories for monorepo or microservices context analysis
6
+ */
7
+
8
+ import fs from 'fs';
9
+ import path from 'path';
10
+ import TokenCalculator from '../analyzers/token-calculator.js';
11
+ import { getLogger } from './logger.js';
12
+
13
+ const logger = getLogger('MultiRepoManager');
14
+
15
+ /**
16
+ * Default configuration structure for repos.json
17
+ */
18
+ const DEFAULT_CONFIG = {
19
+ version: '1.0',
20
+ repos: [],
21
+ };
22
+
23
+ /**
24
+ * MultiRepoManager - Handles multi-repository context analysis
25
+ *
26
+ * Features:
27
+ * - Add/remove repositories
28
+ * - Per-repo token analysis
29
+ * - Combined context generation
30
+ * - Repository configuration persistence
31
+ */
32
+ export class MultiRepoManager {
33
+ /**
34
+ * @param {string} projectRoot - Root directory for configuration
35
+ * @param {object} options - Configuration options
36
+ */
37
+ constructor(projectRoot, options = {}) {
38
+ this.projectRoot = projectRoot;
39
+ this.configPath = options.configPath || path.join(projectRoot, '.ctxman', 'repos.json');
40
+ this.config = null;
41
+ this.options = {
42
+ autoSave: true,
43
+ ...options,
44
+ };
45
+ }
46
+
47
+ /**
48
+ * Load repository configuration from file
49
+ * @returns {object} Configuration object
50
+ */
51
+ load() {
52
+ try {
53
+ if (fs.existsSync(this.configPath)) {
54
+ const content = fs.readFileSync(this.configPath, 'utf-8');
55
+ const parsed = JSON.parse(content);
56
+ // Deep clone to avoid reference issues
57
+ this.config = {
58
+ version: parsed.version || '1.0',
59
+ repos: Array.isArray(parsed.repos) ? [...parsed.repos] : [],
60
+ };
61
+ logger.info(`Loaded ${this.config.repos.length} repositories from config`);
62
+ return this.config;
63
+ }
64
+ } catch (error) {
65
+ logger.warn(`Failed to load config: ${error.message}`);
66
+ }
67
+
68
+ // Return default config if file doesn't exist
69
+ this.config = { ...DEFAULT_CONFIG, repos: [] };
70
+ return this.config;
71
+ }
72
+
73
+ /**
74
+ * Save repository configuration to file
75
+ */
76
+ save() {
77
+ // Ensure directory exists
78
+ const configDir = path.dirname(this.configPath);
79
+ if (!fs.existsSync(configDir)) {
80
+ fs.mkdirSync(configDir, { recursive: true });
81
+ }
82
+
83
+ fs.writeFileSync(this.configPath, JSON.stringify(this.config, null, 2));
84
+ logger.info(`Saved configuration to ${this.configPath}`);
85
+ }
86
+
87
+ /**
88
+ * Add a repository to configuration
89
+ * @param {string} repoPath - Path to repository (relative or absolute)
90
+ * @param {object} options - Repository options
91
+ * @returns {object} Added repository configuration
92
+ */
93
+ addRepo(repoPath, options = {}) {
94
+ if (!this.config) {
95
+ this.load();
96
+ }
97
+
98
+ // Resolve path
99
+ const resolvedPath = this.resolvePath(repoPath);
100
+
101
+ // Check if repo already exists
102
+ const existingIndex = this.config.repos.findIndex(
103
+ (r) => this.resolvePath(r.path) === resolvedPath
104
+ );
105
+
106
+ if (existingIndex !== -1) {
107
+ logger.warn(`Repository already exists: ${repoPath}`);
108
+ return this.config.repos[existingIndex];
109
+ }
110
+
111
+ // Create repo entry
112
+ const repoId = options.id || this.generateRepoId(repoPath);
113
+ const repo = {
114
+ id: repoId,
115
+ path: repoPath,
116
+ alias: options.alias || path.basename(resolvedPath),
117
+ exclude: options.exclude || ['node_modules/**', 'dist/**', 'build/**', 'coverage/**'],
118
+ weight: options.weight || 1.0,
119
+ };
120
+
121
+ this.config.repos.push(repo);
122
+
123
+ if (this.options.autoSave) {
124
+ this.save();
125
+ }
126
+
127
+ logger.info(`Added repository: ${repo.alias} (${repoPath})`);
128
+ return repo;
129
+ }
130
+
131
+ /**
132
+ * Remove a repository from configuration
133
+ * @param {string} repoPath - Path to repository
134
+ * @returns {boolean} True if removed, false if not found
135
+ */
136
+ removeRepo(repoPath) {
137
+ if (!this.config) {
138
+ this.load();
139
+ }
140
+
141
+ const resolvedPath = this.resolvePath(repoPath);
142
+ const initialLength = this.config.repos.length;
143
+
144
+ this.config.repos = this.config.repos.filter((r) => this.resolvePath(r.path) !== resolvedPath);
145
+
146
+ if (this.config.repos.length < initialLength) {
147
+ if (this.options.autoSave) {
148
+ this.save();
149
+ }
150
+ logger.info(`Removed repository: ${repoPath}`);
151
+ return true;
152
+ }
153
+
154
+ logger.warn(`Repository not found: ${repoPath}`);
155
+ return false;
156
+ }
157
+
158
+ /**
159
+ * List all configured repositories
160
+ * @returns {Array} List of repository configurations
161
+ */
162
+ listRepos() {
163
+ if (!this.config) {
164
+ this.load();
165
+ }
166
+
167
+ return this.config.repos || [];
168
+ }
169
+
170
+ /**
171
+ * Analyze all repositories and return combined stats
172
+ * @param {object} options - Analysis options
173
+ * @returns {object} Combined analysis results
174
+ */
175
+ analyzeAll(options = {}) {
176
+ if (!this.config) {
177
+ this.load();
178
+ }
179
+
180
+ const results = {
181
+ repos: [],
182
+ combined: {
183
+ totalFiles: 0,
184
+ totalTokens: 0,
185
+ totalLines: 0,
186
+ totalBytes: 0,
187
+ },
188
+ };
189
+
190
+ for (const repo of this.config.repos) {
191
+ const repoResult = this.analyzeRepo(repo, options);
192
+ results.repos.push(repoResult);
193
+
194
+ results.combined.totalFiles += repoResult.files;
195
+ results.combined.totalTokens += repoResult.tokens;
196
+ results.combined.totalLines += repoResult.lines;
197
+ results.combined.totalBytes += repoResult.bytes;
198
+ }
199
+
200
+ // Calculate percentages
201
+ for (const repo of results.repos) {
202
+ repo.percentage =
203
+ results.combined.totalTokens > 0
204
+ ? ((repo.tokens / results.combined.totalTokens) * 100).toFixed(1)
205
+ : 0;
206
+ }
207
+
208
+ logger.info(`Analyzed ${results.repos.length} repositories`);
209
+ return results;
210
+ }
211
+
212
+ /**
213
+ * Analyze a single repository
214
+ * @param {object} repo - Repository configuration
215
+ * @param {object} options - Analysis options
216
+ * @returns {object} Repository analysis result
217
+ */
218
+ analyzeRepo(repo, options = {}) {
219
+ const resolvedPath = this.resolvePath(repo.path);
220
+ const repoResult = {
221
+ id: repo.id,
222
+ alias: repo.alias,
223
+ path: repo.path,
224
+ resolvedPath,
225
+ files: 0,
226
+ tokens: 0,
227
+ lines: 0,
228
+ bytes: 0,
229
+ byExtension: {},
230
+ };
231
+
232
+ try {
233
+ if (!fs.existsSync(resolvedPath)) {
234
+ logger.warn(`Repository path does not exist: ${resolvedPath}`);
235
+ repoResult.error = 'Path does not exist';
236
+ return repoResult;
237
+ }
238
+
239
+ // Create analyzer with repo-specific excludes
240
+ const analyzer = new TokenCalculator(resolvedPath, {
241
+ verbose: false,
242
+ simple: true,
243
+ ...options,
244
+ });
245
+
246
+ // Run analysis
247
+ const stats = analyzer.run();
248
+
249
+ if (stats) {
250
+ repoResult.files = stats.totalFiles || 0;
251
+ repoResult.tokens = stats.totalTokens || 0;
252
+ repoResult.lines = stats.totalLines || 0;
253
+ repoResult.bytes = stats.totalBytes || 0;
254
+ repoResult.byExtension = stats.byExtension || {};
255
+ repoResult.largestFiles = stats.largestFiles || [];
256
+ }
257
+ } catch (error) {
258
+ logger.error(`Failed to analyze repo ${repo.alias}: ${error.message}`);
259
+ repoResult.error = error.message;
260
+ }
261
+
262
+ return repoResult;
263
+ }
264
+
265
+ /**
266
+ * Resolve repository path (relative or absolute)
267
+ * @param {string} repoPath - Repository path
268
+ * @returns {string} Resolved absolute path
269
+ */
270
+ resolvePath(repoPath) {
271
+ if (path.isAbsolute(repoPath)) {
272
+ return repoPath;
273
+ }
274
+ return path.resolve(this.projectRoot, repoPath);
275
+ }
276
+
277
+ /**
278
+ * Generate a unique repository ID from path
279
+ * @param {string} repoPath - Repository path
280
+ * @returns {string} Generated ID
281
+ */
282
+ generateRepoId(repoPath) {
283
+ const basename = path.basename(repoPath);
284
+ const timestamp = Date.now().toString(36);
285
+ return `${basename}-${timestamp}`;
286
+ }
287
+
288
+ /**
289
+ * Format analysis results for display
290
+ * @param {object} results - Analysis results
291
+ * @returns {string} Formatted output
292
+ */
293
+ formatResults(results) {
294
+ const lines = [];
295
+
296
+ lines.push('🔀 Multi-Repository Context Analysis:');
297
+ lines.push('═'.repeat(50));
298
+
299
+ if (results.repos.length === 0) {
300
+ lines.push(' No repositories configured.');
301
+ lines.push(' Use --add-repo <path> to add repositories.');
302
+ return lines.join('\n');
303
+ }
304
+
305
+ lines.push(` Repositories: ${results.repos.length}`);
306
+ lines.push('');
307
+
308
+ results.repos.forEach((repo, index) => {
309
+ const num = `${index + 1}.`;
310
+ lines.push(` ${num} ${repo.alias} (${repo.path})`);
311
+
312
+ if (repo.error) {
313
+ lines.push(` ⚠️ Error: ${repo.error}`);
314
+ } else {
315
+ lines.push(` Files: ${repo.files.toLocaleString()}`);
316
+ lines.push(` Tokens: ${repo.tokens.toLocaleString()} (${repo.percentage}%)`);
317
+ }
318
+ lines.push('');
319
+ });
320
+
321
+ lines.push(' ' + '─'.repeat(46));
322
+ lines.push(
323
+ ` Combined Total: ${results.combined.totalFiles.toLocaleString()} files, ${results.combined.totalTokens.toLocaleString()} tokens`
324
+ );
325
+
326
+ return lines.join('\n');
327
+ }
328
+
329
+ /**
330
+ * Format repository list for display
331
+ * @returns {string} Formatted output
332
+ */
333
+ formatList() {
334
+ const repos = this.listRepos();
335
+
336
+ if (repos.length === 0) {
337
+ return 'No repositories configured.\nUse --add-repo <path> to add a repository.';
338
+ }
339
+
340
+ const lines = [];
341
+ lines.push('📋 Configured Repositories:');
342
+ lines.push('═'.repeat(50));
343
+
344
+ repos.forEach((repo, index) => {
345
+ const resolvedPath = this.resolvePath(repo.path);
346
+ const exists = fs.existsSync(resolvedPath);
347
+ const status = exists ? '✓' : '⚠';
348
+
349
+ lines.push(`${status} ${index + 1}. ${repo.alias}`);
350
+ lines.push(` Path: ${repo.path}`);
351
+ lines.push(` ID: ${repo.id}`);
352
+
353
+ if (!exists) {
354
+ lines.push(` ⚠️ Path does not exist`);
355
+ }
356
+ lines.push('');
357
+ });
358
+
359
+ lines.push(`Total: ${repos.length} repositories`);
360
+ return lines.join('\n');
361
+ }
362
+ }
363
+
364
+ export default MultiRepoManager;