last30days-cn
A Claude Code skill that researches what Chinese internet users said recently across Weibo, Xiaohongshu, Bilibili, Zhihu, Douyin, WeChat, Baidu and Toutiao. It ranks posts by engagement and recency and writes a sourced research report.
What ainl-cortex runs automatically, and when. A hook is a command Claude Code fires at a fixed moment, without you asking for it.
What fires, and when
SessionStart
Fires once when a session begins, and again after a context compaction. It is where a plugin sets up its environment, or restores state the compaction dropped.
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/run_hook.py" startupUserPromptSubmit
Fires before Claude sees each prompt you send. A plugin can use it to inject context, so the same instruction reaches the model every turn instead of only at session start.
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/run_hook.py" user_prompt_submitpython3 "${CLAUDE_PLUGIN_ROOT}/scripts/run_hook.py" ainl_detectionUserPromptExpansion
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/run_hook.py" user_prompt_expansionPostToolUse
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/run_hook.py" post_tool_usepython3 "${CLAUDE_PLUGIN_ROOT}/scripts/run_hook.py" ainl_validatorPreCompact
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/run_hook.py" pre_compactPostCompact
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/run_hook.py" post_compactStop
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/run_hook.py" stopIn the plugin's words
How ainl-cortex describes its own hook set.
AINL-inspired graph memory system for Claude Code - execution becomes memory
Where it lives
"""
ArmaraOS daemon discovery for the A2A subsystem.
Replaces the old "launch a Python bridge" approach.
ArmaraOS is the A2A bridge — we just discover it via ~/.armaraos/daemon.json.
"""
import json
import os
import socket
import time
from pathlib import Path
from typing import Dict, Any
from shared.armaraos_daemon import (
DAEMON_NOT_FOUND_REASON,
LEGACY_DAEMON_URL_CACHE_NAME,
daemon_url_cache_path,
scan_daemon_listen_port,
)
def _pid_alive(pid: int) -> bool:
try:
os.kill(pid, 0)
return True
except (ProcessLookupError, PermissionError):
return False
def _port_open(host: str, port: int, timeout: float = 1.0) -> bool:
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(timeout)
result = s.connect_ex((host, port))
s.close()
return result == 0
except Exception:
return False
def _write_url_cache(plugin_root: Path, base_url: str, pid, version: str) -> None:
"""Write discovered daemon URL to plugin-local cache for fast reuse."""
cache_file = daemon_url_cache_path(plugin_root)
cache_file.parent.mkdir(parents=True, exist_ok=True)
tmp = cache_file.with_suffix(".tmp")
tmp.write_text(json.dumps({
"base_url": base_url,
"pid": pid,
"version": version,
"discovered_at": int(time.time()),
}), encoding="utf-8")
os.replace(tmp, cache_file)
def ensure_bridge_running(plugin_root: Path, config: dict) -> Dict[str, Any]:
"""
Discover the ArmaraOS daemon, cache its URL, and return its status.
Discovery order:
1. daemon.json — check if the recorded port is still alive
2. lsof scan — daemon may use dynamic ports on restart
Result is written to a2a/armaraos_daemon_url.json so tools skip re-scanning.
"""
a2a_cfg = config.get("a2a", {})
if not a2a_cfg.get("enabled", True):
return {"running": False, "reason": "disabled"}
daemon_json_path = Path(
a2a_cfg.get("daemon_json", "~/.armaraos/daemon.json")
).expanduser()
pid = None
version = "unknown"
# ── Step 1: try daemon.json ───────────────────────────────────────────────
if daemon_json_path.exists():
try:
daemon = json.loads(daemon_json_path.read_text(encoding="utf-8"))
pid = daemon.get("pid")
version = daemon.get("version", "unknown")
listen_addr = daemon.get("listen_addr", "")
if listen_addr:
host, _, port_str = listen_addr.rpartition(":")
port = int(port_str)
if _pid_alive(pid) and _port_open(host, port):
base_url = f"http://{listen_addr}"
_write_url_cache(plugin_root, base_url, pid, version)
return {"running": True, "pid": pid, "port": port, "host": host,
"base_url": base_url, "version": version, "source": "daemon.json"}
except Exception:
pass
# ── Step 2: lsof scan for dynamic port ───────────────────────────────────
host, port = scan_daemon_listen_port()
if host and port:
base_url = f"http://{host}:{port}"
# Confirm it's actually the ArmaraOS API
try:
import urllib.request
resp = urllib.request.urlopen(f"{base_url}/api/health", timeout=2)
data = json.loads(resp.read())
version = data.get("version", version)
# Try to get PID from /api/health or keep what we have from daemon.json
except Exception:
pass
_write_url_cache(plugin_root, base_url, pid, version)
return {"running": True, "pid": pid, "port": port, "host": host,
"base_url": base_url, "version": version, "source": "lsof"}
# ── Not found ─────────────────────────────────────────────────────────────
# Clear stale cache so tools don't use a dead URL
cache_file = daemon_url_cache_path(plugin_root)
if cache_file.exists():
cache_file.unlink(missing_ok=True)
legacy_cache = plugin_root / "a2a" / LEGACY_DAEMON_URL_CACHE_NAME
if legacy_cache.exists():
legacy_cache.unlink(missing_ok=True)
return {"running": False, "reason": DAEMON_NOT_FOUND_REASON}
#!/usr/bin/env python3
"""
A2A inbox writer — delegate script for HERMES_AINL_BRIDGE_CMD.
The A2A bridge calls this script with the inbound message text on stdin.
We write a structured JSON file to the plugin inbox and print "ok" to
stdout so the bridge marks the task completed.
Zero plugin imports — stdlib only so any Python 3.8+ installation works.
"""
import json
import os
import sys
import time
import uuid
from pathlib import Path
PLUGIN_ROOT = Path(os.environ.get("AINL_PLUGIN_ROOT", Path(__file__).resolve().parent.parent))
INBOX_DIR = PLUGIN_ROOT / "a2a" / "inbox"
def extract_header(text: str, header: str, default: str = "") -> str:
"""Extract X-Header: value from message text (convention for structured sends)."""
prefix = f"{header}: "
for line in text.splitlines():
if line.startswith(prefix):
return line[len(prefix):].strip()
return default
def strip_headers(text: str) -> str:
"""Remove X-* header lines from message body."""
lines = []
for line in text.splitlines():
if not (line.startswith("X-") and ": " in line):
lines.append(line)
return "\n".join(lines).strip()
def main():
raw = sys.stdin.read().strip()
if not raw:
print("ok")
return
msg_id = str(uuid.uuid4())
from_agent = extract_header(raw, "X-From-Agent", "unknown")
urgency = extract_header(raw, "X-Urgency", "normal")
thread_id = extract_header(raw, "X-Thread-Id") or None
task_id = extract_header(raw, "X-Task-Id") or None
msg_type = "task_result" if task_id else "message"
body = strip_headers(raw)
msg = {
"id": msg_id,
"type": msg_type,
"from_agent": from_agent,
"to_agent": "claude-code",
"thread_id": thread_id,
"task_id": task_id,
"message": body,
"urgency": urgency if urgency in ("critical", "normal", "low") else "normal",
"received_at": int(time.time()),
}
INBOX_DIR.mkdir(parents=True, exist_ok=True)
tmp = INBOX_DIR / f"{msg_id}.tmp"
dest = INBOX_DIR / f"{msg_id}.json"
tmp.write_text(json.dumps(msg, indent=2), encoding="utf-8")
os.replace(tmp, dest)
print("ok")
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""AINL opportunity detection hook for Claude Code.
Detects when to suggest using .ainl files based on user prompts and context.
Includes persona evolution tracking.
"""
import json
import re
import sys
from pathlib import Path
from typing import Dict, List, Optional, Any
# Add parent directory to path for imports
sys.path.insert(0, str(Path(__file__).parent.parent))
try:
from mcp_server.persona_evolution import (
PersonaEvolutionEngine,
detect_action_from_context
)
PERSONA_AVAILABLE = True
except ImportError:
PERSONA_AVAILABLE = False
# Trigger keywords that suggest AINL usage
RECURRING_KEYWORDS = [
"every", "hourly", "daily", "weekly", "monthly",
"monitor", "check", "recurring", "scheduled", "schedule", "cron",
"repeatedly", "periodic", "regular", "automation", "automat",
]
WORKFLOW_KEYWORDS = [
"workflow", "automation", "pipeline", "process",
"multi-step", "sequence", "orchestrate", "coordinate"
]
API_KEYWORDS = [
"api", "endpoint", "fetch", "http", "webhook",
"rest", "call", "request", "integration"
]
BLOCKCHAIN_KEYWORDS = [
"solana", "blockchain", "wallet", "crypto", "token",
"nft", "defi", "web3", "balance", "transfer"
]
COST_KEYWORDS = [
"cost", "expensive", "budget", "save", "cheap",
"token", "efficient", "optimize"
]
CONDITIONAL_KEYWORDS = [
"if", "when", "then", "else", "condition",
"check if", "depending on", "based on"
]
class AINLDetector:
"""Detects opportunities to suggest AINL."""
def __init__(self, project_id: Optional[str] = None):
self.confidence_threshold = 0.35
self.project_id = project_id
# Initialize persona engine if available
self.persona_engine = None
if PERSONA_AVAILABLE and project_id:
try:
persona_db = Path.home() / ".claude" / "projects" / project_id / "persona.db"
persona_db.parent.mkdir(parents=True, exist_ok=True)
self.persona_engine = PersonaEvolutionEngine(persona_db)
except Exception as e:
sys.stderr.write(f"Failed to initialize persona engine: {e}\n")
self.persona_engine = None
def analyze_prompt(self, prompt: str, context: Dict[str, Any]) -> Dict[str, Any]:
"""
Analyze user prompt to detect AINL opportunities.
Args:
prompt: User's message
context: Additional context (working dir, files, etc.)
Returns:
{
"suggest_ainl": bool,
"confidence": float (0-1),
"reasons": List[str],
"use_case": str,
"suggestion_text": str (markdown)
}
"""
prompt_lower = prompt.lower()
reasons = []
confidence_score = 0.0
# Check for .ainl files in workspace
has_ainl_files = self._check_ainl_files(context)
if has_ainl_files:
confidence_score += 0.2
reasons.append("Existing .ainl files in workspace")
# Check for recurring/scheduled patterns
recurring_matches = sum(1 for kw in RECURRING_KEYWORDS if kw in prompt_lower)
if recurring_matches > 0:
confidence_score += min(0.70, 0.35 + (recurring_matches - 1) * 0.15)
reasons.append(f"Recurring pattern detected ({recurring_matches} keywords)")
# Check for workflow patterns
workflow_matches = sum(1 for kw in WORKFLOW_KEYWORDS if kw in prompt_lower)
if workflow_matches > 0:
confidence_score += min(0.3, workflow_matches * 0.15)
reasons.append(f"Workflow pattern detected ({workflow_matches} keywords)")
# Check for API integration
api_matches = sum(1 for kw in API_KEYWORDS if kw in prompt_lower)
if api_matches >= 1:
confidence_score += min(0.25, api_matches * 0.1)
reasons.append("API integration detected")
# Check for blockchain
blockchain_matches = sum(1 for kw in BLOCKCHAIN_KEYWORDS if kw in prompt_lower)
if blockchain_matches > 0:
confidence_score += 0.5 # Strong signal
reasons.append("Blockchain interaction detected (AINL has Solana adapter)")
# Check for cost concerns
cost_matches = sum(1 for kw in COST_KEYWORDS if kw in prompt_lower)
if cost_matches > 0:
confidence_score += 0.3
reasons.append("Cost/efficiency concern detected")
# Check for conditional logic
conditional_matches = sum(1 for kw in CONDITIONAL_KEYWORDS if kw in prompt_lower)
if conditional_matches > 1:
confidence_score += 0.2
reasons.append("Conditional logic detected")
# Determine use case
use_case = self._determine_use_case(
prompt_lower,
recurring_matches,
workflow_matches,
blockchain_matches,
api_matches
)
# Cap confidence at 1.0
confidence_score = min(1.0, confidence_score)
# Extract persona signals from prompt
if self.persona_engine and PERSONA_AVAILABLE:
try:
action = detect_action_from_context(prompt)
if action:
signals = self.persona_engine.extract_signals(action, context)
if signals:
self.persona_engine.ingest_signals(signals)
except Exception as e:
sys.stderr.write(f"Persona signal extraction failed: {e}\n")
# Generate suggestion text
suggestion_text = ""
persona_traits = ""
if confidence_score >= self.confidence_threshold:
suggestion_text = self._generate_suggestion(
use_case, confidence_score, reasons
)
# Add persona traits if available
if self.persona_engine:
try:
persona_trait#!/usr/bin/env python3
"""AINL auto-validation hook for Claude Code.
Automatically validates .ainl files after tool use (Read/Edit/Write).
"""
import json
import sys
from pathlib import Path
from typing import Dict, Any, Optional
sys.path.insert(0, str(Path(__file__).parent))
from shared.project_id import get_project_id
# Try to import AINL tools
try:
sys.path.insert(0, str(Path(__file__).parent.parent))
from mcp_server.ainl_tools import AINLTools, _HAS_AINL
except ImportError:
_HAS_AINL = False
class AINLValidator:
"""Auto-validates .ainl files after Edit/Write tool use."""
def __init__(self, project_id: Optional[str] = None):
self.tools = AINLTools() if _HAS_AINL else None
self.project_id = project_id
def should_validate(self, event: Dict[str, Any]) -> Optional[str]:
"""
Check if we should validate based on event.
Returns:
File path if should validate, None otherwise
"""
# Claude Code PostToolUse payload uses snake_case field names
tool_name = event.get("tool_name", "")
if tool_name not in ["Edit", "Write"]:
# Read doesn't change the file — no point re-validating on read
return None
tool_input = event.get("tool_input", {}) or {}
file_path = tool_input.get("file_path")
if file_path and file_path.endswith(".ainl"):
return file_path
return None
def validate_file(self, file_path: str) -> Optional[Dict[str, Any]]:
"""
Validate .ainl file and return diagnostics.
Returns:
Validation result or None if can't validate
"""
if not self.tools:
return None
try:
# Read file
with open(file_path, 'r', encoding='utf-8') as f:
source = f.read()
# Validate with strict mode
result = self.tools.validate(source, strict=True)
return result
except FileNotFoundError:
return None
except Exception as e:
return {
"valid": False,
"error": f"Validation error: {e}"
}
def format_validation_output(self, file_path: str, validation: Dict[str, Any]) -> str:
"""Format validation results as a compact markdown block."""
name = Path(file_path).name
if validation.get("valid"):
next_tools = validation.get('recommended_next_tools', [])
msg = validation.get('message', 'Valid')
out = f"**AINL Validation:** ✅ {name}\n{msg}"
if next_tools:
out += f"\n**Next steps:** {', '.join(next_tools)}"
return out
diagnostics = validation.get("diagnostics", [])
primary = validation.get("primary_diagnostic")
out = f"**AINL Validation:** ❌ {name}\n"
if primary:
out += f"**Error:** {primary.get('message', 'Unknown error')}\n"
if "line" in primary:
out += f"**Line:** {primary['line']}\n"
repair_steps = validation.get("agent_repair_steps", [])
if repair_steps:
out += "**How to fix:**\n" + "".join(f"- {s}\n" for s in repair_steps)
if len(diagnostics) > 1:
out += f"\n**{len(diagnostics) - 1} additional issue(s)**"
resources = validation.get("recommended_resources", [])
if resources:
out += f"\n**Resources:** {', '.join(resources)}"
return out
def main():
"""Hook entry point for PostToolUse."""
if not _HAS_AINL:
# Silently skip if AINL not installed
return
try:
from shared.stdin import read_stdin_json
event = read_stdin_json(hook_name="ainl_validator")
# projectId is not in PostToolUse payloads — compute from cwd
cwd = Path(event.get("cwd", str(Path.cwd())))
project_id = get_project_id(cwd)
validator = AINLValidator(project_id=project_id)
# Check if we should validate
file_path = validator.should_validate(event)
if not file_path:
return
# Validate
validation = validator.validate_file(file_path)
if not validation:
return
# Format output
output_text = validator.format_validation_output(file_path, validation)
# PostToolUse context injection: hookSpecificOutput.additionalContext
output = {
"hookSpecificOutput": {
"additionalContext": output_text
}
}
print(json.dumps(output))
except Exception as e:
# Silent failure
sys.stderr.write(f"AINL validator error: {e}\n")
pass
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""
Notification poller for ainl-cortex.
Fetches https://www.ainativelang.com/notifications once per session, filters for
this plugin, surfaces unseen entries in the SessionStart banner, and optionally
applies auto-updates when the server marks a release safe.
Client algorithm (matches server contract):
1. Require schema_version == 1 (forward-compat: accept > 1 silently too).
2. Filter: targets must include "claude-code-plugin", "ainativelang", "ainl", or "*".
3. Drop: expires_at present and now > expires_at.
4. Sort: priority desc (default 0), then published_at desc.
5. Auto-update: enabled + artifact == "ainl-cortex" + version in range.
"""
import json
import os
import ssl
import subprocess
import sys
import urllib.error
import urllib.request
from datetime import datetime, timezone
from pathlib import Path
from typing import Any, Dict, List, Optional, Tuple
def _ssl_context() -> ssl.SSLContext:
"""Return an SSL context that works on macOS without system cert config."""
try:
import certifi
return ssl.create_default_context(cafile=certifi.where())
except ImportError:
pass
return ssl.create_default_context()
NOTIFICATIONS_URL = "https://www.ainativelang.com/notifications"
PLUGIN_ARTIFACT = "ainl-cortex"
OUR_TARGETS: frozenset = frozenset({"claude-code-plugin", "ainativelang", "ainl", "*"})
SEEN_FILE_REL = Path("a2a") / "notifications_seen.json"
PLUGIN_JSON_REL = Path(".claude-plugin") / "plugin.json"
# ── version helpers ────────────────────────────────────────────────────────────
def _read_plugin_version(plugin_root: Path) -> str:
try:
data = json.loads((plugin_root / PLUGIN_JSON_REL).read_text())
return str(data.get("version", "0.0.0"))
except Exception:
return "0.0.0"
def _ver_tuple(v: str) -> Tuple[int, ...]:
try:
return tuple(int(x) for x in v.split(".")[:3])
except Exception:
return (0, 0, 0)
def _version_in_range(current: str, min_v: Optional[str], max_v: Optional[str]) -> bool:
cur = _ver_tuple(current)
if min_v is not None and cur < _ver_tuple(min_v):
return False
if max_v is not None and cur > _ver_tuple(max_v):
return False
return True
# ── datetime helper ────────────────────────────────────────────────────────────
def _parse_dt(s: Optional[str]) -> Optional[datetime]:
if not s:
return None
try:
return datetime.fromisoformat(s.replace("Z", "+00:00"))
except Exception:
return None
# ── seen-ID persistence ────────────────────────────────────────────────────────
def _load_seen(plugin_root: Path) -> set:
path = plugin_root / SEEN_FILE_REL
try:
data = json.loads(path.read_text())
return set(data.get("seen_ids", []))
except Exception:
return set()
def _save_seen(plugin_root: Path, seen: set) -> None:
path = plugin_root / SEEN_FILE_REL
path.parent.mkdir(parents=True, exist_ok=True)
tmp = path.with_suffix(".tmp")
tmp.write_text(json.dumps({"seen_ids": sorted(seen)}, indent=2), encoding="utf-8")
os.replace(tmp, path)
# ── network ────────────────────────────────────────────────────────────────────
def _fetch(url: str, version: str, timeout: float) -> Optional[Dict[str, Any]]:
req = urllib.request.Request(url, method="GET")
req.add_header("Accept", "application/json")
req.add_header("User-Agent", f"ainl-cortex/{version}")
try:
ctx = _ssl_context()
with urllib.request.urlopen(req, timeout=timeout, context=ctx) as resp:
raw = resp.read().decode("utf-8")
return json.loads(raw)
except (urllib.error.URLError, OSError, json.JSONDecodeError):
return None
# ── auto-update ────────────────────────────────────────────────────────────────
def _try_auto_update(plugin_root: Path, notif: Dict[str, Any], current_version: str) -> Optional[str]:
"""
Runs `git pull --ff-only` inside plugin_root.
Returns a human-readable result string, or None if skipped.
"""
au = notif.get("auto_update")
if not (isinstance(au, dict) and au.get("enabled")):
return None
if au.get("artifact") != PLUGIN_ARTIFACT:
return None
if not _version_in_range(current_version, au.get("min_version"), au.get("max_version")):
return None
try:
r = subprocess.run(
["git", "pull", "--ff-only"],
cwd=str(plugin_root),
capture_output=True,
text=True,
timeout=30,
)
if r.returncode == 0:
try:
sys.path.insert(0, str(plugin_root))
from mcp_server.build_stamp import write_install_stamp
from mcp_server.mcp_reload import request_mcp_reload
write_install_stamp(plugin_root)
request_mcp_reload(plugin_root, reason="git_pull_auto_update")
except Exception:
pass
return f"auto-updated ainl-cortex: {r.stdout.strip()[:200]}"
else:
return f"auto-update attempted but failed: {(r.stderr or r.stdout or '').strip()[:200]}"
except Exception as e:
return f"auto-update error: {e}"
# ── public API ─────────────────────────────────────────────────────────────────
def poll(plugin_root: Path, config: Dict[str, Any]) -> Tuple[List[Dict[str, Any]], List[str]]:
"""
Check the notifications feed and return:
new_notifs — list of notification dicts not yet seen (to show in banner)
update_msgs — list of strings from any auto-update attempts
Reads config["notifications"] for:
enabled (bool, default True)
url (str, overrides NOTIFICATIONS_URL)
check_timeout_seconds (float, default 5.0)
auto_update (bool, default False) — gate for running git pull
"""
notif_cfg: Dict[str, Any] = config.get("notifications", {})
if not notif_cfg.get("#!/usr/bin/env python3
"""
PostCompact Hook — Update anchored summary after context compaction.
After compaction, update the anchored summary to reflect the compacted state.
This ensures the next session start injection shows the correct "in progress"
context rather than stale prior-session data.
"""
import json
import sys
import time
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent))
sys.path.insert(0, str(Path(__file__).parent.parent / "mcp_server"))
sys.path.insert(0, str(Path(__file__).parent.parent))
from shared.mcp_bootstrap import ensure_hook_mcp_imports
ensure_hook_mcp_imports()
from shared.project_id import get_project_id
from shared.logger import log_event, get_logger
def _get_compact_project_id(plugin_root: Path) -> str:
"""Get the session's project_id from the startup-written sidecar.
PostCompact payload doesn't include cwd, so we fall back to the sidecar
written by startup.py. Without this, get_project_id() uses Path.cwd()
which is the plugin root, not the user's actual project."""
try:
cid_file = plugin_root / "inbox" / "current_project_id.txt"
if cid_file.exists():
pid = cid_file.read_text(encoding="utf-8").strip()
if pid:
return pid
except Exception:
pass
return get_project_id()
logger = get_logger("post_compact")
try:
import ainl_native as _ainl_native
_NATIVE_OK = True
except ImportError:
_ainl_native = None
_NATIVE_OK = False
def main():
try:
from shared.stdin import read_stdin_json
input_data = read_stdin_json(hook_name="post_compact")
plugin_root = Path(__file__).parent.parent
project_id = _get_compact_project_id(plugin_root)
messages_before = input_data.get('messagesBefore', 0)
messages_after = input_data.get('messagesAfter', 0)
messages_removed = messages_before - messages_after
estimated_tokens_saved = messages_removed * 200
log_event("post_compact", {
"project_id": project_id,
"messages_before": messages_before,
"messages_after": messages_after,
"messages_removed": messages_removed,
"estimated_tokens_saved": estimated_tokens_saved,
})
logger.info(f"PostCompact: {messages_removed} messages removed, ~{estimated_tokens_saved} tokens saved")
# Update anchored summary to reflect post-compaction state
if _NATIVE_OK:
try:
db_path = Path.home() / ".claude" / "projects" / project_id / "graph_memory"
db_path.mkdir(parents=True, exist_ok=True)
native_db = str(db_path / "ainl_native.db")
store = _ainl_native.AinlNativeStore.open(native_db)
# Fetch existing summary to preserve task context
prior_raw = store.fetch_anchored_summary("claude-code")
prior_summary = "session compacted"
prior_ts = int(time.time())
if prior_raw:
try:
p = json.loads(prior_raw)
prior_summary = p.get("task_summary", prior_summary)
prior_ts = p.get("session_ts", prior_ts)
except Exception:
pass
payload = json.dumps({
"schema_version": 1,
"task_summary": prior_summary,
"outcome": "in_progress",
"post_compaction": True,
"messages_after_compaction": messages_after,
"tokens_saved_by_compaction": estimated_tokens_saved,
"session_ts": prior_ts,
"compacted_at": int(time.time()),
"project_id": project_id,
}, separators=(",", ":"))
node_id = store.upsert_anchored_summary("claude-code", payload)
logger.info(f"PostCompact anchored summary updated: {node_id}")
except Exception as e:
logger.debug(f"PostCompact summary update failed (non-fatal): {e}")
print(json.dumps({}), file=sys.stdout)
except Exception as e:
logger.error(f"PostCompact error: {e}")
print(json.dumps({}), file=sys.stdout)
finally:
sys.exit(0)
if __name__ == "__main__":
main()
All 14 scripts are listed above. The source is inlined for 6 of them, starting with whatever hooks.json actually runs. See all of them in the repo.
Read the script before you install anything that runs on your machine. This is the one part of a plugin that acts without being asked.
Graph-native memory and learning for Claude Code — every interaction remembered, every pattern learned, every agent connected. → Install in 30 seconds AINativeLang: Website · X · PyPI · GitHub · Docs · Developer: Steven Hooley | @sbhooley
A Claude Code skill that researches what Chinese internet users said recently across Weibo, Xiaohongshu, Bilibili, Zhihu, Douyin, WeChat, Baidu and Toutiao. It ranks posts by engagement and recency and writes a sourced research report.
Semantic search over past Claude Code conversations, so a session can recall earlier discussions, decisions, and patterns. It adds a remembering-conversations skill that checks prior sessions before the assistant says it doesn't know.
Gives Claude Code a long-term memory layer with semantic search, recall injection, and graph entities, letting agents recall, save, inspect, and audit memories through bundled MCP tools.
Skills and agent plugins that let AI coding agents such as Claude Code work with Databricks, covering AI Functions, Agent Bricks, AI/BI dashboards, GPU training workloads and Databricks Apps design.
A Claude Code plugin for football data work: acquiring data from sources such as StatsBomb, Opta and FBref, analysing it, computing metrics like xG and PPDA, and fixing broken scrapers. Built for analysts, developers, journalists and researchers.
A lightweight plugin that integrates Pinecone vector database capabilities directly into Claude Code, enabling semantic search, index management, and RAG (Retrieval Augmented Generation) workflows.
A research workflow plugin for Claude Code that turns folders of papers, notes and drafts into a linked, Obsidian-compatible knowledge base. It adds skills to adopt existing material, compile sources into concept cards, answer questions with citations, and write up unattended runs.
Archives every Claude Code session locally in a searchable SQLite store and saves durable facts into persistent memory. Adds skills to flush and review memory at session start or before clearing a conversation.