Class: Aidp::Metadata::Scanner

Inherits:
Object
  • Object
show all
Defined in:
lib/aidp/metadata/scanner.rb

Overview

Scans directories for tool files and extracts metadata

Recursively finds all .md files in configured directories, parses their metadata, and returns a collection of ToolMetadata objects.

Examples:

Scanning directories

scanner = Scanner.new([".aidp/skills", ".aidp/templates"])
tools = scanner.scan_all

Instance Method Summary collapse

Constructor Details

#initialize(directories = []) ⇒ Scanner

Initialize scanner with directory paths

Parameters:

  • directories (Array<String>) (defaults to: [])

    Directories to scan



20
21
22
# File 'lib/aidp/metadata/scanner.rb', line 20

def initialize(directories = [])
  @directories = Array(directories)
end

Instance Method Details

#find_markdown_files(directory) ⇒ Array<String>

Find all markdown files in directory recursively

Parameters:

  • directory (String)

    Directory path

Returns:

  • (Array<String>)

    Paths to .md files



89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/aidp/metadata/scanner.rb', line 89

def find_markdown_files(directory)
  pattern = File.join(directory, "**", "*.md")
  files = Dir.glob(pattern)

  Aidp.log_debug(
    "metadata",
    "Found markdown files",
    directory: directory,
    count: files.size
  )

  files
end

#scan_allArray<ToolMetadata>

Scan all configured directories

Returns:



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/aidp/metadata/scanner.rb', line 27

def scan_all
  Aidp.log_debug("metadata", "Scanning directories", directories: @directories)

  all_tools = []
  @directories.each do |dir|
    tools = scan_directory(dir)
    all_tools.concat(tools)
  end

  Aidp.log_info(
    "metadata",
    "Scan complete",
    directories: @directories.size,
    tools_found: all_tools.size
  )

  all_tools
end

#scan_changes(directory, previous_hashes = {}) ⇒ Hash

Scan for changes since last scan

Compares file hashes to detect changes

Parameters:

  • directory (String)

    Directory path

  • previous_hashes (Hash<String, String>) (defaults to: {})

    Map of file_path => file_hash

Returns:

  • (Hash)

    Hash with :added, :modified, :removed keys



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
177
178
179
180
181
182
183
184
185
186
187
188
# File 'lib/aidp/metadata/scanner.rb', line 143

def scan_changes(directory, previous_hashes = {})
  Aidp.log_debug("metadata", "Scanning for changes", directory: directory)

  current_files = find_markdown_files(directory)
  current_hashes = {}

  changes = {
    added: [],
    modified: [],
    removed: [],
    unchanged: []
  }

  # Check for added and modified files
  current_files.each do |file_path|
    content = File.read(file_path, encoding: "UTF-8")
    file_hash = Parser.compute_file_hash(content)
    current_hashes[file_path] = file_hash

    if previous_hashes.key?(file_path)
      if previous_hashes[file_path] != file_hash
        changes[:modified] << file_path
      else
        changes[:unchanged] << file_path
      end
    else
      changes[:added] << file_path
    end
  end

  # Check for removed files
  previous_hashes.keys.each do |file_path|
    changes[:removed] << file_path unless current_hashes.key?(file_path)
  end

  Aidp.log_info(
    "metadata",
    "Change detection complete",
    added: changes[:added].size,
    modified: changes[:modified].size,
    removed: changes[:removed].size,
    unchanged: changes[:unchanged].size
  )

  changes
end

#scan_directory(directory, type: nil) ⇒ Array<ToolMetadata>

Scan a single directory

Parameters:

  • directory (String)

    Directory path to scan

  • type (String, nil) (defaults to: nil)

    Tool type filter or nil for all

Returns:



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
# File 'lib/aidp/metadata/scanner.rb', line 51

def scan_directory(directory, type: nil)
  unless Dir.exist?(directory)
    Aidp.log_warn("metadata", "Directory not found", directory: directory)
    return []
  end

  Aidp.log_debug("metadata", "Scanning directory", directory: directory, type: type)

  tools = []
  md_files = find_markdown_files(directory)

  md_files.each do |file_path|
    tool = Parser.parse_file(file_path, type: type)
    tools << tool if type.nil? || tool.type == type
  rescue Aidp::Errors::ValidationError => e
    Aidp.log_warn(
      "metadata",
      "Failed to parse file",
      file: file_path,
      error: e.message
    )
  end

  Aidp.log_debug(
    "metadata",
    "Directory scan complete",
    directory: directory,
    files_found: md_files.size,
    tools_parsed: tools.size
  )

  tools
end

#scan_with_filter(directory, &filter) ⇒ Array<ToolMetadata>

Scan with file filtering

Parameters:

  • directory (String)

    Directory path

  • filter (Proc)

    Filter proc that receives file_path and returns boolean

Returns:



108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/aidp/metadata/scanner.rb', line 108

def scan_with_filter(directory, &filter)
  unless Dir.exist?(directory)
    Aidp.log_warn("metadata", "Directory not found", directory: directory)
    return []
  end

  tools = []
  md_files = find_markdown_files(directory)

  md_files.each do |file_path|
    next unless filter.call(file_path)

    begin
      tool = Parser.parse_file(file_path)
      tools << tool
    rescue Aidp::Errors::ValidationError => e
      Aidp.log_warn(
        "metadata",
        "Failed to parse file",
        file: file_path,
        error: e.message
      )
    end
  end

  tools
end