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