cc-os/plugins/cc-architect/scripts/plugin_config/runtime_verifier.rb

88 lines
2.1 KiB
Ruby
Raw Normal View History

# frozen_string_literal: true
require 'json'
require 'open3'
module PluginConfig
class RuntimeVerifier
STATUSES = %w[enabled disabled failed_to_load not_present].freeze
def initialize(capture3: Open3.method(:capture3))
@capture3 = capture3
end
def snapshot
stdout, _stderr, status = @capture3.call('claude', 'plugin', 'list')
return {} unless status.success?
parse_plugin_list(stdout)
rescue Errno::ENOENT
{}
end
def verify_status(plugin_key, expected_status)
current = snapshot
actual = current[plugin_key] || 'not_present'
if actual == expected_status
{ verified: true, details: "Plugin '#{plugin_key}' status is '#{expected_status}'" }
else
{ verified: false, details: "Plugin '#{plugin_key}' expected '#{expected_status}', got '#{actual}'" }
end
end
def self.diff(before, after)
all_keys = (before.keys + after.keys).uniq
changed = 0
unchanged = 0
all_keys.each do |key|
if before[key] == after[key]
unchanged += 1
else
changed += 1
end
end
{ changed: changed, unchanged: unchanged }
end
private
def parse_plugin_list(output)
plugins = {}
output.each_line do |line|
line = line.strip
next if line.empty?
# Expected format: "plugin-name@marketplace status"
# or variations with columns/tabs
parts = line.split(/\s{2,}|\t/)
next unless parts.length >= 2
key = parts[0].strip
status = parts[1].strip.downcase
next unless key.include?('@')
normalized = if STATUSES.include?(status)
status
elsif status.include?('enabled')
'enabled'
elsif status.include?('disabled')
'disabled'
elsif status.include?('fail')
'failed_to_load'
else
status
end
plugins[key] = normalized
end
plugins
end
end
end