htm 0.0.15 → 0.0.18

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 (135) hide show
  1. checksums.yaml +4 -4
  2. data/.architecture/decisions/adrs/001-use-postgresql-timescaledb-storage.md +1 -1
  3. data/.architecture/decisions/adrs/011-database-side-embedding-generation-with-pgai.md +4 -4
  4. data/.architecture/decisions/adrs/012-llm-driven-ontology-topic-extraction.md +1 -1
  5. data/.envrc +12 -24
  6. data/.irbrc +7 -7
  7. data/.tbls.yml +2 -2
  8. data/CHANGELOG.md +138 -0
  9. data/README.md +97 -1592
  10. data/Rakefile +8 -3
  11. data/SETUP.md +12 -12
  12. data/bin/htm_mcp +27 -0
  13. data/db/seed_data/README.md +2 -2
  14. data/db/seeds.rb +2 -2
  15. data/docs/api/database.md +37 -37
  16. data/docs/api/htm.md +1 -1
  17. data/docs/api/yard/HTM/ActiveRecordConfig.md +2 -2
  18. data/docs/api/yard/HTM/Configuration.md +26 -15
  19. data/docs/api/yard/HTM/Database.md +7 -8
  20. data/docs/api/yard/HTM/JobAdapter.md +1 -1
  21. data/docs/api/yard/HTM/Railtie.md +2 -2
  22. data/docs/architecture/adrs/001-postgresql-timescaledb.md +1 -1
  23. data/docs/architecture/adrs/011-pgai-integration.md +4 -4
  24. data/docs/database_rake_tasks.md +5 -5
  25. data/docs/development/rake-tasks.md +11 -11
  26. data/docs/development/setup.md +21 -21
  27. data/docs/development/testing.md +1 -1
  28. data/docs/getting-started/installation.md +51 -31
  29. data/docs/getting-started/quick-start.md +12 -12
  30. data/docs/guides/getting-started.md +2 -2
  31. data/docs/guides/long-term-memory.md +1 -1
  32. data/docs/guides/mcp-server.md +464 -29
  33. data/docs/guides/robot-groups.md +8 -8
  34. data/docs/index.md +4 -4
  35. data/docs/multi_framework_support.md +10 -10
  36. data/docs/setup_local_database.md +19 -19
  37. data/docs/using_rake_tasks_in_your_app.md +14 -14
  38. data/examples/README.md +50 -6
  39. data/examples/basic_usage.rb +31 -21
  40. data/examples/cli_app/README.md +8 -8
  41. data/examples/cli_app/htm_cli.rb +5 -5
  42. data/examples/config_file_example/README.md +256 -0
  43. data/examples/config_file_example/config/htm.local.yml +34 -0
  44. data/examples/config_file_example/custom_config.yml +22 -0
  45. data/examples/config_file_example/show_config.rb +125 -0
  46. data/examples/custom_llm_configuration.rb +7 -7
  47. data/examples/example_app/Rakefile +2 -2
  48. data/examples/example_app/app.rb +8 -8
  49. data/examples/file_loader_usage.rb +9 -9
  50. data/examples/mcp_client.rb +7 -7
  51. data/examples/rails_app/.gitignore +2 -0
  52. data/examples/rails_app/Gemfile +22 -0
  53. data/examples/rails_app/Gemfile.lock +430 -0
  54. data/examples/rails_app/Procfile.dev +1 -0
  55. data/examples/rails_app/README.md +98 -0
  56. data/examples/rails_app/Rakefile +5 -0
  57. data/examples/rails_app/app/assets/stylesheets/application.css +83 -0
  58. data/examples/rails_app/app/assets/stylesheets/inter-font.css +6 -0
  59. data/examples/rails_app/app/controllers/application_controller.rb +19 -0
  60. data/examples/rails_app/app/controllers/dashboard_controller.rb +27 -0
  61. data/examples/rails_app/app/controllers/files_controller.rb +205 -0
  62. data/examples/rails_app/app/controllers/memories_controller.rb +102 -0
  63. data/examples/rails_app/app/controllers/robots_controller.rb +44 -0
  64. data/examples/rails_app/app/controllers/search_controller.rb +46 -0
  65. data/examples/rails_app/app/controllers/tags_controller.rb +30 -0
  66. data/examples/rails_app/app/javascript/application.js +4 -0
  67. data/examples/rails_app/app/javascript/controllers/application.js +9 -0
  68. data/examples/rails_app/app/javascript/controllers/index.js +6 -0
  69. data/examples/rails_app/app/views/dashboard/index.html.erb +123 -0
  70. data/examples/rails_app/app/views/files/index.html.erb +108 -0
  71. data/examples/rails_app/app/views/files/new.html.erb +321 -0
  72. data/examples/rails_app/app/views/files/show.html.erb +130 -0
  73. data/examples/rails_app/app/views/layouts/application.html.erb +124 -0
  74. data/examples/rails_app/app/views/memories/_memory_card.html.erb +51 -0
  75. data/examples/rails_app/app/views/memories/deleted.html.erb +62 -0
  76. data/examples/rails_app/app/views/memories/edit.html.erb +35 -0
  77. data/examples/rails_app/app/views/memories/index.html.erb +81 -0
  78. data/examples/rails_app/app/views/memories/new.html.erb +71 -0
  79. data/examples/rails_app/app/views/memories/show.html.erb +126 -0
  80. data/examples/rails_app/app/views/robots/index.html.erb +106 -0
  81. data/examples/rails_app/app/views/robots/new.html.erb +36 -0
  82. data/examples/rails_app/app/views/robots/show.html.erb +79 -0
  83. data/examples/rails_app/app/views/search/index.html.erb +184 -0
  84. data/examples/rails_app/app/views/shared/_navbar.html.erb +52 -0
  85. data/examples/rails_app/app/views/shared/_stat_card.html.erb +52 -0
  86. data/examples/rails_app/app/views/tags/index.html.erb +131 -0
  87. data/examples/rails_app/app/views/tags/show.html.erb +67 -0
  88. data/examples/rails_app/bin/dev +8 -0
  89. data/examples/rails_app/bin/rails +4 -0
  90. data/examples/rails_app/bin/rake +4 -0
  91. data/examples/rails_app/config/application.rb +33 -0
  92. data/examples/rails_app/config/boot.rb +5 -0
  93. data/examples/rails_app/config/database.yml +15 -0
  94. data/examples/rails_app/config/environment.rb +5 -0
  95. data/examples/rails_app/config/importmap.rb +7 -0
  96. data/examples/rails_app/config/routes.rb +38 -0
  97. data/examples/rails_app/config/tailwind.config.js +35 -0
  98. data/examples/rails_app/config.ru +5 -0
  99. data/examples/rails_app/log/.keep +0 -0
  100. data/examples/rails_app/tmp/local_secret.txt +1 -0
  101. data/examples/robot_groups/multi_process.rb +5 -5
  102. data/examples/robot_groups/robot_worker.rb +5 -5
  103. data/examples/robot_groups/same_process.rb +9 -9
  104. data/examples/sinatra_app/app.rb +1 -1
  105. data/examples/timeframe_demo.rb +1 -1
  106. data/lib/htm/active_record_config.rb +12 -28
  107. data/lib/htm/circuit_breaker.rb +0 -2
  108. data/lib/htm/config/defaults.yml +246 -0
  109. data/lib/htm/config.rb +888 -0
  110. data/lib/htm/database.rb +26 -33
  111. data/lib/htm/embedding_service.rb +0 -4
  112. data/lib/htm/integrations/sinatra.rb +3 -7
  113. data/lib/htm/job_adapter.rb +1 -15
  114. data/lib/htm/jobs/generate_embedding_job.rb +1 -7
  115. data/lib/htm/jobs/generate_propositions_job.rb +2 -12
  116. data/lib/htm/jobs/generate_tags_job.rb +1 -8
  117. data/lib/htm/loaders/defaults_loader.rb +143 -0
  118. data/lib/htm/loaders/xdg_config_loader.rb +116 -0
  119. data/lib/htm/mcp/cli.rb +475 -0
  120. data/lib/htm/mcp/group_tools.rb +476 -0
  121. data/lib/htm/mcp/resources.rb +89 -0
  122. data/lib/htm/mcp/server.rb +98 -0
  123. data/lib/htm/mcp/tools.rb +488 -0
  124. data/lib/htm/models/file_source.rb +5 -3
  125. data/lib/htm/proposition_service.rb +2 -12
  126. data/lib/htm/railtie.rb +3 -8
  127. data/lib/htm/tag_service.rb +1 -8
  128. data/lib/htm/tasks.rb +7 -4
  129. data/lib/htm/version.rb +1 -1
  130. data/lib/htm.rb +124 -5
  131. data/lib/tasks/htm.rake +6 -9
  132. metadata +81 -6
  133. data/bin/htm_mcp.rb +0 -621
  134. data/config/database.yml +0 -74
  135. data/lib/htm/configuration.rb +0 -766
