Recently Written · git

ai-config

One canonical AI agent config (rules, skills, MCP servers) rendered into Claude Code, Codex, Copilot CLI and Antigravity by acsync

git clone https://github.com/equwal/ai-config

Log | Files | Refs


skills/memstate/scripts/memstate_search.py (2881 bytes)

1 #!/usr/bin/env python3
2 """Search memories (memstated).
3 
4 Three modes:
5 - hybrid (default): FTS match on any query word fused with the semantic
6                  ranking (reciprocal rank fusion). When the embedder is
7                  unavailable the FTS hits are returned alone and the
8                  response carries a `degraded` reason.
9 - fts:           SQLite FTS5 keyword match on content + keypath. Every
10                  query word must match.
11 - semantic:      cosine similarity between the query and embeddings of
12                  the current content at each keypath. Requires Ollama
13                  running locally with the configured embed model
14                  (default nomic-embed-text; set MEMSTATE_EMBED_MODEL
15                  or start memstated with --embed-model to change it).
16 
17 --scope user searches the reserved user scope and drops hits that describe
18 another machine (host.<other_slug>.*).
19 """
20 import argparse
21 import sys
22 
23 from _client import add_scope_args, emit, fetch, is_other_host, resolve_project
24 
25 
26 def main() -> int:
27     ap = argparse.ArgumentParser(description="Search memories")
28     ap.add_argument("--query", required=True)
29     add_scope_args(ap)
30     ap.add_argument("--all-projects", action="store_true",
31                     help="search every project instead of just this repo's")
32     ap.add_argument("--limit", type=int, default=20)
33     ap.add_argument("--mode", choices=("hybrid", "fts", "semantic"), default="hybrid")
34     ap.add_argument("--threshold", type=float, default=None,
35                     help="semantic and hybrid: cosine floor for hits (default 0.5)")
36     ap.add_argument("--category", default=None,
37                     help="only return memories with this category")
38     ap.add_argument("--topics", default=None,
39                     help="comma-separated: match memories tagged with any of these")
40     ap.add_argument("--keypath-prefix", default=None,
41                     help="only memories at this keypath or below, e.g. branches.feature_x")
42     args = ap.parse_args()
43 
44     body = {"query": args.query, "limit": args.limit, "mode": args.mode}
45     if not args.all_projects:
46         body["project_id"] = resolve_project(args)
47     if args.threshold is not None:
48         body["threshold"] = args.threshold
49     if args.category:
50         body["category"] = args.category
51     if args.topics:
52         body["topics"] = args.topics.split(",")
53     if args.keypath_prefix:
54         body["keypath_prefix"] = args.keypath_prefix
55 
56     def search():
57         out = fetch("POST", "/memories/search", body)
58         if args.scope == "user" and isinstance(out, dict) and isinstance(out.get("results"), list):
59             out["results"] = [h for h in out["results"] if not is_other_host(h.get("keypath", ""))]
60             out["total_found"] = len(out["results"])
61         return out
62 
63     return emit(search)
64 
65 
66 if __name__ == "__main__":
67     sys.exit(main())