Class: AIHype::Memory

Inherits:
Object
  • Object
show all
Defined in:
lib/aihype/memory.rb

Constant Summary collapse

SEMVER_REGEX =
/^\d+\.\d+\.\d+$/

Class Method Summary collapse

Class Method Details

.create_defaultObject



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/aihype/memory.rb', line 43

def self.create_default
  now = Time.now.utc
  safety_rules = Defaults::SAFETY_RULES.map { |rule| "- #{rule}" }.join("\n")

  <<~MARKDOWN
    ---
    version: #{Defaults::DEFAULT_VERSION}
    created: #{now.iso8601}
    updated: #{now.iso8601}
    ---

    # AIHype Blacklist Rules

    ## Default Safety Rules
    #{safety_rules}

    ## User Rules
    - #{Defaults::USER_RULES_PLACEHOLDER}
  MARKDOWN
end

.extract_list_items(section) ⇒ Object



105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/aihype/memory.rb', line 105

def self.extract_list_items(section)
  return [] unless section

  items = section.split("\n").select { |line| line.strip.start_with?('-') }.map do |line|
    line.sub(/^-\s*/, '').strip
  end

  # Validate items before filtering placeholders
  items.each do |item|
    # Skip placeholder validation
    next if item == Defaults::USER_RULES_PLACEHOLDER

    raise InvalidMemoryFileError, 'Rule cannot be empty' if item.strip.empty?
  end

  # Filter out empty items and placeholders
  items.reject { |item| item.empty? || item == Defaults::USER_RULES_PLACEHOLDER }
end

.extract_rules(markdown_content) ⇒ Object



87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/aihype/memory.rb', line 87

def self.extract_rules(markdown_content)
  raise InvalidMemoryFileError, 'Missing "Default Safety Rules" section' unless markdown_content.include?('## Default Safety Rules')

  sections = markdown_content.split(/^## /)
  default_section = sections.find { |s| s.start_with?('Default Safety Rules') }
  user_section = sections.find { |s| s.start_with?('User Rules') }

  default_rules = extract_list_items(default_section)
  user_rules = extract_list_items(user_section)

  validate_rules!(default_rules + user_rules)

  min_rules = Defaults::MIN_SAFETY_RULES
  raise InvalidMemoryFileError, "Default Safety Rules must contain at least #{min_rules} rules" if default_rules.length < min_rules

  [default_rules, user_rules]
end

.load(file_path) ⇒ Object



15
16
17
18
19
20
# File 'lib/aihype/memory.rb', line 15

def self.load(file_path)
  content = File.read(file_path)
  parse(content)
rescue Errno::ENOENT
  nil
end

.parse(content) ⇒ Object



22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/aihype/memory.rb', line 22

def self.parse(content)
  parts = content.split(/^---$/)
  raise InvalidMemoryFileError, 'Missing YAML frontmatter' if parts.length < 3

  yaml_content = parts[1].strip
  markdown_content = parts[2..].join('---').strip

  frontmatter = YAML.safe_load(yaml_content, permitted_classes: [Time])
  validate_frontmatter!(frontmatter)

  default_rules, user_rules = extract_rules(markdown_content)

  MemoryFile.new(
    version: frontmatter['version'],
    created: parse_datetime(frontmatter['created']),
    updated: parse_datetime(frontmatter['updated']),
    default_rules: default_rules,
    user_rules: user_rules
  )
end

.parse_datetime(value) ⇒ Object



82
83
84
85
# File 'lib/aihype/memory.rb', line 82

def self.parse_datetime(value)
  return value if value.is_a?(Time)
  Time.parse(value)
end

.save(file_path, memory_file) ⇒ Object



64
65
66
67
# File 'lib/aihype/memory.rb', line 64

def self.save(file_path, memory_file)
  FileUtils.mkdir_p(File.dirname(file_path))
  File.write(file_path, memory_file.to_markdown)
end

.validate_frontmatter!(frontmatter) ⇒ Object



69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/aihype/memory.rb', line 69

def self.validate_frontmatter!(frontmatter)
  raise InvalidMemoryFileError, 'Missing version field' unless frontmatter['version']

  version = frontmatter['version'].to_s
  raise InvalidMemoryFileError, 'Invalid semver version' unless version =~ SEMVER_REGEX

  # Parse or validate datetime fields (they might already be Time objects from YAML)
  parse_datetime(frontmatter['created'])
  parse_datetime(frontmatter['updated'])
rescue ArgumentError => e
  raise InvalidMemoryFileError, "Invalid datetime: #{e.message}"
end

.validate_rules!(rules) ⇒ Object



124
125
126
127
128
129
# File 'lib/aihype/memory.rb', line 124

def self.validate_rules!(rules)
  rules.each do |rule|
    raise InvalidMemoryFileError, 'Rule cannot be empty' if rule.strip.empty?
    raise InvalidMemoryFileError, 'Rule exceeds 500 characters' if rule.length > 500
  end
end