@@ -0,0 +1,131 @@
1
+ <div class="space-y-6">
2
+ <!-- Header -->
3
+ <div class="flex items-center justify-between">
4
+ <div>
5
+ <h1 class="text-2xl font-bold text-white">Tags</h1>
6
+ <p class="mt-1 text-sm text-gray-400">Hierarchical tag organization</p>
7
+ </div>
8
+ <div class="flex items-center gap-2 rounded-lg bg-gray-800 border border-gray-700 p-1">
9
+ <%= link_to tags_path(view: 'list'), class: "rounded px-3 py-1.5 text-sm font-medium #{@view_type == 'list' ? 'bg-indigo-600 text-white' : 'text-gray-400 hover:text-white'}" do %>
10
+ List
11
+ <% end %>
12
+ <%= link_to tags_path(view: 'tree'), class: "rounded px-3 py-1.5 text-sm font-medium #{@view_type == 'tree' ? 'bg-indigo-600 text-white' : 'text-gray-400 hover:text-white'}" do %>
13
+ Tree
14
+ <% end %>
15
+ <%= link_to tags_path(view: 'mermaid'), class: "rounded px-3 py-1.5 text-sm font-medium #{@view_type == 'mermaid' ? 'bg-indigo-600 text-white' : 'text-gray-400 hover:text-white'}" do %>
16
+ Diagram
17
+ <% end %>
18
+ </div>
19
+ </div>
20
+
21
+ <% case @view_type %>
22
+ <% when 'tree' %>
23
+ <!-- Tree View -->
24
+ <div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
25
+ <!-- Text Tree -->
26
+ <div class="rounded-lg bg-gray-800 border border-gray-700 p-6">
27
+ <h2 class="text-lg font-medium text-white mb-4">Text Tree</h2>
28
+ <% if @tree_string.present? %>
29
+ <pre class="text-sm text-gray-300 font-mono overflow-x-auto"><%= @tree_string %></pre>
30
+ <% else %>
31
+ <p class="text-sm text-gray-500">No tags to display.</p>
32
+ <% end %>
33
+ </div>
34
+
35
+ <!-- SVG Visualization -->
36
+ <div class="rounded-lg bg-gray-800 border border-gray-700 p-6">
37
+ <h2 class="text-lg font-medium text-white mb-4">Visual Hierarchy</h2>
38
+ <% if @tree_svg.present? %>
39
+ <div class="overflow-x-auto">
40
+ <%= raw @tree_svg %>
41
+ </div>
42
+ <% else %>
43
+ <p class="text-sm text-gray-500">No tags to display.</p>
44
+ <% end %>
45
+ </div>
46
+ </div>
47
+
48
+ <% when 'mermaid' %>
49
+ <!-- Mermaid Diagram -->
50
+ <div class="rounded-lg bg-gray-800 border border-gray-700 p-6">
51
+ <h2 class="text-lg font-medium text-white mb-4">Mermaid Diagram</h2>
52
+ <% if @tree_mermaid.present? %>
53
+ <div class="rounded-md bg-gray-900 p-4 overflow-x-auto">
54
+ <pre class="text-sm text-gray-300 font-mono"><%= @tree_mermaid %></pre>
55
+ </div>
56
+ <p class="mt-4 text-xs text-gray-500">Copy this code to render in any Mermaid-compatible viewer.</p>
57
+ <% else %>
58
+ <p class="text-sm text-gray-500">No tags to display.</p>
59
+ <% end %>
60
+ </div>
61
+
62
+ <% else %>
63
+ <!-- List View -->
64
+ <div class="rounded-lg bg-gray-800 border border-gray-700 p-4">
65
+ <%= form_with url: tags_path, method: :get, class: 'flex gap-4' do %>
66
+ <div class="flex-1">
67
+ <%= text_field_tag :prefix, params[:prefix], placeholder: 'Filter by prefix (e.g., database)', class: 'w-full rounded-md bg-gray-700 border-gray-600 text-white placeholder-gray-400 text-sm focus:ring-indigo-500 focus:border-indigo-500' %>
68
+ </div>
69
+ <%= submit_tag 'Filter', class: 'rounded-md bg-indigo-600 px-4 py-2 text-sm font-medium text-white hover:bg-indigo-500 cursor-pointer' %>
70
+ <% if params[:prefix].present? %>
71
+ <%= link_to 'Clear', tags_path, class: 'rounded-md bg-gray-700 px-4 py-2 text-sm font-medium text-white hover:bg-gray-600' %>
72
+ <% end %>
73
+ <% end %>
74
+ </div>
75
+
76
+ <% if @tags.any? %>
77
+ <div class="rounded-lg bg-gray-800 border border-gray-700 overflow-hidden">
78
+ <table class="min-w-full divide-y divide-gray-700">
79
+ <thead class="bg-gray-800/50">
80
+ <tr>
81
+ <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-400 uppercase tracking-wider">Tag</th>
82
+ <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-400 uppercase tracking-wider">Hierarchy</th>
83
+ <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-400 uppercase tracking-wider">Memories</th>
84
+ <th scope="col" class="relative px-6 py-3"><span class="sr-only">Actions</span></th>
85
+ </tr>
86
+ </thead>
87
+ <tbody class="divide-y divide-gray-700">
88
+ <% @tags.each do |tag| %>
89
+ <tr class="hover:bg-gray-700/50 transition-colors">
90
+ <td class="px-6 py-4 whitespace-nowrap">
91
+ <%= link_to tag_path(tag), class: 'inline-flex items-center rounded-full bg-indigo-900/50 border border-indigo-500/30 px-3 py-1 text-sm text-indigo-300 hover:bg-indigo-800/50 transition-colors' do %>
92
+ <%= tag.name %>
93
+ <% end %>
94
+ </td>
95
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-400">
96
+ <% parts = tag.name.split(':') %>
97
+ <% parts.each_with_index do |part, i| %>
98
+ <% if i > 0 %><span class="text-gray-600 mx-1">/</span><% end %>
99
+ <span class="<%= i == parts.length - 1 ? 'text-white' : 'text-gray-500' %>"><%= part %></span>
100
+ <% end %>
101
+ </td>
102
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-400">
103
+ <%= tag.node_count %>
104
+ </td>
105
+ <td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
106
+ <%= link_to 'View', tag_path(tag), class: 'text-indigo-400 hover:text-indigo-300' %>
107
+ <span class="mx-2 text-gray-600">|</span>
108
+ <%= link_to 'Filter Memories', memories_path(tag: tag.name), class: 'text-indigo-400 hover:text-indigo-300' %>
109
+ </td>
110
+ </tr>
111
+ <% end %>
112
+ </tbody>
113
+ </table>
114
+ </div>
115
+ <% else %>
116
+ <div class="rounded-lg bg-gray-800 border border-gray-700 p-8 text-center">
117
+ <svg class="mx-auto h-12 w-12 text-gray-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
118
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z" />
119
+ </svg>
120
+ <h3 class="mt-4 text-lg font-medium text-white">No tags found</h3>
121
+ <p class="mt-2 text-sm text-gray-400">
122
+ <% if params[:prefix].present? %>
123
+ No tags match that prefix. <%= link_to 'Clear filter', tags_path, class: 'text-indigo-400 hover:text-indigo-300' %>.
124
+ <% else %>
125
+ Tags are automatically generated when you add memories.
126
+ <% end %>
127
+ </p>
128
+ </div>
129
+ <% end %>
130
+ <% end %>
131
+ </div>
@@ -0,0 +1,67 @@
1
+ <div class="space-y-6">
2
+ <!-- Back link -->
3
+ <div>
4
+ <%= link_to tags_path, class: 'inline-flex items-center gap-1 text-sm text-gray-400 hover:text-white transition-colors' do %>
5
+ <svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
6
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18" />
7
+ </svg>
8
+ Back to Tags
9
+ <% end %>
10
+ </div>
11
+
12
+ <!-- Tag Header -->
13
+ <div class="rounded-lg bg-gray-800 border border-gray-700 p-6">
14
+ <div class="flex items-start justify-between">
15
+ <div>
16
+ <div class="flex items-center gap-3">
17
+ <div class="rounded-full bg-indigo-900/50 border border-indigo-500/30 p-2">
18
+ <svg class="h-5 w-5 text-indigo-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
19
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z" />
20
+ </svg>
21
+ </div>
22
+ <h1 class="text-2xl font-bold text-white"><%= @tag.name %></h1>
23
+ </div>
24
+
25
+ <!-- Hierarchy breadcrumb -->
26
+ <div class="mt-3 flex items-center gap-1 text-sm">
27
+ <% parts = @tag.name.split(':') %>
28
+ <% parts.each_with_index do |part, i| %>
29
+ <% if i > 0 %>
30
+ <svg class="h-4 w-4 text-gray-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
31
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
32
+ </svg>
33
+ <% end %>
34
+ <% prefix = parts[0..i].join(':') %>
35
+ <% matching_tag = HTM::Models::Tag.find_by(name: prefix) %>
36
+ <% if matching_tag %>
37
+ <%= link_to part, tag_path(matching_tag), class: "#{i == parts.length - 1 ? 'text-indigo-400' : 'text-gray-400 hover:text-white'}" %>
38
+ <% else %>
39
+ <span class="text-gray-500"><%= part %></span>
40
+ <% end %>
41
+ <% end %>
42
+ </div>
43
+ </div>
44
+
45
+ <div class="text-right">
46
+ <p class="text-2xl font-bold text-white"><%= @memories.count %></p>
47
+ <p class="text-sm text-gray-400">memories</p>
48
+ </div>
49
+ </div>
50
+ </div>
51
+
52
+ <!-- Memories with this tag -->
53
+ <div>
54
+ <h2 class="text-lg font-medium text-white mb-4">Memories with this tag</h2>
55
+ <% if @memories.any? %>
56
+ <div class="space-y-4">
57
+ <% @memories.each do |memory| %>
58
+ <%= render 'memories/memory_card', memory: memory %>
59
+ <% end %>
60
+ </div>
61
+ <% else %>
62
+ <div class="rounded-lg bg-gray-800 border border-gray-700 p-8 text-center">
63
+ <p class="text-sm text-gray-500">No memories with this tag.</p>
64
+ </div>
65
+ <% end %>
66
+ </div>
67
+ </div>
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+
3
+ if ! gem list foreman -i --silent; then
4
+ echo "Installing foreman..."
5
+ gem install foreman
6
+ fi
7
+
8
+ exec foreman start -f Procfile.dev "$@"
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ APP_PATH = File.expand_path("../config/application", __dir__)
3
+ require_relative "../config/boot"
4
+ require "rails/commands"
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ require_relative "../config/boot"
3
+ require "rake"
4
+ Rake.application.run
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'boot'
4
+
5
+ require 'rails'
6
+ require 'active_model/railtie'
7
+ require 'active_job/railtie'
8
+ require 'active_record/railtie'
9
+ require 'action_controller/railtie'
10
+ require 'action_view/railtie'
11
+
12
+ # Require HTM - this will auto-load the Railtie
13
+ require 'htm'
14
+
15
+ module HtmRailsExample
16
+ class Application < Rails::Application
17
+ config.load_defaults 7.1
18
+
19
+ # Full-stack Rails app (not API-only)
20
+ config.api_only = false
21
+
22
+ # Use inline jobs for simplicity in demo
23
+ config.active_job.queue_adapter = :inline
24
+
25
+ # Generators config
26
+ config.generators do |g|
27
+ g.test_framework nil
28
+ g.stylesheets false
29
+ g.javascripts false
30
+ g.helper false
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
4
+
5
+ require 'bundler/setup'
@@ -0,0 +1,15 @@
1
+ # Rails app database (not used - HTM uses its own database)
2
+ # This is required for Rails to boot but can be sqlite/memory
3
+
4
+ default: &default
5
+ adapter: postgresql
6
+ encoding: unicode
7
+ pool: 5
8
+
9
+ development:
10
+ <<: *default
11
+ database: htm_rails_example_dev
12
+
13
+ test:
14
+ <<: *default
15
+ database: htm_rails_example_test
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'application'
4
+
5
+ Rails.application.initialize!
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ pin "application"
4
+ pin "@hotwired/turbo-rails", to: "turbo.min.js"
5
+ pin "@hotwired/stimulus", to: "stimulus.min.js"
6
+ pin "@hotwired/stimulus-loading", to: "stimulus-loading.js"
7
+ pin_all_from "app/javascript/controllers", under: "controllers"
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ Rails.application.routes.draw do
4
+ root 'dashboard#index'
5
+
6
+ resources :memories do
7
+ collection do
8
+ get :deleted
9
+ end
10
+ member do
11
+ post :restore
12
+ end
13
+ end
14
+
15
+ resources :tags, only: [:index, :show]
16
+
17
+ resources :robots, only: [:index, :show, :new, :create] do
18
+ member do
19
+ post :switch
20
+ end
21
+ end
22
+
23
+ resource :search, only: [:index], controller: 'search' do
24
+ get '/', action: :index
25
+ end
26
+
27
+ resources :files, only: [:index, :show, :new, :create, :destroy] do
28
+ collection do
29
+ post :load_directory
30
+ post :upload
31
+ post :upload_directory
32
+ post :sync_all
33
+ end
34
+ member do
35
+ post :sync
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,35 @@
1
+ const defaultTheme = require('tailwindcss/defaultTheme')
2
+
3
+ module.exports = {
4
+ darkMode: 'class',
5
+ content: [
6
+ './app/views/**/*.html.erb',
7
+ './app/helpers/**/*.rb',
8
+ './app/javascript/**/*.js',
9
+ './app/assets/stylesheets/**/*.css',
10
+ ],
11
+ theme: {
12
+ extend: {
13
+ fontFamily: {
14
+ sans: ['Inter', ...defaultTheme.fontFamily.sans],
15
+ mono: ['JetBrains Mono', ...defaultTheme.fontFamily.mono],
16
+ },
17
+ colors: {
18
+ htm: {
19
+ 50: '#f0f9ff',
20
+ 100: '#e0f2fe',
21
+ 200: '#bae6fd',
22
+ 300: '#7dd3fc',
23
+ 400: '#38bdf8',
24
+ 500: '#0ea5e9',
25
+ 600: '#0284c7',
26
+ 700: '#0369a1',
27
+ 800: '#075985',
28
+ 900: '#0c4a6e',
29
+ 950: '#082f49',
30
+ },
31
+ },
32
+ },
33
+ },
34
+ plugins: [],
35
+ }
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'config/environment'
4
+
5
+ run Rails.application
File without changes
@@ -0,0 +1 @@
1
+ 8ec864777deb2e477c24acfa26c5a4721c6d36f04e80b9ecb00d22002a591a12d213fd8e4d2107471e65de7f546f2a1e27317d1ea2c0130dc19e39f36af610a9
@@ -13,7 +13,7 @@
13
13
  # 4. Dynamic scaling by spawning new processes
