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.
- 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 +381 -136
- package/bin/cli.js +1880 -529
- package/bin/cm-ask.js +18 -67
- package/bin/cm-audit.js +121 -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 +687 -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 +772 -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 +116 -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 +312 -0
- package/lib/policy/enforce.js +125 -0
- package/lib/policy/ignore-files.js +169 -0
- package/lib/policy/index.js +53 -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/ask.js +122 -0
- package/lib/rag/sources/FileDataSource.js +12 -9
- 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 +246 -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 +257 -0
- package/lib/wizards/project-detector.js +572 -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
|
@@ -4,416 +4,438 @@
|
|
|
4
4
|
* Supports: TOON, JSON, YAML, CSV, XML, Markdown, Protobuf, GitIngest
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import
|
|
8
|
-
import GitIngestFormatter from './gitingest-formatter.js';
|
|
7
|
+
import { encode as encodeToon } from '@toon-format/toon';
|
|
9
8
|
|
|
10
9
|
class FormatRegistry {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
if (options && options.formatter) {
|
|
95
|
-
return options.formatter.generateDigest();
|
|
96
|
-
}
|
|
97
|
-
throw new Error('GitIngest format requires a GitIngestFormatter instance');
|
|
98
|
-
}
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* Register a custom formatter
|
|
104
|
-
*/
|
|
105
|
-
register(name, formatter) {
|
|
106
|
-
if (!formatter.encoder || typeof formatter.encoder !== 'function') {
|
|
107
|
-
throw new Error(`Formatter '${name}' must have an encoder function`);
|
|
10
|
+
constructor() {
|
|
11
|
+
this.formatters = new Map();
|
|
12
|
+
this.registerDefaultFormatters();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Register all default formatters
|
|
17
|
+
*/
|
|
18
|
+
registerDefaultFormatters() {
|
|
19
|
+
// TOON format: measured 25% (file lists) to 66% (method lists) fewer tokens than JSON
|
|
20
|
+
this.register('toon', {
|
|
21
|
+
name: 'TOON',
|
|
22
|
+
description: 'Token-Oriented Object Notation (fewer tokens than JSON)',
|
|
23
|
+
extension: '.toon',
|
|
24
|
+
mimeType: 'text/plain',
|
|
25
|
+
// Official encoder: ToonFormatter.encode()'s legacy sync output is not valid TOON
|
|
26
|
+
encoder: (data) => encodeToon(data),
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// JSON format (standard)
|
|
30
|
+
this.register('json', {
|
|
31
|
+
name: 'JSON',
|
|
32
|
+
description: 'JavaScript Object Notation (standard format)',
|
|
33
|
+
extension: '.json',
|
|
34
|
+
mimeType: 'application/json',
|
|
35
|
+
encoder: (data) => JSON.stringify(data, null, 2),
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// JSON Compact (no whitespace)
|
|
39
|
+
this.register('json-compact', {
|
|
40
|
+
name: 'JSON Compact',
|
|
41
|
+
description: 'Minified JSON without whitespace',
|
|
42
|
+
extension: '.json',
|
|
43
|
+
mimeType: 'application/json',
|
|
44
|
+
encoder: (data) => JSON.stringify(data),
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// YAML format
|
|
48
|
+
this.register('yaml', {
|
|
49
|
+
name: 'YAML',
|
|
50
|
+
description: "YAML Ain't Markup Language (human-readable)",
|
|
51
|
+
extension: '.yaml',
|
|
52
|
+
mimeType: 'text/yaml',
|
|
53
|
+
encoder: (data) => this.encodeYAML(data),
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// Markdown format
|
|
57
|
+
this.register('markdown', {
|
|
58
|
+
name: 'Markdown',
|
|
59
|
+
description: 'Markdown documentation format',
|
|
60
|
+
extension: '.md',
|
|
61
|
+
mimeType: 'text/markdown',
|
|
62
|
+
encoder: (data) => this.encodeMarkdown(data),
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// CSV format
|
|
66
|
+
this.register('csv', {
|
|
67
|
+
name: 'CSV',
|
|
68
|
+
description: 'Comma-Separated Values (spreadsheet compatible)',
|
|
69
|
+
extension: '.csv',
|
|
70
|
+
mimeType: 'text/csv',
|
|
71
|
+
encoder: (data) => this.encodeCSV(data),
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// XML format
|
|
75
|
+
this.register('xml', {
|
|
76
|
+
name: 'XML',
|
|
77
|
+
description: 'Extensible Markup Language (enterprise compatible)',
|
|
78
|
+
extension: '.xml',
|
|
79
|
+
mimeType: 'application/xml',
|
|
80
|
+
encoder: (data) => this.encodeXML(data),
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// GitIngest format
|
|
84
|
+
this.register('gitingest', {
|
|
85
|
+
name: 'GitIngest',
|
|
86
|
+
description: 'Single-file digest for LLM consumption',
|
|
87
|
+
extension: '.txt',
|
|
88
|
+
mimeType: 'text/plain',
|
|
89
|
+
encoder: (data, options) => {
|
|
90
|
+
// GitIngest uses a different interface
|
|
91
|
+
if (options && options.formatter) {
|
|
92
|
+
return options.formatter.generateDigest();
|
|
108
93
|
}
|
|
109
|
-
|
|
94
|
+
throw new Error('GitIngest format requires a GitIngestFormatter instance');
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Register a custom formatter
|
|
101
|
+
*/
|
|
102
|
+
register(name, formatter) {
|
|
103
|
+
if (!formatter.encoder || typeof formatter.encoder !== 'function') {
|
|
104
|
+
throw new Error(`Formatter '${name}' must have an encoder function`);
|
|
110
105
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
106
|
+
this.formatters.set(name, formatter);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Get formatter by name
|
|
111
|
+
*/
|
|
112
|
+
get(name) {
|
|
113
|
+
const formatter = this.formatters.get(name);
|
|
114
|
+
if (!formatter) {
|
|
115
|
+
throw new Error(
|
|
116
|
+
`Unknown format: ${name}. Available formats: ${this.listFormats().join(', ')}`
|
|
117
|
+
);
|
|
121
118
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
119
|
+
return formatter;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Check if format exists
|
|
124
|
+
*/
|
|
125
|
+
has(name) {
|
|
126
|
+
return this.formatters.has(name);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* List all available formats
|
|
131
|
+
*/
|
|
132
|
+
listFormats() {
|
|
133
|
+
return Array.from(this.formatters.keys());
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Get format info
|
|
138
|
+
*/
|
|
139
|
+
getInfo(name) {
|
|
140
|
+
const formatter = this.get(name);
|
|
141
|
+
return {
|
|
142
|
+
name: formatter.name,
|
|
143
|
+
description: formatter.description,
|
|
144
|
+
extension: formatter.extension,
|
|
145
|
+
mimeType: formatter.mimeType,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Get all formats info
|
|
151
|
+
*/
|
|
152
|
+
getAllInfo() {
|
|
153
|
+
const info = {};
|
|
154
|
+
for (const [name, formatter] of this.formatters) {
|
|
155
|
+
info[name] = {
|
|
156
|
+
name: formatter.name,
|
|
157
|
+
description: formatter.description,
|
|
158
|
+
extension: formatter.extension,
|
|
159
|
+
mimeType: formatter.mimeType,
|
|
160
|
+
};
|
|
128
161
|
}
|
|
162
|
+
return info;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Encode data to specified format
|
|
167
|
+
*/
|
|
168
|
+
encode(name, data, options = {}) {
|
|
169
|
+
const formatter = this.get(name);
|
|
170
|
+
return formatter.encoder(data, options);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* YAML encoder (simple implementation)
|
|
175
|
+
*/
|
|
176
|
+
encodeYAML(data, indent = 0) {
|
|
177
|
+
const prefix = ' '.repeat(indent);
|
|
178
|
+
const type = this.getType(data);
|
|
179
|
+
|
|
180
|
+
switch (type) {
|
|
181
|
+
case 'null':
|
|
182
|
+
case 'undefined':
|
|
183
|
+
return 'null';
|
|
184
|
+
|
|
185
|
+
case 'boolean':
|
|
186
|
+
case 'number':
|
|
187
|
+
return String(data);
|
|
188
|
+
|
|
189
|
+
case 'string':
|
|
190
|
+
return this.encodeYAMLString(data);
|
|
191
|
+
|
|
192
|
+
case 'array':
|
|
193
|
+
if (data.length === 0) return '[]';
|
|
194
|
+
let arrResult = '\n';
|
|
195
|
+
for (const item of data) {
|
|
196
|
+
arrResult += prefix + '- ' + this.encodeYAML(item, indent + 1) + '\n';
|
|
197
|
+
}
|
|
198
|
+
return arrResult.trimEnd();
|
|
199
|
+
|
|
200
|
+
case 'object':
|
|
201
|
+
const keys = Object.keys(data);
|
|
202
|
+
if (keys.length === 0) return '{}';
|
|
203
|
+
let objResult = '\n';
|
|
204
|
+
for (const key of keys) {
|
|
205
|
+
const yamlKey = this.encodeYAMLString(key);
|
|
206
|
+
objResult += prefix + yamlKey + ': ' + this.encodeYAML(data[key], indent + 1) + '\n';
|
|
207
|
+
}
|
|
208
|
+
return objResult.trimEnd();
|
|
129
209
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
*/
|
|
133
|
-
listFormats() {
|
|
134
|
-
return Array.from(this.formatters.keys());
|
|
210
|
+
default:
|
|
211
|
+
return 'null';
|
|
135
212
|
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Encode a string scalar or mapping key. Anything a YAML reader would take as
|
|
217
|
+
* another type, as syntax, or would trim is written as a JSON string, which is
|
|
218
|
+
* a valid YAML double-quoted scalar with proper escaping.
|
|
219
|
+
*/
|
|
220
|
+
encodeYAMLString(str) {
|
|
221
|
+
const needsQuotes =
|
|
222
|
+
str === '' ||
|
|
223
|
+
str !== str.trim() ||
|
|
224
|
+
/[:\n\r{}[\],&*#?|<>=!%@`]/.test(str) ||
|
|
225
|
+
/^[-'"]/.test(str) ||
|
|
226
|
+
/^[-+.]?\d/.test(str) ||
|
|
227
|
+
/^(true|false|null|~|[-+]?\.inf|\.nan)$/i.test(str);
|
|
228
|
+
|
|
229
|
+
return needsQuotes ? JSON.stringify(str) : str;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Markdown encoder
|
|
234
|
+
*/
|
|
235
|
+
encodeMarkdown(data) {
|
|
236
|
+
const { project, paths, methods, methodStats } = data;
|
|
237
|
+
|
|
238
|
+
let md = `# ${project?.root || 'Project'} - Context Analysis\n\n`;
|
|
239
|
+
|
|
240
|
+
// Project summary
|
|
241
|
+
if (project) {
|
|
242
|
+
md += '## Project Summary\n\n';
|
|
243
|
+
md += `- **Total Files**: ${project.totalFiles}\n`;
|
|
244
|
+
md += `- **Total Tokens**: ${project.totalTokens?.toLocaleString()}\n\n`;
|
|
148
245
|
}
|
|
149
246
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
for (const
|
|
156
|
-
|
|
157
|
-
name: formatter.name,
|
|
158
|
-
description: formatter.description,
|
|
159
|
-
extension: formatter.extension,
|
|
160
|
-
mimeType: formatter.mimeType
|
|
161
|
-
};
|
|
247
|
+
// Paths
|
|
248
|
+
if (paths) {
|
|
249
|
+
md += '## File Structure\n\n';
|
|
250
|
+
for (const [dir, files] of Object.entries(paths)) {
|
|
251
|
+
md += `### ${dir}\n\n`;
|
|
252
|
+
for (const file of files) {
|
|
253
|
+
md += `- ${file}\n`;
|
|
162
254
|
}
|
|
163
|
-
|
|
255
|
+
md += '\n';
|
|
256
|
+
}
|
|
164
257
|
}
|
|
165
258
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
* YAML encoder (simple implementation)
|
|
176
|
-
*/
|
|
177
|
-
encodeYAML(data, indent = 0) {
|
|
178
|
-
const prefix = ' '.repeat(indent);
|
|
179
|
-
const type = this.getType(data);
|
|
180
|
-
|
|
181
|
-
switch (type) {
|
|
182
|
-
case 'null':
|
|
183
|
-
case 'undefined':
|
|
184
|
-
return 'null';
|
|
185
|
-
|
|
186
|
-
case 'boolean':
|
|
187
|
-
case 'number':
|
|
188
|
-
return String(data);
|
|
189
|
-
|
|
190
|
-
case 'string':
|
|
191
|
-
// Quote if contains special characters
|
|
192
|
-
if (/[:\n\r{}[\],&*#?|<>=!%@`]/.test(data)) {
|
|
193
|
-
return `"${data.replace(/"/g, '\\"')}"`;
|
|
194
|
-
}
|
|
195
|
-
return data;
|
|
196
|
-
|
|
197
|
-
case 'array':
|
|
198
|
-
if (data.length === 0) return '[]';
|
|
199
|
-
let arrResult = '\n';
|
|
200
|
-
for (const item of data) {
|
|
201
|
-
arrResult += prefix + '- ' + this.encodeYAML(item, indent + 1) + '\n';
|
|
202
|
-
}
|
|
203
|
-
return arrResult.trimEnd();
|
|
204
|
-
|
|
205
|
-
case 'object':
|
|
206
|
-
const keys = Object.keys(data);
|
|
207
|
-
if (keys.length === 0) return '{}';
|
|
208
|
-
let objResult = '\n';
|
|
209
|
-
for (const key of keys) {
|
|
210
|
-
objResult += prefix + key + ': ' + this.encodeYAML(data[key], indent + 1) + '\n';
|
|
211
|
-
}
|
|
212
|
-
return objResult.trimEnd();
|
|
213
|
-
|
|
214
|
-
default:
|
|
215
|
-
return 'null';
|
|
259
|
+
// Methods
|
|
260
|
+
if (methods) {
|
|
261
|
+
md += '## Methods\n\n';
|
|
262
|
+
for (const [file, methodList] of Object.entries(methods)) {
|
|
263
|
+
md += `### ${file}\n\n`;
|
|
264
|
+
md += '| Method | Line | Tokens |\n';
|
|
265
|
+
md += '|--------|------|--------|\n';
|
|
266
|
+
for (const method of methodList) {
|
|
267
|
+
md += `| ${method.name} | ${method.line} | ${method.tokens} |\n`;
|
|
216
268
|
}
|
|
269
|
+
md += '\n';
|
|
270
|
+
}
|
|
217
271
|
}
|
|
218
272
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
// Project summary
|
|
228
|
-
if (project) {
|
|
229
|
-
md += '## Project Summary\n\n';
|
|
230
|
-
md += `- **Total Files**: ${project.totalFiles}\n`;
|
|
231
|
-
md += `- **Total Tokens**: ${project.totalTokens?.toLocaleString()}\n\n`;
|
|
232
|
-
}
|
|
273
|
+
// Method stats
|
|
274
|
+
if (methodStats) {
|
|
275
|
+
md += '## Statistics\n\n';
|
|
276
|
+
md += `- **Total Methods**: ${methodStats.totalMethods}\n`;
|
|
277
|
+
md += `- **Included Methods**: ${methodStats.includedMethods}\n`;
|
|
278
|
+
md += `- **Total Method Tokens**: ${methodStats.totalMethodTokens || 0}\n`;
|
|
279
|
+
}
|
|
233
280
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
md += '## File Structure\n\n';
|
|
237
|
-
for (const [dir, files] of Object.entries(paths)) {
|
|
238
|
-
md += `### ${dir}\n\n`;
|
|
239
|
-
for (const file of files) {
|
|
240
|
-
md += `- ${file}\n`;
|
|
241
|
-
}
|
|
242
|
-
md += '\n';
|
|
243
|
-
}
|
|
244
|
-
}
|
|
281
|
+
return md;
|
|
282
|
+
}
|
|
245
283
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
md += '| Method | Line | Tokens |\n';
|
|
252
|
-
md += '|--------|------|--------|\n';
|
|
253
|
-
for (const method of methodList) {
|
|
254
|
-
md += `| ${method.name} | ${method.line} | ${method.tokens} |\n`;
|
|
255
|
-
}
|
|
256
|
-
md += '\n';
|
|
257
|
-
}
|
|
258
|
-
}
|
|
284
|
+
/**
|
|
285
|
+
* CSV encoder
|
|
286
|
+
*/
|
|
287
|
+
encodeCSV(data) {
|
|
288
|
+
const { project: _project, methods } = data;
|
|
259
289
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
md += `- **Total Methods**: ${methodStats.totalMethods}\n`;
|
|
264
|
-
md += `- **Included Methods**: ${methodStats.includedMethods}\n`;
|
|
265
|
-
md += `- **Total Method Tokens**: ${methodStats.totalMethodTokens || 0}\n`;
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
return md;
|
|
290
|
+
if (!methods) {
|
|
291
|
+
// File-level CSV
|
|
292
|
+
return this.encodeFilesCSV(data);
|
|
269
293
|
}
|
|
270
294
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
*/
|
|
274
|
-
encodeCSV(data) {
|
|
275
|
-
const { project, methods } = data;
|
|
295
|
+
// Method-level CSV
|
|
296
|
+
let csv = 'File,Method,Line,Tokens\n';
|
|
276
297
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
298
|
+
for (const [file, methodList] of Object.entries(methods)) {
|
|
299
|
+
for (const method of methodList) {
|
|
300
|
+
csv += `"${file}","${method.name}",${method.line},${method.tokens}\n`;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
281
303
|
|
|
282
|
-
|
|
283
|
-
|
|
304
|
+
return csv;
|
|
305
|
+
}
|
|
284
306
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
307
|
+
/**
|
|
308
|
+
* CSV encoder for file-level data
|
|
309
|
+
*/
|
|
310
|
+
encodeFilesCSV(data) {
|
|
311
|
+
const { paths } = data;
|
|
290
312
|
|
|
291
|
-
|
|
313
|
+
if (!paths) {
|
|
314
|
+
return 'Directory,File\n';
|
|
292
315
|
}
|
|
293
316
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
if (!paths) {
|
|
301
|
-
return 'Directory,File\n';
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
let csv = 'Directory,File\n';
|
|
305
|
-
for (const [dir, files] of Object.entries(paths)) {
|
|
306
|
-
for (const file of files) {
|
|
307
|
-
csv += `"${dir}","${file}"\n`;
|
|
308
|
-
}
|
|
309
|
-
}
|
|
317
|
+
let csv = 'Directory,File\n';
|
|
318
|
+
for (const [dir, files] of Object.entries(paths)) {
|
|
319
|
+
for (const file of files) {
|
|
320
|
+
csv += `"${dir}","${file}"\n`;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
310
323
|
|
|
311
|
-
|
|
324
|
+
return csv;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* XML encoder
|
|
329
|
+
*/
|
|
330
|
+
encodeXML(data, indent = 0) {
|
|
331
|
+
const prefix = ' '.repeat(indent);
|
|
332
|
+
const type = this.getType(data);
|
|
333
|
+
|
|
334
|
+
if (indent === 0) {
|
|
335
|
+
let xml = '<?xml version="1.0" encoding="UTF-8"?>\n';
|
|
336
|
+
xml += '<context>\n';
|
|
337
|
+
xml += this.encodeXML(data, 1);
|
|
338
|
+
xml += '</context>';
|
|
339
|
+
return xml;
|
|
312
340
|
}
|
|
313
341
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
342
|
+
switch (type) {
|
|
343
|
+
case 'null':
|
|
344
|
+
case 'undefined':
|
|
345
|
+
return '';
|
|
346
|
+
|
|
347
|
+
case 'boolean':
|
|
348
|
+
case 'number':
|
|
349
|
+
case 'string':
|
|
350
|
+
return this.escapeXML(String(data));
|
|
351
|
+
|
|
352
|
+
// Recurse with indent + 1, never 0: indent 0 is what emits the document wrapper.
|
|
353
|
+
case 'array':
|
|
354
|
+
let arrXML = '';
|
|
355
|
+
for (const item of data) {
|
|
356
|
+
const itemType = this.getType(item);
|
|
357
|
+
|
|
358
|
+
if (itemType === 'object' || itemType === 'array') {
|
|
359
|
+
arrXML += prefix + '<item>\n';
|
|
360
|
+
arrXML += this.encodeXML(item, indent + 1);
|
|
361
|
+
arrXML += prefix + '</item>\n';
|
|
362
|
+
} else {
|
|
363
|
+
arrXML += prefix + '<item>' + this.encodeXML(item, indent + 1) + '</item>\n';
|
|
364
|
+
}
|
|
327
365
|
}
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
case 'object':
|
|
347
|
-
let objXML = '';
|
|
348
|
-
for (const [key, value] of Object.entries(data)) {
|
|
349
|
-
const safeKey = this.sanitizeXMLTag(key);
|
|
350
|
-
const valueType = this.getType(value);
|
|
351
|
-
|
|
352
|
-
if (valueType === 'object' || valueType === 'array') {
|
|
353
|
-
objXML += prefix + `<${safeKey}>\n`;
|
|
354
|
-
objXML += this.encodeXML(value, indent + 1);
|
|
355
|
-
objXML += prefix + `</${safeKey}>\n`;
|
|
356
|
-
} else {
|
|
357
|
-
objXML += prefix + `<${safeKey}>`;
|
|
358
|
-
objXML += this.encodeXML(value, 0);
|
|
359
|
-
objXML += `</${safeKey}>\n`;
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
return objXML;
|
|
363
|
-
|
|
364
|
-
default:
|
|
365
|
-
return '';
|
|
366
|
+
return arrXML;
|
|
367
|
+
|
|
368
|
+
case 'object':
|
|
369
|
+
let objXML = '';
|
|
370
|
+
for (const [key, value] of Object.entries(data)) {
|
|
371
|
+
const safeKey = this.sanitizeXMLTag(key);
|
|
372
|
+
const valueType = this.getType(value);
|
|
373
|
+
|
|
374
|
+
if (valueType === 'object' || valueType === 'array') {
|
|
375
|
+
objXML += prefix + `<${safeKey}>\n`;
|
|
376
|
+
objXML += this.encodeXML(value, indent + 1);
|
|
377
|
+
objXML += prefix + `</${safeKey}>\n`;
|
|
378
|
+
} else {
|
|
379
|
+
objXML += prefix + `<${safeKey}>`;
|
|
380
|
+
objXML += this.encodeXML(value, indent + 1);
|
|
381
|
+
objXML += `</${safeKey}>\n`;
|
|
382
|
+
}
|
|
366
383
|
}
|
|
367
|
-
|
|
384
|
+
return objXML;
|
|
368
385
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
*/
|
|
372
|
-
escapeXML(str) {
|
|
373
|
-
return str
|
|
374
|
-
.replace(/&/g, '&')
|
|
375
|
-
.replace(/</g, '<')
|
|
376
|
-
.replace(/>/g, '>')
|
|
377
|
-
.replace(/"/g, '"')
|
|
378
|
-
.replace(/'/g, ''');
|
|
386
|
+
default:
|
|
387
|
+
return '';
|
|
379
388
|
}
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Escape XML special characters
|
|
393
|
+
*/
|
|
394
|
+
escapeXML(str) {
|
|
395
|
+
return str
|
|
396
|
+
.replace(/&/g, '&')
|
|
397
|
+
.replace(/</g, '<')
|
|
398
|
+
.replace(/>/g, '>')
|
|
399
|
+
.replace(/"/g, '"')
|
|
400
|
+
.replace(/'/g, ''');
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Sanitize string for use as XML tag
|
|
405
|
+
*/
|
|
406
|
+
sanitizeXMLTag(str) {
|
|
407
|
+
return str.replace(/[^a-zA-Z0-9_-]/g, '_');
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* Get value type
|
|
412
|
+
*/
|
|
413
|
+
getType(value) {
|
|
414
|
+
if (value === null) return 'null';
|
|
415
|
+
if (value === undefined) return 'undefined';
|
|
416
|
+
if (Array.isArray(value)) return 'array';
|
|
417
|
+
return typeof value;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* Auto-detect best format for data
|
|
422
|
+
*/
|
|
423
|
+
suggestFormat(data) {
|
|
424
|
+
const { methods, paths } = data;
|
|
425
|
+
|
|
426
|
+
// Method-level data benefits most from TOON
|
|
427
|
+
if (methods) {
|
|
428
|
+
return 'toon';
|
|
386
429
|
}
|
|
387
430
|
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
getType(value) {
|
|
392
|
-
if (value === null) return 'null';
|
|
393
|
-
if (value === undefined) return 'undefined';
|
|
394
|
-
if (Array.isArray(value)) return 'array';
|
|
395
|
-
return typeof value;
|
|
431
|
+
// File lists work well with TOON too
|
|
432
|
+
if (paths) {
|
|
433
|
+
return 'toon';
|
|
396
434
|
}
|
|
397
435
|
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
suggestFormat(data) {
|
|
402
|
-
const { methods, paths } = data;
|
|
403
|
-
|
|
404
|
-
// Method-level data benefits most from TOON
|
|
405
|
-
if (methods) {
|
|
406
|
-
return 'toon';
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
// File lists work well with TOON too
|
|
410
|
-
if (paths) {
|
|
411
|
-
return 'toon';
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
// Default to JSON
|
|
415
|
-
return 'json';
|
|
416
|
-
}
|
|
436
|
+
// Default to JSON
|
|
437
|
+
return 'json';
|
|
438
|
+
}
|
|
417
439
|
}
|
|
418
440
|
|
|
419
441
|
export default FormatRegistry;
|