Packages
fnord
0.8.73
0.9.40
0.9.39
0.9.38
0.9.37
0.9.36
0.9.35
0.9.34
0.9.33
0.9.32
0.9.31
0.9.30
0.9.29
0.9.28
0.9.27
0.9.26
0.9.25
0.9.24
0.9.23
0.9.22
0.9.21
0.9.20
0.9.19
0.9.18
0.9.17
0.9.16
0.9.15
0.9.14
0.9.13
0.9.12
0.9.11
0.9.10
0.9.9
0.9.8
0.9.7
0.9.6
0.9.5
0.9.4
0.9.3
0.9.2
0.9.1
0.9.0
0.8.99
0.8.98
0.8.97
0.8.96
0.8.95
0.8.94
0.8.93
0.8.92
0.8.91
0.8.90
0.8.89
0.8.88
0.8.87
0.8.86
0.8.85
0.8.84
0.8.83
0.8.82
0.8.81
0.8.80
0.8.79
0.8.78
0.8.77
0.8.76
0.8.75
0.8.74
0.8.73
0.8.72
0.8.71
0.8.70
0.8.69
0.8.68
0.8.67
0.8.66
0.8.65
0.8.64
0.8.63
0.8.62
0.8.61
0.8.60
0.8.59
0.8.58
0.8.57
0.8.56
0.8.55
0.8.54
0.8.53
0.8.52
0.8.51
0.8.50
0.8.49
0.8.48
0.8.47
0.8.46
0.8.45
0.8.44
0.8.43
0.8.42
0.8.41
0.8.40
0.8.39
0.8.38
0.8.37
0.8.36
0.8.35
0.8.34
0.8.33
0.8.32
0.8.31
0.8.30
0.8.29
0.8.27
0.8.26
0.8.25
0.8.24
0.8.23
0.8.22
0.8.21
0.8.20
0.8.19
0.8.18
0.8.17
0.8.16
0.8.15
0.8.14
0.8.13
0.8.12
0.8.11
0.8.1
0.8.0
0.7.24
0.7.23
0.7.22
0.7.21
0.7.20
0.7.19
0.7.18
0.7.17
0.7.16
0.7.15
0.7.14
0.7.13
0.7.12
0.7.11
0.7.10
0.7.9
0.7.8
0.7.7
0.7.6
0.7.5
0.7.3
0.7.2
0.7.1
0.7.0
0.6.9
0.6.8
0.6.7
0.6.6
0.6.5
0.6.4
0.6.3
0.6.1
0.6.0
0.5.9
0.5.8
0.5.7
0.5.6
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.44
0.4.43
0.4.42
0.4.41
0.4.40
0.4.39
0.4.38
0.4.37
0.4.36
0.4.35
0.4.34
0.4.33
0.4.32
0.4.30
0.4.29
0.4.28
0.4.27
0.4.26
0.4.25
0.4.24
0.4.23
0.4.22
0.4.21
0.4.20
0.4.19
0.4.18
0.4.17
0.4.16
0.4.15
0.4.14
0.4.13
0.4.12
0.4.11
0.4.10
0.4.9
0.4.8
0.4.7
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.0
0.2.0
0.1.0
AI code archaeology
Current section
Files
Jump to
Current section
Files
lib/store/memories/meta.ex
defmodule Store.Memories.Meta do
@moduledoc """
Handles meta.json file operations for memories.
Contains: id, slug, label, response_template, scope, parent_id, timestamps, counters, weight
"""
@filename "meta.json"
@doc """
Reads meta.json from a memory directory.
Returns {:ok, map} or {:error, reason}.
"""
@spec read(String.t()) :: {:ok, map} | {:error, term}
def read(memory_dir) do
path = Path.join(memory_dir, @filename)
with {:ok, content} <- File.read(path),
{:ok, data} <- Jason.decode(content) do
# Convert string keys to atoms for Memory struct
{:ok, atomize_keys(data)}
end
end
@doc """
Writes meta.json for a memory using atomic write.
"""
@spec write(AI.Memory.t()) :: :ok | {:error, term}
def write(memory) do
memory_dir = get_memory_dir(memory)
path = Path.join(memory_dir, @filename)
data = %{
id: memory.id,
slug: memory.slug,
label: memory.label,
response_template: memory.response_template,
scope: to_string(memory.scope),
parent_id: memory.parent_id,
weight: memory.weight,
created_at: memory.created_at,
last_fired: memory.last_fired,
fire_count: memory.fire_count,
success_count: memory.success_count
}
with {:ok, json} <- Jason.encode(data, pretty: true) do
write_atomic(path, json)
end
end
# ----------------------------------------------------------------------------
# Private Helpers
# ----------------------------------------------------------------------------
defp get_memory_dir(%AI.Memory{scope: :global, slug: slug}) do
Path.join([Settings.get_user_home(), ".fnord/memories", slug])
end
defp get_memory_dir(%AI.Memory{scope: :project, slug: slug}) do
case Settings.get_selected_project() do
{:ok, project_name} ->
Path.join([Settings.get_user_home(), ".fnord/projects", project_name, "memories", slug])
{:error, :project_not_set} ->
raise "Cannot save project-scoped memory without a selected project. Use 'fnord config set-project <name>' first."
end
end
defp atomize_keys(map) when is_map(map) do
map
|> Enum.map(fn {k, v} -> {String.to_atom(k), v} end)
|> Map.new()
|> then(fn m ->
# Convert scope string back to atom
if m[:scope], do: Map.put(m, :scope, String.to_atom(m[:scope])), else: m
end)
end
defp write_atomic(path, content) do
dir = Path.dirname(path)
base = Path.basename(path)
tmp = Path.join(dir, ".#{base}.#{System.unique_integer([:positive])}.tmp")
with :ok <- File.write(tmp, content),
:ok <- File.rename(tmp, path) do
:ok
else
error ->
File.rm(tmp)
error
end
end
end