14
14
  #
15
15
  # Prerequisites:
16
- # 1. Set HTM_DBURL environment variable
16
+ # 1. Set HTM_DATABASE__URL environment variable
17
17
  # 2. Initialize database schema: rake db_setup
18
18
 
19
19
  require_relative '../../lib/htm'
@@ -36,7 +36,7 @@ class RobotProcess
36
36
 
37
37
  # Spawn the worker process
38
38
  @stdin, @stdout, @stderr, @wait_thread = Open3.popen3(
39
- { 'HTM_DBURL' => ENV['HTM_DBURL'] },
39
+ { 'HTM_DATABASE__URL' => ENV['HTM_DATABASE__URL'] },
40
40
  'ruby', WORKER_SCRIPT, name, group_name
41
41
  )
42
42
  @pid = @wait_thread.pid
@@ -119,9 +119,9 @@ def run_demo
119
119
 
120
120
  BANNER
121
121
 
122
- unless ENV['HTM_DBURL']
123
- puts 'ERROR: HTM_DBURL not set.'
124
- puts ' export HTM_DBURL="postgresql://user@localhost:5432/htm_development"'
122
+ unless ENV['HTM_DATABASE__URL']
123
+ puts 'ERROR: HTM_DATABASE__URL not set.'
124
+ puts ' export HTM_DATABASE__URL="postgresql://user@localhost:5432/htm_development"'
125
125
  exit 1
