memoflow 0.1.2 → 0.1.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cd44156c1785088292a9194c41d5ddec68b3ce93f34b9962671e4b6477ba8ddb
4
- data.tar.gz: '08298b8f84586b763586fd1103a689b81b94eb35bdd6b4c29d9a0e4f5e4a9d01'
3
+ metadata.gz: 5385f8cb2a8ce3914a816df59159162dd0791f6a21700190572714e0a7ac31be
4
+ data.tar.gz: 04b002ac7647a4cb790319be0b00e9ddc19ff865728f4daca202547742ed0b14
5
5
  SHA512:
6
- metadata.gz: 03415cb660077882ae968cee30771b00c5726cf2afdcbf563320cac52fa2e83d5660d20756b196dbf4c309a4658023941d38aa4fcfa3475d8cb58a488519bcf1
7
- data.tar.gz: 28e69feac39e5ca51f58f7e264ed5a72da5f72a16c4403e2ffa2c8e60941a592752727714a0d1288b9b2ed149af3fc38ac4a161055342fc76356a3623fadce6d
6
+ metadata.gz: d037263c24b7fff449a32b380e11a3d882e67d2b8bc1280428bdf18bdf8e362b1f78e3dd01256392fdcc054c12da0f8425b518ea37a9e9aeeb6213e84c8f5432
7
+ data.tar.gz: 4120c688872a8c391a65d4c1e948fa4b653ab94e2e88d45e7da5ef90057d912069d785f219dfac518b342d0a0900336e9fc7738e584db26da5d20bf9f9c3504e
data/README.md CHANGED
@@ -29,6 +29,46 @@ end
29
29
 
30
30
  If you do not want a Rails initializer, the CLI can operate with `MEMOFLOW_KEY` alone.
31
31
 
32
+ ## Encryption key setup
33
+
34
+ Memoflow encrypts stored context locally. `MEMOFLOW_KEY` is the secret used to encrypt and decrypt that repository's stored memory.
35
+
36
+ Generate a strong key with either:
37
+
38
+ ```bash
39
+ ruby -rsecurerandom -e 'puts SecureRandom.hex(32)'
40
+ ```
41
+
42
+ or:
43
+
44
+ ```bash
45
+ openssl rand -hex 32
46
+ ```
47
+
48
+ Then export it in your shell:
49
+
50
+ ```bash
51
+ export MEMOFLOW_KEY="paste-generated-key-here"
52
+ ```
53
+
54
+ Why it should be project-specific:
55
+
56
+ - it limits the blast radius if one key is exposed
57
+ - it keeps one repository's history isolated from another
58
+ - it lets teams share access only to the repos they should decrypt
59
+
60
+ Recommended handling:
61
+
62
+ - use one key per repository
63
+ - store it in a password manager or team secret store
64
+ - do not commit it to git
65
+ - do not reuse production application secrets for Memoflow
66
+
67
+ Important:
68
+
69
+ - if you lose the key, existing Memoflow data for that repository cannot be decrypted
70
+ - if your team needs shared access to the same stored context, everyone must use the same repo key
71
+
32
72
  Optional embedding provider:
33
73
 
34
74
  ```ruby
@@ -81,6 +121,12 @@ bundle exec memoflow annotate "Problem statement: fix webhook retries"
81
121
  bundle exec memoflow query "webhook retries"
82
122
  ```
83
123
 
124
+ ## Ruby compatibility
125
+
126
+ Memoflow is currently targeted at Ruby `>= 2.3.0`.
127
+
128
+ This support floor is chosen to work more cleanly with older Rails applications while avoiding newer Ruby-only core methods in the runtime code.
129
+
84
130
  Rails initializer example:
85
131
 
