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
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File Type Detection and Utilities
|
|
3
|
+
* Identifies text files, code files, and binary files
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import path from 'path';
|
|
7
|
+
|
|
8
|
+
class FileUtils {
|
|
9
|
+
/**
|
|
10
|
+
* Check if file is a text file
|
|
11
|
+
* @param {string} filePath - File path
|
|
12
|
+
* @returns {boolean}
|
|
13
|
+
*/
|
|
14
|
+
static isText(filePath) {
|
|
15
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
16
|
+
const basename = path.basename(filePath).toLowerCase();
|
|
17
|
+
|
|
18
|
+
const textExtensions = new Set([
|
|
19
|
+
'.js', '.ts', '.jsx', '.tsx', '.json', '.md', '.txt', '.yml', '.yaml',
|
|
20
|
+
'.html', '.css', '.scss', '.sass', '.less', '.xml', '.svg',
|
|
21
|
+
'.sh', '.bash', '.zsh', '.py', '.rb', '.php', '.java', '.c', '.cpp', '.cc', '.h', '.hpp',
|
|
22
|
+
'.go', '.rs', '.swift', '.kt', '.kts', '.cs', '.scala', '.sql', '.toml', '.ini', '.conf'
|
|
23
|
+
]);
|
|
24
|
+
|
|
25
|
+
const textFiles = ['dockerfile', 'makefile', 'license', 'readme', 'changelog'];
|
|
26
|
+
|
|
27
|
+
return textExtensions.has(ext) ||
|
|
28
|
+
textFiles.some(name => basename.includes(name));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Check if file is a code file (supports method extraction)
|
|
33
|
+
* @param {string} filePath - File path
|
|
34
|
+
* @returns {boolean}
|
|
35
|
+
*/
|
|
36
|
+
static isCode(filePath) {
|
|
37
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
38
|
+
return [
|
|
39
|
+
'.js', '.ts', '.jsx', '.tsx', // JavaScript/TypeScript
|
|
40
|
+
'.rs', // Rust
|
|
41
|
+
'.cs', // C#
|
|
42
|
+
'.go', // Go
|
|
43
|
+
'.java', // Java
|
|
44
|
+
'.py', // Python
|
|
45
|
+
'.php', // PHP
|
|
46
|
+
'.rb', // Ruby
|
|
47
|
+
'.kt', '.kts', // Kotlin
|
|
48
|
+
'.swift', // Swift
|
|
49
|
+
'.c', '.cpp', '.cc', '.h', '.hpp', // C/C++
|
|
50
|
+
'.scala' // Scala
|
|
51
|
+
].includes(ext);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Get file type category
|
|
56
|
+
* @param {string} filePath - File path
|
|
57
|
+
* @returns {string} Type: 'code', 'config', 'doc', 'style', 'other'
|
|
58
|
+
*/
|
|
59
|
+
static getType(filePath) {
|
|
60
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
61
|
+
|
|
62
|
+
const categories = {
|
|
63
|
+
code: ['.js', '.ts', '.jsx', '.tsx', '.py', '.rb', '.php', '.java', '.c', '.cpp', '.cc', '.h', '.hpp', '.go', '.rs', '.cs', '.kt', '.kts', '.swift', '.scala'],
|
|
64
|
+
config: ['.json', '.yml', '.yaml', '.toml', '.ini', '.conf', '.xml'],
|
|
65
|
+
doc: ['.md', '.txt', '.pdf', '.doc', '.docx'],
|
|
66
|
+
style: ['.css', '.scss', '.sass', '.less'],
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
for (const [type, extensions] of Object.entries(categories)) {
|
|
70
|
+
if (extensions.includes(ext)) {
|
|
71
|
+
return type;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return 'other';
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Get file extension without dot
|
|
80
|
+
* @param {string} filePath - File path
|
|
81
|
+
* @returns {string} Extension or 'no-extension'
|
|
82
|
+
*/
|
|
83
|
+
static getExtension(filePath) {
|
|
84
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
85
|
+
return ext ? ext.slice(1) : 'no-extension';
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export default FileUtils;
|
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Format Converter Utilities
|
|
3
|
+
* v2.3.2 - Convert between different output formats
|
|
4
|
+
*
|
|
5
|
+
* Supports conversion between:
|
|
6
|
+
* - TOON ↔ JSON
|
|
7
|
+
* - JSON ↔ YAML
|
|
8
|
+
* - JSON ↔ XML
|
|
9
|
+
* - JSON ↔ CSV
|
|
10
|
+
* - JSON ↔ Markdown
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import FormatRegistry from '../formatters/format-registry.js';
|
|
14
|
+
import ToonFormatter from '../formatters/toon-formatter.js';
|
|
15
|
+
import fs from 'fs';
|
|
16
|
+
import path from 'path';
|
|
17
|
+
|
|
18
|
+
class FormatConverter {
|
|
19
|
+
constructor() {
|
|
20
|
+
this.registry = new FormatRegistry();
|
|
21
|
+
this.toonFormatter = new ToonFormatter();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Convert from one format to another
|
|
26
|
+
* @param {string} input - Input data as string
|
|
27
|
+
* @param {string} fromFormat - Source format
|
|
28
|
+
* @param {string} toFormat - Target format
|
|
29
|
+
* @returns {Object} Conversion result with output and metadata
|
|
30
|
+
*/
|
|
31
|
+
convert(input, fromFormat, toFormat) {
|
|
32
|
+
// Step 1: Parse input to JavaScript object
|
|
33
|
+
const data = this.parse(input, fromFormat);
|
|
34
|
+
|
|
35
|
+
// Step 2: Encode to target format
|
|
36
|
+
const output = this.encode(data, toFormat);
|
|
37
|
+
|
|
38
|
+
// Step 3: Calculate metadata
|
|
39
|
+
const inputSize = input.length;
|
|
40
|
+
const outputSize = output.length;
|
|
41
|
+
const savings = inputSize - outputSize;
|
|
42
|
+
const savingsPercentage = inputSize > 0 ? ((savings / inputSize) * 100).toFixed(1) : '0.0';
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
output,
|
|
46
|
+
metadata: {
|
|
47
|
+
inputSize,
|
|
48
|
+
outputSize,
|
|
49
|
+
savings,
|
|
50
|
+
savingsPercentage: parseFloat(savingsPercentage),
|
|
51
|
+
fromFormat,
|
|
52
|
+
toFormat
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Parse string input to JavaScript object
|
|
59
|
+
*/
|
|
60
|
+
parse(input, format) {
|
|
61
|
+
switch (format.toLowerCase()) {
|
|
62
|
+
case 'json':
|
|
63
|
+
case 'json-compact':
|
|
64
|
+
return JSON.parse(input);
|
|
65
|
+
|
|
66
|
+
case 'yaml':
|
|
67
|
+
return this.parseYAML(input);
|
|
68
|
+
|
|
69
|
+
case 'toon':
|
|
70
|
+
throw new Error('TOON parsing not yet implemented. Use JSON as intermediate format.');
|
|
71
|
+
|
|
72
|
+
case 'xml':
|
|
73
|
+
throw new Error('XML parsing not yet implemented. Use JSON as intermediate format.');
|
|
74
|
+
|
|
75
|
+
case 'csv':
|
|
76
|
+
return this.parseCSV(input);
|
|
77
|
+
|
|
78
|
+
case 'markdown':
|
|
79
|
+
throw new Error('Markdown parsing not yet implemented. Use JSON as intermediate format.');
|
|
80
|
+
|
|
81
|
+
default:
|
|
82
|
+
throw new Error(`Unknown format for parsing: ${format}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Encode JavaScript object to format
|
|
88
|
+
*/
|
|
89
|
+
encode(data, format) {
|
|
90
|
+
try {
|
|
91
|
+
return this.registry.encode(format, data);
|
|
92
|
+
} catch (error) {
|
|
93
|
+
throw new Error(`Failed to encode to ${format}: ${error.message}`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Simple YAML parser
|
|
99
|
+
* Note: This is a basic implementation. For complex YAML, use a proper library.
|
|
100
|
+
*/
|
|
101
|
+
parseYAML(yamlString) {
|
|
102
|
+
const lines = yamlString.split('\n').filter(line => line.trim() && !line.trim().startsWith('#'));
|
|
103
|
+
const result = {};
|
|
104
|
+
let currentKey = null;
|
|
105
|
+
let currentArray = null;
|
|
106
|
+
|
|
107
|
+
for (const line of lines) {
|
|
108
|
+
const trimmed = line.trim();
|
|
109
|
+
|
|
110
|
+
// Array item
|
|
111
|
+
if (trimmed.startsWith('- ')) {
|
|
112
|
+
const value = trimmed.substring(2).trim();
|
|
113
|
+
|
|
114
|
+
// Initialize array if needed
|
|
115
|
+
if (currentKey && (!result[currentKey] || !Array.isArray(result[currentKey]))) {
|
|
116
|
+
result[currentKey] = [];
|
|
117
|
+
currentArray = result[currentKey];
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (currentArray) {
|
|
121
|
+
currentArray.push(this.parseYAMLValue(value));
|
|
122
|
+
}
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Key-value pair
|
|
127
|
+
const colonIndex = trimmed.indexOf(':');
|
|
128
|
+
if (colonIndex !== -1) {
|
|
129
|
+
const key = trimmed.substring(0, colonIndex).trim();
|
|
130
|
+
const value = trimmed.substring(colonIndex + 1).trim();
|
|
131
|
+
|
|
132
|
+
if (value === '') {
|
|
133
|
+
// Object or array follows
|
|
134
|
+
currentKey = key;
|
|
135
|
+
result[key] = {}; // Default to object, will be overwritten if array items found
|
|
136
|
+
currentArray = null; // Reset current array
|
|
137
|
+
} else {
|
|
138
|
+
result[key] = this.parseYAMLValue(value);
|
|
139
|
+
currentKey = null;
|
|
140
|
+
currentArray = null;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return result;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Parse YAML value (string, number, boolean)
|
|
150
|
+
*/
|
|
151
|
+
parseYAMLValue(value) {
|
|
152
|
+
if (value === 'true') return true;
|
|
153
|
+
if (value === 'false') return false;
|
|
154
|
+
if (value === 'null') return null;
|
|
155
|
+
if (/^-?\d+$/.test(value)) return parseInt(value, 10);
|
|
156
|
+
if (/^-?\d+\.\d+$/.test(value)) return parseFloat(value);
|
|
157
|
+
if (value.startsWith('"') && value.endsWith('"')) {
|
|
158
|
+
return value.substring(1, value.length - 1);
|
|
159
|
+
}
|
|
160
|
+
return value;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Simple CSV parser
|
|
165
|
+
*/
|
|
166
|
+
parseCSV(csvString) {
|
|
167
|
+
const lines = csvString.split('\n').filter(line => line.trim());
|
|
168
|
+
if (lines.length === 0) return { rows: [] };
|
|
169
|
+
|
|
170
|
+
const headers = this.parseCSVLine(lines[0]);
|
|
171
|
+
const rows = [];
|
|
172
|
+
|
|
173
|
+
for (let i = 1; i < lines.length; i++) {
|
|
174
|
+
const values = this.parseCSVLine(lines[i]);
|
|
175
|
+
const row = {};
|
|
176
|
+
headers.forEach((header, index) => {
|
|
177
|
+
row[header] = values[index] || '';
|
|
178
|
+
});
|
|
179
|
+
rows.push(row);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return { headers, rows };
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Parse CSV line (handles quoted values)
|
|
187
|
+
*/
|
|
188
|
+
parseCSVLine(line) {
|
|
189
|
+
const values = [];
|
|
190
|
+
let current = '';
|
|
191
|
+
let inQuotes = false;
|
|
192
|
+
|
|
193
|
+
for (let i = 0; i < line.length; i++) {
|
|
194
|
+
const char = line[i];
|
|
195
|
+
|
|
196
|
+
if (char === '"') {
|
|
197
|
+
inQuotes = !inQuotes;
|
|
198
|
+
} else if (char === ',' && !inQuotes) {
|
|
199
|
+
values.push(current.trim());
|
|
200
|
+
current = '';
|
|
201
|
+
} else {
|
|
202
|
+
current += char;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
values.push(current.trim());
|
|
207
|
+
return values;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Convert file from one format to another
|
|
212
|
+
* @param {string} inputFile - Input file path
|
|
213
|
+
* @param {string} outputFile - Output file path
|
|
214
|
+
* @param {string} fromFormat - Source format (auto-detect from extension if not provided)
|
|
215
|
+
* @param {string} toFormat - Target format (auto-detect from extension if not provided)
|
|
216
|
+
*/
|
|
217
|
+
convertFile(inputFile, outputFile, fromFormat = null, toFormat = null) {
|
|
218
|
+
// Auto-detect formats from file extensions
|
|
219
|
+
if (!fromFormat) {
|
|
220
|
+
const ext = path.extname(inputFile).substring(1);
|
|
221
|
+
fromFormat = this.extensionToFormat(ext);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (!toFormat) {
|
|
225
|
+
const ext = path.extname(outputFile).substring(1);
|
|
226
|
+
toFormat = this.extensionToFormat(ext);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Read input file
|
|
230
|
+
const input = fs.readFileSync(inputFile, 'utf8');
|
|
231
|
+
|
|
232
|
+
// Convert
|
|
233
|
+
const result = this.convert(input, fromFormat, toFormat);
|
|
234
|
+
|
|
235
|
+
// Write output file
|
|
236
|
+
fs.writeFileSync(outputFile, result.output, 'utf8');
|
|
237
|
+
|
|
238
|
+
return {
|
|
239
|
+
inputFile,
|
|
240
|
+
outputFile,
|
|
241
|
+
fromFormat,
|
|
242
|
+
toFormat,
|
|
243
|
+
inputSize: input.length,
|
|
244
|
+
outputSize: result.output.length,
|
|
245
|
+
savings: input.length - result.output.length,
|
|
246
|
+
savingsPercent: result.metadata.savingsPercentage + '%'
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Map file extension to format name
|
|
252
|
+
*/
|
|
253
|
+
extensionToFormat(ext) {
|
|
254
|
+
const map = {
|
|
255
|
+
'json': 'json',
|
|
256
|
+
'toon': 'toon',
|
|
257
|
+
'yaml': 'yaml',
|
|
258
|
+
'yml': 'yaml',
|
|
259
|
+
'csv': 'csv',
|
|
260
|
+
'xml': 'xml',
|
|
261
|
+
'md': 'markdown',
|
|
262
|
+
'txt': 'gitingest'
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
return map[ext.toLowerCase()] || 'json';
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Batch convert multiple files
|
|
270
|
+
* @param {Array} files - Array of {input, output, from, to}
|
|
271
|
+
* @returns {Array} Conversion results
|
|
272
|
+
*/
|
|
273
|
+
batchConvert(files) {
|
|
274
|
+
const results = [];
|
|
275
|
+
|
|
276
|
+
for (const file of files) {
|
|
277
|
+
try {
|
|
278
|
+
const result = this.convertFile(
|
|
279
|
+
file.input,
|
|
280
|
+
file.output,
|
|
281
|
+
file.from || null,
|
|
282
|
+
file.to || null
|
|
283
|
+
);
|
|
284
|
+
results.push({ success: true, ...result });
|
|
285
|
+
} catch (error) {
|
|
286
|
+
results.push({
|
|
287
|
+
success: false,
|
|
288
|
+
inputFile: file.input,
|
|
289
|
+
error: error.message
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
return results;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Get supported conversions
|
|
299
|
+
*/
|
|
300
|
+
getSupportedConversions() {
|
|
301
|
+
return {
|
|
302
|
+
fullySupported: [
|
|
303
|
+
'JSON → TOON',
|
|
304
|
+
'JSON → YAML',
|
|
305
|
+
'JSON → CSV',
|
|
306
|
+
'JSON → XML',
|
|
307
|
+
'JSON → Markdown',
|
|
308
|
+
'YAML → JSON (basic)',
|
|
309
|
+
'CSV → JSON'
|
|
310
|
+
],
|
|
311
|
+
partialSupport: [
|
|
312
|
+
'TOON → JSON (decoder not implemented)',
|
|
313
|
+
'XML → JSON (parser not implemented)',
|
|
314
|
+
'Markdown → JSON (parser not implemented)'
|
|
315
|
+
],
|
|
316
|
+
planned: [
|
|
317
|
+
'TOON ↔ JSON (bidirectional)',
|
|
318
|
+
'XML ↔ JSON (bidirectional)',
|
|
319
|
+
'Advanced YAML parsing'
|
|
320
|
+
]
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
export default FormatConverter;
|