126
126
  end
127
127
 
@@ -37,11 +37,11 @@ end
37
37
 
38
38
  # Configure HTM with logger to stderr (keep stdout clean for JSON)
39
39
  HTM.configure do |config|
40
- config.embedding_provider = :ollama
41
- config.embedding_model = 'nomic-embed-text:latest'
42
- config.embedding_dimensions = 768
43
- config.tag_provider = :ollama
44
- config.tag_model = 'gemma3:latest'
40
+ config.embedding.provider = :ollama
41
+ config.embedding.model = 'nomic-embed-text:latest'
42
+ config.embedding.dimensions = 768
43
+ config.tag.provider = :ollama
44
+ config.tag.model = 'gemma3:latest'
45
45
  config.logger = Logger.new($stderr, level: Logger::WARN)
46
46
  end
47
47
 
@@ -19,7 +19,7 @@
19
19
  # synchronization of in-memory working memory across robots.
20
20
  #
21
21
  # Prerequisites:
22
- # 1. Set HTM_DBURL environment variable
22
+ # 1. Set HTM_DATABASE__URL environment variable
23
23
  # 2. Initialize database schema: rake db_setup
24
24
  # 3. Install dependencies: bundle install
25
25
 
@@ -33,9 +33,9 @@ require 'json'
33
33
  puts 'HTM Robot Group Demo - Shared Working Memory & Failover'
