Class: ClaudeMemory::Recall

Inherits:
Object
  • Object
show all
Defined in:
lib/claude_memory/recall.rb,
lib/claude_memory/recall/query_core.rb,
lib/claude_memory/recall/dual_engine.rb,
lib/claude_memory/recall/legacy_engine.rb,
lib/claude_memory/recall/expansion_detector.rb,
lib/claude_memory/recall/dual_query_template.rb

Overview

Query interface for facts across dual databases (global + project). Delegates to DualEngine or LegacyEngine depending on the store type.

Defined Under Namespace

Modules: ExpansionDetector, QueryCore Classes: DualEngine, DualQueryTemplate, LegacyEngine

Constant Summary collapse

SCOPE_PROJECT =

Returns query only project-scoped facts.

Returns:

  • (String)

    query only project-scoped facts

"project"
SCOPE_GLOBAL =

Returns query only global-scoped facts.

Returns:

  • (String)

    query only global-scoped facts

"global"
SCOPE_ALL =

Returns query both project and global facts (default).

Returns:

  • (String)

    query both project and global facts (default)

"all"

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(store_or_manager, fts: nil, project_path: nil, env: ENV, embedding_generator: nil) ⇒ Recall

Returns a new instance of Recall.

Parameters:

  • store_or_manager (Store::SQLiteStore, Store::StoreManager)

    database store or dual-database manager

  • fts (Index::LexicalFTS, nil) (defaults to: nil)

    full-text search index (used only with legacy single-store)

  • project_path (String, nil) (defaults to: nil)

    project root path (defaults to Configuration#project_dir)

  • env (Hash) (defaults to: ENV)

    environment variables

  • embedding_generator (Object, nil) (defaults to: nil)

    vector embedding generator for semantic search



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/claude_memory/recall.rb', line 49

def initialize(store_or_manager, fts: nil, project_path: nil, env: ENV, embedding_generator: nil)
  config = Configuration.new(env)
  resolved_project_path = project_path || config.project_dir
  resolved_generator = embedding_generator || Embeddings.resolve(env: env)

  @engine = if store_or_manager.is_a?(Store::StoreManager)
    DualEngine.new(
      store_or_manager,
      embedding_generator: resolved_generator,
      project_path: resolved_project_path
    )
  else
    LegacyEngine.new(
      store_or_manager,
      fts: fts || Index::LexicalFTS.new(store_or_manager),
      embedding_generator: resolved_generator,
      project_path: resolved_project_path
    )
  end
end

Class Method Details

.architecture_choices(manager, limit: 10) ⇒ Array<Hash>

Returns architecture-related facts.

Parameters:

  • manager (Store::StoreManager)

    dual-database manager

  • limit (Integer) (defaults to: 10)

    max results

Returns:

  • (Array<Hash>)

    architecture-related facts



25
26
27
# File 'lib/claude_memory/recall.rb', line 25

def architecture_choices(manager, limit: 10)
  Shortcuts.for(:architecture, manager, limit: limit)
end

.conventions(manager, limit: 20) ⇒ Array<Hash>

Returns convention facts.

Parameters:

  • manager (Store::StoreManager)

    dual-database manager

  • limit (Integer) (defaults to: 20)

    max results

Returns:

  • (Array<Hash>)

    convention facts



32
33
34
# File 'lib/claude_memory/recall.rb', line 32

def conventions(manager, limit: 20)
  Shortcuts.for(:conventions, manager, limit: limit)
end

.project_config(manager, limit: 10) ⇒ Array<Hash>

Returns project configuration facts.

Parameters:

  • manager (Store::StoreManager)

    dual-database manager

  • limit (Integer) (defaults to: 10)

    max results

Returns:

  • (Array<Hash>)

    project configuration facts



39
40
41
# File 'lib/claude_memory/recall.rb', line 39

def project_config(manager, limit: 10)
  Shortcuts.for(:project_config, manager, limit: limit)
end

.recent_decisions(manager, limit: 10) ⇒ Array<Hash>

Returns recent decision facts.

Parameters:

  • manager (Store::StoreManager)

    dual-database manager

  • limit (Integer) (defaults to: 10)

    max results

Returns:

  • (Array<Hash>)

    recent decision facts



18
19
20
# File 'lib/claude_memory/recall.rb', line 18

def recent_decisions(manager, limit: 10)
  Shortcuts.for(:decisions, manager, limit: limit)
end

Instance Method Details

#changes(since:, limit: 50, scope: SCOPE_ALL) ⇒ Array<Hash>

List facts created or modified since a given time

Parameters:

  • since (String)

    ISO 8601 timestamp

  • limit (Integer) (defaults to: 50)

    max results

  • scope (String) (defaults to: SCOPE_ALL)

    one of SCOPE_ALL, SCOPE_PROJECT, SCOPE_GLOBAL

Returns:

  • (Array<Hash>)

    recently changed facts



113
114
115
# File 'lib/claude_memory/recall.rb', line 113

def changes(since:, limit: 50, scope: SCOPE_ALL)
  @engine.changes(since: since, limit: limit, scope: scope)
end

#conflicts(scope: SCOPE_ALL) ⇒ Array<Hash>

List open fact conflicts

Parameters:

  • scope (String) (defaults to: SCOPE_ALL)

    one of SCOPE_ALL, SCOPE_PROJECT, SCOPE_GLOBAL

Returns:

  • (Array<Hash>)

    unresolved conflicts



120
121
122
# File 'lib/claude_memory/recall.rb', line 120

def conflicts(scope: SCOPE_ALL)
  @engine.conflicts(scope: scope)
end

#explain(fact_id_or_docid, scope: nil) ⇒ Hash

Show provenance chain for a fact

Parameters:

  • fact_id_or_docid (Integer, String)

    fact ID or document ID

  • scope (String, nil) (defaults to: nil)

    optional scope filter

Returns:

  • (Hash)

    provenance details including source content



104
105
106
# File 'lib/claude_memory/recall.rb', line 104

def explain(fact_id_or_docid, scope: nil)
  @engine.explain(fact_id_or_docid, scope: scope)
end

#fact_graph(fact_id, depth: 2, scope: nil) ⇒ Hash

Traverse fact relationships (supersessions, conflicts) as a graph

Parameters:

  • fact_id (Integer)

    starting fact ID

  • depth (Integer) (defaults to: 2)

    traversal depth

  • scope (String, nil) (defaults to: nil)

    optional scope filter

Returns:

  • (Hash)

    graph with nodes and edges



96
97
98
# File 'lib/claude_memory/recall.rb', line 96

def fact_graph(fact_id, depth: 2, scope: nil)
  @engine.fact_graph(fact_id, depth: depth, scope: scope)
end

#facts_by_branch(branch_name, limit: 20, scope: SCOPE_ALL) ⇒ Array<Hash>

Find facts associated with a git branch

Parameters:

  • branch_name (String)

    git branch name

  • limit (Integer) (defaults to: 20)

    max results

  • scope (String) (defaults to: SCOPE_ALL)

    one of SCOPE_ALL, SCOPE_PROJECT, SCOPE_GLOBAL

Returns:

  • (Array<Hash>)

    facts from the given branch



129
130
131
# File 'lib/claude_memory/recall.rb', line 129

def facts_by_branch(branch_name, limit: 20, scope: SCOPE_ALL)
  @engine.facts_by_branch(branch_name, limit: limit, scope: scope)
end

#facts_by_directory(cwd, limit: 20, scope: SCOPE_ALL) ⇒ Array<Hash>

Find facts associated with a working directory

Parameters:

  • cwd (String)

    directory path

  • limit (Integer) (defaults to: 20)

    max results

  • scope (String) (defaults to: SCOPE_ALL)

    one of SCOPE_ALL, SCOPE_PROJECT, SCOPE_GLOBAL

Returns:

  • (Array<Hash>)

    facts from the given directory



138
139
140
# File 'lib/claude_memory/recall.rb', line 138

def facts_by_directory(cwd, limit: 20, scope: SCOPE_ALL)
  @engine.facts_by_directory(cwd, limit: limit, scope: scope)
end

#facts_by_tool(tool_name, limit: 20, scope: SCOPE_ALL) ⇒ Array<Hash>

Find facts associated with a specific tool

Parameters:

  • tool_name (String)

    tool name (e.g., “Read”, “Bash”)

  • limit (Integer) (defaults to: 20)

    max results

  • scope (String) (defaults to: SCOPE_ALL)

    one of SCOPE_ALL, SCOPE_PROJECT, SCOPE_GLOBAL

Returns:

  • (Array<Hash>)

    facts from sessions using the given tool



147
148
149
# File 'lib/claude_memory/recall.rb', line 147

def facts_by_tool(tool_name, limit: 20, scope: SCOPE_ALL)
  @engine.facts_by_tool(tool_name, limit: limit, scope: scope)
end

#query(query_text, limit: 10, scope: SCOPE_ALL, include_raw_text: false, intent: nil) ⇒ Array<Hash>

Search facts by text query using FTS5

Parameters:

  • query_text (String)

    search terms

  • limit (Integer) (defaults to: 10)

    max results

  • scope (String) (defaults to: SCOPE_ALL)

    one of SCOPE_ALL, SCOPE_PROJECT, SCOPE_GLOBAL

  • include_raw_text (Boolean) (defaults to: false)

    include source content text in results

  • intent (String, nil) (defaults to: nil)

    query intent hint for ranking

Returns:

  • (Array<Hash>)

    matching facts with provenance



77
78
79
# File 'lib/claude_memory/recall.rb', line 77

def query(query_text, limit: 10, scope: SCOPE_ALL, include_raw_text: false, intent: nil)
  @engine.query(query_text, limit: limit, scope: scope, include_raw_text: include_raw_text, intent: intent)
end

#query_concepts(concepts, limit: 10, scope: SCOPE_ALL) ⇒ Array<Hash>

Find facts at the intersection of multiple concepts

Parameters:

  • concepts (Array<String>)

    2-5 concept terms to intersect

  • limit (Integer) (defaults to: 10)

    max results

  • scope (String) (defaults to: SCOPE_ALL)

    one of SCOPE_ALL, SCOPE_PROJECT, SCOPE_GLOBAL

Returns:

  • (Array<Hash>)

    facts matching all given concepts

Raises:

  • (ArgumentError)

    if concepts count is not 2-5



169
170
171
172
173
# File 'lib/claude_memory/recall.rb', line 169

def query_concepts(concepts, limit: 10, scope: SCOPE_ALL)
  raise ArgumentError, "Must provide 2-5 concepts" unless (2..5).cover?(concepts.size)

  @engine.query_concepts(concepts, limit: limit, scope: scope)
end

#query_index(query_text, limit: 20, scope: SCOPE_ALL, intent: nil) ⇒ Array<Hash>

Search content items (not facts) via FTS5 index

Parameters:

  • query_text (String)

    search terms

  • limit (Integer) (defaults to: 20)

    max results

  • scope (String) (defaults to: SCOPE_ALL)

    one of SCOPE_ALL, SCOPE_PROJECT, SCOPE_GLOBAL

  • intent (String, nil) (defaults to: nil)

    query intent hint for ranking

Returns:

  • (Array<Hash>)

    matching content items



87
88
89
# File 'lib/claude_memory/recall.rb', line 87

def query_index(query_text, limit: 20, scope: SCOPE_ALL, intent: nil)
  @engine.query_index(query_text, limit: limit, scope: scope, intent: intent)
end

#query_semantic(text, limit: 10, scope: SCOPE_ALL, mode: :both, explain: false, intent: nil) ⇒ Array<Hash>

Search facts using vector embeddings (semantic similarity)

Parameters:

  • text (String)

    natural language query

  • limit (Integer) (defaults to: 10)

    max results

  • scope (String) (defaults to: SCOPE_ALL)

    one of SCOPE_ALL, SCOPE_PROJECT, SCOPE_GLOBAL

  • mode (Symbol) (defaults to: :both)

    :vector, :lexical, or :both (hybrid RRF)

  • explain (Boolean) (defaults to: false)

    include scoring breakdown in results

  • intent (String, nil) (defaults to: nil)

    query intent hint for ranking

Returns:

  • (Array<Hash>)

    semantically similar facts



159
160
161
# File 'lib/claude_memory/recall.rb', line 159

def query_semantic(text, limit: 10, scope: SCOPE_ALL, mode: :both, explain: false, intent: nil)
  @engine.query_semantic(text, limit: limit, scope: scope, mode: mode, explain: explain, intent: intent)
end