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
@@ -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;