11package main
22
33import (
4+ "encoding/json"
45 "fmt"
56 "os"
67 "path/filepath"
@@ -10,6 +11,35 @@ import (
1011 "github.com/JordanCoin/docmap/render"
1112)
1213
14+ // JSON output structures
15+ type JSONOutput struct {
16+ Root string `json:"root"`
17+ TotalTokens int `json:"total_tokens"`
18+ TotalDocs int `json:"total_docs"`
19+ Documents []JSONDocument `json:"documents"`
20+ }
21+
22+ type JSONDocument struct {
23+ Filename string `json:"filename"`
24+ Tokens int `json:"tokens"`
25+ Sections []JSONSection `json:"sections"`
26+ References []JSONRef `json:"references,omitempty"`
27+ }
28+
29+ type JSONSection struct {
30+ Level int `json:"level"`
31+ Title string `json:"title"`
32+ Tokens int `json:"tokens"`
33+ KeyTerms []string `json:"key_terms,omitempty"`
34+ Children []JSONSection `json:"children,omitempty"`
35+ }
36+
37+ type JSONRef struct {
38+ Text string `json:"text"`
39+ Target string `json:"target"`
40+ Line int `json:"line"`
41+ }
42+
1343var version = "dev"
1444
1545func main () {
@@ -36,6 +66,7 @@ func main() {
3666 var sectionFilter string
3767 var expandSection string
3868 var showRefs bool
69+ var jsonMode bool
3970 for i := 2 ; i < len (os .Args ); i ++ {
4071 switch os .Args [i ] {
4172 case "--section" , "-s" :
@@ -50,6 +81,8 @@ func main() {
5081 }
5182 case "--refs" , "-r" :
5283 showRefs = true
84+ case "--json" , "-j" :
85+ jsonMode = true
5386 }
5487 }
5588
@@ -67,7 +100,10 @@ func main() {
67100 fmt .Println ("No markdown files found" )
68101 os .Exit (1 )
69102 }
70- if showRefs {
103+ if jsonMode {
104+ absPath , _ := filepath .Abs (target )
105+ outputJSON (docs , absPath )
106+ } else if showRefs {
71107 render .RefsTree (docs , target )
72108 } else {
73109 render .MultiTree (docs , target )
@@ -84,7 +120,10 @@ func main() {
84120 parts := strings .Split (target , "/" )
85121 doc .Filename = parts [len (parts )- 1 ]
86122
87- if expandSection != "" {
123+ if jsonMode {
124+ absPath , _ := filepath .Abs (target )
125+ outputJSON ([]* parser.Document {doc }, absPath )
126+ } else if expandSection != "" {
88127 render .ExpandSection (doc , expandSection )
89128 } else if sectionFilter != "" {
90129 render .FilteredTree (doc , sectionFilter )
@@ -130,6 +169,50 @@ func parseDirectory(dir string) []*parser.Document {
130169 return docs
131170}
132171
172+ func outputJSON (docs []* parser.Document , root string ) {
173+ output := JSONOutput {
174+ Root : root ,
175+ TotalDocs : len (docs ),
176+ }
177+
178+ for _ , doc := range docs {
179+ jsonDoc := JSONDocument {
180+ Filename : doc .Filename ,
181+ Tokens : doc .TotalTokens ,
182+ Sections : convertSections (doc .Sections ),
183+ }
184+
185+ // Add references
186+ for _ , ref := range doc .References {
187+ jsonDoc .References = append (jsonDoc .References , JSONRef {
188+ Text : ref .Text ,
189+ Target : ref .Target ,
190+ Line : ref .Line ,
191+ })
192+ }
193+
194+ output .Documents = append (output .Documents , jsonDoc )
195+ output .TotalTokens += doc .TotalTokens
196+ }
197+
198+ json .NewEncoder (os .Stdout ).Encode (output )
199+ }
200+
201+ func convertSections (sections []* parser.Section ) []JSONSection {
202+ var result []JSONSection
203+ for _ , s := range sections {
204+ js := JSONSection {
205+ Level : s .Level ,
206+ Title : s .Title ,
207+ Tokens : s .Tokens ,
208+ KeyTerms : s .KeyTerms ,
209+ Children : convertSections (s .Children ),
210+ }
211+ result = append (result , js )
212+ }
213+ return result
214+ }
215+
133216func printUsage () {
134217 fmt .Println (`docmap - instant documentation structure for LLMs and humans
135218
@@ -148,6 +231,7 @@ Flags:
148231 -s, --section <name> Filter to a specific section
149232 -e, --expand <name> Show full content of a section
150233 -r, --refs Show cross-references between markdown files
234+ -j, --json Output JSON format
151235 -v, --version Print version
152236 -h, --help Show this help
153237
0 commit comments