htm 0.0.20 → 0.0.31

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.
Files changed (153) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +60 -0
  3. data/Rakefile +104 -18
  4. data/db/migrate/00001_enable_extensions.rb +9 -5
  5. data/db/migrate/00002_create_robots.rb +18 -6
  6. data/db/migrate/00003_create_file_sources.rb +30 -17
  7. data/db/migrate/00004_create_nodes.rb +60 -48
  8. data/db/migrate/00005_create_tags.rb +24 -12
  9. data/db/migrate/00006_create_node_tags.rb +28 -13
  10. data/db/migrate/00007_create_robot_nodes.rb +40 -26
  11. data/db/schema.sql +17 -1
  12. data/db/seeds.rb +33 -33
  13. data/docs/database/naming-convention.md +244 -0
  14. data/docs/database_rake_tasks.md +31 -0
  15. data/docs/development/rake-tasks.md +80 -35
  16. data/docs/guides/mcp-server.md +70 -1
  17. data/examples/.envrc +6 -0
  18. data/examples/.gitignore +2 -0
  19. data/examples/00_create_examples_db.rb +94 -0
  20. data/examples/{basic_usage.rb → 01_basic_usage.rb} +12 -16
  21. data/examples/{custom_llm_configuration.rb → 03_custom_llm_configuration.rb} +13 -3
  22. data/examples/{file_loader_usage.rb → 04_file_loader_usage.rb} +11 -14
  23. data/examples/{timeframe_demo.rb → 05_timeframe_demo.rb} +10 -3
  24. data/examples/{example_app → 06_example_app}/app.rb +15 -15
  25. data/examples/{cli_app → 07_cli_app}/htm_cli.rb +15 -22
  26. data/examples/08_sinatra_app/Gemfile.lock +241 -0
  27. data/examples/{sinatra_app → 08_sinatra_app}/app.rb +19 -18
  28. data/examples/{mcp_client.rb → 09_mcp_client.rb} +5 -8
  29. data/examples/{telemetry → 10_telemetry}/SETUP_README.md +1 -1
  30. data/examples/{telemetry → 10_telemetry}/demo.rb +14 -10
  31. data/examples/11_robot_groups/README.md +335 -0
  32. data/examples/{robot_groups → 11_robot_groups/lib}/robot_worker.rb +17 -3
  33. data/examples/{robot_groups → 11_robot_groups}/multi_process.rb +9 -9
  34. data/examples/{robot_groups → 11_robot_groups}/same_process.rb +9 -12
  35. data/examples/{rails_app → 12_rails_app}/Gemfile +3 -0
  36. data/examples/{rails_app → 12_rails_app}/Gemfile.lock +87 -58
  37. data/examples/{rails_app → 12_rails_app}/app/controllers/dashboard_controller.rb +10 -6
  38. data/examples/{rails_app → 12_rails_app}/app/controllers/files_controller.rb +5 -5
  39. data/examples/{rails_app → 12_rails_app}/app/controllers/memories_controller.rb +11 -7
  40. data/examples/{rails_app → 12_rails_app}/app/controllers/robots_controller.rb +8 -8
  41. data/examples/12_rails_app/app/controllers/tags_controller.rb +36 -0
  42. data/examples/{rails_app → 12_rails_app}/app/views/dashboard/index.html.erb +2 -2
  43. data/examples/{rails_app → 12_rails_app}/app/views/files/new.html.erb +5 -2
  44. data/examples/{rails_app → 12_rails_app}/app/views/memories/_memory_card.html.erb +3 -3
  45. data/examples/{rails_app → 12_rails_app}/app/views/memories/deleted.html.erb +3 -3
  46. data/examples/{rails_app → 12_rails_app}/app/views/memories/edit.html.erb +3 -3
  47. data/examples/{rails_app → 12_rails_app}/app/views/memories/show.html.erb +4 -4
  48. data/examples/{rails_app → 12_rails_app}/app/views/robots/index.html.erb +2 -2
  49. data/examples/{rails_app → 12_rails_app}/app/views/robots/show.html.erb +4 -4
  50. data/examples/{rails_app → 12_rails_app}/app/views/search/index.html.erb +1 -1
  51. data/examples/{rails_app → 12_rails_app}/app/views/tags/index.html.erb +2 -2
  52. data/examples/{rails_app → 12_rails_app}/app/views/tags/show.html.erb +1 -1
  53. data/examples/12_rails_app/config/initializers/htm.rb +7 -0
  54. data/examples/12_rails_app/config/initializers/rack.rb +5 -0
  55. data/examples/README.md +230 -211
  56. data/examples/examples_helper.rb +138 -0
  57. data/lib/htm/config/builder.rb +167 -0
  58. data/lib/htm/config/database.rb +317 -0
  59. data/lib/htm/config/defaults.yml +62 -22
  60. data/lib/htm/config/validator.rb +83 -0
  61. data/lib/htm/config.rb +75 -462
  62. data/lib/htm/database.rb +85 -127
  63. data/lib/htm/errors.rb +14 -0
  64. data/lib/htm/integrations/sinatra.rb +13 -44
  65. data/lib/htm/jobs/generate_embedding_job.rb +3 -4
  66. data/lib/htm/jobs/generate_propositions_job.rb +4 -5
  67. data/lib/htm/jobs/generate_tags_job.rb +16 -15
  68. data/lib/htm/loaders/markdown_loader.rb +17 -15
  69. data/lib/htm/long_term_memory/fulltext_search.rb +14 -14
  70. data/lib/htm/long_term_memory/hybrid_search.rb +396 -229
  71. data/lib/htm/long_term_memory/node_operations.rb +24 -23
  72. data/lib/htm/long_term_memory/relevance_scorer.rb +23 -20
  73. data/lib/htm/long_term_memory/robot_operations.rb +4 -4
  74. data/lib/htm/long_term_memory/tag_operations.rb +91 -77
  75. data/lib/htm/long_term_memory/vector_search.rb +4 -5
  76. data/lib/htm/long_term_memory.rb +13 -13
  77. data/lib/htm/mcp/cli.rb +115 -8
  78. data/lib/htm/mcp/resources.rb +4 -3
  79. data/lib/htm/mcp/server.rb +5 -4
  80. data/lib/htm/mcp/tools.rb +37 -28
  81. data/lib/htm/migration.rb +72 -0
  82. data/lib/htm/models/file_source.rb +52 -31
  83. data/lib/htm/models/node.rb +224 -108
  84. data/lib/htm/models/node_tag.rb +49 -28
  85. data/lib/htm/models/robot.rb +38 -27
  86. data/lib/htm/models/robot_node.rb +63 -35
  87. data/lib/htm/models/tag.rb +126 -123
  88. data/lib/htm/observability.rb +45 -41
  89. data/lib/htm/proposition_service.rb +76 -7
  90. data/lib/htm/railtie.rb +2 -2
  91. data/lib/htm/robot_group.rb +30 -18
  92. data/lib/htm/sequel_config.rb +215 -0
  93. data/lib/htm/sql_builder.rb +14 -16
  94. data/lib/htm/tag_service.rb +78 -0
  95. data/lib/htm/tasks.rb +3 -0
  96. data/lib/htm/version.rb +1 -1
  97. data/lib/htm/workflows/remember_workflow.rb +6 -5
  98. data/lib/htm.rb +26 -22
  99. data/lib/tasks/db.rake +0 -2
  100. data/lib/tasks/doc.rake +2 -2
  101. data/lib/tasks/files.rake +11 -18
  102. data/lib/tasks/htm.rake +190 -62
  103. data/lib/tasks/jobs.rake +179 -54
  104. data/lib/tasks/tags.rake +8 -13
  105. data/scripts/backfill_parent_tags.rb +376 -0
  106. data/scripts/normalize_plural_tags.rb +335 -0
  107. metadata +111 -85
  108. data/examples/rails_app/app/controllers/tags_controller.rb +0 -30
  109. data/examples/sinatra_app/Gemfile.lock +0 -166
  110. data/lib/htm/active_record_config.rb +0 -104
  111. data/lib/htm/loaders/defaults_loader.rb +0 -143
  112. data/lib/htm/loaders/xdg_config_loader.rb +0 -116
  113. /data/examples/{config_file_example → 02_config_file_example}/README.md +0 -0
  114. /data/examples/{config_file_example → 02_config_file_example}/config/htm.local.yml +0 -0
  115. /data/examples/{config_file_example → 02_config_file_example}/custom_config.yml +0 -0
  116. /data/examples/{config_file_example → 02_config_file_example}/show_config.rb +0 -0
  117. /data/examples/{example_app → 06_example_app}/Rakefile +0 -0
  118. /data/examples/{cli_app → 07_cli_app}/README.md +0 -0
  119. /data/examples/{sinatra_app → 08_sinatra_app}/Gemfile +0 -0
  120. /data/examples/{telemetry → 10_telemetry}/README.md +0 -0
  121. /data/examples/{telemetry → 10_telemetry}/grafana/dashboards/htm-metrics.json +0 -0
  122. /data/examples/{rails_app → 12_rails_app}/.gitignore +0 -0
  123. /data/examples/{rails_app → 12_rails_app}/Procfile.dev +0 -0
  124. /data/examples/{rails_app → 12_rails_app}/README.md +0 -0
  125. /data/examples/{rails_app → 12_rails_app}/Rakefile +0 -0
  126. /data/examples/{rails_app → 12_rails_app}/app/assets/stylesheets/application.css +0 -0
  127. /data/examples/{rails_app → 12_rails_app}/app/assets/stylesheets/inter-font.css +0 -0
  128. /data/examples/{rails_app → 12_rails_app}/app/controllers/application_controller.rb +0 -0
  129. /data/examples/{rails_app → 12_rails_app}/app/controllers/search_controller.rb +0 -0
  130. /data/examples/{rails_app → 12_rails_app}/app/javascript/application.js +0 -0
  131. /data/examples/{rails_app → 12_rails_app}/app/javascript/controllers/application.js +0 -0
  132. /data/examples/{rails_app → 12_rails_app}/app/javascript/controllers/index.js +0 -0
  133. /data/examples/{rails_app → 12_rails_app}/app/views/files/index.html.erb +0 -0
  134. /data/examples/{rails_app → 12_rails_app}/app/views/files/show.html.erb +0 -0
  135. /data/examples/{rails_app → 12_rails_app}/app/views/layouts/application.html.erb +0 -0
  136. /data/examples/{rails_app → 12_rails_app}/app/views/memories/index.html.erb +0 -0
  137. /data/examples/{rails_app → 12_rails_app}/app/views/memories/new.html.erb +0 -0
  138. /data/examples/{rails_app → 12_rails_app}/app/views/robots/new.html.erb +0 -0
  139. /data/examples/{rails_app → 12_rails_app}/app/views/shared/_navbar.html.erb +0 -0
  140. /data/examples/{rails_app → 12_rails_app}/app/views/shared/_stat_card.html.erb +0 -0
  141. /data/examples/{rails_app → 12_rails_app}/bin/dev +0 -0
  142. /data/examples/{rails_app → 12_rails_app}/bin/rails +0 -0
  143. /data/examples/{rails_app → 12_rails_app}/bin/rake +0 -0
  144. /data/examples/{rails_app → 12_rails_app}/config/application.rb +0 -0
  145. /data/examples/{rails_app → 12_rails_app}/config/boot.rb +0 -0
  146. /data/examples/{rails_app → 12_rails_app}/config/database.yml +0 -0
  147. /data/examples/{rails_app → 12_rails_app}/config/environment.rb +0 -0
  148. /data/examples/{rails_app → 12_rails_app}/config/importmap.rb +0 -0
  149. /data/examples/{rails_app → 12_rails_app}/config/routes.rb +0 -0
  150. /data/examples/{rails_app → 12_rails_app}/config/tailwind.config.js +0 -0
  151. /data/examples/{rails_app → 12_rails_app}/config.ru +0 -0
  152. /data/examples/{rails_app → 12_rails_app}/log/.keep +0 -0
  153. /data/examples/{rails_app → 12_rails_app}/tmp/local_secret.txt +0 -0
