chore: track claude skills, tools, templates, reference code and research-wiki

- Add all claude skills (brainstorming, commit, debugging, TDD, etc.)
- Add claude hooks (pre-commit-guard, post-edit-quality)
- Add research templates (experiment plan, research brief, etc.)
- Add claude tools (arxiv/semantic_scholar/openalex fetch, wiki, exa)
- Add TRM4 reference implementation as algorithm fidelity baseline
- Add research-wiki content (plans, index, graph, query_pack)
- Update .gitignore to exclude .graphify_version runtime state
This commit is contained in:
2026-07-06 20:59:03 -04:00
parent 0616d16956
commit 6bdb802f01
98 changed files with 19321 additions and 0 deletions
+144
View File
@@ -0,0 +1,144 @@
"""Exa 搜索命令行工具。"""
from __future__ import annotations
import argparse
import json
import os
import sys
from dataclasses import asdict, is_dataclass
from typing import Any
try:
import exa
except ImportError:
print("警告: 未安装 exa-py SDK,已跳过 Exa CLI。", file=sys.stderr)
sys.exit(0)
def _to_jsonable(value: Any) -> Any:
"""将任意 SDK 返回值递归转换为可序列化对象。"""
if value is None or isinstance(value, (str, int, float, bool)):
return value
if isinstance(value, bytes):
return value.decode("utf-8", errors="replace")
if isinstance(value, dict):
return {str(key): _to_jsonable(item) for key, item in value.items()}
if isinstance(value, (list, tuple, set)):
return [_to_jsonable(item) for item in value]
if is_dataclass(value):
return _to_jsonable(asdict(value))
model_dump = getattr(value, "model_dump", None)
if callable(model_dump):
return _to_jsonable(model_dump())
if hasattr(value, "__dict__"):
data = {
key: _to_jsonable(item)
for key, item in vars(value).items()
if not key.startswith("_")
}
if data:
return data
return str(value)
def _build_contents_option(content_mode: str) -> dict[str, Any]:
"""根据命令行参数构造 Exa contents 配置。"""
if content_mode == "text":
return {"text": True}
return {"highlights": True}
def _extract_results(response: Any) -> list[dict[str, Any]]:
"""从 Exa 响应中提取结果列表并标准化为 JSON 对象。"""
raw_results: Any
if isinstance(response, dict):
raw_results = response.get("results", [])
else:
raw_results = getattr(response, "results", [])
if not isinstance(raw_results, list):
return []
normalized_results: list[dict[str, Any]] = []
for item in raw_results:
normalized_item = _to_jsonable(item)
if isinstance(normalized_item, dict):
normalized_results.append(normalized_item)
else:
normalized_results.append({"value": normalized_item})
return normalized_results
def search_exa(
query: str,
max_results: int,
category: str | None,
content_mode: str,
) -> list[dict[str, Any]]:
"""执行 Exa 搜索并返回结果列表。"""
api_key = os.environ.get("EXA_API_KEY", "").strip()
if not api_key:
print("警告: 缺少 EXA_API_KEY 环境变量,已跳过 Exa CLI。", file=sys.stderr)
sys.exit(0)
client = exa.Exa(api_key=api_key)
search_kwargs: dict[str, Any] = {
"num_results": max_results,
"contents": _build_contents_option(content_mode),
}
if category:
search_kwargs["category"] = category
response = client.search(query, **search_kwargs)
return _extract_results(response)
def build_parser() -> argparse.ArgumentParser:
"""构建命令行参数解析器。"""
parser = argparse.ArgumentParser(description="Exa 搜索工具")
subparsers = parser.add_subparsers(dest="command", required=True)
search_parser = subparsers.add_parser("search", help="搜索 Exa 索引")
search_parser.add_argument("query", help="搜索关键词")
search_parser.add_argument("--max", dest="max_results", type=int, default=10)
search_parser.add_argument(
"--category",
default=None,
help="数据类别,例如 research paper、news、pdf 等",
)
search_parser.add_argument(
"--content",
choices=("highlights", "text"),
default="highlights",
help="返回的内容类型",
)
return parser
def main(argv: list[str] | None = None) -> int:
"""CLI 入口。"""
parser = build_parser()
args = parser.parse_args(argv)
try:
if args.command == "search":
results = search_exa(
query=args.query,
max_results=args.max_results,
category=args.category,
content_mode=args.content,
)
json.dump(results, sys.stdout, ensure_ascii=False, indent=2)
sys.stdout.write("\n")
return 0
except Exception as exc:
print(f"错误: {exc}", file=sys.stderr)
return 1
parser.error(f"未知命令: {args.command}")
return 2
if __name__ == "__main__":
sys.exit(main())