ctxman 3.0.0 → 3.1.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/llm-profiles.json +1067 -39
- package/.ctxman/profiles/backend-team.json +13 -0
- package/.ctxman/profiles/default.json +12 -0
- package/.ctxman/profiles/devops-team.json +21 -0
- package/.ctxman/profiles/frontend-team.json +20 -0
- package/.ctxman/templates/bug-fix.json +17 -0
- package/.ctxman/templates/code-review.json +9 -0
- package/.ctxman/templates/documentation.json +9 -0
- package/.ctxman/templates/feature.json +9 -0
- package/.ctxman/templates/full-context.json +9 -0
- package/.ctxman/templates/refactoring.json +18 -0
- package/.ctxman/templates/testing.json +9 -0
- package/.ctxman/wizard-profiles/security-audit/profile.json +7 -1
- package/LICENSE +21 -0
- package/README.md +380 -136
- package/bin/cli.js +1880 -529
- package/bin/cm-ask.js +18 -13
- package/bin/cm-audit.js +124 -0
- package/bin/cm-count.js +151 -0
- package/bin/cm-gitingest.js +128 -115
- package/bin/cm-map.js +242 -0
- package/bin/cm-mcp.js +293 -0
- package/bin/cm-pack.js +405 -0
- package/bin/cm-policy.js +170 -0
- package/bin/cm-pr.js +248 -0
- package/bin/cm-search.js +178 -0
- package/bin/cm-update.js +128 -128
- package/bin/cm-why.js +197 -0
- package/bin/mcp-server.js +8 -24
- package/ctxman.js +237 -183
- package/examples/README.md +19 -4
- package/examples/github-actions/ctxman-check.yml +49 -0
- package/examples/wizard-profiles/security-audit/profile.json +7 -1
- package/index.d.ts +1422 -0
- package/index.js +55 -31
- package/lib/agents/pr-failure-agent.js +48 -45
- package/lib/analyzers/ai-suggester.js +451 -0
- package/lib/analyzers/dependency-scanner.js +505 -0
- package/lib/analyzers/duplicate-detector.js +264 -0
- package/lib/analyzers/go-method-analyzer.js +151 -110
- package/lib/analyzers/import-tracker.js +431 -0
- package/lib/analyzers/method-analyzer.js +1055 -434
- package/lib/analyzers/performance-dashboard.js +683 -0
- package/lib/analyzers/token-calculator.js +895 -504
- package/lib/analyzers/unused-exports-detector.js +336 -0
- package/lib/api/mcp/install.js +424 -0
- package/lib/api/mcp/prompts.js +281 -281
- package/lib/api/mcp/resources.js +300 -241
- package/lib/api/mcp/sandbox.js +89 -0
- package/lib/api/mcp/server.js +675 -312
- package/lib/api/mcp/start.js +125 -0
- package/lib/api/mcp/transports/HttpSseTransport.js +60 -52
- package/lib/api/rest/server.js +105 -28
- package/lib/cache/CacheManager.js +9 -17
- package/lib/core/Analyzer.js +16 -10
- package/lib/core/ContextBuilder.js +19 -28
- package/lib/core/Reporter.js +15 -14
- package/lib/core/Scanner.js +81 -12
- package/lib/count/index.js +100 -0
- package/lib/formatters/format-registry.js +394 -372
- package/lib/formatters/gitingest-formatter.js +656 -621
- package/lib/formatters/toon-formatter-v1.3.js +637 -632
- package/lib/formatters/toon-formatter.js +239 -240
- package/lib/integrations/git/BlameTracker.js +4 -4
- package/lib/integrations/git/DiffAnalyzer.js +38 -31
- package/lib/integrations/git/GitClient.js +71 -53
- package/lib/lsp/server.js +311 -0
- package/lib/map/graph.js +299 -0
- package/lib/map/imports.js +233 -0
- package/lib/map/index.js +33 -0
- package/lib/map/rank.js +253 -0
- package/lib/map/render.js +346 -0
- package/lib/map/resolver.js +574 -0
- package/lib/pack/budget.js +111 -0
- package/lib/pack/code-lexer.js +360 -0
- package/lib/pack/compress.js +414 -0
- package/lib/pack/index.js +354 -0
- package/lib/pack/renderers/common.js +134 -0
- package/lib/pack/renderers/index.js +24 -0
- package/lib/pack/renderers/json.js +47 -0
- package/lib/pack/renderers/markdown.js +54 -0
- package/lib/pack/renderers/plain.js +38 -0
- package/lib/pack/renderers/xml.js +85 -0
- package/lib/pack/select.js +177 -0
- package/lib/pack/split.js +182 -0
- package/lib/pack/tree.js +35 -0
- package/lib/parsers/gitignore-parser.js +113 -107
- package/lib/parsers/method-filter-parser.js +50 -33
- package/lib/plugins/ExporterPlugin.js +3 -3
- package/lib/plugins/LanguagePlugin.js +3 -3
- package/lib/plugins/PluginManager.js +7 -13
- package/lib/policy/audit.js +294 -0
- package/lib/policy/enforce.js +125 -0
- package/lib/policy/ignore-files.js +169 -0
- package/lib/policy/index.js +52 -0
- package/lib/policy/policy.js +489 -0
- package/lib/pr/diff-parser.js +267 -0
- package/lib/pr/method-mapper.js +148 -0
- package/lib/pr/pr-context.js +829 -0
- package/lib/pr/pr-git.js +149 -0
- package/lib/pr/pr-renderer.js +644 -0
- package/lib/pr/related-tests.js +296 -0
- package/lib/rag/DataSourcePlugin.js +5 -5
- package/lib/rag/EmbeddingProvider.js +19 -8
- package/lib/rag/Indexer.js +14 -5
- package/lib/rag/VectorStoreFactory.js +3 -3
- package/lib/rag/sources/FileDataSource.js +6 -6
- package/lib/rag/stores/BaseVectorStore.js +5 -5
- package/lib/rag/stores/ChromaDBStore.js +10 -10
- package/lib/rag/stores/LanceDBStore.js +29 -19
- package/lib/rag/stores/PineconeStore.js +11 -9
- package/lib/search/bm25.js +433 -0
- package/lib/search/code-units.js +201 -0
- package/lib/search/symbols.js +189 -0
- package/lib/ui/dashboard.js +120 -89
- package/lib/ui/index.js +1 -6
- package/lib/ui/init-wizard-ui.js +550 -0
- package/lib/ui/progress-bar.js +94 -88
- package/lib/ui/select-input.js +40 -32
- package/lib/ui/template-selector.js +99 -0
- package/lib/ui/wizard.js +374 -329
- package/lib/utils/claude-token-counter.js +151 -0
- package/lib/utils/cli-output.js +17 -0
- package/lib/utils/clipboard-utils.js +49 -50
- package/lib/utils/config-utils.js +81 -66
- package/lib/utils/context-versioning.js +532 -0
- package/lib/utils/error-handler.js +112 -112
- package/lib/utils/file-utils.js +153 -69
- package/lib/utils/format-converter.js +356 -283
- package/lib/utils/git-utils.js +366 -294
- package/lib/utils/llm-cost-estimator.js +281 -0
- package/lib/utils/llm-detector.js +242 -78
- package/lib/utils/logger.js +243 -237
- package/lib/utils/messages.js +355 -0
- package/lib/utils/multi-repo-manager.js +364 -0
- package/lib/utils/profile-manager.js +380 -0
- package/lib/utils/safe-read.js +61 -0
- package/lib/utils/secret-redactor.js +61 -0
- package/lib/utils/snapshot-manager.js +679 -0
- package/lib/utils/template-manager.js +378 -0
- package/lib/utils/token-utils.js +201 -170
- package/lib/utils/tokenizer-adapter.js +421 -377
- package/lib/utils/updater.js +539 -506
- package/lib/watch/FileWatcher.js +12 -8
- package/lib/watch/IncrementalAnalyzer.js +7 -7
- package/lib/why/coupling.js +81 -0
- package/lib/why/errors.js +23 -0
- package/lib/why/git.js +169 -0
- package/lib/why/history.js +108 -0
- package/lib/why/index.js +720 -0
- package/lib/why/ownership.js +79 -0
- package/lib/why/paths.js +207 -0
- package/lib/why/references.js +259 -0
- package/lib/why/render.js +722 -0
- package/lib/why/search.js +358 -0
- package/lib/why/target.js +389 -0
- package/lib/wizards/config-generator.js +255 -0
- package/lib/wizards/project-detector.js +571 -0
- package/package.json +44 -10
- package/.ctxman/logs/ctxman-2026-07-02.log +0 -156
- 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
|
@@ -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
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
73
|
-
|
|
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
|
-
|
|
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
|
-
|
|
98
|
-
|
|
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
|
-
|
|
101
|
-
|
|
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
|
-
|
|
105
|
-
}
|
|
121
|
+
let result = `{${keys.join(',')}}:\n`;
|
|
106
122
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
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
|
-
|
|
122
|
-
|
|
123
|
-
const keys = Object.keys(arr[0]).sort();
|
|
124
|
-
|
|
125
|
-
let result = `{${keys.join(',')}}:\n`;
|
|
129
|
+
return result;
|
|
130
|
+
}
|
|
126
131
|
|
|
127
|
-
|
|
128
|
-
|
|
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
|
-
|
|
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
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
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
|
-
|
|
155
|
-
|
|
156
|
-
if (keys.length === 0) return '{}';
|
|
150
|
+
return result;
|
|
151
|
+
}
|
|
157
152
|
|
|
158
|
-
|
|
159
|
-
|
|
153
|
+
encodeObject(obj, indent = 0, compact = true) {
|
|
154
|
+
const keys = Object.keys(obj);
|
|
155
|
+
if (keys.length === 0) return '{}';
|
|
160
156
|
|
|
161
|
-
|
|
162
|
-
|
|
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
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
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
|
-
|
|
173
|
-
|
|
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
|
-
|
|
177
|
-
|
|
171
|
+
if (i < keys.length - 1) result += ',';
|
|
172
|
+
result += '\n';
|
|
178
173
|
}
|
|
179
174
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
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
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
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
|
|
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
|
}
|