@@ -1,143 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'anyway_config'
4
- require 'yaml'
5
-
6
- class HTM
7
- module Loaders
8
- # Bundled Defaults Loader for Anyway Config
9
- #
10
- # Loads default configuration values from a YAML file bundled with the gem.
11
- # This ensures defaults are always available regardless of where HTM is installed.
12
- #
13
- # The defaults.yml file has this structure:
14
- # defaults: # Base values for all environments
15
- # database:
16
- # host: localhost
17
- # port: 5432
18
- # development: # Overrides for development
19
- # database:
20
- # name: htm_development
21
- # test: # Overrides for test
22
- # database:
23
- # name: htm_test
24
- # production: # Overrides for production
25
- # database:
26
- # sslmode: require
27
- #
28
- # This loader deep-merges `defaults` with the current environment's overrides.
29
- #
30
- # This loader runs at LOWEST priority (before XDG), so all other sources
31
- # can override these bundled defaults:
32
- # 1. Bundled defaults (this loader)
33
- # 2. XDG user config (~/.config/htm/htm.yml)
34
- # 3. Project config (./config/htm.yml)
35
- # 4. Local overrides (./config/htm.local.yml)
36
- # 5. Environment variables (HTM_*)
37
- # 6. Programmatic (configure block)
38
- #
39
- class DefaultsLoader < Anyway::Loaders::Base
40
- DEFAULTS_PATH = File.expand_path('../config/defaults.yml', __dir__).freeze
41
-
42
- class << self
43
- # Returns the path to the bundled defaults file
44
- #
45
- # @return [String] path to defaults.yml
46
- def defaults_path
47
- DEFAULTS_PATH
48
- end
49
-
50
- # Check if defaults file exists
51
- #
52
- # @return [Boolean]
53
- def defaults_exist?
54
- File.exist?(DEFAULTS_PATH)
55
- end
56
-
57
- # Load and parse the raw YAML content
58
- #
59
- # @return [Hash] parsed YAML with symbolized keys
60
- def load_raw_yaml
61
- return {} unless defaults_exist?
62
-
63
- content = File.read(defaults_path)
64
- YAML.safe_load(
65
- content,
66
- permitted_classes: [Symbol],
67
- symbolize_names: true,
68
- aliases: true
69
- ) || {}
70
- rescue Psych::SyntaxError => e
71
- warn "HTM: Failed to parse bundled defaults #{defaults_path}: #{e.message}"
72
- {}
73
- end
74
-
75
- # Extract the schema (attribute names) from the defaults section
76
- #
77
- # @return [Hash] the defaults section containing all attribute definitions
78
- def schema
79
- raw = load_raw_yaml
80
- raw[:defaults] || {}
81
- end
82
- end
83
-
84
- def call(name:, **_options)
85
- return {} unless self.class.defaults_exist?
86
-
87
- trace!(:bundled_defaults, path: self.class.defaults_path) do
88
- load_and_merge_for_environment
89
- end
90
- end
91
-
92
- private
93
-
94
- # Load defaults and deep merge with environment-specific overrides
95
- #
96
- # @return [Hash] merged configuration for current environment
97
- def load_and_merge_for_environment
98
- raw = self.class.load_raw_yaml
99
- return {} if raw.empty?
100
-
101
- # Start with the defaults section
102
- defaults = raw[:defaults] || {}
103
-
104
- # Deep merge with environment-specific overrides
105
- env = current_environment
106
- env_overrides = raw[env.to_sym] || {}
107
-
108
- deep_merge(defaults, env_overrides)
109
- end
110
-
111
- # Deep merge two hashes, with overlay taking precedence
112
- #
113
- # @param base [Hash] base configuration
114
- # @param overlay [Hash] overlay configuration (takes precedence)
115
- # @return [Hash] merged result
116
- def deep_merge(base, overlay)
117
- base.merge(overlay) do |_key, old_val, new_val|
118
- if old_val.is_a?(Hash) && new_val.is_a?(Hash)
119
- deep_merge(old_val, new_val)
120
- else
121
- new_val
122
- end
123
- end
124
- end
125
-
126
- # Determine the current environment
127
- #
128
- # Priority: HTM_ENV > RAILS_ENV > RACK_ENV > 'development'
129
- #
130
- # @return [String] current environment name
131
- def current_environment
132
- ENV['HTM_ENV'] || ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'development'
133
- end
134
- end
135
- end
136
- end
137
-
138
- # Register the defaults loader at LOWEST priority (before :yml loader)
139
- # This ensures bundled defaults are overridden by all other sources:
140
- # - XDG user config (registered after this, also before :yml)
141
- # - Project config (:yml loader)
142
- # - Environment variables (:env loader)
143
- Anyway.loaders.insert_before :yml, :bundled_defaults, HTM::Loaders::DefaultsLoader
@@ -1,116 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'anyway_config'
4
- require 'yaml'
5
-
6
- class HTM
7
- module Loaders
8
- # XDG Base Directory Specification loader for Anyway Config
9
- #
10
- # Loads configuration from XDG-compliant paths:
11
- # 1. $XDG_CONFIG_HOME/htm/htm.yml (if XDG_CONFIG_HOME is set)
12
- # 2. ~/.config/htm/htm.yml (XDG default fallback)
13
- #
14
- # On macOS, also checks:
15
- # 3. ~/Library/Application Support/htm/htm.yml
16
- #
17
- # This loader runs BEFORE the project-local config loader,
18
- # so project configs take precedence over user-global configs.
19
- #
20
- # @example XDG config file location
21
- # ~/.config/htm/htm.yml
22
- #
23
- # @example Custom XDG_CONFIG_HOME
24
- # export XDG_CONFIG_HOME=/my/config
25
- # # Looks for /my/config/htm/htm.yml
26
- #
27
- class XdgConfigLoader < Anyway::Loaders::Base
28
- class << self
29
- # Returns all XDG config paths to check, in order of priority (lowest first)
30
- #
31
- # @return [Array<String>] list of potential config file paths
32
- def config_paths
33
- paths = []
34
-
35
- # macOS Application Support (lowest priority for XDG loader)
36
- if macos?
37
- macos_path = File.expand_path('~/Library/Application Support/htm')
38
- paths << macos_path if Dir.exist?(File.dirname(macos_path))
39
- end
40
-
41
- # XDG default: ~/.config/htm
42
- xdg_default = File.expand_path('~/.config/htm')
43
- paths << xdg_default
44
-
45
- # XDG_CONFIG_HOME override (highest priority for XDG loader)
46
- if ENV['XDG_CONFIG_HOME'] && !ENV['XDG_CONFIG_HOME'].empty?
47
- xdg_home = File.join(ENV['XDG_CONFIG_HOME'], 'htm')
48
- paths << xdg_home unless xdg_home == xdg_default
49
- end
50
-
51
- paths
52
- end
53
-
54
- # Find the first existing config file
55
- #
56
- # @param name [String] config name (e.g., 'htm')
57
- # @return [String, nil] path to config file or nil if not found
58
- def find_config_file(name)
59
- config_paths.reverse_each do |dir|
60
- file = File.join(dir, "#{name}.yml")
61
- return file if File.exist?(file)
62
- end
63
- nil
64
- end
65
-
66
- private
67
-
68
- def macos?
69
- RUBY_PLATFORM.include?('darwin')
70
- end
71
- end
72
-
73
- def call(name:, **_options)
74
- config_file = self.class.find_config_file(name)
75
- return {} unless config_file
76
-
77
- trace!(:xdg, path: config_file) do
78
- load_yaml(config_file, name)
79
- end
80
- end
81
-
82
- private
83
-
84
- def load_yaml(path, name)
85
- return {} unless File.exist?(path)
86
-
87
- content = File.read(path)
88
- parsed = YAML.safe_load(content, permitted_classes: [Symbol], symbolize_names: true, aliases: true) || {}
89
-
90
- # Support environment-specific configs
91
- env = Anyway::Settings.current_environment ||
92
- ENV['HTM_ENV'] ||
93
- ENV['RAILS_ENV'] ||
94
- ENV['RACK_ENV'] ||
95
- 'development'
96
-
97
- # Check for environment key first, fall back to root level
98
- if parsed.key?(env.to_sym)
99
- parsed[env.to_sym] || {}
100
- elsif parsed.key?(env.to_s)
101
- parsed[env.to_s] || {}
102
- else
103
- # No environment key, treat as flat config
104
- parsed
105
- end
106
- rescue Psych::SyntaxError => e
107
- warn "HTM: Failed to parse XDG config #{path}: #{e.message}"
108
- {}
109
- end
110
- end
111
- end
112
- end
113
-
114
- # Register the XDG loader with Anyway Config
115
- # Insert before :yml so project-local config takes precedence
116
- Anyway.loaders.insert_before :yml, :xdg, HTM::Loaders::XdgConfigLoader
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes