Module: Legion::Extensions::Coldstart::Helpers::ClaudeParser
- Defined in:
- lib/legion/extensions/coldstart/helpers/claude_parser.rb
Constant Summary collapse
- SECTION_TYPE_MAP =
{ /\bhard rules\b/i => :firmware, /\bidentity auth\b/i => :identity, /\barchitecture\b/i => :semantic, /\bkey concepts?\b|terminology/i => :semantic, /\bproject structure\b/i => :semantic, /\bdigital worker\b/i => :semantic, /\bgotcha|caveat|pitfall|known issue/i => :procedural, /\bcli\b|command|usage/i => :procedural, /\bapi\b|routes?\b|endpoint/i => :procedural, /\bmcp\b/i => :procedural, /\bconfig|settings?\b|scaffold/i => :procedural, /\bskills?\b/i => :procedural, /\bdevelopment|conventions?\b|workflow/i => :procedural, /\brubocop|lint/i => :procedural, /\bdependenc/i => :semantic, /\bfile (map|structure)\b/i => :semantic, /\bwhat is\b/i => :semantic, /\bpurpose\b/i => :semantic, /\bstatus|stub|todo\b/i => :semantic, /\bagentic\b/i => :semantic, /\bjwt\b|auth|crypt|secur/i => :procedural, /\bsinatra\b|rest\b/i => :procedural, /\btransport|rabbitmq|amqp/i => :semantic }.freeze
- DEFAULT_TRACE_TYPE =
:semantic- SECTION_VALENCE_MAP =
{ /\bgotcha|caveat|pitfall|known issue|error|stub|todo\b/i => { valence: -0.4, intensity: 0.5 }, /\bhard rules?\b/i => { valence: 0.0, intensity: 0.8 }, /\barchitecture\b|design|key concepts?\b/i => { valence: 0.3, intensity: 0.4 }, /\bdependenc|integration|requirements?\b/i => { valence: 0.1, intensity: 0.3 } }.freeze
- DEFAULT_VALENCE =
{ valence: 0.1, intensity: 0.3 }.freeze
Class Method Summary collapse
-
.classify_section(heading) ⇒ Object
Classify a section heading into a trace type.
-
.classify_valence(heading) ⇒ Object
Classify a section heading into an emotional valence hash.
-
.detect_file_type(file_path) ⇒ Object
Detect whether a file is a MEMORY.md or CLAUDE.md.
-
.extract_dir_context(file_path) ⇒ Object
Extract directory context for domain tagging (e.g., "lex-coldstart", "LegionIO").
-
.extract_inline_tags(text) ⇒ Object
Extract backtick-quoted terms and bold terms as domain tags.
-
.extract_items(body) ⇒ Object
Extract individual items from a section body.
-
.parse_directory(dir_path, pattern: '**/{CLAUDE,MEMORY}.md') ⇒ Object
Parse all matching markdown files under a directory.
-
.parse_file(file_path) ⇒ Object
Parse a markdown file into an array of trace candidates.
- .skip_path?(path) ⇒ Boolean
- .slugify(text) ⇒ Object
-
.split_sections(content) ⇒ Object
Split markdown content into sections by ## headers.
Class Method Details
.classify_section(heading) ⇒ Object
Classify a section heading into a trace type.
189 190 191 192 193 194 |
# File 'lib/legion/extensions/coldstart/helpers/claude_parser.rb', line 189 def classify_section(heading) SECTION_TYPE_MAP.each do |pattern, type| return type if heading.match?(pattern) end DEFAULT_TRACE_TYPE end |
.classify_valence(heading) ⇒ Object
Classify a section heading into an emotional valence hash.
197 198 199 200 201 202 |
# File 'lib/legion/extensions/coldstart/helpers/claude_parser.rb', line 197 def classify_valence(heading) SECTION_VALENCE_MAP.each do |pattern, vals| return vals if heading.match?(pattern) end DEFAULT_VALENCE end |
.detect_file_type(file_path) ⇒ Object
Detect whether a file is a MEMORY.md or CLAUDE.md
96 97 98 99 100 101 102 103 104 105 |
# File 'lib/legion/extensions/coldstart/helpers/claude_parser.rb', line 96 def detect_file_type(file_path) basename = File.basename(file_path).downcase if basename.include?('memory') && basename.end_with?('.md') :memory elsif basename.include?('claude') && basename.end_with?('.md') :claude_md else :markdown end end |
.extract_dir_context(file_path) ⇒ Object
Extract directory context for domain tagging (e.g., "lex-coldstart", "LegionIO").
205 206 207 208 209 210 211 212 |
# File 'lib/legion/extensions/coldstart/helpers/claude_parser.rb', line 205 def extract_dir_context(file_path) parts = file_path.split('/') # Find the most meaningful directory name meaningful = parts.reverse.find do |p| p.match?(/\A(lex-|legion-|Legion|extensions)/) && p != File.basename(file_path) end meaningful || parts[-2] end |
.extract_inline_tags(text) ⇒ Object
Extract backtick-quoted terms and bold terms as domain tags.
179 180 181 182 183 184 185 186 |
# File 'lib/legion/extensions/coldstart/helpers/claude_parser.rb', line 179 def (text) = [] text.scan(/`([^`]+)`/) { |m| << m[0] } text.scan(/\*\*([^*]+)\*\*/) { |m| << m[0] } .map { |t| t.gsub(/[^a-zA-Z0-9_\-.]/, '_').downcase } .reject { |t| t.length > 60 || t.length < 2 } .uniq end |
.extract_items(body) ⇒ Object
Extract individual items from a section body. Bullets become individual items; paragraphs become single items; code blocks are kept as single items.
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 |
# File 'lib/legion/extensions/coldstart/helpers/claude_parser.rb', line 133 def extract_items(body) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity items = [] current_item = nil in_code_block = false body.each_line do |line| if line.start_with?('```') if in_code_block current_item << line items << current_item current_item = nil in_code_block = false else items << current_item if current_item && !current_item.strip.empty? current_item = line in_code_block = true end next end if in_code_block current_item << line next end if line.match?(/\A\s*[-*]\s+/) items << current_item if current_item && !current_item.strip.empty? current_item = line.sub(/\A\s*[-*]\s+/, '').strip elsif line.match?(/\A\s{2,}[-*]\s+/) || (current_item && line.match?(/\A\s{2,}\S/)) # Sub-bullet or continuation of current bullet current_item = "#{current_item || ''} #{line.strip}" elsif line.strip.empty? items << current_item if current_item && !current_item.strip.empty? current_item = nil elsif current_item current_item += " #{line.strip}" else current_item = line.strip end end items << current_item if current_item && !current_item.strip.empty? items.compact.map(&:strip).reject(&:empty?) end |
.parse_directory(dir_path, pattern: '**/{CLAUDE,MEMORY}.md') ⇒ Object
Parse all matching markdown files under a directory.
Returns Array
87 88 89 90 91 92 93 |
# File 'lib/legion/extensions/coldstart/helpers/claude_parser.rb', line 87 def parse_directory(dir_path, pattern: '**/{CLAUDE,MEMORY}.md') Dir.glob(File.join(dir_path, pattern)).flat_map do |path| next [] if skip_path?(path) parse_file(path) end end |
.parse_file(file_path) ⇒ Object
Parse a markdown file into an array of trace candidates. Each candidate is a Hash ready for lex-memory's store_trace.
Returns Array
:trace_type, :content_payload, :domain_tags, :origin, :confidence, :source_file
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 |
# File 'lib/legion/extensions/coldstart/helpers/claude_parser.rb', line 52 def parse_file(file_path) content = File.read(file_path) file_type = detect_file_type(file_path) sections = split_sections(content) source_name = File.basename(file_path) dir_context = extract_dir_context(file_path) traces = [] sections.each do |section| trace_type = classify_section(section[:heading]) = [file_type.to_s, source_name, dir_context].compact << section[:heading_slug] if section[:heading_slug] items = extract_items(section[:body]) items.each do |item| = (item) section_valence = classify_valence(section[:heading]) traces << { trace_type: trace_type, content_payload: item.strip, domain_tags: ( + ).uniq, origin: file_type == :memory ? :firmware : :direct_experience, confidence: trace_type == :firmware ? 1.0 : 0.7, emotional_valence: trace_type == :firmware ? 0.0 : section_valence[:valence], emotional_intensity: section_valence[:intensity], source_file: file_path } end end traces.reject { |t| t[:content_payload].empty? } end |
.skip_path?(path) ⇒ Boolean
218 219 220 221 222 223 224 225 226 |
# File 'lib/legion/extensions/coldstart/helpers/claude_parser.rb', line 218 def skip_path?(path) path.include?('/_deprecated/') || path.include?('/_ignored/') || path.include?('/z_other/') || path.include?('_working/') || path.include?('/test/') || path.include?('/.terraform/') || path.include?('/references/') end |
.slugify(text) ⇒ Object
214 215 216 |
# File 'lib/legion/extensions/coldstart/helpers/claude_parser.rb', line 214 def slugify(text) text.downcase.gsub(/[^a-z0-9]+/, '-').gsub(/\A-|-\z/, '') end |
.split_sections(content) ⇒ Object
Split markdown content into sections by ## headers. Returns Array<Hash{ heading:, heading_slug:, body: }>
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 |
# File 'lib/legion/extensions/coldstart/helpers/claude_parser.rb', line 109 def split_sections(content) sections = [] current = { heading: 'preamble', heading_slug: 'preamble', body: +'' } content.each_line do |line| if line.match?(/\A##\s+/) sections << current unless current[:body].strip.empty? heading = line.sub(/\A##\s+/, '').strip current = { heading: heading, heading_slug: slugify(heading), body: +'' } else current[:body] << line end end sections << current unless current[:body].strip.empty? sections end |