34
34
  puts '=' * 60
35
35
 
36
- unless ENV['HTM_DBURL']
37
- puts 'ERROR: HTM_DBURL not set. Please set it:'
38
- puts ' export HTM_DBURL="postgresql://postgres@localhost:5432/htm_development"'
36
+ unless ENV['HTM_DATABASE__URL']
37
+ puts 'ERROR: HTM_DATABASE__URL not set. Please set it:'
38
+ puts ' export HTM_DATABASE__URL="postgresql://postgres@localhost:5432/htm_development"'
39
39
  exit 1
40
40
  end
41
41
 
@@ -43,11 +43,11 @@ begin
43
43
  # Configure HTM
44
44
  puts "\n1. Configuring HTM..."
45
45
  HTM.configure do |config|
46
- config.embedding_provider = :ollama
47
- config.embedding_model = 'nomic-embed-text:latest'
48
- config.embedding_dimensions = 768
49
- config.tag_provider = :ollama
50
- config.tag_model = 'gemma3:latest'
46
+ config.embedding.provider = :ollama
47
+ config.embedding.model = 'nomic-embed-text:latest'
48
+ config.embedding.dimensions = 768
49
+ config.tag.provider = :ollama
50
+ config.tag.model = 'gemma3:latest'
51
51
  end
