claude_swarm 1.0.1 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.claude/commands/release.md +1 -1
- data/.claude/hooks/lint-code-files.rb +65 -0
- data/.rubocop.yml +22 -2
- data/CHANGELOG.md +14 -1
- data/CLAUDE.md +1 -1
- data/CONTRIBUTING.md +69 -0
- data/README.md +27 -2
- data/Rakefile +71 -3
- data/analyze_coverage.rb +94 -0
- data/docs/v2/CHANGELOG.swarm_cli.md +43 -0
- data/docs/v2/CHANGELOG.swarm_memory.md +379 -0
- data/docs/v2/CHANGELOG.swarm_sdk.md +362 -0
- data/docs/v2/README.md +308 -0
- data/docs/v2/guides/claude-code-agents.md +262 -0
- data/docs/v2/guides/complete-tutorial.md +3088 -0
- data/docs/v2/guides/getting-started.md +1456 -0
- data/docs/v2/guides/memory-adapters.md +998 -0
- data/docs/v2/guides/plugins.md +816 -0
- data/docs/v2/guides/quick-start-cli.md +1745 -0
- data/docs/v2/guides/rails-integration.md +1902 -0
- data/docs/v2/guides/swarm-memory.md +599 -0
- data/docs/v2/reference/cli.md +729 -0
- data/docs/v2/reference/ruby-dsl.md +2154 -0
- data/docs/v2/reference/yaml.md +1835 -0
- data/docs-team-swarm.yml +2222 -0
- data/examples/learning-assistant/assistant.md +7 -0
- data/examples/learning-assistant/example-memories/concept-example.md +90 -0
- data/examples/learning-assistant/example-memories/experience-example.md +66 -0
- data/examples/learning-assistant/example-memories/fact-example.md +76 -0
- data/examples/learning-assistant/example-memories/memory-index.md +78 -0
- data/examples/learning-assistant/example-memories/skill-example.md +168 -0
- data/examples/learning-assistant/learning_assistant.rb +34 -0
- data/examples/learning-assistant/learning_assistant.yml +20 -0
- data/examples/v2/dsl/01_basic.rb +44 -0
- data/examples/v2/dsl/02_core_parameters.rb +59 -0
- data/examples/v2/dsl/03_capabilities.rb +71 -0
- data/examples/v2/dsl/04_llm_parameters.rb +56 -0
- data/examples/v2/dsl/05_advanced_flags.rb +73 -0
- data/examples/v2/dsl/06_permissions.rb +80 -0
- data/examples/v2/dsl/07_mcp_server.rb +62 -0
- data/examples/v2/dsl/08_swarm_hooks.rb +53 -0
- data/examples/v2/dsl/09_agent_hooks.rb +67 -0
- data/examples/v2/dsl/10_all_agents_hooks.rb +67 -0
- data/examples/v2/dsl/11_delegation.rb +60 -0
- data/examples/v2/dsl/12_complete_integration.rb +137 -0
- data/examples/v2/file_tools_swarm.yml +102 -0
- data/examples/v2/hooks/01_basic_hooks.rb +133 -0
- data/examples/v2/hooks/02_usage_tracking.rb +201 -0
- data/examples/v2/hooks/03_production_monitoring.rb +429 -0
- data/examples/v2/hooks/agent_stop_exit_0.yml +21 -0
- data/examples/v2/hooks/agent_stop_exit_1.yml +21 -0
- data/examples/v2/hooks/agent_stop_exit_2.yml +26 -0
- data/examples/v2/hooks/multiple_hooks_all_pass.yml +37 -0
- data/examples/v2/hooks/multiple_hooks_first_fails.yml +37 -0
- data/examples/v2/hooks/multiple_hooks_second_fails.yml +37 -0
- data/examples/v2/hooks/multiple_hooks_warnings.yml +37 -0
- data/examples/v2/hooks/post_tool_use_exit_0.yml +24 -0
- data/examples/v2/hooks/post_tool_use_exit_1.yml +24 -0
- data/examples/v2/hooks/post_tool_use_exit_2.yml +24 -0
- data/examples/v2/hooks/post_tool_use_multi_matcher_exit_0.yml +26 -0
- data/examples/v2/hooks/post_tool_use_multi_matcher_exit_1.yml +26 -0
- data/examples/v2/hooks/post_tool_use_multi_matcher_exit_2.yml +26 -0
- data/examples/v2/hooks/pre_tool_use_exit_0.yml +24 -0
- data/examples/v2/hooks/pre_tool_use_exit_1.yml +24 -0
- data/examples/v2/hooks/pre_tool_use_exit_2.yml +24 -0
- data/examples/v2/hooks/pre_tool_use_multi_matcher_exit_0.yml +26 -0
- data/examples/v2/hooks/pre_tool_use_multi_matcher_exit_1.yml +26 -0
- data/examples/v2/hooks/pre_tool_use_multi_matcher_exit_2.yml +27 -0
- data/examples/v2/hooks/swarm_summary.sh +44 -0
- data/examples/v2/hooks/user_prompt_exit_0.yml +21 -0
- data/examples/v2/hooks/user_prompt_exit_1.yml +21 -0
- data/examples/v2/hooks/user_prompt_exit_2.yml +21 -0
- data/examples/v2/hooks/validate_bash.rb +59 -0
- data/examples/v2/multi_directory_permissions.yml +221 -0
- data/examples/v2/node_context_demo.rb +127 -0
- data/examples/v2/node_workflow.rb +173 -0
- data/examples/v2/path_resolution_demo.rb +216 -0
- data/examples/v2/simple-swarm-v2.rb +90 -0
- data/examples/v2/simple-swarm-v2.yml +62 -0
- data/examples/v2/swarm.yml +71 -0
- data/examples/v2/swarm_with_hooks.yml +61 -0
- data/examples/v2/swarm_with_hooks_simple.yml +25 -0
- data/examples/v2/think_tool_demo.rb +62 -0
- data/exe/swarm +6 -0
- data/lib/claude_swarm/claude_mcp_server.rb +0 -6
- data/lib/claude_swarm/cli.rb +10 -3
- data/lib/claude_swarm/commands/ps.rb +19 -20
- data/lib/claude_swarm/commands/show.rb +1 -1
- data/lib/claude_swarm/configuration.rb +10 -12
- data/lib/claude_swarm/mcp_generator.rb +10 -1
- data/lib/claude_swarm/orchestrator.rb +73 -49
- data/lib/claude_swarm/system_utils.rb +37 -11
- data/lib/claude_swarm/version.rb +1 -1
- data/lib/claude_swarm/worktree_manager.rb +1 -0
- data/lib/claude_swarm/yaml_loader.rb +22 -0
- data/lib/claude_swarm.rb +7 -2
- data/lib/swarm_cli/cli.rb +201 -0
- data/lib/swarm_cli/command_registry.rb +61 -0
- data/lib/swarm_cli/commands/mcp_serve.rb +130 -0
- data/lib/swarm_cli/commands/mcp_tools.rb +148 -0
- data/lib/swarm_cli/commands/migrate.rb +55 -0
- data/lib/swarm_cli/commands/run.rb +173 -0
- data/lib/swarm_cli/config_loader.rb +97 -0
- data/lib/swarm_cli/formatters/human_formatter.rb +711 -0
- data/lib/swarm_cli/formatters/json_formatter.rb +51 -0
- data/lib/swarm_cli/interactive_repl.rb +918 -0
- data/lib/swarm_cli/mcp_serve_options.rb +44 -0
- data/lib/swarm_cli/mcp_tools_options.rb +59 -0
- data/lib/swarm_cli/migrate_options.rb +54 -0
- data/lib/swarm_cli/migrator.rb +132 -0
- data/lib/swarm_cli/options.rb +151 -0
- data/lib/swarm_cli/ui/components/agent_badge.rb +33 -0
- data/lib/swarm_cli/ui/components/content_block.rb +120 -0
- data/lib/swarm_cli/ui/components/divider.rb +57 -0
- data/lib/swarm_cli/ui/components/panel.rb +62 -0
- data/lib/swarm_cli/ui/components/usage_stats.rb +70 -0
- data/lib/swarm_cli/ui/formatters/cost.rb +49 -0
- data/lib/swarm_cli/ui/formatters/number.rb +58 -0
- data/lib/swarm_cli/ui/formatters/text.rb +77 -0
- data/lib/swarm_cli/ui/formatters/time.rb +73 -0
- data/lib/swarm_cli/ui/icons.rb +59 -0
- data/lib/swarm_cli/ui/renderers/event_renderer.rb +188 -0
- data/lib/swarm_cli/ui/state/agent_color_cache.rb +45 -0
- data/lib/swarm_cli/ui/state/depth_tracker.rb +40 -0
- data/lib/swarm_cli/ui/state/spinner_manager.rb +170 -0
- data/lib/swarm_cli/ui/state/usage_tracker.rb +62 -0
- data/lib/swarm_cli/version.rb +5 -0
- data/lib/swarm_cli.rb +44 -0
- data/lib/swarm_memory/adapters/base.rb +141 -0
- data/lib/swarm_memory/adapters/filesystem_adapter.rb +845 -0
- data/lib/swarm_memory/chat_extension.rb +34 -0
- data/lib/swarm_memory/cli/commands.rb +306 -0
- data/lib/swarm_memory/core/entry.rb +37 -0
- data/lib/swarm_memory/core/frontmatter_parser.rb +108 -0
- data/lib/swarm_memory/core/metadata_extractor.rb +68 -0
- data/lib/swarm_memory/core/path_normalizer.rb +75 -0
- data/lib/swarm_memory/core/semantic_index.rb +244 -0
- data/lib/swarm_memory/core/storage.rb +288 -0
- data/lib/swarm_memory/core/storage_read_tracker.rb +63 -0
- data/lib/swarm_memory/dsl/builder_extension.rb +40 -0
- data/lib/swarm_memory/dsl/memory_config.rb +113 -0
- data/lib/swarm_memory/embeddings/embedder.rb +36 -0
- data/lib/swarm_memory/embeddings/informers_embedder.rb +152 -0
- data/lib/swarm_memory/errors.rb +21 -0
- data/lib/swarm_memory/integration/cli_registration.rb +30 -0
- data/lib/swarm_memory/integration/configuration.rb +43 -0
- data/lib/swarm_memory/integration/registration.rb +31 -0
- data/lib/swarm_memory/integration/sdk_plugin.rb +531 -0
- data/lib/swarm_memory/optimization/analyzer.rb +244 -0
- data/lib/swarm_memory/optimization/defragmenter.rb +863 -0
- data/lib/swarm_memory/prompts/memory.md.erb +109 -0
- data/lib/swarm_memory/prompts/memory_assistant.md.erb +181 -0
- data/lib/swarm_memory/prompts/memory_researcher.md.erb +281 -0
- data/lib/swarm_memory/prompts/memory_retrieval.md.erb +78 -0
- data/lib/swarm_memory/search/semantic_search.rb +112 -0
- data/lib/swarm_memory/search/text_search.rb +42 -0
- data/lib/swarm_memory/search/text_similarity.rb +80 -0
- data/lib/swarm_memory/skills/meta/deep-learning.md +101 -0
- data/lib/swarm_memory/skills/meta/deep-learning.yml +14 -0
- data/lib/swarm_memory/tools/load_skill.rb +313 -0
- data/lib/swarm_memory/tools/memory_defrag.rb +382 -0
- data/lib/swarm_memory/tools/memory_delete.rb +99 -0
- data/lib/swarm_memory/tools/memory_edit.rb +185 -0
- data/lib/swarm_memory/tools/memory_glob.rb +160 -0
- data/lib/swarm_memory/tools/memory_grep.rb +247 -0
- data/lib/swarm_memory/tools/memory_multi_edit.rb +281 -0
- data/lib/swarm_memory/tools/memory_read.rb +123 -0
- data/lib/swarm_memory/tools/memory_write.rb +231 -0
- data/lib/swarm_memory/utils.rb +50 -0
- data/lib/swarm_memory/version.rb +5 -0
- data/lib/swarm_memory.rb +166 -0
- data/lib/swarm_sdk/agent/RETRY_LOGIC.md +127 -0
- data/lib/swarm_sdk/agent/builder.rb +461 -0
- data/lib/swarm_sdk/agent/chat/context_tracker.rb +314 -0
- data/lib/swarm_sdk/agent/chat/hook_integration.rb +372 -0
- data/lib/swarm_sdk/agent/chat/logging_helpers.rb +116 -0
- data/lib/swarm_sdk/agent/chat/system_reminder_injector.rb +152 -0
- data/lib/swarm_sdk/agent/chat.rb +1159 -0
- data/lib/swarm_sdk/agent/context.rb +112 -0
- data/lib/swarm_sdk/agent/context_manager.rb +309 -0
- data/lib/swarm_sdk/agent/definition.rb +556 -0
- data/lib/swarm_sdk/claude_code_agent_adapter.rb +205 -0
- data/lib/swarm_sdk/configuration.rb +296 -0
- data/lib/swarm_sdk/context_compactor/metrics.rb +147 -0
- data/lib/swarm_sdk/context_compactor/token_counter.rb +106 -0
- data/lib/swarm_sdk/context_compactor.rb +340 -0
- data/lib/swarm_sdk/hooks/adapter.rb +359 -0
- data/lib/swarm_sdk/hooks/context.rb +197 -0
- data/lib/swarm_sdk/hooks/definition.rb +80 -0
- data/lib/swarm_sdk/hooks/error.rb +29 -0
- data/lib/swarm_sdk/hooks/executor.rb +146 -0
- data/lib/swarm_sdk/hooks/registry.rb +147 -0
- data/lib/swarm_sdk/hooks/result.rb +150 -0
- data/lib/swarm_sdk/hooks/shell_executor.rb +254 -0
- data/lib/swarm_sdk/hooks/tool_call.rb +35 -0
- data/lib/swarm_sdk/hooks/tool_result.rb +62 -0
- data/lib/swarm_sdk/log_collector.rb +51 -0
- data/lib/swarm_sdk/log_stream.rb +69 -0
- data/lib/swarm_sdk/markdown_parser.rb +75 -0
- data/lib/swarm_sdk/model_aliases.json +5 -0
- data/lib/swarm_sdk/models.json +1 -0
- data/lib/swarm_sdk/models.rb +120 -0
- data/lib/swarm_sdk/node/agent_config.rb +49 -0
- data/lib/swarm_sdk/node/builder.rb +439 -0
- data/lib/swarm_sdk/node/transformer_executor.rb +248 -0
- data/lib/swarm_sdk/node_context.rb +170 -0
- data/lib/swarm_sdk/node_orchestrator.rb +384 -0
- data/lib/swarm_sdk/permissions/config.rb +239 -0
- data/lib/swarm_sdk/permissions/error_formatter.rb +121 -0
- data/lib/swarm_sdk/permissions/path_matcher.rb +35 -0
- data/lib/swarm_sdk/permissions/validator.rb +173 -0
- data/lib/swarm_sdk/permissions_builder.rb +122 -0
- data/lib/swarm_sdk/plugin.rb +147 -0
- data/lib/swarm_sdk/plugin_registry.rb +101 -0
- data/lib/swarm_sdk/prompts/base_system_prompt.md.erb +243 -0
- data/lib/swarm_sdk/providers/openai_with_responses.rb +582 -0
- data/lib/swarm_sdk/result.rb +97 -0
- data/lib/swarm_sdk/swarm/agent_initializer.rb +334 -0
- data/lib/swarm_sdk/swarm/all_agents_builder.rb +140 -0
- data/lib/swarm_sdk/swarm/builder.rb +586 -0
- data/lib/swarm_sdk/swarm/mcp_configurator.rb +151 -0
- data/lib/swarm_sdk/swarm/tool_configurator.rb +419 -0
- data/lib/swarm_sdk/swarm.rb +982 -0
- data/lib/swarm_sdk/tools/bash.rb +274 -0
- data/lib/swarm_sdk/tools/clock.rb +44 -0
- data/lib/swarm_sdk/tools/delegate.rb +164 -0
- data/lib/swarm_sdk/tools/document_converters/base_converter.rb +83 -0
- data/lib/swarm_sdk/tools/document_converters/docx_converter.rb +99 -0
- data/lib/swarm_sdk/tools/document_converters/html_converter.rb +101 -0
- data/lib/swarm_sdk/tools/document_converters/pdf_converter.rb +78 -0
- data/lib/swarm_sdk/tools/document_converters/xlsx_converter.rb +194 -0
- data/lib/swarm_sdk/tools/edit.rb +150 -0
- data/lib/swarm_sdk/tools/glob.rb +158 -0
- data/lib/swarm_sdk/tools/grep.rb +228 -0
- data/lib/swarm_sdk/tools/image_extractors/docx_image_extractor.rb +43 -0
- data/lib/swarm_sdk/tools/image_extractors/pdf_image_extractor.rb +163 -0
- data/lib/swarm_sdk/tools/image_formats/tiff_builder.rb +65 -0
- data/lib/swarm_sdk/tools/multi_edit.rb +232 -0
- data/lib/swarm_sdk/tools/path_resolver.rb +43 -0
- data/lib/swarm_sdk/tools/read.rb +251 -0
- data/lib/swarm_sdk/tools/registry.rb +93 -0
- data/lib/swarm_sdk/tools/scratchpad/scratchpad_list.rb +96 -0
- data/lib/swarm_sdk/tools/scratchpad/scratchpad_read.rb +76 -0
- data/lib/swarm_sdk/tools/scratchpad/scratchpad_write.rb +91 -0
- data/lib/swarm_sdk/tools/stores/read_tracker.rb +61 -0
- data/lib/swarm_sdk/tools/stores/scratchpad_storage.rb +224 -0
- data/lib/swarm_sdk/tools/stores/storage.rb +148 -0
- data/lib/swarm_sdk/tools/stores/todo_manager.rb +65 -0
- data/lib/swarm_sdk/tools/think.rb +95 -0
- data/lib/swarm_sdk/tools/todo_write.rb +216 -0
- data/lib/swarm_sdk/tools/web_fetch.rb +261 -0
- data/lib/swarm_sdk/tools/write.rb +117 -0
- data/lib/swarm_sdk/utils.rb +50 -0
- data/lib/swarm_sdk/version.rb +5 -0
- data/lib/swarm_sdk.rb +157 -0
- data/llm.v2.txt +13407 -0
- data/rubocop/cop/security/no_reflection_methods.rb +47 -0
- data/rubocop/cop/security/no_ruby_llm_logger.rb +32 -0
- data/swarm_cli.gemspec +57 -0
- data/swarm_memory.gemspec +28 -0
- data/swarm_sdk.gemspec +41 -0
- data/team.yml +1 -1
- data/team_full.yml +1875 -0
- data/{team_v2.yml → team_sdk.yml} +121 -52
- metadata +249 -6
- data/EXAMPLES.md +0 -164
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SwarmMemory
|
|
4
|
+
module Optimization
|
|
5
|
+
# Analyzes memory health and generates statistics
|
|
6
|
+
#
|
|
7
|
+
# Provides insights about memory state, quality, and organization.
|
|
8
|
+
class Analyzer
|
|
9
|
+
# Initialize analyzer
|
|
10
|
+
#
|
|
11
|
+
# @param adapter [Adapters::Base] Storage adapter
|
|
12
|
+
def initialize(adapter:)
|
|
13
|
+
@adapter = adapter
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Analyze overall memory health
|
|
17
|
+
#
|
|
18
|
+
# @return [Hash] Comprehensive statistics and health metrics
|
|
19
|
+
#
|
|
20
|
+
# @example
|
|
21
|
+
# stats = analyzer.analyze
|
|
22
|
+
# stats[:health_score] # => 75
|
|
23
|
+
# stats[:total_entries] # => 42
|
|
24
|
+
def analyze
|
|
25
|
+
# List entries (handle errors gracefully)
|
|
26
|
+
entries = @adapter.list
|
|
27
|
+
return empty_analysis if entries.empty?
|
|
28
|
+
|
|
29
|
+
stats = {
|
|
30
|
+
total_entries: entries.size,
|
|
31
|
+
total_size: @adapter.total_size,
|
|
32
|
+
by_type: Hash.new(0),
|
|
33
|
+
by_confidence: Hash.new(0),
|
|
34
|
+
with_frontmatter: 0,
|
|
35
|
+
with_embeddings: 0,
|
|
36
|
+
with_tags: 0,
|
|
37
|
+
with_links: 0,
|
|
38
|
+
quality_scores: [],
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
# Analyze each entry
|
|
42
|
+
entries.each do |entry_info|
|
|
43
|
+
analyze_entry(entry_info[:path], stats)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Calculate averages
|
|
47
|
+
stats[:average_quality] = if stats[:quality_scores].empty?
|
|
48
|
+
0
|
|
49
|
+
else
|
|
50
|
+
stats[:quality_scores].sum / stats[:quality_scores].size
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Calculate health score
|
|
54
|
+
stats[:health_score] = calculate_health_score(stats)
|
|
55
|
+
|
|
56
|
+
stats.except(:quality_scores) # Remove internal array
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Generate formatted health report
|
|
60
|
+
#
|
|
61
|
+
# @return [String] Human-readable health report
|
|
62
|
+
def health_report
|
|
63
|
+
stats = analyze
|
|
64
|
+
format_health_report(stats)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
private
|
|
68
|
+
|
|
69
|
+
def empty_analysis
|
|
70
|
+
{
|
|
71
|
+
total_entries: 0,
|
|
72
|
+
total_size: 0,
|
|
73
|
+
by_type: {},
|
|
74
|
+
by_confidence: {},
|
|
75
|
+
with_frontmatter: 0,
|
|
76
|
+
with_embeddings: 0,
|
|
77
|
+
with_tags: 0,
|
|
78
|
+
with_links: 0,
|
|
79
|
+
average_quality: 0,
|
|
80
|
+
health_score: 0,
|
|
81
|
+
message: "Memory is empty. No entries to analyze.",
|
|
82
|
+
}
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def analyze_entry(path, stats)
|
|
86
|
+
entry = @adapter.read_entry(file_path: path)
|
|
87
|
+
|
|
88
|
+
# Get metadata from entry (always has string keys from .yml file)
|
|
89
|
+
metadata = entry.metadata || {}
|
|
90
|
+
|
|
91
|
+
# All keys are strings (guaranteed by FilesystemAdapter stringification)
|
|
92
|
+
type = metadata["type"]
|
|
93
|
+
confidence = metadata["confidence"]
|
|
94
|
+
tags = metadata["tags"] || []
|
|
95
|
+
related = metadata["related"] || []
|
|
96
|
+
|
|
97
|
+
# Count by type
|
|
98
|
+
if type
|
|
99
|
+
stats[:by_type][type] += 1
|
|
100
|
+
stats[:with_frontmatter] += 1 # Has metadata
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Count by confidence
|
|
104
|
+
stats[:by_confidence][confidence] += 1 if confidence
|
|
105
|
+
|
|
106
|
+
# Count embeddings
|
|
107
|
+
stats[:with_embeddings] += 1 if entry.embedded?
|
|
108
|
+
|
|
109
|
+
# Count tags and links
|
|
110
|
+
stats[:with_tags] += 1 unless tags.empty?
|
|
111
|
+
stats[:with_links] += 1 unless related.empty?
|
|
112
|
+
|
|
113
|
+
# Track quality scores (calculate from metadata, not content)
|
|
114
|
+
quality = calculate_quality_from_metadata(metadata)
|
|
115
|
+
stats[:quality_scores] << quality
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def calculate_health_score(stats)
|
|
119
|
+
total = stats[:total_entries]
|
|
120
|
+
return 0 if total.zero?
|
|
121
|
+
|
|
122
|
+
score = 0
|
|
123
|
+
|
|
124
|
+
# Frontmatter coverage (30 points)
|
|
125
|
+
frontmatter_pct = (stats[:with_frontmatter].to_f / total * 100).round
|
|
126
|
+
score += 30 if frontmatter_pct > 80
|
|
127
|
+
score += 20 if frontmatter_pct > 50 && frontmatter_pct <= 80
|
|
128
|
+
score += 10 if frontmatter_pct > 20 && frontmatter_pct <= 50
|
|
129
|
+
|
|
130
|
+
# Tags coverage (20 points)
|
|
131
|
+
tags_pct = (stats[:with_tags].to_f / total * 100).round
|
|
132
|
+
score += 20 if tags_pct > 60
|
|
133
|
+
score += 10 if tags_pct > 30 && tags_pct <= 60
|
|
134
|
+
|
|
135
|
+
# Links coverage (20 points)
|
|
136
|
+
links_pct = (stats[:with_links].to_f / total * 100).round
|
|
137
|
+
score += 20 if links_pct > 40
|
|
138
|
+
score += 10 if links_pct > 20 && links_pct <= 40
|
|
139
|
+
|
|
140
|
+
# Embedding coverage (15 points)
|
|
141
|
+
embedding_pct = (stats[:with_embeddings].to_f / total * 100).round
|
|
142
|
+
score += 15 if embedding_pct > 80
|
|
143
|
+
score += 8 if embedding_pct > 50 && embedding_pct <= 80
|
|
144
|
+
|
|
145
|
+
# High confidence ratio (15 points)
|
|
146
|
+
high_confidence = stats[:by_confidence]["high"] || 0
|
|
147
|
+
high_confidence_pct = (high_confidence.to_f / total * 100).round
|
|
148
|
+
score += 15 if high_confidence_pct > 50
|
|
149
|
+
score += 8 if high_confidence_pct > 25 && high_confidence_pct <= 50
|
|
150
|
+
|
|
151
|
+
score
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def format_health_report(stats)
|
|
155
|
+
report = []
|
|
156
|
+
report << "# Memory Health Report"
|
|
157
|
+
report << ""
|
|
158
|
+
report << "## Overview"
|
|
159
|
+
report << "- Total entries: #{stats[:total_entries]}"
|
|
160
|
+
report << "- Total size: #{format_bytes(stats[:total_size])}"
|
|
161
|
+
report << "- Entries with frontmatter: #{stats[:with_frontmatter]} (#{percentage(stats[:with_frontmatter], stats[:total_entries])}%)"
|
|
162
|
+
report << "- Entries with embeddings: #{stats[:with_embeddings]} (#{percentage(stats[:with_embeddings], stats[:total_entries])}%)"
|
|
163
|
+
report << "- Entries with tags: #{stats[:with_tags]} (#{percentage(stats[:with_tags], stats[:total_entries])}%)"
|
|
164
|
+
report << "- Entries with related links: #{stats[:with_links]} (#{percentage(stats[:with_links], stats[:total_entries])}%)"
|
|
165
|
+
report << "- Average quality score: #{stats[:average_quality]}/100"
|
|
166
|
+
report << ""
|
|
167
|
+
|
|
168
|
+
unless stats[:by_type].empty?
|
|
169
|
+
report << "## By Type"
|
|
170
|
+
stats[:by_type].sort_by { |_, count| -count }.each do |type, count|
|
|
171
|
+
report << "- #{type}: #{count} (#{percentage(count, stats[:total_entries])}%)"
|
|
172
|
+
end
|
|
173
|
+
report << ""
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
unless stats[:by_confidence].empty?
|
|
177
|
+
report << "## By Confidence"
|
|
178
|
+
confidence_order = { "high" => 0, "medium" => 1, "low" => 2 }
|
|
179
|
+
stats[:by_confidence].sort_by { |k, _| confidence_order[k] || 999 }.each do |conf, count|
|
|
180
|
+
report << "- #{conf}: #{count} (#{percentage(count, stats[:total_entries])}%)"
|
|
181
|
+
end
|
|
182
|
+
report << ""
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
report << "## Health Score: #{stats[:health_score]}/100"
|
|
186
|
+
report << health_score_interpretation(stats[:health_score])
|
|
187
|
+
|
|
188
|
+
report.join("\n")
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def percentage(part, total)
|
|
192
|
+
return 0 if total.zero?
|
|
193
|
+
|
|
194
|
+
((part.to_f / total) * 100).round
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def health_score_interpretation(score)
|
|
198
|
+
case score
|
|
199
|
+
when 80..100
|
|
200
|
+
"Excellent - Memory is well-organized and high-quality"
|
|
201
|
+
when 60..79
|
|
202
|
+
"Good - Memory is decent but could use some improvements"
|
|
203
|
+
when 40..59
|
|
204
|
+
"Fair - Consider running defrag to improve organization"
|
|
205
|
+
when 20..39
|
|
206
|
+
"Poor - Memory needs significant cleanup and reorganization"
|
|
207
|
+
else
|
|
208
|
+
"Critical - Memory is poorly organized and needs immediate attention"
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def format_bytes(bytes)
|
|
213
|
+
if bytes >= 1_000_000
|
|
214
|
+
"#{(bytes.to_f / 1_000_000).round(1)}MB"
|
|
215
|
+
elsif bytes >= 1_000
|
|
216
|
+
"#{(bytes.to_f / 1_000).round(1)}KB"
|
|
217
|
+
else
|
|
218
|
+
"#{bytes}B"
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
# Calculate quality score from metadata (not content parsing)
|
|
223
|
+
#
|
|
224
|
+
# @param metadata [Hash] Metadata hash from .yml file (string keys)
|
|
225
|
+
# @return [Integer] Quality score 0-100
|
|
226
|
+
def calculate_quality_from_metadata(metadata)
|
|
227
|
+
return 0 if metadata.nil? || metadata.empty?
|
|
228
|
+
|
|
229
|
+
score = 0
|
|
230
|
+
|
|
231
|
+
# All keys are strings (no defensive || checks needed)
|
|
232
|
+
score += 20 if metadata["type"]
|
|
233
|
+
score += 20 if metadata["confidence"]
|
|
234
|
+
score += 15 unless (metadata["tags"] || []).empty?
|
|
235
|
+
score += 15 unless (metadata["related"] || []).empty?
|
|
236
|
+
score += 10 if metadata["domain"]
|
|
237
|
+
score += 10 if metadata["last_verified"]
|
|
238
|
+
score += 10 if metadata["confidence"] == "high"
|
|
239
|
+
|
|
240
|
+
score
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
end
|