Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 22 additions & 7 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ var (
excludePatterns []string
includePatterns []string
branch string
outputFormat string
)

var rootCmd = &cobra.Command{
Expand Down Expand Up @@ -84,7 +85,19 @@ You can specify a local path or a repository URL as the source.`,
os.Exit(1)
}

ingestResult.FormatOutput(opts)
var outputContent string

if outputFormat == "json" {
jsonBytes, errJSON := ingestResult.FormatJSON(opts)
if errJSON != nil {
fmt.Fprintf(os.Stderr, "Error formatting JSON output: %v\n", errJSON)
os.Exit(1)
}
outputContent = string(jsonBytes)
} else {
ingestResult.FormatOutput(opts)
outputContent = ingestResult.TreeStructure + "\n" + ingestResult.FileContents
}

if opts.OutputFile != "" && opts.OutputFile != "-" {
outputDir := filepath.Dir(opts.OutputFile)
Expand All @@ -95,20 +108,21 @@ You can specify a local path or a repository URL as the source.`,
}
}

fileContentToWrite := ingestResult.TreeStructure + "\n" + ingestResult.FileContents
err = os.WriteFile(opts.OutputFile, []byte(fileContentToWrite), 0644)
err = os.WriteFile(opts.OutputFile, []byte(outputContent), 0644)
if err != nil {
fmt.Fprintf(os.Stderr, "Error writing to output file %s: %v\n", opts.OutputFile, err)
os.Exit(1)
}
fmt.Fprintf(os.Stderr, "Digest written to: %s\n", opts.OutputFile)
} else {
fmt.Println(ingestResult.TreeStructure)
fmt.Println(ingestResult.FileContents)
fmt.Println(outputContent)
}

fmt.Fprintln(os.Stderr, "\n--- Summary ---")
fmt.Fprint(os.Stderr, ingestResult.Summary)
if outputFormat != "json" {
ingestResult.FormatOutput(opts)
fmt.Fprintln(os.Stderr, "\n--- Summary ---")
fmt.Fprint(os.Stderr, ingestResult.Summary)
}

},
}
Expand Down Expand Up @@ -142,4 +156,5 @@ func init() {
rootCmd.Flags().StringSliceP("exclude-pattern", "e", []string{}, "Comma-separated glob patterns to exclude (adds to defaults)")
rootCmd.Flags().StringSliceVarP(&includePatterns, "include-pattern", "i", []string{}, "Comma-separated glob patterns to include (overrides excludes)")
rootCmd.Flags().StringVarP(&branch, "branch", "b", "", "Branch to clone and ingest (if source is a Git URL)")
rootCmd.Flags().StringVarP(&outputFormat, "format", "f", "text", "Output format: text or json")
}
140 changes: 140 additions & 0 deletions internal/digest/json.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
package digest

import (
"encoding/json"
"path/filepath"
)

type JSONOutput struct {
Summary JSONSummary `json:"summary"`
Tree []*JSONNode `json:"tree"`
Files []JSONFile `json:"files"`
GitInfo *JSONGitInfo `json:"git_info,omitempty"`
}

type JSONSummary struct {
Source string `json:"source"`
TotalFiles int `json:"total_files"`
TotalSize int64 `json:"total_size"`
TotalSizeHuman string `json:"total_size_human"`
ExcludePatterns []string `json:"exclude_patterns,omitempty"`
IncludePatterns []string `json:"include_patterns,omitempty"`
MaxFileSize int64 `json:"max_file_size,omitempty"`
}

type JSONNode struct {
Name string `json:"name"`
Path string `json:"path"`
Type string `json:"type"`
Size int64 `json:"size,omitempty"`
Children []*JSONNode `json:"children,omitempty"`
}

type JSONFile struct {
Path string `json:"path"`
Size int64 `json:"size"`
Type string `json:"type"`
Content string `json:"content,omitempty"`
}

type JSONGitInfo struct {
RepoURL string `json:"repo_url,omitempty"`
Branch string `json:"branch,omitempty"`
Commit string `json:"commit,omitempty"`
User string `json:"user,omitempty"`
RepoName string `json:"repo_name,omitempty"`
}

func (r *Result) FormatJSON(opts IngestionOptions) ([]byte, error) {
output := JSONOutput{
Summary: JSONSummary{
Source: opts.Source,
TotalFiles: r.TotalFiles,
TotalSize: r.TotalSize,
TotalSizeHuman: formatBytes(r.TotalSize),
ExcludePatterns: opts.ExcludePatterns,
IncludePatterns: opts.IncludePatterns,
MaxFileSize: opts.MaxFileSize,
},
Tree: buildJSONTree(r.RootNode),
Files: gatherJSONFiles(r.RootNode),
}

if r.GitInfo != nil {
output.GitInfo = &JSONGitInfo{
RepoURL: r.GitInfo.RepoURL,
Branch: r.GitInfo.Branch,
Commit: r.GitInfo.Commit,
User: r.GitInfo.User,
RepoName: r.GitInfo.RepoName,
}
}

return json.MarshalIndent(output, "", " ")
}

func buildJSONTree(node *FileNode) []*JSONNode {
if node == nil {
return nil
}

if node.Type == NodeTypeDir && node.Children != nil {
result := make([]*JSONNode, 0, len(node.Children))
for _, child := range node.Children {
result = append(result, fileNodeToJSON(child))
}
return result
}

return []*JSONNode{fileNodeToJSON(node)}
}

func fileNodeToJSON(node *FileNode) *JSONNode {
jn := &JSONNode{
Name: node.Name,
Path: filepath.ToSlash(node.Path),
Type: string(node.Type),
Size: node.Size,
}

if node.Type == NodeTypeDir && node.Children != nil {
jn.Children = make([]*JSONNode, 0, len(node.Children))
for _, child := range node.Children {
jn.Children = append(jn.Children, fileNodeToJSON(child))
}
}

return jn
}

func gatherJSONFiles(node *FileNode) []JSONFile {
var files []JSONFile
gatherJSONFilesRecursive(node, &files)
return files
}

func gatherJSONFilesRecursive(node *FileNode, files *[]JSONFile) {
if node.Type == NodeTypeFile {
f := JSONFile{
Path: filepath.ToSlash(node.Path),
Size: node.Size,
Type: string(node.Type),
}
if node.Content != "" {
f.Content = node.Content
}
*files = append(*files, f)
} else if node.Type == NodeTypeNotText || node.Type == NodeTypeTooLarge {
*files = append(*files, JSONFile{
Path: filepath.ToSlash(node.Path),
Size: node.Size,
Type: string(node.Type),
})
}

if node.Type == NodeTypeDir {
for _, child := range node.Children {
gatherJSONFilesRecursive(child, files)
}
}
}
Loading