52
52
  puts '✓ HTM configured'
53
53
 
@@ -14,7 +14,7 @@
14
14
  # bundle exec ruby app.rb
15
15
  #
16
16
  # Environment:
17
- # HTM_DBURL - PostgreSQL connection URL (required)
17
+ # HTM_DATABASE__URL - PostgreSQL connection URL (required)
18
18
  # REDIS_URL - Redis connection URL (for Sidekiq, default: redis://localhost:6379/0)
19
19
  # OLLAMA_URL - Ollama server URL (default: http://localhost:11434)
20
20
  #
@@ -4,7 +4,7 @@
4
4
  # Timeframe Demo - Demonstrates the various ways to use timeframes with recall
5
5
  #
6
6
  # Run with:
7
- # HTM_DBURL="postgresql://localhost/htm_development" ruby examples/timeframe_demo.rb
7
+ # HTM_DATABASE__URL="postgresql://localhost/htm_development" ruby examples/timeframe_demo.rb
8
8
 
9
9
  require_relative "../lib/htm"
10
10
 
@@ -3,14 +3,17 @@
3
3
  require 'active_record'
4
4
  require 'pg'
5
5
  require 'neighbor'
6
- require 'erb'
7
- require 'yaml'
8
6
 
9
7
  class HTM
10
8
  # ActiveRecord database configuration and model loading
