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:
- Stay on per-project DBs (lose cross-project recall entirely), or
- 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:
-
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.
-
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).
-
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.
Platform
Pi / OpenClaw (Pi Agent) — but applies to all platforms using
CONTEXT_MODE_PROJECT_DIRto 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_searchhas no way to scope results to the current project. The only user-facing filter parameters today aresource(label partial-match),sort,contentType, andlimit.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. Usingsource: "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)—projectDirparam exists and filters correctlysearchAutoMemory(queries, limit, projectDir, configDir, adapter)—projectDirparam existsContentStorechunks carrysession_id/event_idFK attribution (written viaindex({ attribution })) which links back to a session that has aproject_dirThe gap is that
ctx_searchnever surfacesprojectDiras 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_searchcall 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:
Today the only workaround is either:
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
projectparameter toctx_search:When
CONTEXT_MODE_PROJECT_DIRis 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:
ContentStorechunk attribution — theattributionFK (session_id,event_id) already written on indexed chunks can join tosession_events.project_dir. AWHERE project_dir = ?filter on the FTS5 join would scope FTS5 results without schema changes beyond adding the join.searchAllSourcesinsrc/search/unified.ts— already receivesprojectDirinSearchAllSourcesOptsand passes it tosessionDB.searchEventsandsearchAutoMemory. The missing piece is forwarding it to the ContentStore search path (Source 1 insearchAllSources).ctx_searchtool handler insrc/server.ts— addprojectto the Zod schema, resolve"current"togetProjectDir(),"global"toundefined, and thread the resolved value through tosearchAllSources.Backwards compat: when
projectis omitted the behaviour is identical to today.