Skip to content

[Feature]: ctx_search project: filter — scope FTS5 ContentStore to current project when using a global DB #737

@jetnet

Description

@jetnet

Platform

Pi / OpenClaw (Pi Agent) — but applies to all platforms using CONTEXT_MODE_PROJECT_DIR to opt into a global shared DB.

Feature description

When opting into a global knowledge base via CONTEXT_MODE_PROJECT_DIR=$HOME (or any single shared path), ctx_search has no way to scope results to the current project. The only user-facing filter parameters today are source (label partial-match), sort, contentType, and limit.

The problem: auto-captured session memory — indexed under fixed source labels like session-events, project/<file>, user/<file>, memory/<file> — cannot be namespaced by project because those labels are generated automatically and contain no project identifier. Using source: "myproject/" only works for content explicitly indexed by the user with a matching prefix; it silently misses all auto-memory.

The internal plumbing already supports per-project filtering at every layer:

  • SessionDB.searchEvents(query, limit, projectDir, source)projectDir param exists and filters correctly
  • searchAutoMemory(queries, limit, projectDir, configDir, adapter)projectDir param exists
  • ContentStore chunks carry session_id / event_id FK attribution (written via index({ attribution })) which links back to a session that has a project_dir

The gap is that ctx_search never surfaces projectDir as a user-facing parameter, so it is inaccessible from MCP tool calls.

Use case

I want a single global knowledge base so I can search across all my projects in one ctx_search call when I need cross-project recall. But I also want to narrow results to just the current project during normal work — without having to maintain per-project DB configs or restart sessions.

Concrete workflow:

# default: search only current project
ctx_search(queries: ["apk install"])
# → only returns results from /home/jet/src/temp

# explicit global search
ctx_search(queries: ["apk install"], project: "global")
# → returns results from all projects

Today the only workaround is either:

  1. Stay on per-project DBs (lose cross-project recall entirely), or
  2. Use the global DB and get undifferentiated results from all projects mixed together

Related: #663 fixed cross-project auto-memory leakage in per-project DB mode. This feature is the complementary ask for global DB mode — intentional cross-project sharing with opt-in per-project scoping.

Proposed solution

Add an optional project parameter to ctx_search:

project?: "current" | "global" | string
// "current" (default when global DB is active) → filter to current session's projectDir
// "global"                                     → no filter, search all projects (today's behaviour)
// "/path/to/project"                           → filter to that specific project

When CONTEXT_MODE_PROJECT_DIR is not set (default per-project DB mode), the parameter is a no-op — the DB is already isolated by hash, so every search is implicitly project-scoped.

Implementation sketch:

  1. ContentStore chunk attribution — the attribution FK (session_id, event_id) already written on indexed chunks can join to session_events.project_dir. A WHERE project_dir = ? filter on the FTS5 join would scope FTS5 results without schema changes beyond adding the join.

  2. searchAllSources in src/search/unified.ts — already receives projectDir in SearchAllSourcesOpts and passes it to sessionDB.searchEvents and searchAutoMemory. The missing piece is forwarding it to the ContentStore search path (Source 1 in searchAllSources).

  3. ctx_search tool handler in src/server.ts — add project to the Zod schema, resolve "current" to getProjectDir(), "global" to undefined, and thread the resolved value through to searchAllSources.

Backwards compat: when project is omitted the behaviour is identical to today.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions