ctxman 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (190) hide show
  1. package/.ctxman/custom-profiles.example.json +20 -0
  2. package/.ctxman/llm-profiles.json +131 -0
  3. package/.ctxman/logs/ctxman-2026-07-02.log +156 -0
  4. package/.ctxman/self-knowledge/documents.lance/_transactions/0-b3dd1558-f95b-44b9-a154-18e8c330ae7a.txn +0 -0
  5. package/.ctxman/self-knowledge/documents.lance/_transactions/1-44bff6d4-e552-4010-a769-e09ad822698d.txn +0 -0
  6. package/.ctxman/self-knowledge/documents.lance/_transactions/10-37260794-5947-4ecb-b4c8-d732f58f40f1.txn +0 -0
  7. package/.ctxman/self-knowledge/documents.lance/_transactions/11-0d52a883-5518-463c-9b6d-986c3af89dfd.txn +0 -0
  8. package/.ctxman/self-knowledge/documents.lance/_transactions/12-ae354646-52f2-49c9-90fb-2e6ed8932663.txn +0 -0
  9. package/.ctxman/self-knowledge/documents.lance/_transactions/13-5d7d5e57-27da-498b-b734-091b9d38da13.txn +0 -0
  10. package/.ctxman/self-knowledge/documents.lance/_transactions/14-558ce928-810b-4252-a364-fdd1ee6f8a25.txn +0 -0
  11. package/.ctxman/self-knowledge/documents.lance/_transactions/15-27e3a58f-8b64-43b8-a9f4-e3369446968b.txn +0 -0
  12. package/.ctxman/self-knowledge/documents.lance/_transactions/16-a110c390-e5c8-4464-b976-bc7f5195fa15.txn +0 -0
  13. package/.ctxman/self-knowledge/documents.lance/_transactions/17-97513225-53de-4216-b61a-6337bd0cc4ad.txn +0 -0
  14. package/.ctxman/self-knowledge/documents.lance/_transactions/18-5ba450f0-c527-459c-bf4f-6d79b5e4285f.txn +0 -0
  15. package/.ctxman/self-knowledge/documents.lance/_transactions/19-600d3a63-5225-49e9-b8b7-30e037b564ab.txn +0 -0
  16. package/.ctxman/self-knowledge/documents.lance/_transactions/2-48a61b4e-92cf-4342-8376-914c1b99a9f9.txn +0 -0
  17. package/.ctxman/self-knowledge/documents.lance/_transactions/20-121dd912-5410-410e-84ff-82c7a23604a0.txn +0 -0
  18. package/.ctxman/self-knowledge/documents.lance/_transactions/3-bc6844ab-7ea3-4bb3-9ee1-538ce5664a9c.txn +0 -0
  19. package/.ctxman/self-knowledge/documents.lance/_transactions/4-d5e1754f-dbb5-454e-a9be-2a7da8d7bcf9.txn +0 -0
  20. package/.ctxman/self-knowledge/documents.lance/_transactions/5-4fae93df-0568-4b6f-8acf-8bef4bba6047.txn +0 -0
  21. package/.ctxman/self-knowledge/documents.lance/_transactions/6-4b1b8b87-068e-49a3-ba2b-d8dae628b252.txn +0 -0
  22. package/.ctxman/self-knowledge/documents.lance/_transactions/7-63d040fb-d2fa-4c0f-bed0-53411918a7b0.txn +0 -0
  23. package/.ctxman/self-knowledge/documents.lance/_transactions/8-c5628cf6-264c-420f-aab8-bc96a9083591.txn +0 -0
  24. package/.ctxman/self-knowledge/documents.lance/_transactions/9-c94570aa-5e53-4fcb-971f-cecc1d1d5492.txn +0 -0
  25. package/.ctxman/self-knowledge/documents.lance/_versions/1.manifest +0 -0
  26. package/.ctxman/self-knowledge/documents.lance/_versions/10.manifest +0 -0
  27. package/.ctxman/self-knowledge/documents.lance/_versions/11.manifest +0 -0
  28. package/.ctxman/self-knowledge/documents.lance/_versions/12.manifest +0 -0
  29. package/.ctxman/self-knowledge/documents.lance/_versions/13.manifest +0 -0
  30. package/.ctxman/self-knowledge/documents.lance/_versions/14.manifest +0 -0
  31. package/.ctxman/self-knowledge/documents.lance/_versions/15.manifest +0 -0
  32. package/.ctxman/self-knowledge/documents.lance/_versions/16.manifest +0 -0
  33. package/.ctxman/self-knowledge/documents.lance/_versions/17.manifest +0 -0
  34. package/.ctxman/self-knowledge/documents.lance/_versions/18.manifest +0 -0
  35. package/.ctxman/self-knowledge/documents.lance/_versions/19.manifest +0 -0
  36. package/.ctxman/self-knowledge/documents.lance/_versions/2.manifest +0 -0
  37. package/.ctxman/self-knowledge/documents.lance/_versions/20.manifest +0 -0
  38. package/.ctxman/self-knowledge/documents.lance/_versions/21.manifest +0 -0
  39. package/.ctxman/self-knowledge/documents.lance/_versions/3.manifest +0 -0
  40. package/.ctxman/self-knowledge/documents.lance/_versions/4.manifest +0 -0
  41. package/.ctxman/self-knowledge/documents.lance/_versions/5.manifest +0 -0
  42. package/.ctxman/self-knowledge/documents.lance/_versions/6.manifest +0 -0
  43. package/.ctxman/self-knowledge/documents.lance/_versions/7.manifest +0 -0
  44. package/.ctxman/self-knowledge/documents.lance/_versions/8.manifest +0 -0
  45. package/.ctxman/self-knowledge/documents.lance/_versions/9.manifest +0 -0
  46. package/.ctxman/self-knowledge/documents.lance/data/000010010011011101000010de8621405d9d5cb06540dbf02c.lance +0 -0
  47. package/.ctxman/self-knowledge/documents.lance/data/0000101001011100011000003fb62a4aa8978974075c144f1c.lance +0 -0
  48. package/.ctxman/self-knowledge/documents.lance/data/000011010000100111001011c6cc824d628f3bb1f4e04cd5d0.lance +0 -0
  49. package/.ctxman/self-knowledge/documents.lance/data/001000001000011001011100d8f74349ada090b332cd1df669.lance +0 -0
  50. package/.ctxman/self-knowledge/documents.lance/data/01000001111010001000010045ab44467bb1e7e4d8ae06bd58.lance +0 -0
  51. package/.ctxman/self-knowledge/documents.lance/data/01000101101110111110011131ef904241a89e1ffb6ddaf8e3.lance +0 -0
  52. package/.ctxman/self-knowledge/documents.lance/data/010010110000010000011010fbfcc145c6a8a827b28057a07e.lance +0 -0
  53. package/.ctxman/self-knowledge/documents.lance/data/1000011000101010001110003fb4f045c3835982efa1405324.lance +0 -0
  54. package/.ctxman/self-knowledge/documents.lance/data/100111011101110111101011f4396b410fa06af3b028d204e6.lance +0 -0
  55. package/.ctxman/self-knowledge/documents.lance/data/100111100111101000001100596b174e00995d6cc2969cc8dc.lance +0 -0
  56. package/.ctxman/self-knowledge/documents.lance/data/1010001100011000110010102ea16b484d887c7c9725feffd6.lance +0 -0
  57. package/.ctxman/self-knowledge/documents.lance/data/101101100010101111110111c5230944d9b3af7301b6392dda.lance +0 -0
  58. package/.ctxman/self-knowledge/documents.lance/data/1011011100100111111110105892bf41b5a530fedd7cf516f1.lance +0 -0
  59. package/.ctxman/self-knowledge/documents.lance/data/10111110000010001011110164e8774742bade87b483c10f36.lance +0 -0
  60. package/.ctxman/self-knowledge/documents.lance/data/1100000100010110001110103ffaca40a2a42c85067ebaf9ae.lance +0 -0
  61. package/.ctxman/self-knowledge/documents.lance/data/110000010001110110101110664bde4c76a78dfc14b05e83b8.lance +0 -0
  62. package/.ctxman/self-knowledge/documents.lance/data/11001100111000010100110050645c4281b1f35b2022234f7f.lance +0 -0
  63. package/.ctxman/self-knowledge/documents.lance/data/1101111011001100110111017214924a368f5719eab2246619.lance +0 -0
  64. package/.ctxman/self-knowledge/documents.lance/data/1110000111001010001000002150c24dcab653d39d07032e63.lance +0 -0
  65. package/.ctxman/self-knowledge/documents.lance/data/1110100000000100000011014d20534967a18b5c87721659ff.lance +0 -0
  66. package/.ctxman/self-knowledge/documents.lance/data/11110101111010000101011135a0114ef2b35dc6f1297a3381.lance +0 -0
  67. package/.ctxman/wizard-profiles/code-review/.contextignore +41 -0
  68. package/.ctxman/wizard-profiles/code-review/.contextinclude +30 -0
  69. package/.ctxman/wizard-profiles/code-review/.methodignore +26 -0
  70. package/.ctxman/wizard-profiles/code-review/.methodinclude +47 -0
  71. package/.ctxman/wizard-profiles/code-review/profile.json +26 -0
  72. package/.ctxman/wizard-profiles/documentation/.contextignore +21 -0
  73. package/.ctxman/wizard-profiles/documentation/.contextinclude +24 -0
  74. package/.ctxman/wizard-profiles/documentation/.methodignore +19 -0
  75. package/.ctxman/wizard-profiles/documentation/.methodinclude +44 -0
  76. package/.ctxman/wizard-profiles/documentation/profile.json +27 -0
  77. package/.ctxman/wizard-profiles/full/.contextignore +23 -0
  78. package/.ctxman/wizard-profiles/full/.contextinclude +38 -0
  79. package/.ctxman/wizard-profiles/full/.methodignore +5 -0
  80. package/.ctxman/wizard-profiles/full/.methodinclude +5 -0
  81. package/.ctxman/wizard-profiles/full/profile.json +28 -0
  82. package/.ctxman/wizard-profiles/llm-explain/.contextignore +25 -0
  83. package/.ctxman/wizard-profiles/llm-explain/.contextinclude +33 -0
  84. package/.ctxman/wizard-profiles/llm-explain/.methodignore +19 -0
  85. package/.ctxman/wizard-profiles/llm-explain/.methodinclude +48 -0
  86. package/.ctxman/wizard-profiles/llm-explain/profile.json +27 -0
  87. package/.ctxman/wizard-profiles/minimal/.contextignore +27 -0
  88. package/.ctxman/wizard-profiles/minimal/.contextinclude +14 -0
  89. package/.ctxman/wizard-profiles/minimal/.methodignore +22 -0
  90. package/.ctxman/wizard-profiles/minimal/.methodinclude +18 -0
  91. package/.ctxman/wizard-profiles/minimal/profile.json +26 -0
  92. package/.ctxman/wizard-profiles/security-audit/.contextignore +36 -0
  93. package/.ctxman/wizard-profiles/security-audit/.contextinclude +37 -0
  94. package/.ctxman/wizard-profiles/security-audit/.methodignore +29 -0
  95. package/.ctxman/wizard-profiles/security-audit/.methodinclude +60 -0
  96. package/.ctxman/wizard-profiles/security-audit/profile.json +34 -0
  97. package/README.md +1167 -0
  98. package/bin/cli.js +650 -0
  99. package/bin/cm-ask.js +95 -0
  100. package/bin/cm-gitingest.js +134 -0
  101. package/bin/cm-update.js +175 -0
  102. package/bin/mcp-server.js +31 -0
  103. package/ctxman.js +236 -0
  104. package/examples/README.md +298 -0
  105. package/examples/custom-llm-profiles.example.json +19 -0
  106. package/examples/wizard-profiles/code-review/.contextignore +41 -0
  107. package/examples/wizard-profiles/code-review/.contextinclude +30 -0
  108. package/examples/wizard-profiles/code-review/.methodignore +26 -0
  109. package/examples/wizard-profiles/code-review/.methodinclude +47 -0
  110. package/examples/wizard-profiles/code-review/profile.json +26 -0
  111. package/examples/wizard-profiles/documentation/.contextignore +21 -0
  112. package/examples/wizard-profiles/documentation/.contextinclude +24 -0
  113. package/examples/wizard-profiles/documentation/.methodignore +19 -0
  114. package/examples/wizard-profiles/documentation/.methodinclude +44 -0
  115. package/examples/wizard-profiles/documentation/profile.json +27 -0
  116. package/examples/wizard-profiles/full/.contextignore +23 -0
  117. package/examples/wizard-profiles/full/.contextinclude +38 -0
  118. package/examples/wizard-profiles/full/.methodignore +5 -0
  119. package/examples/wizard-profiles/full/.methodinclude +5 -0
  120. package/examples/wizard-profiles/full/profile.json +28 -0
  121. package/examples/wizard-profiles/llm-explain/.contextignore +25 -0
  122. package/examples/wizard-profiles/llm-explain/.contextinclude +33 -0
  123. package/examples/wizard-profiles/llm-explain/.methodignore +19 -0
  124. package/examples/wizard-profiles/llm-explain/.methodinclude +48 -0
  125. package/examples/wizard-profiles/llm-explain/profile.json +27 -0
  126. package/examples/wizard-profiles/minimal/.contextignore +27 -0
  127. package/examples/wizard-profiles/minimal/.contextinclude +14 -0
  128. package/examples/wizard-profiles/minimal/.methodignore +22 -0
  129. package/examples/wizard-profiles/minimal/.methodinclude +18 -0
  130. package/examples/wizard-profiles/minimal/profile.json +26 -0
  131. package/examples/wizard-profiles/security-audit/.contextignore +36 -0
  132. package/examples/wizard-profiles/security-audit/.contextinclude +37 -0
  133. package/examples/wizard-profiles/security-audit/.methodignore +29 -0
  134. package/examples/wizard-profiles/security-audit/.methodinclude +60 -0
  135. package/examples/wizard-profiles/security-audit/profile.json +34 -0
  136. package/index.js +68 -0
  137. package/lib/agents/pr-failure-agent.js +201 -0
  138. package/lib/analyzers/go-method-analyzer.js +126 -0
  139. package/lib/analyzers/method-analyzer.js +457 -0
  140. package/lib/analyzers/token-calculator.js +577 -0
  141. package/lib/api/mcp/prompts.js +319 -0
  142. package/lib/api/mcp/resources.js +269 -0
  143. package/lib/api/mcp/server.js +339 -0
  144. package/lib/api/mcp/transports/HttpSseTransport.js +63 -0
  145. package/lib/api/rest/server.js +405 -0
  146. package/lib/cache/CacheManager.js +388 -0
  147. package/lib/core/Analyzer.js +180 -0
  148. package/lib/core/ContextBuilder.js +325 -0
  149. package/lib/core/Reporter.js +270 -0
  150. package/lib/core/Scanner.js +129 -0
  151. package/lib/formatters/format-registry.js +419 -0
  152. package/lib/formatters/gitingest-formatter.js +746 -0
  153. package/lib/formatters/toon-formatter-v1.3.js +731 -0
  154. package/lib/formatters/toon-formatter.js +291 -0
  155. package/lib/integrations/git/BlameTracker.js +142 -0
  156. package/lib/integrations/git/DiffAnalyzer.js +260 -0
  157. package/lib/integrations/git/GitClient.js +336 -0
  158. package/lib/parsers/gitignore-parser.js +127 -0
  159. package/lib/parsers/method-filter-parser.js +50 -0
  160. package/lib/plugins/ExporterPlugin.js +102 -0
  161. package/lib/plugins/LanguagePlugin.js +100 -0
  162. package/lib/plugins/PluginManager.js +302 -0
  163. package/lib/rag/DataSourcePlugin.js +31 -0
  164. package/lib/rag/EmbeddingProvider.js +75 -0
  165. package/lib/rag/Indexer.js +83 -0
  166. package/lib/rag/VectorStoreFactory.js +24 -0
  167. package/lib/rag/sources/FileDataSource.js +70 -0
  168. package/lib/rag/stores/BaseVectorStore.js +43 -0
  169. package/lib/rag/stores/ChromaDBStore.js +85 -0
  170. package/lib/rag/stores/LanceDBStore.js +109 -0
  171. package/lib/rag/stores/PineconeStore.js +74 -0
  172. package/lib/ui/dashboard.js +109 -0
  173. package/lib/ui/index.js +15 -0
  174. package/lib/ui/progress-bar.js +110 -0
  175. package/lib/ui/select-input.js +51 -0
  176. package/lib/ui/wizard.js +359 -0
  177. package/lib/utils/clipboard-utils.js +66 -0
  178. package/lib/utils/config-utils.js +94 -0
  179. package/lib/utils/error-handler.js +145 -0
  180. package/lib/utils/file-utils.js +89 -0
  181. package/lib/utils/format-converter.js +325 -0
  182. package/lib/utils/git-utils.js +337 -0
  183. package/lib/utils/llm-detector.js +409 -0
  184. package/lib/utils/logger.js +278 -0
  185. package/lib/utils/token-utils.js +200 -0
  186. package/lib/utils/tokenizer-adapter.js +454 -0
  187. package/lib/utils/updater.js +565 -0
  188. package/lib/watch/FileWatcher.js +208 -0
  189. package/lib/watch/IncrementalAnalyzer.js +206 -0
  190. package/package.json +118 -0
