Module: CcExport::CLI

Defined in:
lib/ccexport/cli.rb

Class Method Summary collapse

Class Method Details

.check_dependencies(silent = false) ⇒ Object



115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'lib/ccexport/cli.rb', line 115

def self.check_dependencies(silent = false)
  missing_deps = []
  missing_deps << 'trufflehog' unless system('which trufflehog > /dev/null 2>&1')
  missing_deps << 'cmark-gfm' unless system('which cmark-gfm > /dev/null 2>&1')
  return if missing_deps.empty?

  puts "Missing required dependencies: #{missing_deps.join(', ')}" unless silent

  if system('which brew > /dev/null 2>&1')
    puts "\nAttempting to install missing dependencies via Homebrew..." unless silent

    success = true
    missing_deps.each do |dep|
      puts "Installing #{dep}..." unless silent
      unless system(silent ? "brew install #{dep} > /dev/null 2>&1" : "brew install #{dep}")
        puts "Failed to install #{dep}" unless silent
        success = false
      end
    end

    if success
      puts "✅ All dependencies installed successfully!" unless silent
      return
    else
      puts "❌ Some dependencies failed to install" unless silent
    end
  else
    puts "\nHomebrew not found." unless silent
  end

  unless silent
    puts "\nPlease install the missing dependencies manually:"
    puts "- TruffleHog: https://github.com/trufflesecurity/trufflehog#installation"
    puts "- cmark-gfm: Install via your system package manager or from source"
    puts "\nFor detailed installation instructions, see:"
    puts "https://github.com/marcheiligers/ccexport#installation"
    puts "\nAlternatively, install Homebrew and run ccexport again for automatic installation:"
    puts "https://brew.sh"
  end

  exit 1
end

.run(argv = ARGV) ⇒ Object



8
9
10
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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/ccexport/cli.rb', line 8

def self.run(argv = ARGV)
  options = {}

  OptionParser.new do |parser|
    parser.banner = "Usage: ccexport [options]"

    parser.on("--from DATE", "Filter messages from this date (YYYY-MM-DD)") do |date|
      options[:from] = date
    end

    parser.on("--to DATE", "Filter messages to this date (YYYY-MM-DD)") do |date|
      options[:to] = date
    end

    parser.on("--today", "Filter messages from today only") do
      options[:today] = true
    end

    parser.on("--in PATH", "Project path to export conversations from (default: current directory)") do |path|
      options[:in] = path
    end

    parser.on("--out PATH", "Output directory or file path (default: claude-conversations)") do |path|
      options[:out] = path
    end

    parser.on("--timestamps", "Show timestamps with each message") do
      options[:timestamps] = true
    end

    parser.on("--preview", "Generate HTML preview and open in browser") do
      options[:preview] = true
    end

    parser.on("--no-open", "Generate HTML preview without opening in browser (requires --preview)") do
      options[:no_open] = true
    end

    parser.on("--template NAME_OR_PATH", "HTML template name (from templates dir) or file path (default: default)") do |template|
      options[:template] = template
    end

    parser.on("--jsonl FILE", "Process a specific JSONL file instead of scanning directories") do |file|
      options[:jsonl] = file
    end

    parser.on("--session ID", "Export a specific session by its UUID") do |id|
      options[:session] = id
    end

    parser.on("--clean", "Export only visible conversation (no tool calls, thinking, or metadata)") do
      options[:clean] = true
    end

    parser.on("--stdout", "Write markdown to stdout instead of a file (implies --silent)") do
      options[:stdout] = true
    end

    parser.on("-s", "--silent", "Silent mode - suppress all output") do
      options[:silent] = true
    end

    parser.on("--skip-dependency-check", "Skip checking for external dependencies") do
      options[:skip_dependency_check] = true
    end

    parser.on("-h", "--help", "Show this help message") do
      puts parser
      exit
    end
  end.parse!(argv)

  check_dependencies(options[:silent]) unless options[:skip_dependency_check]

  begin
    if options[:no_open] && !options[:preview]
      puts "Error: --no-open requires --preview" unless options[:silent]
      exit 1
    end

    if options[:jsonl] && !File.exist?(options[:jsonl])
      puts "Error: JSONL file not found: #{options[:jsonl]}" unless options[:silent]
      exit 1
    end

    if options[:jsonl] && !options[:jsonl].end_with?('.jsonl')
      puts "Error: File must have .jsonl extension: #{options[:jsonl]}" unless options[:silent]
      exit 1
    end

    project_path = options[:in] || Dir.pwd
    output_dir = options[:out] || 'claude-conversations'
    result = ClaudeConversationExporter.export(project_path, output_dir, options)

    if options[:preview]
      preview_target = result[:output_file] || output_dir
      template_name = options[:template] || 'default'
      ClaudeConversationExporter.generate_preview(preview_target, !options[:no_open], result[:leaf_summaries] || [], template_name, options[:silent])
    end

    exit 0
  rescue StandardError => e
    puts "Error: #{e.message}" unless options[:silent]
    exit 1
  end
end