ctxman 3.0.0 → 3.1.1

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 (224) hide show
  1. package/.ctxman/llm-profiles.json +1067 -39
  2. package/.ctxman/profiles/backend-team.json +13 -0
  3. package/.ctxman/profiles/default.json +12 -0
  4. package/.ctxman/profiles/devops-team.json +21 -0
  5. package/.ctxman/profiles/frontend-team.json +20 -0
  6. package/.ctxman/templates/bug-fix.json +17 -0
  7. package/.ctxman/templates/code-review.json +9 -0
  8. package/.ctxman/templates/documentation.json +9 -0
  9. package/.ctxman/templates/feature.json +9 -0
  10. package/.ctxman/templates/full-context.json +9 -0
  11. package/.ctxman/templates/refactoring.json +18 -0
  12. package/.ctxman/templates/testing.json +9 -0
  13. package/.ctxman/wizard-profiles/security-audit/profile.json +7 -1
  14. package/LICENSE +21 -0
  15. package/README.md +381 -136
  16. package/bin/cli.js +1880 -529
  17. package/bin/cm-ask.js +18 -67
  18. package/bin/cm-audit.js +121 -0
  19. package/bin/cm-count.js +151 -0
  20. package/bin/cm-gitingest.js +128 -115
  21. package/bin/cm-map.js +242 -0
  22. package/bin/cm-mcp.js +293 -0
  23. package/bin/cm-pack.js +405 -0
  24. package/bin/cm-policy.js +170 -0
  25. package/bin/cm-pr.js +248 -0
  26. package/bin/cm-search.js +178 -0
  27. package/bin/cm-update.js +128 -128
  28. package/bin/cm-why.js +197 -0
  29. package/bin/mcp-server.js +8 -24
  30. package/ctxman.js +237 -183
  31. package/examples/README.md +19 -4
  32. package/examples/github-actions/ctxman-check.yml +49 -0
  33. package/examples/wizard-profiles/security-audit/profile.json +7 -1
  34. package/index.d.ts +1422 -0
  35. package/index.js +55 -31
  36. package/lib/agents/pr-failure-agent.js +48 -45
  37. package/lib/analyzers/ai-suggester.js +451 -0
  38. package/lib/analyzers/dependency-scanner.js +505 -0
  39. package/lib/analyzers/duplicate-detector.js +264 -0
  40. package/lib/analyzers/go-method-analyzer.js +151 -110
  41. package/lib/analyzers/import-tracker.js +431 -0
  42. package/lib/analyzers/method-analyzer.js +1055 -434
  43. package/lib/analyzers/performance-dashboard.js +687 -0
  44. package/lib/analyzers/token-calculator.js +895 -504
  45. package/lib/analyzers/unused-exports-detector.js +336 -0
  46. package/lib/api/mcp/install.js +424 -0
  47. package/lib/api/mcp/prompts.js +281 -281
  48. package/lib/api/mcp/resources.js +300 -241
  49. package/lib/api/mcp/sandbox.js +89 -0
  50. package/lib/api/mcp/server.js +772 -312
  51. package/lib/api/mcp/start.js +125 -0
  52. package/lib/api/mcp/transports/HttpSseTransport.js +60 -52
  53. package/lib/api/rest/server.js +105 -28
  54. package/lib/cache/CacheManager.js +9 -17
  55. package/lib/core/Analyzer.js +16 -10
  56. package/lib/core/ContextBuilder.js +19 -28
  57. package/lib/core/Reporter.js +15 -14
  58. package/lib/core/Scanner.js +81 -12
  59. package/lib/count/index.js +100 -0
  60. package/lib/formatters/format-registry.js +394 -372
  61. package/lib/formatters/gitingest-formatter.js +656 -621
  62. package/lib/formatters/toon-formatter-v1.3.js +637 -632
  63. package/lib/formatters/toon-formatter.js +239 -240
  64. package/lib/integrations/git/BlameTracker.js +4 -4
  65. package/lib/integrations/git/DiffAnalyzer.js +38 -31
  66. package/lib/integrations/git/GitClient.js +71 -53
  67. package/lib/lsp/server.js +311 -0
  68. package/lib/map/graph.js +299 -0
  69. package/lib/map/imports.js +233 -0
  70. package/lib/map/index.js +33 -0
  71. package/lib/map/rank.js +253 -0
  72. package/lib/map/render.js +346 -0
  73. package/lib/map/resolver.js +574 -0
  74. package/lib/pack/budget.js +111 -0
  75. package/lib/pack/code-lexer.js +360 -0
  76. package/lib/pack/compress.js +414 -0
  77. package/lib/pack/index.js +354 -0
  78. package/lib/pack/renderers/common.js +134 -0
  79. package/lib/pack/renderers/index.js +24 -0
  80. package/lib/pack/renderers/json.js +47 -0
  81. package/lib/pack/renderers/markdown.js +54 -0
  82. package/lib/pack/renderers/plain.js +38 -0
  83. package/lib/pack/renderers/xml.js +85 -0
  84. package/lib/pack/select.js +177 -0
  85. package/lib/pack/split.js +182 -0
  86. package/lib/pack/tree.js +35 -0
  87. package/lib/parsers/gitignore-parser.js +116 -107
  88. package/lib/parsers/method-filter-parser.js +50 -33
  89. package/lib/plugins/ExporterPlugin.js +3 -3
  90. package/lib/plugins/LanguagePlugin.js +3 -3
  91. package/lib/plugins/PluginManager.js +7 -13
  92. package/lib/policy/audit.js +312 -0
  93. package/lib/policy/enforce.js +125 -0
  94. package/lib/policy/ignore-files.js +169 -0
  95. package/lib/policy/index.js +53 -0
  96. package/lib/policy/policy.js +489 -0
  97. package/lib/pr/diff-parser.js +267 -0
  98. package/lib/pr/method-mapper.js +148 -0
  99. package/lib/pr/pr-context.js +829 -0
  100. package/lib/pr/pr-git.js +149 -0
  101. package/lib/pr/pr-renderer.js +644 -0
  102. package/lib/pr/related-tests.js +296 -0
  103. package/lib/rag/DataSourcePlugin.js +5 -5
  104. package/lib/rag/EmbeddingProvider.js +19 -8
  105. package/lib/rag/Indexer.js +14 -5
  106. package/lib/rag/VectorStoreFactory.js +3 -3
  107. package/lib/rag/ask.js +122 -0
  108. package/lib/rag/sources/FileDataSource.js +12 -9
  109. package/lib/rag/stores/BaseVectorStore.js +5 -5
  110. package/lib/rag/stores/ChromaDBStore.js +10 -10
  111. package/lib/rag/stores/LanceDBStore.js +29 -19
  112. package/lib/rag/stores/PineconeStore.js +11 -9
  113. package/lib/search/bm25.js +433 -0
  114. package/lib/search/code-units.js +201 -0
  115. package/lib/search/symbols.js +189 -0
  116. package/lib/ui/dashboard.js +120 -89
  117. package/lib/ui/index.js +1 -6
  118. package/lib/ui/init-wizard-ui.js +550 -0
  119. package/lib/ui/progress-bar.js +94 -88
  120. package/lib/ui/select-input.js +40 -32
  121. package/lib/ui/template-selector.js +99 -0
  122. package/lib/ui/wizard.js +374 -329
  123. package/lib/utils/claude-token-counter.js +151 -0
  124. package/lib/utils/cli-output.js +17 -0
  125. package/lib/utils/clipboard-utils.js +49 -50
  126. package/lib/utils/config-utils.js +81 -66
  127. package/lib/utils/context-versioning.js +532 -0
  128. package/lib/utils/error-handler.js +112 -112
  129. package/lib/utils/file-utils.js +153 -69
  130. package/lib/utils/format-converter.js +356 -283
  131. package/lib/utils/git-utils.js +366 -294
  132. package/lib/utils/llm-cost-estimator.js +281 -0
  133. package/lib/utils/llm-detector.js +242 -78
  134. package/lib/utils/logger.js +246 -237
  135. package/lib/utils/messages.js +355 -0
  136. package/lib/utils/multi-repo-manager.js +364 -0
  137. package/lib/utils/profile-manager.js +380 -0
  138. package/lib/utils/safe-read.js +61 -0
  139. package/lib/utils/secret-redactor.js +61 -0
  140. package/lib/utils/snapshot-manager.js +679 -0
  141. package/lib/utils/template-manager.js +378 -0
  142. package/lib/utils/token-utils.js +201 -170
  143. package/lib/utils/tokenizer-adapter.js +421 -377
  144. package/lib/utils/updater.js +539 -506
  145. package/lib/watch/FileWatcher.js +12 -8
  146. package/lib/watch/IncrementalAnalyzer.js +7 -7
  147. package/lib/why/coupling.js +81 -0
  148. package/lib/why/errors.js +23 -0
  149. package/lib/why/git.js +169 -0
  150. package/lib/why/history.js +108 -0
  151. package/lib/why/index.js +720 -0
  152. package/lib/why/ownership.js +79 -0
  153. package/lib/why/paths.js +207 -0
  154. package/lib/why/references.js +259 -0
  155. package/lib/why/render.js +722 -0
  156. package/lib/why/search.js +358 -0
  157. package/lib/why/target.js +389 -0
  158. package/lib/wizards/config-generator.js +257 -0
  159. package/lib/wizards/project-detector.js +572 -0
  160. package/package.json +44 -10
  161. package/.ctxman/logs/ctxman-2026-07-02.log +0 -156
  162. package/.ctxman/self-knowledge/documents.lance/_transactions/0-b3dd1558-f95b-44b9-a154-18e8c330ae7a.txn +0 -0
  163. package/.ctxman/self-knowledge/documents.lance/_transactions/1-44bff6d4-e552-4010-a769-e09ad822698d.txn +0 -0
  164. package/.ctxman/self-knowledge/documents.lance/_transactions/10-37260794-5947-4ecb-b4c8-d732f58f40f1.txn +0 -0
  165. package/.ctxman/self-knowledge/documents.lance/_transactions/11-0d52a883-5518-463c-9b6d-986c3af89dfd.txn +0 -0
  166. package/.ctxman/self-knowledge/documents.lance/_transactions/12-ae354646-52f2-49c9-90fb-2e6ed8932663.txn +0 -0
  167. package/.ctxman/self-knowledge/documents.lance/_transactions/13-5d7d5e57-27da-498b-b734-091b9d38da13.txn +0 -0
  168. package/.ctxman/self-knowledge/documents.lance/_transactions/14-558ce928-810b-4252-a364-fdd1ee6f8a25.txn +0 -0
  169. package/.ctxman/self-knowledge/documents.lance/_transactions/15-27e3a58f-8b64-43b8-a9f4-e3369446968b.txn +0 -0
  170. package/.ctxman/self-knowledge/documents.lance/_transactions/16-a110c390-e5c8-4464-b976-bc7f5195fa15.txn +0 -0
  171. package/.ctxman/self-knowledge/documents.lance/_transactions/17-97513225-53de-4216-b61a-6337bd0cc4ad.txn +0 -0
  172. package/.ctxman/self-knowledge/documents.lance/_transactions/18-5ba450f0-c527-459c-bf4f-6d79b5e4285f.txn +0 -0
  173. package/.ctxman/self-knowledge/documents.lance/_transactions/19-600d3a63-5225-49e9-b8b7-30e037b564ab.txn +0 -0
  174. package/.ctxman/self-knowledge/documents.lance/_transactions/2-48a61b4e-92cf-4342-8376-914c1b99a9f9.txn +0 -0
  175. package/.ctxman/self-knowledge/documents.lance/_transactions/20-121dd912-5410-410e-84ff-82c7a23604a0.txn +0 -0
  176. package/.ctxman/self-knowledge/documents.lance/_transactions/3-bc6844ab-7ea3-4bb3-9ee1-538ce5664a9c.txn +0 -0
  177. package/.ctxman/self-knowledge/documents.lance/_transactions/4-d5e1754f-dbb5-454e-a9be-2a7da8d7bcf9.txn +0 -0
  178. package/.ctxman/self-knowledge/documents.lance/_transactions/5-4fae93df-0568-4b6f-8acf-8bef4bba6047.txn +0 -0
  179. package/.ctxman/self-knowledge/documents.lance/_transactions/6-4b1b8b87-068e-49a3-ba2b-d8dae628b252.txn +0 -0
  180. package/.ctxman/self-knowledge/documents.lance/_transactions/7-63d040fb-d2fa-4c0f-bed0-53411918a7b0.txn +0 -0
  181. package/.ctxman/self-knowledge/documents.lance/_transactions/8-c5628cf6-264c-420f-aab8-bc96a9083591.txn +0 -0
  182. package/.ctxman/self-knowledge/documents.lance/_transactions/9-c94570aa-5e53-4fcb-971f-cecc1d1d5492.txn +0 -0
  183. package/.ctxman/self-knowledge/documents.lance/_versions/1.manifest +0 -0
  184. package/.ctxman/self-knowledge/documents.lance/_versions/10.manifest +0 -0
  185. package/.ctxman/self-knowledge/documents.lance/_versions/11.manifest +0 -0
  186. package/.ctxman/self-knowledge/documents.lance/_versions/12.manifest +0 -0
  187. package/.ctxman/self-knowledge/documents.lance/_versions/13.manifest +0 -0
  188. package/.ctxman/self-knowledge/documents.lance/_versions/14.manifest +0 -0
  189. package/.ctxman/self-knowledge/documents.lance/_versions/15.manifest +0 -0
  190. package/.ctxman/self-knowledge/documents.lance/_versions/16.manifest +0 -0
  191. package/.ctxman/self-knowledge/documents.lance/_versions/17.manifest +0 -0
  192. package/.ctxman/self-knowledge/documents.lance/_versions/18.manifest +0 -0
  193. package/.ctxman/self-knowledge/documents.lance/_versions/19.manifest +0 -0
  194. package/.ctxman/self-knowledge/documents.lance/_versions/2.manifest +0 -0
  195. package/.ctxman/self-knowledge/documents.lance/_versions/20.manifest +0 -0
  196. package/.ctxman/self-knowledge/documents.lance/_versions/21.manifest +0 -0
  197. package/.ctxman/self-knowledge/documents.lance/_versions/3.manifest +0 -0
  198. package/.ctxman/self-knowledge/documents.lance/_versions/4.manifest +0 -0
  199. package/.ctxman/self-knowledge/documents.lance/_versions/5.manifest +0 -0
  200. package/.ctxman/self-knowledge/documents.lance/_versions/6.manifest +0 -0
  201. package/.ctxman/self-knowledge/documents.lance/_versions/7.manifest +0 -0
  202. package/.ctxman/self-knowledge/documents.lance/_versions/8.manifest +0 -0
  203. package/.ctxman/self-knowledge/documents.lance/_versions/9.manifest +0 -0
  204. package/.ctxman/self-knowledge/documents.lance/data/000010010011011101000010de8621405d9d5cb06540dbf02c.lance +0 -0
  205. package/.ctxman/self-knowledge/documents.lance/data/0000101001011100011000003fb62a4aa8978974075c144f1c.lance +0 -0
  206. package/.ctxman/self-knowledge/documents.lance/data/000011010000100111001011c6cc824d628f3bb1f4e04cd5d0.lance +0 -0
  207. package/.ctxman/self-knowledge/documents.lance/data/001000001000011001011100d8f74349ada090b332cd1df669.lance +0 -0
  208. package/.ctxman/self-knowledge/documents.lance/data/01000001111010001000010045ab44467bb1e7e4d8ae06bd58.lance +0 -0
  209. package/.ctxman/self-knowledge/documents.lance/data/01000101101110111110011131ef904241a89e1ffb6ddaf8e3.lance +0 -0
  210. package/.ctxman/self-knowledge/documents.lance/data/010010110000010000011010fbfcc145c6a8a827b28057a07e.lance +0 -0
  211. package/.ctxman/self-knowledge/documents.lance/data/1000011000101010001110003fb4f045c3835982efa1405324.lance +0 -0
  212. package/.ctxman/self-knowledge/documents.lance/data/100111011101110111101011f4396b410fa06af3b028d204e6.lance +0 -0
  213. package/.ctxman/self-knowledge/documents.lance/data/100111100111101000001100596b174e00995d6cc2969cc8dc.lance +0 -0
  214. package/.ctxman/self-knowledge/documents.lance/data/1010001100011000110010102ea16b484d887c7c9725feffd6.lance +0 -0
  215. package/.ctxman/self-knowledge/documents.lance/data/101101100010101111110111c5230944d9b3af7301b6392dda.lance +0 -0
  216. package/.ctxman/self-knowledge/documents.lance/data/1011011100100111111110105892bf41b5a530fedd7cf516f1.lance +0 -0
  217. package/.ctxman/self-knowledge/documents.lance/data/10111110000010001011110164e8774742bade87b483c10f36.lance +0 -0
  218. package/.ctxman/self-knowledge/documents.lance/data/1100000100010110001110103ffaca40a2a42c85067ebaf9ae.lance +0 -0
  219. package/.ctxman/self-knowledge/documents.lance/data/110000010001110110101110664bde4c76a78dfc14b05e83b8.lance +0 -0
  220. package/.ctxman/self-knowledge/documents.lance/data/11001100111000010100110050645c4281b1f35b2022234f7f.lance +0 -0
  221. package/.ctxman/self-knowledge/documents.lance/data/1101111011001100110111017214924a368f5719eab2246619.lance +0 -0
  222. package/.ctxman/self-knowledge/documents.lance/data/1110000111001010001000002150c24dcab653d39d07032e63.lance +0 -0
  223. package/.ctxman/self-knowledge/documents.lance/data/1110100000000100000011014d20534967a18b5c87721659ff.lance +0 -0
  224. package/.ctxman/self-knowledge/documents.lance/data/11110101111010000101011135a0114ef2b35dc6f1297a3381.lance +0 -0