package/bin/cli.js ADDED
@@ -0,0 +1,650 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { TokenAnalyzer } from '../index.js';
4
+ import FormatRegistry from '../lib/formatters/format-registry.js';
5
+ import FormatConverter from '../lib/utils/format-converter.js';
6
+ import { LLMDetector } from '../lib/utils/llm-detector.js';
7
+ import APIServer from '../lib/api/rest/server.js';
8
+ import FileWatcher from '../lib/watch/FileWatcher.js';
9
+ import IncrementalAnalyzer from '../lib/watch/IncrementalAnalyzer.js';
10
+ import DiffAnalyzer from '../lib/integrations/git/DiffAnalyzer.js';
11
+ import { execSync } from 'child_process';
12
+ import { fileURLToPath } from 'url';
13
+ import { dirname, resolve } from 'path';
14
+ import { readFileSync } from 'fs';
15
+
16
+ // ESM equivalents for __dirname and __filename
17
+ const __filename = fileURLToPath(import.meta.url);
18
+ const __dirname = dirname(__filename);
19
+
20
+ // Load package.json
21
+ const pkg = JSON.parse(readFileSync(resolve(__dirname, '../package.json'), 'utf-8'));
22
+
23
+ async function main() {
24
+ const args = process.argv.slice(2);
25
+
26
+ if (args.includes('--help') || args.includes('-h')) {
27
+ printHelp();
28
+ return;
29
+ }
30
+
31
+ // Check for version flag
32
+ if (args.includes('--version')) {
33
+ console.log(`Ctxman v${pkg.version}`);
34
+ return;
35
+ }
36
+
37
+ // Check for format listing
38
+ if (args.includes('--list-formats')) {
39
+ listFormats();
40
+ return;
41
+ }
42
+
43
+ // Check for LLM model listing (v2.3.7)
44
+ if (args.includes('--list-llms')) {
45
+ listLLMs();
46
+ return;
47
+ }
48
+
49
+ // Check for format conversion mode (v2.3.2)
50
+ if (args.includes('convert')) {
51
+ runFormatConversion(args);
52
+ return;
53
+ }
54
+
55
+ // Check for GitHub GitIngest mode (v2.3.6+)
56
+ if (args.includes('github') || args.includes('git')) {
57
+ const commandPath = resolve(__dirname, './cm-gitingest.js');
58
+ const gitArgs = args.filter(arg => arg !== 'github' && arg !== 'git');
59
+ execSync(`node "${commandPath}" ${gitArgs.join(' ')}`, { stdio: 'inherit' });
60
+ return;
61
+ }
62
+
63
+ // Check for RAG 'ask' mode (v3.1.0)
64
+ if (args.includes('ask')) {
65
+ const commandPath = resolve(__dirname, './cm-ask.js');
66
+ const askArgs = args.filter(arg => arg !== 'ask');
67
+ // Pass remaining args as the query
68
+ execSync(`node "${commandPath}" "${askArgs.join(' ')}"`, { stdio: 'inherit' });
69
+ return;
70
+ }
71
+
72
+ // Check for API server mode (v3.0.0)
73
+ if (args.includes('serve')) {
74
+ await runAPIServer(args);
75
+ return;
76
+ }
77
+
78
+ // Check for watch mode (v3.0.0)
79
+ if (args.includes('watch')) {
80
+ await runWatchMode(args);
81
+ return;
82
+ }
83
+
84
+ // Check for explicit dashboard mode
85
+ if (args.includes('--dashboard')) {
86
+ try {
87
+ await runDashboard();
88
+ return;
89
+ } catch (error) {
90
+ console.error('āš ļø Live dashboard failed.');
91
+ console.error(' Error:', error.message);
92
+ console.error(' Falling back to standard mode...\n');
93
+ }
94
+ }
95
+
96
+ // Check if CLI mode is explicitly requested OR any analysis flags are present
97
+ const hasAnalysisFlags = args.some(arg =>
98
+ arg.startsWith('-') &&
99
+ arg !== '--cli' &&
100
+ arg !== '--wizard' &&
101
+ arg !== '--dashboard'
102
+ );
103
+
104
+ const cliMode = args.includes('--cli') || hasAnalysisFlags;
105
+
106
+ // DEFAULT: Run wizard mode unless --cli flag or other flags are present
107
+ if (!cliMode) {
108
+ try {
109
+ await runWizard();
110
+ return;
111
+ } catch (error) {
112
+ // If wizard fails, fall through to normal mode
113
+ console.error('āš ļø Interactive wizard mode failed.');
114
+ console.error(' Error:', error.message);
115
+ console.error(' Falling back to CLI mode...\n');
116
+ console.error(' Tip: Use --cli flag to skip wizard mode\n');
117
+ }
118
+ }
119
+
120
+ // CLI Mode: Run traditional command-line analysis
121
+ const options = parseArguments(args);
122
+
123
+ // Git integration: Filter to changed files only (v3.0.0)
124
+ if (options.changedOnly || options.changedSince) {
125
+ await runChangedFilesAnalysis(options);
126
+ return;
127
+ }
128
+
129
+ printStartupInfo(options);
130
+
131
+ const analyzer = new TokenAnalyzer(options.projectRoot, options);
132
+ analyzer.run();
133
+ }
134
+
135
+ function parseArguments(args) {
136
+ return {
137
+ // Output options
138
+ saveReport: args.includes('--save-report') || args.includes('-s'),
139
+ verbose: args.includes('--verbose') || args.includes('-v'),
140
+ contextExport: args.includes('--context-export'),
141
+ contextToClipboard: args.includes('--context-clipboard'),
142
+ json: args.includes('--json'), // NEW: JSON output mode
143
+
144
+ // Analysis options
145
+ methodLevel: args.includes('--method-level') || args.includes('-m'),
146
+ gitingest: args.includes('--gitingest') || args.includes('-g'),
147
+
148
+ // Format options (v2.3.0)
149
+ outputFormat: getOutputFormat(args),
150
+
151
+ // LLM options (v2.3.7)
152
+ targetModel: getTargetModel(args),
153
+ autoDetectLLM: args.includes('--auto-detect-llm'),
154
+
155
+ // Git options (v3.0.0)
156
+ changedOnly: args.includes('--changed-only'),
157
+ changedSince: getChangedSince(args),
158
+ withAuthors: args.includes('--with-authors'),
159
+ withHistory: args.includes('--with-history'),
160
+
161
+ // UI options (v2.3.0)
162
+ simple: args.includes('--simple'),
163
+ dashboard: args.includes('--dashboard'),
164
+
165
+ // Chunking options (v2.3.0)
166
+ chunking: {
167
+ enabled: args.includes('--chunk'),
168
+ strategy: getChunkStrategy(args),
169
+ maxTokensPerChunk: getChunkSize(args)
170
+ },
171
+
172
+ projectRoot: process.cwd()
173
+ };
174
+ }
175
+
176
+ function getOutputFormat(args) {
177
+ const formatIndex = args.findIndex(arg => arg === '--output' || arg === '-o');
178
+ if (formatIndex !== -1 && args[formatIndex + 1]) {
179
+ return args[formatIndex + 1];
180
+ }
181
+ return 'toon'; // Default to TOON format in v2.3.0
182
+ }
183
+
184
+ function getChunkStrategy(args) {
185
+ const strategyIndex = args.findIndex(arg => arg === '--chunk-strategy');
186
+ if (strategyIndex !== -1 && args[strategyIndex + 1]) {
187
+ return args[strategyIndex + 1];
188
+ }
189
+ return 'smart'; // Default strategy
190
+ }
191
+
192
+ function getChunkSize(args) {
193
+ const sizeIndex = args.findIndex(arg => arg === '--chunk-size');
194
+ if (sizeIndex !== -1 && args[sizeIndex + 1]) {
195
+ return parseInt(args[sizeIndex + 1], 10);
196
+ }
197
+ return 100000; // Default 100k tokens
198
+ }
199
+
200
+ function getTargetModel(args) {
201
+ // Check for explicit model flag
202
+ const modelIndex = args.findIndex(arg => arg === '--target-model');
203
+ if (modelIndex !== -1 && args[modelIndex + 1]) {
204
+ return args[modelIndex + 1];
205
+ }
206
+
207
+ // Check for auto-detect flag
208
+ if (args.includes('--auto-detect-llm')) {
209
+ const detected = LLMDetector.detect();
210
+ if (detected !== 'unknown') {
211
+ console.log(`āœ… Auto-detected LLM: ${LLMDetector.getProfile(detected).name}`);
212
+ return detected;
213
+ }
214
+ }
215
+
216
+ return null; // No model specified
217
+ }
218
+
219
+ function listLLMs() {
220
+ console.log('\nšŸ“‹ Supported LLM Models (v2.3.7):\n');
221
+ console.log('═'.repeat(70));
222
+
223
+ const models = LLMDetector.getModelList();
224
+
225
+ // Group by vendor
226
+ const byVendor = {};
227
+ models.forEach(model => {
228
+ if (!byVendor[model.vendor]) {
229
+ byVendor[model.vendor] = [];
230
+ }
231
+ byVendor[model.vendor].push(model);
232
+ });
233
+
234
+ // Display by vendor
235
+ Object.entries(byVendor).forEach(([vendor, models]) => {
236
+ console.log(`\n${vendor}:`);
237
+ models.forEach(model => {
238
+ const contextDisplay = model.contextWindow >= 1000000
239
+ ? `${(model.contextWindow / 1000000).toFixed(1)}M`
240
+ : `${Math.floor(model.contextWindow / 1000)}k`;
241
+ console.log(` ${model.id.padEnd(25)} ${model.name.padEnd(25)} (${contextDisplay} context)`);
242
+ });
243
+ });
244
+
245
+ console.log('\n' + '═'.repeat(70));
246
+ console.log('\nUsage:');
247
+ console.log(' ctxman --target-model <MODEL_ID>');
248
+ console.log(' ctxman --auto-detect-llm');
249
+ console.log('\nExample:');
250
+ console.log(' ctxman --target-model claude-sonnet-4.5');
251
+ console.log(' ctxman --auto-detect-llm --cli\n');
252
+ }
253
+
254
+ function printStartupInfo(options) {
255
+ console.log('šŸš€ Ctxman v3.0.0');
256
+ console.log('='.repeat(50));
257
+
258
+ // Only show active options if any are set
259
+ const hasOptions = options.outputFormat || options.methodLevel || options.chunking?.enabled ||
260
+ options.saveReport || options.verbose || options.contextExport ||
261
+ options.contextToClipboard || options.gitingest;
262
+
263
+ if (hasOptions) {
264
+ console.log('šŸ“‹ Active options:');
265
+ if (options.outputFormat) {
266
+ console.log(` Output format: ${options.outputFormat}`);
267
+ }
268
+ if (options.methodLevel) {
269
+ console.log(' Method-level analysis: enabled');
270
+ }
271
+ if (options.saveReport) {
272
+ console.log(' Save report: enabled');
273
+ }
274
+ if (options.gitingest) {
275
+ console.log(' GitIngest format: enabled');
276
+ }
277
+ if (options.contextExport) {
278
+ console.log(' Context export: enabled');
279
+ }
280
+ if (options.contextToClipboard) {
281
+ console.log(' Copy to clipboard: enabled');
282
+ }
283
+ if (options.chunking?.enabled) {
284
+ console.log(` Chunking: ${options.chunking.strategy} (${options.chunking.maxTokensPerChunk.toLocaleString()} tokens/chunk)`);
285
+ }
286
+ console.log();
287
+ }
288
+ }
289
+
290
+ function printHelp() {
291
+ console.log('Ctxman v3.0.0 - AI Development Platform with Plugin Architecture and Git Integration');
292
+ console.log();
293
+ console.log('Usage: ctxman [options]');
294
+ console.log();
295
+ console.log('Default Mode:');
296
+ console.log(' ctxman Launch interactive wizard (DEFAULT)');
297
+ console.log(' --cli Use CLI mode instead of wizard');
298
+ console.log();
299
+ console.log('Analysis Options:');
300
+ console.log(' -s, --save-report Save detailed JSON report');
301
+ console.log(' -v, --verbose Show all included files');
302
+ console.log(' -m, --method-level Enable method-level analysis');
303
+ console.log(' -g, --gitingest Generate GitIngest-style digest');
304
+ console.log();
305
+ console.log('Output Options (v2.3.0):');
306
+ console.log(' -o, --output FORMAT Output format (default: toon)');
307
+ console.log(' Formats: toon, json, yaml, csv, xml, markdown, gitingest');
308
+ console.log(' --context-export Generate LLM context file');
309
+ console.log(' --context-clipboard Copy context to clipboard');
310
+ console.log(' --list-formats List all available output formats');
311
+ console.log();
312
+ console.log('UI Options (v2.3.0):');
313
+ console.log(' --simple Simple text-based output (no fancy UI)');
314
+ console.log(' --dashboard Live dashboard mode');
315
+ console.log(' --wizard Force interactive wizard mode');
316
+ console.log();
317
+ console.log('Chunking Options (v2.3.0):');
318
+ console.log(' --chunk Enable smart chunking for large repos');
319
+ console.log(' --chunk-strategy TYPE Chunking strategy (smart, size, file, directory)');
320
+ console.log(' --chunk-size TOKENS Max tokens per chunk (default: 100000)');
321
+ console.log();
322
+ console.log('LLM Optimization (v2.3.7):');
323
+ console.log(' --target-model MODEL Optimize for specific LLM (e.g., claude-sonnet-4.5)');
324
+ console.log(' --auto-detect-llm Auto-detect LLM from environment variables');
325
+ console.log(' --list-llms List all supported LLM models');
326
+ console.log();
327
+ console.log('Git Integration (v3.0.0):');
328
+ console.log(' --changed-only Analyze only files with uncommitted changes');
329
+ console.log(' --changed-since REF Analyze files changed since commit/branch');
330
+ console.log(' --with-authors Include author information');
331
+ console.log(' --with-history Include commit history');
332
+ console.log();
333
+ console.log('Platform Features (v3.0.0):');
334
+ console.log(' serve [options] Start REST API server');
335
+ console.log(' --port PORT Server port (default: 3000)');
336
+ console.log(' --auth-token TOKEN API authentication token');
337
+ console.log(' watch [options] Watch mode with auto-analysis');
338
+ console.log(' --debounce MS Debounce delay (default: 1000ms)');
339
+ console.log();
340
+ console.log('General Options:');
341
+ console.log(' -h, --help Show this help');
342
+ console.log(' --version Show version number');
343
+ console.log();
344
+ console.log('Configuration Files:');
345
+ console.log(' .contextinclude Include only specified files');
346
+ console.log(' .contextignore Exclude specified files');
347
+ console.log(' .methodinclude Include only specified methods');
348
+ console.log(' .methodignore Exclude specified methods');
349
+ console.log();
350
+ console.log('Format Conversion (v2.3.2):');
351
+ console.log(' convert INPUT --from FORMAT --to FORMAT');
352
+ console.log(' Convert between formats');
353
+ console.log(' Examples:');
354
+ console.log(' ctxman convert report.json --from json --to toon');
355
+ console.log(' ctxman convert data.toon --from toon --to yaml');
356
+ console.log(' ctxman convert context.yaml --from yaml --to json');
357
+ console.log();
358
+ console.log('GitHub Integration (v2.3.6+):');
359
+ console.log(' github URL [options] Generate GitIngest from GitHub repository');
360
+ console.log(' git URL [options] Alias for github command');
361
+ console.log(' Examples:');
362
+ console.log(' ctxman github facebook/react');
363
+ console.log(' ctxman github https://github.com/vercel/next.js --branch canary');
364
+ console.log(' ctxman git angular/angular -o docs/angular.txt');
365
+ console.log();
366
+ console.log('Examples:');
367
+ console.log(' ctxman # Launch interactive wizard (DEFAULT)');
368
+ console.log(' ctxman --cli # Use CLI mode');
369
+ console.log(' ctxman --cli -o json --save-report # CLI: JSON format + save report');
370
+ console.log(' ctxman --cli -o toon --context-clipboard # CLI: TOON to clipboard');
371
+ console.log(' ctxman --cli --gitingest --chunk # CLI: GitIngest with chunking');
372
+ console.log(' ctxman --cli -m -o yaml # CLI: Method-level + YAML format');
373
+ console.log(' ctxman --cli --chunk --chunk-strategy smart # CLI: Smart chunking');
374
+ console.log(' ctxman convert data.json --from json --to toon # Convert formats');
375
+ console.log();
376
+ console.log('Format Comparison (token efficiency):');
377
+ console.log(' TOON: 40-50% reduction (most efficient)');
378
+ console.log(' JSON: Standard format (baseline)');
379
+ console.log(' YAML: Human-readable (5-10% larger than JSON)');
380
+ console.log(' Markdown: Documentation-friendly (20-30% larger)');
381
+ console.log();
382
+ console.log('For more information: https://github.com/hakkisagdic/ctxman');
383
+ }
384
+
385
+ function listFormats() {
386
+ const registry = new FormatRegistry();
387
+ const formats = registry.getAllInfo();
388
+
389
+ console.log('šŸ“‹ Available Output Formats:\n');
390
+ console.log('Format'.padEnd(15) + 'Description'.padEnd(50) + 'Extension');
391
+ console.log('='.repeat(80));
392
+
393
+ for (const [name, info] of Object.entries(formats)) {
394
+ console.log(
395
+ name.padEnd(15) +
396
+ info.description.substring(0, 48).padEnd(50) +
397
+ info.extension
398
+ );
399
+ }
400
+
401
+ console.log();
402
+ console.log('Usage: ctxman --output <format>');
403
+ console.log('Example: ctxman --output toon --context-clipboard');
404
+ }
405
+
406
+ function getChangedSince(args) {
407
+ const sinceIndex = args.findIndex(arg => arg === '--changed-since');
408
+ if (sinceIndex !== -1 && args[sinceIndex + 1]) {
409
+ return args[sinceIndex + 1];
410
+ }
411
+ return null;
412
+ }
413
+
414
+ async function runAPIServer(args) {
415
+ const portIndex = args.findIndex(arg => arg === '--port');
416
+ const port = portIndex !== -1 && args[portIndex + 1]
417
+ ? parseInt(args[portIndex + 1], 10)
418
+ : 3000;
419
+
420
+ const authTokenIndex = args.findIndex(arg => arg === '--auth-token');
421
+ const authToken = authTokenIndex !== -1 && args[authTokenIndex + 1]
422
+ ? args[authTokenIndex + 1]
423
+ : null;
424
+
425
+ const server = new APIServer({ port, authToken });
426
+
427
+ // Handle shutdown
428
+ process.on('SIGINT', () => {
429
+ console.log('\n\nšŸ›‘ Shutting down server...');
430
+ server.stop();
431
+ process.exit(0);
432
+ });
433
+
434
+ server.start();
435
+ }
436
+
437
+ async function runChangedFilesAnalysis(options) {
438
+ console.log('šŸ”€ Git Integration - Analyzing Changed Files');
439
+ console.log('═'.repeat(60));
440
+ console.log();
441
+
442
+ const diffAnalyzer = new DiffAnalyzer(options.projectRoot);
443
+ const changes = diffAnalyzer.analyzeChanges(options.changedSince);
444
+
445
+ console.log(`šŸ“ Found ${changes.totalChangedFiles} changed files`);
446
+ if (options.changedSince) {
447
+ console.log(` Since: ${options.changedSince}`);
448
+ }
449
+ console.log(` Impact: ${changes.impact.level.toUpperCase()} (score: ${changes.impact.score})`);
450
+ console.log();
451
+
452
+ if (changes.totalChangedFiles === 0) {
453
+ console.log('āœ… No changed files to analyze');
454
+ return;
455
+ }
456
+
457
+ // Analyze only changed files
458
+ const analyzer = new TokenAnalyzer(options.projectRoot, {
459
+ ...options,
460
+ fileFilter: (filePath) => changes.changedFiles.includes(filePath)
461
+ });
462
+
463
+ analyzer.run();
464
+ }
465
+
466
+ async function runWatchMode(args) {
467
+ console.log('šŸ‘ļø Starting watch mode...\n');
468
+
469
+ const projectRoot = process.cwd();
470
+ const debounce = args.includes('--debounce')
471
+ ? parseInt(args[args.indexOf('--debounce') + 1], 10) || 1000
472
+ : 1000;
473
+
474
+ const watcher = new FileWatcher(projectRoot, { debounce });
475
+ const analyzer = new IncrementalAnalyzer({ methodLevel: args.includes('-m') });
476
+
477
+ // Handle file changes
478
+ watcher.on('file:changed', async (event) => {
479
+ console.log(`\nšŸ“ File ${event.type}: ${event.relativePath}`);
480
+ await analyzer.analyzeChange(event);
481
+ });
482
+
483
+ // Handle analysis complete
484
+ analyzer.on('analysis:complete', (event) => {
485
+ console.log(` āœ… Analysis complete: ${event.analysis.tokens} tokens (${event.elapsed}ms)`);
486
+
487
+ const stats = analyzer.getStats();
488
+ console.log(` šŸ“Š Total: ${stats.totalFiles} files, ${stats.totalTokens.toLocaleString()} tokens`);
489
+ });
490
+
491
+ // Handle file deletion
492
+ analyzer.on('file:deleted', (event) => {
493
+ console.log(` šŸ—‘ļø File deleted: ${event.file} (-${event.oldTokens} tokens)`);
494
+ });
495
+
496
+ // Start watching
497
+ watcher.start();
498
+
499
+ console.log('āœ… Watch mode active');
500
+ console.log(' Press Ctrl+C to stop\n');
501
+
502
+ // Keep process alive
503
+ process.on('SIGINT', () => {
504
+ console.log('\n\nšŸ›‘ Stopping watch mode...');
505
+ watcher.stop();
506
+ process.exit(0);
507
+ });
508
+ }
509
+
510
+ async function runWizard() {
511
+ try {
512
+ // Dynamic imports for ESM modules
513
+ const ReactModule = await import('react');
514
+ const React = ReactModule.default || ReactModule;
515
+ const { render } = await import('ink');
516
+ const Wizard = (await import('../lib/ui/wizard.js')).default;
517
+
518
+ // Clear screen for clean wizard display
519
+ console.clear();
520
+ console.log('šŸ§™ Starting interactive wizard...\n');
521
+
522
+ const instance = render(
523
+ React.createElement(Wizard, {
524
+ onComplete: (answers) => {
525
+ instance.unmount();
526
+
527
+ console.log('\n✨ Wizard complete! Running analysis with your configuration...\n');
528
+
529
+ // Run analyzer with wizard configuration
530
+ const options = {
531
+ outputFormat: answers.outputFormat,
532
+ useCase: answers.useCase,
533
+ targetModel: answers.targetModel,
534
+ projectRoot: process.cwd(),
535
+ simple: true, // No export menu
536
+ contextExport: true // Auto-export to file
537
+ };
538
+
539
+ const analyzer = new TokenAnalyzer(options.projectRoot, options);
540
+ analyzer.run();
541
+
542
+ console.log('\nāœ… Analysis complete! Context exported to llm-context.json\n');
543
+ }
544
+ })
545
+ );
546
+ } catch (error) {
547
+ throw error; // Re-throw to be caught by main()
548
+ }
549
+ }
550
+
551
+ async function runDashboard() {
552
+ try {
553
+ // Dynamic imports for ESM modules
554
+ const ReactModule = await import('react');
555
+ const React = ReactModule.default || ReactModule;
556
+ const { render } = await import('ink');
557
+ const Dashboard = (await import('../lib/ui/dashboard.js')).default;
558
+
559
+ // Clear console and show loading message
560
+ console.clear();
561
+ console.log('šŸ“Š Analyzing project for dashboard...\n');
562
+
563
+ // Run analyzer silently (no console output)
564
+ const originalLog = console.log;
565
+ const logs = [];
566
+ console.log = (...args) => logs.push(args); // Capture logs
567
+
568
+ const analyzer = new TokenAnalyzer(process.cwd(), {
569
+ simple: true,
570
+ verbose: false,
571
+ dashboard: true // Skip export handling
572
+ });
573
+ const stats = analyzer.run();
574
+
575
+ console.log = originalLog; // Restore console.log
576
+
577
+ // Clear and render dashboard
578
+ console.clear();
579
+
580
+ const instance = render(
581
+ React.createElement(Dashboard, {
582
+ stats,
583
+ topFiles: stats.largestFiles || [],
584
+ status: 'complete',
585
+ onExit: () => {
586
+ instance.unmount();
587
+ process.exit(0);
588
+ }
589
+ })
590
+ );
591
+ } catch (error) {
592
+ throw error; // Re-throw to be caught by main()
593
+ }
594
+ }
595
+
596
+ function runFormatConversion(args) {
597
+ // v2.3.2: Format conversion utility
598
+ const converter = new FormatConverter();
599
+
600
+ // Parse arguments
601
+ const fromIndex = args.findIndex(arg => arg === '--from');
602
+ const toIndex = args.findIndex(arg => arg === '--to');
603
+ const inputIndex = args.findIndex(arg => arg === 'convert') + 1;
604
+
605
+ if (fromIndex === -1 || toIndex === -1) {
606
+ console.error('āŒ Format conversion requires --from and --to flags');
607
+ console.error(' Usage: ctxman convert input.json --from json --to toon');
608
+ process.exit(1);
609
+ }
610
+
611
+ const inputFile = args[inputIndex];
612
+ const fromFormat = args[fromIndex + 1];
613
+ const toFormat = args[toIndex + 1];
614
+
615
+ if (!inputFile) {
616
+ console.error('āŒ No input file specified');
617
+ process.exit(1);
618
+ }
619
+
620
+ // Generate output filename
621
+ const outputFile = inputFile.replace(/\.[^.]+$/, `.${toFormat}`);
622
+
623
+ console.log('šŸ”„ Converting formats...');
624
+ console.log(` Input: ${inputFile} (${fromFormat})`);
625
+ console.log(` Output: ${outputFile} (${toFormat})`);
626
+ console.log();
627
+
628
+ try {
629
+ const result = converter.convertFile(inputFile, outputFile, fromFormat, toFormat);
630
+
631
+ console.log('āœ… Conversion successful!');
632
+ console.log(` Input size: ${result.inputSize.toLocaleString()} chars`);
633
+ console.log(` Output size: ${result.outputSize.toLocaleString()} chars`);
634
+ console.log(` Savings: ${result.savingsPercent} (${result.savings} chars)`);
635
+ console.log(` Output file: ${result.outputFile}`);
636
+ } catch (error) {
637
+ console.error('āŒ Conversion failed:', error.message);
638
+ process.exit(1);
639
+ }
640
+ }
641
+
642
+ // ESM entry point
643
+ main().catch(error => {
644
+ console.error('āŒ Error:', error.message);
645
+ // Exit codes:
646
+ // 0: Success
647
+ // 1: General error
648
+ // 2: Validation/argument error
649
+ process.exit(1);
650
+ });