9
+ #
10
+ # Uses HTM::Config for database settings. Configuration can come from:
11
+ # - Environment variables (HTM_DATABASE__URL, HTM_DATABASE__HOST, etc.)
12
+ # - Programmatic configuration via HTM.configure
13
+ #
11
14
  class ActiveRecordConfig
12
15
  class << self
13
- # Establish database connection from config/database.yml
16
+ # Establish database connection from HTM::Config
14
17
  def establish_connection!
15
18
  config = load_database_config
16
19
 
@@ -25,30 +28,12 @@ class HTM
25
28
  true
26
29
  end
27
30
 
28
- # Load and parse database configuration from YAML with ERB
31
+ # Load database configuration from HTM::Config
32
+ #
33
+ # @return [Hash] ActiveRecord-compatible configuration hash
34
+ #
29
35
  def load_database_config
30
- config_path = File.expand_path('../../config/database.yml', __dir__)
31
-
32
- unless File.exist?(config_path)
33
- raise "Database configuration file not found at #{config_path}"
34
- end
35
-
36
- # Read and parse ERB
37
- erb_content = ERB.new(File.read(config_path)).result
38
- db_config = YAML.safe_load(erb_content, aliases: true)
39
-
40
- # Determine environment
41
- env = ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'development'
42
-
43
- # Get configuration for current environment
44
- config = db_config[env]
45
-
46
- unless config
47
- raise "No database configuration found for environment: #{env}"
48
- end
49
-
50
- # Convert string keys to symbols for ActiveRecord
51
- config.transform_keys(&:to_sym)
36
+ HTM.config.database_config
52
37
  end
53
38
 
54
39
  # Check if connection is established and active
@@ -59,8 +44,7 @@ class HTM
59
44
  ActiveRecord::Base.connected? && ActiveRecord::Base.connection.active?
60
45
  rescue ActiveRecord::ConnectionNotDefined, ActiveRecord::ConnectionNotEstablished
61
46
  false
62
- rescue StandardError => e
63
- HTM.logger.debug "Connection check failed: #{e.class} - #{e.message}"
47
+ rescue StandardError
64
48
  false
65
49
  end
66
50
 
@@ -73,8 +73,6 @@ class HTM
73
73
  HTM.logger.warn "CircuitBreaker[#{@name}]: Circuit is OPEN, failing fast"
74
74
  raise CircuitBreakerOpenError, "Circuit breaker '#{@name}' is open. Service unavailable."
75
75
  end
76
- when :half_open
77
- HTM.logger.debug "CircuitBreaker[#{@name}]: Circuit is HALF-OPEN, testing service"
78
76
  end
79
77
  end
80
78