fix(opencode-plugin): fix tsconfig, add publishConfig and bun lockfile (#5273)
This commit is contained in:
@@ -149,9 +149,9 @@ See the [migration guide](https://docs.mem0.ai/migration/platform-v2-to-v3) for
|
||||
For the latest docs beyond what's in the references, use the doc search tool:
|
||||
|
||||
```bash
|
||||
python ${CLAUDE_SKILL_DIR}/scripts/mem0_doc_search.py --query "topic"
|
||||
python ${CLAUDE_SKILL_DIR}/scripts/mem0_doc_search.py --page "/platform/features/graph-memory"
|
||||
python ${CLAUDE_SKILL_DIR}/scripts/mem0_doc_search.py --index
|
||||
bun ${CLAUDE_SKILL_DIR}/scripts/mem0_doc_search.ts --query "topic"
|
||||
bun ${CLAUDE_SKILL_DIR}/scripts/mem0_doc_search.ts --page "/platform/features/graph-memory"
|
||||
bun ${CLAUDE_SKILL_DIR}/scripts/mem0_doc_search.ts --index
|
||||
```
|
||||
|
||||
No API key needed — searches docs.mem0.ai directly.
|
||||
|
||||
@@ -1,224 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Mem0 Documentation Search Agent (Mintlify-based)
|
||||
On-demand search tool for querying Mem0 documentation without storing content locally.
|
||||
|
||||
This tool leverages Mintlify's documentation structure to perform just-in-time
|
||||
retrieval of technical information from docs.mem0.ai.
|
||||
|
||||
Usage:
|
||||
python mem0_doc_search.py --query "how to add graph memory"
|
||||
python mem0_doc_search.py --query "filter syntax for categories"
|
||||
python mem0_doc_search.py --page "/platform/features/graph-memory"
|
||||
python mem0_doc_search.py --index
|
||||
python mem0_doc_search.py --query "webhook events" --section platform
|
||||
|
||||
Purpose:
|
||||
- Avoid bloating local context with full documentation
|
||||
- Enable just-in-time retrieval of technical details
|
||||
- Query specific documentation pages on demand
|
||||
- Search across the full Mem0 documentation site
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
import urllib.error
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
|
||||
DOCS_BASE = "https://docs.mem0.ai"
|
||||
SEARCH_ENDPOINT = f"{DOCS_BASE}/api/search"
|
||||
LLMS_INDEX = f"{DOCS_BASE}/llms.txt"
|
||||
|
||||
# Known documentation sections for targeted retrieval
|
||||
SECTION_MAP = {
|
||||
"platform": [
|
||||
"/platform/overview",
|
||||
"/platform/quickstart",
|
||||
"/platform/features",
|
||||
"/platform/features/graph-memory",
|
||||
"/platform/features/selective-memory",
|
||||
"/platform/features/custom-categories",
|
||||
"/platform/features/v2-memory-filters",
|
||||
"/platform/features/async-client",
|
||||
"/platform/features/webhooks",
|
||||
"/platform/features/multimodal-support",
|
||||
],
|
||||
"api": [
|
||||
"/api-reference/memory/add-memories",
|
||||
"/api-reference/memory/v2-search-memories",
|
||||
"/api-reference/memory/v2-get-memories",
|
||||
"/api-reference/memory/get-memory",
|
||||
"/api-reference/memory/update-memory",
|
||||
"/api-reference/memory/delete-memory",
|
||||
],
|
||||
"open-source": [
|
||||
"/open-source/overview",
|
||||
"/open-source/python-quickstart",
|
||||
"/open-source/node-quickstart",
|
||||
"/open-source/features",
|
||||
"/open-source/features/graph-memory",
|
||||
"/open-source/features/rest-api",
|
||||
"/open-source/configure-components",
|
||||
],
|
||||
"openmemory": [
|
||||
"/openmemory/overview",
|
||||
"/openmemory/quickstart",
|
||||
],
|
||||
"sdks": [
|
||||
"/sdks/python",
|
||||
"/sdks/js",
|
||||
],
|
||||
"integrations": [
|
||||
"/integrations",
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def fetch_url(url: str) -> str:
|
||||
"""Fetch content from a URL."""
|
||||
req = urllib.request.Request(url, headers={"User-Agent": "Mem0DocSearchAgent/1.0"})
|
||||
try:
|
||||
with urllib.request.urlopen(req, timeout=15) as resp:
|
||||
return resp.read().decode("utf-8")
|
||||
except urllib.error.HTTPError as e:
|
||||
return f"HTTP Error {e.code}: {e.reason}"
|
||||
except urllib.error.URLError as e:
|
||||
return f"URL Error: {e.reason}"
|
||||
|
||||
|
||||
def search_docs(query: str, section: str | None = None) -> dict:
|
||||
"""
|
||||
Search Mem0 documentation using Mintlify's search API.
|
||||
Falls back to the llms.txt index for keyword matching if the API is unavailable.
|
||||
"""
|
||||
# Try Mintlify search API first
|
||||
params = urllib.parse.urlencode({"query": query})
|
||||
search_url = f"{SEARCH_ENDPOINT}?{params}"
|
||||
|
||||
try:
|
||||
result = fetch_url(search_url)
|
||||
data = json.loads(result)
|
||||
if isinstance(data, dict) and data.get("results"):
|
||||
results = data["results"]
|
||||
if section and section in SECTION_MAP:
|
||||
section_paths = SECTION_MAP[section]
|
||||
results = [r for r in results if any(r.get("url", "").startswith(p) for p in section_paths)]
|
||||
return {"source": "mintlify_search", "results": results}
|
||||
except (json.JSONDecodeError, Exception):
|
||||
pass
|
||||
|
||||
# Fallback: search llms.txt index for matching URLs
|
||||
index_content = fetch_url(LLMS_INDEX)
|
||||
query_lower = query.lower()
|
||||
matching_urls = []
|
||||
|
||||
for line in index_content.splitlines():
|
||||
line = line.strip()
|
||||
if not line or line.startswith("#"):
|
||||
continue
|
||||
if query_lower in line.lower():
|
||||
matching_urls.append(line)
|
||||
|
||||
if section and section in SECTION_MAP:
|
||||
section_paths = SECTION_MAP[section]
|
||||
matching_urls = [u for u in matching_urls if any(p in u for p in section_paths)]
|
||||
|
||||
return {
|
||||
"source": "llms_txt_index",
|
||||
"query": query,
|
||||
"matching_urls": matching_urls[:20],
|
||||
"suggestion": "Fetch specific URLs for detailed content",
|
||||
}
|
||||
|
||||
|
||||
def fetch_page(page_path: str) -> dict:
|
||||
"""Fetch a specific documentation page."""
|
||||
url = f"{DOCS_BASE}{page_path}" if page_path.startswith("/") else page_path
|
||||
content = fetch_url(url)
|
||||
return {"url": url, "content": content[:10000], "truncated": len(content) > 10000}
|
||||
|
||||
|
||||
def get_index() -> dict:
|
||||
"""Fetch the full documentation index from llms.txt."""
|
||||
content = fetch_url(LLMS_INDEX)
|
||||
urls = [line.strip() for line in content.splitlines() if line.strip() and not line.startswith("#")]
|
||||
return {"total_pages": len(urls), "urls": urls, "sections": list(SECTION_MAP.keys())}
|
||||
|
||||
|
||||
def list_section(section: str) -> dict:
|
||||
"""List all known pages in a documentation section."""
|
||||
if section not in SECTION_MAP:
|
||||
return {"error": f"Unknown section: {section}", "available": list(SECTION_MAP.keys())}
|
||||
return {
|
||||
"section": section,
|
||||
"pages": [f"{DOCS_BASE}{p}" for p in SECTION_MAP[section]],
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Search Mem0 documentation on demand")
|
||||
parser.add_argument("--query", help="Search query for documentation")
|
||||
parser.add_argument("--page", help="Fetch a specific page path (e.g., /platform/features/graph-memory)")
|
||||
parser.add_argument("--index", action="store_true", help="Show full documentation index")
|
||||
parser.add_argument("--section", help="Filter by section or list section pages")
|
||||
parser.add_argument("--json", action="store_true", help="Output as JSON")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.index:
|
||||
result = get_index()
|
||||
elif args.section and not args.query:
|
||||
result = list_section(args.section)
|
||||
elif args.page:
|
||||
result = fetch_page(args.page)
|
||||
elif args.query:
|
||||
result = search_docs(args.query, section=args.section)
|
||||
else:
|
||||
parser.print_help()
|
||||
sys.exit(1)
|
||||
|
||||
if args.json:
|
||||
print(json.dumps(result, indent=2))
|
||||
else:
|
||||
if isinstance(result, dict):
|
||||
if "results" in result:
|
||||
print(f"Source: {result.get('source', 'unknown')}")
|
||||
for r in result["results"]:
|
||||
print(f" - {r.get('title', 'N/A')}: {r.get('url', 'N/A')}")
|
||||
if r.get("description"):
|
||||
print(f" {r['description'][:200]}")
|
||||
elif "matching_urls" in result:
|
||||
print(f"Source: {result['source']}")
|
||||
print(f"Query: {result['query']}")
|
||||
for url in result["matching_urls"]:
|
||||
print(f" - {url}")
|
||||
if result.get("suggestion"):
|
||||
print(f"\n{result['suggestion']}")
|
||||
elif "urls" in result:
|
||||
print(f"Total documentation pages: {result['total_pages']}")
|
||||
print(f"Sections: {', '.join(result['sections'])}")
|
||||
for url in result["urls"][:30]:
|
||||
print(f" - {url}")
|
||||
if result["total_pages"] > 30:
|
||||
print(f" ... and {result['total_pages'] - 30} more")
|
||||
elif "pages" in result:
|
||||
print(f"Section: {result['section']}")
|
||||
for page in result["pages"]:
|
||||
print(f" - {page}")
|
||||
elif "content" in result:
|
||||
print(f"URL: {result['url']}")
|
||||
if result.get("truncated"):
|
||||
print("[Content truncated to 10000 chars]")
|
||||
print(result["content"])
|
||||
elif "error" in result:
|
||||
print(f"Error: {result['error']}")
|
||||
if result.get("available"):
|
||||
print(f"Available sections: {', '.join(result['available'])}")
|
||||
else:
|
||||
print(json.dumps(result, indent=2))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,181 @@
|
||||
#!/usr/bin/env bun
|
||||
export {};
|
||||
const DOCS_BASE = "https://docs.mem0.ai";
|
||||
const SEARCH_ENDPOINT = `${DOCS_BASE}/api/search`;
|
||||
const LLMS_INDEX = `${DOCS_BASE}/llms.txt`;
|
||||
|
||||
const SECTION_MAP: Record<string, string[]> = {
|
||||
platform: [
|
||||
"/platform/overview",
|
||||
"/platform/quickstart",
|
||||
"/platform/features",
|
||||
"/platform/features/graph-memory",
|
||||
"/platform/features/selective-memory",
|
||||
"/platform/features/custom-categories",
|
||||
"/platform/features/v2-memory-filters",
|
||||
"/platform/features/async-client",
|
||||
"/platform/features/webhooks",
|
||||
"/platform/features/multimodal-support",
|
||||
],
|
||||
api: [
|
||||
"/api-reference/memory/add-memories",
|
||||
"/api-reference/memory/v2-search-memories",
|
||||
"/api-reference/memory/v2-get-memories",
|
||||
"/api-reference/memory/get-memory",
|
||||
"/api-reference/memory/update-memory",
|
||||
"/api-reference/memory/delete-memory",
|
||||
],
|
||||
"open-source": [
|
||||
"/open-source/overview",
|
||||
"/open-source/python-quickstart",
|
||||
"/open-source/node-quickstart",
|
||||
"/open-source/features",
|
||||
"/open-source/features/graph-memory",
|
||||
"/open-source/features/rest-api",
|
||||
"/open-source/configure-components",
|
||||
],
|
||||
openmemory: [
|
||||
"/openmemory/overview",
|
||||
"/openmemory/quickstart",
|
||||
],
|
||||
sdks: ["/sdks/python", "/sdks/js"],
|
||||
integrations: ["/integrations"],
|
||||
};
|
||||
|
||||
async function fetchUrl(url: string): Promise<string> {
|
||||
try {
|
||||
const res = await fetch(url, {
|
||||
headers: { "User-Agent": "Mem0DocSearchAgent/1.0" },
|
||||
signal: AbortSignal.timeout(15000),
|
||||
});
|
||||
if (!res.ok) return `HTTP Error ${res.status}: ${res.statusText}`;
|
||||
return await res.text();
|
||||
} catch (e: any) {
|
||||
return `Error: ${e.message}`;
|
||||
}
|
||||
}
|
||||
|
||||
async function searchDocs(query: string, section?: string) {
|
||||
const params = new URLSearchParams({ query });
|
||||
try {
|
||||
const result = await fetchUrl(`${SEARCH_ENDPOINT}?${params}`);
|
||||
const data = JSON.parse(result);
|
||||
if (data?.results) {
|
||||
let results = data.results;
|
||||
if (section && SECTION_MAP[section]) {
|
||||
const paths = SECTION_MAP[section];
|
||||
results = results.filter((r: any) =>
|
||||
paths.some((p) => r.url?.startsWith(p)),
|
||||
);
|
||||
}
|
||||
return { source: "mintlify_search", results };
|
||||
}
|
||||
} catch {}
|
||||
|
||||
const indexContent = await fetchUrl(LLMS_INDEX);
|
||||
const queryLower = query.toLowerCase();
|
||||
let matching = indexContent
|
||||
.split("\n")
|
||||
.map((l) => l.trim())
|
||||
.filter((l) => l && !l.startsWith("#") && l.toLowerCase().includes(queryLower));
|
||||
|
||||
if (section && SECTION_MAP[section]) {
|
||||
const paths = SECTION_MAP[section];
|
||||
matching = matching.filter((u) => paths.some((p) => u.includes(p)));
|
||||
}
|
||||
|
||||
return {
|
||||
source: "llms_txt_index",
|
||||
query,
|
||||
matching_urls: matching.slice(0, 20),
|
||||
suggestion: "Fetch specific URLs for detailed content",
|
||||
};
|
||||
}
|
||||
|
||||
async function fetchPage(pagePath: string) {
|
||||
const url = pagePath.startsWith("/") ? `${DOCS_BASE}${pagePath}` : pagePath;
|
||||
const content = await fetchUrl(url);
|
||||
return { url, content: content.slice(0, 10000), truncated: content.length > 10000 };
|
||||
}
|
||||
|
||||
async function getIndex() {
|
||||
const content = await fetchUrl(LLMS_INDEX);
|
||||
const urls = content
|
||||
.split("\n")
|
||||
.map((l) => l.trim())
|
||||
.filter((l) => l && !l.startsWith("#"));
|
||||
return { total_pages: urls.length, urls, sections: Object.keys(SECTION_MAP) };
|
||||
}
|
||||
|
||||
function listSection(section: string) {
|
||||
if (!SECTION_MAP[section]) {
|
||||
return { error: `Unknown section: ${section}`, available: Object.keys(SECTION_MAP) };
|
||||
}
|
||||
return { section, pages: SECTION_MAP[section].map((p) => `${DOCS_BASE}${p}`) };
|
||||
}
|
||||
|
||||
function printResult(result: any) {
|
||||
if (result.results) {
|
||||
console.log(`Source: ${result.source ?? "unknown"}`);
|
||||
for (const r of result.results) {
|
||||
console.log(` - ${r.title ?? "N/A"}: ${r.url ?? "N/A"}`);
|
||||
if (r.description) console.log(` ${r.description.slice(0, 200)}`);
|
||||
}
|
||||
} else if (result.matching_urls) {
|
||||
console.log(`Source: ${result.source}`);
|
||||
console.log(`Query: ${result.query}`);
|
||||
for (const url of result.matching_urls) console.log(` - ${url}`);
|
||||
if (result.suggestion) console.log(`\n${result.suggestion}`);
|
||||
} else if (result.urls) {
|
||||
console.log(`Total documentation pages: ${result.total_pages}`);
|
||||
console.log(`Sections: ${result.sections.join(", ")}`);
|
||||
for (const url of result.urls.slice(0, 30)) console.log(` - ${url}`);
|
||||
if (result.total_pages > 30) console.log(` ... and ${result.total_pages - 30} more`);
|
||||
} else if (result.pages) {
|
||||
console.log(`Section: ${result.section}`);
|
||||
for (const page of result.pages) console.log(` - ${page}`);
|
||||
} else if (result.content) {
|
||||
console.log(`URL: ${result.url}`);
|
||||
if (result.truncated) console.log("[Content truncated to 10000 chars]");
|
||||
console.log(result.content);
|
||||
} else if (result.error) {
|
||||
console.log(`Error: ${result.error}`);
|
||||
if (result.available) console.log(`Available sections: ${result.available.join(", ")}`);
|
||||
} else {
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
}
|
||||
}
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
const flags: Record<string, string | boolean> = {};
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
if (args[i] === "--query" && args[i + 1]) flags.query = args[++i];
|
||||
else if (args[i] === "--page" && args[i + 1]) flags.page = args[++i];
|
||||
else if (args[i] === "--section" && args[i + 1]) flags.section = args[++i];
|
||||
else if (args[i] === "--index") flags.index = true;
|
||||
else if (args[i] === "--json") flags.json = true;
|
||||
}
|
||||
|
||||
let result: any;
|
||||
if (flags.index) {
|
||||
result = await getIndex();
|
||||
} else if (flags.section && !flags.query) {
|
||||
result = listSection(flags.section as string);
|
||||
} else if (flags.page) {
|
||||
result = await fetchPage(flags.page as string);
|
||||
} else if (flags.query) {
|
||||
result = await searchDocs(flags.query as string, flags.section as string | undefined);
|
||||
} else {
|
||||
console.log("Usage:");
|
||||
console.log(" bun scripts/mem0_doc_search.ts --query \"topic\"");
|
||||
console.log(" bun scripts/mem0_doc_search.ts --page \"/platform/features/graph-memory\"");
|
||||
console.log(" bun scripts/mem0_doc_search.ts --index");
|
||||
console.log(" bun scripts/mem0_doc_search.ts --section platform");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (flags.json) {
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
} else {
|
||||
printResult(result);
|
||||
}
|
||||
Reference in New Issue
Block a user