86
132
  ```ruby
@@ -114,7 +114,8 @@ module Memoflow
114
114
  end
115
115
 
116
116
  def current_task_id
117
- @store.read_state("current_task")&.dig(:task_id)
117
+ state = @store.read_state("current_task")
118
+ state ? state[:task_id] : nil
118
119
  end
119
120
 
120
121
  def query(term = nil, limit: 5)
@@ -173,8 +174,8 @@ module Memoflow
173
174
 
174
175
  tokens = term.downcase.split(/\s+/)
175
176
  fields = normalized_fields(entry)
176
- score = tokens.sum do |token|
177
- field_score(fields, token)
177
+ score = tokens.inject(0) do |total, token|
178
+ total + field_score(fields, token)
178
179
  end
179
180
 
180
181
  score + recency_boost(entry) + task_boost(entry) + semantic_boost(entry, term)
@@ -41,7 +41,7 @@ module Memoflow
41
41
  end
42
42
 
43
43
  def normalize(vector)
44
- magnitude = Math.sqrt(vector.sum { |value| value * value })
44
+ magnitude = Math.sqrt(vector.inject(0.0) { |total, value| total + value * value })
45
45
  return vector if magnitude.zero?
46
46
 
47
47
  vector.map { |value| (value / magnitude).round(6) }
@@ -42,7 +42,7 @@ module Memoflow
42
42
  def unpack(blob)
43
43
  cursor = 0
44
44
  parts = 3.times.map do
45
- length = blob.byteslice(cursor, 4).unpack1("N")
45
+ length = blob.byteslice(cursor, 4).unpack("N").first
46
46
  cursor += 4
47
47
  part = blob.byteslice(cursor, length)
48
48
  cursor += length
@@ -10,11 +10,11 @@ module Memoflow
10
10
  end
11
11
 
12
12
  def capture
13
- {
13
+ compact_hash(
14
14
  provider: provider,
15
15
  repository: repository,
16
16
  pull_request: pull_request
17
- }.compact
17
+ )
18
18
  end
19
19
 
20
20
  private
@@ -68,6 +68,10 @@ module Memoflow
68
68
  "#{parts[-2]}/#{parts[-1].sub(/\.git\z/, "")}"
69
69
  end
70
70
 
71
+ def compact_hash(hash)
72
+ hash.reject { |_, value| value.nil? }
73
+ end
74
+
71
75
  def source_text
72
76
  [@remote_url, @env["GITHUB_SERVER_URL"], @env["CI_SERVER_URL"]].compact.join(" ").downcase
73
77
  end
@@ -21,11 +21,11 @@ module Memoflow
21
21
  rescue IOError, SystemCallError
22
22
  break unless @running
23
23
  ensure
24
- socket&.close
24
+ socket.close if socket
25
25
  end
26
26
  end
27
27
  ensure
28
- @server&.close
28
+ @server.close if @server
29
29
  end
30
30
 
31
31
  private
@@ -91,7 +91,7 @@ module Memoflow
91
91
  %w[INT TERM].each do |signal|
92
92
  trap(signal) do
93
93
  @running = false
94
- @server&.close
94
+ @server.close if @server
95
95
  end
96
96
  end
97
97
  end
@@ -120,7 +120,7 @@ module Memoflow
120
120
  with_lock do
121
121
  setup!
122
122
  path = @root.join(scope, filename)
123
- payload = JSON.generate(envelope(record).transform_keys(&:to_s))
123
+ payload = JSON.generate(stringify_keys(envelope(record)))
124
124
  atomic_write(path, @encryptor.encrypt(payload))
125
125
  end
126
126
  end
@@ -170,9 +170,11 @@ module Memoflow
170
170
  setup!
171
171
  File.open(@root.join(".lock"), File::RDWR | File::CREAT, 0o644) do |file|
172
172
  file.flock(File::LOCK_EX)
173
- yield
174
- ensure
175
- file.flock(File::LOCK_UN)
173
+ begin
174
+ yield
175
+ ensure
176
+ file.flock(File::LOCK_UN)
177
+ end
176
178
  end
177
179
  end
178
180
 
@@ -184,5 +186,13 @@ module Memoflow
184
186
  ensure
185
187
  File.delete(temp_path) if temp_path && File.exist?(temp_path)
186
188
  end
189
+
190
+ def stringify_keys(hash)
191
+ result = {}
192
+ hash.each do |key, value|
193
+ result[key.to_s] = value
194
+ end
195
+ result
196
+ end
187
197
  end
188
198
  end
@@ -19,7 +19,9 @@ module Memoflow
19
19
  return 0.0 if left.nil? || right.nil?
20
20
  return 0.0 if left.empty? || right.empty?
21
21
 
22
- left.zip(right).sum { |a, b| a.to_f * b.to_f }
22
+ left.zip(right).inject(0.0) do |total, pair|
23
+ total + pair[0].to_f * pair[1].to_f
24
+ end
23
25
  end
24
26
 
25
27
  def indexable_text(entry)
@@ -47,7 +49,7 @@ module Memoflow
47
49
  end
48
50
 
49
51
  def normalize(vector)
50
- magnitude = Math.sqrt(vector.sum { |value| value * value })
52
+ magnitude = Math.sqrt(vector.inject(0.0) { |total, value| total + value * value })
51
53
  return vector if magnitude.zero?
52
54
 
53
55
  vector.map { |value| (value / magnitude).round(6) }
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Memoflow
4
- VERSION = "0.1.2"
4
+ VERSION = "0.1.3"
5
5
  SCHEMA_VERSION = 1
6
6
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: memoflow
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jayesh Pawar
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-03-26 00:00:00.000000000 Z
11
+ date: 2026-03-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest
@@ -65,7 +65,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - ">="
67
67
  - !ruby/object:Gem::Version
68
- version: '3.1'
68
+ version: 2.3.0
69
69
  required_rubygems_version: !ruby/object:Gem::Requirement
70
70
  requirements:
71
71
  - - ">="