@@ -15,277 +15,276 @@
15
15
  * - Full decoder support
16
16
  */
17
17
 
18
+ import { encode as encodeToon, decode as decodeToon } from '@toon-format/toon';
19
+
18
20
  class ToonFormatter {
19
- constructor(options = {}) {
20
- this.options = {
21
- indent: options.indent || 2,
22
- delimiter: options.delimiter || ',',
23
- lengthMarker: options.lengthMarker || false
24
- };
25
-
26
- // Initialize indentChar
27
- this.indentChar = ' '.repeat(this.options.indent);
28
-
29
- // Lazy load official TOON package (ESM)
30
- this._toonModule = null;
21
+ constructor(options = {}) {
22
+ this.options = {
23
+ indent: options.indent || 2,
24
+ delimiter: options.delimiter || ',',
25
+ lengthMarker: options.lengthMarker || false,
26
+ };
27
+
28
+ // Initialize indentChar
29
+ this.indentChar = ' '.repeat(this.options.indent);
30
+
31
+ // Lazy load official TOON package (ESM)
32
+ this._toonModule = null;
33
+ }
34
+
35
+ /**
36
+ * Get official TOON module (lazy load)
37
+ */
38
+ async getToonModule() {
39
+ if (!this._toonModule) {
40
+ this._toonModule = await import('@toon-format/toon');
31
41
  }
32
-
33
- /**
34
- * Get official TOON module (lazy load)
35
- */
36
- async getToonModule() {
37
- if (!this._toonModule) {
38
- this._toonModule = await import('@toon-format/toon');
39
- }
40
- return this._toonModule;
42
+ return this._toonModule;
43
+ }
44
+
45
+ /**
46
+ * Encode data to TOON format (Async - uses official package)
47
+ */
48
+ async encodeAsync(data, options = {}) {
49
+ const toon = await this.getToonModule();
50
+ const opts = { ...this.options, ...options };
51
+ return toon.encode(data, opts);
52
+ }
53
+
54
+ /**
55
+ * Encode data to TOON format (Sync - official encoder)
56
+ */
57
+ encode(data, options = {}) {
58
+ return this.encodeSync(data, options);
59
+ }
60
+
61
+ /**
62
+ * Synchronous encoder. The official package encodes synchronously; the custom encoder
63
+ * below (encodeValue and friends) wrote output its decoder rejects.
64
+ */
65
+ encodeSync(data, options = {}) {
66
+ return encodeToon(data, { ...this.options, ...options });
67
+ }
68
+
69
+ encodeValue(value, indent = 0, compact = true) {
70
+ if (value === null || value === undefined) return 'null';
71
+ if (typeof value === 'boolean') return String(value);
72
+ if (typeof value === 'number') return String(value);
73
+ if (typeof value === 'string') return this.encodeString(value);
74
+ if (Array.isArray(value)) return this.encodeArray(value, indent, compact);
75
+ if (typeof value === 'object') return this.encodeObject(value, indent, compact);
76
+ return 'null';
77
+ }
78
+
79
+ encodeString(str) {
80
+ const escaped = str
81
+ .replace(/\\/g, '\\\\')
82
+ .replace(/"/g, '\\"')
83
+ .replace(/\n/g, '\\n')
84
+ .replace(/\r/g, '\\r')
85
+ .replace(/\t/g, '\\t');
86
+
87
+ if (/[\s,:{}\[\]]/.test(escaped)) {
88
+ return `"${escaped}"`;
41
89
  }
42
90
 
43
- /**
44
- * Encode data to TOON format (Async - uses official package)
45
- */
46
- async encodeAsync(data, options = {}) {
47
- const toon = await this.getToonModule();
48
- const opts = { ...this.options, ...options };
49
- return toon.encode(data, opts);
50
- }
91
+ return escaped;
92
+ }
51
93
 
52
- /**
53
- * Encode data to TOON format (Sync - uses custom implementation for backward compat)
54
- */
55
- encode(data, options = {}) {
56
- // Fallback to synchronous custom implementation
57
- // This maintains backward compatibility with existing code
58
- const opts = { ...this.options, ...options };
59
- return this.encodeSync(data, opts);
60
- }
94
+ encodeArray(arr, indent = 0, compact = true) {
95
+ if (arr.length === 0) return '[]';
61
96
 
62
- /**
63
- * Synchronous encoder (custom implementation - basic features only)
64
- * For full spec compliance, use encodeAsync()
65
- */
66
- encodeSync(data, options = {}) {
67
- const { indent = 2 } = options;
68
- this.indentChar = ' '.repeat(indent);
69
- return this.encodeValue(data, 0, true);
97
+ if (this.isTabularArray(arr)) {
98
+ return this.encodeTabular(arr, indent, compact);
70
99
  }
71
100
 
72
- encodeValue(value, indent = 0, compact = true) {
73
- if (value === null || value === undefined) return 'null';
74
- if (typeof value === 'boolean') return String(value);
75
- if (typeof value === 'number') return String(value);
76
- if (typeof value === 'string') return this.encodeString(value);
77
- if (Array.isArray(value)) return this.encodeArray(value, indent, compact);
78
- if (typeof value === 'object') return this.encodeObject(value, indent, compact);
79
- return 'null';
80
- }
81
-
82
- encodeString(str) {
83
- const escaped = str
84
- .replace(/\\/g, '\\\\')
85
- .replace(/"/g, '\\"')
86
- .replace(/\n/g, '\\n')
87
- .replace(/\r/g, '\\r')
88
- .replace(/\t/g, '\\t');
89
-
90
- if (/[\s,:{}\[\]]/.test(escaped)) {
91
- return `"${escaped}"`;
92
- }
101
+ return this.encodeRegularArray(arr, indent, compact);
102
+ }
93
103
 
94
- return escaped;
104
+ isTabularArray(arr) {
105
+ if (arr.length < 2) return false;
106
+ if (!arr.every((item) => typeof item === 'object' && item !== null && !Array.isArray(item))) {
107
+ return false;
95
108
  }
96
109
 
97
- encodeArray(arr, indent = 0, compact = true) {
98
- if (arr.length === 0) return '[]';
110
+ const firstKeys = Object.keys(arr[0]).sort();
111
+ return arr.every((item) => {
112
+ const keys = Object.keys(item).sort();
113
+ return keys.length === firstKeys.length && keys.every((key, i) => key === firstKeys[i]);
114
+ });
115
+ }
99
116
 
100
- if (this.isTabularArray(arr)) {
101
- return this.encodeTabular(arr, indent, compact);
102
- }
117
+ encodeTabular(arr, indent = 0, _compact = true) {
118
+ const prefix = this.indentChar.repeat(indent);
119
+ const keys = Object.keys(arr[0]).sort();
103
120
 
104
- return this.encodeRegularArray(arr, indent, compact);
105
- }
121
+ let result = `{${keys.join(',')}}:\n`;
106
122
 
107
- isTabularArray(arr) {
108
- if (arr.length < 2) return false;
109
- if (!arr.every(item => typeof item === 'object' && item !== null && !Array.isArray(item))) {
110
- return false;
111
- }
112
-
113
- const firstKeys = Object.keys(arr[0]).sort();
114
- return arr.every(item => {
115
- const keys = Object.keys(item).sort();
116
- return keys.length === firstKeys.length &&
117
- keys.every((key, i) => key === firstKeys[i]);
118
- });
123
+ for (const item of arr) {
124
+ result += prefix + this.indentChar;
125
+ const values = keys.map((key) => this.encodeValue(item[key], 0, true));
126
+ result += values.join(',') + '\n';
119
127
  }
120
128
 
121
- encodeTabular(arr, indent = 0, compact = true) {
122
- const prefix = this.indentChar.repeat(indent);
123
- const keys = Object.keys(arr[0]).sort();
124
-
125
- let result = `{${keys.join(',')}}:\n`;
129
+ return result;
130
+ }
126
131
 
127
- for (const item of arr) {
128
- result += prefix + this.indentChar;
129
- const values = keys.map(key => this.encodeValue(item[key], 0, true));
130
- result += values.join(',') + '\n';
131
- }
132
+ encodeRegularArray(arr, indent = 0, compact = true) {
133
+ const prefix = this.indentChar.repeat(indent);
132
134
 
133
- return result;
135
+ if (
136
+ compact &&
137
+ arr.every((item) => ['string', 'number', 'boolean'].includes(typeof item) || item === null)
138
+ ) {
139
+ return '[' + arr.map((item) => this.encodeValue(item, 0, true)).join(',') + ']';
134
140
  }
135
141
 
136
- encodeRegularArray(arr, indent = 0, compact = true) {
137
- const prefix = this.indentChar.repeat(indent);
138
-
139
- if (compact && arr.every(item => ['string', 'number', 'boolean'].includes(typeof item) || item === null)) {
140
- return '[' + arr.map(item => this.encodeValue(item, 0, true)).join(',') + ']';
141
- }
142
-
143
- let result = '[\n';
144
- for (let i = 0; i < arr.length; i++) {
145
- result += prefix + this.indentChar + this.encodeValue(arr[i], indent + 1, compact);
146
- if (i < arr.length - 1) result += ',';
147
- result += '\n';
148
- }
149
- result += prefix + ']';
150
-
151
- return result;
142
+ let result = '[\n';
143
+ for (let i = 0; i < arr.length; i++) {
144
+ result += prefix + this.indentChar + this.encodeValue(arr[i], indent + 1, compact);
145
+ if (i < arr.length - 1) result += ',';
146
+ result += '\n';
152
147
  }
148
+ result += prefix + ']';
153
149
 
154
- encodeObject(obj, indent = 0, compact = true) {
155
- const keys = Object.keys(obj);
156
- if (keys.length === 0) return '{}';
150
+ return result;
151
+ }
157
152
 
158
- const prefix = this.indentChar.repeat(indent);
159
- let result = '{\n';
153
+ encodeObject(obj, indent = 0, compact = true) {
154
+ const keys = Object.keys(obj);
155
+ if (keys.length === 0) return '{}';
160
156
 
161
- for (let i = 0; i < keys.length; i++) {
162
- const key = keys[i];
163
- const value = obj[key];
164
- result += prefix + this.indentChar + key + ': ';
157
+ const prefix = this.indentChar.repeat(indent);
158
+ let result = '{\n';
165
159
 
166
- if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
167
- result += '\n' + this.encodeObject(value, indent + 1, compact);
168
- } else {
169
- result += this.encodeValue(value, indent + 1, compact);
170
- }
160
+ for (let i = 0; i < keys.length; i++) {
161
+ const key = keys[i];
162
+ const value = obj[key];
163
+ result += prefix + this.indentChar + key + ': ';
171
164
 
172
- if (i < keys.length - 1) result += ',';
173
- result += '\n';
174
- }
165
+ if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
166
+ result += '\n' + this.encodeObject(value, indent + 1, compact);
167
+ } else {
168
+ result += this.encodeValue(value, indent + 1, compact);
169
+ }
175
170
 
176
- result += prefix + '}';
177
- return result;
171
+ if (i < keys.length - 1) result += ',';
172
+ result += '\n';
178
173
  }
179
174
 
180
- /**
181
- * Decode TOON string to JavaScript value (Async - uses official package)
182
- */
183
- async decodeAsync(toonString, options = {}) {
184
- const toon = await this.getToonModule();
185
- return toon.decode(toonString, options);
175
+ result += prefix + '}';
176
+ return result;
177
+ }
178
+
179
+ /**
180
+ * Decode TOON string to JavaScript value (Async - uses official package)
181
+ */
182
+ async decodeAsync(toonString, options = {}) {
183
+ const toon = await this.getToonModule();
184
+ return toon.decode(toonString, options);
185
+ }
186
+
187
+ /**
188
+ * Decode TOON string (Sync - official decoder)
189
+ */
190
+ decode(toonString, options = {}) {
191
+ return decodeToon(toonString, options);
192
+ }
193
+
194
+ /**
195
+ * Validate TOON string
196
+ */
197
+ validate(toonString) {
198
+ const errors = [];
199
+ let braceDepth = 0;
200
+ let bracketDepth = 0;
201
+
202
+ for (const char of toonString) {
203
+ if (char === '{') braceDepth++;
204
+ if (char === '}') braceDepth--;
205
+ if (char === '[') bracketDepth++;
206
+ if (char === ']') bracketDepth--;
207
+
208
+ if (braceDepth < 0) errors.push('Unbalanced braces: extra closing brace');
209
+ if (bracketDepth < 0) errors.push('Unbalanced brackets: extra closing bracket');
186
210
  }
187
211
 
188
- /**
189
- * Decode TOON string (Sync - throws error, decoder not available in sync mode)
190
- */
191
- decode(toonString, options = {}) {
192
- throw new Error('Synchronous decoder not available. Use decodeAsync() instead.');
193
- }
194
-
195
- /**
196
- * Validate TOON string
197
- */
198
- validate(toonString) {
199
- const errors = [];
200
- let braceDepth = 0;
201
- let bracketDepth = 0;
202
-
203
- for (const char of toonString) {
204
- if (char === '{') braceDepth++;
205
- if (char === '}') braceDepth--;
206
- if (char === '[') bracketDepth++;
207
- if (char === ']') bracketDepth--;
208
-
209
- if (braceDepth < 0) errors.push('Unbalanced braces: extra closing brace');
210
- if (bracketDepth < 0) errors.push('Unbalanced brackets: extra closing bracket');
211
- }
212
-
213
- if (braceDepth > 0) errors.push('Unbalanced braces: missing closing braces');
214
- if (bracketDepth > 0) errors.push('Unbalanced brackets: missing closing brackets');
215
-
216
- return {
217
- valid: errors.length === 0,
218
- errors
219
- };
220
- }
221
-
222
- /**
223
- * Estimate tokens (GPT-4 compatible)
224
- */
225
- estimateTokens(toonString) {
226
- return Math.ceil(toonString.length / 4);
227
- }
228
-
229
- /**
230
- * Optimize TOON output
231
- */
232
- optimize(toonString) {
233
- const lines = toonString.split('\n');
234
- const optimized = lines.map(line => line.trimEnd()).join('\n');
235
- return optimized.replace(/\n{3,}/g, '\n\n');
236
- }
237
-
238
- /**
239
- * Minify TOON output
240
- */
241
- minify(toonString) {
242
- return toonString
243
- .split('\n')
244
- .map(line => line.trim())
245
- .filter(line => line.length > 0)
246
- .join('\n');
247
- }
248
-
249
- /**
250
- * Compare TOON with JSON (token savings)
251
- */
252
- compareWithJSON(data) {
253
- const toon = this.encode(data);
254
- const json = JSON.stringify(data, null, 2);
255
-
256
- const toonSize = toon.length;
257
- const jsonSize = json.length;
258
- const savings = jsonSize - toonSize;
259
- const savingsPercentage = ((savings / jsonSize) * 100).toFixed(1);
260
-
261
- return {
262
- toon,
263
- json,
264
- toonSize,
265
- jsonSize,
266
- savings,
267
- savingsPercentage: parseFloat(savingsPercentage),
268
- toonTokens: this.estimateTokens(toon),
269
- jsonTokens: this.estimateTokens(json)
270
- };
271
- }
272
-
273
- /**
274
- * Compare with official TOON encoder (async)
275
- */
276
- async compareWithOfficial(data, options = {}) {
277
- const customEncoded = this.encode(data);
278
- const officialEncoded = await this.encodeAsync(data, options);
279
-
280
- return {
281
- custom: customEncoded,
282
- official: officialEncoded,
283
- customSize: customEncoded.length,
284
- officialSize: officialEncoded.length,
285
- difference: customEncoded.length - officialEncoded.length,
286
- officialIsBetter: officialEncoded.length < customEncoded.length
287
- };
288
- }
212
+ if (braceDepth > 0) errors.push('Unbalanced braces: missing closing braces');
213
+ if (bracketDepth > 0) errors.push('Unbalanced brackets: missing closing brackets');
214
+
215
+ return {
216
+ valid: errors.length === 0,
217
+ errors,
218
+ };
219
+ }
220
+
221
+ /**
222
+ * Estimate tokens (GPT-4 compatible)
223
+ */
224
+ estimateTokens(toonString) {
225
+ return Math.ceil(toonString.length / 4);
226
+ }
227
+
228
+ /**
229
+ * Optimize TOON output
230
+ */
231
+ optimize(toonString) {
232
+ const lines = toonString.split('\n');
233
+ const optimized = lines.map((line) => line.trimEnd()).join('\n');
234
+ return optimized.replace(/\n{3,}/g, '\n\n');
235
+ }
236
+
237
+ /**
238
+ * Minify TOON output
239
+ */
240
+ minify(toonString) {
241
+ return toonString
242
+ .split('\n')
243
+ .map((line) => line.trim())
244
+ .filter((line) => line.length > 0)
245
+ .join('\n');
246
+ }
247
+
248
+ /**
249
+ * Compare TOON with JSON (token savings)
250
+ */
251
+ compareWithJSON(data) {
252
+ const toon = this.encode(data);
253
+ const json = JSON.stringify(data, null, 2);
254
+
255
+ const toonSize = toon.length;
256
+ const jsonSize = json.length;
257
+ const savings = jsonSize - toonSize;
258
+ const savingsPercentage = ((savings / jsonSize) * 100).toFixed(1);
259
+
260
+ return {
261
+ toon,
262
+ json,
263
+ toonSize,
264
+ jsonSize,
265
+ savings,
266
+ savingsPercentage: parseFloat(savingsPercentage),
267
+ toonTokens: this.estimateTokens(toon),
268
+ jsonTokens: this.estimateTokens(json),
269
+ };
270
+ }
271
+
272
+ /**
273
+ * Compare with official TOON encoder (async)
274
+ */
275
+ async compareWithOfficial(data, options = {}) {
276
+ const customEncoded = this.encode(data);
277
+ const officialEncoded = await this.encodeAsync(data, options);
278
+
279
+ return {
280
+ custom: customEncoded,
281
+ official: officialEncoded,
282
+ customSize: customEncoded.length,
283
+ officialSize: officialEncoded.length,
284
+ difference: customEncoded.length - officialEncoded.length,
285
+ officialIsBetter: officialEncoded.length < customEncoded.length,
286
+ };
287
+ }
289
288
  }
290
289
 
291
290
  export default ToonFormatter;
@@ -12,7 +12,7 @@
12
12
  import GitClient from './GitClient.js';
13
13
  import { getLogger } from '../../utils/logger.js';
14
14
 
15
- const logger = getLogger('BlameTracker');
15
+ const _logger = getLogger('BlameTracker');
16
16
 
17
17
  export class BlameTracker {
18
18
  constructor(repositoryPath) {
@@ -46,7 +46,7 @@ export class BlameTracker {
46
46
  name: author.name,
47
47
  email: author.email,
48
48
  filesOwned: 0,
49
- totalCommits: 0
49
+ totalCommits: 0,
50
50
  });
51
51
  }
52
52
 
@@ -97,7 +97,7 @@ export class BlameTracker {
97
97
  commitCount,
98
98
  authorCount: authors.length,
99
99
  primaryAuthor: authors[0],
100
- risk: this.calculateRiskScore(commitCount, authors.length)
100
+ risk: this.calculateRiskScore(commitCount, authors.length),
101
101
  });
102
102
  }
103
103
  }
@@ -134,7 +134,7 @@ export class BlameTracker {
134
134
  totalAuthors: contributions.size,
135
135
  totalFiles: filePaths.length,
136
136
  topContributors,
137
- averageFilesPerAuthor: filePaths.length / contributions.size
137
+ averageFilesPerAuthor: filePaths.length / contributions.size,
138
138
  };
139
139
  }
140
140
  }