memoflow 0.1.1 → 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: a7b740677dd782355d12ca87b5841fc9d2060246053a7fd6bafdee8ef48430e9
4
- data.tar.gz: 2ec2c7c8827235e89eb4908e2494044c6a6badc11b05ccb70a60cfa6dd363039
3
+ metadata.gz: 5385f8cb2a8ce3914a816df59159162dd0791f6a21700190572714e0a7ac31be
4
+ data.tar.gz: 04b002ac7647a4cb790319be0b00e9ddc19ff865728f4daca202547742ed0b14
5
5
  SHA512:
6
- metadata.gz: c265d8d249762b5887fa3c58ac96e7a5f047c3d039920accbe2977df60fa8489a421856a529c49612a93cbf2631b38760eed328d4d543e68ea5f31c668818e99
7
- data.tar.gz: bed11e21bfea20fd6e9c54b36762a3d9a3cf1538cadc3b27e0f35d7405a7cce92260d803d0850a2875d186bb5822622b63d3d49d77af74e39bb2f47ca87f1061
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
data/lib/memoflow/cli.rb CHANGED
@@ -76,7 +76,8 @@ module Memoflow
76
76
  puts JSON.pretty_generate(client.start_task(title))
77
77
  when "finish"
78
78
  id = argv.shift
79
- puts JSON.pretty_generate(client.finish_task(id))
79
+ result = id.to_s.empty? ? client.finish_task : client.finish_task(id)
80
+ puts JSON.pretty_generate(result)
80
81
  when "resume"
81
82
  id = argv.shift
82
83
  raise Error, "task id is required" if id.to_s.empty?
@@ -74,7 +74,8 @@ module Memoflow
74
74
  task.merge(current_session_id: session[:id])
75
75
  end
76
76
 
77
- def finish_task(task_id = current_task_id, status: "completed")
77
+ def finish_task(task_id = nil, status: "completed")
78
+ task_id ||= current_task_id
78
79
  raise Error, "no active task" unless task_id
79
80
 
80
81
  task = find_task(task_id)
@@ -113,7 +114,8 @@ module Memoflow
113
114
  end
114
115
 
115
116
  def current_task_id
116
- @store.read_state("current_task")&.dig(:task_id)
117
+ state = @store.read_state("current_task")
118
+ state ? state[:task_id] : nil
117
119
  end
118
120
 
119
121
  def query(term = nil, limit: 5)
@@ -172,8 +174,8 @@ module Memoflow
172
174
 
173
175
  tokens = term.downcase.split(/\s+/)
174
176
  fields = normalized_fields(entry)
175
- score = tokens.sum do |token|
176
- field_score(fields, token)
177
+ score = tokens.inject(0) do |total, token|
178
+ total + field_score(fields, token)
177
179
  end
178
180
 
179
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.1"
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.1
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-25 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
@@ -27,7 +27,7 @@ dependencies:
27
27
  description: Memoflow captures git-backed development context and exposes a lightweight
28
28
  query API for AI assistants.
29
29
  email:
30
- - opensource@ikshalabs.com
30
+ - jayesh.pawar@ikshalabs.com
31
31
  executables:
32
32
  - memoflow
33
33
  extensions: []
@@ -51,12 +51,12 @@ files:
51
51
  - lib/memoflow/store.rb
52
52
  - lib/memoflow/vectorizer.rb
53
53
  - lib/memoflow/version.rb
54
- homepage: https://github.com/ikshalabs/memoflow
54
+ homepage: https://github.com/jayesh-ikshalabs/memoflow
55
55
  licenses:
56
56
  - MIT
57
57
  metadata:
58
- source_code_uri: https://github.com/ikshalabs/memoflow
59
- changelog_uri: https://github.com/ikshalabs/memoflow/releases
58
+ source_code_uri: https://github.com/jayesh-ikshalabs/memoflow
59
+ changelog_uri: https://github.com/jayesh-ikshalabs/memoflow/releases
60
60
  post_install_message:
61
61
  rdoc_options: []
62
62
  require_paths:
@@ -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
  - - ">="